* Copyright (c) 1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
* $Id: com.c,v 1.6 1993/11/25 01:31:30 wollman Exp $
* COM driver, based on HP dca driver
* uses National Semiconductor NS16450/NS16550AF UART
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/comreg.h"
#include "i386/isa/ic/ns16550.h"
static int commctl(int /*dev_t*/, int, int);
static void comstart(struct tty
*);
static int comparam(struct tty
*, struct termios
*);
static void comeint(int, int, int);
static void commint(int, int);
static void cominit(int, int);
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
;
#define UNIT(x) (minor(x))
/* force access to id reg */
outb(dev
->id_iobase
+com_cfcr
, 0);
outb(dev
->id_iobase
+com_iir
, 0);
if ((inb(dev
->id_iobase
+com_iir
) & 0x38) == 0)
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_4
);
if ((inb(port
+com_iir
) & IIR_FIFO_MASK
) == IIR_FIFO_MASK
) {
com_hasfifo
|= 1 << unit
;
printf("com%d: fifo\n", unit
);
outb(port
+com_mcr
, 0 | MCR_IENABLE
);
if (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(int /*dev_t*/ dev
, int flag
, int mode
, struct proc
*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_raw
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
, 0);
comclose(dev
, flag
, mode
, p
)
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
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
))
else if (stat
& LSR_OE
) { /* 30 Aug 92*/
c
|= TTY_PE
; /* Ought to have it's own define... */
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_cflag
& 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
= 0, 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_4
);
if (tp
->t_state
& (TS_TIMEOUT
|TS_TTSTOP
))
if (RB_LEN(&tp
->t_out
) <= tp
->t_lowat
) {
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_out
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
if (RB_LEN(&tp
->t_out
) == 0)
if (inb(com
+com_lsr
) & LSR_TXRDY
) {
if (com_hasfifo
& (1 << unit
))
for (c
= 1; c
< 16 && RB_LEN(&tp
->t_out
); ++c
)
outb(com
+com_data
, getc(&tp
->t_out
));
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_4
);
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 */
register struct tty
*tp
= &com_tty
[UNIT(dev
)];
((tp
->t_cflag
&CLOCAL
) == 0 && (tp
->t_state
&TS_CARR_ON
) == 0))
if (tp
->t_rsel
&& (selp
= pfind(tp
->t_rsel
)) && selp
->p_wchan
== (caddr_t
)&selwait
)
if (RB_LEN(&tp
->t_out
) <= tp
->t_lowat
)
if (tp
->t_wsel
&& (selp
= pfind(tp
->t_wsel
)) && selp
->p_wchan
== (caddr_t
)&selwait
)