02b07e440c2bc884a9cab07fe78663f054d4d233
[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_kqfilter =   ttykqfilter,
252         .d_revoke =     ttyrevoke
253 };
254
255 static int
256 dgmmodhandler(module_t mod, int event, void *arg)
257 {
258         int res = 0;
259
260         switch (event) {
261         case MOD_LOAD:
262                 break;
263         case MOD_UNLOAD:
264                 break;
265         }
266
267         return res;
268 }
269
270 DRIVER_MODULE(dgm, isa, dgmdriver, dgmdevclass, dgmmodhandler, 0);
271
272 static  speed_t dgmdefaultrate = TTYDEF_SPEED;
273
274 static  struct speedtab dgmspeedtab[] = {
275         { 0,            FEP_B0 }, /* old (sysV-like) Bx codes */
276         { 50,           FEP_B50 },
277         { 75,           FEP_B75 },
278         { 110,          FEP_B110 },
279         { 134,          FEP_B134 },
280         { 150,          FEP_B150 },
281         { 200,          FEP_B200 },
282         { 300,          FEP_B300 },
283         { 600,          FEP_B600 },
284         { 1200,         FEP_B1200 },
285         { 1800,         FEP_B1800 },
286         { 2400,         FEP_B2400 },
287         { 4800,         FEP_B4800 },
288         { 9600,         FEP_B9600 },
289         { 19200,        FEP_B19200 },
290         { 38400,        FEP_B38400 },
291         { 57600,        (FEP_FASTBAUD|FEP_B50) }, /* B50 & fast baud table */
292         { 115200,       (FEP_FASTBAUD|FEP_B110) }, /* B100 & fast baud table */
293         { -1,   -1 }
294 };
295
296 static struct dbgflagtbl {
297         tcflag_t in_mask;
298         tcflag_t in_val;
299         tcflag_t out_val;
300 } dgm_cflags[] = {
301         { PARODD,   PARODD,     FEP_PARODD  },
302         { PARENB,   PARENB,     FEP_PARENB  },
303         { CSTOPB,   CSTOPB,     FEP_CSTOPB  },
304         { CSIZE,    CS5,        FEP_CS6     },
305         { CSIZE,    CS6,        FEP_CS6     },
306         { CSIZE,    CS7,        FEP_CS7     },
307         { CSIZE,    CS8,        FEP_CS8     },
308         { CLOCAL,   CLOCAL,     FEP_CLOCAL  },
309         { (tcflag_t)-1 }
310 }, dgm_iflags[] = {
311         { IGNBRK,   IGNBRK,     FEP_IGNBRK  },
312         { BRKINT,   BRKINT,     FEP_BRKINT  },
313         { IGNPAR,   IGNPAR,     FEP_IGNPAR  },
314         { PARMRK,   PARMRK,     FEP_PARMRK  },
315         { INPCK,    INPCK,      FEP_INPCK   },
316         { ISTRIP,   ISTRIP,     FEP_ISTRIP  },
317         { IXON,     IXON,       FEP_IXON    },
318         { IXOFF,    IXOFF,      FEP_IXOFF   },
319         { IXANY,    IXANY,      FEP_IXANY   },
320         { (tcflag_t)-1 }
321 }, dgm_flow[] = {
322         { CRTSCTS,  CRTSCTS,    CTS|RTS },
323         { CRTSCTS,  CCTS_OFLOW, CTS     },
324         { CRTSCTS,  CRTS_IFLOW, RTS     },
325         { (tcflag_t)-1 }
326 };
327
328 /* xlat bsd termios flags to dgm sys-v style */
329 static tcflag_t
330 dgmflags(struct dbgflagtbl *tbl, tcflag_t input)
331 {
332         tcflag_t output = 0;
333         int i;
334
335         for (i = 0; tbl[i].in_mask != (tcflag_t)-1; i++)
336                 if ((input & tbl[i].in_mask) == tbl[i].in_val)
337                         output |= tbl[i].out_val;
338
339         return output;
340 }
341
342 static int dgmdebug = 0;
343 SYSCTL_INT(_debug, OID_AUTO, dgm_debug, CTLFLAG_RW, &dgmdebug, 0, "");
344
345 static __inline int setwin(struct dgm_softc *, unsigned);
346 static __inline void hidewin(struct dgm_softc *);
347 static __inline void towin(struct dgm_softc *, int);
348
349 /*Helg: to allow recursive dgm...() calls */
350 typedef struct {
351         /* If we were called and don't want to disturb we need: */
352         int port;       /* write to this port */
353         u_char data;    /* this data on exit */
354         /* or DATA_WINOFF  to close memory window on entry */
355 } BoardMemWinState;     /* so several channels and even boards can coexist */
356
357 #define DATA_WINOFF 0
358 static BoardMemWinState bmws;
359
360 static u_long validio[] = { 0x104, 0x114, 0x124, 0x204, 0x224, 0x304, 0x324 };
361 static u_long validmem[] = {
362         0x80000, 0x88000, 0x90000, 0x98000, 0xa0000, 0xa8000, 0xb0000, 0xb8000,
363         0xc0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000, 0xe8000, 0xf0000, 0xf8000,
364         0xf0000000, 0xf1000000, 0xf2000000, 0xf3000000, 0xf4000000, 0xf5000000,
365         0xf6000000, 0xf7000000, 0xf8000000, 0xf9000000, 0xfa000000, 0xfb000000,
366         0xfc000000, 0xfd000000, 0xfe000000, 0xff000000
367 };
368
369 /* return current memory window state and close window */
370 static BoardMemWinState
371 bmws_get(void)
372 {
373         BoardMemWinState bmwsRet = bmws;
374
375         if (bmws.data != DATA_WINOFF)
376                 outb(bmws.port, bmws.data = DATA_WINOFF);
377         return bmwsRet;
378 }
379
380 /* restore memory window state */
381 static void
382 bmws_set(BoardMemWinState ws)
383 {
384         if (ws.data != bmws.data || ws.port != bmws.port) {
385                 if (bmws.data != DATA_WINOFF)
386                         outb(bmws.port, DATA_WINOFF);
387                 if (ws.data != DATA_WINOFF)
388                         outb(ws.port, ws.data);
389                 bmws = ws;
390         }
391 }
392
393 static __inline int
394 setwin(struct dgm_softc *sc, unsigned int addr)
395 {
396         outb(bmws.port = sc->port + 1, bmws.data = FEPWIN|(addr >> 15));
397         return (addr & 0x7FFF);
398 }
399
400 static __inline void
401 hidewin(struct dgm_softc *sc)
402 {
403         bmws.data = 0;
404         outb(bmws.port = sc->port + 1, bmws.data);
405 }
406
407 static __inline void
408 towin(struct dgm_softc *sc, int win)
409 {
410         outb(bmws.port = sc->port + 1, bmws.data = win);
411 }
412
413 static int
414 dgmprobe(device_t dev)
415 {
416         struct dgm_softc *sc = device_get_softc(dev);
417         int i, v;
418
419         /*
420          * Assign unit number.  Due to bits we use in the minor number for
421          * the various tty types, only 4 units are supported.
422          */
423         sc->unit = device_get_unit(dev);
424         if (sc->unit > 3) {
425                 device_printf(dev, "Too many units, only 4 supported\n");
426                 return(ENXIO);
427         }
428
429         /* Check that we've got a valid i/o address */
430         if ((sc->port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0)
431                 return (ENXIO);
432         for (i = sizeof (validio) / sizeof (validio[0]) - 1; i >= 0; i--)
433                 if (sc->port == validio[i])
434                         break;
435         if (i == -1) {
436                 device_printf(dev, "0x%03lx: Invalid i/o address\n", sc->port);
437                 return (ENXIO);
438         }
439
440         /* Ditto for our memory address */
441         if ((sc->pmem = bus_get_resource_start(dev, SYS_RES_MEMORY, 0)) == 0)
442                 return (ENXIO);
443         for (i = sizeof (validmem) / sizeof (validmem[0]) - 1; i >= 0; i--)
444                 if (sc->pmem == validmem[i])
445                         break;
446         if (i == -1) {
447                 device_printf(dev, "0x%lx: Invalid memory address\n", sc->pmem);
448                 return (ENXIO);
449         }
450         if ((sc->pmem & 0xFFFFFFul) != sc->pmem) {
451                 device_printf(dev, "0x%lx: Memory address not supported\n",
452                     sc->pmem);
453                 return (ENXIO);
454         }
455         sc->vmem = (u_char *)sc->pmem;
456
457         DPRINT4(DB_INFO, "dgm%d: port 0x%lx mem 0x%lx\n", sc->unit,
458             sc->port, sc->pmem);
459
460         /* Temporarily map our io ports */
461         sc->iorid = 0;
462         sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iorid,
463             0ul, ~0ul, IO_SIZE, RF_ACTIVE);
464         if (sc->io_res == NULL)
465                 return (ENXIO);
466
467         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         for (i = 0; i < sc->numports; i++, bc++) {
787                 DPRINT3(DB_INFO, "dgm%d: Set up port %d\n", sc->unit, i);
788                 port = &sc->ports[i];
789                 port->sc = sc;
790
791                 port->tty = &sc->ttys[i];
792
793                 port->brdchan = bc;
794
795                 port->dcd = CD;
796                 port->dsr = DSR;
797                 port->pnum = i;
798
799                 DPRINT3(DB_INFO, "dgm%d port %d: shrinkmem ?\n", sc->unit, i);
800                 if (shrinkmem) {
801                         DPRINT2(DB_INFO, "dgm%d: shrinking memory\n", sc->unit);
802                         fepcmd(port, SETBUFFER, 32, 0, 0, 0);
803                         shrinkmem = 0;
804                 }
805
806                 DPRINT3(DB_INFO, "dgm%d port %d: assign ptrs\n", sc->unit, i);
807                 port->txptr = mem + ((bc->tseg << 4) & 0x7FFF);
808                 port->rxptr = mem + ((bc->rseg << 4) & 0x7FFF);
809                 port->txwin = FEPWIN | (bc->tseg >> 11);
810                 port->rxwin = FEPWIN | (bc->rseg >> 11);
811
812                 port->txbufhead = 0;
813                 port->rxbufhead = 0;
814                 port->txbufsize = bc->tmax + 1;
815                 port->rxbufsize = bc->rmax + 1;
816
817                 lowwater = (port->txbufsize >= 2000) ?
818                     1024 : (port->txbufsize / 2);
819
820                 setwin(sc, 0);
821                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd STXLWATER %d\n",
822                     sc->unit, i, lowwater);
823                 fepcmd(port, STXLWATER, lowwater, 0, 10, 0);
824                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd SRXLWATER %d\n",
825                     sc->unit, i, port->rxbufsize / 4);
826                 fepcmd(port, SRXLWATER, port->rxbufsize / 4, 0, 10, 0);
827                 DPRINT4(DB_INFO, "dgm%d port %d: fepcmd SRXHWATER %d\n",
828                     sc->unit, i, 3 * port->rxbufsize / 4);
829                 fepcmd(port, SRXHWATER, 3 * port->rxbufsize / 4, 0, 10, 0);
830
831                 bc->edelay = 100;
832                 bc->idata = 1;
833
834                 port->startc = bc->startc;
835                 port->startca = bc->startca;
836                 port->stopc = bc->stopc;
837                 port->stopca = bc->stopca;
838
839                 /* port->close_delay = 50; */
840                 port->close_delay = 3 * hz;
841                 port->do_timestamp = 0;
842                 port->do_dcd_timestamp = 0;
843
844                 DPRINT3(DB_INFO, "dgm%d port %d: setup flags\n", sc->unit, i);
845                 /*
846                  * We don't use all the flags from <sys/ttydefaults.h> since
847                  * they are only relevant for logins.  It's important to have
848                  * echo off initially so that the line doesn't start
849                  * blathering before the echo flag can be turned off.
850                  */
851                 port->it_in.c_iflag = TTYDEF_IFLAG;
852                 port->it_in.c_oflag = TTYDEF_OFLAG;
853                 port->it_in.c_cflag = TTYDEF_CFLAG;
854                 port->it_in.c_lflag = TTYDEF_LFLAG;
855                 termioschars(&port->it_in);
856                 port->it_in.c_ispeed = port->it_in.c_ospeed = dgmdefaultrate;
857                 port->it_out = port->it_in;
858
859                 DPRINT3(DB_INFO, "dgm%d port %d: make devices\n", sc->unit, i);
860                 make_dev(&dgm_ops, (sc->unit*65536) + i, UID_ROOT,
861                     GID_WHEEL, 0600, "ttyM%d%x", sc->unit, i + 0xa0);
862                 make_dev(&dgm_ops, sc->unit * 65536 + i + 64, UID_ROOT,
863                     GID_WHEEL, 0600, "ttyiM%d%x", sc->unit, i + 0xa0);
864                 make_dev(&dgm_ops, sc->unit * 65536 + i + 128, UID_ROOT,
865                     GID_WHEEL, 0600, "ttylM%d%x", sc->unit, i + 0xa0);
866                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262144, UID_UUCP,
867                     GID_DIALER, 0660, "cuaM%d%x", sc->unit, i + 0xa0);
868                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262208, UID_UUCP,
869                     GID_DIALER, 0660, "cuaiM%d%x", sc->unit, i + 0xa0);
870                 make_dev(&dgm_ops, sc->unit * 65536 + i + 262272, UID_UUCP,
871                     GID_DIALER, 0660, "cualM%d%x", sc->unit, i + 0xa0);
872         }
873
874         DPRINT3(DB_INFO, "dgm%d: %d device nodes created\n", sc->unit, sc->numports);
875
876         hidewin(sc);
877
878         /* start the polling function */
879         callout_reset(&sc->toh, hz / POLLSPERSEC,
880                         dgmpoll, (void *)(int)sc->unit);
881
882         DPRINT2(DB_INFO, "dgm%d: poll thread started\n", sc->unit);
883
884         return (0);
885 }
886
887 static int
888 dgmdetach(device_t dev)
889 {
890         struct dgm_softc *sc = device_get_softc(dev);
891         int i;
892
893         for (i = 0; i < sc->numports; i++)
894                 if (sc->ttys[i].t_state & TS_ISOPEN)
895                         return (EBUSY);
896
897         DPRINT2(DB_INFO, "dgm%d: detach\n", sc->unit);
898
899         /*
900          * The dev_ops_remove() call will destroy all associated devices
901          * and dereference any ad-hoc-created devices, but does not
902          * dereference devices created via make_dev().
903          */
904         dev_ops_remove_minor(&dgm_ops/*, DGM_UNITMASK*/, DGM_UNIT(sc->unit));
905
906         callout_stop(&sc->toh);
907
908         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
909         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
910
911         FREE(sc->ports, M_TTYS);
912         FREE(sc->ttys, M_TTYS);
913
914         if (sc->vmem) {
915                 pmap_unmapdev((vm_offset_t)sc->vmem, sc->msize);
916                 sc->vmem = NULL;
917         }
918
919         return (0);
920 }
921
922 int
923 dgmshutdown(device_t dev)
924 {
925 #ifdef DEBUG
926         struct dgm_softc *sc = device_get_softc(dev);
927
928         DPRINT2(DB_INFO, "dgm%d: shutdown\n", sc->unit);
929 #endif
930
931         return 0;
932 }
933
934 /* ARGSUSED */
935 static int
936 dgmopen(struct dev_open_args *ap)
937 {
938         cdev_t dev = ap->a_head.a_dev;
939         struct dgm_softc *sc;
940         struct tty *tp;
941         int unit;
942         int mynor;
943         int pnum;
944         struct dgm_p *port;
945         int error;
946         volatile struct board_chan *bc;
947
948         error = 0;
949         mynor = minor(dev);
950         unit = MINOR_TO_UNIT(mynor);
951         pnum = MINOR_TO_PORT(mynor);
952
953         sc = devclass_get_softc(dgmdevclass, unit);
954         if (sc == NULL) {
955                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a nonexisting card\n",
956                     unit);
957                 return ENXIO;
958         }
959
960         DPRINT2(DB_INFO, "dgm%d: open\n", sc->unit);
961
962         if (!sc->enabled) {
963                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a disabled card\n",
964                     unit);
965                 return ENXIO;
966         }
967
968         if (pnum >= sc->numports) {
969                 DPRINT3(DB_EXCEPT, "dgm%d: try to open non-existing port %d\n",
970                     unit, pnum);
971                 return ENXIO;
972         }
973
974         if (mynor & CONTROL_MASK)
975                 return 0;
976
977         tp = &sc->ttys[pnum];
978         dev->si_tty = tp;
979         port = &sc->ports[pnum];
980         bc = port->brdchan;
981
982 open_top:
983         crit_enter();
984
985         while (port->closing) {
986                 error = tsleep(&port->closing, PCATCH, "dgocl", 0);
987
988                 if (error) {
989                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgocl)"
990                             " error = %d\n", unit, pnum, error);
991                         goto out;
992                 }
993         }
994
995         if (tp->t_state & TS_ISOPEN) {
996                 /*
997                  * The device is open, so everything has been initialized.
998                  * Handle conflicts.
999                  */
1000                 if (mynor & CALLOUT_MASK) {
1001                         if (!port->active_out) {
1002                                 error = EBUSY;
1003                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
1004                                     " BUSY error = %d\n", unit, pnum, error);
1005                                 goto out;
1006                         }
1007                 } else if (port->active_out) {
1008                         if (ap->a_oflags & O_NONBLOCK) {
1009                                 error = EBUSY;
1010                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
1011                                     " BUSY error = %d\n", unit, pnum, error);
1012                                 goto out;
1013                         }
1014                         error = tsleep(&port->active_out, PCATCH, "dgmi", 0);
1015                         if (error != 0) {
1016                                 DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgmi)"
1017                                     " error = %d\n", unit, pnum, error);
1018                                 goto out;
1019                         }
1020                         crit_exit();
1021                         goto open_top;
1022                 }
1023                 if (tp->t_state & TS_XCLUDE && priv_check_cred(ap->a_cred, PRIV_ROOT, 0)) {
1024                         error = EBUSY;
1025                         goto out;
1026                 }
1027         } else {
1028                 /*
1029                  * The device isn't open, so there are no conflicts.
1030                  * Initialize it.  Initialization is done twice in many
1031                  * cases: to preempt sleeping callin opens if we are
1032                  * callout, and to complete a callin open after DCD rises.
1033                  */
1034                 tp->t_oproc = dgmstart;
1035                 tp->t_param = dgmparam;
1036                 tp->t_stop = dgmstop;
1037                 tp->t_dev = dev;
1038                 tp->t_termios= (mynor & CALLOUT_MASK) ?
1039                                                         port->it_out :
1040                                                         port->it_in;
1041
1042                 crit_enter();
1043                 setwin(sc, 0);
1044                 port->imodem = bc->mstat;
1045                 bc->rout = bc->rin; /* clear input queue */
1046                 bc->idata = 1;
1047 #ifdef PRINT_BUFSIZE
1048                 kprintf("dgm buffers tx = %x:%x rx = %x:%x\n",
1049                     bc->tseg, bc->tmax, bc->rseg, bc->rmax);
1050 #endif
1051
1052                 hidewin(sc);
1053                 crit_exit();
1054
1055                 port->wopeners++;
1056                 error = dgmparam(tp, &tp->t_termios);
1057                 port->wopeners--;
1058
1059                 if (error != 0) {
1060                         DPRINT4(DB_OPEN, "dgm%d: port%d: dgmparam error = %d\n",
1061                             unit, pnum, error);
1062                         goto out;
1063                 }
1064
1065                 /* handle fake DCD for callout devices */
1066                 /* and initial DCD */
1067
1068                 if ((port->imodem & port->dcd) || mynor & CALLOUT_MASK)
1069                         linesw[tp->t_line].l_modem(tp, 1);
1070         }
1071
1072         /*
1073          * Wait for DCD if necessary.
1074          */
1075         if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
1076             && !(tp->t_cflag & CLOCAL) && !(ap->a_oflags & O_NONBLOCK)) {
1077                 ++port->wopeners;
1078                 error = tsleep(TSA_CARR_ON(tp), PCATCH, "dgdcd", 0);
1079                 --port->wopeners;
1080                 if (error != 0) {
1081                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgdcd)"
1082                             " error = %d\n", unit, pnum, error);
1083                         goto out;
1084                 }
1085                 crit_exit();
1086                 goto open_top;
1087         }
1088         error = linesw[tp->t_line].l_open(dev, tp);
1089         disc_optim(tp, &tp->t_termios);
1090         DPRINT4(DB_OPEN, "dgm%d: port%d: l_open error = %d\n",
1091             unit, pnum, error);
1092
1093         if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
1094                 port->active_out = 1;
1095
1096         port->used = 1;
1097
1098         /* If any port is open (i.e. the open() call is completed for it)
1099          * the device is busy
1100          */
1101
1102 out:
1103         disc_optim(tp, &tp->t_termios);
1104         crit_exit();
1105
1106         if (!(tp->t_state & TS_ISOPEN) && port->wopeners == 0)
1107                 dgmhardclose(port);
1108
1109         DPRINT4(DB_OPEN, "dgm%d: port%d: open() returns %d\n",
1110             unit, pnum, error);
1111
1112         return error;
1113 }
1114
1115 /*ARGSUSED*/
1116 static int
1117 dgmclose(struct dev_close_args *ap)
1118 {
1119         cdev_t dev = ap->a_head.a_dev;
1120         int             mynor;
1121         struct tty      *tp;
1122         int unit, pnum;
1123         struct dgm_softc *sc;
1124         struct dgm_p *port;
1125         int i;
1126
1127         mynor = minor(dev);
1128         if (mynor & CONTROL_MASK)
1129                 return 0;
1130         unit = MINOR_TO_UNIT(mynor);
1131         pnum = MINOR_TO_PORT(mynor);
1132
1133         sc = devclass_get_softc(dgmdevclass, unit);
1134         tp = &sc->ttys[pnum];
1135         port = sc->ports + pnum;
1136
1137         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing\n", unit, pnum);
1138
1139         DPRINT3(DB_CLOSE, "dgm%d: port%d: draining port\n", unit, pnum);
1140         dgm_drain_or_flush(port);
1141
1142         crit_enter();
1143
1144         port->closing = 1;
1145         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing line disc\n", unit, pnum);
1146         linesw[tp->t_line].l_close(tp, ap->a_fflag);
1147         disc_optim(tp, &tp->t_termios);
1148
1149         DPRINT3(DB_CLOSE, "dgm%d: port%d: hard closing\n", unit, pnum);
1150         dgmhardclose(port);
1151         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing tty\n", unit, pnum);
1152         ttyclose(tp);
1153         port->closing = 0;
1154         wakeup(&port->closing);
1155         port->used = 0;
1156
1157         /* mark the card idle when all ports are closed */
1158
1159         for (i = 0; i < sc->numports; i++)
1160                 if (sc->ports[i].used)
1161                         break;
1162
1163         crit_exit();
1164
1165         DPRINT3(DB_CLOSE, "dgm%d: port%d: closed\n", unit, pnum);
1166
1167         wakeup(TSA_CARR_ON(tp));
1168         wakeup(&port->active_out);
1169         port->active_out = 0;
1170
1171         DPRINT3(DB_CLOSE, "dgm%d: port%d: close exit\n", unit, pnum);
1172
1173         return 0;
1174 }
1175
1176 static void
1177 dgmhardclose(struct dgm_p *port)
1178 {
1179         volatile struct board_chan *bc = port->brdchan;
1180         struct dgm_softc *sc;
1181
1182         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1183         DPRINT2(DB_INFO, "dgm%d: dgmhardclose\n", sc->unit);
1184         crit_enter();
1185         port->do_timestamp = 0;
1186         setwin(sc, 0);
1187
1188         bc->idata = 0;
1189         bc->iempty = 0;
1190         bc->ilow = 0;
1191         if (port->tty->t_cflag & HUPCL) {
1192                 port->omodem &= ~(RTS|DTR);
1193                 fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1);
1194         }
1195
1196         hidewin(sc);
1197         crit_exit();
1198
1199         callout_reset(&port->hc_timeout, hz / 2, dgm_pause, &port->brdchan);
1200         tsleep(&port->brdchan, PCATCH, "dgclo", 0);
1201 }
1202
1203 static void
1204 dgm_pause(void *chan)
1205 {
1206         wakeup((caddr_t)chan);
1207 }
1208
1209 static void
1210 dgmpoll(void *unit_c)
1211 {
1212         int unit = (int)unit_c;
1213         int pnum;
1214         struct dgm_p *port;
1215         struct dgm_softc *sc;
1216         int head, tail;
1217         u_char *eventbuf;
1218         int event, mstat, lstat;
1219         volatile struct board_chan *bc;
1220         struct tty *tp;
1221         int rhead, rtail;
1222         int whead, wtail;
1223         int size;
1224         u_char *ptr;
1225         int ocount;
1226         int ibuf_full, obuf_full;
1227         BoardMemWinState ws = bmws_get();
1228
1229         sc = devclass_get_softc(dgmdevclass, unit);
1230         DPRINT2(DB_INFO, "dgm%d: poll\n", sc->unit);
1231
1232         if (!sc->enabled) {
1233                 kprintf("dgm%d: polling of disabled board stopped\n", unit);
1234                 return;
1235         }
1236
1237         setwin(sc, 0);
1238
1239         head = sc->mailbox->ein;
1240         tail = sc->mailbox->eout;
1241
1242         while (head != tail) {
1243                 if (head >= FEP_IMAX - FEP_ISTART
1244                 || tail >= FEP_IMAX - FEP_ISTART
1245                 || (head|tail) & 03 ) {
1246                         kprintf("dgm%d: event queue's head or tail is wrong!"
1247                             " hd = %d, tl = %d\n", unit, head, tail);
1248                         break;
1249                 }
1250
1251                 eventbuf = sc->vmem + tail + FEP_ISTART;
1252                 pnum = eventbuf[0];
1253                 event = eventbuf[1];
1254                 mstat = eventbuf[2];
1255                 lstat = eventbuf[3];
1256
1257                 port = &sc->ports[pnum];
1258                 bc = port->brdchan;
1259                 tp = &sc->ttys[pnum];
1260
1261                 if (pnum >= sc->numports || !port->enabled) {
1262                         kprintf("dgm%d: port%d: got event on nonexisting port\n",
1263                             unit, pnum);
1264                 } else if (port->used || port->wopeners > 0 ) {
1265
1266                         int wrapmask = port->rxbufsize - 1;
1267
1268                         if (!(event & ALL_IND))
1269                                 kprintf("dgm%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
1270                                         unit, pnum, event, mstat, lstat);
1271
1272                         if (event & DATA_IND) {
1273                                 DPRINT3(DB_DATA, "dgm%d: port%d: DATA_IND\n",
1274                                     unit, pnum);
1275
1276                                 rhead = bc->rin & wrapmask;
1277                                 rtail = bc->rout & wrapmask;
1278
1279                                 if (!(tp->t_cflag & CREAD) || !port->used ) {
1280                                         bc->rout = rhead;
1281                                         goto end_of_data;
1282                                 }
1283
1284                                 if (bc->orun) {
1285                                         kprintf("dgm%d: port%d: overrun\n", unit, pnum);
1286                                         bc->orun = 0;
1287                                 }
1288
1289                                 if (!(tp->t_state & TS_ISOPEN))
1290                                         goto end_of_data;
1291
1292                                 for (ibuf_full = FALSE; rhead != rtail && !ibuf_full;) {
1293                                         DPRINT5(DB_RXDATA, "dgm%d: port%d:"
1294                                             " p rx head = %d tail = %d\n", unit,
1295                                             pnum, rhead, rtail);
1296
1297                                         if (rhead > rtail)
1298                                                 size = rhead - rtail;
1299                                         else
1300                                                 size = port->rxbufsize - rtail;
1301
1302                                         ptr = port->rxptr + rtail;
1303
1304 /* Helg: */
1305                                         if (tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) {
1306                                                 size = DGB_IBUFSIZE - tp->t_rawq.c_cc;
1307                                                 DPRINT1(DB_RXDATA, "*");
1308                                                 ibuf_full = TRUE;
1309                                         }
1310
1311                                         if (size) {
1312                                                 if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1313                                                         DPRINT1(DB_RXDATA, "!");
1314                                                         towin(sc, port->rxwin);
1315                                                         tk_nin += size;
1316                                                         tk_rawcc += size;
1317                                                         tp->t_rawcc += size;
1318                                                         b_to_q(ptr, size,
1319                                                             &tp->t_rawq);
1320                                                         setwin(sc, 0);
1321                                                 } else {
1322                                                         int i = size;
1323                                                         unsigned char chr;
1324                                                         do {
1325                                                                 towin(sc, port->rxwin);
1326                                                                 chr = *ptr++;
1327                                                                 hidewin(sc);
1328                                                                (*linesw[tp->t_line].l_rint)(chr, tp);
1329                                                         } while (--i > 0 );
1330                                                         setwin(sc, 0);
1331                                                 }
1332                                         }
1333                                         rtail= (rtail + size) & wrapmask;
1334                                         bc->rout = rtail;
1335                                         rhead = bc->rin & wrapmask;
1336                                         hidewin(sc);
1337                                         ttwakeup(tp);
1338                                         setwin(sc, 0);
1339                                 }
1340                         end_of_data: ;
1341                         }
1342
1343                         if (event & MODEMCHG_IND) {
1344                                 DPRINT3(DB_MODEM, "dgm%d: port%d: "
1345                                     "MODEMCHG_IND\n", unit, pnum);
1346                                 port->imodem = mstat;
1347                                 if (mstat & port->dcd) {
1348                                         hidewin(sc);
1349                                         linesw[tp->t_line].l_modem(tp, 1);
1350                                         setwin(sc, 0);
1351                                         wakeup(TSA_CARR_ON(tp));
1352                                 } else {
1353                                         hidewin(sc);
1354                                         linesw[tp->t_line].l_modem(tp, 0);
1355                                         setwin(sc, 0);
1356                                         if (port->draining) {
1357                                                 port->draining = 0;
1358                                                 wakeup(&port->draining);
1359                                         }
1360                                 }
1361                         }
1362
1363                         if (event & BREAK_IND) {
1364                                 if ((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK))        {
1365                                         DPRINT3(DB_BREAK, "dgm%d: port%d:"
1366                                             " BREAK_IND\n", unit, pnum);
1367                                         hidewin(sc);
1368                                         linesw[tp->t_line].l_rint(TTY_BI, tp);
1369                                         setwin(sc, 0);
1370                                 }
1371                         }
1372
1373 /* Helg: with output flow control */
1374
1375                         if (event & (LOWTX_IND | EMPTYTX_IND) ) {
1376                                 DPRINT3(DB_TXDATA, "dgm%d: port%d:"
1377                                     " LOWTX_IND or EMPTYTX_IND\n", unit, pnum);
1378
1379                                 if ((event & EMPTYTX_IND ) &&
1380                                     tp->t_outq.c_cc == 0 && port->draining) {
1381                                         port->draining = 0;
1382                                         wakeup(&port->draining);
1383                                         bc->ilow = 0;
1384                                         bc->iempty = 0;
1385                                 } else {
1386
1387                                         int wrapmask = port->txbufsize - 1;
1388
1389                                         for (obuf_full = FALSE;
1390                                             tp->t_outq.c_cc != 0 && !obuf_full;
1391                                             ) {
1392                                                 /* add "last-minute" data to write buffer */
1393                                                 if (!(tp->t_state & TS_BUSY)) {
1394                                                         hidewin(sc);
1395 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1396                                                         ttwwakeup(tp);
1397 #else
1398                                                         if (tp->t_outq.c_cc <= tp->t_lowat) {
1399                                                                 if (tp->t_state & TS_ASLEEP) {
1400                                                                         tp->t_state &= ~TS_ASLEEP;
1401                                                                         wakeup(TSA_OLOWAT(tp));
1402                                                                 }
1403                                                                 /* selwakeup(&tp->t_wsel); */
1404                                                         }
1405 #endif
1406                                                         setwin(sc, 0);
1407                                                 }
1408                                                 crit_enter();
1409
1410                                         whead = bc->tin & wrapmask;
1411                                         wtail = bc->tout & wrapmask;
1412
1413                                         if (whead < wtail)
1414                                                 size = wtail - whead - 1;
1415                                         else {
1416                                                 size = port->txbufsize - whead;
1417                                                 if (wtail == 0)
1418                                                         size--;
1419                                         }
1420
1421                                         if (size == 0) {
1422                                                 DPRINT5(DB_WR, "dgm: head = %d tail = %d size = %d full = %d\n",
1423                                                         whead, wtail, size, obuf_full);
1424                                                 bc->iempty = 1;
1425                                                 bc->ilow = 1;
1426                                                 obuf_full = TRUE;
1427                                                 crit_exit();
1428                                                 break;
1429                                         }
1430
1431                                         towin(sc, port->txwin);
1432
1433                                         ocount = q_to_b(&tp->t_outq, port->txptr + whead, size);
1434                                         whead += ocount;
1435
1436                                         setwin(sc, 0);
1437                                         bc->tin = whead;
1438                                         bc->tin = whead & wrapmask;
1439                                         crit_exit();
1440                                 }
1441
1442                                 if (obuf_full) {
1443                                         DPRINT1(DB_WR, " +BUSY\n");
1444                                         tp->t_state |= TS_BUSY;
1445                                 } else {
1446                                         DPRINT1(DB_WR, " -BUSY\n");
1447                                         hidewin(sc);
1448 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1449                                         /* should clear TS_BUSY before ttwwakeup */
1450                                         if (tp->t_state & TS_BUSY)      {
1451                                                 tp->t_state &= ~TS_BUSY;
1452                                                 linesw[tp->t_line].l_start(tp);
1453                                                 ttwwakeup(tp);
1454                                         }
1455 #else
1456                                 if (tp->t_state & TS_ASLEEP) {
1457                                         tp->t_state &= ~TS_ASLEEP;
1458                                         wakeup(TSA_OLOWAT(tp));
1459                                 }
1460                                 tp->t_state &= ~TS_BUSY;
1461 #endif
1462                                         setwin(sc, 0);
1463                                         }
1464                                 }
1465                         }
1466                         bc->idata = 1;   /* require event on incoming data */
1467
1468                 } else {
1469                         bc = port->brdchan;
1470                         DPRINT4(DB_EXCEPT, "dgm%d: port%d: got event 0x%x on closed port\n",
1471                                 unit, pnum, event);
1472                         bc->rout = bc->rin;
1473                         bc->idata = bc->iempty = bc->ilow = 0;
1474                 }
1475
1476                 tail = (tail + 4) & (FEP_IMAX - FEP_ISTART - 4);
1477         }
1478
1479         sc->mailbox->eout = tail;
1480         bmws_set(ws);
1481
1482         callout_reset(&sc->toh, hz / POLLSPERSEC, dgmpoll, unit_c);
1483
1484         DPRINT2(DB_INFO, "dgm%d: poll done\n", sc->unit);
1485 }
1486
1487 static int
1488 dgmioctl(struct dev_ioctl_args *ap)
1489 {
1490         cdev_t dev = ap->a_head.a_dev;
1491         u_long cmd = ap->a_cmd;
1492         caddr_t data = ap->a_data;
1493         struct dgm_softc *sc;
1494         int unit, pnum;
1495         struct dgm_p *port;
1496         int mynor;
1497         struct tty *tp;
1498         volatile struct board_chan *bc;
1499         int error;
1500         int tiocm_xxx;
1501
1502 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1503         u_long          oldcmd;
1504         struct termios  term;
1505 #endif
1506
1507         BoardMemWinState ws = bmws_get();
1508
1509         mynor = minor(dev);
1510         unit = MINOR_TO_UNIT(mynor);
1511         pnum = MINOR_TO_PORT(mynor);
1512
1513         sc = devclass_get_softc(dgmdevclass, unit);
1514         port = &sc->ports[pnum];
1515         tp = &sc->ttys[pnum];
1516         bc = port->brdchan;
1517
1518         if (mynor & CONTROL_MASK) {
1519                 struct termios *ct;
1520
1521                 switch (mynor & CONTROL_MASK) {
1522                 case CONTROL_INIT_STATE:
1523                         ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in;
1524                         break;
1525                 case CONTROL_LOCK_STATE:
1526                         ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
1527                         break;
1528                 default:
1529                         return (ENODEV);        /* /dev/nodev */
1530                 }
1531                 switch (cmd) {
1532                 case TIOCSETA:
1533                         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
1534                         if (error != 0)
1535                                 return (error);
1536                         *ct = *(struct termios *)data;
1537                         return (0);
1538                 case TIOCGETA:
1539                         *(struct termios *)data = *ct;
1540                         return (0);
1541                 case TIOCGETD:
1542                         *(int *)data = TTYDISC;
1543                         return (0);
1544                 case TIOCGWINSZ:
1545                         bzero(data, sizeof(struct winsize));
1546                         return (0);
1547                 default:
1548                         return (ENOTTY);
1549                 }
1550         }
1551
1552 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1553         term = tp->t_termios;
1554         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1555           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);
1556         }
1557         oldcmd = cmd;
1558         error = ttsetcompat(tp, &cmd, data, &term);
1559         if (error != 0)
1560                 return (error);
1561         if (cmd != oldcmd)
1562                 data = (caddr_t)&term;
1563 #endif
1564
1565         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1566                 int     cc;
1567                 struct termios *dt = (struct termios *)data;
1568                 struct termios *lt = mynor & CALLOUT_MASK
1569                                      ? &port->lt_out : &port->lt_in;
1570
1571                 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);
1572                 dt->c_iflag = (tp->t_iflag & lt->c_iflag)
1573                               | (dt->c_iflag & ~lt->c_iflag);
1574                 dt->c_oflag = (tp->t_oflag & lt->c_oflag)
1575                               | (dt->c_oflag & ~lt->c_oflag);
1576                 dt->c_cflag = (tp->t_cflag & lt->c_cflag)
1577                               | (dt->c_cflag & ~lt->c_cflag);
1578                 dt->c_lflag = (tp->t_lflag & lt->c_lflag)
1579                               | (dt->c_lflag & ~lt->c_lflag);
1580                 for (cc = 0; cc < NCCS; ++cc)
1581                         if (lt->c_cc[cc] != 0)
1582                                 dt->c_cc[cc] = tp->t_cc[cc];
1583                 if (lt->c_ispeed != 0)
1584                         dt->c_ispeed = tp->t_ispeed;
1585                 if (lt->c_ospeed != 0)
1586                         dt->c_ospeed = tp->t_ospeed;
1587         }
1588
1589         if (cmd == TIOCSTOP) {
1590                 crit_enter();
1591                 setwin(sc, 0);
1592                 fepcmd(port, PAUSETX, 0, 0, 0, 0);
1593                 bmws_set(ws);
1594                 crit_exit();
1595                 return 0;
1596         } else if (cmd == TIOCSTART) {
1597                 crit_enter();
1598                 setwin(sc, 0);
1599                 fepcmd(port, RESUMETX, 0, 0, 0, 0);
1600                 bmws_set(ws);
1601                 crit_exit();
1602                 return 0;
1603         }
1604
1605         if (cmd == TIOCSETAW || cmd == TIOCSETAF)
1606                 port->mustdrain = 1;
1607
1608         error = linesw[tp->t_line].l_ioctl(tp, cmd, data,
1609                                            ap->a_fflag, ap->a_cred);
1610         if (error != ENOIOCTL)
1611                 return error;
1612         crit_enter();
1613         error = ttioctl(tp, cmd, data, ap->a_fflag);
1614         disc_optim(tp, &tp->t_termios);
1615         port->mustdrain = 0;
1616         if (error != ENOIOCTL) {
1617                 crit_exit();
1618                 if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1619                         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);
1620                 }
1621                 return error;
1622         }
1623
1624         switch (cmd) {
1625         case TIOCSBRK:
1626 #if 0
1627                 error = dgmdrain(port);
1628
1629                 if (error != 0) {
1630                         crit_exit();
1631                         return error;
1632                 }
1633 #endif
1634
1635                 crit_enter();
1636                 setwin(sc, 0);
1637
1638                 /* now it sends 400 millisecond break because I don't know */
1639                 /* how to send an infinite break */
1640
1641                 fepcmd(port, SENDBREAK, 400, 0, 10, 0);
1642                 hidewin(sc);
1643                 crit_exit();
1644                 break;
1645         case TIOCCBRK:
1646                 /* now it's empty */
1647                 break;
1648         case TIOCSDTR:
1649                 DPRINT3(DB_MODEM, "dgm%d: port%d: set DTR\n", unit, pnum);
1650                 port->omodem |= DTR;
1651                 crit_enter();
1652                 setwin(sc, 0);
1653                 fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1);
1654
1655                 if (!(bc->mstat & DTR))
1656                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is off\n", unit, pnum);
1657
1658                 hidewin(sc);
1659                 crit_exit();
1660                 break;
1661         case TIOCCDTR:
1662                 DPRINT3(DB_MODEM, "dgm%d: port%d: reset DTR\n", unit, pnum);
1663                 port->omodem &= ~DTR;
1664                 crit_enter();
1665                 setwin(sc, 0);
1666                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1667
1668                 if (bc->mstat & DTR) {
1669                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is on\n", unit, pnum);
1670                 }
1671
1672                 hidewin(sc);
1673                 crit_exit();
1674                 break;
1675         case TIOCMSET:
1676                 if (*(int *)data & TIOCM_DTR)
1677                         port->omodem |= DTR;
1678                 else
1679                         port->omodem &= ~DTR;
1680
1681                 if (*(int *)data & TIOCM_RTS)
1682                         port->omodem |= RTS;
1683                 else
1684                         port->omodem &= ~RTS;
1685
1686                 crit_enter();
1687                 setwin(sc, 0);
1688                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1689                 hidewin(sc);
1690                 crit_exit();
1691                 break;
1692         case TIOCMBIS:
1693                 if (*(int *)data & TIOCM_DTR)
1694                         port->omodem |= DTR;
1695
1696                 if (*(int *)data & TIOCM_RTS)
1697                         port->omodem |= RTS;
1698
1699                 crit_enter();
1700                 setwin(sc, 0);
1701                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1702                 hidewin(sc);
1703                 crit_exit();
1704                 break;
1705         case TIOCMBIC:
1706                 if (*(int *)data & TIOCM_DTR)
1707                         port->omodem &= ~DTR;
1708
1709                 if (*(int *)data & TIOCM_RTS)
1710                         port->omodem &= ~RTS;
1711
1712                 crit_enter();
1713                 setwin(sc, 0);
1714                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1715                 hidewin(sc);
1716                 crit_exit();
1717                 break;
1718         case TIOCMGET:
1719                 setwin(sc, 0);
1720                 port->imodem = bc->mstat;
1721                 hidewin(sc);
1722
1723                 tiocm_xxx = TIOCM_LE;   /* XXX - always enabled while open */
1724
1725                 DPRINT3(DB_MODEM, "dgm%d: port%d: modem stat -- ", unit, pnum);
1726
1727                 if (port->imodem & DTR) {
1728                         DPRINT1(DB_MODEM, "DTR ");
1729                         tiocm_xxx |= TIOCM_DTR;
1730                 }
1731                 if (port->imodem & RTS) {
1732                         DPRINT1(DB_MODEM, "RTS ");
1733                         tiocm_xxx |= TIOCM_RTS;
1734                 }
1735                 if (port->imodem & CTS) {
1736                         DPRINT1(DB_MODEM, "CTS ");
1737                         tiocm_xxx |= TIOCM_CTS;
1738                 }
1739                 if (port->imodem & port->dcd) {
1740                         DPRINT1(DB_MODEM, "DCD ");
1741                         tiocm_xxx |= TIOCM_CD;
1742                 }
1743                 if (port->imodem & port->dsr) {
1744                         DPRINT1(DB_MODEM, "DSR ");
1745                         tiocm_xxx |= TIOCM_DSR;
1746                 }
1747                 if (port->imodem & RI) {
1748                         DPRINT1(DB_MODEM, "RI ");
1749                         tiocm_xxx |= TIOCM_RI;
1750                 }
1751                 *(int *)data = tiocm_xxx;
1752                 DPRINT1(DB_MODEM, "--\n");
1753                 break;
1754         case TIOCMSDTRWAIT:
1755                 /* must be root since the wait applies to following logins */
1756                 error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
1757                 if (error != 0) {
1758                         crit_exit();
1759                         return (error);
1760                 }
1761                 port->close_delay = *(int *)data * hz / 100;
1762                 break;
1763         case TIOCMGDTRWAIT:
1764                 *(int *)data = port->close_delay * 100 / hz;
1765                 break;
1766         case TIOCTIMESTAMP:
1767                 port->do_timestamp = 1;
1768                 *(struct timeval *)data = port->timestamp;
1769                 break;
1770         case TIOCDCDTIMESTAMP:
1771                 port->do_dcd_timestamp = 1;
1772                 *(struct timeval *)data = port->dcd_timestamp;
1773                 break;
1774         default:
1775                 bmws_set(ws);
1776                 crit_exit();
1777                 return ENOTTY;
1778         }
1779         bmws_set(ws);
1780         crit_exit();
1781
1782         return 0;
1783 }
1784
1785 static void
1786 wakeflush(void *p)
1787 {
1788         struct dgm_p *port = p;
1789
1790         wakeup(&port->draining);
1791 }
1792
1793 /* wait for the output to drain */
1794
1795 static int
1796 dgmdrain(struct dgm_p *port)
1797 {
1798         volatile struct board_chan *bc = port->brdchan;
1799         struct dgm_softc *sc;
1800         int error;
1801         int head, tail;
1802         BoardMemWinState ws = bmws_get();
1803
1804         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1805
1806         setwin(sc, 0);
1807
1808         bc->iempty = 1;
1809         tail = bc->tout;
1810         head = bc->tin;
1811
1812         while (tail != head) {
1813                 DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1814                         port->sc->unit, port->pnum, head, tail);
1815
1816                 hidewin(sc);
1817                 port->draining = 1;
1818                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1819                 error = tsleep(&port->draining, PCATCH, "dgdrn", 0);
1820                 port->draining = 0;
1821                 setwin(sc, 0);
1822
1823                 if (error != 0) {
1824                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgdrn) error = %d\n",
1825                                 port->sc->unit, port->pnum, error);
1826
1827                         bc->iempty = 0;
1828                         bmws_set(ws);
1829                         return error;
1830                 }
1831
1832                 tail = bc->tout;
1833                 head = bc->tin;
1834         }
1835         DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1836                 port->sc->unit, port->pnum, head, tail);
1837         bmws_set(ws);
1838         return 0;
1839 }
1840
1841 /* wait for the output to drain */
1842 /* or simply clear the buffer it it's stopped */
1843
1844 static void
1845 dgm_drain_or_flush(struct dgm_p *port)
1846 {
1847         volatile struct board_chan *bc = port->brdchan;
1848         struct tty *tp = port->tty;
1849         struct dgm_softc *sc;
1850         int error;
1851         int lasttail;
1852         int head, tail;
1853
1854         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1855         setwin(sc, 0);
1856
1857         lasttail = -1;
1858         bc->iempty = 1;
1859         tail = bc->tout;
1860         head = bc->tin;
1861
1862         while (tail != head /* && tail != lasttail */ ) {
1863                 DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1864                         port->sc->unit, port->pnum, head, tail);
1865
1866                 /* if there is no carrier simply clean the buffer */
1867                 if (!(tp->t_state & TS_CARR_ON)) {
1868                         bc->tout = bc->tin = 0;
1869                         bc->iempty = 0;
1870                         hidewin(sc);
1871                         return;
1872                 }
1873
1874                 hidewin(sc);
1875                 port->draining = 1;
1876                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1877                 error = tsleep(&port->draining, PCATCH, "dgfls", 0);
1878                 port->draining = 0;
1879                 setwin(sc, 0);
1880
1881                 if (error != 0) {
1882                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgfls)"
1883                             " error = %d\n", port->sc->unit, port->pnum, error);
1884
1885                         /* silently clean the buffer */
1886
1887                         bc->tout = bc->tin = 0;
1888                         bc->iempty = 0;
1889                         hidewin(sc);
1890                         return;
1891                 }
1892
1893                 lasttail = tail;
1894                 tail = bc->tout;
1895                 head = bc->tin;
1896         }
1897         hidewin(sc);
1898         DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1899                         port->sc->unit, port->pnum, head, tail);
1900 }
1901
1902 static int
1903 dgmparam(struct tty *tp, struct termios *t)
1904 {
1905         int unit = MINOR_TO_UNIT(minor(tp->t_dev));
1906         int pnum = MINOR_TO_PORT(minor(tp->t_dev));
1907         volatile struct board_chan *bc;
1908         struct dgm_softc *sc;
1909         struct dgm_p *port;
1910         int cflag;
1911         int head;
1912         int mval;
1913         int iflag;
1914         int hflow;
1915         BoardMemWinState ws = bmws_get();
1916
1917         sc = devclass_get_softc(dgmdevclass, unit);
1918         port = &sc->ports[pnum];
1919         bc = port->brdchan;
1920
1921         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);
1922
1923         if (port->mustdrain) {
1924                 DPRINT3(DB_PARAM, "dgm%d: port%d: must call dgmdrain()\n", unit, pnum);
1925                 dgmdrain(port);
1926         }
1927
1928         cflag = ttspeedtab(t->c_ospeed, dgmspeedtab);
1929
1930         if (t->c_ispeed == 0)
1931                 t->c_ispeed = t->c_ospeed;
1932
1933         if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) {
1934                 DPRINT4(DB_PARAM, "dgm%d: port%d: invalid cflag = 0%o\n", unit, pnum, cflag);
1935                 return (EINVAL);
1936         }
1937
1938         crit_enter();
1939         setwin(sc, 0);
1940
1941         if (cflag == 0) { /* hangup */
1942                 DPRINT3(DB_PARAM, "dgm%d: port%d: hangup\n", unit, pnum);
1943                 head = bc->rin;
1944                 bc->rout = head;
1945                 head = bc->tin;
1946                 fepcmd(port, STOUT, (unsigned)head, 0, 0, 0);
1947                 mval= port->omodem & ~(DTR|RTS);
1948         } else {
1949                 cflag |= dgmflags(dgm_cflags, t->c_cflag);
1950
1951                 if (cflag != port->fepcflag) {
1952                         port->fepcflag = cflag;
1953                         DPRINT5(DB_PARAM, "dgm%d: port%d: set cflag = 0x%x c = 0x%x\n",
1954                                         unit, pnum, cflag, t->c_cflag&~CRTSCTS);
1955                         fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0);
1956                 }
1957                 mval= port->omodem | (DTR|RTS);
1958         }
1959
1960         iflag = dgmflags(dgm_iflags, t->c_iflag);
1961         if (iflag != port->fepiflag) {
1962                 port->fepiflag = iflag;
1963                 DPRINT5(DB_PARAM, "dgm%d: port%d: set iflag = 0x%x c = 0x%x\n", unit, pnum, iflag, t->c_iflag);
1964                 fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0);
1965         }
1966
1967         bc->mint = port->dcd;
1968
1969         hflow = dgmflags(dgm_flow, t->c_cflag);
1970         if (hflow != port->hflow) {
1971                 port->hflow = hflow;
1972                 DPRINT5(DB_PARAM, "dgm%d: port%d: set hflow = 0x%x f = 0x%x\n", unit, pnum, hflow, t->c_cflag&CRTSCTS);
1973                 fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1);
1974         }
1975
1976         if (port->omodem != mval) {
1977                 DPRINT5(DB_PARAM, "dgm%d: port%d: setting modem parameters 0x%x was 0x%x\n",
1978                         unit, pnum, mval, port->omodem);
1979                 port->omodem = mval;
1980                 fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1);
1981         }
1982
1983         if (port->fepstartc != t->c_cc[VSTART] ||
1984             port->fepstopc != t->c_cc[VSTOP]) {
1985                 DPRINT5(DB_PARAM, "dgm%d: port%d: set startc = %d, stopc = %d\n", unit, pnum, t->c_cc[VSTART], t->c_cc[VSTOP]);
1986                 port->fepstartc = t->c_cc[VSTART];
1987                 port->fepstopc = t->c_cc[VSTOP];
1988                 fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1);
1989         }
1990
1991         bmws_set(ws);
1992         crit_exit();
1993
1994         return 0;
1995
1996 }
1997
1998 static void
1999 dgmstart(struct tty *tp)
2000 {
2001         int unit;
2002         int pnum;
2003         struct dgm_p *port;
2004         struct dgm_softc *sc;
2005         volatile struct board_chan *bc;
2006         int head, tail;
2007         int size, ocount;
2008         int wmask;
2009
2010         BoardMemWinState ws = bmws_get();
2011
2012         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2013         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2014         sc = devclass_get_softc(dgmdevclass, unit);
2015         port = &sc->ports[pnum];
2016         bc = port->brdchan;
2017
2018         wmask = port->txbufsize - 1;
2019
2020         crit_enter();
2021
2022         while (tp->t_outq.c_cc != 0) {
2023 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2024                 ttwwakeup(tp);
2025 #else
2026                 if (tp->t_outq.c_cc <= tp->t_lowat) {
2027                         if (tp->t_state & TS_ASLEEP) {
2028                                 tp->t_state &= ~TS_ASLEEP;
2029                                 wakeup(TSA_OLOWAT(tp));
2030                         }
2031                         /*selwakeup(&tp->t_wsel);*/
2032                 }
2033 #endif
2034                 crit_enter();
2035                 setwin(sc, 0);
2036
2037                 head = bc->tin & wmask;
2038
2039                 do { tail = bc->tout; } while (tail != bc->tout);
2040                 tail = bc->tout & wmask;
2041
2042                 DPRINT5(DB_WR, "dgm%d: port%d: s tx head = %d tail = %d\n", unit, pnum, head, tail);
2043
2044 #ifdef LEAVE_FREE_CHARS
2045                 if (tail > head) {
2046                         size = tail - head - LEAVE_FREE_CHARS;
2047                         if (size < 0)
2048                                 size = 0;
2049                         else {
2050                                 size = port->txbufsize - head;
2051                                 if (tail + port->txbufsize < head)
2052                                         size = 0;
2053                         }
2054                 }
2055 #else
2056                 if (tail > head)
2057                         size = tail - head - 1;
2058                 else {
2059                         size = port->txbufsize - head;
2060                         if (tail == 0)
2061                                 size--;
2062                 }
2063 #endif
2064
2065                 if (size == 0) {
2066                         bc->iempty = 1;
2067                         bc->ilow = 1;
2068                         crit_exit();
2069                         bmws_set(ws);
2070                         tp->t_state |= TS_BUSY;
2071                         crit_exit();
2072                         return;
2073                 }
2074
2075                 towin(sc, port->txwin);
2076
2077                 ocount = q_to_b(&tp->t_outq, port->txptr + head, size);
2078                 head += ocount;
2079                 if (head >= port->txbufsize)
2080                         head -= port->txbufsize;
2081
2082                 setwin(sc, 0);
2083                 bc->tin = head;
2084
2085                 DPRINT5(DB_WR, "dgm%d: port%d: tx avail = %d count = %d\n",
2086                     unit, pnum, size, ocount);
2087                 hidewin(sc);
2088                 crit_exit();
2089         }
2090
2091         bmws_set(ws);
2092         crit_exit();
2093
2094 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2095         if (tp->t_state & TS_BUSY) {
2096                 tp->t_state &= ~TS_BUSY;
2097                 linesw[tp->t_line].l_start(tp);
2098                 ttwwakeup(tp);
2099         }
2100 #else
2101         if (tp->t_state & TS_ASLEEP) {
2102                 tp->t_state &= ~TS_ASLEEP;
2103                 wakeup(TSA_OLOWAT(tp));
2104         }
2105         tp->t_state& = ~TS_BUSY;
2106 #endif
2107 }
2108
2109 void
2110 dgmstop(struct tty *tp, int rw)
2111 {
2112         int unit;
2113         int pnum;
2114         struct dgm_p *port;
2115         struct dgm_softc *sc;
2116         volatile struct board_chan *bc;
2117
2118         BoardMemWinState ws = bmws_get();
2119
2120         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2121         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2122
2123         sc = devclass_get_softc(dgmdevclass, unit);
2124         port = &sc->ports[pnum];
2125         bc = port->brdchan;
2126
2127         DPRINT3(DB_WR, "dgm%d: port%d: stop\n", port->sc->unit, port->pnum);
2128
2129         crit_enter();
2130         setwin(sc, 0);
2131
2132         if (rw & FWRITE) {
2133                 /* clear output queue */
2134                 bc->tout = bc->tin = 0;
2135                 bc->ilow = 0;
2136                 bc->iempty = 0;
2137         }
2138         if (rw & FREAD) {
2139                 /* clear input queue */
2140                 bc->rout = bc->rin;
2141                 bc->idata = 1;
2142         }
2143         hidewin(sc);
2144         bmws_set(ws);
2145         crit_exit();
2146         dgmstart(tp);
2147 }
2148
2149 static void
2150 fepcmd(struct dgm_p *port,
2151         unsigned cmd,
2152         unsigned op1,
2153         unsigned op2,
2154         unsigned ncmds,
2155         unsigned bytecmd)
2156 {
2157         u_char *mem;
2158         unsigned tail, head;
2159         int count, n;
2160
2161         KASSERT(port->sc, ("Couldn't (re)obtain driver softc"));
2162         mem = port->sc->vmem;
2163
2164         if (!port->enabled) {
2165                 kprintf("dgm%d: port%d: FEP command on disabled port\n",
2166                         port->sc->unit, port->pnum);
2167                 return;
2168         }
2169
2170         /* setwin(port->sc, 0); Require this to be set by caller */
2171         head = port->sc->mailbox->cin;
2172
2173         if (head >= FEP_CMAX - FEP_CSTART || (head & 3)) {
2174                 kprintf("dgm%d: port%d: wrong pointer head of command queue : 0x%x\n",
2175                         port->sc->unit, port->pnum, head);
2176                 return;
2177         }
2178
2179         mem[head + FEP_CSTART] = cmd;
2180         mem[head + FEP_CSTART + 1] = port->pnum;
2181         if (bytecmd) {
2182                 mem[head + FEP_CSTART + 2] = op1;
2183                 mem[head + FEP_CSTART + 3] = op2;
2184         } else {
2185                 mem[head + FEP_CSTART + 2] = op1 & 0xff;
2186                 mem[head + FEP_CSTART + 3] = (op1 >> 8) & 0xff;
2187         }
2188
2189         DPRINT7(DB_FEP, "dgm%d: port%d: %s cmd = 0x%x op1 = 0x%x op2 = 0x%x\n", port->sc->unit, port->pnum,
2190                         (bytecmd)?"byte":"word", cmd, mem[head + FEP_CSTART + 2], mem[head + FEP_CSTART + 3]);
2191
2192         head = (head + 4) & (FEP_CMAX - FEP_CSTART - 4);
2193         port->sc->mailbox->cin = head;
2194
2195         count = FEPTIMEOUT;
2196
2197         while (count-- != 0) {
2198                 head = port->sc->mailbox->cin;
2199                 tail = port->sc->mailbox->cout;
2200
2201                 n = (head - tail) & (FEP_CMAX - FEP_CSTART - 4);
2202                 if (n <= ncmds * (sizeof(ushort)*4))
2203                         return;
2204         }
2205         kprintf("dgm%d(%d): timeout on FEP cmd = 0x%x\n", port->sc->unit, port->pnum, cmd);
2206 }
2207
2208 static void
2209 disc_optim(struct tty *tp, struct termios *t)
2210 {
2211         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2212             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2213             && (!(t->c_iflag & PARMRK)
2214                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2215             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2216             && linesw[tp->t_line].l_rint == ttyinput)
2217                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
2218         else
2219                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2220 }