xref: /original-bsd/sys/i386/isa/com.c (revision ba762ddc)
1 /*-
2  * Copyright (c) 1982, 1986, 1990, 1991 The Regents of the University of
3  * California. All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * the University of Utah and William Jolitz.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)com.c	7.1 (Berkeley) 04/03/91
11  */
12 
13 #include "dca.h"
14 #if NDCA > 0
15 /*
16  * COM driver, from hp300 dca.c 98626/98644/internal serial interface
17  */
18 #include "sys/param.h"
19 #include "sys/systm.h"
20 #include "sys/ioctl.h"
21 #include "sys/tty.h"
22 #include "sys/user.h"
23 #include "sys/conf.h"
24 #include "sys/file.h"
25 #include "sys/uio.h"
26 #include "sys/kernel.h"
27 #include "sys/syslog.h"
28 
29 #include "i386/isa/isa_device.h"
30 #include "i386/isa/comreg.h"
31 #include "i386/isa/ic/ns16450.h"
32 
33 int 	comprobe(), comattach(), comintr(), comstart(), comparam();
34 
35 struct	isa_driver comdriver = {
36 	comprobe, comattach, "com"
37 };
38 
39 int	comsoftCAR;
40 int	com_active;
41 int	ncom = NDCA;
42 int	comconsole = -1;
43 int	comdefaultrate = TTYDEF_SPEED;
44 short com_addr[NDCA];
45 struct	tty com_tty[NDCA];
46 
47 struct speedtab comspeedtab[] = {
48 	0,	0,
49 	50,	COMBRD(50),
50 	75,	COMBRD(75),
51 	110,	COMBRD(110),
52 	134,	COMBRD(134),
53 	150,	COMBRD(150),
54 	200,	COMBRD(200),
55 	300,	COMBRD(300),
56 	600,	COMBRD(600),
57 	1200,	COMBRD(1200),
58 	1800,	COMBRD(1800),
59 	2400,	COMBRD(2400),
60 	4800,	COMBRD(4800),
61 	9600,	COMBRD(9600),
62 	19200,	COMBRD(19200),
63 	38400,	COMBRD(38400),
64 	57600,	COMBRD(57600),
65 	-1,	-1
66 };
67 
68 extern	struct tty *constty;
69 #ifdef KGDB
70 extern int kgdb_dev;
71 extern int kgdb_rate;
72 extern int kgdb_debug_init;
73 #endif
74 
75 #define	UNIT(x)		minor(x)
76 
77 comprobe(dev)
78 struct isa_device *dev;
79 {
80 	if ((inb(dev->id_iobase+com_iir) & 0xf8) == 0)
81 		return(1);
82 	return(0);
83 
84 }
85 
86 
87 int
88 comattach(isdp)
89 struct isa_device *isdp;
90 {
91 	struct	tty	*tp;
92 	u_char		unit;
93 	int		port = isdp->id_iobase;
94 
95 	if (unit == comconsole)
96 		DELAY(100000);
97 	unit = isdp->id_unit;
98 	com_addr[unit] = port;
99 	com_active |= 1 << unit;
100 	/* comsoftCAR = isdp->id_flags; */
101 	outb(port+com_ier, 0);
102 	outb(port+com_mcr, 0 | MCR_IENABLE);
103 #ifdef KGDB
104 	if (kgdb_dev == makedev(1, unit)) {
105 		if (comconsole == unit)
106 			kgdb_dev = -1;	/* can't debug over console port */
107 		else {
108 			(void) cominit(unit);
109 			comconsole = -2; /* XXX */
110 			if (kgdb_debug_init) {
111 				printf("com%d: kgdb waiting...", unit);
112 				/* trap into kgdb */
113 				asm("trap #15;");
114 				printf("connected.\n");
115 			} else
116 				printf("com%d: kgdb enabled\n", unit);
117 		}
118 	}
119 #endif
120 	/*
121 	 * Need to reset baud rate, etc. of next print so reset comconsole.
122 	 * Also make sure console is always "hardwired"
123 	 */
124 	if (unit == comconsole) {
125 		comconsole = -1;
126 		comsoftCAR |= (1 << unit);
127 	}
128 comsoftCAR |= (1 << unit);
129 	return (1);
130 }
131 
132 comopen(dev, flag)
133 	dev_t dev;
134 {
135 	register struct tty *tp;
136 	register int unit;
137 	int error = 0;
138 
139 	unit = UNIT(dev);
140 	if (unit >= NDCA || (com_active & (1 << unit)) == 0)
141 		return (ENXIO);
142 	tp = &com_tty[unit];
143 	tp->t_oproc = comstart;
144 	tp->t_param = comparam;
145 	tp->t_dev = dev;
146 	if ((tp->t_state & TS_ISOPEN) == 0) {
147 		tp->t_state |= TS_WOPEN;
148 		ttychars(tp);
149 		tp->t_iflag = TTYDEF_IFLAG;
150 		tp->t_oflag = TTYDEF_OFLAG;
151 		tp->t_cflag = TTYDEF_CFLAG;
152 		tp->t_lflag = TTYDEF_LFLAG;
153 		tp->t_ispeed = tp->t_ospeed = comdefaultrate;
154 		comparam(tp, &tp->t_termios);
155 		ttsetwater(tp);
156 	} else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
157 		return (EBUSY);
158 	(void) spltty();
159 	(void) commctl(dev, MCR_DTR | MCR_RTS, DMSET);
160 	if ((comsoftCAR & (1 << unit)) || (commctl(dev, 0, DMGET) & MSR_DCD))
161 		tp->t_state |= TS_CARR_ON;
162 	while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
163 	       (tp->t_state & TS_CARR_ON) == 0) {
164 		tp->t_state |= TS_WOPEN;
165 		if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
166 		    ttopen, 0))
167 			break;
168 	}
169 	(void) spl0();
170 	if (error == 0)
171 		error = (*linesw[tp->t_line].l_open)(dev, tp);
172 	return (error);
173 }
174 
175 /*ARGSUSED*/
176 comclose(dev, flag)
177 	dev_t dev;
178 {
179 	register struct tty *tp;
180 	register com;
181 	register int unit;
182 
183 	unit = UNIT(dev);
184 	com = com_addr[unit];
185 	tp = &com_tty[unit];
186 	(*linesw[tp->t_line].l_close)(tp);
187 	outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK);
188 #ifdef KGDB
189 	/* do not disable interrupts if debugging */
190 	if (kgdb_dev != makedev(1, unit))
191 #endif
192 	outb(com+com_ier, 0);
193 	if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
194 	    (tp->t_state&TS_ISOPEN) == 0)
195 		(void) commctl(dev, 0, DMSET);
196 	ttyclose(tp);
197 	return(0);
198 }
199 
200 comread(dev, uio, flag)
201 	dev_t dev;
202 	struct uio *uio;
203 {
204 	register struct tty *tp = &com_tty[UNIT(dev)];
205 
206 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
207 }
208 
209 comwrite(dev, uio, flag)
210 	dev_t dev;
211 	struct uio *uio;
212 {
213 	int unit = UNIT(dev);
214 	register struct tty *tp = &com_tty[unit];
215 
216 #ifdef notyet
217 	/*
218 	 * (XXX) We disallow virtual consoles if the physical console is
219 	 * a serial port.  This is in case there is a display attached that
220 	 * is not the console.  In that situation we don't need/want the X
221 	 * server taking over the console.
222 	 */
223 	if (constty && unit == comconsole)
224 		constty = NULL;
225 #endif
226 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
227 }
228 
229 comintr(unit)
230 	register int unit;
231 {
232 	register com;
233 	register u_char code;
234 	register struct tty *tp;
235 
236 	com = com_addr[unit];
237 	while (1) {
238 		code = inb(com+com_iir);
239 		switch (code) {
240 		case IIR_NOPEND:
241 			return (1);
242 		case IIR_RXRDY:
243 			/* do time-critical read in-line */
244 			tp = &com_tty[unit];
245 			code = inb(com+com_data);
246 			if ((tp->t_state & TS_ISOPEN) == 0) {
247 #ifdef KGDB
248 				if (kgdb_dev == makedev(1, unit) &&
249 				    code == '!') {
250 					printf("kgdb trap from com%d\n", unit);
251 					/* trap into kgdb */
252 					asm("trap #15;");
253 				}
254 #endif
255 			} else
256 				(*linesw[tp->t_line].l_rint)(code, tp);
257 			break;
258 		case IIR_TXRDY:
259 			tp = &com_tty[unit];
260 			tp->t_state &=~ (TS_BUSY|TS_FLUSH);
261 			if (tp->t_line)
262 				(*linesw[tp->t_line].l_start)(tp);
263 			else
264 				comstart(tp);
265 			break;
266 		case IIR_RLS:
267 			comeint(unit, com);
268 			break;
269 		default:
270 			if (code & IIR_NOPEND)
271 				return (1);
272 			log(LOG_WARNING, "com%d: weird interrupt: 0x%x\n",
273 			    unit, code);
274 			/* fall through */
275 		case IIR_MLSC:
276 			commint(unit, com);
277 			break;
278 		}
279 	}
280 }
281 
282 comeint(unit, com)
283 	register int unit;
284 	register com;
285 {
286 	register struct tty *tp;
287 	register int stat, c;
288 
289 	tp = &com_tty[unit];
290 	stat = inb(com+com_lsr);
291 	c = inb(com+com_data);
292 	if ((tp->t_state & TS_ISOPEN) == 0) {
293 #ifdef KGDB
294 		/* we don't care about parity errors */
295 		if (((stat & (LSR_BI|LSR_FE|LSR_PE)) == LSR_PE) &&
296 		    kgdb_dev == makedev(1, unit) && c == '!') {
297 			printf("kgdb trap from com%d\n", unit);
298 			/* trap into kgdb */
299 			asm("trap #15;");
300 		}
301 #endif
302 		return;
303 	}
304 	if (stat & (LSR_BI | LSR_FE))
305 		c |= TTY_FE;
306 	else if (stat & LSR_PE)
307 		c |= TTY_PE;
308 	else if (stat & LSR_OE)
309 		log(LOG_WARNING, "com%d: silo overflow\n", unit);
310 	(*linesw[tp->t_line].l_rint)(c, tp);
311 }
312 
313 commint(unit, com)
314 	register int unit;
315 	register com;
316 {
317 	register struct tty *tp;
318 	register int stat;
319 
320 	tp = &com_tty[unit];
321 	stat = inb(com+com_msr);
322 	if ((stat & MSR_DDCD) && (comsoftCAR & (1 << unit)) == 0) {
323 		if (stat & MSR_DCD)
324 			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
325 		else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
326 			outb(com+com_mcr,
327 				inb(com+com_mcr) & ~(MCR_DTR | MCR_RTS) | MCR_IENABLE);
328 	} else if ((stat & MSR_DCTS) && (tp->t_state & TS_ISOPEN) &&
329 		   (tp->t_flags & CRTSCTS)) {
330 		/* the line is up and we want to do rts/cts flow control */
331 		if (stat & MSR_CTS) {
332 			tp->t_state &=~ TS_TTSTOP;
333 			ttstart(tp);
334 		} else
335 			tp->t_state |= TS_TTSTOP;
336 	}
337 }
338 
339 comioctl(dev, cmd, data, flag)
340 	dev_t dev;
341 	caddr_t data;
342 {
343 	register struct tty *tp;
344 	register int unit = UNIT(dev);
345 	register com;
346 	register int error;
347 
348 	tp = &com_tty[unit];
349 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
350 	if (error >= 0)
351 		return (error);
352 	error = ttioctl(tp, cmd, data, flag);
353 	if (error >= 0)
354 		return (error);
355 
356 	com = com_addr[unit];
357 	switch (cmd) {
358 
359 	case TIOCSBRK:
360 		outb(com+com_cfcr, inb(com+com_cfcr) | CFCR_SBREAK);
361 		break;
362 
363 	case TIOCCBRK:
364 		outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK);
365 		break;
366 
367 	case TIOCSDTR:
368 		(void) commctl(dev, MCR_DTR | MCR_RTS, DMBIS);
369 		break;
370 
371 	case TIOCCDTR:
372 		(void) commctl(dev, MCR_DTR | MCR_RTS, DMBIC);
373 		break;
374 
375 	case TIOCMSET:
376 		(void) commctl(dev, *(int *)data, DMSET);
377 		break;
378 
379 	case TIOCMBIS:
380 		(void) commctl(dev, *(int *)data, DMBIS);
381 		break;
382 
383 	case TIOCMBIC:
384 		(void) commctl(dev, *(int *)data, DMBIC);
385 		break;
386 
387 	case TIOCMGET:
388 		*(int *)data = commctl(dev, 0, DMGET);
389 		break;
390 
391 	default:
392 		return (ENOTTY);
393 	}
394 	return (0);
395 }
396 
397 comparam(tp, t)
398 	register struct tty *tp;
399 	register struct termios *t;
400 {
401 	register com;
402 	register int cfcr, cflag = t->c_cflag;
403 	int unit = UNIT(tp->t_dev);
404 	int ospeed = ttspeedtab(t->c_ospeed, comspeedtab);
405 
406 	/* check requested parameters */
407         if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
408                 return(EINVAL);
409         /* and copy to tty */
410         tp->t_ispeed = t->c_ispeed;
411         tp->t_ospeed = t->c_ospeed;
412         tp->t_cflag = cflag;
413 
414 	com = com_addr[unit];
415 	outb(com+com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS /*| IER_EMSC*/);
416 	if (ospeed == 0) {
417 		(void) commctl(unit, 0, DMSET);	/* hang up line */
418 		return(0);
419 	}
420 	outb(com+com_cfcr, inb(com+com_cfcr) | CFCR_DLAB);
421 	outb(com+com_data, ospeed & 0xFF);
422 	outb(com+com_ier, ospeed >> 8);
423 	switch (cflag&CSIZE) {
424 	case CS5:
425 		cfcr = CFCR_5BITS; break;
426 	case CS6:
427 		cfcr = CFCR_6BITS; break;
428 	case CS7:
429 		cfcr = CFCR_7BITS; break;
430 	case CS8:
431 		cfcr = CFCR_8BITS; break;
432 	}
433 	if (cflag&PARENB) {
434 		cfcr |= CFCR_PENAB;
435 		if ((cflag&PARODD) == 0)
436 			cfcr |= CFCR_PEVEN;
437 	}
438 	if (cflag&CSTOPB)
439 		cfcr |= CFCR_STOPB;
440 	outb(com+com_cfcr, cfcr);
441 	return(0);
442 }
443 
444 comstart(tp)
445 	register struct tty *tp;
446 {
447 	register com;
448 	int s, unit, c;
449 
450 	unit = UNIT(tp->t_dev);
451 	com = com_addr[unit];
452 	s = spltty();
453 	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
454 		goto out;
455 	if (tp->t_outq.c_cc <= tp->t_lowat) {
456 		if (tp->t_state&TS_ASLEEP) {
457 			tp->t_state &= ~TS_ASLEEP;
458 			wakeup((caddr_t)&tp->t_outq);
459 		}
460 		if (tp->t_wsel) {
461 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
462 			tp->t_wsel = 0;
463 			tp->t_state &= ~TS_WCOLL;
464 		}
465 	}
466 	if (tp->t_outq.c_cc == 0)
467 		goto out;
468 	if (inb(com+com_lsr) & LSR_TXRDY) {
469 		c = getc(&tp->t_outq);
470 		tp->t_state |= TS_BUSY;
471 		outb(com+com_data, c);
472 	}
473 out:
474 	splx(s);
475 }
476 
477 /*
478  * Stop output on a line.
479  */
480 /*ARGSUSED*/
481 comstop(tp, flag)
482 	register struct tty *tp;
483 {
484 	register int s;
485 
486 	s = spltty();
487 	if (tp->t_state & TS_BUSY) {
488 		if ((tp->t_state&TS_TTSTOP)==0)
489 			tp->t_state |= TS_FLUSH;
490 	}
491 	splx(s);
492 }
493 
494 commctl(dev, bits, how)
495 	dev_t dev;
496 	int bits, how;
497 {
498 	register com;
499 	register int unit;
500 	int s;
501 
502 	unit = UNIT(dev);
503 	com = com_addr[unit];
504 	s = spltty();
505 	switch (how) {
506 
507 	case DMSET:
508 		outb(com+com_mcr, bits | MCR_IENABLE);
509 		break;
510 
511 	case DMBIS:
512 		outb(com+com_mcr, inb(com+com_mcr) | bits | MCR_IENABLE);
513 		break;
514 
515 	case DMBIC:
516 		outb(com+com_mcr, inb(com+com_mcr) & ~bits | MCR_IENABLE);
517 		break;
518 
519 	case DMGET:
520 		bits = inb(com+com_msr);
521 		break;
522 	}
523 	(void) splx(s);
524 	return(bits);
525 }
526 #endif
527