* 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
* @(#)sio.c 7.5 (Berkeley) 5/16/91
* 27 May 93 Bruce Evans From com-0.2 package, fast interrupt
* 27 May 93 Guido van Rooij Ported in Chris Demetriou's BIDIR
* code, add multiport support.
* 27 May 93 Rodney W. Grimes I then renamed it to sio.c for putting
* into the patch kit. Added in sioselect
* from com.c. Added port 4 support.
static char rcsid
[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/com.c,v 1.2 92/01/21 14:34:11 william Exp $";
* COM driver, based on HP dca driver.
* Mostly rewritten to use pseudo-DMA.
* Works for National Semiconductor NS8250-NS16550AF UARTs.
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/comreg.h"
#include "i386/isa/ic/ns16550.h"
#define CRTS_IFLOW CRTSCTS /* XXX, CCTS_OFLOW = CRTSCTS already */
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
#define TS_RTSBLOCK TS_TBLOCK /* XXX */
#define TTY_BI TTY_FE /* XXX */
#define TTY_OE TTY_PE /* XXX */
#define UNIT(x) (minor(x)) /* XXX */
#define COM_UNITMASK 0x7f
#define COM_CALLOUTMASK 0x80
#define UNIT(x) (minor(x) & COM_UNITMASK)
#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK)
/* checks in flags for multiport and which is multiport "master chip"
#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
#endif /* COM_MULTIPORT */
#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
#define schedsoftcom() (ipending |= 1 << 4) /* XXX */
* Input buffer watermarks.
* The external device is asked to stop sending when the buffer exactly reaches
* high water, or when the high level requests it.
* The high level is notified immediately (rather than at a later clock tick)
* when this watermark is reached.
* The buffer size is chosen so the watermark should almost never be reached.
* The low watermark is invisibly 0 since the buffer is always emptied all at
#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
* (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
* than the other bits so that they can be tested as a group without masking
* The following com and tty flags correspond closely:
* TS_BUSY = CS_BUSY (maintained by comstart() and comflush())
* CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
* CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
* CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
* Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
#define CS_BUSY 0x80 /* output in progress */
#define CS_TTGO 0x40 /* output not stopped by XOFF */
#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
#define CS_CHECKMSR 1 /* check of MSR scheduled */
#define CS_CTS_OFLOW 2 /* use CTS output flow control */
#define CS_ODONE 4 /* output completed */
#define CS_RTS_IFLOW 8 /* use RTS input flow control */
static char *error_desc
[] = {
#define CE_INTERRUPT_BUF_OVERFLOW 1
"interrupt-level buffer overflow",
#define CE_TTY_BUF_OVERFLOW 2
"tty-level buffer overflow",
#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
/* types. XXX - should be elsewhere */
typedef u_int Port_t
; /* hardware port */
typedef int Bool_t
; /* promoted boolean */
typedef u_char bool_t
; /* boolean */
/* com device structure */
u_char state
; /* miscellaneous flag bits */
u_char cfcr_image
; /* copy of value written to CFCR */
bool_t hasfifo
; /* nonzero for 16550 UARTs */
u_char mcr_image
; /* copy of value written to MCR */
bool_t softDCD
; /* nonzero for faked carrier detect */
bool_t bidir
; /* is this unit bidirectional? */
bool_t active
; /* is the port active _at all_? */
bool_t active_in
; /* is the incoming port in use? */
bool_t active_out
; /* is the outgoing port in use? */
bool_t multiport
; /* is this unit part of a multiport device? */
#endif /* COM_MULTIPORT */
* The high level of the driver never reads status registers directly
* because there would be too many side effects to handle conveniently.
* Instead, it reads copies of the registers stored here by the
u_char last_modem_status
; /* last MSR read by intr handler */
u_char prev_modem_status
; /* last MSR handled by high level */
u_char
*ibuf
; /* start of input buffer */
u_char
*ibufend
; /* end of input buffer */
u_char
*ihighwater
; /* threshold in input buffer */
u_char
*iptr
; /* next free spot in input buffer */
u_char
*obufend
; /* end of output buffer */
int ocount
; /* original count for current output */
u_char
*optr
; /* next char to output */
Port_t data_port
; /* i/o ports */
Port_t modem_status_port
;
struct tty
*tp
; /* cross reference */
u_long bytes_in
; /* statistics */
u_int delta_error_counts
[CE_NTYPES
];
u_int error_counts
[CE_NTYPES
];
* Ping-pong input buffers. The extra factor of 2 in the sizes is
* to allow for an error byte for each input byte.
#define CE_INPUT_OFFSET RS_IBUFSIZE
u_char ibuf1
[2 * RS_IBUFSIZE
];
u_char ibuf2
[2 * RS_IBUFSIZE
];
* These functions in the com module ought to be declared (with a prototype)
* in a com-driver system header. The void ones may need to be int to match
* ancient devswitch declarations, but they don't actually return anything.
#define Dev_t int /* promoted dev_t */
int sioclose
__P((Dev_t dev
, int fflag
, int devtype
,
void siointr
__P((int unit
));
bool_t comintr1
__P((struct com_s
*com
));
#endif /* COM_MULTIPORT */
int sioioctl
__P((Dev_t dev
, int cmd
, caddr_t data
,
int fflag
, struct proc
*p
));
int siocngetc
__P((Dev_t dev
));
void siocninit
__P((struct consdev
*cp
));
void siocnprobe
__P((struct consdev
*cp
));
void siocnputc
__P((Dev_t dev
, int c
));
int sioopen
__P((Dev_t dev
, int oflags
, int devtype
,
* sioopen gets compared to the d_open entry in struct cdevsw. d_open and
* other functions are declared in <sys/conf.h> with short types like dev_t
* in the prototype. Such declarations are broken because they vary with
* __P (significantly in theory - the compiler is allowed to push a short
* arg if it has seen the prototype; insignificantly in practice - gcc
* doesn't push short args and it would be slower on 386's to do so).
* Also, most of the device switch functions are still declared old-style
* so they take a Dev_t arg and shorten it to a dev_t. It would be simpler
* and faster if dev_t's were always promoted (to ints or whatever) as
* Until <sys/conf.h> is fixed, we cast sioopen to the following `wrong' type
* when comparing it to the d_open entry just to avoid compiler warnings.
typedef int (*bogus_open_t
) __P((dev_t dev
, int oflags
, int devtype
,
int sioread
__P((Dev_t dev
, struct uio
*uio
, int ioflag
));
void siostop
__P((struct tty
*tp
, int rw
));
int siowrite
__P((Dev_t dev
, struct uio
*uio
, int ioflag
));
void softsio0
__P((void));
void softsio1
__P((void));
void softsio2
__P((void));
void softsio3
__P((void));
void softsio4
__P((void));
void softsio5
__P((void));
void softsio6
__P((void));
void softsio7
__P((void));
void softsio8
__P((void));
static int sioattach
__P((struct isa_device
*dev
));
static void comflush
__P((struct com_s
*com
));
static void comhardclose
__P((struct com_s
*com
));
static void cominit
__P((int unit
, int rate
));
static int commctl
__P((struct com_s
*com
, int bits
, int how
));
static int comparam
__P((struct tty
*tp
, struct termios
*t
));
static int sioprobe
__P((struct isa_device
*dev
));
static void compoll
__P((void));
static int comstart
__P((struct tty
*tp
));
static void comwakeup
__P((void));
/* table and macro for fast conversion from a unit number to its com struct */
static struct com_s
*p_com_addr
[NSIO
];
#define com_addr(unit) (p_com_addr[unit])
static struct com_s com_structs
[NSIO
];
struct isa_driver siodriver
= {
sioprobe
, sioattach
, "sio"
static int comconsole
= COMCONSOLE
;
static int comconsole
= -1;
static bool_t comconsinit
;
static speed_t comdefaultrate
= TTYDEF_SPEED
;
static u_int com_events
; /* input chars + weighted output completions */
struct tty sio_tty
[NSIO
];
extern struct tty
*constty
;
extern u_int ipending
; /* XXX */
extern int tk_nin
; /* XXX */
extern int tk_rawcc
; /* XXX */
#include "machine/remote-sl.h"
extern int kgdb_debug_init
;
static struct speedtab comspeedtab
[] = {
/* XXX - configure this list */
static Port_t likely_com_ports
[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
static bool_t already_init
;
* Turn off MCR_IENABLE for all likely serial ports. An unused
* port with its MCR_IENABLE gate open will inhibit interrupts
* from any used port that shares the interrupt vector.
for (com_ptr
= likely_com_ports
;
com_ptr
< &likely_com_ports
[sizeof likely_com_ports
/ sizeof likely_com_ports
[0]];
outb(*com_ptr
+ com_mcr
, 0);
* We don't want to get actual interrupts, just masked ones.
* Interrupts from this line should already be masked in the ICU,
* but mask them in the processor as well in case there are some
* (misconfigured) shared interrupts.
* Enable output interrupts (only) and check the following:
* o the CFCR, IER and MCR in UART hold the values written to them
* (the values happen to be all distinct - this is good for
* avoiding false positive tests from bus echoes).
* o an output interrupt is generated and its vector is correct.
* o the interrupt goes away when the IIR in the UART is read.
outb(iobase
+ com_cfcr
, CFCR_8BITS
); /* ensure IER is addressed */
outb(iobase
+ com_mcr
, MCR_IENABLE
); /* open gate early */
outb(iobase
+ com_ier
, 0); /* ensure edge on next intr */
outb(iobase
+ com_ier
, IER_ETXRDY
); /* generate interrupt */
if ( inb(iobase
+ com_cfcr
) != CFCR_8BITS
|| inb(iobase
+ com_ier
) != IER_ETXRDY
|| inb(iobase
+ com_mcr
) != MCR_IENABLE
|| (inb(iobase
+ com_iir
) & IIR_IMASK
) != IIR_TXRDY
|| (inb(iobase
+ com_iir
) & IIR_IMASK
) != IIR_NOPEND
)
* Turn off all device interrupts and check that they go off properly.
* Leave MCR_IENABLE set. It gates the OUT2 output of the UART to
* the ICU input. Closing the gate would give a floating ICU input
* (unless there is another device driving at) and spurious interrupts.
* (On the system that this was first tested on, the input floats high
* and gives a (masked) interrupt as soon as the gate is closed.)
outb(iobase
+ com_ier
, 0);
outb(iobase
+ com_mcr
, MCR_IENABLE
); /* dummy to avoid bus echo */
if ( inb(iobase
+ com_ier
) != 0
|| (inb(iobase
+ com_iir
) & IIR_IMASK
) != IIR_NOPEND
)
static int /* XXX - should be void */
static bool_t comwakeup_started
= FALSE
;
iobase
= isdp
->id_iobase
;
* sioprobe() has initialized the device registers as follows:
* It is most important that CFCR_DLAB is off, so that the
* data port is not hidden when we enable interrupts.
* Interrupts are only enabled when the line is open.
* Keeping MCR_DTR and MCR_RTS off might stop the external
* device from sending before we are ready.
com
= &com_structs
[unit
];
com
->cfcr_image
= CFCR_8BITS
;
com
->mcr_image
= MCR_IENABLE
;
com
->iptr
= com
->ibuf
= com
->ibuf1
;
com
->ibufend
= com
->ibuf1
+ RS_IBUFSIZE
;
com
->ihighwater
= com
->ibuf1
+ RS_IHIGHWATER
;
com
->data_port
= iobase
+ com_data
;
com
->int_id_port
= iobase
+ com_iir
;
com
->modem_ctl_port
= iobase
+ com_mcr
;
com
->line_status_port
= iobase
+ com_lsr
;
com
->modem_status_port
= iobase
+ com_msr
;
com
->tp
= &sio_tty
[unit
];
* if bidirectional ports possible, clear the bidir port info;
com
->active_in
= com
->active_out
= FALSE
;
/* attempt to determine UART type */
scr
= inb(iobase
+ com_scr
);
outb(iobase
+ com_scr
, 0xa5);
scr1
= inb(iobase
+ com_scr
);
outb(iobase
+ com_scr
, 0x5a);
scr2
= inb(iobase
+ com_scr
);
outb(iobase
+ com_scr
, scr
);
if (scr1
!= 0xa5 || scr2
!= 0x5a)
outb(iobase
+ com_fifo
, FIFO_ENABLE
| FIFO_TRIGGER_14
);
switch (inb(iobase
+ com_iir
) & IIR_FIFO_MASK
) {
outb(iobase
+ com_fifo
, 0);
if (COM_ISMULTIPORT(isdp
)) {
struct isa_device
*masterdev
;
/* set the master's common-interrupt-enable reg.,
* as appropriate. YYY See your manual
/* enable only common interrupt for port */
outb(iobase
+ com_mcr
, 0);
masterdev
= find_isadev(isa_devtab_tty
, &siodriver
,
outb(masterdev
->id_iobase
+com_scr
, 0x80);
#endif /* COM_MULTIPORT */
if (kgdb_dev
== makedev(commajor
, unit
)) {
kgdb_dev
= -1; /* can't debug over console port */
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
) {
if (!comwakeup_started
) {
comwakeup_started
= TRUE
;
sioopen(dev
, flag
, mode
, p
)
bool_t callout
= CALLOUT(dev
);
if ((u_int
) unit
>= NSIO
|| (com
= com_addr(unit
)) == NULL
)
/* if it's a callout device, and bidir not possible on that dev, die */
if (callout
&& !(com
->bidir
))
/* if it's bidirectional, we've gotta deal with it... */
/* it's ours. lock it down, and set it up */
/* it's busy, outgoing. wait, if possible */
error
= tsleep(&com
->active_out
,
/* if there was an error, take off. */
/* else take it from the top */
} else if (com
->last_modem_status
& MSR_DCD
) {
/* there's a carrier on the line; we win */
/* there is no carrier on the line */
/* can't wait; let it open */
/* NOTE: cgd'sdriver used the ier register
* to enable/disable interrupts. This one
* uses both ier and IENABLE in the mcr.
(void) commctl(com
, MCR_DTR
| MCR_RTS
, DMSET
);
outb(com
->iobase
+ com_ier
, IER_EMSC
);
error
= tsleep(&com
->active_in
,
/* if not active, turn DTR & RTS off */
(void) commctl(com
, MCR_DTR
| MCR_RTS
, DMBIC
);
/* if there was an error, take off. */
/* else take it from the top */
if (!(tp
->t_state
& TS_ISOPEN
)) {
* We no longer use the flags from <sys/ttydefaults.h>
* since those are only relevant for logins. It's
* important to have echo off initially so that the
* line doesn't start blathering before the echo flag
* can be turned off. It's useful to have clocal on
* initially so that "stty changed-defaults </dev/comx"
* doesn't hang waiting for carrier.
tp
->t_cflag
= CREAD
| CS8
| CLOCAL
;
tp
->t_ispeed
= tp
->t_ospeed
= comdefaultrate
;
(void) commctl(com
, MCR_DTR
| MCR_RTS
, DMSET
);
error
= comparam(tp
, &tp
->t_termios
);
/* (re)enable and drain FIFO */
outb(iobase
+ com_fifo
, FIFO_ENABLE
| FIFO_TRIGGER_14
| FIFO_RCV_RST
| FIFO_XMT_RST
);
(void) inb(com
->line_status_port
);
(void) inb(com
->data_port
);
com
->prev_modem_status
= inb(com
->modem_status_port
);
outb(iobase
+ com_ier
, IER_ERXRDY
| IER_ETXRDY
| IER_ERLS
if (com
->softDCD
|| com
->prev_modem_status
& MSR_DCD
)
tp
->t_state
|= TS_CARR_ON
;
else if (tp
->t_state
& TS_XCLUDE
&& p
->p_ucred
->cr_uid
!= 0) {
while (!(flag
& O_NONBLOCK
) && !(tp
->t_cflag
& CLOCAL
)
/* We went through a lot of trouble to open it,
* but it's certain we have a carrier now, so
* don't spend any time on it now.
&& !(tp
->t_state
& TS_CARR_ON
)) {
error
= ttysleep(tp
, (caddr_t
)&tp
->t_raw
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
wakeup((caddr_t
) &com
->active_in
);
* XXX - the next step was once not done, so interrupts, DTR and RTS
* remainded hot if the process was killed while it was sleeping
* waiting for carrier. Now there is the opposite problem. If several
* processes are sleeping waiting for carrier on the same line and one
* is killed, interrupts are turned off so the other processes will
* never see the carrier rise.
if (error
!= 0 && !(tp
->t_state
& TS_ISOPEN
))
tp
->t_state
&= ~TS_WOPEN
;
sioclose(dev
, flag
, mode
, p
)
com
= com_addr(UNIT(dev
));
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
outb(iobase
+ com_cfcr
, com
->cfcr_image
&= ~CFCR_SBREAK
);
/* do not disable interrupts if debugging */
if (kgdb_dev
!= makedev(commajor
, com
- &com_structs
[0]))
outb(iobase
+ com_ier
, 0);
if (tp
->t_cflag
& HUPCL
|| tp
->t_state
& TS_WOPEN
|| !(tp
->t_state
& TS_ISOPEN
))
(void) commctl(com
, 0, DMSET
);
com
->active
= com
->active_in
= com
->active_out
= FALSE
;
/* wakeup sleepers who are waiting for out to finish */
wakeup((caddr_t
) &com
->active_out
);
struct tty
*tp
= com_addr(UNIT(dev
))->tp
;
return ((*linesw
[tp
->t_line
].l_read
)(tp
, uio
, flag
));
struct tty
*tp
= com_addr(unit
)->tp
;
* (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
));
#else /* COM_MULTIPORT */
/* XXX When siointr is called
* it should be changed to a
* call to comintr1. Doesn't
* seem a good idea: interrupts
* are disabled all the time.
donesomething
= comintr1(com
);
comintr1(struct com_s
*com
)
#endif /* COM_MULTIPORT */
line_status
= inb(com
->line_status_port
);
/* input event? (check first to help avoid overruns) */
while (line_status
& LSR_RCV_MASK
) {
/* break/unnattached error bits or real input? */
#endif /* COM_MULTIPORT */
if (!(line_status
& LSR_RXRDY
))
recv_data
= inb(com
->data_port
);
/* trap into kgdb? (XXX - needs testing and optim) */
if (recv_data
== FRAME_END
&& !(com
->tp
->t_state
& TS_ISOPEN
)
&& kgdb_dev
== makedev(commajor
, unit
)) {
if (ioptr
>= com
->ibufend
)
CE_RECORD(com
, CE_INTERRUPT_BUF_OVERFLOW
);
ioptr
[CE_INPUT_OFFSET
] = line_status
;
if (ioptr
== com
->ihighwater
&& com
->state
& CS_RTS_IFLOW
)
outb(com
->modem_ctl_port
,
com
->mcr_image
&= ~MCR_RTS
);
* "& 0x7F" is to avoid the gcc-1.40 generating a slow
* jump from the top of the loop to here
line_status
= inb(com
->line_status_port
) & 0x7F;
/* modem status change? (always check before doing output) */
modem_status
= inb(com
->modem_status_port
);
if (modem_status
!= com
->last_modem_status
) {
* Schedule high level to handle DCD changes. Note
* that we don't use the delta bits anywhere. Some
* UARTs mess them up, and it's easy to remember the
* previous bits and calculate the delta.
#endif /* COM_MULTIPORT */
com
->last_modem_status
= modem_status
;
if (!(com
->state
& CS_CHECKMSR
)) {
com_events
+= LOTS_OF_EVENTS
;
com
->state
|= CS_CHECKMSR
;
/* handle CTS change immediately for crisp flow ctl */
if (com
->state
& CS_CTS_OFLOW
) {
if (modem_status
& MSR_CTS
)
com
->state
|= CS_ODEVREADY
;
com
->state
&= ~CS_ODEVREADY
;
/* output queued and everything ready? */
if (line_status
& LSR_TXRDY
&& com
->state
>= (CS_ODEVREADY
| CS_BUSY
| CS_TTGO
)) {
#endif /* COM_MULTIPORT */
outb(com
->data_port
, *ioptr
);
if (ioptr
>= com
->obufend
) {
/* output just completed */
com_events
+= LOTS_OF_EVENTS
;
com
->state
^= (CS_ODONE
| CS_BUSY
);
schedsoftcom(); /* handle at high level ASAP */
if ((inb(com
->int_id_port
) & IIR_IMASK
) == IIR_NOPEND
)
#endif /* COM_MULTIPORT */
sioioctl(dev
, cmd
, data
, flag
, p
)
com
= com_addr(UNIT(dev
));
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
);
error
= ttioctl(tp
, cmd
, data
, flag
);
outb(iobase
+ com_cfcr
, com
->cfcr_image
|= CFCR_SBREAK
);
outb(iobase
+ com_cfcr
, com
->cfcr_image
&= ~CFCR_SBREAK
);
(void) commctl(com
, MCR_DTR
| MCR_RTS
, DMBIS
);
(void) commctl(com
, MCR_DTR
| MCR_RTS
, DMBIC
);
(void) commctl(com
, *(int *)data
, DMSET
);
(void) commctl(com
, *(int *)data
, DMBIS
);
(void) commctl(com
, *(int *)data
, DMBIC
);
*(int *)data
= commctl(com
, 0, DMGET
);
/* must be root to set bidir. capability */
if (p
->p_ucred
->cr_uid
!= 0)
/* if it's the console, can't do it */
if (UNIT(dev
) == comconsole
)
/* can't do the next, for obvious reasons...
* but there are problems to be looked at...
/* if the port is active, don't do it */
com
->bidir
= *(int *)data
;
*(int *)data
= com
->bidir
;
/* cancel pending output */
if (com
->state
& CS_ODONE
)
com_events
-= LOTS_OF_EVENTS
;
com
->state
&= ~(CS_ODONE
| CS_BUSY
);
rbp
->rb_hd
+= com
->ocount
;
rbp
->rb_hd
= RB_ROLLOVER(rbp
, rbp
->rb_hd
);
com
->tp
->t_state
&= ~TS_BUSY
;
static bool_t awake
= FALSE
;
for (unit
= 0; unit
< NSIO
; ++unit
) {
/* switch the role of the low-level input buffers */
if (com
->iptr
== (ibuf
= com
->ibuf
))
com
->ibufend
= ibuf
+ RS_IBUFSIZE
;
com
->ihighwater
= ibuf
+ RS_IHIGHWATER
;
* There is now room for another low-level buffer full
* of input, so enable RTS if it is now disabled and
* there is room in the high-level buffer.
if (!(com
->mcr_image
& MCR_RTS
)
&& !(tp
->t_state
& TS_RTSBLOCK
))
outb(com
->modem_ctl_port
,
com
->mcr_image
|= MCR_RTS
);
if (com
->state
& CS_CHECKMSR
) {
u_char delta_modem_status
;
delta_modem_status
= com
->last_modem_status
^ com
->prev_modem_status
;
com
->prev_modem_status
= com
->last_modem_status
;
com_events
-= LOTS_OF_EVENTS
;
com
->state
&= ~CS_CHECKMSR
;
if (delta_modem_status
& MSR_DCD
&&
if (com
->prev_modem_status
& MSR_DCD
) {
(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
wakeup((caddr_t
) &com
->active_in
);
if (com
->prev_modem_status
& MSR_DCD
)
(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
else if ((*linesw
[tp
->t_line
].l_modem
)(tp
, 0)
outb(com
->modem_ctl_port
,
&= ~(MCR_DTR
| MCR_RTS
));
u_int delta_error_counts
[CE_NTYPES
];
bcopy(com
->delta_error_counts
, delta_error_counts
,
sizeof delta_error_counts
);
bzero(com
->delta_error_counts
,
sizeof delta_error_counts
);
for (errnum
= 0; errnum
< CE_NTYPES
; ++errnum
) {
delta
= delta_error_counts
[errnum
];
com
->error_counts
[errnum
] += delta
;
"com%d: %u more %s%s (total %lu)\n",
unit
, delta
, error_desc
[errnum
],
delta
== 1 ? "" : "s", total
);
if (com
->state
& CS_ODONE
) {
/* XXX - why isn't the table used for t_line == 0? */
(*linesw
[tp
->t_line
].l_start
)(tp
);
if (incc
<= 0 || !(tp
->t_state
& TS_ISOPEN
))
if (com
->state
& CS_RTS_IFLOW
&& RB_LEN(&tp
->t_raw
) + incc
>= RB_I_HIGH_WATER
&& !(tp
->t_state
& TS_RTSBLOCK
)
* XXX - need RTS flow control for all line disciplines.
* Only have it in standard one now.
&& linesw
[tp
->t_line
].l_rint
== ttyinput
) {
tp
->t_state
|= TS_RTSBLOCK
;
* Avoid the grotesquely inefficient lineswitch routine
* (ttyinput) in "raw" mode. It usually takes about 450
* instructions (that's without canonical processing or echo!).
* slinput is reasonably fast (usually 40 instructions plus
if (!(tp
->t_iflag
& (ICRNL
| IGNCR
| IMAXBEL
| INLCR
| ISTRIP
&& !(tp
->t_lflag
& (ECHO
| ECHONL
| ICANON
| IEXTEN
| ISIG
&& !(tp
->t_state
& (TS_CNTTB
| TS_LNCH
))
&& linesw
[tp
->t_line
].l_rint
== ttyinput
) {
com
->delta_error_counts
[CE_TTY_BUF_OVERFLOW
]
+= incc
- rb_write(&tp
->t_raw
, (char *) buf
,
if (tp
->t_state
& TS_TTSTOP
|| tp
->t_cc
[VSTART
] == tp
->t_cc
[VSTOP
])) {
tp
->t_state
&= ~TS_TTSTOP
;
line_status
= (u_char
) buf
[CE_INPUT_OFFSET
];
recv_data
= (u_char
) *buf
++;
& (LSR_BI
| LSR_FE
| LSR_OE
| LSR_PE
)) {
if (line_status
& LSR_BI
)
if (line_status
& LSR_FE
)
if (line_status
& LSR_OE
)
if (line_status
& LSR_PE
)
(*linesw
[tp
->t_line
].l_rint
)(recv_data
, tp
);
if (com_events
>= LOTS_OF_EVENTS
)
/* check requested parameters */
divisor
= ttspeedtab(t
->c_ospeed
, comspeedtab
);
if (divisor
< 0 || t
->c_ispeed
!= 0 && t
->c_ispeed
!= t
->c_ospeed
)
/* parameters are OK, convert them to the com struct and the device */
(void) commctl(com
, 0, DMSET
); /* hang up line */
* Some UARTs lock up if the divisor latch registers are selected
* while the UART is doing output (they refuse to transmit anything
* more until given a hard reset). Fix this by stopping filling
* the device buffers and waiting for them to drain. Reading the
* line status port outside of siointr() might lose some receiver
* error bits, but that is acceptable here.
while ((inb(com
->line_status_port
) & (LSR_TSRE
| LSR_TXRDY
))
!= (LSR_TSRE
| LSR_TXRDY
)) {
error
= ttysleep(tp
, (caddr_t
)&tp
->t_raw
, TTIPRI
| PCATCH
,
if (error
!= 0 && error
!= EAGAIN
) {
if (!(tp
->t_state
& TS_TTSTOP
)) {
disable_intr(); /* very important while com_data is hidden */
outb(iobase
+ com_cfcr
, cfcr
| CFCR_DLAB
);
outb(iobase
+ com_dlbl
, divisor
& 0xFF);
outb(iobase
+ com_dlbh
, (u_int
) divisor
>> 8);
outb(iobase
+ com_cfcr
, com
->cfcr_image
= cfcr
);
if (!(tp
->t_state
& TS_TTSTOP
))
com
->state
|= CS_RTS_IFLOW
; /* XXX - secondary changes? */
com
->state
&= ~CS_RTS_IFLOW
;
* Set up state to handle output flow control.
* XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
* Now has 16+ msec latency, while CTS flow has 50- usec latency.
* Note that DCD flow control stupidly uses the same state flag
* (TS_TTSTOP) as XON/XOFF flow control.
com
->state
&= ~CS_CTS_OFLOW
;
com
->state
|= CS_ODEVREADY
;
if (cflag
& CCTS_OFLOW
) {
com
->state
|= CS_CTS_OFLOW
;
if (!(com
->prev_modem_status
& MSR_CTS
))
com
->state
&= ~CS_ODEVREADY
;
siointr(unit
); /* recover from fiddling with CS_TTGO */
static int /* XXX - should be void */
if (tp
->t_state
& TS_TTSTOP
)
if (tp
->t_state
& TS_RTSBLOCK
) {
if (com
->mcr_image
& MCR_RTS
&& com
->state
& CS_RTS_IFLOW
)
outb(com
->modem_ctl_port
, com
->mcr_image
&= ~MCR_RTS
);
if (!(com
->mcr_image
& MCR_RTS
) && com
->iptr
< com
->ihighwater
)
outb(com
->modem_ctl_port
, com
->mcr_image
|= MCR_RTS
);
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
;
else if (RB_LEN(&tp
->t_out
) != 0) {
com
->ocount
= RB_CONTIGGET(&tp
->t_out
);
com
->obufend
= (com
->optr
= (u_char
*) tp
->t_out
.rb_hd
)
siointr(unit
); /* fake interrupt to start output */
com
= com_addr(UNIT(tp
->t_dev
));
if (tp
->t_state
& TS_TTSTOP
)
/* YYY maybe your card doesn't want IENABLE to be reset? */
outb(com
->modem_ctl_port
,
#endif /* COM_MULTIPORT */
outb(com
->modem_ctl_port
,
com
->mcr_image
= bits
| MCR_IENABLE
);
outb(com
->modem_ctl_port
, com
->mcr_image
|= bits
);
/* YYY maybe your card doesn't want IENABLE to be reset? */
outb(com
->modem_ctl_port
,
com
->mcr_image
&= ~(bits
));
#endif /* COM_MULTIPORT */
outb(com
->modem_ctl_port
,
com
->mcr_image
&= ~(bits
& ~MCR_IENABLE
));
bits
= com
->prev_modem_status
;
timeout((timeout_func_t
) comwakeup
, (caddr_t
) NULL
, 1);
/* schedule compoll() to run when the cpl allows */
/* recover from lost output interrupts */
for (unit
= 0; unit
< NSIO
; ++unit
) {
if (com
!= NULL
&& com
->state
>= (CS_BUSY
| CS_TTGO
)) {
softsio0() { compoll(); }
softsio1() { compoll(); }
softsio2() { compoll(); }
softsio3() { compoll(); }
softsio4() { compoll(); }
softsio5() { compoll(); }
softsio6() { compoll(); }
softsio7() { compoll(); }
softsio8() { compoll(); }
* Following are all routines needed for COM to act as console
* XXX - not tested in this version
* XXX - check that the corresponding serial interrupts are never enabled
#include "i386/i386/cons.h"
/* locate the major number */
for (commajor
= 0; commajor
< nchrdev
; commajor
++)
if (cdevsw
[commajor
].d_open
== (bogus_open_t
) sioopen
)
com_addr(unit
) = &com_structs
[unit
];
com_addr(unit
)->iobase
= CONADDR
;
/* make sure hardware exists? XXX */
/* initialize required fields */
cp
->cn_dev
= makedev(commajor
, unit
);
cp
->cn_tp
= &sio_tty
[unit
];
cp
->cn_pri
= CN_REMOTE
; /* Force a serial port console */
cominit(unit
, comdefaultrate
);
iobase
= com_addr(unit
)->iobase
;
outb(iobase
+ com_cfcr
, CFCR_DLAB
);
rate
= ttspeedtab(comdefaultrate
, comspeedtab
);
outb(iobase
+ com_data
, rate
& 0xFF);
outb(iobase
+ com_ier
, rate
>> 8);
outb(iobase
+ com_cfcr
, CFCR_8BITS
);
* XXX - fishy to enable interrupts and then poll.
* It shouldn't be necessary to ready the iir.
outb(iobase
+ com_ier
, IER_ERXRDY
| IER_ETXRDY
| IER_ERLS
| IER_EMSC
);
FIFO_ENABLE
| FIFO_RCV_RST
| FIFO_XMT_RST
| FIFO_TRIGGER_14
);
(void) inb(iobase
+ com_iir
);
iobase
= com_addr(UNIT(dev
))->iobase
;
while (!(inb(iobase
+ com_lsr
) & LSR_RXRDY
))
c
= inb(iobase
+ com_data
);
(void) inb(iobase
+ com_iir
);
iobase
= com_addr(UNIT(dev
))->iobase
;
(void) cominit(UNIT(dev
), comdefaultrate
);
/* wait for any pending transmission to finish */
while (!(inb(iobase
+ com_lsr
) & LSR_TXRDY
) && --timo
)
outb(iobase
+ com_data
, c
);
/* wait for this transmission to complete */
while (!(inb(iobase
+ com_lsr
) & LSR_TXRDY
) && --timo
)
/* clear any interrupts generated by this transmission */
(void) inb(iobase
+ com_iir
);
* 10 Feb 93 Jordan K. Hubbard Added select code
* 27 May 93 Rodney W. Grimes Stole the select code from com.c.pl5
register struct tty
*tp
= &sio_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
)