Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / sys / dev / serial / dgb / dgm.c
1 /*-
2  * $FreeBSD: src/sys/dev/dgb/dgm.c,v 1.31.2.3 2001/10/07 09:02:25 brian Exp $
3  * $DragonFly: src/sys/dev/serial/dgb/dgm.c,v 1.16 2006/12/22 23:26:24 swildner Exp $
4  *
5  *  This driver and the associated header files support the ISA PC/Xem
6  *  Digiboards.  Its evolutionary roots are described below.
7  *  Jack O'Neill <jack@diamond.xtalwind.net>
8  *
9  *  Digiboard driver.
10  *
11  *  Stage 1. "Better than nothing".
12  *  Stage 2. "Gee, it works!".
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions, and the following disclaimer,
19  *    without modification, immediately at the beginning of the file.
20  * 2. Redistributions of binary code must retain the above copyright
21  *    notice, this list of conditions, and the following disclaimer,
22  *    without modification, in the accompanying documentation.
23  * 3. The name of the author may not be used to endorse or promote products
24  *    derived from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
30  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *  Written by Sergey Babkin,
39  *      Joint Stock Commercial Bank "Chelindbank"
40  *      (Chelyabinsk, Russia)
41  *      babkin@freebsd.org
42  *
43  *  Assorted hacks to make it more functional and working under 3.0-current.
44  *  Fixed broken routines to prevent processes hanging on closed (thanks
45  *  to Bruce for his patience and assistance). Thanks also to Maxim Bolotin
46  *  <max@run.net> for his patches which did most of the work to get this
47  *  running under 2.2/3.0-current.
48  *  Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP &
49  *  TIOCDCDTIMESTAMP.
50  *  Sysctl debug flag is now a bitflag, to filter noise during debugging.
51  *      David L. Nugent <davidn@blaze.net.au>
52  *
53  * New-busification by Brian Somers <brian@Awfulhak.org>
54  *
55  * There was a copyright confusion: I thought that having read the
56  * GLPed drivers makes me mentally contaminated but in fact it does
57  * not. Since the Linux driver by Troy De Jongh <troyd@digibd.com> or
58  * <troyd@skypoint.com> was used only to learn the Digi's interface,
59  * I've returned this driver to a BSD-style license. I tried to contact
60  * all the contributors and those who replied agreed with license
61  * change. If you did any contribution when the driver was GPLed and do
62  * not agree with the BSD-style re-licensing please contact me.
63  *  -SB
64  */
65
66 /* How often to run dgmpoll */
67 #define POLLSPERSEC 25
68
69 /* How many charactes can we write to input tty rawq */
70 #define DGB_IBUFSIZE (TTYHOG - 100)
71
72 /* the overall number of ports controlled by this driver */
73
74 #include <sys/param.h>
75
76 #include <sys/systm.h>
77 #include <sys/proc.h>
78 #include <sys/conf.h>
79 #include <sys/dkstat.h>
80 #include <sys/fcntl.h>
81 #include <sys/kernel.h>
82 #include <sys/sysctl.h>
83 #include <sys/malloc.h>
84 #include <sys/sysctl.h>
85 #include <sys/tty.h>
86 #include <sys/bus.h>
87 #include <sys/kobj.h>
88 #include <sys/bus.h>
89 #include <sys/rman.h>
90 #include <sys/thread2.h>
91
92 #include <machine/clock.h>
93
94 #include <vm/vm.h>
95 #include <vm/pmap.h>
96
97 #include "dgmfep.h"
98 #include "dgmbios.h"
99 #include "dgmreg.h"
100
101 #define CALLOUT_MASK            0x40000
102 #define CONTROL_MASK            0xC0
103 #define CONTROL_INIT_STATE      0x40
104 #define CONTROL_LOCK_STATE      0x80
105 #define UNIT_MASK               0x30000
106 #define PORT_MASK               0x3F
107 #define DEV_TO_UNIT(dev)        (MINOR_TO_UNIT(minor(dev)))
108 #define MINOR_MAGIC_MASK        (CALLOUT_MASK | CONTROL_MASK)
109 #define MINOR_TO_UNIT(mynor)    (((mynor) & UNIT_MASK) >> 16)
110 #define MINOR_TO_PORT(mynor)    ((mynor) & PORT_MASK)
111 #define IO_SIZE                 0x04
112 #define MEM_SIZE                0x8000
113
114 #define DGM_UNITMASK            0x30000
115 #define DGM_UNIT(unit)          ((unit) << 16)
116
117 struct dgm_softc;
118
119 /* digiboard port structure */
120 struct dgm_p {
121         unsigned enabled : 1;
122
123         struct dgm_softc *sc;  /* parent softc */
124         u_char pnum;           /* port number */
125         u_char omodem;         /* FEP output modem status     */
126         u_char imodem;         /* FEP input modem status      */
127         u_char modemfake;      /* Modem values to be forced   */
128         u_char modem;          /* Force values                */
129         u_char hflow;
130         u_char dsr;
131         u_char dcd;
132         u_char stopc;
133         u_char startc;
134         u_char stopca;
135         u_char startca;
136         u_char fepstopc;
137         u_char fepstartc;
138         u_char fepstopca;
139         u_char fepstartca;
140         u_char txwin;
141         u_char rxwin;
142         ushort fepiflag;
143         ushort fepcflag;
144         ushort fepoflag;
145         ushort txbufhead;
146         ushort txbufsize;
147         ushort rxbufhead;
148         ushort rxbufsize;
149         int close_delay;
150         u_char *txptr;
151         u_char *rxptr;
152         volatile struct board_chan *brdchan;
153         struct tty *tty;
154
155         u_char  active_out;     /* nonzero if the callout device is open */
156         u_int   wopeners;       /* # processes waiting for DCD in open() */
157
158         /* Initial state. */
159         struct termios  it_in;  /* should be in struct tty */
160         struct termios  it_out;
161
162         /* Lock state. */
163         struct termios  lt_in;  /* should be in struct tty */
164         struct termios  lt_out;
165
166         unsigned do_timestamp : 1;
167         unsigned do_dcd_timestamp : 1;
168         struct timeval  timestamp;
169         struct timeval  dcd_timestamp;
170
171         /* flags of state, are used in sleep() too */
172         u_char closing; /* port is being closed now */
173         u_char draining; /* port is being drained now */
174         u_char used;    /* port is being used now */
175         u_char mustdrain; /* data must be waited to drain in dgmparam() */
176
177         struct callout  hc_timeout;
178         struct callout  wf_timeout;
179 };
180
181 /* Digiboard per-board structure */
182 struct dgm_softc {
183         /* struct board_info */
184         unsigned enabled : 1;
185         u_char unit;                    /* unit number */
186         u_char type;                    /* type of card: PCXE, PCXI, PCXEVE */
187         u_char altpin;                  /* do we need alternate pin setting ? */
188         int numports;                   /* number of ports on card */
189         u_long port;                    /* I/O port */
190         u_char *vmem;                   /* virtual memory address */
191         u_long pmem;                    /* physical memory address */
192         int mem_seg;                    /* internal memory segment */
193         struct dgm_p *ports;            /* ptr to array of port descriptors */
194         struct tty *ttys;               /* ptr to array of TTY structures */
195         volatile struct global_data *mailbox;
196         struct resource *io_res;
197         struct resource *mem_res;
198         int iorid;
199         int mrid;
200         struct callout  toh;            /* poll timeout handle */
201 };
202
203 static void     dgmpoll(void *);
204 static int      dgmprobe(device_t);
205 static int      dgmattach(device_t);
206 static int      dgmdetach(device_t);
207 static int      dgmshutdown(device_t);
208 static void     fepcmd(struct dgm_p *, unsigned, unsigned, unsigned, unsigned,
209                     unsigned);
210 static void     dgmstart(struct tty *);
211 static void     dgmstop(struct tty *, int);
212 static int      dgmparam(struct tty *, struct termios *);
213 static void     dgmhardclose(struct dgm_p *);
214 static void     dgm_drain_or_flush(struct dgm_p *);
215 static int      dgmdrain(struct dgm_p *);
216 static void     dgm_pause(void *);
217 static void     wakeflush(void *);
218 static void     disc_optim(struct tty *, struct termios *);
219
220 static  d_open_t        dgmopen;
221 static  d_close_t       dgmclose;
222 static  d_ioctl_t       dgmioctl;
223
224 static device_method_t dgmmethods[] = {
225         /* Device interface */
226         DEVMETHOD(device_probe, dgmprobe),
227         DEVMETHOD(device_attach, dgmattach),
228         DEVMETHOD(device_detach, dgmdetach),
229         DEVMETHOD(device_shutdown, dgmshutdown),
230         { 0, 0 }
231 };
232
233 static driver_t dgmdriver = {
234         "dgm",
235         dgmmethods,
236         sizeof (struct dgm_softc),
237 };
238
239 static devclass_t dgmdevclass;
240
241 #define CDEV_MAJOR      101
242 static struct dev_ops dgm_ops = {
243         { "dgm", CDEV_MAJOR, D_TTY | D_KQFILTER },
244         .d_open =       dgmopen,
245         .d_close =      dgmclose,
246         .d_read =       ttyread,
247         .d_write =      ttywrite,
248         .d_ioctl =      dgmioctl,
249         .d_poll =       ttypoll,
250         .d_kqfilter =   ttykqfilter
251 };
252
253 static int
254 dgmmodhandler(module_t mod, int event, void *arg)
255 {
256         int res = 0;
257
258         switch (event) {
259         case MOD_LOAD:
260                 break;
261         case MOD_UNLOAD:
262                 break;
263         }
264
265         return res;
266 }
267
268 DRIVER_MODULE(dgm, isa, dgmdriver, dgmdevclass, dgmmodhandler, 0);
269
270 static  speed_t dgmdefaultrate = TTYDEF_SPEED;
271
272 static  struct speedtab dgmspeedtab[] = {
273         { 0,            FEP_B0 }, /* old (sysV-like) Bx codes */
274         { 50,           FEP_B50 },
275         { 75,           FEP_B75 },
276         { 110,          FEP_B110 },
277         { 134,          FEP_B134 },
278         { 150,          FEP_B150 },
279         { 200,          FEP_B200 },
280         { 300,          FEP_B300 },
281         { 600,          FEP_B600 },
282         { 1200,         FEP_B1200 },
283         { 1800,         FEP_B1800 },
284         { 2400,         FEP_B2400 },
285         { 4800,         FEP_B4800 },
286         { 9600,         FEP_B9600 },
287         { 19200,        FEP_B19200 },
288         { 38400,        FEP_B38400 },
289         { 57600,        (FEP_FASTBAUD|FEP_B50) }, /* B50 & fast baud table */
290         { 115200,       (FEP_FASTBAUD|FEP_B110) }, /* B100 & fast baud table */
291         { -1,   -1 }
292 };
293
294 static struct dbgflagtbl {
295         tcflag_t in_mask;
296         tcflag_t in_val;
297         tcflag_t out_val;
298 } dgm_cflags[] = {
299         { PARODD,   PARODD,     FEP_PARODD  },
300         { PARENB,   PARENB,     FEP_PARENB  },
301         { CSTOPB,   CSTOPB,     FEP_CSTOPB  },
302         { CSIZE,    CS5,        FEP_CS6     },
303         { CSIZE,    CS6,        FEP_CS6     },
304         { CSIZE,    CS7,        FEP_CS7     },
305         { CSIZE,    CS8,        FEP_CS8     },
306         { CLOCAL,   CLOCAL,     FEP_CLOCAL  },
307         { (tcflag_t)-1 }
308 }, dgm_iflags[] = {
309         { IGNBRK,   IGNBRK,     FEP_IGNBRK  },
310         { BRKINT,   BRKINT,     FEP_BRKINT  },
311         { IGNPAR,   IGNPAR,     FEP_IGNPAR  },
312         { PARMRK,   PARMRK,     FEP_PARMRK  },
313         { INPCK,    INPCK,      FEP_INPCK   },
314         { ISTRIP,   ISTRIP,     FEP_ISTRIP  },
315         { IXON,     IXON,       FEP_IXON    },
316         { IXOFF,    IXOFF,      FEP_IXOFF   },
317         { IXANY,    IXANY,      FEP_IXANY   },
318         { (tcflag_t)-1 }
319 }, dgm_flow[] = {
320         { CRTSCTS,  CRTSCTS,    CTS|RTS },
321         { CRTSCTS,  CCTS_OFLOW, CTS     },
322         { CRTSCTS,  CRTS_IFLOW, RTS     },
323         { (tcflag_t)-1 }
324 };
325
326 /* xlat bsd termios flags to dgm sys-v style */
327 static tcflag_t
328 dgmflags(struct dbgflagtbl *tbl, tcflag_t input)
329 {
330         tcflag_t output = 0;
331         int i;
332
333         for (i = 0; tbl[i].in_mask != (tcflag_t)-1; i++)
334                 if ((input & tbl[i].in_mask) == tbl[i].in_val)
335                         output |= tbl[i].out_val;
336
337         return output;
338 }
339
340 static int dgmdebug = 0;
341 SYSCTL_INT(_debug, OID_AUTO, dgm_debug, CTLFLAG_RW, &dgmdebug, 0, "");
342
343 static __inline int setwin(struct dgm_softc *, unsigned);
344 static __inline void hidewin(struct dgm_softc *);
345 static __inline void towin(struct dgm_softc *, int);
346
347 /*Helg: to allow recursive dgm...() calls */
348 typedef struct {
349         /* If we were called and don't want to disturb we need: */
350         int port;       /* write to this port */
351         u_char data;    /* this data on exit */
352         /* or DATA_WINOFF  to close memory window on entry */
353 } BoardMemWinState;     /* so several channels and even boards can coexist */
354
355 #define DATA_WINOFF 0
356 static BoardMemWinState bmws;
357
358 static u_long validio[] = { 0x104, 0x114, 0x124, 0x204, 0x224, 0x304, 0x324 };
359 static u_long validmem[] = {
360         0x80000, 0x88000, 0x90000, 0x98000, 0xa0000, 0xa8000, 0xb0000, 0xb8000,
361         0xc0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000, 0xe8000, 0xf0000, 0xf8000,
362         0xf0000000, 0xf1000000, 0xf2000000, 0xf3000000, 0xf4000000, 0xf5000000,
363         0xf6000000, 0xf7000000, 0xf8000000, 0xf9000000, 0xfa000000, 0xfb000000,
364         0xfc000000, 0xfd000000, 0xfe000000, 0xff000000
365 };
366
367 /* return current memory window state and close window */
368 static BoardMemWinState
369 bmws_get(void)
370 {
371         BoardMemWinState bmwsRet = bmws;
372
373         if (bmws.data != DATA_WINOFF)
374                 outb(bmws.port, bmws.data = DATA_WINOFF);
375         return bmwsRet;
376 }
377
378 /* restore memory window state */
379 static void
380 bmws_set(BoardMemWinState ws)
381 {
382         if (ws.data != bmws.data || ws.port != bmws.port) {
383                 if (bmws.data != DATA_WINOFF)
384                         outb(bmws.port, DATA_WINOFF);
385                 if (ws.data != DATA_WINOFF)
386                         outb(ws.port, ws.data);
387                 bmws = ws;
388         }
389 }
390
391 static __inline int
392 setwin(struct dgm_softc *sc, unsigned int addr)
393 {
394         outb(bmws.port = sc->port + 1, bmws.data = FEPWIN|(addr >> 15));
395         return (addr & 0x7FFF);
396 }
397
398 static __inline void
399 hidewin(struct dgm_softc *sc)
400 {
401         bmws.data = 0;
402         outb(bmws.port = sc->port + 1, bmws.data);
403 }
404
405 static __inline void
406 towin(struct dgm_softc *sc, int win)
407 {
408         outb(bmws.port = sc->port + 1, bmws.data = win);
409 }
410
411 static int
412 dgmprobe(device_t dev)
413 {
414         struct dgm_softc *sc = device_get_softc(dev);
415         int i, v;
416
417         /*
418          * Assign unit number.  Due to bits we use in the minor number for
419          * the various tty types, only 4 units are supported.
420          */
421         sc->unit = device_get_unit(dev);
422         if (sc->unit > 3) {
423                 device_printf(dev, "Too many units, only 4 supported\n");
424                 return(ENXIO);
425         }
426
427         /* Check that we've got a valid i/o address */
428         if ((sc->port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
429                 return (ENXIO);
430         for (i = sizeof (validio) / sizeof (validio[0]) - 1; i >= 0; i--)
431                 if (sc->port == validio[i])
432                         break;
433         if (i == -1) {
434                 device_printf(dev, "0x%03lx: Invalid i/o address\n", sc->port);
435                 return (ENXIO);
436         }
437
438         /* Ditto for our memory address */
439         if ((sc->pmem = bus_get_resource_start(dev, SYS_RES_MEMORY, 0)) == 0)
440                 return (ENXIO);
441         for (i = sizeof (validmem) / sizeof (validmem[0]) - 1; i >= 0; i--)
442                 if (sc->pmem == validmem[i])
443                         break;
444         if (i == -1) {
445                 device_printf(dev, "0x%lx: Invalid memory address\n", sc->pmem);
446                 return (ENXIO);
447         }
448         if ((sc->pmem & 0xFFFFFFul) != sc->pmem) {
449                 device_printf(dev, "0x%lx: Memory address not supported\n",
450                     sc->pmem);
451                 return (ENXIO);
452         }
453         sc->vmem = (u_char *)sc->pmem;
454
455         DPRINT4(DB_INFO, "dgm%d: port 0x%lx mem 0x%lx\n", sc->unit,
456             sc->port, sc->pmem);
457
458         /* Temporarily map our io ports */
459         sc->iorid = 0;
460         sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iorid,
461             0ul, ~0ul, IO_SIZE, RF_ACTIVE);
462         if (sc->io_res == NULL)
463                 return (ENXIO);
464
465         outb(sc->port, FEPRST);
466         sc->enabled = 0;
467
468         for (i = 0; i < 1000; i++) {
469                 DELAY(1);
470                 if ((inb(sc->port) & FEPMASK) == FEPRST) {
471                         sc->enabled = 1;
472                         DPRINT3(DB_EXCEPT, "dgm%d: got reset after %d us\n",
473                             sc->unit, i);
474                         break;
475                 }
476         }
477
478         if (!sc->enabled) {
479                 DPRINT2(DB_EXCEPT, "dgm%d: failed to respond\n", sc->unit);
480                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
481                 return (ENXIO);
482         }
483
484         /* check type of card and get internal memory characteristics */
485
486         v = inb(sc->port);
487
488         if (!(v & 0x1)) {
489                 int second;
490
491                 outb(sc->port, 1);
492                 second = inb(sc->port);
493                 kprintf("dgm%d: PC/Xem (type %d, %d)\n", sc->unit, v, second);
494         } else
495                 kprintf("dgm%d: PC/Xem (type %d)\n", sc->unit, v);
496
497         sc->type = PCXEM;
498         sc->mem_seg = 0x8000;
499
500         /* Temporarily map our memory too */
501         sc->mrid = 0;
502         sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid,
503             0ul, ~0ul, MEM_SIZE, RF_ALLOCATED);
504         if (sc->mem_res == NULL) {
505                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
506                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
507                 return (ENXIO);
508         }
509
510         outb(sc->port, FEPCLR); /* drop RESET */
511         hidewin(sc);    /* Helg: to set initial bmws state */
512
513         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
514         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
515
516         bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port, IO_SIZE);
517         bus_set_resource(dev, SYS_RES_MEMORY, 0, sc->pmem, MEM_SIZE);
518
519         DPRINT2(DB_INFO, "dgm%d: Probe returns 0\n", sc->unit);
520
521         return (0);
522 }
523
524 static int
525 dgmattach(device_t dev)
526 {
527         struct dgm_softc *sc = device_get_softc(dev);
528         int i, t;
529         u_char *mem;
530         u_char *ptr;
531         int addr;
532         struct dgm_p *port;
533         volatile struct board_chan *bc;
534         int shrinkmem;
535         int lowwater;
536         u_long msize, iosize;
537
538         DPRINT2(DB_INFO, "dbg%d: attaching\n", device_get_unit(dev));
539
540         callout_init(&sc->toh);
541         sc->unit = device_get_unit(dev);
542         bus_get_resource(dev, SYS_RES_IOPORT, 0, &sc->port, &iosize);
543         bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &msize);
544         sc->altpin = !!(device_get_flags(dev) & DGBFLAG_ALTPIN);
545         sc->type = PCXEM;
546         sc->mem_seg = 0x8000;
547         sc->enabled = 1;
548         sc->type = PCXEM;
549         sc->mem_seg = 0x8000;
550
551         /* Allocate resources (should have been verified in dgmprobe()) */
552         sc->iorid = 0;
553         sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iorid,
554             0ul, ~0ul, iosize, RF_ACTIVE);
555         if (sc->io_res == NULL)
556                 return (ENXIO);
557         sc->mrid = 0;
558         sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mrid,
559             0ul, ~0ul, msize, RF_ACTIVE);
560         if (sc->mem_res == NULL) {
561                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
562                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
563                 return (ENXIO);
564         }
565
566         /* map memory */
567         mem = sc->vmem = pmap_mapdev(sc->pmem, msize);
568
569         DPRINT3(DB_INFO, "dgm%d: internal memory segment 0x%x\n", sc->unit,
570             sc->mem_seg);
571
572         outb(sc->port, FEPRST);
573         DELAY(1);
574
575         for (i = 0; (inb(sc->port) & FEPMASK) != FEPRST; i++) {
576                 if (i > 10000) {
577                         device_printf(dev, "1st reset failed\n");
578                         sc->enabled = 0;
579                         hidewin(sc);
580                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
581                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
582                         return (ENXIO);
583                 }
584                 DELAY(1);
585         }
586
587         DPRINT3(DB_INFO, "dgm%d: got reset after %d us\n", sc->unit, i);
588
589         t = sc->pmem >> 8;      /* disable windowing */
590         outb(sc->port + 2, t & 0xFF);
591         outb(sc->port + 3, t >> 8);
592
593         mem = sc->vmem;
594
595         /* very short memory test */
596         DPRINT2(DB_INFO, "dbg%d: short memory test\n", sc->unit);
597
598         addr = setwin(sc, BOTWIN);
599         *(u_long *)(mem + addr) = 0xA55A3CC3;
600         if (*(u_long *)(mem + addr) != 0xA55A3CC3) {
601                 device_printf(dev, "1st memory test failed\n");
602                 sc->enabled = 0;
603                 hidewin(sc);
604                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
605                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
606                 return (ENXIO);
607         }
608
609         DPRINT2(DB_INFO, "dbg%d: 1st memory test ok\n", sc->unit);
610
611         addr = setwin(sc, TOPWIN);
612         *(u_long *)(mem + addr) = 0x5AA5C33C;
613         if (*(u_long *)(mem + addr) != 0x5AA5C33C) {
614                 device_printf(dev, "2nd memory test failed\n");
615                 sc->enabled = 0;
616                 hidewin(sc);
617                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
618                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
619                 return (ENXIO);
620         }
621
622         DPRINT2(DB_INFO, "dbg%d: 2nd memory test ok\n", sc->unit);
623
624         addr = setwin(sc, BIOSCODE + ((0xF000 - sc->mem_seg) << 4));
625         *(u_long *)(mem + addr) = 0x5AA5C33C;
626         if (*(u_long *)(mem + addr) != 0x5AA5C33C)
627                 device_printf(dev, "3rd (BIOS) memory test failed\n");
628
629         DPRINT2(DB_INFO, "dbg%d: 3rd memory test ok\n", sc->unit);
630
631         addr = setwin(sc, MISCGLOBAL);
632         for (i = 0; i < 16; i++)
633                 mem[addr + i] = 0;
634
635         addr = setwin(sc, BIOSOFFSET);
636         ptr = mem + addr;
637         for (i = 0; ptr < mem + msize; i++)
638                 *ptr++ = pcem_bios[i];
639
640         ptr = mem + BIOSOFFSET;
641         for (i = 0; ptr < mem + msize; i++) {
642                 if (*ptr++ != pcem_bios[i]) {
643                         kprintf("Low BIOS load failed\n");
644                         sc->enabled = 0;
645                         hidewin(sc);
646                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
647                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
648                         return (ENXIO);
649                 }
650         }
651         DPRINT2(DB_INFO, "dbg%d: pcem_bios seg 1 loaded\n", sc->unit);
652
653         addr = setwin(sc, msize);
654         ptr = mem + addr;
655         for (;i < pcem_nbios; i++)
656                 *ptr++ = pcem_bios[i];
657
658         ptr = mem;
659         for (i = msize - BIOSOFFSET; i < pcem_nbios; i++) {
660                 if (*ptr++ != pcem_bios[i]) {
661                         kprintf("High BIOS load failed\n");
662                         sc->enabled = 0;
663                         hidewin(sc);
664                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
665                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
666                         return (ENXIO);
667                 }
668         }
669         DPRINT2(DB_INFO, "dbg%d: pcem_bios seg 2 loaded\n", sc->unit);
670         device_printf(dev, "DigiBIOS loaded, initializing");
671
672         addr = setwin(sc, 0);
673
674         *(u_int *)(mem + addr) = 0x0bf00401;
675         *(u_int *)(mem + addr + 4) = 0;
676         *(ushort *)(mem + addr + 0xc00) = 0;
677         outb(sc->port, 0);
678
679         for (i = 0; *(u_char *)(mem + addr + 0xc00) != 0x47; i++) {
680                 DELAY(10000);
681                 if (i > 3000) {
682                         kprintf("\nBIOS initialize failed(1)\n");
683                         sc->enabled = 0;
684                         hidewin(sc);
685                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
686                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
687                         return (ENXIO);
688                 }
689         }
690
691         if (*(u_char *)(mem + addr + 0xc01) != 0x44) {
692                 kprintf("\nBIOS initialize failed(2)\n");
693                 sc->enabled = 0;
694                 hidewin(sc);
695                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
696                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
697                 return (ENXIO);
698         }
699         kprintf(", DigiBIOS running\n");
700
701         DELAY(10000);
702
703         addr = setwin(sc, BIOSOFFSET);
704         ptr = mem + addr;
705         for (i = 0; i < pcem_ncook; i++)
706                 *ptr++ = pcem_cook[i];
707
708         ptr = mem + BIOSOFFSET;
709         for (i = 0; i < pcem_ncook; i++) {
710                 if (*ptr++ != pcem_cook[i]) {
711                         kprintf("FEP/OS load failed\n");
712                         sc->enabled = 0;
713                         hidewin(sc);
714                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
715                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
716                         return (ENXIO);
717                 }
718         }
719         device_printf(dev, "FEP/OS loaded, initializing");
720
721         addr = setwin(sc, 0);
722         *(ushort *)(mem + addr + 0xd20) = 0;
723         *(u_int *)(mem + addr + 0xc34) = 0xbfc01004;
724         *(u_int *)(mem + addr + 0xc30) = 0x3L;
725         outb(sc->port, 0);
726
727         for (i = 0; *(u_char *)(mem + addr + 0xd20) != 'O'; i++) {
728                 DELAY(10000);
729                 if (i > 3000) {
730                         kprintf("\nFEP/OS initialize failed(1)\n");
731                         sc->enabled = 0;
732                         hidewin(sc);
733                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
734                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
735                         return (ENXIO);
736                 }
737         }
738
739         if (*(u_char *)(mem + addr + 0xd21) != 'S') {
740                 kprintf("\nFEP/OS initialize failed(2)\n");
741                 sc->enabled = 0;
742                 hidewin(sc);
743                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
744                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
745                 return (ENXIO);
746         }
747         kprintf(", FEP/OS running\n");
748
749         sc->numports = *(ushort *)(mem + setwin(sc, NPORT));
750         device_printf(dev, "%d ports attached\n", sc->numports);
751
752         if (sc->numports > MAX_DGM_PORTS) {
753                 kprintf("dgm%d: too many ports\n", sc->unit);
754                 sc->enabled = 0;
755                 hidewin(sc);
756                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
757                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
758                 return (ENXIO);
759         }
760
761         MALLOC(sc->ports, struct dgm_p *, sizeof (*sc->ports) * sc->numports,
762             M_TTYS, M_WAITOK|M_ZERO);
763         MALLOC(sc->ttys, struct tty *, sizeof (*sc->ttys) * sc->numports,
764             M_TTYS, M_WAITOK|M_ZERO);
765
766         DPRINT3(DB_INFO, "dgm%d: enable %d ports\n", sc->unit, sc->numports);
767         for (i = 0; i < sc->numports; i++) {
768                 sc->ports[i].enabled = 1;
769                 callout_init(&sc->ports[i].hc_timeout);
770                 callout_init(&sc->ports[i].wf_timeout);
771         }
772
773         /* We should now init per-port structures */
774         setwin(sc, 0);
775         bc = (volatile struct board_chan *)(mem + CHANSTRUCT);
776         sc->mailbox = (volatile struct global_data *)(mem + FEP_GLOBAL);
777
778         if (sc->numports < 3)
779                 shrinkmem = 1;
780         else
781                 shrinkmem = 0;
782
783         dev_ops_add(&dgm_ops, DGM_UNITMASK, DGM_UNIT(sc->unit));
784         for (i = 0; i < sc->numports; i++, bc++) {
785                 DPRINT3(DB_INFO, "dgm%d: Set up port %d\n", sc->unit, i);
786                 port = &sc->ports[i];
787                 port->sc = sc;
788
789                 port->tty = &sc->ttys[i];
790
791                 port->brdchan = bc;
792
793                 port->dcd = CD;
794                 port->dsr = DSR;
795                 port->pnum = i;
796
797                 DPRINT3(DB_INFO, "dgm%d port %d: shrinkmem ?\n", sc->unit, i);
798                 if (shrinkmem) {
799                         DPRINT2(DB_INFO, "dgm%d: shrinking memory\n", sc->unit);
800                         fepcmd(port, SETBUFFER, 32, 0, 0, 0);
801                         shrinkmem = 0;
802                 }
803
804                 DPRINT3(DB_INFO, "dgm%d port %d: assign ptrs\n", sc->unit, i);
805                 port->txptr = mem + ((bc->tseg << 4) & 0x7FFF);
806                 port->rxptr = mem + ((bc->rseg << 4) & 0x7FFF);
807                 port->txwin = FEPWIN | (bc->tseg >> 11);
808                 port->rxwin = FEPWIN | (bc->rseg >> 11);
809
810                 port->txbufhead = 0;
811                 port->rxbufhead = 0;
812                 port->txbufsize = bc->tmax + 1;
813                 port->rxbufsize = bc->rmax + 1;
814
815                 lowwater = (port->txbufsize >= 2000) ?
816                     1024 : (port->txbufsize / 2);
817
818                 setwin(sc, 0);
819                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd STXLWATER %d\n",
820                     sc->unit, i, lowwater);
821                 fepcmd(port, STXLWATER, lowwater, 0, 10, 0);
822                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd SRXLWATER %d\n",
823                     sc->unit, i, port->rxbufsize / 4);
824                 fepcmd(port, SRXLWATER, port->rxbufsize / 4, 0, 10, 0);
825                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd SRXHWATER %d\n",
826                     sc->unit, i, 3 * port->rxbufsize / 4);
827                 fepcmd(port, SRXHWATER, 3 * port->rxbufsize / 4, 0, 10, 0);
828
829                 bc->edelay = 100;
830                 bc->idata = 1;
831
832                 port->startc = bc->startc;
833                 port->startca = bc->startca;
834                 port->stopc = bc->stopc;
835                 port->stopca = bc->stopca;
836
837                 /* port->close_delay = 50; */
838                 port->close_delay = 3 * hz;
839                 port->do_timestamp = 0;
840                 port->do_dcd_timestamp = 0;
841
842                 DPRINT3(DB_INFO, "dgm%d port %d: setup flags\n", sc->unit, i);
843                 /*
844                  * We don't use all the flags from <sys/ttydefaults.h> since
845                  * they are only relevant for logins.  It's important to have
846                  * echo off initially so that the line doesn't start
847                  * blathering before the echo flag can be turned off.
848                  */
849                 port->it_in.c_iflag = TTYDEF_IFLAG;
850                 port->it_in.c_oflag = TTYDEF_OFLAG;
851                 port->it_in.c_cflag = TTYDEF_CFLAG;
852                 port->it_in.c_lflag = TTYDEF_LFLAG;
853                 termioschars(&port->it_in);
854                 port->it_in.c_ispeed = port->it_in.c_ospeed = dgmdefaultrate;
855                 port->it_out = port->it_in;
856
857                 DPRINT3(DB_INFO, "dgm%d port %d: make devices\n", sc->unit, i);
858                 make_dev(&dgm_ops, (sc->unit*65536) + i, UID_ROOT,
859                     GID_WHEEL, 0600, "ttyM%d%x", sc->unit, i + 0xa0);
860                 make_dev(&dgm_ops, sc->unit * 65536 + i + 64, UID_ROOT,
861                     GID_WHEEL, 0600, "ttyiM%d%x", sc->unit, i + 0xa0);
862                 make_dev(&dgm_ops, sc->unit * 65536 + i + 128, UID_ROOT,
863                     GID_WHEEL, 0600, "ttylM%d%x", sc->unit, i + 0xa0);
864                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262144, UID_UUCP,
865                     GID_DIALER, 0660, "cuaM%d%x", sc->unit, i + 0xa0);
866                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262208, UID_UUCP,
867                     GID_DIALER, 0660, "cuaiM%d%x", sc->unit, i + 0xa0);
868                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262272, UID_UUCP,
869                     GID_DIALER, 0660, "cualM%d%x", sc->unit, i + 0xa0);
870         }
871
872         DPRINT3(DB_INFO, "dgm%d: %d device nodes created\n", sc->unit, sc->numports);
873
874         hidewin(sc);
875
876         /* start the polling function */
877         callout_reset(&sc->toh, hz / POLLSPERSEC,
878                         dgmpoll, (void *)(int)sc->unit);
879
880         DPRINT2(DB_INFO, "dgm%d: poll thread started\n", sc->unit);
881
882         return (0);
883 }
884
885 static int
886 dgmdetach(device_t dev)
887 {
888         struct dgm_softc *sc = device_get_softc(dev);
889         int i;
890
891         for (i = 0; i < sc->numports; i++)
892                 if (sc->ttys[i].t_state & TS_ISOPEN)
893                         return (EBUSY);
894
895         DPRINT2(DB_INFO, "dgm%d: detach\n", sc->unit);
896
897         /*
898          * The dev_ops_remove() call will destroy all associated devices
899          * and dereference any ad-hoc-created devices, but does not
900          * dereference devices created via make_dev().
901          */
902         dev_ops_remove(&dgm_ops, DGM_UNITMASK, DGM_UNIT(sc->unit));
903
904         callout_stop(&sc->toh);
905
906         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
907         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
908
909         FREE(sc->ports, M_TTYS);
910         FREE(sc->ttys, M_TTYS);
911
912         return (0);
913 }
914
915 int
916 dgmshutdown(device_t dev)
917 {
918 #ifdef DEBUG
919         struct dgm_softc *sc = device_get_softc(dev);
920
921         DPRINT2(DB_INFO, "dgm%d: shutdown\n", sc->unit);
922 #endif
923
924         return 0;
925 }
926
927 /* ARGSUSED */
928 static int
929 dgmopen(struct dev_open_args *ap)
930 {
931         cdev_t dev = ap->a_head.a_dev;
932         struct dgm_softc *sc;
933         struct tty *tp;
934         int unit;
935         int mynor;
936         int pnum;
937         struct dgm_p *port;
938         int error;
939         volatile struct board_chan *bc;
940
941         error = 0;
942         mynor = minor(dev);
943         unit = MINOR_TO_UNIT(mynor);
944         pnum = MINOR_TO_PORT(mynor);
945
946         sc = devclass_get_softc(dgmdevclass, unit);
947         if (sc == NULL) {
948                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a nonexisting card\n",
949                     unit);
950                 return ENXIO;
951         }
952
953         DPRINT2(DB_INFO, "dgm%d: open\n", sc->unit);
954
955         if (!sc->enabled) {
956                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a disabled card\n",
957                     unit);
958                 return ENXIO;
959         }
960
961         if (pnum >= sc->numports) {
962                 DPRINT3(DB_EXCEPT, "dgm%d: try to open non-existing port %d\n",
963                     unit, pnum);
964                 return ENXIO;
965         }
966
967         if (mynor & CONTROL_MASK)
968                 return 0;
969
970         tp = &sc->ttys[pnum];
971         dev->si_tty = tp;
972         port = &sc->ports[pnum];
973         bc = port->brdchan;
974
975 open_top:
976         crit_enter();
977
978         while (port->closing) {
979                 error = tsleep(&port->closing, PCATCH, "dgocl", 0);
980
981                 if (error) {
982                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgocl)"
983                             " error = %d\n", unit, pnum, error);
984                         goto out;
985                 }
986         }
987
988         if (tp->t_state & TS_ISOPEN) {
989                 /*
990                  * The device is open, so everything has been initialized.
991                  * Handle conflicts.
992                  */
993                 if (mynor & CALLOUT_MASK) {
994                         if (!port->active_out) {
995                                 error = EBUSY;
996                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
997                                     " BUSY error = %d\n", unit, pnum, error);
998                                 goto out;
999                         }
1000                 } else if (port->active_out) {
1001                         if (ap->a_oflags & O_NONBLOCK) {
1002                                 error = EBUSY;
1003                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
1004                                     " BUSY error = %d\n", unit, pnum, error);
1005                                 goto out;
1006                         }
1007                         error = tsleep(&port->active_out, PCATCH, "dgmi", 0);
1008                         if (error != 0) {
1009                                 DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgmi)"
1010                                     " error = %d\n", unit, pnum, error);
1011                                 goto out;
1012                         }
1013                         crit_exit();
1014                         goto open_top;
1015                 }
1016                 if (tp->t_state & TS_XCLUDE && suser_cred(ap->a_cred, 0)) {
1017                         error = EBUSY;
1018                         goto out;
1019                 }
1020         } else {
1021                 /*
1022                  * The device isn't open, so there are no conflicts.
1023                  * Initialize it.  Initialization is done twice in many
1024                  * cases: to preempt sleeping callin opens if we are
1025                  * callout, and to complete a callin open after DCD rises.
1026                  */
1027                 tp->t_oproc = dgmstart;
1028                 tp->t_param = dgmparam;
1029                 tp->t_stop = dgmstop;
1030                 tp->t_dev = dev;
1031                 tp->t_termios= (mynor & CALLOUT_MASK) ?
1032                                                         port->it_out :
1033                                                         port->it_in;
1034
1035                 crit_enter();
1036                 setwin(sc, 0);
1037                 port->imodem = bc->mstat;
1038                 bc->rout = bc->rin; /* clear input queue */
1039                 bc->idata = 1;
1040 #ifdef PRINT_BUFSIZE
1041                 kprintf("dgm buffers tx = %x:%x rx = %x:%x\n",
1042                     bc->tseg, bc->tmax, bc->rseg, bc->rmax);
1043 #endif
1044
1045                 hidewin(sc);
1046                 crit_exit();
1047
1048                 port->wopeners++;
1049                 error = dgmparam(tp, &tp->t_termios);
1050                 port->wopeners--;
1051
1052                 if (error != 0) {
1053                         DPRINT4(DB_OPEN, "dgm%d: port%d: dgmparam error = %d\n",
1054                             unit, pnum, error);
1055                         goto out;
1056                 }
1057
1058                 /* handle fake DCD for callout devices */
1059                 /* and initial DCD */
1060
1061                 if ((port->imodem & port->dcd) || mynor & CALLOUT_MASK)
1062                         linesw[tp->t_line].l_modem(tp, 1);
1063         }
1064
1065         /*
1066          * Wait for DCD if necessary.
1067          */
1068         if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
1069             && !(tp->t_cflag & CLOCAL) && !(ap->a_oflags & O_NONBLOCK)) {
1070                 ++port->wopeners;
1071                 error = tsleep(TSA_CARR_ON(tp), PCATCH, "dgdcd", 0);
1072                 --port->wopeners;
1073                 if (error != 0) {
1074                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgdcd)"
1075                             " error = %d\n", unit, pnum, error);
1076                         goto out;
1077                 }
1078                 crit_exit();
1079                 goto open_top;
1080         }
1081         error = linesw[tp->t_line].l_open(dev, tp);
1082         disc_optim(tp, &tp->t_termios);
1083         DPRINT4(DB_OPEN, "dgm%d: port%d: l_open error = %d\n",
1084             unit, pnum, error);
1085
1086         if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
1087                 port->active_out = 1;
1088
1089         port->used = 1;
1090
1091         /* If any port is open (i.e. the open() call is completed for it)
1092          * the device is busy
1093          */
1094
1095 out:
1096         disc_optim(tp, &tp->t_termios);
1097         crit_exit();
1098
1099         if (!(tp->t_state & TS_ISOPEN) && port->wopeners == 0)
1100                 dgmhardclose(port);
1101
1102         DPRINT4(DB_OPEN, "dgm%d: port%d: open() returns %d\n",
1103             unit, pnum, error);
1104
1105         return error;
1106 }
1107
1108 /*ARGSUSED*/
1109 static int
1110 dgmclose(struct dev_close_args *ap)
1111 {
1112         cdev_t dev = ap->a_head.a_dev;
1113         int             mynor;
1114         struct tty      *tp;
1115         int unit, pnum;
1116         struct dgm_softc *sc;
1117         struct dgm_p *port;
1118         int i;
1119
1120         mynor = minor(dev);
1121         if (mynor & CONTROL_MASK)
1122                 return 0;
1123         unit = MINOR_TO_UNIT(mynor);
1124         pnum = MINOR_TO_PORT(mynor);
1125
1126         sc = devclass_get_softc(dgmdevclass, unit);
1127         tp = &sc->ttys[pnum];
1128         port = sc->ports + pnum;
1129
1130         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing\n", unit, pnum);
1131
1132         DPRINT3(DB_CLOSE, "dgm%d: port%d: draining port\n", unit, pnum);
1133         dgm_drain_or_flush(port);
1134
1135         crit_enter();
1136
1137         port->closing = 1;
1138         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing line disc\n", unit, pnum);
1139         linesw[tp->t_line].l_close(tp, ap->a_fflag);
1140         disc_optim(tp, &tp->t_termios);
1141
1142         DPRINT3(DB_CLOSE, "dgm%d: port%d: hard closing\n", unit, pnum);
1143         dgmhardclose(port);
1144         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing tty\n", unit, pnum);
1145         ttyclose(tp);
1146         port->closing = 0;
1147         wakeup(&port->closing);
1148         port->used = 0;
1149
1150         /* mark the card idle when all ports are closed */
1151
1152         for (i = 0; i < sc->numports; i++)
1153                 if (sc->ports[i].used)
1154                         break;
1155
1156         crit_exit();
1157
1158         DPRINT3(DB_CLOSE, "dgm%d: port%d: closed\n", unit, pnum);
1159
1160         wakeup(TSA_CARR_ON(tp));
1161         wakeup(&port->active_out);
1162         port->active_out = 0;
1163
1164         DPRINT3(DB_CLOSE, "dgm%d: port%d: close exit\n", unit, pnum);
1165
1166         return 0;
1167 }
1168
1169 static void
1170 dgmhardclose(struct dgm_p *port)
1171 {
1172         volatile struct board_chan *bc = port->brdchan;
1173         struct dgm_softc *sc;
1174
1175         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1176         DPRINT2(DB_INFO, "dgm%d: dgmhardclose\n", sc->unit);
1177         crit_enter();
1178         port->do_timestamp = 0;
1179         setwin(sc, 0);
1180
1181         bc->idata = 0;
1182         bc->iempty = 0;
1183         bc->ilow = 0;
1184         if (port->tty->t_cflag & HUPCL) {
1185                 port->omodem &= ~(RTS|DTR);
1186                 fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1);
1187         }
1188
1189         hidewin(sc);
1190         crit_exit();
1191
1192         callout_reset(&port->hc_timeout, hz / 2, dgm_pause, &port->brdchan);
1193         tsleep(&port->brdchan, PCATCH, "dgclo", 0);
1194 }
1195
1196 static void
1197 dgm_pause(void *chan)
1198 {
1199         wakeup((caddr_t)chan);
1200 }
1201
1202 static void
1203 dgmpoll(void *unit_c)
1204 {
1205         int unit = (int)unit_c;
1206         int pnum;
1207         struct dgm_p *port;
1208         struct dgm_softc *sc;
1209         int head, tail;
1210         u_char *eventbuf;
1211         int event, mstat, lstat;
1212         volatile struct board_chan *bc;
1213         struct tty *tp;
1214         int rhead, rtail;
1215         int whead, wtail;
1216         int size;
1217         u_char *ptr;
1218         int ocount;
1219         int ibuf_full, obuf_full;
1220         BoardMemWinState ws = bmws_get();
1221
1222         sc = devclass_get_softc(dgmdevclass, unit);
1223         DPRINT2(DB_INFO, "dgm%d: poll\n", sc->unit);
1224
1225         if (!sc->enabled) {
1226                 kprintf("dgm%d: polling of disabled board stopped\n", unit);
1227                 return;
1228         }
1229
1230         setwin(sc, 0);
1231
1232         head = sc->mailbox->ein;
1233         tail = sc->mailbox->eout;
1234
1235         while (head != tail) {
1236                 if (head >= FEP_IMAX - FEP_ISTART
1237                 || tail >= FEP_IMAX - FEP_ISTART
1238                 || (head|tail) & 03 ) {
1239                         kprintf("dgm%d: event queue's head or tail is wrong!"
1240                             " hd = %d, tl = %d\n", unit, head, tail);
1241                         break;
1242                 }
1243
1244                 eventbuf = sc->vmem + tail + FEP_ISTART;
1245                 pnum = eventbuf[0];
1246                 event = eventbuf[1];
1247                 mstat = eventbuf[2];
1248                 lstat = eventbuf[3];
1249
1250                 port = &sc->ports[pnum];
1251                 bc = port->brdchan;
1252                 tp = &sc->ttys[pnum];
1253
1254                 if (pnum >= sc->numports || !port->enabled) {
1255                         kprintf("dgm%d: port%d: got event on nonexisting port\n",
1256                             unit, pnum);
1257                 } else if (port->used || port->wopeners > 0 ) {
1258
1259                         int wrapmask = port->rxbufsize - 1;
1260
1261                         if (!(event & ALL_IND))
1262                                 kprintf("dgm%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
1263                                         unit, pnum, event, mstat, lstat);
1264
1265                         if (event & DATA_IND) {
1266                                 DPRINT3(DB_DATA, "dgm%d: port%d: DATA_IND\n",
1267                                     unit, pnum);
1268
1269                                 rhead = bc->rin & wrapmask;
1270                                 rtail = bc->rout & wrapmask;
1271
1272                                 if (!(tp->t_cflag & CREAD) || !port->used ) {
1273                                         bc->rout = rhead;
1274                                         goto end_of_data;
1275                                 }
1276
1277                                 if (bc->orun) {
1278                                         kprintf("dgm%d: port%d: overrun\n", unit, pnum);
1279                                         bc->orun = 0;
1280                                 }
1281
1282                                 if (!(tp->t_state & TS_ISOPEN))
1283                                         goto end_of_data;
1284
1285                                 for (ibuf_full = FALSE; rhead != rtail && !ibuf_full;) {
1286                                         DPRINT5(DB_RXDATA, "dgm%d: port%d:"
1287                                             " p rx head = %d tail = %d\n", unit,
1288                                             pnum, rhead, rtail);
1289
1290                                         if (rhead > rtail)
1291                                                 size = rhead - rtail;
1292                                         else
1293                                                 size = port->rxbufsize - rtail;
1294
1295                                         ptr = port->rxptr + rtail;
1296
1297 /* Helg: */
1298                                         if (tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) {
1299                                                 size = DGB_IBUFSIZE - tp->t_rawq.c_cc;
1300                                                 DPRINT1(DB_RXDATA, "*");
1301                                                 ibuf_full = TRUE;
1302                                         }
1303
1304                                         if (size) {
1305                                                 if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1306                                                         DPRINT1(DB_RXDATA, "!");
1307                                                         towin(sc, port->rxwin);
1308                                                         tk_nin += size;
1309                                                         tk_rawcc += size;
1310                                                         tp->t_rawcc += size;
1311                                                         b_to_q(ptr, size,
1312                                                             &tp->t_rawq);
1313                                                         setwin(sc, 0);
1314                                                 } else {
1315                                                         int i = size;
1316                                                         unsigned char chr;
1317                                                         do {
1318                                                                 towin(sc, port->rxwin);
1319                                                                 chr = *ptr++;
1320                                                                 hidewin(sc);
1321                                                                (*linesw[tp->t_line].l_rint)(chr, tp);
1322                                                         } while (--i > 0 );
1323                                                         setwin(sc, 0);
1324                                                 }
1325                                         }
1326                                         rtail= (rtail + size) & wrapmask;
1327                                         bc->rout = rtail;
1328                                         rhead = bc->rin & wrapmask;
1329                                         hidewin(sc);
1330                                         ttwakeup(tp);
1331                                         setwin(sc, 0);
1332                                 }
1333                         end_of_data: ;
1334                         }
1335
1336                         if (event & MODEMCHG_IND) {
1337                                 DPRINT3(DB_MODEM, "dgm%d: port%d: "
1338                                     "MODEMCHG_IND\n", unit, pnum);
1339                                 port->imodem = mstat;
1340                                 if (mstat & port->dcd) {
1341                                         hidewin(sc);
1342                                         linesw[tp->t_line].l_modem(tp, 1);
1343                                         setwin(sc, 0);
1344                                         wakeup(TSA_CARR_ON(tp));
1345                                 } else {
1346                                         hidewin(sc);
1347                                         linesw[tp->t_line].l_modem(tp, 0);
1348                                         setwin(sc, 0);
1349                                         if (port->draining) {
1350                                                 port->draining = 0;
1351                                                 wakeup(&port->draining);
1352                                         }
1353                                 }
1354                         }
1355
1356                         if (event & BREAK_IND) {
1357                                 if ((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK))        {
1358                                         DPRINT3(DB_BREAK, "dgm%d: port%d:"
1359                                             " BREAK_IND\n", unit, pnum);
1360                                         hidewin(sc);
1361                                         linesw[tp->t_line].l_rint(TTY_BI, tp);
1362                                         setwin(sc, 0);
1363                                 }
1364                         }
1365
1366 /* Helg: with output flow control */
1367
1368                         if (event & (LOWTX_IND | EMPTYTX_IND) ) {
1369                                 DPRINT3(DB_TXDATA, "dgm%d: port%d:"
1370                                     " LOWTX_IND or EMPTYTX_IND\n", unit, pnum);
1371
1372                                 if ((event & EMPTYTX_IND ) &&
1373                                     tp->t_outq.c_cc == 0 && port->draining) {
1374                                         port->draining = 0;
1375                                         wakeup(&port->draining);
1376                                         bc->ilow = 0;
1377                                         bc->iempty = 0;
1378                                 } else {
1379
1380                                         int wrapmask = port->txbufsize - 1;
1381
1382                                         for (obuf_full = FALSE;
1383                                             tp->t_outq.c_cc != 0 && !obuf_full;
1384                                             ) {
1385                                                 /* add "last-minute" data to write buffer */
1386                                                 if (!(tp->t_state & TS_BUSY)) {
1387                                                         hidewin(sc);
1388 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1389                                                         ttwwakeup(tp);
1390 #else
1391                                                         if (tp->t_outq.c_cc <= tp->t_lowat) {
1392                                                                 if (tp->t_state & TS_ASLEEP) {
1393                                                                         tp->t_state &= ~TS_ASLEEP;
1394                                                                         wakeup(TSA_OLOWAT(tp));
1395                                                                 }
1396                                                                 /* selwakeup(&tp->t_wsel); */
1397                                                         }
1398 #endif
1399                                                         setwin(sc, 0);
1400                                                 }
1401                                                 crit_enter();
1402
1403                                         whead = bc->tin & wrapmask;
1404                                         wtail = bc->tout & wrapmask;
1405
1406                                         if (whead < wtail)
1407                                                 size = wtail - whead - 1;
1408                                         else {
1409                                                 size = port->txbufsize - whead;
1410                                                 if (wtail == 0)
1411                                                         size--;
1412                                         }
1413
1414                                         if (size == 0) {
1415                                                 DPRINT5(DB_WR, "dgm: head = %d tail = %d size = %d full = %d\n",
1416                                                         whead, wtail, size, obuf_full);
1417                                                 bc->iempty = 1;
1418                                                 bc->ilow = 1;
1419                                                 obuf_full = TRUE;
1420                                                 crit_exit();
1421                                                 break;
1422                                         }
1423
1424                                         towin(sc, port->txwin);
1425
1426                                         ocount = q_to_b(&tp->t_outq, port->txptr + whead, size);
1427                                         whead += ocount;
1428
1429                                         setwin(sc, 0);
1430                                         bc->tin = whead;
1431                                         bc->tin = whead & wrapmask;
1432                                         crit_exit();
1433                                 }
1434
1435                                 if (obuf_full) {
1436                                         DPRINT1(DB_WR, " +BUSY\n");
1437                                         tp->t_state |= TS_BUSY;
1438                                 } else {
1439                                         DPRINT1(DB_WR, " -BUSY\n");
1440                                         hidewin(sc);
1441 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1442                                         /* should clear TS_BUSY before ttwwakeup */
1443                                         if (tp->t_state & TS_BUSY)      {
1444                                                 tp->t_state &= ~TS_BUSY;
1445                                                 linesw[tp->t_line].l_start(tp);
1446                                                 ttwwakeup(tp);
1447                                         }
1448 #else
1449                                 if (tp->t_state & TS_ASLEEP) {
1450                                         tp->t_state &= ~TS_ASLEEP;
1451                                         wakeup(TSA_OLOWAT(tp));
1452                                 }
1453                                 tp->t_state &= ~TS_BUSY;
1454 #endif
1455                                         setwin(sc, 0);
1456                                         }
1457                                 }
1458                         }
1459                         bc->idata = 1;   /* require event on incoming data */
1460
1461                 } else {
1462                         bc = port->brdchan;
1463                         DPRINT4(DB_EXCEPT, "dgm%d: port%d: got event 0x%x on closed port\n",
1464                                 unit, pnum, event);
1465                         bc->rout = bc->rin;
1466                         bc->idata = bc->iempty = bc->ilow = 0;
1467                 }
1468
1469                 tail = (tail + 4) & (FEP_IMAX - FEP_ISTART - 4);
1470         }
1471
1472         sc->mailbox->eout = tail;
1473         bmws_set(ws);
1474
1475         callout_reset(&sc->toh, hz / POLLSPERSEC, dgmpoll, unit_c);
1476
1477         DPRINT2(DB_INFO, "dgm%d: poll done\n", sc->unit);
1478 }
1479
1480 static int
1481 dgmioctl(struct dev_ioctl_args *ap)
1482 {
1483         cdev_t dev = ap->a_head.a_dev;
1484         u_long cmd = ap->a_cmd;
1485         caddr_t data = ap->a_data;
1486         struct dgm_softc *sc;
1487         int unit, pnum;
1488         struct dgm_p *port;
1489         int mynor;
1490         struct tty *tp;
1491         volatile struct board_chan *bc;
1492         int error;
1493         int tiocm_xxx;
1494
1495 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1496         u_long          oldcmd;
1497         struct termios  term;
1498 #endif
1499
1500         BoardMemWinState ws = bmws_get();
1501
1502         mynor = minor(dev);
1503         unit = MINOR_TO_UNIT(mynor);
1504         pnum = MINOR_TO_PORT(mynor);
1505
1506         sc = devclass_get_softc(dgmdevclass, unit);
1507         port = &sc->ports[pnum];
1508         tp = &sc->ttys[pnum];
1509         bc = port->brdchan;
1510
1511         if (mynor & CONTROL_MASK) {
1512                 struct termios *ct;
1513
1514                 switch (mynor & CONTROL_MASK) {
1515                 case CONTROL_INIT_STATE:
1516                         ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in;
1517                         break;
1518                 case CONTROL_LOCK_STATE:
1519                         ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
1520                         break;
1521                 default:
1522                         return (ENODEV);        /* /dev/nodev */
1523                 }
1524                 switch (cmd) {
1525                 case TIOCSETA:
1526                         error = suser_cred(ap->a_cred, 0);
1527                         if (error != 0)
1528                                 return (error);
1529                         *ct = *(struct termios *)data;
1530                         return (0);
1531                 case TIOCGETA:
1532                         *(struct termios *)data = *ct;
1533                         return (0);
1534                 case TIOCGETD:
1535                         *(int *)data = TTYDISC;
1536                         return (0);
1537                 case TIOCGWINSZ:
1538                         bzero(data, sizeof(struct winsize));
1539                         return (0);
1540                 default:
1541                         return (ENOTTY);
1542                 }
1543         }
1544
1545 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1546         term = tp->t_termios;
1547         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1548           DPRINT6(DB_PARAM, "dgm%d: port%d: dgmioctl-ISNOW c = 0x%x i = 0x%x l = 0x%x\n", unit, pnum, term.c_cflag, term.c_iflag, term.c_lflag);
1549         }
1550         oldcmd = cmd;
1551         error = ttsetcompat(tp, &cmd, data, &term);
1552         if (error != 0)
1553                 return (error);
1554         if (cmd != oldcmd)
1555                 data = (caddr_t)&term;
1556 #endif
1557
1558         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1559                 int     cc;
1560                 struct termios *dt = (struct termios *)data;
1561                 struct termios *lt = mynor & CALLOUT_MASK
1562                                      ? &port->lt_out : &port->lt_in;
1563
1564                 DPRINT6(DB_PARAM, "dgm%d: port%d: dgmioctl-TOSET c = 0x%x i = 0x%x l = 0x%x\n", unit, pnum, dt->c_cflag, dt->c_iflag, dt->c_lflag);
1565                 dt->c_iflag = (tp->t_iflag & lt->c_iflag)
1566                               | (dt->c_iflag & ~lt->c_iflag);
1567                 dt->c_oflag = (tp->t_oflag & lt->c_oflag)
1568                               | (dt->c_oflag & ~lt->c_oflag);
1569                 dt->c_cflag = (tp->t_cflag & lt->c_cflag)
1570                               | (dt->c_cflag & ~lt->c_cflag);
1571                 dt->c_lflag = (tp->t_lflag & lt->c_lflag)
1572                               | (dt->c_lflag & ~lt->c_lflag);
1573                 for (cc = 0; cc < NCCS; ++cc)
1574                         if (lt->c_cc[cc] != 0)
1575                                 dt->c_cc[cc] = tp->t_cc[cc];
1576                 if (lt->c_ispeed != 0)
1577                         dt->c_ispeed = tp->t_ispeed;
1578                 if (lt->c_ospeed != 0)
1579                         dt->c_ospeed = tp->t_ospeed;
1580         }
1581
1582         if (cmd == TIOCSTOP) {
1583                 crit_enter();
1584                 setwin(sc, 0);
1585                 fepcmd(port, PAUSETX, 0, 0, 0, 0);
1586                 bmws_set(ws);
1587                 crit_exit();
1588                 return 0;
1589         } else if (cmd == TIOCSTART) {
1590                 crit_enter();
1591                 setwin(sc, 0);
1592                 fepcmd(port, RESUMETX, 0, 0, 0, 0);
1593                 bmws_set(ws);
1594                 crit_exit();
1595                 return 0;
1596         }
1597
1598         if (cmd == TIOCSETAW || cmd == TIOCSETAF)
1599                 port->mustdrain = 1;
1600
1601         error = linesw[tp->t_line].l_ioctl(tp, cmd, data,
1602                                            ap->a_fflag, ap->a_cred);
1603         if (error != ENOIOCTL)
1604                 return error;
1605         crit_enter();
1606         error = ttioctl(tp, cmd, data, ap->a_fflag);
1607         disc_optim(tp, &tp->t_termios);
1608         port->mustdrain = 0;
1609         if (error != ENOIOCTL) {
1610                 crit_exit();
1611                 if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1612                         DPRINT6(DB_PARAM, "dgm%d: port%d: dgmioctl-RES c = 0x%x i = 0x%x l = 0x%x\n", unit, pnum, tp->t_cflag, tp->t_iflag, tp->t_lflag);
1613                 }
1614                 return error;
1615         }
1616
1617         switch (cmd) {
1618         case TIOCSBRK:
1619 #if 0
1620                 error = dgmdrain(port);
1621
1622                 if (error != 0) {
1623                         crit_exit();
1624                         return error;
1625                 }
1626 #endif
1627
1628                 crit_enter();
1629                 setwin(sc, 0);
1630
1631                 /* now it sends 400 millisecond break because I don't know */
1632                 /* how to send an infinite break */
1633
1634                 fepcmd(port, SENDBREAK, 400, 0, 10, 0);
1635                 hidewin(sc);
1636                 crit_exit();
1637                 break;
1638         case TIOCCBRK:
1639                 /* now it's empty */
1640                 break;
1641         case TIOCSDTR:
1642                 DPRINT3(DB_MODEM, "dgm%d: port%d: set DTR\n", unit, pnum);
1643                 port->omodem |= DTR;
1644                 crit_enter();
1645                 setwin(sc, 0);
1646                 fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1);
1647
1648                 if (!(bc->mstat & DTR))
1649                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is off\n", unit, pnum);
1650
1651                 hidewin(sc);
1652                 crit_exit();
1653                 break;
1654         case TIOCCDTR:
1655                 DPRINT3(DB_MODEM, "dgm%d: port%d: reset DTR\n", unit, pnum);
1656                 port->omodem &= ~DTR;
1657                 crit_enter();
1658                 setwin(sc, 0);
1659                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1660
1661                 if (bc->mstat & DTR) {
1662                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is on\n", unit, pnum);
1663                 }
1664
1665                 hidewin(sc);
1666                 crit_exit();
1667                 break;
1668         case TIOCMSET:
1669                 if (*(int *)data & TIOCM_DTR)
1670                         port->omodem |= DTR;
1671                 else
1672                         port->omodem &= ~DTR;
1673
1674                 if (*(int *)data & TIOCM_RTS)
1675                         port->omodem |= RTS;
1676                 else
1677                         port->omodem &= ~RTS;
1678
1679                 crit_enter();
1680                 setwin(sc, 0);
1681                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1682                 hidewin(sc);
1683                 crit_exit();
1684                 break;
1685         case TIOCMBIS:
1686                 if (*(int *)data & TIOCM_DTR)
1687                         port->omodem |= DTR;
1688
1689                 if (*(int *)data & TIOCM_RTS)
1690                         port->omodem |= RTS;
1691
1692                 crit_enter();
1693                 setwin(sc, 0);
1694                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1695                 hidewin(sc);
1696                 crit_exit();
1697                 break;
1698         case TIOCMBIC:
1699                 if (*(int *)data & TIOCM_DTR)
1700                         port->omodem &= ~DTR;
1701
1702                 if (*(int *)data & TIOCM_RTS)
1703                         port->omodem &= ~RTS;
1704
1705                 crit_enter();
1706                 setwin(sc, 0);
1707                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1708                 hidewin(sc);
1709                 crit_exit();
1710                 break;
1711         case TIOCMGET:
1712                 setwin(sc, 0);
1713                 port->imodem = bc->mstat;
1714                 hidewin(sc);
1715
1716                 tiocm_xxx = TIOCM_LE;   /* XXX - always enabled while open */
1717
1718                 DPRINT3(DB_MODEM, "dgm%d: port%d: modem stat -- ", unit, pnum);
1719
1720                 if (port->imodem & DTR) {
1721                         DPRINT1(DB_MODEM, "DTR ");
1722                         tiocm_xxx |= TIOCM_DTR;
1723                 }
1724                 if (port->imodem & RTS) {
1725                         DPRINT1(DB_MODEM, "RTS ");
1726                         tiocm_xxx |= TIOCM_RTS;
1727                 }
1728                 if (port->imodem & CTS) {
1729                         DPRINT1(DB_MODEM, "CTS ");
1730                         tiocm_xxx |= TIOCM_CTS;
1731                 }
1732                 if (port->imodem & port->dcd) {
1733                         DPRINT1(DB_MODEM, "DCD ");
1734                         tiocm_xxx |= TIOCM_CD;
1735                 }
1736                 if (port->imodem & port->dsr) {
1737                         DPRINT1(DB_MODEM, "DSR ");
1738                         tiocm_xxx |= TIOCM_DSR;
1739                 }
1740                 if (port->imodem & RI) {
1741                         DPRINT1(DB_MODEM, "RI ");
1742                         tiocm_xxx |= TIOCM_RI;
1743                 }
1744                 *(int *)data = tiocm_xxx;
1745                 DPRINT1(DB_MODEM, "--\n");
1746                 break;
1747         case TIOCMSDTRWAIT:
1748                 /* must be root since the wait applies to following logins */
1749                 error = suser_cred(ap->a_cred, 0);
1750                 if (error != 0) {
1751                         crit_exit();
1752                         return (error);
1753                 }
1754                 port->close_delay = *(int *)data * hz / 100;
1755                 break;
1756         case TIOCMGDTRWAIT:
1757                 *(int *)data = port->close_delay * 100 / hz;
1758                 break;
1759         case TIOCTIMESTAMP:
1760                 port->do_timestamp = 1;
1761                 *(struct timeval *)data = port->timestamp;
1762                 break;
1763         case TIOCDCDTIMESTAMP:
1764                 port->do_dcd_timestamp = 1;
1765                 *(struct timeval *)data = port->dcd_timestamp;
1766                 break;
1767         default:
1768                 bmws_set(ws);
1769                 crit_exit();
1770                 return ENOTTY;
1771         }
1772         bmws_set(ws);
1773         crit_exit();
1774
1775         return 0;
1776 }
1777
1778 static void
1779 wakeflush(void *p)
1780 {
1781         struct dgm_p *port = p;
1782
1783         wakeup(&port->draining);
1784 }
1785
1786 /* wait for the output to drain */
1787
1788 static int
1789 dgmdrain(struct dgm_p *port)
1790 {
1791         volatile struct board_chan *bc = port->brdchan;
1792         struct dgm_softc *sc;
1793         int error;
1794         int head, tail;
1795         BoardMemWinState ws = bmws_get();
1796
1797         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1798
1799         setwin(sc, 0);
1800
1801         bc->iempty = 1;
1802         tail = bc->tout;
1803         head = bc->tin;
1804
1805         while (tail != head) {
1806                 DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1807                         port->sc->unit, port->pnum, head, tail);
1808
1809                 hidewin(sc);
1810                 port->draining = 1;
1811                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1812                 error = tsleep(&port->draining, PCATCH, "dgdrn", 0);
1813                 port->draining = 0;
1814                 setwin(sc, 0);
1815
1816                 if (error != 0) {
1817                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgdrn) error = %d\n",
1818                                 port->sc->unit, port->pnum, error);
1819
1820                         bc->iempty = 0;
1821                         bmws_set(ws);
1822                         return error;
1823                 }
1824
1825                 tail = bc->tout;
1826                 head = bc->tin;
1827         }
1828         DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1829                 port->sc->unit, port->pnum, head, tail);
1830         bmws_set(ws);
1831         return 0;
1832 }
1833
1834 /* wait for the output to drain */
1835 /* or simply clear the buffer it it's stopped */
1836
1837 static void
1838 dgm_drain_or_flush(struct dgm_p *port)
1839 {
1840         volatile struct board_chan *bc = port->brdchan;
1841         struct tty *tp = port->tty;
1842         struct dgm_softc *sc;
1843         int error;
1844         int lasttail;
1845         int head, tail;
1846
1847         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1848         setwin(sc, 0);
1849
1850         lasttail = -1;
1851         bc->iempty = 1;
1852         tail = bc->tout;
1853         head = bc->tin;
1854
1855         while (tail != head /* && tail != lasttail */ ) {
1856                 DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1857                         port->sc->unit, port->pnum, head, tail);
1858
1859                 /* if there is no carrier simply clean the buffer */
1860                 if (!(tp->t_state & TS_CARR_ON)) {
1861                         bc->tout = bc->tin = 0;
1862                         bc->iempty = 0;
1863                         hidewin(sc);
1864                         return;
1865                 }
1866
1867                 hidewin(sc);
1868                 port->draining = 1;
1869                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1870                 error = tsleep(&port->draining, PCATCH, "dgfls", 0);
1871                 port->draining = 0;
1872                 setwin(sc, 0);
1873
1874                 if (error != 0) {
1875                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgfls)"
1876                             " error = %d\n", port->sc->unit, port->pnum, error);
1877
1878                         /* silently clean the buffer */
1879
1880                         bc->tout = bc->tin = 0;
1881                         bc->iempty = 0;
1882                         hidewin(sc);
1883                         return;
1884                 }
1885
1886                 lasttail = tail;
1887                 tail = bc->tout;
1888                 head = bc->tin;
1889         }
1890         hidewin(sc);
1891         DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1892                         port->sc->unit, port->pnum, head, tail);
1893 }
1894
1895 static int
1896 dgmparam(struct tty *tp, struct termios *t)
1897 {
1898         int unit = MINOR_TO_UNIT(minor(tp->t_dev));
1899         int pnum = MINOR_TO_PORT(minor(tp->t_dev));
1900         volatile struct board_chan *bc;
1901         struct dgm_softc *sc;
1902         struct dgm_p *port;
1903         int cflag;
1904         int head;
1905         int mval;
1906         int iflag;
1907         int hflow;
1908         BoardMemWinState ws = bmws_get();
1909
1910         sc = devclass_get_softc(dgmdevclass, unit);
1911         port = &sc->ports[pnum];
1912         bc = port->brdchan;
1913
1914         DPRINT6(DB_PARAM, "dgm%d: port%d: dgmparm c = 0x%x i = 0x%x l = 0x%x\n", unit, pnum, t->c_cflag, t->c_iflag, t->c_lflag);
1915
1916         if (port->mustdrain) {
1917                 DPRINT3(DB_PARAM, "dgm%d: port%d: must call dgmdrain()\n", unit, pnum);
1918                 dgmdrain(port);
1919         }
1920
1921         cflag = ttspeedtab(t->c_ospeed, dgmspeedtab);
1922
1923         if (t->c_ispeed == 0)
1924                 t->c_ispeed = t->c_ospeed;
1925
1926         if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) {
1927                 DPRINT4(DB_PARAM, "dgm%d: port%d: invalid cflag = 0%o\n", unit, pnum, cflag);
1928                 return (EINVAL);
1929         }
1930
1931         crit_enter();
1932         setwin(sc, 0);
1933
1934         if (cflag == 0) { /* hangup */
1935                 DPRINT3(DB_PARAM, "dgm%d: port%d: hangup\n", unit, pnum);
1936                 head = bc->rin;
1937                 bc->rout = head;
1938                 head = bc->tin;
1939                 fepcmd(port, STOUT, (unsigned)head, 0, 0, 0);
1940                 mval= port->omodem & ~(DTR|RTS);
1941         } else {
1942                 cflag |= dgmflags(dgm_cflags, t->c_cflag);
1943
1944                 if (cflag != port->fepcflag) {
1945                         port->fepcflag = cflag;
1946                         DPRINT5(DB_PARAM, "dgm%d: port%d: set cflag = 0x%x c = 0x%x\n",
1947                                         unit, pnum, cflag, t->c_cflag&~CRTSCTS);
1948                         fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0);
1949                 }
1950                 mval= port->omodem | (DTR|RTS);
1951         }
1952
1953         iflag = dgmflags(dgm_iflags, t->c_iflag);
1954         if (iflag != port->fepiflag) {
1955                 port->fepiflag = iflag;
1956                 DPRINT5(DB_PARAM, "dgm%d: port%d: set iflag = 0x%x c = 0x%x\n", unit, pnum, iflag, t->c_iflag);
1957                 fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0);
1958         }
1959
1960         bc->mint = port->dcd;
1961
1962         hflow = dgmflags(dgm_flow, t->c_cflag);
1963         if (hflow != port->hflow) {
1964                 port->hflow = hflow;
1965                 DPRINT5(DB_PARAM, "dgm%d: port%d: set hflow = 0x%x f = 0x%x\n", unit, pnum, hflow, t->c_cflag&CRTSCTS);
1966                 fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1);
1967         }
1968
1969         if (port->omodem != mval) {
1970                 DPRINT5(DB_PARAM, "dgm%d: port%d: setting modem parameters 0x%x was 0x%x\n",
1971                         unit, pnum, mval, port->omodem);
1972                 port->omodem = mval;
1973                 fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1);
1974         }
1975
1976         if (port->fepstartc != t->c_cc[VSTART] ||
1977             port->fepstopc != t->c_cc[VSTOP]) {
1978                 DPRINT5(DB_PARAM, "dgm%d: port%d: set startc = %d, stopc = %d\n", unit, pnum, t->c_cc[VSTART], t->c_cc[VSTOP]);
1979                 port->fepstartc = t->c_cc[VSTART];
1980                 port->fepstopc = t->c_cc[VSTOP];
1981                 fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1);
1982         }
1983
1984         bmws_set(ws);
1985         crit_exit();
1986
1987         return 0;
1988
1989 }
1990
1991 static void
1992 dgmstart(struct tty *tp)
1993 {
1994         int unit;
1995         int pnum;
1996         struct dgm_p *port;
1997         struct dgm_softc *sc;
1998         volatile struct board_chan *bc;
1999         int head, tail;
2000         int size, ocount;
2001         int wmask;
2002
2003         BoardMemWinState ws = bmws_get();
2004
2005         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2006         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2007         sc = devclass_get_softc(dgmdevclass, unit);
2008         port = &sc->ports[pnum];
2009         bc = port->brdchan;
2010
2011         wmask = port->txbufsize - 1;
2012
2013         crit_enter();
2014
2015         while (tp->t_outq.c_cc != 0) {
2016 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2017                 ttwwakeup(tp);
2018 #else
2019                 if (tp->t_outq.c_cc <= tp->t_lowat) {
2020                         if (tp->t_state & TS_ASLEEP) {
2021                                 tp->t_state &= ~TS_ASLEEP;
2022                                 wakeup(TSA_OLOWAT(tp));
2023                         }
2024                         /*selwakeup(&tp->t_wsel);*/
2025                 }
2026 #endif
2027                 crit_enter();
2028                 setwin(sc, 0);
2029
2030                 head = bc->tin & wmask;
2031
2032                 do { tail = bc->tout; } while (tail != bc->tout);
2033                 tail = bc->tout & wmask;
2034
2035                 DPRINT5(DB_WR, "dgm%d: port%d: s tx head = %d tail = %d\n", unit, pnum, head, tail);
2036
2037 #ifdef LEAVE_FREE_CHARS
2038                 if (tail > head) {
2039                         size = tail - head - LEAVE_FREE_CHARS;
2040                         if (size < 0)
2041                                 size = 0;
2042                         else {
2043                                 size = port->txbufsize - head;
2044                                 if (tail + port->txbufsize < head)
2045                                         size = 0;
2046                         }
2047                 }
2048 #else
2049                 if (tail > head)
2050                         size = tail - head - 1;
2051                 else {
2052                         size = port->txbufsize - head;
2053                         if (tail == 0)
2054                                 size--;
2055                 }
2056 #endif
2057
2058                 if (size == 0) {
2059                         bc->iempty = 1;
2060                         bc->ilow = 1;
2061                         crit_exit();
2062                         bmws_set(ws);
2063                         tp->t_state |= TS_BUSY;
2064                         crit_exit();
2065                         return;
2066                 }
2067
2068                 towin(sc, port->txwin);
2069
2070                 ocount = q_to_b(&tp->t_outq, port->txptr + head, size);
2071                 head += ocount;
2072                 if (head >= port->txbufsize)
2073                         head -= port->txbufsize;
2074
2075                 setwin(sc, 0);
2076                 bc->tin = head;
2077
2078                 DPRINT5(DB_WR, "dgm%d: port%d: tx avail = %d count = %d\n",
2079                     unit, pnum, size, ocount);
2080                 hidewin(sc);
2081                 crit_exit();
2082         }
2083
2084         bmws_set(ws);
2085         crit_exit();
2086
2087 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2088         if (tp->t_state & TS_BUSY) {
2089                 tp->t_state &= ~TS_BUSY;
2090                 linesw[tp->t_line].l_start(tp);
2091                 ttwwakeup(tp);
2092         }
2093 #else
2094         if (tp->t_state & TS_ASLEEP) {
2095                 tp->t_state &= ~TS_ASLEEP;
2096                 wakeup(TSA_OLOWAT(tp));
2097         }
2098         tp->t_state& = ~TS_BUSY;
2099 #endif
2100 }
2101
2102 void
2103 dgmstop(struct tty *tp, int rw)
2104 {
2105         int unit;
2106         int pnum;
2107         struct dgm_p *port;
2108         struct dgm_softc *sc;
2109         volatile struct board_chan *bc;
2110
2111         BoardMemWinState ws = bmws_get();
2112
2113         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2114         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2115
2116         sc = devclass_get_softc(dgmdevclass, unit);
2117         port = &sc->ports[pnum];
2118         bc = port->brdchan;
2119
2120         DPRINT3(DB_WR, "dgm%d: port%d: stop\n", port->sc->unit, port->pnum);
2121
2122         crit_enter();
2123         setwin(sc, 0);
2124
2125         if (rw & FWRITE) {
2126                 /* clear output queue */
2127                 bc->tout = bc->tin = 0;
2128                 bc->ilow = 0;
2129                 bc->iempty = 0;
2130         }
2131         if (rw & FREAD) {
2132                 /* clear input queue */
2133                 bc->rout = bc->rin;
2134                 bc->idata = 1;
2135         }
2136         hidewin(sc);
2137         bmws_set(ws);
2138         crit_exit();
2139         dgmstart(tp);
2140 }
2141
2142 static void
2143 fepcmd(struct dgm_p *port,
2144         unsigned cmd,
2145         unsigned op1,
2146         unsigned op2,
2147         unsigned ncmds,
2148         unsigned bytecmd)
2149 {
2150         u_char *mem;
2151         unsigned tail, head;
2152         int count, n;
2153
2154         KASSERT(port->sc, ("Couldn't (re)obtain driver softc"));
2155         mem = port->sc->vmem;
2156
2157         if (!port->enabled) {
2158                 kprintf("dgm%d: port%d: FEP command on disabled port\n",
2159                         port->sc->unit, port->pnum);
2160                 return;
2161         }
2162
2163         /* setwin(port->sc, 0); Require this to be set by caller */
2164         head = port->sc->mailbox->cin;
2165
2166         if (head >= FEP_CMAX - FEP_CSTART || (head & 3)) {
2167                 kprintf("dgm%d: port%d: wrong pointer head of command queue : 0x%x\n",
2168                         port->sc->unit, port->pnum, head);
2169                 return;
2170         }
2171
2172         mem[head + FEP_CSTART] = cmd;
2173         mem[head + FEP_CSTART + 1] = port->pnum;
2174         if (bytecmd) {
2175                 mem[head + FEP_CSTART + 2] = op1;
2176                 mem[head + FEP_CSTART + 3] = op2;
2177         } else {
2178                 mem[head + FEP_CSTART + 2] = op1 & 0xff;
2179                 mem[head + FEP_CSTART + 3] = (op1 >> 8) & 0xff;
2180         }
2181
2182         DPRINT7(DB_FEP, "dgm%d: port%d: %s cmd = 0x%x op1 = 0x%x op2 = 0x%x\n", port->sc->unit, port->pnum,
2183                         (bytecmd)?"byte":"word", cmd, mem[head + FEP_CSTART + 2], mem[head + FEP_CSTART + 3]);
2184
2185         head = (head + 4) & (FEP_CMAX - FEP_CSTART - 4);
2186         port->sc->mailbox->cin = head;
2187
2188         count = FEPTIMEOUT;
2189
2190         while (count-- != 0) {
2191                 head = port->sc->mailbox->cin;
2192                 tail = port->sc->mailbox->cout;
2193
2194                 n = (head - tail) & (FEP_CMAX - FEP_CSTART - 4);
2195                 if (n <= ncmds * (sizeof(ushort)*4))
2196                         return;
2197         }
2198         kprintf("dgm%d(%d): timeout on FEP cmd = 0x%x\n", port->sc->unit, port->pnum, cmd);
2199 }
2200
2201 static void
2202 disc_optim(struct tty *tp, struct termios *t)
2203 {
2204         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2205             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2206             && (!(t->c_iflag & PARMRK)
2207                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2208             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2209             && linesw[tp->t_line].l_rint == ttyinput)
2210                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
2211         else
2212                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2213 }