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