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