Device layer rollup commit.
[dragonfly.git] / sys / dev / serial / dgb / dgm.c
... / ...
CommitLineData
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
118struct dgm_softc;
119
120/* digiboard port structure */
121struct 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 */
180struct 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
201static void dgmpoll(void *);
202static int dgmprobe(device_t);
203static int dgmattach(device_t);
204static int dgmdetach(device_t);
205static int dgmshutdown(device_t);
206static void fepcmd(struct dgm_p *, unsigned, unsigned, unsigned, unsigned,
207 unsigned);
208static void dgmstart(struct tty *);
209static void dgmstop(struct tty *, int);
210static int dgmparam(struct tty *, struct termios *);
211static void dgmhardclose(struct dgm_p *);
212static void dgm_drain_or_flush(struct dgm_p *);
213static int dgmdrain(struct dgm_p *);
214static void dgm_pause(void *);
215static void wakeflush(void *);
216static void disc_optim(struct tty *, struct termios *);
217
218static d_open_t dgmopen;
219static d_close_t dgmclose;
220static d_ioctl_t dgmioctl;
221
222static 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
231static driver_t dgmdriver = {
232 "dgm",
233 dgmmethods,
234 sizeof (struct dgm_softc),
235};
236
237static devclass_t dgmdevclass;
238
239#define CDEV_MAJOR 101
240static 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
260static int
261dgmmodhandler(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
275DRIVER_MODULE(dgm, isa, dgmdriver, dgmdevclass, dgmmodhandler, 0);
276
277static speed_t dgmdefaultrate = TTYDEF_SPEED;
278
279static 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
301static 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 */
334static tcflag_t
335dgmflags(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
347static int dgmdebug = 0;
348SYSCTL_INT(_debug, OID_AUTO, dgm_debug, CTLFLAG_RW, &dgmdebug, 0, "");
349
350static __inline int setwin(struct dgm_softc *, unsigned);
351static __inline void hidewin(struct dgm_softc *);
352static __inline void towin(struct dgm_softc *, int);
353
354/*Helg: to allow recursive dgm...() calls */
355typedef 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
363static BoardMemWinState bmws;
364
365static u_long validio[] = { 0x104, 0x114, 0x124, 0x204, 0x224, 0x304, 0x324 };
366static 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 */
375static BoardMemWinState
376bmws_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 */
386static void
387bmws_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
398static __inline int
399setwin(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
405static __inline void
406hidewin(struct dgm_softc *sc)
407{
408 bmws.data = 0;
409 outb(bmws.port = sc->port + 1, bmws.data);
410}
411
412static __inline void
413towin(struct dgm_softc *sc, int win)
414{
415 outb(bmws.port = sc->port + 1, bmws.data = win);
416}
417
418static int
419dgmprobe(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
531static int
532dgmattach(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
887static int
888dgmdetach(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
918int
919dgmshutdown(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 */
931static int
932dgmopen(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
978open_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
1098out:
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*/
1112static int
1113dgmclose(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
1172static void
1173dgmhardclose(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
1200static void
1201dgm_pause(void *chan)
1202{
1203 wakeup((caddr_t)chan);
1204}
1205
1206static void
1207dgmpoll(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
1486static int
1487dgmioctl(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
1781static void
1782wakeflush(void *p)
1783{
1784 struct dgm_p *port = p;
1785
1786 wakeup(&port->draining);
1787}
1788
1789/* wait for the output to drain */
1790
1791static int
1792dgmdrain(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
1840static void
1841dgm_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
1898static int
1899dgmparam(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
1995static void
1996dgmstart(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
2108void
2109dgmstop(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
2149static void
2150fepcmd(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
2208static void
2209disc_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}