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