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