Merge from vendor branch BIND:
[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.14 2006/09/10 01:26:36 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/conf.h>
79 #include <sys/dkstat.h>
80 #include <sys/fcntl.h>
81 #include <sys/kernel.h>
82 #include <sys/sysctl.h>
83 #include <sys/malloc.h>
84 #include <sys/sysctl.h>
85 #include <sys/tty.h>
86 #include <sys/bus.h>
87 #include <sys/kobj.h>
88 #include <sys/bus.h>
89 #include <sys/rman.h>
90 #include <sys/thread2.h>
91 #include <machine/bus.h>
92 #include <machine/resource.h>
93
94 #include <machine/clock.h>
95
96 #include <vm/vm.h>
97 #include <vm/pmap.h>
98
99 #include "dgmfep.h"
100 #include "dgmbios.h"
101 #include "dgmreg.h"
102
103 #define CALLOUT_MASK            0x40000
104 #define CONTROL_MASK            0xC0
105 #define CONTROL_INIT_STATE      0x40
106 #define CONTROL_LOCK_STATE      0x80
107 #define UNIT_MASK               0x30000
108 #define PORT_MASK               0x3F
109 #define DEV_TO_UNIT(dev)        (MINOR_TO_UNIT(minor(dev)))
110 #define MINOR_MAGIC_MASK        (CALLOUT_MASK | CONTROL_MASK)
111 #define MINOR_TO_UNIT(mynor)    (((mynor) & UNIT_MASK) >> 16)
112 #define MINOR_TO_PORT(mynor)    ((mynor) & PORT_MASK)
113 #define IO_SIZE                 0x04
114 #define MEM_SIZE                0x8000
115
116 #define DGM_UNITMASK            0x30000
117 #define DGM_UNIT(unit)          ((unit) << 16)
118
119 struct dgm_softc;
120
121 /* digiboard port structure */
122 struct dgm_p {
123         unsigned enabled : 1;
124
125         struct dgm_softc *sc;  /* parent softc */
126         u_char pnum;           /* port number */
127         u_char omodem;         /* FEP output modem status     */
128         u_char imodem;         /* FEP input modem status      */
129         u_char modemfake;      /* Modem values to be forced   */
130         u_char modem;          /* Force values                */
131         u_char hflow;
132         u_char dsr;
133         u_char dcd;
134         u_char stopc;
135         u_char startc;
136         u_char stopca;
137         u_char startca;
138         u_char fepstopc;
139         u_char fepstartc;
140         u_char fepstopca;
141         u_char fepstartca;
142         u_char txwin;
143         u_char rxwin;
144         ushort fepiflag;
145         ushort fepcflag;
146         ushort fepoflag;
147         ushort txbufhead;
148         ushort txbufsize;
149         ushort rxbufhead;
150         ushort rxbufsize;
151         int close_delay;
152         u_char *txptr;
153         u_char *rxptr;
154         volatile struct board_chan *brdchan;
155         struct tty *tty;
156
157         u_char  active_out;     /* nonzero if the callout device is open */
158         u_int   wopeners;       /* # processes waiting for DCD in open() */
159
160         /* Initial state. */
161         struct termios  it_in;  /* should be in struct tty */
162         struct termios  it_out;
163
164         /* Lock state. */
165         struct termios  lt_in;  /* should be in struct tty */
166         struct termios  lt_out;
167
168         unsigned do_timestamp : 1;
169         unsigned do_dcd_timestamp : 1;
170         struct timeval  timestamp;
171         struct timeval  dcd_timestamp;
172
173         /* flags of state, are used in sleep() too */
174         u_char closing; /* port is being closed now */
175         u_char draining; /* port is being drained now */
176         u_char used;    /* port is being used now */
177         u_char mustdrain; /* data must be waited to drain in dgmparam() */
178
179         struct callout  hc_timeout;
180         struct callout  wf_timeout;
181 };
182
183 /* Digiboard per-board structure */
184 struct dgm_softc {
185         /* struct board_info */
186         unsigned enabled : 1;
187         u_char unit;                    /* unit number */
188         u_char type;                    /* type of card: PCXE, PCXI, PCXEVE */
189         u_char altpin;                  /* do we need alternate pin setting ? */
190         int numports;                   /* number of ports on card */
191         u_long port;                    /* I/O port */
192         u_char *vmem;                   /* virtual memory address */
193         u_long pmem;                    /* physical memory address */
194         int mem_seg;                    /* internal memory segment */
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                 printf("dgm%d: PC/Xem (type %d, %d)\n", sc->unit, v, second);
496         } else
497                 printf("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
571         DPRINT3(DB_INFO, "dgm%d: internal memory segment 0x%x\n", sc->unit,
572             sc->mem_seg);
573
574         outb(sc->port, FEPRST);
575         DELAY(1);
576
577         for (i = 0; (inb(sc->port) & FEPMASK) != FEPRST; i++) {
578                 if (i > 10000) {
579                         device_printf(dev, "1st reset failed\n");
580                         sc->enabled = 0;
581                         hidewin(sc);
582                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
583                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
584                         return (ENXIO);
585                 }
586                 DELAY(1);
587         }
588
589         DPRINT3(DB_INFO, "dgm%d: got reset after %d us\n", sc->unit, i);
590
591         t = sc->pmem >> 8;      /* disable windowing */
592         outb(sc->port + 2, t & 0xFF);
593         outb(sc->port + 3, t >> 8);
594
595         mem = sc->vmem;
596
597         /* very short memory test */
598         DPRINT2(DB_INFO, "dbg%d: short memory test\n", sc->unit);
599
600         addr = setwin(sc, BOTWIN);
601         *(u_long *)(mem + addr) = 0xA55A3CC3;
602         if (*(u_long *)(mem + addr) != 0xA55A3CC3) {
603                 device_printf(dev, "1st memory test failed\n");
604                 sc->enabled = 0;
605                 hidewin(sc);
606                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
607                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
608                 return (ENXIO);
609         }
610
611         DPRINT2(DB_INFO, "dbg%d: 1st memory test ok\n", sc->unit);
612
613         addr = setwin(sc, TOPWIN);
614         *(u_long *)(mem + addr) = 0x5AA5C33C;
615         if (*(u_long *)(mem + addr) != 0x5AA5C33C) {
616                 device_printf(dev, "2nd memory test failed\n");
617                 sc->enabled = 0;
618                 hidewin(sc);
619                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
620                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
621                 return (ENXIO);
622         }
623
624         DPRINT2(DB_INFO, "dbg%d: 2nd memory test ok\n", sc->unit);
625
626         addr = setwin(sc, BIOSCODE + ((0xF000 - sc->mem_seg) << 4));
627         *(u_long *)(mem + addr) = 0x5AA5C33C;
628         if (*(u_long *)(mem + addr) != 0x5AA5C33C)
629                 device_printf(dev, "3rd (BIOS) memory test failed\n");
630
631         DPRINT2(DB_INFO, "dbg%d: 3rd memory test ok\n", sc->unit);
632
633         addr = setwin(sc, MISCGLOBAL);
634         for (i = 0; i < 16; i++)
635                 mem[addr + i] = 0;
636
637         addr = setwin(sc, BIOSOFFSET);
638         ptr = mem + addr;
639         for (i = 0; ptr < mem + msize; i++)
640                 *ptr++ = pcem_bios[i];
641
642         ptr = mem + BIOSOFFSET;
643         for (i = 0; ptr < mem + msize; i++) {
644                 if (*ptr++ != pcem_bios[i]) {
645                         printf("Low BIOS load failed\n");
646                         sc->enabled = 0;
647                         hidewin(sc);
648                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
649                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
650                         return (ENXIO);
651                 }
652         }
653         DPRINT2(DB_INFO, "dbg%d: pcem_bios seg 1 loaded\n", sc->unit);
654
655         addr = setwin(sc, msize);
656         ptr = mem + addr;
657         for (;i < pcem_nbios; i++)
658                 *ptr++ = pcem_bios[i];
659
660         ptr = mem;
661         for (i = msize - BIOSOFFSET; i < pcem_nbios; i++) {
662                 if (*ptr++ != pcem_bios[i]) {
663                         printf("High BIOS load failed\n");
664                         sc->enabled = 0;
665                         hidewin(sc);
666                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
667                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
668                         return (ENXIO);
669                 }
670         }
671         DPRINT2(DB_INFO, "dbg%d: pcem_bios seg 2 loaded\n", sc->unit);
672         device_printf(dev, "DigiBIOS loaded, initializing");
673
674         addr = setwin(sc, 0);
675
676         *(u_int *)(mem + addr) = 0x0bf00401;
677         *(u_int *)(mem + addr + 4) = 0;
678         *(ushort *)(mem + addr + 0xc00) = 0;
679         outb(sc->port, 0);
680
681         for (i = 0; *(u_char *)(mem + addr + 0xc00) != 0x47; i++) {
682                 DELAY(10000);
683                 if (i > 3000) {
684                         printf("\nBIOS initialize failed(1)\n");
685                         sc->enabled = 0;
686                         hidewin(sc);
687                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
688                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
689                         return (ENXIO);
690                 }
691         }
692
693         if (*(u_char *)(mem + addr + 0xc01) != 0x44) {
694                 printf("\nBIOS initialize failed(2)\n");
695                 sc->enabled = 0;
696                 hidewin(sc);
697                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
698                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
699                 return (ENXIO);
700         }
701         printf(", DigiBIOS running\n");
702
703         DELAY(10000);
704
705         addr = setwin(sc, BIOSOFFSET);
706         ptr = mem + addr;
707         for (i = 0; i < pcem_ncook; i++)
708                 *ptr++ = pcem_cook[i];
709
710         ptr = mem + BIOSOFFSET;
711         for (i = 0; i < pcem_ncook; i++) {
712                 if (*ptr++ != pcem_cook[i]) {
713                         printf("FEP/OS load failed\n");
714                         sc->enabled = 0;
715                         hidewin(sc);
716                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
717                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
718                         return (ENXIO);
719                 }
720         }
721         device_printf(dev, "FEP/OS loaded, initializing");
722
723         addr = setwin(sc, 0);
724         *(ushort *)(mem + addr + 0xd20) = 0;
725         *(u_int *)(mem + addr + 0xc34) = 0xbfc01004;
726         *(u_int *)(mem + addr + 0xc30) = 0x3L;
727         outb(sc->port, 0);
728
729         for (i = 0; *(u_char *)(mem + addr + 0xd20) != 'O'; i++) {
730                 DELAY(10000);
731                 if (i > 3000) {
732                         printf("\nFEP/OS initialize failed(1)\n");
733                         sc->enabled = 0;
734                         hidewin(sc);
735                         bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
736                         bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
737                         return (ENXIO);
738                 }
739         }
740
741         if (*(u_char *)(mem + addr + 0xd21) != 'S') {
742                 printf("\nFEP/OS initialize failed(2)\n");
743                 sc->enabled = 0;
744                 hidewin(sc);
745                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
746                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
747                 return (ENXIO);
748         }
749         printf(", FEP/OS running\n");
750
751         sc->numports = *(ushort *)(mem + setwin(sc, NPORT));
752         device_printf(dev, "%d ports attached\n", sc->numports);
753
754         if (sc->numports > MAX_DGM_PORTS) {
755                 printf("dgm%d: too many ports\n", sc->unit);
756                 sc->enabled = 0;
757                 hidewin(sc);
758                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mrid, sc->mem_res);
759                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->io_res);
760                 return (ENXIO);
761         }
762
763         MALLOC(sc->ports, struct dgm_p *, sizeof (*sc->ports) * sc->numports,
764             M_TTYS, M_WAITOK|M_ZERO);
765         MALLOC(sc->ttys, struct tty *, sizeof (*sc->ttys) * sc->numports,
766             M_TTYS, M_WAITOK|M_ZERO);
767
768         DPRINT3(DB_INFO, "dgm%d: enable %d ports\n", sc->unit, sc->numports);
769         for (i = 0; i < sc->numports; i++) {
770                 sc->ports[i].enabled = 1;
771                 callout_init(&sc->ports[i].hc_timeout);
772                 callout_init(&sc->ports[i].wf_timeout);
773         }
774
775         /* We should now init per-port structures */
776         setwin(sc, 0);
777         bc = (volatile struct board_chan *)(mem + CHANSTRUCT);
778         sc->mailbox = (volatile struct global_data *)(mem + FEP_GLOBAL);
779
780         if (sc->numports < 3)
781                 shrinkmem = 1;
782         else
783                 shrinkmem = 0;
784
785         dev_ops_add(&dgm_ops, DGM_UNITMASK, DGM_UNIT(sc->unit));
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(&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         return (0);
915 }
916
917 int
918 dgmshutdown(device_t dev)
919 {
920 #ifdef DEBUG
921         struct dgm_softc *sc = device_get_softc(dev);
922
923         DPRINT2(DB_INFO, "dgm%d: shutdown\n", sc->unit);
924 #endif
925
926         return 0;
927 }
928
929 /* ARGSUSED */
930 static int
931 dgmopen(struct dev_open_args *ap)
932 {
933         cdev_t dev = ap->a_head.a_dev;
934         struct dgm_softc *sc;
935         struct tty *tp;
936         int unit;
937         int mynor;
938         int pnum;
939         struct dgm_p *port;
940         int error;
941         volatile struct board_chan *bc;
942
943         error = 0;
944         mynor = minor(dev);
945         unit = MINOR_TO_UNIT(mynor);
946         pnum = MINOR_TO_PORT(mynor);
947
948         sc = devclass_get_softc(dgmdevclass, unit);
949         if (sc == NULL) {
950                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a nonexisting card\n",
951                     unit);
952                 return ENXIO;
953         }
954
955         DPRINT2(DB_INFO, "dgm%d: open\n", sc->unit);
956
957         if (!sc->enabled) {
958                 DPRINT2(DB_EXCEPT, "dgm%d: try to open a disabled card\n",
959                     unit);
960                 return ENXIO;
961         }
962
963         if (pnum >= sc->numports) {
964                 DPRINT3(DB_EXCEPT, "dgm%d: try to open non-existing port %d\n",
965                     unit, pnum);
966                 return ENXIO;
967         }
968
969         if (mynor & CONTROL_MASK)
970                 return 0;
971
972         tp = &sc->ttys[pnum];
973         dev->si_tty = tp;
974         port = &sc->ports[pnum];
975         bc = port->brdchan;
976
977 open_top:
978         crit_enter();
979
980         while (port->closing) {
981                 error = tsleep(&port->closing, PCATCH, "dgocl", 0);
982
983                 if (error) {
984                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgocl)"
985                             " error = %d\n", unit, pnum, error);
986                         goto out;
987                 }
988         }
989
990         if (tp->t_state & TS_ISOPEN) {
991                 /*
992                  * The device is open, so everything has been initialized.
993                  * Handle conflicts.
994                  */
995                 if (mynor & CALLOUT_MASK) {
996                         if (!port->active_out) {
997                                 error = EBUSY;
998                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
999                                     " BUSY error = %d\n", unit, pnum, error);
1000                                 goto out;
1001                         }
1002                 } else if (port->active_out) {
1003                         if (ap->a_oflags & O_NONBLOCK) {
1004                                 error = EBUSY;
1005                                 DPRINT4(DB_OPEN, "dgm%d: port%d:"
1006                                     " BUSY error = %d\n", unit, pnum, error);
1007                                 goto out;
1008                         }
1009                         error = tsleep(&port->active_out, PCATCH, "dgmi", 0);
1010                         if (error != 0) {
1011                                 DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgmi)"
1012                                     " error = %d\n", unit, pnum, error);
1013                                 goto out;
1014                         }
1015                         crit_exit();
1016                         goto open_top;
1017                 }
1018                 if (tp->t_state & TS_XCLUDE && suser_cred(ap->a_cred, 0)) {
1019                         error = EBUSY;
1020                         goto out;
1021                 }
1022         } else {
1023                 /*
1024                  * The device isn't open, so there are no conflicts.
1025                  * Initialize it.  Initialization is done twice in many
1026                  * cases: to preempt sleeping callin opens if we are
1027                  * callout, and to complete a callin open after DCD rises.
1028                  */
1029                 tp->t_oproc = dgmstart;
1030                 tp->t_param = dgmparam;
1031                 tp->t_stop = dgmstop;
1032                 tp->t_dev = dev;
1033                 tp->t_termios= (mynor & CALLOUT_MASK) ?
1034                                                         port->it_out :
1035                                                         port->it_in;
1036
1037                 crit_enter();
1038                 setwin(sc, 0);
1039                 port->imodem = bc->mstat;
1040                 bc->rout = bc->rin; /* clear input queue */
1041                 bc->idata = 1;
1042 #ifdef PRINT_BUFSIZE
1043                 printf("dgm buffers tx = %x:%x rx = %x:%x\n",
1044                     bc->tseg, bc->tmax, bc->rseg, bc->rmax);
1045 #endif
1046
1047                 hidewin(sc);
1048                 crit_exit();
1049
1050                 port->wopeners++;
1051                 error = dgmparam(tp, &tp->t_termios);
1052                 port->wopeners--;
1053
1054                 if (error != 0) {
1055                         DPRINT4(DB_OPEN, "dgm%d: port%d: dgmparam error = %d\n",
1056                             unit, pnum, error);
1057                         goto out;
1058                 }
1059
1060                 /* handle fake DCD for callout devices */
1061                 /* and initial DCD */
1062
1063                 if ((port->imodem & port->dcd) || mynor & CALLOUT_MASK)
1064                         linesw[tp->t_line].l_modem(tp, 1);
1065         }
1066
1067         /*
1068          * Wait for DCD if necessary.
1069          */
1070         if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
1071             && !(tp->t_cflag & CLOCAL) && !(ap->a_oflags & O_NONBLOCK)) {
1072                 ++port->wopeners;
1073                 error = tsleep(TSA_CARR_ON(tp), PCATCH, "dgdcd", 0);
1074                 --port->wopeners;
1075                 if (error != 0) {
1076                         DPRINT4(DB_OPEN, "dgm%d: port%d: tsleep(dgdcd)"
1077                             " error = %d\n", unit, pnum, error);
1078                         goto out;
1079                 }
1080                 crit_exit();
1081                 goto open_top;
1082         }
1083         error = linesw[tp->t_line].l_open(dev, tp);
1084         disc_optim(tp, &tp->t_termios);
1085         DPRINT4(DB_OPEN, "dgm%d: port%d: l_open error = %d\n",
1086             unit, pnum, error);
1087
1088         if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
1089                 port->active_out = 1;
1090
1091         port->used = 1;
1092
1093         /* If any port is open (i.e. the open() call is completed for it)
1094          * the device is busy
1095          */
1096
1097 out:
1098         disc_optim(tp, &tp->t_termios);
1099         crit_exit();
1100
1101         if (!(tp->t_state & TS_ISOPEN) && port->wopeners == 0)
1102                 dgmhardclose(port);
1103
1104         DPRINT4(DB_OPEN, "dgm%d: port%d: open() returns %d\n",
1105             unit, pnum, error);
1106
1107         return error;
1108 }
1109
1110 /*ARGSUSED*/
1111 static int
1112 dgmclose(struct dev_close_args *ap)
1113 {
1114         cdev_t dev = ap->a_head.a_dev;
1115         int             mynor;
1116         struct tty      *tp;
1117         int unit, pnum;
1118         struct dgm_softc *sc;
1119         struct dgm_p *port;
1120         int i;
1121
1122         mynor = minor(dev);
1123         if (mynor & CONTROL_MASK)
1124                 return 0;
1125         unit = MINOR_TO_UNIT(mynor);
1126         pnum = MINOR_TO_PORT(mynor);
1127
1128         sc = devclass_get_softc(dgmdevclass, unit);
1129         tp = &sc->ttys[pnum];
1130         port = sc->ports + pnum;
1131
1132         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing\n", unit, pnum);
1133
1134         DPRINT3(DB_CLOSE, "dgm%d: port%d: draining port\n", unit, pnum);
1135         dgm_drain_or_flush(port);
1136
1137         crit_enter();
1138
1139         port->closing = 1;
1140         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing line disc\n", unit, pnum);
1141         linesw[tp->t_line].l_close(tp, ap->a_fflag);
1142         disc_optim(tp, &tp->t_termios);
1143
1144         DPRINT3(DB_CLOSE, "dgm%d: port%d: hard closing\n", unit, pnum);
1145         dgmhardclose(port);
1146         DPRINT3(DB_CLOSE, "dgm%d: port%d: closing tty\n", unit, pnum);
1147         ttyclose(tp);
1148         port->closing = 0;
1149         wakeup(&port->closing);
1150         port->used = 0;
1151
1152         /* mark the card idle when all ports are closed */
1153
1154         for (i = 0; i < sc->numports; i++)
1155                 if (sc->ports[i].used)
1156                         break;
1157
1158         crit_exit();
1159
1160         DPRINT3(DB_CLOSE, "dgm%d: port%d: closed\n", unit, pnum);
1161
1162         wakeup(TSA_CARR_ON(tp));
1163         wakeup(&port->active_out);
1164         port->active_out = 0;
1165
1166         DPRINT3(DB_CLOSE, "dgm%d: port%d: close exit\n", unit, pnum);
1167
1168         return 0;
1169 }
1170
1171 static void
1172 dgmhardclose(struct dgm_p *port)
1173 {
1174         volatile struct board_chan *bc = port->brdchan;
1175         struct dgm_softc *sc;
1176
1177         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1178         DPRINT2(DB_INFO, "dgm%d: dgmhardclose\n", sc->unit);
1179         crit_enter();
1180         port->do_timestamp = 0;
1181         setwin(sc, 0);
1182
1183         bc->idata = 0;
1184         bc->iempty = 0;
1185         bc->ilow = 0;
1186         if (port->tty->t_cflag & HUPCL) {
1187                 port->omodem &= ~(RTS|DTR);
1188                 fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1);
1189         }
1190
1191         hidewin(sc);
1192         crit_exit();
1193
1194         callout_reset(&port->hc_timeout, hz / 2, dgm_pause, &port->brdchan);
1195         tsleep(&port->brdchan, PCATCH, "dgclo", 0);
1196 }
1197
1198 static void
1199 dgm_pause(void *chan)
1200 {
1201         wakeup((caddr_t)chan);
1202 }
1203
1204 static void
1205 dgmpoll(void *unit_c)
1206 {
1207         int unit = (int)unit_c;
1208         int pnum;
1209         struct dgm_p *port;
1210         struct dgm_softc *sc;
1211         int head, tail;
1212         u_char *eventbuf;
1213         int event, mstat, lstat;
1214         volatile struct board_chan *bc;
1215         struct tty *tp;
1216         int rhead, rtail;
1217         int whead, wtail;
1218         int size;
1219         u_char *ptr;
1220         int ocount;
1221         int ibuf_full, obuf_full;
1222         BoardMemWinState ws = bmws_get();
1223
1224         sc = devclass_get_softc(dgmdevclass, unit);
1225         DPRINT2(DB_INFO, "dgm%d: poll\n", sc->unit);
1226
1227         if (!sc->enabled) {
1228                 printf("dgm%d: polling of disabled board stopped\n", unit);
1229                 return;
1230         }
1231
1232         setwin(sc, 0);
1233
1234         head = sc->mailbox->ein;
1235         tail = sc->mailbox->eout;
1236
1237         while (head != tail) {
1238                 if (head >= FEP_IMAX - FEP_ISTART
1239                 || tail >= FEP_IMAX - FEP_ISTART
1240                 || (head|tail) & 03 ) {
1241                         printf("dgm%d: event queue's head or tail is wrong!"
1242                             " hd = %d, tl = %d\n", unit, head, tail);
1243                         break;
1244                 }
1245
1246                 eventbuf = sc->vmem + tail + FEP_ISTART;
1247                 pnum = eventbuf[0];
1248                 event = eventbuf[1];
1249                 mstat = eventbuf[2];
1250                 lstat = eventbuf[3];
1251
1252                 port = &sc->ports[pnum];
1253                 bc = port->brdchan;
1254                 tp = &sc->ttys[pnum];
1255
1256                 if (pnum >= sc->numports || !port->enabled) {
1257                         printf("dgm%d: port%d: got event on nonexisting port\n",
1258                             unit, pnum);
1259                 } else if (port->used || port->wopeners > 0 ) {
1260
1261                         int wrapmask = port->rxbufsize - 1;
1262
1263                         if (!(event & ALL_IND))
1264                                 printf("dgm%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n",
1265                                         unit, pnum, event, mstat, lstat);
1266
1267                         if (event & DATA_IND) {
1268                                 DPRINT3(DB_DATA, "dgm%d: port%d: DATA_IND\n",
1269                                     unit, pnum);
1270
1271                                 rhead = bc->rin & wrapmask;
1272                                 rtail = bc->rout & wrapmask;
1273
1274                                 if (!(tp->t_cflag & CREAD) || !port->used ) {
1275                                         bc->rout = rhead;
1276                                         goto end_of_data;
1277                                 }
1278
1279                                 if (bc->orun) {
1280                                         printf("dgm%d: port%d: overrun\n", unit, pnum);
1281                                         bc->orun = 0;
1282                                 }
1283
1284                                 if (!(tp->t_state & TS_ISOPEN))
1285                                         goto end_of_data;
1286
1287                                 for (ibuf_full = FALSE; rhead != rtail && !ibuf_full;) {
1288                                         DPRINT5(DB_RXDATA, "dgm%d: port%d:"
1289                                             " p rx head = %d tail = %d\n", unit,
1290                                             pnum, rhead, rtail);
1291
1292                                         if (rhead > rtail)
1293                                                 size = rhead - rtail;
1294                                         else
1295                                                 size = port->rxbufsize - rtail;
1296
1297                                         ptr = port->rxptr + rtail;
1298
1299 /* Helg: */
1300                                         if (tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) {
1301                                                 size = DGB_IBUFSIZE - tp->t_rawq.c_cc;
1302                                                 DPRINT1(DB_RXDATA, "*");
1303                                                 ibuf_full = TRUE;
1304                                         }
1305
1306                                         if (size) {
1307                                                 if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1308                                                         DPRINT1(DB_RXDATA, "!");
1309                                                         towin(sc, port->rxwin);
1310                                                         tk_nin += size;
1311                                                         tk_rawcc += size;
1312                                                         tp->t_rawcc += size;
1313                                                         b_to_q(ptr, size,
1314                                                             &tp->t_rawq);
1315                                                         setwin(sc, 0);
1316                                                 } else {
1317                                                         int i = size;
1318                                                         unsigned char chr;
1319                                                         do {
1320                                                                 towin(sc, port->rxwin);
1321                                                                 chr = *ptr++;
1322                                                                 hidewin(sc);
1323                                                                (*linesw[tp->t_line].l_rint)(chr, tp);
1324                                                         } while (--i > 0 );
1325                                                         setwin(sc, 0);
1326                                                 }
1327                                         }
1328                                         rtail= (rtail + size) & wrapmask;
1329                                         bc->rout = rtail;
1330                                         rhead = bc->rin & wrapmask;
1331                                         hidewin(sc);
1332                                         ttwakeup(tp);
1333                                         setwin(sc, 0);
1334                                 }
1335                         end_of_data: ;
1336                         }
1337
1338                         if (event & MODEMCHG_IND) {
1339                                 DPRINT3(DB_MODEM, "dgm%d: port%d: "
1340                                     "MODEMCHG_IND\n", unit, pnum);
1341                                 port->imodem = mstat;
1342                                 if (mstat & port->dcd) {
1343                                         hidewin(sc);
1344                                         linesw[tp->t_line].l_modem(tp, 1);
1345                                         setwin(sc, 0);
1346                                         wakeup(TSA_CARR_ON(tp));
1347                                 } else {
1348                                         hidewin(sc);
1349                                         linesw[tp->t_line].l_modem(tp, 0);
1350                                         setwin(sc, 0);
1351                                         if (port->draining) {
1352                                                 port->draining = 0;
1353                                                 wakeup(&port->draining);
1354                                         }
1355                                 }
1356                         }
1357
1358                         if (event & BREAK_IND) {
1359                                 if ((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK))        {
1360                                         DPRINT3(DB_BREAK, "dgm%d: port%d:"
1361                                             " BREAK_IND\n", unit, pnum);
1362                                         hidewin(sc);
1363                                         linesw[tp->t_line].l_rint(TTY_BI, tp);
1364                                         setwin(sc, 0);
1365                                 }
1366                         }
1367
1368 /* Helg: with output flow control */
1369
1370                         if (event & (LOWTX_IND | EMPTYTX_IND) ) {
1371                                 DPRINT3(DB_TXDATA, "dgm%d: port%d:"
1372                                     " LOWTX_IND or EMPTYTX_IND\n", unit, pnum);
1373
1374                                 if ((event & EMPTYTX_IND ) &&
1375                                     tp->t_outq.c_cc == 0 && port->draining) {
1376                                         port->draining = 0;
1377                                         wakeup(&port->draining);
1378                                         bc->ilow = 0;
1379                                         bc->iempty = 0;
1380                                 } else {
1381
1382                                         int wrapmask = port->txbufsize - 1;
1383
1384                                         for (obuf_full = FALSE;
1385                                             tp->t_outq.c_cc != 0 && !obuf_full;
1386                                             ) {
1387                                                 /* add "last-minute" data to write buffer */
1388                                                 if (!(tp->t_state & TS_BUSY)) {
1389                                                         hidewin(sc);
1390 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1391                                                         ttwwakeup(tp);
1392 #else
1393                                                         if (tp->t_outq.c_cc <= tp->t_lowat) {
1394                                                                 if (tp->t_state & TS_ASLEEP) {
1395                                                                         tp->t_state &= ~TS_ASLEEP;
1396                                                                         wakeup(TSA_OLOWAT(tp));
1397                                                                 }
1398                                                                 /* selwakeup(&tp->t_wsel); */
1399                                                         }
1400 #endif
1401                                                         setwin(sc, 0);
1402                                                 }
1403                                                 crit_enter();
1404
1405                                         whead = bc->tin & wrapmask;
1406                                         wtail = bc->tout & wrapmask;
1407
1408                                         if (whead < wtail)
1409                                                 size = wtail - whead - 1;
1410                                         else {
1411                                                 size = port->txbufsize - whead;
1412                                                 if (wtail == 0)
1413                                                         size--;
1414                                         }
1415
1416                                         if (size == 0) {
1417                                                 DPRINT5(DB_WR, "dgm: head = %d tail = %d size = %d full = %d\n",
1418                                                         whead, wtail, size, obuf_full);
1419                                                 bc->iempty = 1;
1420                                                 bc->ilow = 1;
1421                                                 obuf_full = TRUE;
1422                                                 crit_exit();
1423                                                 break;
1424                                         }
1425
1426                                         towin(sc, port->txwin);
1427
1428                                         ocount = q_to_b(&tp->t_outq, port->txptr + whead, size);
1429                                         whead += ocount;
1430
1431                                         setwin(sc, 0);
1432                                         bc->tin = whead;
1433                                         bc->tin = whead & wrapmask;
1434                                         crit_exit();
1435                                 }
1436
1437                                 if (obuf_full) {
1438                                         DPRINT1(DB_WR, " +BUSY\n");
1439                                         tp->t_state |= TS_BUSY;
1440                                 } else {
1441                                         DPRINT1(DB_WR, " -BUSY\n");
1442                                         hidewin(sc);
1443 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
1444                                         /* should clear TS_BUSY before ttwwakeup */
1445                                         if (tp->t_state & TS_BUSY)      {
1446                                                 tp->t_state &= ~TS_BUSY;
1447                                                 linesw[tp->t_line].l_start(tp);
1448                                                 ttwwakeup(tp);
1449                                         }
1450 #else
1451                                 if (tp->t_state & TS_ASLEEP) {
1452                                         tp->t_state &= ~TS_ASLEEP;
1453                                         wakeup(TSA_OLOWAT(tp));
1454                                 }
1455                                 tp->t_state &= ~TS_BUSY;
1456 #endif
1457                                         setwin(sc, 0);
1458                                         }
1459                                 }
1460                         }
1461                         bc->idata = 1;   /* require event on incoming data */
1462
1463                 } else {
1464                         bc = port->brdchan;
1465                         DPRINT4(DB_EXCEPT, "dgm%d: port%d: got event 0x%x on closed port\n",
1466                                 unit, pnum, event);
1467                         bc->rout = bc->rin;
1468                         bc->idata = bc->iempty = bc->ilow = 0;
1469                 }
1470
1471                 tail = (tail + 4) & (FEP_IMAX - FEP_ISTART - 4);
1472         }
1473
1474         sc->mailbox->eout = tail;
1475         bmws_set(ws);
1476
1477         callout_reset(&sc->toh, hz / POLLSPERSEC, dgmpoll, unit_c);
1478
1479         DPRINT2(DB_INFO, "dgm%d: poll done\n", sc->unit);
1480 }
1481
1482 static int
1483 dgmioctl(struct dev_ioctl_args *ap)
1484 {
1485         cdev_t dev = ap->a_head.a_dev;
1486         u_long cmd = ap->a_cmd;
1487         caddr_t data = ap->a_data;
1488         struct dgm_softc *sc;
1489         int unit, pnum;
1490         struct dgm_p *port;
1491         int mynor;
1492         struct tty *tp;
1493         volatile struct board_chan *bc;
1494         int error;
1495         int tiocm_xxx;
1496
1497 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1498         u_long          oldcmd;
1499         struct termios  term;
1500 #endif
1501
1502         BoardMemWinState ws = bmws_get();
1503
1504         mynor = minor(dev);
1505         unit = MINOR_TO_UNIT(mynor);
1506         pnum = MINOR_TO_PORT(mynor);
1507
1508         sc = devclass_get_softc(dgmdevclass, unit);
1509         port = &sc->ports[pnum];
1510         tp = &sc->ttys[pnum];
1511         bc = port->brdchan;
1512
1513         if (mynor & CONTROL_MASK) {
1514                 struct termios *ct;
1515
1516                 switch (mynor & CONTROL_MASK) {
1517                 case CONTROL_INIT_STATE:
1518                         ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in;
1519                         break;
1520                 case CONTROL_LOCK_STATE:
1521                         ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in;
1522                         break;
1523                 default:
1524                         return (ENODEV);        /* /dev/nodev */
1525                 }
1526                 switch (cmd) {
1527                 case TIOCSETA:
1528                         error = suser_cred(ap->a_cred, 0);
1529                         if (error != 0)
1530                                 return (error);
1531                         *ct = *(struct termios *)data;
1532                         return (0);
1533                 case TIOCGETA:
1534                         *(struct termios *)data = *ct;
1535                         return (0);
1536                 case TIOCGETD:
1537                         *(int *)data = TTYDISC;
1538                         return (0);
1539                 case TIOCGWINSZ:
1540                         bzero(data, sizeof(struct winsize));
1541                         return (0);
1542                 default:
1543                         return (ENOTTY);
1544                 }
1545         }
1546
1547 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
1548         term = tp->t_termios;
1549         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1550           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);
1551         }
1552         oldcmd = cmd;
1553         error = ttsetcompat(tp, &cmd, data, &term);
1554         if (error != 0)
1555                 return (error);
1556         if (cmd != oldcmd)
1557                 data = (caddr_t)&term;
1558 #endif
1559
1560         if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1561                 int     cc;
1562                 struct termios *dt = (struct termios *)data;
1563                 struct termios *lt = mynor & CALLOUT_MASK
1564                                      ? &port->lt_out : &port->lt_in;
1565
1566                 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);
1567                 dt->c_iflag = (tp->t_iflag & lt->c_iflag)
1568                               | (dt->c_iflag & ~lt->c_iflag);
1569                 dt->c_oflag = (tp->t_oflag & lt->c_oflag)
1570                               | (dt->c_oflag & ~lt->c_oflag);
1571                 dt->c_cflag = (tp->t_cflag & lt->c_cflag)
1572                               | (dt->c_cflag & ~lt->c_cflag);
1573                 dt->c_lflag = (tp->t_lflag & lt->c_lflag)
1574                               | (dt->c_lflag & ~lt->c_lflag);
1575                 for (cc = 0; cc < NCCS; ++cc)
1576                         if (lt->c_cc[cc] != 0)
1577                                 dt->c_cc[cc] = tp->t_cc[cc];
1578                 if (lt->c_ispeed != 0)
1579                         dt->c_ispeed = tp->t_ispeed;
1580                 if (lt->c_ospeed != 0)
1581                         dt->c_ospeed = tp->t_ospeed;
1582         }
1583
1584         if (cmd == TIOCSTOP) {
1585                 crit_enter();
1586                 setwin(sc, 0);
1587                 fepcmd(port, PAUSETX, 0, 0, 0, 0);
1588                 bmws_set(ws);
1589                 crit_exit();
1590                 return 0;
1591         } else if (cmd == TIOCSTART) {
1592                 crit_enter();
1593                 setwin(sc, 0);
1594                 fepcmd(port, RESUMETX, 0, 0, 0, 0);
1595                 bmws_set(ws);
1596                 crit_exit();
1597                 return 0;
1598         }
1599
1600         if (cmd == TIOCSETAW || cmd == TIOCSETAF)
1601                 port->mustdrain = 1;
1602
1603         error = linesw[tp->t_line].l_ioctl(tp, cmd, data,
1604                                            ap->a_fflag, ap->a_cred);
1605         if (error != ENOIOCTL)
1606                 return error;
1607         crit_enter();
1608         error = ttioctl(tp, cmd, data, ap->a_fflag);
1609         disc_optim(tp, &tp->t_termios);
1610         port->mustdrain = 0;
1611         if (error != ENOIOCTL) {
1612                 crit_exit();
1613                 if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1614                         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);
1615                 }
1616                 return error;
1617         }
1618
1619         switch (cmd) {
1620         case TIOCSBRK:
1621 #if 0
1622                 error = dgmdrain(port);
1623
1624                 if (error != 0) {
1625                         crit_exit();
1626                         return error;
1627                 }
1628 #endif
1629
1630                 crit_enter();
1631                 setwin(sc, 0);
1632
1633                 /* now it sends 400 millisecond break because I don't know */
1634                 /* how to send an infinite break */
1635
1636                 fepcmd(port, SENDBREAK, 400, 0, 10, 0);
1637                 hidewin(sc);
1638                 crit_exit();
1639                 break;
1640         case TIOCCBRK:
1641                 /* now it's empty */
1642                 break;
1643         case TIOCSDTR:
1644                 DPRINT3(DB_MODEM, "dgm%d: port%d: set DTR\n", unit, pnum);
1645                 port->omodem |= DTR;
1646                 crit_enter();
1647                 setwin(sc, 0);
1648                 fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1);
1649
1650                 if (!(bc->mstat & DTR))
1651                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is off\n", unit, pnum);
1652
1653                 hidewin(sc);
1654                 crit_exit();
1655                 break;
1656         case TIOCCDTR:
1657                 DPRINT3(DB_MODEM, "dgm%d: port%d: reset DTR\n", unit, pnum);
1658                 port->omodem &= ~DTR;
1659                 crit_enter();
1660                 setwin(sc, 0);
1661                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1662
1663                 if (bc->mstat & DTR) {
1664                         DPRINT3(DB_MODEM, "dgm%d: port%d: DTR is on\n", unit, pnum);
1665                 }
1666
1667                 hidewin(sc);
1668                 crit_exit();
1669                 break;
1670         case TIOCMSET:
1671                 if (*(int *)data & TIOCM_DTR)
1672                         port->omodem |= DTR;
1673                 else
1674                         port->omodem &= ~DTR;
1675
1676                 if (*(int *)data & TIOCM_RTS)
1677                         port->omodem |= RTS;
1678                 else
1679                         port->omodem &= ~RTS;
1680
1681                 crit_enter();
1682                 setwin(sc, 0);
1683                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1684                 hidewin(sc);
1685                 crit_exit();
1686                 break;
1687         case TIOCMBIS:
1688                 if (*(int *)data & TIOCM_DTR)
1689                         port->omodem |= DTR;
1690
1691                 if (*(int *)data & TIOCM_RTS)
1692                         port->omodem |= RTS;
1693
1694                 crit_enter();
1695                 setwin(sc, 0);
1696                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1697                 hidewin(sc);
1698                 crit_exit();
1699                 break;
1700         case TIOCMBIC:
1701                 if (*(int *)data & TIOCM_DTR)
1702                         port->omodem &= ~DTR;
1703
1704                 if (*(int *)data & TIOCM_RTS)
1705                         port->omodem &= ~RTS;
1706
1707                 crit_enter();
1708                 setwin(sc, 0);
1709                 fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1);
1710                 hidewin(sc);
1711                 crit_exit();
1712                 break;
1713         case TIOCMGET:
1714                 setwin(sc, 0);
1715                 port->imodem = bc->mstat;
1716                 hidewin(sc);
1717
1718                 tiocm_xxx = TIOCM_LE;   /* XXX - always enabled while open */
1719
1720                 DPRINT3(DB_MODEM, "dgm%d: port%d: modem stat -- ", unit, pnum);
1721
1722                 if (port->imodem & DTR) {
1723                         DPRINT1(DB_MODEM, "DTR ");
1724                         tiocm_xxx |= TIOCM_DTR;
1725                 }
1726                 if (port->imodem & RTS) {
1727                         DPRINT1(DB_MODEM, "RTS ");
1728                         tiocm_xxx |= TIOCM_RTS;
1729                 }
1730                 if (port->imodem & CTS) {
1731                         DPRINT1(DB_MODEM, "CTS ");
1732                         tiocm_xxx |= TIOCM_CTS;
1733                 }
1734                 if (port->imodem & port->dcd) {
1735                         DPRINT1(DB_MODEM, "DCD ");
1736                         tiocm_xxx |= TIOCM_CD;
1737                 }
1738                 if (port->imodem & port->dsr) {
1739                         DPRINT1(DB_MODEM, "DSR ");
1740                         tiocm_xxx |= TIOCM_DSR;
1741                 }
1742                 if (port->imodem & RI) {
1743                         DPRINT1(DB_MODEM, "RI ");
1744                         tiocm_xxx |= TIOCM_RI;
1745                 }
1746                 *(int *)data = tiocm_xxx;
1747                 DPRINT1(DB_MODEM, "--\n");
1748                 break;
1749         case TIOCMSDTRWAIT:
1750                 /* must be root since the wait applies to following logins */
1751                 error = suser_cred(ap->a_cred, 0);
1752                 if (error != 0) {
1753                         crit_exit();
1754                         return (error);
1755                 }
1756                 port->close_delay = *(int *)data * hz / 100;
1757                 break;
1758         case TIOCMGDTRWAIT:
1759                 *(int *)data = port->close_delay * 100 / hz;
1760                 break;
1761         case TIOCTIMESTAMP:
1762                 port->do_timestamp = 1;
1763                 *(struct timeval *)data = port->timestamp;
1764                 break;
1765         case TIOCDCDTIMESTAMP:
1766                 port->do_dcd_timestamp = 1;
1767                 *(struct timeval *)data = port->dcd_timestamp;
1768                 break;
1769         default:
1770                 bmws_set(ws);
1771                 crit_exit();
1772                 return ENOTTY;
1773         }
1774         bmws_set(ws);
1775         crit_exit();
1776
1777         return 0;
1778 }
1779
1780 static void
1781 wakeflush(void *p)
1782 {
1783         struct dgm_p *port = p;
1784
1785         wakeup(&port->draining);
1786 }
1787
1788 /* wait for the output to drain */
1789
1790 static int
1791 dgmdrain(struct dgm_p *port)
1792 {
1793         volatile struct board_chan *bc = port->brdchan;
1794         struct dgm_softc *sc;
1795         int error;
1796         int head, tail;
1797         BoardMemWinState ws = bmws_get();
1798
1799         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1800
1801         setwin(sc, 0);
1802
1803         bc->iempty = 1;
1804         tail = bc->tout;
1805         head = bc->tin;
1806
1807         while (tail != head) {
1808                 DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1809                         port->sc->unit, port->pnum, head, tail);
1810
1811                 hidewin(sc);
1812                 port->draining = 1;
1813                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1814                 error = tsleep(&port->draining, PCATCH, "dgdrn", 0);
1815                 port->draining = 0;
1816                 setwin(sc, 0);
1817
1818                 if (error != 0) {
1819                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgdrn) error = %d\n",
1820                                 port->sc->unit, port->pnum, error);
1821
1822                         bc->iempty = 0;
1823                         bmws_set(ws);
1824                         return error;
1825                 }
1826
1827                 tail = bc->tout;
1828                 head = bc->tin;
1829         }
1830         DPRINT5(DB_WR, "dgm%d: port%d: drain: head = %d tail = %d\n",
1831                 port->sc->unit, port->pnum, head, tail);
1832         bmws_set(ws);
1833         return 0;
1834 }
1835
1836 /* wait for the output to drain */
1837 /* or simply clear the buffer it it's stopped */
1838
1839 static void
1840 dgm_drain_or_flush(struct dgm_p *port)
1841 {
1842         volatile struct board_chan *bc = port->brdchan;
1843         struct tty *tp = port->tty;
1844         struct dgm_softc *sc;
1845         int error;
1846         int lasttail;
1847         int head, tail;
1848
1849         sc = devclass_get_softc(dgmdevclass, port->sc->unit);
1850         setwin(sc, 0);
1851
1852         lasttail = -1;
1853         bc->iempty = 1;
1854         tail = bc->tout;
1855         head = bc->tin;
1856
1857         while (tail != head /* && tail != lasttail */ ) {
1858                 DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1859                         port->sc->unit, port->pnum, head, tail);
1860
1861                 /* if there is no carrier simply clean the buffer */
1862                 if (!(tp->t_state & TS_CARR_ON)) {
1863                         bc->tout = bc->tin = 0;
1864                         bc->iempty = 0;
1865                         hidewin(sc);
1866                         return;
1867                 }
1868
1869                 hidewin(sc);
1870                 port->draining = 1;
1871                 callout_reset(&port->wf_timeout, hz, wakeflush, port);
1872                 error = tsleep(&port->draining, PCATCH, "dgfls", 0);
1873                 port->draining = 0;
1874                 setwin(sc, 0);
1875
1876                 if (error != 0) {
1877                         DPRINT4(DB_WR, "dgm%d: port%d: tsleep(dgfls)"
1878                             " error = %d\n", port->sc->unit, port->pnum, error);
1879
1880                         /* silently clean the buffer */
1881
1882                         bc->tout = bc->tin = 0;
1883                         bc->iempty = 0;
1884                         hidewin(sc);
1885                         return;
1886                 }
1887
1888                 lasttail = tail;
1889                 tail = bc->tout;
1890                 head = bc->tin;
1891         }
1892         hidewin(sc);
1893         DPRINT5(DB_WR, "dgm%d: port%d: flush: head = %d tail = %d\n",
1894                         port->sc->unit, port->pnum, head, tail);
1895 }
1896
1897 static int
1898 dgmparam(struct tty *tp, struct termios *t)
1899 {
1900         int unit = MINOR_TO_UNIT(minor(tp->t_dev));
1901         int pnum = MINOR_TO_PORT(minor(tp->t_dev));
1902         volatile struct board_chan *bc;
1903         struct dgm_softc *sc;
1904         struct dgm_p *port;
1905         int cflag;
1906         int head;
1907         int mval;
1908         int iflag;
1909         int hflow;
1910         BoardMemWinState ws = bmws_get();
1911
1912         sc = devclass_get_softc(dgmdevclass, unit);
1913         port = &sc->ports[pnum];
1914         bc = port->brdchan;
1915
1916         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);
1917
1918         if (port->mustdrain) {
1919                 DPRINT3(DB_PARAM, "dgm%d: port%d: must call dgmdrain()\n", unit, pnum);
1920                 dgmdrain(port);
1921         }
1922
1923         cflag = ttspeedtab(t->c_ospeed, dgmspeedtab);
1924
1925         if (t->c_ispeed == 0)
1926                 t->c_ispeed = t->c_ospeed;
1927
1928         if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) {
1929                 DPRINT4(DB_PARAM, "dgm%d: port%d: invalid cflag = 0%o\n", unit, pnum, cflag);
1930                 return (EINVAL);
1931         }
1932
1933         crit_enter();
1934         setwin(sc, 0);
1935
1936         if (cflag == 0) { /* hangup */
1937                 DPRINT3(DB_PARAM, "dgm%d: port%d: hangup\n", unit, pnum);
1938                 head = bc->rin;
1939                 bc->rout = head;
1940                 head = bc->tin;
1941                 fepcmd(port, STOUT, (unsigned)head, 0, 0, 0);
1942                 mval= port->omodem & ~(DTR|RTS);
1943         } else {
1944                 cflag |= dgmflags(dgm_cflags, t->c_cflag);
1945
1946                 if (cflag != port->fepcflag) {
1947                         port->fepcflag = cflag;
1948                         DPRINT5(DB_PARAM, "dgm%d: port%d: set cflag = 0x%x c = 0x%x\n",
1949                                         unit, pnum, cflag, t->c_cflag&~CRTSCTS);
1950                         fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0);
1951                 }
1952                 mval= port->omodem | (DTR|RTS);
1953         }
1954
1955         iflag = dgmflags(dgm_iflags, t->c_iflag);
1956         if (iflag != port->fepiflag) {
1957                 port->fepiflag = iflag;
1958                 DPRINT5(DB_PARAM, "dgm%d: port%d: set iflag = 0x%x c = 0x%x\n", unit, pnum, iflag, t->c_iflag);
1959                 fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0);
1960         }
1961
1962         bc->mint = port->dcd;
1963
1964         hflow = dgmflags(dgm_flow, t->c_cflag);
1965         if (hflow != port->hflow) {
1966                 port->hflow = hflow;
1967                 DPRINT5(DB_PARAM, "dgm%d: port%d: set hflow = 0x%x f = 0x%x\n", unit, pnum, hflow, t->c_cflag&CRTSCTS);
1968                 fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1);
1969         }
1970
1971         if (port->omodem != mval) {
1972                 DPRINT5(DB_PARAM, "dgm%d: port%d: setting modem parameters 0x%x was 0x%x\n",
1973                         unit, pnum, mval, port->omodem);
1974                 port->omodem = mval;
1975                 fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1);
1976         }
1977
1978         if (port->fepstartc != t->c_cc[VSTART] ||
1979             port->fepstopc != t->c_cc[VSTOP]) {
1980                 DPRINT5(DB_PARAM, "dgm%d: port%d: set startc = %d, stopc = %d\n", unit, pnum, t->c_cc[VSTART], t->c_cc[VSTOP]);
1981                 port->fepstartc = t->c_cc[VSTART];
1982                 port->fepstopc = t->c_cc[VSTOP];
1983                 fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1);
1984         }
1985
1986         bmws_set(ws);
1987         crit_exit();
1988
1989         return 0;
1990
1991 }
1992
1993 static void
1994 dgmstart(struct tty *tp)
1995 {
1996         int unit;
1997         int pnum;
1998         struct dgm_p *port;
1999         struct dgm_softc *sc;
2000         volatile struct board_chan *bc;
2001         int head, tail;
2002         int size, ocount;
2003         int wmask;
2004
2005         BoardMemWinState ws = bmws_get();
2006
2007         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2008         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2009         sc = devclass_get_softc(dgmdevclass, unit);
2010         port = &sc->ports[pnum];
2011         bc = port->brdchan;
2012
2013         wmask = port->txbufsize - 1;
2014
2015         crit_enter();
2016
2017         while (tp->t_outq.c_cc != 0) {
2018 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2019                 ttwwakeup(tp);
2020 #else
2021                 if (tp->t_outq.c_cc <= tp->t_lowat) {
2022                         if (tp->t_state & TS_ASLEEP) {
2023                                 tp->t_state &= ~TS_ASLEEP;
2024                                 wakeup(TSA_OLOWAT(tp));
2025                         }
2026                         /*selwakeup(&tp->t_wsel);*/
2027                 }
2028 #endif
2029                 crit_enter();
2030                 setwin(sc, 0);
2031
2032                 head = bc->tin & wmask;
2033
2034                 do { tail = bc->tout; } while (tail != bc->tout);
2035                 tail = bc->tout & wmask;
2036
2037                 DPRINT5(DB_WR, "dgm%d: port%d: s tx head = %d tail = %d\n", unit, pnum, head, tail);
2038
2039 #ifdef LEAVE_FREE_CHARS
2040                 if (tail > head) {
2041                         size = tail - head - LEAVE_FREE_CHARS;
2042                         if (size < 0)
2043                                 size = 0;
2044                         else {
2045                                 size = port->txbufsize - head;
2046                                 if (tail + port->txbufsize < head)
2047                                         size = 0;
2048                         }
2049                 }
2050 #else
2051                 if (tail > head)
2052                         size = tail - head - 1;
2053                 else {
2054                         size = port->txbufsize - head;
2055                         if (tail == 0)
2056                                 size--;
2057                 }
2058 #endif
2059
2060                 if (size == 0) {
2061                         bc->iempty = 1;
2062                         bc->ilow = 1;
2063                         crit_exit();
2064                         bmws_set(ws);
2065                         tp->t_state |= TS_BUSY;
2066                         crit_exit();
2067                         return;
2068                 }
2069
2070                 towin(sc, port->txwin);
2071
2072                 ocount = q_to_b(&tp->t_outq, port->txptr + head, size);
2073                 head += ocount;
2074                 if (head >= port->txbufsize)
2075                         head -= port->txbufsize;
2076
2077                 setwin(sc, 0);
2078                 bc->tin = head;
2079
2080                 DPRINT5(DB_WR, "dgm%d: port%d: tx avail = %d count = %d\n",
2081                     unit, pnum, size, ocount);
2082                 hidewin(sc);
2083                 crit_exit();
2084         }
2085
2086         bmws_set(ws);
2087         crit_exit();
2088
2089 #ifndef TS_ASLEEP       /* post 2.0.5 FreeBSD */
2090         if (tp->t_state & TS_BUSY) {
2091                 tp->t_state &= ~TS_BUSY;
2092                 linesw[tp->t_line].l_start(tp);
2093                 ttwwakeup(tp);
2094         }
2095 #else
2096         if (tp->t_state & TS_ASLEEP) {
2097                 tp->t_state &= ~TS_ASLEEP;
2098                 wakeup(TSA_OLOWAT(tp));
2099         }
2100         tp->t_state& = ~TS_BUSY;
2101 #endif
2102 }
2103
2104 void
2105 dgmstop(struct tty *tp, int rw)
2106 {
2107         int unit;
2108         int pnum;
2109         struct dgm_p *port;
2110         struct dgm_softc *sc;
2111         volatile struct board_chan *bc;
2112
2113         BoardMemWinState ws = bmws_get();
2114
2115         unit = MINOR_TO_UNIT(minor(tp->t_dev));
2116         pnum = MINOR_TO_PORT(minor(tp->t_dev));
2117
2118         sc = devclass_get_softc(dgmdevclass, unit);
2119         port = &sc->ports[pnum];
2120         bc = port->brdchan;
2121
2122         DPRINT3(DB_WR, "dgm%d: port%d: stop\n", port->sc->unit, port->pnum);
2123
2124         crit_enter();
2125         setwin(sc, 0);
2126
2127         if (rw & FWRITE) {
2128                 /* clear output queue */
2129                 bc->tout = bc->tin = 0;
2130                 bc->ilow = 0;
2131                 bc->iempty = 0;
2132         }
2133         if (rw & FREAD) {
2134                 /* clear input queue */
2135                 bc->rout = bc->rin;
2136                 bc->idata = 1;
2137         }
2138         hidewin(sc);
2139         bmws_set(ws);
2140         crit_exit();
2141         dgmstart(tp);
2142 }
2143
2144 static void
2145 fepcmd(struct dgm_p *port,
2146         unsigned cmd,
2147         unsigned op1,
2148         unsigned op2,
2149         unsigned ncmds,
2150         unsigned bytecmd)
2151 {
2152         u_char *mem;
2153         unsigned tail, head;
2154         int count, n;
2155
2156         KASSERT(port->sc, ("Couldn't (re)obtain driver softc"));
2157         mem = port->sc->vmem;
2158
2159         if (!port->enabled) {
2160                 printf("dgm%d: port%d: FEP command on disabled port\n",
2161                         port->sc->unit, port->pnum);
2162                 return;
2163         }
2164
2165         /* setwin(port->sc, 0); Require this to be set by caller */
2166         head = port->sc->mailbox->cin;
2167
2168         if (head >= FEP_CMAX - FEP_CSTART || (head & 3)) {
2169                 printf("dgm%d: port%d: wrong pointer head of command queue : 0x%x\n",
2170                         port->sc->unit, port->pnum, head);
2171                 return;
2172         }
2173
2174         mem[head + FEP_CSTART] = cmd;
2175         mem[head + FEP_CSTART + 1] = port->pnum;
2176         if (bytecmd) {
2177                 mem[head + FEP_CSTART + 2] = op1;
2178                 mem[head + FEP_CSTART + 3] = op2;
2179         } else {
2180                 mem[head + FEP_CSTART + 2] = op1 & 0xff;
2181                 mem[head + FEP_CSTART + 3] = (op1 >> 8) & 0xff;
2182         }
2183
2184         DPRINT7(DB_FEP, "dgm%d: port%d: %s cmd = 0x%x op1 = 0x%x op2 = 0x%x\n", port->sc->unit, port->pnum,
2185                         (bytecmd)?"byte":"word", cmd, mem[head + FEP_CSTART + 2], mem[head + FEP_CSTART + 3]);
2186
2187         head = (head + 4) & (FEP_CMAX - FEP_CSTART - 4);
2188         port->sc->mailbox->cin = head;
2189
2190         count = FEPTIMEOUT;
2191
2192         while (count-- != 0) {
2193                 head = port->sc->mailbox->cin;
2194                 tail = port->sc->mailbox->cout;
2195
2196                 n = (head - tail) & (FEP_CMAX - FEP_CSTART - 4);
2197                 if (n <= ncmds * (sizeof(ushort)*4))
2198                         return;
2199         }
2200         printf("dgm%d(%d): timeout on FEP cmd = 0x%x\n", port->sc->unit, port->pnum, cmd);
2201 }
2202
2203 static void
2204 disc_optim(struct tty *tp, struct termios *t)
2205 {
2206         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2207             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2208             && (!(t->c_iflag & PARMRK)
2209                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2210             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2211             && linesw[tp->t_line].l_rint == ttyinput)
2212                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
2213         else
2214                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2215 }