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