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