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