Commit | Line | Data |
---|---|---|
60f56dfc KM |
1 | /* |
2 | * Copyright (c) 1988 University of Utah. | |
3 | * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * the Systems Programming Group of the University of Utah Computer | |
8 | * Science Department. | |
9 | * | |
10 | * %sccs.include.redist.c% | |
11 | * | |
9acfa6cd | 12 | * from Utah: $Hdr: dcm.c 1.29 92/01/21$ |
60f56dfc | 13 | * |
31b504a7 | 14 | * @(#)dcm.c 7.19 (Berkeley) %G% |
60f56dfc KM |
15 | */ |
16 | ||
17 | /* | |
fdf2ec5d KM |
18 | * TODO: |
19 | * Timeouts | |
2723e1ed | 20 | * Test console support. |
60f56dfc KM |
21 | */ |
22 | ||
23 | #include "dcm.h" | |
24 | #if NDCM > 0 | |
25 | /* | |
26 | * 98642/MUX | |
27 | */ | |
b28b3a13 KB |
28 | #include "sys/param.h" |
29 | #include "sys/systm.h" | |
30 | #include "sys/ioctl.h" | |
2723e1ed | 31 | #include "sys/proc.h" |
2d32402a | 32 | #include "sys/tty.h" |
b28b3a13 KB |
33 | #include "sys/conf.h" |
34 | #include "sys/file.h" | |
35 | #include "sys/uio.h" | |
36 | #include "sys/kernel.h" | |
37 | #include "sys/syslog.h" | |
38 | #include "sys/time.h" | |
60f56dfc | 39 | |
9acfa6cd | 40 | #include "hp/dev/device.h" |
60f56dfc | 41 | #include "dcmreg.h" |
2723e1ed | 42 | #include "machine/cpu.h" |
b28b3a13 | 43 | #include "../hp300/isr.h" |
60f56dfc | 44 | |
fdf2ec5d KM |
45 | #ifndef DEFAULT_BAUD_RATE |
46 | #define DEFAULT_BAUD_RATE 9600 | |
47 | #endif | |
48 | ||
49 | int ttrstrt(); | |
ae20553e KM |
50 | int dcmprobe(), dcmintr(), dcmparam(); |
51 | void dcmstart(); | |
60f56dfc KM |
52 | struct driver dcmdriver = { |
53 | dcmprobe, "dcm", | |
54 | }; | |
55 | ||
56 | #define NDCMLINE (NDCM*4) | |
57 | ||
fdf2ec5d | 58 | struct tty dcm_tty[NDCMLINE]; |
22d09b27 | 59 | struct modemreg *dcm_modem[NDCMLINE]; |
dca9e3a6 | 60 | char mcndlast[NDCMLINE]; /* XXX last modem status for line */ |
fdf2ec5d KM |
61 | int ndcm = NDCMLINE; |
62 | ||
60f56dfc | 63 | int dcm_active; |
fdf2ec5d | 64 | int dcmsoftCAR[NDCM]; |
60f56dfc | 65 | struct dcmdevice *dcm_addr[NDCM]; |
60f56dfc | 66 | struct isr dcmisr[NDCM]; |
60f56dfc KM |
67 | |
68 | struct speedtab dcmspeedtab[] = { | |
69 | 0, BR_0, | |
70 | 50, BR_50, | |
71 | 75, BR_75, | |
72 | 110, BR_110, | |
73 | 134, BR_134, | |
74 | 150, BR_150, | |
75 | 300, BR_300, | |
76 | 600, BR_600, | |
77 | 1200, BR_1200, | |
78 | 1800, BR_1800, | |
79 | 2400, BR_2400, | |
80 | 4800, BR_4800, | |
81 | 9600, BR_9600, | |
82 | 19200, BR_19200, | |
83 | 38400, BR_38400, | |
84 | -1, -1 | |
85 | }; | |
86 | ||
fdf2ec5d KM |
87 | /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */ |
88 | #define DCM_USPERCH(s) (10000000 / (s)) | |
89 | ||
90 | /* | |
91 | * Per board interrupt scheme. 16.7ms is the polling interrupt rate | |
b323f0e1 | 92 | * (16.7ms is about 550 baud, 38.4k is 72 chars in 16.7ms). |
fdf2ec5d KM |
93 | */ |
94 | #define DIS_TIMER 0 | |
95 | #define DIS_PERCHAR 1 | |
96 | #define DIS_RESET 2 | |
97 | ||
98 | int dcmistype = -1; /* -1 == dynamic, 0 == timer, 1 == perchar */ | |
99 | int dcminterval = 5; /* interval (secs) between checks */ | |
100 | struct dcmischeme { | |
101 | int dis_perchar; /* non-zero if interrupting per char */ | |
102 | long dis_time; /* last time examined */ | |
103 | int dis_intr; /* recv interrupts during last interval */ | |
104 | int dis_char; /* characters read during last interval */ | |
105 | } dcmischeme[NDCM]; | |
106 | ||
107 | /* | |
108 | * Console support | |
109 | */ | |
2723e1ed MK |
110 | #ifdef DCMCONSOLE |
111 | int dcmconsole = DCMCONSOLE; | |
112 | #else | |
fdf2ec5d | 113 | int dcmconsole = -1; |
2723e1ed MK |
114 | #endif |
115 | int dcmconsinit; | |
fdf2ec5d KM |
116 | int dcmdefaultrate = DEFAULT_BAUD_RATE; |
117 | int dcmconbrdbusy = 0; | |
2723e1ed | 118 | int dcmmajor; |
fdf2ec5d KM |
119 | extern struct tty *constty; |
120 | ||
121 | #ifdef KGDB | |
122 | /* | |
123 | * Kernel GDB support | |
124 | */ | |
2723e1ed MK |
125 | #include "machine/remote-sl.h" |
126 | ||
b323f0e1 | 127 | extern dev_t kgdb_dev; |
fdf2ec5d KM |
128 | extern int kgdb_rate; |
129 | extern int kgdb_debug_init; | |
130 | #endif | |
131 | ||
132 | /* #define IOSTATS */ | |
133 | ||
60f56dfc | 134 | #ifdef DEBUG |
22d09b27 | 135 | int dcmdebug = 0x0; |
60f56dfc KM |
136 | #define DDB_SIOERR 0x01 |
137 | #define DDB_PARAM 0x02 | |
138 | #define DDB_INPUT 0x04 | |
139 | #define DDB_OUTPUT 0x08 | |
140 | #define DDB_INTR 0x10 | |
fdf2ec5d KM |
141 | #define DDB_IOCTL 0x20 |
142 | #define DDB_INTSCHM 0x40 | |
143 | #define DDB_MODEM 0x80 | |
60f56dfc | 144 | #define DDB_OPENCLOSE 0x100 |
60f56dfc KM |
145 | #endif |
146 | ||
fdf2ec5d KM |
147 | #ifdef IOSTATS |
148 | #define DCMRBSIZE 94 | |
149 | #define DCMXBSIZE 24 | |
150 | ||
151 | struct dcmstats { | |
152 | long xints; /* # of xmit ints */ | |
153 | long xchars; /* # of xmit chars */ | |
154 | long xempty; /* times outq is empty in dcmstart */ | |
155 | long xrestarts; /* times completed while xmitting */ | |
156 | long rints; /* # of recv ints */ | |
157 | long rchars; /* # of recv chars */ | |
158 | long xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */ | |
159 | long rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */ | |
160 | } dcmstats[NDCM]; | |
161 | #endif | |
60f56dfc KM |
162 | |
163 | #define UNIT(x) minor(x) | |
fdf2ec5d | 164 | #define BOARD(x) (((x) >> 2) & 0x3f) |
60f56dfc KM |
165 | #define PORT(x) ((x) & 3) |
166 | #define MKUNIT(b,p) (((b) << 2) | (p)) | |
167 | ||
b323f0e1 MK |
168 | /* |
169 | * Conversion from "HP DCE" to almost-normal DCE: on the 638 8-port mux, | |
170 | * the distribution panel uses "HP DCE" conventions. If requested via | |
171 | * the device flags, we swap the inputs to something closer to normal DCE, | |
172 | * allowing a straight-through cable to a DTE or a reversed cable | |
173 | * to a DCE (reversing 2-3, 4-5, 8-20 and leaving 6 unconnected; | |
174 | * this gets "DCD" on pin 20 and "CTS" on 4, but doesn't connect | |
175 | * DSR or make RTS work, though). The following gives the full | |
176 | * details of a cable from this mux panel to a modem: | |
177 | * | |
178 | * HP modem | |
179 | * name pin pin name | |
180 | * HP inputs: | |
181 | * "Rx" 2 3 Tx | |
182 | * CTS 4 5 CTS (only needed for CCTS_OFLOW) | |
183 | * DCD 20 8 DCD | |
184 | * "DSR" 9 6 DSR (unneeded) | |
185 | * RI 22 22 RI (unneeded) | |
186 | * | |
187 | * HP outputs: | |
188 | * "Tx" 3 2 Rx | |
189 | * "DTR" 6 not connected | |
190 | * "RTS" 8 20 DTR | |
191 | * "SR" 23 4 RTS (often not needed) | |
192 | */ | |
193 | #define FLAG_STDDCE 0x10 /* map inputs if this bit is set in flags */ | |
194 | #define hp2dce_in(ibits) (iconv[(ibits) & 0xf]) | |
195 | static char iconv[16] = { | |
196 | 0, MI_DM, MI_CTS, MI_CTS|MI_DM, | |
197 | MI_CD, MI_CD|MI_DM, MI_CD|MI_CTS, MI_CD|MI_CTS|MI_DM, | |
198 | MI_RI, MI_RI|MI_DM, MI_RI|MI_CTS, MI_RI|MI_CTS|MI_DM, | |
199 | MI_RI|MI_CD, MI_RI|MI_CD|MI_DM, MI_RI|MI_CD|MI_CTS, | |
200 | MI_RI|MI_CD|MI_CTS|MI_DM | |
201 | }; | |
202 | ||
60f56dfc KM |
203 | dcmprobe(hd) |
204 | register struct hp_device *hd; | |
205 | { | |
206 | register struct dcmdevice *dcm; | |
207 | register int i; | |
208 | register int timo = 0; | |
b323f0e1 | 209 | int s, brd, isconsole, mbits; |
60f56dfc KM |
210 | |
211 | dcm = (struct dcmdevice *)hd->hp_addr; | |
212 | if ((dcm->dcm_rsid & 0x1f) != DCMID) | |
213 | return (0); | |
214 | brd = hd->hp_unit; | |
fdf2ec5d KM |
215 | isconsole = (brd == BOARD(dcmconsole)); |
216 | /* | |
217 | * XXX selected console device (CONSUNIT) as determined by | |
218 | * dcmcnprobe does not agree with logical numbering imposed | |
219 | * by the config file (i.e. lowest address DCM is not unit | |
220 | * CONSUNIT). Don't recognize this card. | |
221 | */ | |
222 | if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)]) | |
b323f0e1 | 223 | return (0); |
fdf2ec5d KM |
224 | |
225 | /* | |
226 | * Empirically derived self-test magic | |
227 | */ | |
60f56dfc KM |
228 | s = spltty(); |
229 | dcm->dcm_rsid = DCMRS; | |
230 | DELAY(50000); /* 5000 is not long enough */ | |
231 | dcm->dcm_rsid = 0; | |
232 | dcm->dcm_ic = IC_IE; | |
233 | dcm->dcm_cr = CR_SELFT; | |
fdf2ec5d KM |
234 | while ((dcm->dcm_ic & IC_IR) == 0) |
235 | if (++timo == 20000) | |
b323f0e1 | 236 | return (0); |
60f56dfc | 237 | DELAY(50000) /* XXX why is this needed ???? */ |
fdf2ec5d KM |
238 | while ((dcm->dcm_iir & IIR_SELFT) == 0) |
239 | if (++timo == 400000) | |
b323f0e1 | 240 | return (0); |
60f56dfc KM |
241 | DELAY(50000) /* XXX why is this needed ???? */ |
242 | if (dcm->dcm_stcon != ST_OK) { | |
fdf2ec5d KM |
243 | if (!isconsole) |
244 | printf("dcm%d: self test failed: %x\n", | |
245 | brd, dcm->dcm_stcon); | |
b323f0e1 | 246 | return (0); |
60f56dfc KM |
247 | } |
248 | dcm->dcm_ic = IC_ID; | |
249 | splx(s); | |
250 | ||
251 | hd->hp_ipl = DCMIPL(dcm->dcm_ic); | |
60f56dfc KM |
252 | dcm_addr[brd] = dcm; |
253 | dcm_active |= 1 << brd; | |
254 | dcmsoftCAR[brd] = hd->hp_flags; | |
fdf2ec5d KM |
255 | dcmisr[brd].isr_ipl = hd->hp_ipl; |
256 | dcmisr[brd].isr_arg = brd; | |
257 | dcmisr[brd].isr_intr = dcmintr; | |
60f56dfc | 258 | isrlink(&dcmisr[brd]); |
fdf2ec5d | 259 | #ifdef KGDB |
2723e1ed | 260 | if (major(kgdb_dev) == dcmmajor && BOARD(kgdb_dev) == brd) { |
fdf2ec5d | 261 | if (dcmconsole == UNIT(kgdb_dev)) |
b323f0e1 | 262 | kgdb_dev = NODEV; /* can't debug over console port */ |
19a031b5 | 263 | #ifndef KGDB_CHEAT |
2723e1ed MK |
264 | /* |
265 | * The following could potentially be replaced | |
266 | * by the corresponding code in dcmcnprobe. | |
267 | */ | |
fdf2ec5d KM |
268 | else { |
269 | (void) dcminit(kgdb_dev, kgdb_rate); | |
270 | if (kgdb_debug_init) { | |
2723e1ed MK |
271 | printf("dcm%d: ", UNIT(kgdb_dev)); |
272 | kgdb_connect(1); | |
fdf2ec5d | 273 | } else |
2723e1ed | 274 | printf("dcm%d: kgdb enabled\n", UNIT(kgdb_dev)); |
fdf2ec5d | 275 | } |
2723e1ed | 276 | /* end could be replaced */ |
19a031b5 | 277 | #endif |
fdf2ec5d KM |
278 | } |
279 | #endif | |
280 | if (dcmistype == DIS_TIMER) | |
281 | dcmsetischeme(brd, DIS_RESET|DIS_TIMER); | |
282 | else | |
283 | dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR); | |
22d09b27 KM |
284 | |
285 | /* load pointers to modem control */ | |
286 | dcm_modem[MKUNIT(brd, 0)] = &dcm->dcm_modem0; | |
287 | dcm_modem[MKUNIT(brd, 1)] = &dcm->dcm_modem1; | |
288 | dcm_modem[MKUNIT(brd, 2)] = &dcm->dcm_modem2; | |
289 | dcm_modem[MKUNIT(brd, 3)] = &dcm->dcm_modem3; | |
290 | /* set DCD (modem) and CTS (flow control) on all ports */ | |
b323f0e1 MK |
291 | if (dcmsoftCAR[brd] & FLAG_STDDCE) |
292 | mbits = hp2dce_in(MI_CD|MI_CTS); | |
293 | else | |
294 | mbits = MI_CD|MI_CTS; | |
22d09b27 | 295 | for (i = 0; i < 4; i++) |
b323f0e1 | 296 | dcm_modem[MKUNIT(brd, i)]->mdmmsk = mbits; |
22d09b27 | 297 | |
fdf2ec5d | 298 | dcm->dcm_ic = IC_IE; /* turn all interrupts on */ |
60f56dfc KM |
299 | /* |
300 | * Need to reset baud rate, etc. of next print so reset dcmconsole. | |
301 | * Also make sure console is always "hardwired" | |
302 | */ | |
fdf2ec5d | 303 | if (isconsole) { |
2723e1ed | 304 | dcmconsinit = 0; |
fdf2ec5d | 305 | dcmsoftCAR[brd] |= (1 << PORT(dcmconsole)); |
60f56dfc KM |
306 | } |
307 | return (1); | |
308 | } | |
309 | ||
2723e1ed MK |
310 | /* ARGSUSED */ |
311 | #ifdef __STDC__ | |
312 | dcmopen(dev_t dev, int flag, int mode, struct proc *p) | |
313 | #else | |
314 | dcmopen(dev, flag, mode, p) | |
60f56dfc | 315 | dev_t dev; |
2723e1ed MK |
316 | int flag, mode; |
317 | struct proc *p; | |
318 | #endif | |
60f56dfc KM |
319 | { |
320 | register struct tty *tp; | |
321 | register int unit, brd; | |
b323f0e1 | 322 | int error = 0, mbits; |
60f56dfc KM |
323 | |
324 | unit = UNIT(dev); | |
325 | brd = BOARD(unit); | |
fdf2ec5d | 326 | if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0) |
60f56dfc KM |
327 | return (ENXIO); |
328 | tp = &dcm_tty[unit]; | |
329 | tp->t_oproc = dcmstart; | |
fdf2ec5d | 330 | tp->t_param = dcmparam; |
60f56dfc KM |
331 | tp->t_dev = dev; |
332 | if ((tp->t_state & TS_ISOPEN) == 0) { | |
c98f3d99 | 333 | tp->t_state |= TS_WOPEN; |
60f56dfc | 334 | ttychars(tp); |
2723e1ed MK |
335 | if (tp->t_ispeed == 0) { |
336 | tp->t_iflag = TTYDEF_IFLAG; | |
337 | tp->t_oflag = TTYDEF_OFLAG; | |
338 | tp->t_cflag = TTYDEF_CFLAG; | |
339 | tp->t_lflag = TTYDEF_LFLAG; | |
340 | tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; | |
341 | } | |
fdf2ec5d | 342 | (void) dcmparam(tp, &tp->t_termios); |
60f56dfc | 343 | ttsetwater(tp); |
2723e1ed | 344 | } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) |
60f56dfc | 345 | return (EBUSY); |
b323f0e1 MK |
346 | mbits = MO_ON; |
347 | if (dcmsoftCAR[brd] & FLAG_STDDCE) | |
348 | mbits |= MO_SR; /* pin 23, could be used as RTS */ | |
349 | (void) dcmmctl(dev, mbits, DMSET); /* enable port */ | |
2723e1ed MK |
350 | if ((dcmsoftCAR[brd] & (1 << PORT(unit))) || |
351 | (dcmmctl(dev, MO_OFF, DMGET) & MI_CD)) | |
60f56dfc | 352 | tp->t_state |= TS_CARR_ON; |
22d09b27 KM |
353 | #ifdef DEBUG |
354 | if (dcmdebug & DDB_MODEM) | |
355 | printf("dcm%d: dcmopen port %d softcarr %c\n", | |
356 | brd, unit, (tp->t_state & TS_CARR_ON) ? '1' : '0'); | |
357 | #endif | |
60f56dfc | 358 | (void) spltty(); |
23166e33 | 359 | while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && |
b323f0e1 | 360 | (tp->t_state & TS_CARR_ON) == 0) { |
60f56dfc | 361 | tp->t_state |= TS_WOPEN; |
23166e33 MH |
362 | if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, |
363 | ttopen, 0)) | |
364 | break; | |
60f56dfc KM |
365 | } |
366 | (void) spl0(); | |
22d09b27 | 367 | |
60f56dfc KM |
368 | #ifdef DEBUG |
369 | if (dcmdebug & DDB_OPENCLOSE) | |
370 | printf("dcmopen: u %x st %x fl %x\n", | |
371 | unit, tp->t_state, tp->t_flags); | |
372 | #endif | |
23166e33 MH |
373 | if (error == 0) |
374 | error = (*linesw[tp->t_line].l_open)(dev, tp); | |
375 | return (error); | |
60f56dfc KM |
376 | } |
377 | ||
378 | /*ARGSUSED*/ | |
f5daa13d | 379 | dcmclose(dev, flag, mode, p) |
60f56dfc | 380 | dev_t dev; |
f5daa13d MT |
381 | int flag, mode; |
382 | struct proc *p; | |
60f56dfc KM |
383 | { |
384 | register struct tty *tp; | |
385 | int unit; | |
386 | ||
387 | unit = UNIT(dev); | |
388 | tp = &dcm_tty[unit]; | |
f5daa13d | 389 | (*linesw[tp->t_line].l_close)(tp, flag); |
b323f0e1 MK |
390 | if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || |
391 | (tp->t_state&TS_ISOPEN) == 0) | |
392 | (void) dcmmctl(dev, MO_OFF, DMSET); | |
60f56dfc KM |
393 | #ifdef DEBUG |
394 | if (dcmdebug & DDB_OPENCLOSE) | |
395 | printf("dcmclose: u %x st %x fl %x\n", | |
396 | unit, tp->t_state, tp->t_flags); | |
397 | #endif | |
398 | ttyclose(tp); | |
b323f0e1 | 399 | return (0); |
60f56dfc KM |
400 | } |
401 | ||
402 | dcmread(dev, uio, flag) | |
403 | dev_t dev; | |
404 | struct uio *uio; | |
405 | { | |
406 | register struct tty *tp; | |
407 | ||
408 | tp = &dcm_tty[UNIT(dev)]; | |
409 | return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); | |
410 | } | |
411 | ||
412 | dcmwrite(dev, uio, flag) | |
413 | dev_t dev; | |
414 | struct uio *uio; | |
415 | { | |
416 | int unit = UNIT(dev); | |
417 | register struct tty *tp; | |
418 | ||
419 | tp = &dcm_tty[unit]; | |
fdf2ec5d KM |
420 | /* |
421 | * XXX we disallow virtual consoles if the physical console is | |
422 | * a serial port. This is in case there is a display attached that | |
423 | * is not the console. In that situation we don't need/want the X | |
424 | * server taking over the console. | |
425 | */ | |
426 | if (constty && unit == dcmconsole) | |
427 | constty = NULL; | |
60f56dfc KM |
428 | return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); |
429 | } | |
430 | ||
431 | dcmintr(brd) | |
432 | register int brd; | |
433 | { | |
fdf2ec5d KM |
434 | register struct dcmdevice *dcm = dcm_addr[brd]; |
435 | register struct dcmischeme *dis; | |
22d09b27 KM |
436 | register int unit = MKUNIT(brd, 0); |
437 | register int code, i; | |
438 | int pcnd[4], mcode, mcnd[4]; | |
60f56dfc | 439 | |
fdf2ec5d KM |
440 | /* |
441 | * Do all guarded register accesses right off to minimize | |
442 | * block out of hardware. | |
443 | */ | |
60f56dfc KM |
444 | SEM_LOCK(dcm); |
445 | if ((dcm->dcm_ic & IC_IR) == 0) { | |
446 | SEM_UNLOCK(dcm); | |
b323f0e1 | 447 | return (0); |
60f56dfc KM |
448 | } |
449 | for (i = 0; i < 4; i++) { | |
450 | pcnd[i] = dcm->dcm_icrtab[i].dcm_data; | |
451 | dcm->dcm_icrtab[i].dcm_data = 0; | |
b323f0e1 MK |
452 | code = dcm_modem[unit+i]->mdmin; |
453 | if (dcmsoftCAR[brd] & FLAG_STDDCE) | |
454 | code = hp2dce_in(code); | |
455 | mcnd[i] = code; | |
60f56dfc | 456 | } |
60f56dfc | 457 | code = dcm->dcm_iir & IIR_MASK; |
fdf2ec5d | 458 | dcm->dcm_iir = 0; /* XXX doc claims read clears interrupt?! */ |
22d09b27 KM |
459 | mcode = dcm->dcm_modemintr; |
460 | dcm->dcm_modemintr = 0; | |
60f56dfc KM |
461 | SEM_UNLOCK(dcm); |
462 | ||
463 | #ifdef DEBUG | |
22d09b27 KM |
464 | if (dcmdebug & DDB_INTR) { |
465 | printf("dcmintr(%d): iir %x pc %x/%x/%x/%x ", | |
466 | brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3]); | |
467 | printf("miir %x mc %x/%x/%x/%x\n", | |
468 | mcode, mcnd[0], mcnd[1], mcnd[2], mcnd[3]); | |
469 | } | |
60f56dfc | 470 | #endif |
fdf2ec5d KM |
471 | if (code & IIR_TIMEO) |
472 | dcmrint(brd, dcm); | |
60f56dfc | 473 | if (code & IIR_PORT0) |
22d09b27 | 474 | dcmpint(unit+0, pcnd[0], dcm); |
60f56dfc | 475 | if (code & IIR_PORT1) |
22d09b27 | 476 | dcmpint(unit+1, pcnd[1], dcm); |
60f56dfc | 477 | if (code & IIR_PORT2) |
22d09b27 | 478 | dcmpint(unit+2, pcnd[2], dcm); |
60f56dfc | 479 | if (code & IIR_PORT3) |
22d09b27 KM |
480 | dcmpint(unit+3, pcnd[3], dcm); |
481 | if (code & IIR_MODM) { | |
482 | if (mcode == 0 || mcode & 0x1) /* mcode==0 -> 98642 board */ | |
483 | dcmmint(unit+0, mcnd[0], dcm); | |
484 | if (mcode & 0x2) | |
485 | dcmmint(unit+1, mcnd[1], dcm); | |
486 | if (mcode & 0x4) | |
487 | dcmmint(unit+2, mcnd[2], dcm); | |
488 | if (mcode & 0x8) | |
489 | dcmmint(unit+3, mcnd[3], dcm); | |
490 | } | |
60f56dfc | 491 | |
fdf2ec5d KM |
492 | dis = &dcmischeme[brd]; |
493 | /* | |
494 | * Chalk up a receiver interrupt if the timer running or one of | |
495 | * the ports reports a special character interrupt. | |
496 | */ | |
497 | if ((code & IIR_TIMEO) || | |
498 | ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC)) | |
499 | dis->dis_intr++; | |
60f56dfc | 500 | /* |
fdf2ec5d | 501 | * See if it is time to check/change the interrupt rate. |
60f56dfc | 502 | */ |
fdf2ec5d | 503 | if (dcmistype < 0 && |
22d09b27 | 504 | (i = time.tv_sec - dis->dis_time) >= dcminterval) { |
60f56dfc | 505 | /* |
fdf2ec5d KM |
506 | * If currently per-character and averaged over 70 interrupts |
507 | * per-second (66 is threshold of 600 baud) in last interval, | |
508 | * switch to timer mode. | |
509 | * | |
510 | * XXX decay counts ala load average to avoid spikes? | |
60f56dfc | 511 | */ |
22d09b27 | 512 | if (dis->dis_perchar && dis->dis_intr > 70 * i) |
fdf2ec5d KM |
513 | dcmsetischeme(brd, DIS_TIMER); |
514 | /* | |
515 | * If currently using timer and had more interrupts than | |
516 | * received characters in the last interval, switch back | |
517 | * to per-character. Note that after changing to per-char | |
518 | * we must process any characters already in the queue | |
519 | * since they may have arrived before the bitmap was setup. | |
520 | * | |
521 | * XXX decay counts? | |
522 | */ | |
523 | else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) { | |
524 | dcmsetischeme(brd, DIS_PERCHAR); | |
60f56dfc KM |
525 | dcmrint(brd, dcm); |
526 | } | |
fdf2ec5d KM |
527 | dis->dis_intr = dis->dis_char = 0; |
528 | dis->dis_time = time.tv_sec; | |
529 | } | |
b323f0e1 | 530 | return (1); |
60f56dfc KM |
531 | } |
532 | ||
533 | /* | |
534 | * Port interrupt. Can be two things: | |
535 | * First, it might be a special character (exception interrupt); | |
536 | * Second, it may be a buffer empty (transmit interrupt); | |
537 | */ | |
538 | dcmpint(unit, code, dcm) | |
539 | int unit, code; | |
fdf2ec5d | 540 | struct dcmdevice *dcm; |
60f56dfc | 541 | { |
fdf2ec5d | 542 | struct tty *tp = &dcm_tty[unit]; |
60f56dfc | 543 | |
fdf2ec5d KM |
544 | if (code & IT_SPEC) |
545 | dcmreadbuf(unit, dcm, tp); | |
60f56dfc | 546 | if (code & IT_TX) |
fdf2ec5d | 547 | dcmxint(unit, dcm, tp); |
60f56dfc KM |
548 | } |
549 | ||
550 | dcmrint(brd, dcm) | |
551 | int brd; | |
552 | register struct dcmdevice *dcm; | |
553 | { | |
60f56dfc | 554 | register int i, unit; |
fdf2ec5d | 555 | register struct tty *tp; |
60f56dfc KM |
556 | |
557 | unit = MKUNIT(brd, 0); | |
558 | tp = &dcm_tty[unit]; | |
fdf2ec5d KM |
559 | for (i = 0; i < 4; i++, tp++, unit++) |
560 | dcmreadbuf(unit, dcm, tp); | |
60f56dfc KM |
561 | } |
562 | ||
fdf2ec5d | 563 | dcmreadbuf(unit, dcm, tp) |
60f56dfc KM |
564 | int unit; |
565 | register struct dcmdevice *dcm; | |
566 | register struct tty *tp; | |
60f56dfc | 567 | { |
fdf2ec5d KM |
568 | int port = PORT(unit); |
569 | register struct dcmpreg *pp = dcm_preg(dcm, port); | |
570 | register struct dcmrfifo *fifo; | |
60f56dfc KM |
571 | register int c, stat; |
572 | register unsigned head; | |
fdf2ec5d KM |
573 | int nch = 0; |
574 | #ifdef IOSTATS | |
575 | struct dcmstats *dsp = &dcmstats[BOARD(unit)]; | |
576 | ||
577 | dsp->rints++; | |
60f56dfc | 578 | #endif |
dbfb5080 | 579 | if ((tp->t_state & TS_ISOPEN) == 0) { |
fdf2ec5d | 580 | #ifdef KGDB |
2723e1ed | 581 | if ((makedev(dcmmajor, unit) == kgdb_dev) && |
fdf2ec5d | 582 | (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) && |
31b504a7 | 583 | dcm->dcm_rfifos[3-port][head>>1].data_char == FRAME_START) { |
fdf2ec5d | 584 | pp->r_head = (head + 2) & RX_MASK; |
2723e1ed | 585 | kgdb_connect(0); /* trap into kgdb */ |
fdf2ec5d KM |
586 | return; |
587 | } | |
2723e1ed | 588 | #endif /* KGDB */ |
fdf2ec5d KM |
589 | pp->r_head = pp->r_tail & RX_MASK; |
590 | return; | |
591 | } | |
592 | ||
593 | head = pp->r_head & RX_MASK; | |
594 | fifo = &dcm->dcm_rfifos[3-port][head>>1]; | |
595 | /* | |
596 | * XXX upper bound on how many chars we will take in one swallow? | |
597 | */ | |
598 | while (head != (pp->r_tail & RX_MASK)) { | |
599 | /* | |
600 | * Get character/status and update head pointer as fast | |
601 | * as possible to make room for more characters. | |
602 | */ | |
603 | c = fifo->data_char; | |
604 | stat = fifo->data_stat; | |
60f56dfc | 605 | head = (head + 2) & RX_MASK; |
fdf2ec5d KM |
606 | pp->r_head = head; |
607 | fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0]; | |
608 | nch++; | |
60f56dfc KM |
609 | |
610 | #ifdef DEBUG | |
611 | if (dcmdebug & DDB_INPUT) | |
fdf2ec5d KM |
612 | printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n", |
613 | unit, c&0xFF, c, stat&0xFF, | |
614 | tp->t_flags, head, pp->r_tail); | |
60f56dfc | 615 | #endif |
fdf2ec5d KM |
616 | /* |
617 | * Check for and handle errors | |
618 | */ | |
619 | if (stat & RD_MASK) { | |
60f56dfc | 620 | #ifdef DEBUG |
fdf2ec5d KM |
621 | if (dcmdebug & (DDB_INPUT|DDB_SIOERR)) |
622 | printf("dcmreadbuf(%d): err: c%x('%c') s%x\n", | |
623 | unit, stat, c&0xFF, c); | |
60f56dfc KM |
624 | #endif |
625 | if (stat & (RD_BD | RD_FE)) | |
626 | c |= TTY_FE; | |
627 | else if (stat & RD_PE) | |
628 | c |= TTY_PE; | |
629 | else if (stat & RD_OVF) | |
630 | log(LOG_WARNING, | |
fdf2ec5d | 631 | "dcm%d: silo overflow\n", unit); |
60f56dfc KM |
632 | else if (stat & RD_OE) |
633 | log(LOG_WARNING, | |
fdf2ec5d | 634 | "dcm%d: uart overflow\n", unit); |
60f56dfc KM |
635 | } |
636 | (*linesw[tp->t_line].l_rint)(c, tp); | |
637 | } | |
fdf2ec5d KM |
638 | dcmischeme[BOARD(unit)].dis_char += nch; |
639 | #ifdef IOSTATS | |
640 | dsp->rchars += nch; | |
641 | if (nch <= DCMRBSIZE) | |
642 | dsp->rsilo[nch]++; | |
60f56dfc | 643 | else |
fdf2ec5d | 644 | dsp->rsilo[DCMRBSIZE+1]++; |
60f56dfc KM |
645 | #endif |
646 | } | |
647 | ||
fdf2ec5d | 648 | dcmxint(unit, dcm, tp) |
60f56dfc KM |
649 | int unit; |
650 | struct dcmdevice *dcm; | |
60f56dfc | 651 | register struct tty *tp; |
fdf2ec5d | 652 | { |
60f56dfc KM |
653 | tp->t_state &= ~TS_BUSY; |
654 | if (tp->t_state & TS_FLUSH) | |
655 | tp->t_state &= ~TS_FLUSH; | |
2723e1ed | 656 | (*linesw[tp->t_line].l_start)(tp); |
60f56dfc KM |
657 | } |
658 | ||
659 | dcmmint(unit, mcnd, dcm) | |
660 | register int unit; | |
661 | register struct dcmdevice *dcm; | |
662 | int mcnd; | |
663 | { | |
664 | register struct tty *tp; | |
dca9e3a6 | 665 | int delta; |
60f56dfc KM |
666 | |
667 | #ifdef DEBUG | |
fdf2ec5d | 668 | if (dcmdebug & DDB_MODEM) |
22d09b27 | 669 | printf("dcmmint: port %d mcnd %x mcndlast %x\n", |
dca9e3a6 | 670 | unit, mcnd, mcndlast[unit]); |
fdf2ec5d | 671 | #endif |
60f56dfc | 672 | tp = &dcm_tty[unit]; |
dca9e3a6 MH |
673 | delta = mcnd ^ mcndlast[unit]; |
674 | mcndlast[unit] = mcnd; | |
2723e1ed | 675 | if ((delta & MI_CTS) && (tp->t_state & TS_ISOPEN) && |
b323f0e1 | 676 | (tp->t_flags & CCTS_OFLOW)) { |
2723e1ed MK |
677 | if (mcnd & MI_CTS) { |
678 | tp->t_state &= ~TS_TTSTOP; | |
679 | ttstart(tp); | |
680 | } else | |
681 | tp->t_state |= TS_TTSTOP; /* inline dcmstop */ | |
682 | } | |
b323f0e1 | 683 | if (delta & MI_CD) { |
60f56dfc | 684 | if (mcnd & MI_CD) |
dca9e3a6 | 685 | (void)(*linesw[tp->t_line].l_modem)(tp, 1); |
b323f0e1 MK |
686 | else if ((dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0 && |
687 | (*linesw[tp->t_line].l_modem)(tp, 0) == 0) { | |
688 | dcm_modem[unit]->mdmout = MO_OFF; | |
60f56dfc | 689 | SEM_LOCK(dcm); |
22d09b27 | 690 | dcm->dcm_modemchng |= 1<<(unit & 3); |
60f56dfc KM |
691 | dcm->dcm_cr |= CR_MODM; |
692 | SEM_UNLOCK(dcm); | |
fdf2ec5d | 693 | DELAY(10); /* time to change lines */ |
60f56dfc KM |
694 | } |
695 | } | |
696 | } | |
697 | ||
9aceb128 | 698 | dcmioctl(dev, cmd, data, flag, p) |
60f56dfc | 699 | dev_t dev; |
9aceb128 | 700 | int cmd; |
60f56dfc | 701 | caddr_t data; |
9aceb128 KM |
702 | int flag; |
703 | struct proc *p; | |
60f56dfc KM |
704 | { |
705 | register struct tty *tp; | |
706 | register int unit = UNIT(dev); | |
707 | register struct dcmdevice *dcm; | |
708 | register int port; | |
fdf2ec5d | 709 | int error, s; |
60f56dfc KM |
710 | |
711 | #ifdef DEBUG | |
712 | if (dcmdebug & DDB_IOCTL) | |
713 | printf("dcmioctl: unit %d cmd %x data %x flag %x\n", | |
714 | unit, cmd, *data, flag); | |
715 | #endif | |
716 | tp = &dcm_tty[unit]; | |
9aceb128 | 717 | error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); |
60f56dfc KM |
718 | if (error >= 0) |
719 | return (error); | |
720 | error = ttioctl(tp, cmd, data, flag); | |
721 | if (error >= 0) | |
722 | return (error); | |
723 | ||
724 | port = PORT(unit); | |
725 | dcm = dcm_addr[BOARD(unit)]; | |
726 | switch (cmd) { | |
727 | case TIOCSBRK: | |
fdf2ec5d KM |
728 | /* |
729 | * Wait for transmitter buffer to empty | |
730 | */ | |
731 | s = spltty(); | |
732 | while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) | |
733 | DELAY(DCM_USPERCH(tp->t_ospeed)); | |
60f56dfc | 734 | SEM_LOCK(dcm); |
fdf2ec5d KM |
735 | dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; |
736 | dcm->dcm_cr |= (1 << port); /* start break */ | |
60f56dfc | 737 | SEM_UNLOCK(dcm); |
fdf2ec5d | 738 | splx(s); |
60f56dfc KM |
739 | break; |
740 | ||
741 | case TIOCCBRK: | |
60f56dfc | 742 | SEM_LOCK(dcm); |
fdf2ec5d KM |
743 | dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; |
744 | dcm->dcm_cr |= (1 << port); /* end break */ | |
60f56dfc KM |
745 | SEM_UNLOCK(dcm); |
746 | break; | |
747 | ||
748 | case TIOCSDTR: | |
749 | (void) dcmmctl(dev, MO_ON, DMBIS); | |
750 | break; | |
751 | ||
752 | case TIOCCDTR: | |
753 | (void) dcmmctl(dev, MO_ON, DMBIC); | |
754 | break; | |
755 | ||
756 | case TIOCMSET: | |
757 | (void) dcmmctl(dev, *(int *)data, DMSET); | |
758 | break; | |
759 | ||
760 | case TIOCMBIS: | |
761 | (void) dcmmctl(dev, *(int *)data, DMBIS); | |
762 | break; | |
763 | ||
764 | case TIOCMBIC: | |
765 | (void) dcmmctl(dev, *(int *)data, DMBIC); | |
766 | break; | |
767 | ||
768 | case TIOCMGET: | |
769 | *(int *)data = dcmmctl(dev, 0, DMGET); | |
770 | break; | |
771 | ||
772 | default: | |
773 | return (ENOTTY); | |
774 | } | |
775 | return (0); | |
776 | } | |
777 | ||
778 | dcmparam(tp, t) | |
779 | register struct tty *tp; | |
780 | register struct termios *t; | |
781 | { | |
782 | register struct dcmdevice *dcm; | |
fdf2ec5d | 783 | register int port, mode, cflag = t->c_cflag; |
60f56dfc | 784 | int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab); |
fdf2ec5d | 785 | |
60f56dfc KM |
786 | /* check requested parameters */ |
787 | if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) | |
b323f0e1 | 788 | return (EINVAL); |
60f56dfc KM |
789 | /* and copy to tty */ |
790 | tp->t_ispeed = t->c_ispeed; | |
791 | tp->t_ospeed = t->c_ospeed; | |
792 | tp->t_cflag = cflag; | |
60f56dfc | 793 | if (ospeed == 0) { |
fdf2ec5d | 794 | (void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET); |
b323f0e1 | 795 | return (0); |
60f56dfc | 796 | } |
fdf2ec5d KM |
797 | |
798 | mode = 0; | |
60f56dfc KM |
799 | switch (cflag&CSIZE) { |
800 | case CS5: | |
801 | mode = LC_5BITS; break; | |
802 | case CS6: | |
803 | mode = LC_6BITS; break; | |
804 | case CS7: | |
805 | mode = LC_7BITS; break; | |
806 | case CS8: | |
807 | mode = LC_8BITS; break; | |
808 | } | |
809 | if (cflag&PARENB) { | |
810 | if (cflag&PARODD) | |
811 | mode |= LC_PODD; | |
812 | else | |
813 | mode |= LC_PEVEN; | |
814 | } | |
815 | if (cflag&CSTOPB) | |
816 | mode |= LC_2STOP; | |
817 | else | |
818 | mode |= LC_1STOP; | |
819 | #ifdef DEBUG | |
820 | if (dcmdebug & DDB_PARAM) | |
fdf2ec5d KM |
821 | printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n", |
822 | UNIT(tp->t_dev), cflag, mode, tp->t_ospeed, | |
823 | DCM_USPERCH(tp->t_ospeed)); | |
60f56dfc | 824 | #endif |
60f56dfc | 825 | |
fdf2ec5d KM |
826 | port = PORT(tp->t_dev); |
827 | dcm = dcm_addr[BOARD(tp->t_dev)]; | |
828 | /* | |
829 | * Wait for transmitter buffer to empty. | |
830 | */ | |
831 | while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) | |
832 | DELAY(DCM_USPERCH(tp->t_ospeed)); | |
833 | /* | |
834 | * Make changes known to hardware. | |
835 | */ | |
836 | dcm->dcm_data[port].dcm_baud = ospeed; | |
60f56dfc | 837 | dcm->dcm_data[port].dcm_conf = mode; |
60f56dfc | 838 | SEM_LOCK(dcm); |
fdf2ec5d KM |
839 | dcm->dcm_cmdtab[port].dcm_data |= CT_CON; |
840 | dcm->dcm_cr |= (1 << port); | |
60f56dfc | 841 | SEM_UNLOCK(dcm); |
fdf2ec5d | 842 | /* |
2723e1ed | 843 | * Delay for config change to take place. Weighted by baud. |
fdf2ec5d KM |
844 | * XXX why do we do this? |
845 | */ | |
846 | DELAY(16 * DCM_USPERCH(tp->t_ospeed)); | |
b323f0e1 | 847 | return (0); |
60f56dfc KM |
848 | } |
849 | ||
ae20553e | 850 | void |
60f56dfc KM |
851 | dcmstart(tp) |
852 | register struct tty *tp; | |
853 | { | |
854 | register struct dcmdevice *dcm; | |
fdf2ec5d KM |
855 | register struct dcmpreg *pp; |
856 | register struct dcmtfifo *fifo; | |
857 | register char *bp; | |
858 | register unsigned tail, next; | |
859 | register int port, nch; | |
860 | unsigned head; | |
861 | char buf[16]; | |
862 | int s; | |
863 | #ifdef IOSTATS | |
864 | struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)]; | |
865 | int tch = 0; | |
866 | #endif | |
867 | ||
60f56dfc | 868 | s = spltty(); |
fdf2ec5d KM |
869 | #ifdef IOSTATS |
870 | dsp->xints++; | |
871 | #endif | |
60f56dfc KM |
872 | #ifdef DEBUG |
873 | if (dcmdebug & DDB_OUTPUT) | |
fdf2ec5d KM |
874 | printf("dcmstart(%d): state %x flags %x outcc %d\n", |
875 | UNIT(tp->t_dev), tp->t_state, tp->t_flags, | |
876 | tp->t_outq.c_cc); | |
60f56dfc KM |
877 | #endif |
878 | if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) | |
879 | goto out; | |
880 | if (tp->t_outq.c_cc <= tp->t_lowat) { | |
881 | if (tp->t_state&TS_ASLEEP) { | |
882 | tp->t_state &= ~TS_ASLEEP; | |
883 | wakeup((caddr_t)&tp->t_outq); | |
884 | } | |
2d32402a | 885 | selwakeup(&tp->t_wsel); |
60f56dfc | 886 | } |
fdf2ec5d KM |
887 | if (tp->t_outq.c_cc == 0) { |
888 | #ifdef IOSTATS | |
889 | dsp->xempty++; | |
890 | #endif | |
891 | goto out; | |
892 | } | |
893 | ||
894 | dcm = dcm_addr[BOARD(tp->t_dev)]; | |
895 | port = PORT(tp->t_dev); | |
896 | pp = dcm_preg(dcm, port); | |
897 | tail = pp->t_tail & TX_MASK; | |
898 | next = (tail + 1) & TX_MASK; | |
899 | head = pp->t_head & TX_MASK; | |
900 | if (head == next) | |
901 | goto out; | |
902 | fifo = &dcm->dcm_tfifos[3-port][tail]; | |
903 | again: | |
904 | nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK); | |
905 | #ifdef IOSTATS | |
906 | tch += nch; | |
907 | #endif | |
60f56dfc | 908 | #ifdef DEBUG |
fdf2ec5d KM |
909 | if (dcmdebug & DDB_OUTPUT) |
910 | printf("\thead %x tail %x nch %d\n", head, tail, nch); | |
60f56dfc | 911 | #endif |
fdf2ec5d KM |
912 | /* |
913 | * Loop transmitting all the characters we can. | |
914 | */ | |
915 | for (bp = buf; --nch >= 0; bp++) { | |
916 | fifo->data_char = *bp; | |
917 | pp->t_tail = next; | |
918 | /* | |
919 | * If this is the first character, | |
920 | * get the hardware moving right now. | |
921 | */ | |
922 | if (bp == buf) { | |
923 | tp->t_state |= TS_BUSY; | |
924 | SEM_LOCK(dcm); | |
925 | dcm->dcm_cmdtab[port].dcm_data |= CT_TX; | |
926 | dcm->dcm_cr |= (1 << port); | |
927 | SEM_UNLOCK(dcm); | |
928 | } | |
60f56dfc | 929 | tail = next; |
fdf2ec5d KM |
930 | fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0]; |
931 | next = (next + 1) & TX_MASK; | |
60f56dfc | 932 | } |
fdf2ec5d KM |
933 | /* |
934 | * Head changed while we were loading the buffer, | |
935 | * go back and load some more if we can. | |
936 | */ | |
937 | if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) { | |
938 | #ifdef IOSTATS | |
939 | dsp->xrestarts++; | |
940 | #endif | |
941 | head = pp->t_head & TX_MASK; | |
942 | goto again; | |
943 | } | |
2723e1ed | 944 | |
fdf2ec5d KM |
945 | /* |
946 | * Kick it one last time in case it finished while we were | |
dca9e3a6 | 947 | * loading the last bunch. |
fdf2ec5d KM |
948 | */ |
949 | if (bp > &buf[1]) { | |
60f56dfc KM |
950 | tp->t_state |= TS_BUSY; |
951 | SEM_LOCK(dcm); | |
fdf2ec5d KM |
952 | dcm->dcm_cmdtab[port].dcm_data |= CT_TX; |
953 | dcm->dcm_cr |= (1 << port); | |
954 | SEM_UNLOCK(dcm); | |
955 | } | |
60f56dfc KM |
956 | #ifdef DEBUG |
957 | if (dcmdebug & DDB_INTR) | |
dbfb5080 MH |
958 | printf("dcmstart(%d): head %x tail %x outqcc %d\n", |
959 | UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc); | |
60f56dfc | 960 | #endif |
60f56dfc | 961 | out: |
fdf2ec5d KM |
962 | #ifdef IOSTATS |
963 | dsp->xchars += tch; | |
964 | if (tch <= DCMXBSIZE) | |
965 | dsp->xsilo[tch]++; | |
966 | else | |
967 | dsp->xsilo[DCMXBSIZE+1]++; | |
968 | #endif | |
60f56dfc KM |
969 | splx(s); |
970 | } | |
971 | ||
972 | /* | |
973 | * Stop output on a line. | |
974 | */ | |
975 | dcmstop(tp, flag) | |
976 | register struct tty *tp; | |
977 | { | |
978 | int s; | |
979 | ||
980 | s = spltty(); | |
981 | if (tp->t_state & TS_BUSY) { | |
fdf2ec5d | 982 | /* XXX is there some way to safely stop transmission? */ |
dca9e3a6 | 983 | if ((tp->t_state&TS_TTSTOP) == 0) |
60f56dfc KM |
984 | tp->t_state |= TS_FLUSH; |
985 | } | |
986 | splx(s); | |
987 | } | |
988 | ||
22d09b27 KM |
989 | /* |
990 | * Modem control | |
991 | */ | |
60f56dfc KM |
992 | dcmmctl(dev, bits, how) |
993 | dev_t dev; | |
994 | int bits, how; | |
995 | { | |
996 | register struct dcmdevice *dcm; | |
b323f0e1 | 997 | int s, unit, brd, hit = 0; |
60f56dfc | 998 | |
22d09b27 KM |
999 | unit = UNIT(dev); |
1000 | #ifdef DEBUG | |
1001 | if (dcmdebug & DDB_MODEM) | |
1002 | printf("dcmmctl(%d) unit %d bits 0x%x how %x\n", | |
1003 | BOARD(unit), unit, bits, how); | |
1004 | #endif | |
60f56dfc | 1005 | |
b323f0e1 MK |
1006 | brd = BOARD(unit); |
1007 | dcm = dcm_addr[brd]; | |
60f56dfc KM |
1008 | s = spltty(); |
1009 | switch (how) { | |
1010 | ||
1011 | case DMSET: | |
22d09b27 | 1012 | dcm_modem[unit]->mdmout = bits; |
60f56dfc KM |
1013 | hit++; |
1014 | break; | |
1015 | ||
1016 | case DMBIS: | |
22d09b27 | 1017 | dcm_modem[unit]->mdmout |= bits; |
60f56dfc KM |
1018 | hit++; |
1019 | break; | |
1020 | ||
1021 | case DMBIC: | |
22d09b27 | 1022 | dcm_modem[unit]->mdmout &= ~bits; |
60f56dfc KM |
1023 | hit++; |
1024 | break; | |
1025 | ||
1026 | case DMGET: | |
22d09b27 | 1027 | bits = dcm_modem[unit]->mdmin; |
b323f0e1 MK |
1028 | if (dcmsoftCAR[brd] & FLAG_STDDCE) |
1029 | bits = hp2dce_in(bits); | |
60f56dfc KM |
1030 | break; |
1031 | } | |
1032 | if (hit) { | |
1033 | SEM_LOCK(dcm); | |
22d09b27 | 1034 | dcm->dcm_modemchng |= 1<<(unit & 3); |
60f56dfc KM |
1035 | dcm->dcm_cr |= CR_MODM; |
1036 | SEM_UNLOCK(dcm); | |
fdf2ec5d | 1037 | DELAY(10); /* delay until done */ |
60f56dfc KM |
1038 | (void) splx(s); |
1039 | } | |
b323f0e1 | 1040 | return (bits); |
60f56dfc KM |
1041 | } |
1042 | ||
fdf2ec5d KM |
1043 | /* |
1044 | * Set board to either interrupt per-character or at a fixed interval. | |
1045 | */ | |
1046 | dcmsetischeme(brd, flags) | |
1047 | int brd, flags; | |
60f56dfc | 1048 | { |
fdf2ec5d KM |
1049 | register struct dcmdevice *dcm = dcm_addr[brd]; |
1050 | register struct dcmischeme *dis = &dcmischeme[brd]; | |
60f56dfc | 1051 | register int i; |
fdf2ec5d KM |
1052 | u_char mask; |
1053 | int perchar = flags & DIS_PERCHAR; | |
60f56dfc KM |
1054 | |
1055 | #ifdef DEBUG | |
fdf2ec5d KM |
1056 | if (dcmdebug & DDB_INTSCHM) |
1057 | printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n", | |
1058 | brd, perchar, dis->dis_perchar, | |
1059 | dis->dis_intr, dis->dis_char); | |
1060 | if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) { | |
1061 | printf("dcmsetischeme(%d): redundent request %d\n", | |
1062 | brd, perchar); | |
60f56dfc KM |
1063 | return; |
1064 | } | |
fdf2ec5d KM |
1065 | #endif |
1066 | /* | |
1067 | * If perchar is non-zero, we enable interrupts on all characters | |
1068 | * otherwise we disable perchar interrupts and use periodic | |
1069 | * polling interrupts. | |
1070 | */ | |
1071 | dis->dis_perchar = perchar; | |
1072 | mask = perchar ? 0xf : 0x0; | |
1073 | for (i = 0; i < 256; i++) | |
1074 | dcm->dcm_bmap[i].data_data = mask; | |
1075 | /* | |
1076 | * Don't slow down tandem mode, interrupt on flow control | |
1077 | * chars for any port on the board. | |
1078 | */ | |
1079 | if (!perchar) { | |
1080 | register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)]; | |
1081 | int c; | |
1082 | ||
1083 | for (i = 0; i < 4; i++, tp++) { | |
1084 | if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE) | |
1085 | dcm->dcm_bmap[c].data_data |= (1 << i); | |
1086 | if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE) | |
1087 | dcm->dcm_bmap[c].data_data |= (1 << i); | |
1088 | } | |
60f56dfc | 1089 | } |
fdf2ec5d KM |
1090 | /* |
1091 | * Board starts with timer disabled so if first call is to | |
1092 | * set perchar mode then we don't want to toggle the timer. | |
1093 | */ | |
1094 | if (flags == (DIS_RESET|DIS_PERCHAR)) | |
1095 | return; | |
1096 | /* | |
1097 | * Toggle card 16.7ms interrupts (we first make sure that card | |
1098 | * has cleared the bit so it will see the toggle). | |
1099 | */ | |
1100 | while (dcm->dcm_cr & CR_TIMER) | |
1101 | ; | |
60f56dfc | 1102 | SEM_LOCK(dcm); |
fdf2ec5d | 1103 | dcm->dcm_cr |= CR_TIMER; |
60f56dfc KM |
1104 | SEM_UNLOCK(dcm); |
1105 | } | |
1106 | ||
60f56dfc KM |
1107 | /* |
1108 | * Following are all routines needed for DCM to act as console | |
1109 | */ | |
9acfa6cd | 1110 | #include "hp/dev/cons.h" |
60f56dfc | 1111 | |
fdf2ec5d KM |
1112 | dcmcnprobe(cp) |
1113 | struct consdev *cp; | |
60f56dfc | 1114 | { |
fdf2ec5d | 1115 | register struct hp_hw *hw; |
2723e1ed MK |
1116 | int unit; |
1117 | ||
1118 | /* locate the major number */ | |
1119 | for (dcmmajor = 0; dcmmajor < nchrdev; dcmmajor++) | |
1120 | if (cdevsw[dcmmajor].d_open == dcmopen) | |
1121 | break; | |
fdf2ec5d KM |
1122 | |
1123 | /* | |
1124 | * Implicitly assigns the lowest select code DCM card found to be | |
1125 | * logical unit 0 (actually CONUNIT). If your config file does | |
1126 | * anything different, you're screwed. | |
1127 | */ | |
1128 | for (hw = sc_table; hw->hw_type; hw++) | |
19a031b5 | 1129 | if (HW_ISDEV(hw, D_COMMDCM) && !badaddr((short *)hw->hw_kva)) |
fdf2ec5d | 1130 | break; |
19a031b5 | 1131 | if (!HW_ISDEV(hw, D_COMMDCM)) { |
fdf2ec5d KM |
1132 | cp->cn_pri = CN_DEAD; |
1133 | return; | |
1134 | } | |
1135 | unit = CONUNIT; | |
19a031b5 | 1136 | dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_kva; |
fdf2ec5d | 1137 | |
fdf2ec5d | 1138 | /* initialize required fields */ |
2723e1ed | 1139 | cp->cn_dev = makedev(dcmmajor, unit); |
fdf2ec5d KM |
1140 | cp->cn_tp = &dcm_tty[unit]; |
1141 | switch (dcm_addr[BOARD(unit)]->dcm_rsid) { | |
1142 | case DCMID: | |
1143 | cp->cn_pri = CN_NORMAL; | |
1144 | break; | |
1145 | case DCMID|DCMCON: | |
1146 | cp->cn_pri = CN_REMOTE; | |
1147 | break; | |
1148 | default: | |
1149 | cp->cn_pri = CN_DEAD; | |
2723e1ed | 1150 | return; |
fdf2ec5d | 1151 | } |
2723e1ed MK |
1152 | /* |
1153 | * If dcmconsole is initialized, raise our priority. | |
1154 | */ | |
1155 | if (dcmconsole == UNIT(unit)) | |
1156 | cp->cn_pri = CN_REMOTE; | |
19a031b5 | 1157 | #ifdef KGDB_CHEAT |
2723e1ed MK |
1158 | /* |
1159 | * This doesn't currently work, at least not with ite consoles; | |
1160 | * the console hasn't been initialized yet. | |
1161 | */ | |
1162 | if (major(kgdb_dev) == dcmmajor && BOARD(kgdb_dev) == BOARD(unit)) { | |
1163 | (void) dcminit(kgdb_dev, kgdb_rate); | |
1164 | if (kgdb_debug_init) { | |
1165 | /* | |
1166 | * We assume that console is ready for us... | |
1167 | * this assumes that a dca or ite console | |
1168 | * has been selected already and will init | |
1169 | * on the first putc. | |
1170 | */ | |
1171 | printf("dcm%d: ", UNIT(kgdb_dev)); | |
1172 | kgdb_connect(1); | |
1173 | } | |
1174 | } | |
1175 | #endif | |
fdf2ec5d KM |
1176 | } |
1177 | ||
1178 | dcmcninit(cp) | |
1179 | struct consdev *cp; | |
1180 | { | |
1181 | dcminit(cp->cn_dev, dcmdefaultrate); | |
2723e1ed | 1182 | dcmconsinit = 1; |
fdf2ec5d KM |
1183 | dcmconsole = UNIT(cp->cn_dev); |
1184 | } | |
60f56dfc | 1185 | |
fdf2ec5d KM |
1186 | dcminit(dev, rate) |
1187 | dev_t dev; | |
1188 | int rate; | |
1189 | { | |
1190 | register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; | |
1191 | int s, mode, port; | |
60f56dfc | 1192 | |
fdf2ec5d KM |
1193 | port = PORT(dev); |
1194 | mode = LC_8BITS | LC_1STOP; | |
60f56dfc | 1195 | s = splhigh(); |
fdf2ec5d KM |
1196 | /* |
1197 | * Wait for transmitter buffer to empty. | |
1198 | */ | |
1199 | while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) | |
1200 | DELAY(DCM_USPERCH(rate)); | |
1201 | /* | |
1202 | * Make changes known to hardware. | |
1203 | */ | |
1204 | dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab); | |
1205 | dcm->dcm_data[port].dcm_conf = mode; | |
1206 | SEM_LOCK(dcm); | |
1207 | dcm->dcm_cmdtab[port].dcm_data |= CT_CON; | |
1208 | dcm->dcm_cr |= (1 << port); | |
1209 | SEM_UNLOCK(dcm); | |
1210 | /* | |
2723e1ed | 1211 | * Delay for config change to take place. Weighted by baud. |
fdf2ec5d KM |
1212 | * XXX why do we do this? |
1213 | */ | |
1214 | DELAY(16 * DCM_USPERCH(rate)); | |
60f56dfc | 1215 | splx(s); |
60f56dfc KM |
1216 | } |
1217 | ||
1218 | dcmcngetc(dev) | |
fdf2ec5d | 1219 | dev_t dev; |
60f56dfc | 1220 | { |
fdf2ec5d KM |
1221 | register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; |
1222 | register struct dcmrfifo *fifo; | |
1223 | register struct dcmpreg *pp; | |
1224 | register unsigned head; | |
1225 | int s, c, stat, port; | |
1226 | ||
1227 | port = PORT(dev); | |
1228 | pp = dcm_preg(dcm, port); | |
1229 | s = splhigh(); | |
1230 | head = pp->r_head & RX_MASK; | |
1231 | fifo = &dcm->dcm_rfifos[3-port][head>>1]; | |
1232 | while (head == (pp->r_tail & RX_MASK)) | |
1233 | ; | |
1234 | /* | |
1235 | * If board interrupts are enabled, just let our received char | |
1236 | * interrupt through in case some other port on the board was | |
1237 | * busy. Otherwise we must clear the interrupt. | |
1238 | */ | |
1239 | SEM_LOCK(dcm); | |
1240 | if ((dcm->dcm_ic & IC_IE) == 0) | |
1241 | stat = dcm->dcm_iir; | |
1242 | SEM_UNLOCK(dcm); | |
1243 | c = fifo->data_char; | |
1244 | stat = fifo->data_stat; | |
1245 | pp->r_head = (head + 2) & RX_MASK; | |
1246 | splx(s); | |
b323f0e1 | 1247 | return (c); |
60f56dfc KM |
1248 | } |
1249 | ||
1250 | /* | |
1251 | * Console kernel output character routine. | |
1252 | */ | |
1253 | dcmcnputc(dev, c) | |
1254 | dev_t dev; | |
fdf2ec5d | 1255 | int c; |
60f56dfc KM |
1256 | { |
1257 | register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; | |
fdf2ec5d KM |
1258 | register struct dcmpreg *pp; |
1259 | unsigned tail; | |
1260 | int s, port, stat; | |
60f56dfc | 1261 | |
fdf2ec5d KM |
1262 | port = PORT(dev); |
1263 | pp = dcm_preg(dcm, port); | |
1264 | s = splhigh(); | |
1265 | #ifdef KGDB | |
1266 | if (dev != kgdb_dev) | |
1267 | #endif | |
2723e1ed | 1268 | if (dcmconsinit == 0) { |
fdf2ec5d | 1269 | (void) dcminit(dev, dcmdefaultrate); |
2723e1ed | 1270 | dcmconsinit = 1; |
fdf2ec5d KM |
1271 | } |
1272 | tail = pp->t_tail & TX_MASK; | |
1273 | while (tail != (pp->t_head & TX_MASK)) | |
1274 | ; | |
1275 | dcm->dcm_tfifos[3-port][tail].data_char = c; | |
1276 | pp->t_tail = tail = (tail + 1) & TX_MASK; | |
60f56dfc | 1277 | SEM_LOCK(dcm); |
fdf2ec5d KM |
1278 | dcm->dcm_cmdtab[port].dcm_data |= CT_TX; |
1279 | dcm->dcm_cr |= (1 << port); | |
60f56dfc | 1280 | SEM_UNLOCK(dcm); |
fdf2ec5d KM |
1281 | while (tail != (pp->t_head & TX_MASK)) |
1282 | ; | |
1283 | /* | |
1284 | * If board interrupts are enabled, just let our completion | |
1285 | * interrupt through in case some other port on the board | |
1286 | * was busy. Otherwise we must clear the interrupt. | |
1287 | */ | |
1288 | if ((dcm->dcm_ic & IC_IE) == 0) { | |
1289 | SEM_LOCK(dcm); | |
1290 | stat = dcm->dcm_iir; | |
1291 | SEM_UNLOCK(dcm); | |
1292 | } | |
60f56dfc KM |
1293 | splx(s); |
1294 | } | |
1295 | #endif |