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