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