* Copyright (c) 1991 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)com.c 7.3 (Berkeley) %G%
* COM driver, based on HP dca driver
* uses National Semiconductor NS16450/NS16550AF UART
#include "i386/isa/isa_device.h"
#include "i386/isa/comreg.h"
#include "i386/isa/ic/ns16550.h"
int comprobe(), comattach(), comintr(), comstart(), comparam();
struct isa_driver comdriver
= {
comprobe
, comattach
, "com"
int comconsole
= COMCONSOLE
;
int comdefaultrate
= TTYDEF_SPEED
;
struct tty com_tty
[NCOM
];
struct speedtab comspeedtab
[] = {
extern struct tty
*constty
;
#include "machine/remote-sl.h"
extern int kgdb_debug_init
;
/*if ((inb(dev->id_iobase+com_iir) & 0x38) == 0)
printf("base %x val %x ", dev->id_iobase,
inb(dev->id_iobase+com_iir));*/
int port
= isdp
->id_iobase
;
comsoftCAR
|= 1 << unit
; /* XXX */
/* look for a NS 16550AF UART with FIFOs */
outb(port
+com_fifo
, FIFO_ENABLE
|FIFO_RCV_RST
|FIFO_XMT_RST
|FIFO_TRIGGER_14
);
if ((inb(port
+com_iir
) & IIR_FIFO_MASK
) == IIR_FIFO_MASK
)
com_hasfifo
|= 1 << unit
;
outb(port
+com_mcr
, 0 | MCR_IENABLE
);
if (1/*kgdb_dev == makedev(commajor, unit)*/) {
kgdb_dev
= -1; /* can't debug over console port */
(void) cominit(unit
, kgdb_rate
);
* Print prefix of device name,
* let kgdb_connect print the rest.
printf("com%d: kgdb enabled\n", unit
);
* Need to reset baud rate, etc. of next print so reset comconsinit.
* Also make sure console is always "hardwired"
if (unit
== comconsole
) {
comsoftCAR
|= (1 << unit
);
comopen(dev_t dev
, int flag
, int mode
, struct proc
*p
)
comopen(dev
, flag
, mode
, p
)
if (unit
>= NCOM
|| (com_active
& (1 << unit
)) == 0)
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
tp
->t_iflag
= TTYDEF_IFLAG
;
tp
->t_oflag
= TTYDEF_OFLAG
;
tp
->t_cflag
= TTYDEF_CFLAG
;
tp
->t_lflag
= TTYDEF_LFLAG
;
tp
->t_ispeed
= tp
->t_ospeed
= comdefaultrate
;
comparam(tp
, &tp
->t_termios
);
} else if (tp
->t_state
&TS_XCLUDE
&& p
->p_ucred
->cr_uid
!= 0)
(void) commctl(dev
, MCR_DTR
| MCR_RTS
, DMSET
);
if ((comsoftCAR
& (1 << unit
)) || (commctl(dev
, 0, DMGET
) & MSR_DCD
))
tp
->t_state
|= TS_CARR_ON
;
while ((flag
&O_NONBLOCK
) == 0 && (tp
->t_cflag
&CLOCAL
) == 0 &&
(tp
->t_state
& TS_CARR_ON
) == 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_rawq
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
(*linesw
[tp
->t_line
].l_close
)(tp
);
outb(com
+com_cfcr
, inb(com
+com_cfcr
) & ~CFCR_SBREAK
);
/* do not disable interrupts if debugging */
if (kgdb_dev
!= makedev(commajor
, unit
))
if (tp
->t_cflag
&HUPCL
|| tp
->t_state
&TS_WOPEN
||
(tp
->t_state
&TS_ISOPEN
) == 0)
(void) commctl(dev
, 0, DMSET
);
register struct tty
*tp
= &com_tty
[UNIT(dev
)];
return ((*linesw
[tp
->t_line
].l_read
)(tp
, uio
, flag
));
register struct tty
*tp
= &com_tty
[unit
];
* (XXX) We disallow virtual consoles if the physical console is
* a serial port. This is in case there is a display attached that
* is not the console. In that situation we don't need/want the X
* server taking over the console.
if (constty
&& unit
== comconsole
)
return ((*linesw
[tp
->t_line
].l_write
)(tp
, uio
, flag
));
switch (code
& IIR_IMASK
) {
* Process received bytes. Inline for speed...
code = inb(com+com_data); \
if ((tp->t_state & TS_ISOPEN) == 0) { \
if (kgdb_dev == makedev(commajor, unit) && \
kgdb_connect(0); /* trap into kgdb */ \
(*linesw[tp->t_line].l_rint)(code, tp)
code = inb(com+com_data); \
if (tp->t_state & TS_ISOPEN) \
(*linesw[tp->t_line].l_rint)(code, tp)
if (com_hasfifo
& (1 << unit
))
while ((code
= inb(com
+com_lsr
)) & LSR_RCV_MASK
) {
comeint(unit
, code
, com
);
tp
->t_state
&=~ (TS_BUSY
|TS_FLUSH
);
(*linesw
[tp
->t_line
].l_start
)(tp
);
comeint(unit
, inb(com
+com_lsr
), com
);
log(LOG_WARNING
, "com%d: weird interrupt: 0x%x\n",
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
/* we don't care about parity errors */
if (((stat
& (LSR_BI
|LSR_FE
|LSR_PE
)) == LSR_PE
) &&
kgdb_dev
== makedev(commajor
, unit
) && c
== FRAME_END
)
kgdb_connect(0); /* trap into kgdb */
if (stat
& (LSR_BI
| LSR_FE
))
log(LOG_WARNING
, "com%d: silo overflow\n", unit
);
(*linesw
[tp
->t_line
].l_rint
)(c
, tp
);
if ((stat
& MSR_DDCD
) && (comsoftCAR
& (1 << unit
)) == 0) {
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
else if ((*linesw
[tp
->t_line
].l_modem
)(tp
, 0) == 0)
inb(com
+com_mcr
) & ~(MCR_DTR
| MCR_RTS
) | MCR_IENABLE
);
} else if ((stat
& MSR_DCTS
) && (tp
->t_state
& TS_ISOPEN
) &&
(tp
->t_flags
& CRTSCTS
)) {
/* the line is up and we want to do rts/cts flow control */
tp
->t_state
&=~ TS_TTSTOP
;
tp
->t_state
|= TS_TTSTOP
;
comioctl(dev
, cmd
, data
, flag
)
register int unit
= UNIT(dev
);
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
);
error
= ttioctl(tp
, cmd
, data
, flag
);
outb(com
+com_cfcr
, inb(com
+com_cfcr
) | CFCR_SBREAK
);
outb(com
+com_cfcr
, inb(com
+com_cfcr
) & ~CFCR_SBREAK
);
(void) commctl(dev
, MCR_DTR
| MCR_RTS
, DMBIS
);
(void) commctl(dev
, MCR_DTR
| MCR_RTS
, DMBIC
);
(void) commctl(dev
, *(int *)data
, DMSET
);
(void) commctl(dev
, *(int *)data
, DMBIS
);
(void) commctl(dev
, *(int *)data
, DMBIC
);
*(int *)data
= commctl(dev
, 0, DMGET
);
register struct termios
*t
;
register int cfcr
, cflag
= t
->c_cflag
;
int unit
= UNIT(tp
->t_dev
);
int ospeed
= ttspeedtab(t
->c_ospeed
, comspeedtab
);
/* check requested parameters */
if (ospeed
< 0 || (t
->c_ispeed
&& t
->c_ispeed
!= t
->c_ospeed
))
tp
->t_ispeed
= t
->c_ispeed
;
tp
->t_ospeed
= t
->c_ospeed
;
outb(com
+com_ier
, IER_ERXRDY
| IER_ETXRDY
| IER_ERLS
/*| IER_EMSC*/);
(void) commctl(unit
, 0, DMSET
); /* hang up line */
outb(com
+com_cfcr
, inb(com
+com_cfcr
) | CFCR_DLAB
);
outb(com
+com_data
, ospeed
& 0xFF);
outb(com
+com_ier
, ospeed
>> 8);
cfcr
= CFCR_5BITS
; break;
cfcr
= CFCR_6BITS
; break;
cfcr
= CFCR_7BITS
; break;
cfcr
= CFCR_8BITS
; break;
outb(com
+com_cfcr
, cfcr
);
if (com_hasfifo
& (1 << unit
))
outb(com
+com_fifo
, FIFO_ENABLE
| FIFO_TRIGGER_14
);
if (tp
->t_state
& (TS_TIMEOUT
|TS_TTSTOP
))
if (tp
->t_outq
.c_cc
<= tp
->t_lowat
) {
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_outq
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
if (tp
->t_outq
.c_cc
== 0)
if (inb(com
+com_lsr
) & LSR_TXRDY
) {
if (com_hasfifo
& (1 << unit
))
for (c
= 1; c
< 16 && tp
->t_outq
.c_cc
; ++c
)
outb(com
+com_data
, getc(&tp
->t_outq
));
if (tp
->t_state
& TS_BUSY
) {
if ((tp
->t_state
&TS_TTSTOP
)==0)
outb(com
+com_mcr
, bits
| MCR_IENABLE
);
outb(com
+com_mcr
, inb(com
+com_mcr
) | bits
| MCR_IENABLE
);
outb(com
+com_mcr
, inb(com
+com_mcr
) & ~bits
| MCR_IENABLE
);
* Following are all routines needed for COM to act as console
#include "i386/i386/cons.h"
/* locate the major number */
for (commajor
= 0; commajor
< nchrdev
; commajor
++)
if (cdevsw
[commajor
].d_open
== comopen
)
com_addr
[CONUNIT
] = CONADDR
;
/* make sure hardware exists? XXX */
/* initialize required fields */
cp
->cn_dev
= makedev(commajor
, unit
);
cp
->cn_tp
= &com_tty
[unit
];
cp
->cn_pri
= CN_REMOTE
; /* Force a serial port console */
int unit
= UNIT(cp
->cn_dev
);
cominit(unit
, comdefaultrate
);
stat
= unit
; if (stat
) return;
outb(com
+com_cfcr
, CFCR_DLAB
);
rate
= ttspeedtab(comdefaultrate
, comspeedtab
);
outb(com
+com_data
, rate
& 0xFF);
outb(com
+com_ier
, rate
>> 8);
outb(com
+com_cfcr
, CFCR_8BITS
);
outb(com
+com_ier
, IER_ERXRDY
| IER_ETXRDY
);
outb(com
+com_fifo
, FIFO_ENABLE
|FIFO_RCV_RST
|FIFO_XMT_RST
|FIFO_TRIGGER_14
);
register com
= com_addr
[UNIT(dev
)];
stat
= dev
; if (stat
) return(0);
while (((stat
= inb(com
+com_lsr
)) & LSR_RXRDY
) == 0)
* Console kernel output character routine.
register com
= com_addr
[UNIT(dev
)];
stat
= dev
; if (stat
) return;
(void) cominit(UNIT(dev
), comdefaultrate
);
/* wait for any pending transmission to finish */
while (((stat
= inb(com
+com_lsr
)) & LSR_TXRDY
) == 0 && --timo
)
/* wait for this transmission to complete */
while (((stat
= inb(com
+com_lsr
)) & LSR_TXRDY
) == 0 && --timo
)
/* clear any interrupts generated by this transmission */