name changed to allow support of virtual console code, other
[unix-history] / usr / src / sys / i386 / isa / com.c
CommitLineData
3bf20cd8 1/*-
54d5ccc6
WN
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
3bf20cd8
WN
4 *
5 * %sccs.include.redist.c%
6 *
54d5ccc6 7 * @(#)com.c 7.3 (Berkeley) %G%
3bf20cd8
WN
8 */
9
17441d36
WN
10#include "com.h"
11#if NCOM > 0
3bf20cd8 12/*
54d5ccc6
WN
13 * COM driver, based on HP dca driver
14 * uses National Semiconductor NS16450/NS16550AF UART
3bf20cd8 15 */
54d5ccc6
WN
16#include "param.h"
17#include "systm.h"
18#include "ioctl.h"
19#include "tty.h"
20#include "proc.h"
21#include "user.h"
22#include "conf.h"
23#include "file.h"
24#include "uio.h"
25#include "kernel.h"
26#include "syslog.h"
3bf20cd8
WN
27
28#include "i386/isa/isa_device.h"
29#include "i386/isa/comreg.h"
54d5ccc6 30#include "i386/isa/ic/ns16550.h"
3bf20cd8
WN
31
32int comprobe(), comattach(), comintr(), comstart(), comparam();
33
34struct isa_driver comdriver = {
35 comprobe, comattach, "com"
36};
37
38int comsoftCAR;
39int com_active;
54d5ccc6 40int com_hasfifo;
17441d36 41int ncom = NCOM;
54d5ccc6
WN
42#ifdef COMCONSOLE
43int comconsole = COMCONSOLE;
44#else
3bf20cd8 45int comconsole = -1;
54d5ccc6
WN
46#endif
47int comconsinit;
3bf20cd8 48int comdefaultrate = TTYDEF_SPEED;
54d5ccc6 49int commajor;
17441d36
WN
50short com_addr[NCOM];
51struct tty com_tty[NCOM];
3bf20cd8
WN
52
53struct speedtab comspeedtab[] = {
54 0, 0,
55 50, COMBRD(50),
56 75, COMBRD(75),
57 110, COMBRD(110),
58 134, COMBRD(134),
59 150, COMBRD(150),
60 200, COMBRD(200),
61 300, COMBRD(300),
62 600, COMBRD(600),
63 1200, COMBRD(1200),
64 1800, COMBRD(1800),
65 2400, COMBRD(2400),
66 4800, COMBRD(4800),
67 9600, COMBRD(9600),
68 19200, COMBRD(19200),
69 38400, COMBRD(38400),
70 57600, COMBRD(57600),
71 -1, -1
72};
73
74extern struct tty *constty;
75#ifdef KGDB
54d5ccc6
WN
76#include "machine/remote-sl.h"
77
3bf20cd8
WN
78extern int kgdb_dev;
79extern int kgdb_rate;
80extern int kgdb_debug_init;
81#endif
82
83#define UNIT(x) minor(x)
84
85comprobe(dev)
86struct isa_device *dev;
87{
54d5ccc6 88 /*if ((inb(dev->id_iobase+com_iir) & 0x38) == 0)
3bf20cd8 89 return(1);
54d5ccc6
WN
90printf("base %x val %x ", dev->id_iobase,
91 inb(dev->id_iobase+com_iir));*/
92 return(1);
3bf20cd8
WN
93
94}
95
96
97int
98comattach(isdp)
99struct isa_device *isdp;
100{
101 struct tty *tp;
102 u_char unit;
103 int port = isdp->id_iobase;
104
54d5ccc6 105 unit = isdp->id_unit;
3bf20cd8
WN
106 if (unit == comconsole)
107 DELAY(100000);
3bf20cd8
WN
108 com_addr[unit] = port;
109 com_active |= 1 << unit;
54d5ccc6
WN
110 comsoftCAR |= 1 << unit; /* XXX */
111
112 /* look for a NS 16550AF UART with FIFOs */
113 outb(port+com_fifo, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_14);
114 DELAY(100);
115 if ((inb(port+com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK)
116 com_hasfifo |= 1 << unit;
117
3bf20cd8
WN
118 outb(port+com_ier, 0);
119 outb(port+com_mcr, 0 | MCR_IENABLE);
120#ifdef KGDB
54d5ccc6 121 if (1/*kgdb_dev == makedev(commajor, unit)*/) {
3bf20cd8
WN
122 if (comconsole == unit)
123 kgdb_dev = -1; /* can't debug over console port */
124 else {
54d5ccc6 125 (void) cominit(unit, kgdb_rate);
3bf20cd8 126 if (kgdb_debug_init) {
54d5ccc6
WN
127 /*
128 * Print prefix of device name,
129 * let kgdb_connect print the rest.
130 */
131 printf("com%d: ", unit);
132 kgdb_connect(1);
3bf20cd8
WN
133 } else
134 printf("com%d: kgdb enabled\n", unit);
135 }
136 }
137#endif
138 /*
54d5ccc6 139 * Need to reset baud rate, etc. of next print so reset comconsinit.
3bf20cd8
WN
140 * Also make sure console is always "hardwired"
141 */
142 if (unit == comconsole) {
54d5ccc6 143 comconsinit = 0;
3bf20cd8
WN
144 comsoftCAR |= (1 << unit);
145 }
3bf20cd8
WN
146 return (1);
147}
148
17441d36
WN
149/* ARGSUSED */
150#ifdef __STDC__
151comopen(dev_t dev, int flag, int mode, struct proc *p)
152#else
153comopen(dev, flag, mode, p)
3bf20cd8 154 dev_t dev;
17441d36
WN
155 int flag, mode;
156 struct proc *p;
157#endif
3bf20cd8
WN
158{
159 register struct tty *tp;
160 register int unit;
161 int error = 0;
162
163 unit = UNIT(dev);
17441d36 164 if (unit >= NCOM || (com_active & (1 << unit)) == 0)
3bf20cd8
WN
165 return (ENXIO);
166 tp = &com_tty[unit];
167 tp->t_oproc = comstart;
168 tp->t_param = comparam;
169 tp->t_dev = dev;
170 if ((tp->t_state & TS_ISOPEN) == 0) {
171 tp->t_state |= TS_WOPEN;
172 ttychars(tp);
54d5ccc6
WN
173 if (tp->t_ispeed == 0) {
174 tp->t_iflag = TTYDEF_IFLAG;
175 tp->t_oflag = TTYDEF_OFLAG;
176 tp->t_cflag = TTYDEF_CFLAG;
177 tp->t_lflag = TTYDEF_LFLAG;
178 tp->t_ispeed = tp->t_ospeed = comdefaultrate;
179 }
3bf20cd8
WN
180 comparam(tp, &tp->t_termios);
181 ttsetwater(tp);
17441d36 182 } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
3bf20cd8
WN
183 return (EBUSY);
184 (void) spltty();
185 (void) commctl(dev, MCR_DTR | MCR_RTS, DMSET);
186 if ((comsoftCAR & (1 << unit)) || (commctl(dev, 0, DMGET) & MSR_DCD))
187 tp->t_state |= TS_CARR_ON;
188 while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
189 (tp->t_state & TS_CARR_ON) == 0) {
190 tp->t_state |= TS_WOPEN;
191 if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
192 ttopen, 0))
193 break;
194 }
195 (void) spl0();
196 if (error == 0)
197 error = (*linesw[tp->t_line].l_open)(dev, tp);
198 return (error);
199}
200
201/*ARGSUSED*/
202comclose(dev, flag)
203 dev_t dev;
204{
205 register struct tty *tp;
206 register com;
207 register int unit;
208
209 unit = UNIT(dev);
210 com = com_addr[unit];
211 tp = &com_tty[unit];
212 (*linesw[tp->t_line].l_close)(tp);
213 outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK);
214#ifdef KGDB
215 /* do not disable interrupts if debugging */
54d5ccc6 216 if (kgdb_dev != makedev(commajor, unit))
3bf20cd8
WN
217#endif
218 outb(com+com_ier, 0);
219 if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
220 (tp->t_state&TS_ISOPEN) == 0)
221 (void) commctl(dev, 0, DMSET);
222 ttyclose(tp);
223 return(0);
224}
225
226comread(dev, uio, flag)
227 dev_t dev;
228 struct uio *uio;
229{
230 register struct tty *tp = &com_tty[UNIT(dev)];
231
232 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
233}
234
235comwrite(dev, uio, flag)
236 dev_t dev;
237 struct uio *uio;
238{
239 int unit = UNIT(dev);
240 register struct tty *tp = &com_tty[unit];
241
3bf20cd8
WN
242 /*
243 * (XXX) We disallow virtual consoles if the physical console is
244 * a serial port. This is in case there is a display attached that
245 * is not the console. In that situation we don't need/want the X
246 * server taking over the console.
247 */
248 if (constty && unit == comconsole)
249 constty = NULL;
3bf20cd8
WN
250 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
251}
252
253comintr(unit)
254 register int unit;
255{
256 register com;
257 register u_char code;
258 register struct tty *tp;
259
260 com = com_addr[unit];
261 while (1) {
262 code = inb(com+com_iir);
54d5ccc6 263 switch (code & IIR_IMASK) {
3bf20cd8
WN
264 case IIR_NOPEND:
265 return (1);
54d5ccc6 266 case IIR_RXTOUT:
3bf20cd8 267 case IIR_RXRDY:
3bf20cd8 268 tp = &com_tty[unit];
54d5ccc6
WN
269/*
270 * Process received bytes. Inline for speed...
271 */
3bf20cd8 272#ifdef KGDB
54d5ccc6
WN
273#define RCVBYTE() \
274 code = inb(com+com_data); \
275 if ((tp->t_state & TS_ISOPEN) == 0) { \
276 if (kgdb_dev == makedev(commajor, unit) && \
277 code == FRAME_END) \
278 kgdb_connect(0); /* trap into kgdb */ \
279 } else \
280 (*linesw[tp->t_line].l_rint)(code, tp)
281#else
282#define RCVBYTE() \
283 code = inb(com+com_data); \
284 if (tp->t_state & TS_ISOPEN) \
285 (*linesw[tp->t_line].l_rint)(code, tp)
3bf20cd8 286#endif
54d5ccc6
WN
287
288 RCVBYTE();
289
290 if (com_hasfifo & (1 << unit))
291 while ((code = inb(com+com_lsr)) & LSR_RCV_MASK) {
292 if (code == LSR_RXRDY) {
293 RCVBYTE();
294 } else
295 comeint(unit, code, com);
296 }
3bf20cd8
WN
297 break;
298 case IIR_TXRDY:
299 tp = &com_tty[unit];
300 tp->t_state &=~ (TS_BUSY|TS_FLUSH);
301 if (tp->t_line)
302 (*linesw[tp->t_line].l_start)(tp);
303 else
304 comstart(tp);
305 break;
306 case IIR_RLS:
54d5ccc6 307 comeint(unit, inb(com+com_lsr), com);
3bf20cd8
WN
308 break;
309 default:
310 if (code & IIR_NOPEND)
311 return (1);
312 log(LOG_WARNING, "com%d: weird interrupt: 0x%x\n",
313 unit, code);
314 /* fall through */
315 case IIR_MLSC:
316 commint(unit, com);
317 break;
318 }
319 }
320}
321
54d5ccc6
WN
322comeint(unit, stat, com)
323 register int unit, stat;
3bf20cd8
WN
324 register com;
325{
326 register struct tty *tp;
54d5ccc6 327 register int c;
3bf20cd8
WN
328
329 tp = &com_tty[unit];
3bf20cd8
WN
330 c = inb(com+com_data);
331 if ((tp->t_state & TS_ISOPEN) == 0) {
332#ifdef KGDB
333 /* we don't care about parity errors */
334 if (((stat & (LSR_BI|LSR_FE|LSR_PE)) == LSR_PE) &&
54d5ccc6
WN
335 kgdb_dev == makedev(commajor, unit) && c == FRAME_END)
336 kgdb_connect(0); /* trap into kgdb */
3bf20cd8
WN
337#endif
338 return;
339 }
340 if (stat & (LSR_BI | LSR_FE))
341 c |= TTY_FE;
342 else if (stat & LSR_PE)
343 c |= TTY_PE;
344 else if (stat & LSR_OE)
345 log(LOG_WARNING, "com%d: silo overflow\n", unit);
346 (*linesw[tp->t_line].l_rint)(c, tp);
347}
348
349commint(unit, com)
350 register int unit;
351 register com;
352{
353 register struct tty *tp;
354 register int stat;
355
356 tp = &com_tty[unit];
357 stat = inb(com+com_msr);
358 if ((stat & MSR_DDCD) && (comsoftCAR & (1 << unit)) == 0) {
359 if (stat & MSR_DCD)
360 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
361 else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
362 outb(com+com_mcr,
363 inb(com+com_mcr) & ~(MCR_DTR | MCR_RTS) | MCR_IENABLE);
364 } else if ((stat & MSR_DCTS) && (tp->t_state & TS_ISOPEN) &&
365 (tp->t_flags & CRTSCTS)) {
366 /* the line is up and we want to do rts/cts flow control */
367 if (stat & MSR_CTS) {
368 tp->t_state &=~ TS_TTSTOP;
369 ttstart(tp);
370 } else
371 tp->t_state |= TS_TTSTOP;
372 }
373}
374
375comioctl(dev, cmd, data, flag)
376 dev_t dev;
377 caddr_t data;
378{
379 register struct tty *tp;
380 register int unit = UNIT(dev);
381 register com;
382 register int error;
383
384 tp = &com_tty[unit];
385 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
386 if (error >= 0)
387 return (error);
388 error = ttioctl(tp, cmd, data, flag);
389 if (error >= 0)
390 return (error);
391
392 com = com_addr[unit];
393 switch (cmd) {
394
395 case TIOCSBRK:
396 outb(com+com_cfcr, inb(com+com_cfcr) | CFCR_SBREAK);
397 break;
398
399 case TIOCCBRK:
400 outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK);
401 break;
402
403 case TIOCSDTR:
404 (void) commctl(dev, MCR_DTR | MCR_RTS, DMBIS);
405 break;
406
407 case TIOCCDTR:
408 (void) commctl(dev, MCR_DTR | MCR_RTS, DMBIC);
409 break;
410
411 case TIOCMSET:
412 (void) commctl(dev, *(int *)data, DMSET);
413 break;
414
415 case TIOCMBIS:
416 (void) commctl(dev, *(int *)data, DMBIS);
417 break;
418
419 case TIOCMBIC:
420 (void) commctl(dev, *(int *)data, DMBIC);
421 break;
422
423 case TIOCMGET:
424 *(int *)data = commctl(dev, 0, DMGET);
425 break;
426
427 default:
428 return (ENOTTY);
429 }
430 return (0);
431}
432
433comparam(tp, t)
434 register struct tty *tp;
435 register struct termios *t;
436{
437 register com;
438 register int cfcr, cflag = t->c_cflag;
439 int unit = UNIT(tp->t_dev);
440 int ospeed = ttspeedtab(t->c_ospeed, comspeedtab);
441
442 /* check requested parameters */
443 if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
444 return(EINVAL);
445 /* and copy to tty */
446 tp->t_ispeed = t->c_ispeed;
447 tp->t_ospeed = t->c_ospeed;
448 tp->t_cflag = cflag;
449
450 com = com_addr[unit];
451 outb(com+com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS /*| IER_EMSC*/);
452 if (ospeed == 0) {
453 (void) commctl(unit, 0, DMSET); /* hang up line */
454 return(0);
455 }
456 outb(com+com_cfcr, inb(com+com_cfcr) | CFCR_DLAB);
457 outb(com+com_data, ospeed & 0xFF);
458 outb(com+com_ier, ospeed >> 8);
459 switch (cflag&CSIZE) {
460 case CS5:
461 cfcr = CFCR_5BITS; break;
462 case CS6:
463 cfcr = CFCR_6BITS; break;
464 case CS7:
465 cfcr = CFCR_7BITS; break;
466 case CS8:
467 cfcr = CFCR_8BITS; break;
468 }
469 if (cflag&PARENB) {
470 cfcr |= CFCR_PENAB;
471 if ((cflag&PARODD) == 0)
472 cfcr |= CFCR_PEVEN;
473 }
474 if (cflag&CSTOPB)
475 cfcr |= CFCR_STOPB;
476 outb(com+com_cfcr, cfcr);
54d5ccc6
WN
477
478 if (com_hasfifo & (1 << unit))
479 outb(com+com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
480
3bf20cd8
WN
481 return(0);
482}
483
484comstart(tp)
485 register struct tty *tp;
486{
487 register com;
488 int s, unit, c;
489
490 unit = UNIT(tp->t_dev);
491 com = com_addr[unit];
492 s = spltty();
493 if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
494 goto out;
495 if (tp->t_outq.c_cc <= tp->t_lowat) {
496 if (tp->t_state&TS_ASLEEP) {
497 tp->t_state &= ~TS_ASLEEP;
498 wakeup((caddr_t)&tp->t_outq);
499 }
500 if (tp->t_wsel) {
501 selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
502 tp->t_wsel = 0;
503 tp->t_state &= ~TS_WCOLL;
504 }
505 }
506 if (tp->t_outq.c_cc == 0)
507 goto out;
508 if (inb(com+com_lsr) & LSR_TXRDY) {
509 c = getc(&tp->t_outq);
510 tp->t_state |= TS_BUSY;
511 outb(com+com_data, c);
54d5ccc6
WN
512 if (com_hasfifo & (1 << unit))
513 for (c = 1; c < 16 && tp->t_outq.c_cc; ++c)
514 outb(com+com_data, getc(&tp->t_outq));
3bf20cd8
WN
515 }
516out:
517 splx(s);
518}
519
520/*
521 * Stop output on a line.
522 */
523/*ARGSUSED*/
524comstop(tp, flag)
525 register struct tty *tp;
526{
527 register int s;
528
529 s = spltty();
530 if (tp->t_state & TS_BUSY) {
531 if ((tp->t_state&TS_TTSTOP)==0)
532 tp->t_state |= TS_FLUSH;
533 }
534 splx(s);
535}
536
537commctl(dev, bits, how)
538 dev_t dev;
539 int bits, how;
540{
541 register com;
542 register int unit;
543 int s;
544
545 unit = UNIT(dev);
546 com = com_addr[unit];
547 s = spltty();
548 switch (how) {
549
550 case DMSET:
551 outb(com+com_mcr, bits | MCR_IENABLE);
552 break;
553
554 case DMBIS:
555 outb(com+com_mcr, inb(com+com_mcr) | bits | MCR_IENABLE);
556 break;
557
558 case DMBIC:
559 outb(com+com_mcr, inb(com+com_mcr) & ~bits | MCR_IENABLE);
560 break;
561
562 case DMGET:
563 bits = inb(com+com_msr);
564 break;
565 }
566 (void) splx(s);
567 return(bits);
568}
54d5ccc6
WN
569
570/*
571 * Following are all routines needed for COM to act as console
572 */
573#include "i386/i386/cons.h"
574
575comcnprobe(cp)
576 struct consdev *cp;
577{
578 int unit;
579 extern int comopen();
580
581 /* locate the major number */
582 for (commajor = 0; commajor < nchrdev; commajor++)
583 if (cdevsw[commajor].d_open == comopen)
584 break;
585
586 /* XXX: ick */
587 unit = CONUNIT;
588 com_addr[CONUNIT] = CONADDR;
589
590 /* make sure hardware exists? XXX */
591
592 /* initialize required fields */
593 cp->cn_dev = makedev(commajor, unit);
594 cp->cn_tp = &com_tty[unit];
595#ifdef COMCONSOLE
596 cp->cn_pri = CN_REMOTE; /* Force a serial port console */
597#else
598 cp->cn_pri = CN_NORMAL;
599#endif
600}
601
602comcninit(cp)
603 struct consdev *cp;
604{
605 int unit = UNIT(cp->cn_dev);
606
607 cominit(unit, comdefaultrate);
608 comconsole = unit;
609 comconsinit = 1;
610}
611
612cominit(unit, rate)
613 int unit, rate;
614{
615 register int com;
616 int s;
617 short stat;
618
619#ifdef lint
620 stat = unit; if (stat) return;
621#endif
622 com = com_addr[unit];
623 s = splhigh();
624 outb(com+com_cfcr, CFCR_DLAB);
625 rate = ttspeedtab(comdefaultrate, comspeedtab);
626 outb(com+com_data, rate & 0xFF);
627 outb(com+com_ier, rate >> 8);
628 outb(com+com_cfcr, CFCR_8BITS);
629 outb(com+com_ier, IER_ERXRDY | IER_ETXRDY);
630 outb(com+com_fifo, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_14);
631 stat = inb(com+com_iir);
632 splx(s);
633}
634
635comcngetc(dev)
636{
637 register com = com_addr[UNIT(dev)];
638 short stat;
639 int c, s;
640
641#ifdef lint
642 stat = dev; if (stat) return(0);
643#endif
644 s = splhigh();
645 while (((stat = inb(com+com_lsr)) & LSR_RXRDY) == 0)
646 ;
647 c = inb(com+com_data);
648 stat = inb(com+com_iir);
649 splx(s);
650 return(c);
651}
652
653/*
654 * Console kernel output character routine.
655 */
656comcnputc(dev, c)
657 dev_t dev;
658 register int c;
659{
660 register com = com_addr[UNIT(dev)];
661 register int timo;
662 short stat;
663 int s = splhigh();
664
665#ifdef lint
666 stat = dev; if (stat) return;
667#endif
668#ifdef KGDB
669 if (dev != kgdb_dev)
670#endif
671 if (comconsinit == 0) {
672 (void) cominit(UNIT(dev), comdefaultrate);
673 comconsinit = 1;
674 }
675 /* wait for any pending transmission to finish */
676 timo = 50000;
677 while (((stat = inb(com+com_lsr)) & LSR_TXRDY) == 0 && --timo)
678 ;
679 outb(com+com_data, c);
680 /* wait for this transmission to complete */
681 timo = 1500000;
682 while (((stat = inb(com+com_lsr)) & LSR_TXRDY) == 0 && --timo)
683 ;
684 /* clear any interrupts generated by this transmission */
685 stat = inb(com+com_iir);
686 splx(s);
687}
3bf20cd8 688#endif