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