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