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