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