version 1.183, 2001/01/14 23:50:28 |
version 1.183.2.4, 2001/09/21 22:35:35 |
|
|
* @(#)com.c 7.5 (Berkeley) 5/16/91 |
* @(#)com.c 7.5 (Berkeley) 5/16/91 |
*/ |
*/ |
|
|
#if defined(__sparc__) || defined(__sparc_v9__) |
|
#define DDB_BREAK_CHAR 1 /* L1 or Stop key */ |
|
#endif |
|
|
|
/* |
/* |
* COM driver, uses National Semiconductor NS16450/NS16550AF UART |
* COM driver, uses National Semiconductor NS16450/NS16550AF UART |
* Supports automatic hardware flow control on StarTech ST16C650A UART |
* Supports automatic hardware flow control on StarTech ST16C650A UART |
*/ |
*/ |
|
|
|
#include "opt_com.h" |
#include "opt_ddb.h" |
#include "opt_ddb.h" |
#include "opt_ddbparam.h" |
#include "opt_ddbparam.h" |
#include "opt_com.h" |
#include "opt_kgdb.h" |
|
|
#include "rnd.h" |
#include "rnd.h" |
#if NRND > 0 && defined(RND_COM) |
#if NRND > 0 && defined(RND_COM) |
#include <sys/rnd.h> |
#include <sys/rnd.h> |
#endif |
#endif |
|
|
|
/* |
|
* Override cnmagic(9) macro before including <sys/systm.h>. |
|
* We need to know if cn_check_magic triggered debugger, so set a flag. |
|
* Callers of cn_check_magic must declare int cn_trapped = 0; |
|
* XXX: this is *ugly*! |
|
*/ |
|
#define cn_trap() \ |
|
do { \ |
|
console_debugger(); \ |
|
cn_trapped = 1; \ |
|
} while (/* CONSTCOND */ 0) |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
|
|
#define com_lcr com_cfcr |
#define com_lcr com_cfcr |
#include <dev/cons.h> |
#include <dev/cons.h> |
|
|
#include "com.h" |
|
|
|
#ifdef COM_HAYESP |
#ifdef COM_HAYESP |
int comprobeHAYESP __P((bus_space_handle_t hayespioh, struct com_softc *sc)); |
int comprobeHAYESP __P((bus_space_handle_t hayespioh, struct com_softc *sc)); |
#endif |
#endif |
|
|
#if defined(DDB) || defined(KGDB) |
|
static void com_enable_debugport __P((struct com_softc *)); |
static void com_enable_debugport __P((struct com_softc *)); |
#endif |
|
void com_config __P((struct com_softc *)); |
void com_config __P((struct com_softc *)); |
void com_shutdown __P((struct com_softc *)); |
void com_shutdown __P((struct com_softc *)); |
int comspeed __P((long, long)); |
int comspeed __P((long, long)); |
static u_char cflag2lcr __P((tcflag_t)); |
static u_char cflag2lcr __P((tcflag_t)); |
int comparam __P((struct tty *, struct termios *)); |
int comparam __P((struct tty *, struct termios *)); |
void comstart __P((struct tty *)); |
void comstart __P((struct tty *)); |
void comstop __P((struct tty *, int)); |
|
int comhwiflow __P((struct tty *, int)); |
int comhwiflow __P((struct tty *, int)); |
|
|
void com_loadchannelregs __P((struct com_softc *)); |
void com_loadchannelregs __P((struct com_softc *)); |
Line 146 void tiocm_to_com __P((struct com_softc |
|
Line 151 void tiocm_to_com __P((struct com_softc |
|
int com_to_tiocm __P((struct com_softc *)); |
int com_to_tiocm __P((struct com_softc *)); |
void com_iflush __P((struct com_softc *)); |
void com_iflush __P((struct com_softc *)); |
|
|
int com_common_getc __P((bus_space_tag_t, bus_space_handle_t)); |
int com_common_getc __P((dev_t, bus_space_tag_t, bus_space_handle_t)); |
void com_common_putc __P((bus_space_tag_t, bus_space_handle_t, int)); |
void com_common_putc __P((dev_t, bus_space_tag_t, bus_space_handle_t, int)); |
|
|
/* XXX: These belong elsewhere */ |
int cominit __P((bus_space_tag_t, bus_addr_t, int, int, tcflag_t, |
|
bus_space_handle_t *)); |
|
|
|
/* XXX: This belongs elsewhere */ |
cdev_decl(com); |
cdev_decl(com); |
bdev_decl(com); |
|
|
|
int comcngetc __P((dev_t)); |
int comcngetc __P((dev_t)); |
void comcnputc __P((dev_t, int)); |
void comcnputc __P((dev_t, int)); |
Line 192 static bus_space_handle_t comconsioh; |
|
Line 199 static bus_space_handle_t comconsioh; |
|
static int comconsattached; |
static int comconsattached; |
static int comconsrate; |
static int comconsrate; |
static tcflag_t comconscflag; |
static tcflag_t comconscflag; |
|
static struct cnm_state com_cnm_state; |
|
|
static int ppscap = |
static int ppscap = |
PPS_TSFMT_TSPEC | |
PPS_TSFMT_TSPEC | |
Line 382 comprobeHAYESP(hayespioh, sc) |
|
Line 390 comprobeHAYESP(hayespioh, sc) |
|
} |
} |
#endif |
#endif |
|
|
#if defined(DDB) || defined(KGDB) |
|
static void |
static void |
com_enable_debugport(sc) |
com_enable_debugport(sc) |
struct com_softc *sc; |
struct com_softc *sc; |
Line 399 com_enable_debugport(sc) |
|
Line 406 com_enable_debugport(sc) |
|
COM_UNLOCK(sc); |
COM_UNLOCK(sc); |
splx(s); |
splx(s); |
} |
} |
#endif |
|
|
|
void |
void |
com_attach_subr(sc) |
com_attach_subr(sc) |
Line 436 com_attach_subr(sc) |
|
Line 442 com_attach_subr(sc) |
|
} |
} |
|
|
#ifdef COM_HAYESP |
#ifdef COM_HAYESP |
|
sc->sc_prescaler = 0; /* set prescaler to x1. */ |
|
|
/* Look for a Hayes ESP board. */ |
/* Look for a Hayes ESP board. */ |
for (hayespp = hayesp_ports; *hayespp != 0; hayespp++) { |
for (hayespp = hayesp_ports; *hayespp != 0; hayespp++) { |
bus_space_handle_t hayespioh; |
bus_space_handle_t hayespioh; |
|
|
} |
} |
#endif |
#endif |
|
|
#ifdef DDB |
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE|COM_HW_KGDB)) |
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) |
|
com_enable_debugport(sc); |
com_enable_debugport(sc); |
#endif |
|
|
|
#ifdef KGDB |
|
/* |
|
* Allow kgdb to "take over" this port. If this is |
|
* the kgdb device, it has exclusive use. |
|
*/ |
|
if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) |
|
com_enable_debugport(sc); |
|
#endif |
|
} |
} |
|
|
int |
int |
Line 752 com_shutdown(sc) |
|
Line 749 com_shutdown(sc) |
|
} |
} |
|
|
/* Turn off interrupts. */ |
/* Turn off interrupts. */ |
#ifdef DDB |
|
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) |
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) |
sc->sc_ier = IER_ERXRDY; /* interrupt on break */ |
sc->sc_ier = IER_ERXRDY; /* interrupt on break */ |
else |
else |
#endif |
|
sc->sc_ier = 0; |
sc->sc_ier = 0; |
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier); |
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier); |
|
|
Line 986 comwrite(dev, uio, flag) |
|
Line 981 comwrite(dev, uio, flag) |
|
return ((*tp->t_linesw->l_write)(tp, uio, flag)); |
return ((*tp->t_linesw->l_write)(tp, uio, flag)); |
} |
} |
|
|
|
int |
|
compoll(dev, events, p) |
|
dev_t dev; |
|
int events; |
|
struct proc *p; |
|
{ |
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev)); |
|
struct tty *tp = sc->sc_tty; |
|
|
|
if (COM_ISALIVE(sc) == 0) |
|
return (EIO); |
|
|
|
return ((*tp->t_linesw->l_poll)(tp, events, p)); |
|
} |
|
|
struct tty * |
struct tty * |
comtty(dev) |
comtty(dev) |
dev_t dev; |
dev_t dev; |
Line 1348 comparam(tp, t) |
|
Line 1358 comparam(tp, t) |
|
struct termios *t; |
struct termios *t; |
{ |
{ |
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev)); |
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev)); |
int ospeed = comspeed(t->c_ospeed, sc->sc_frequency); |
int ospeed; |
u_char lcr; |
u_char lcr; |
int s; |
int s; |
|
|
if (COM_ISALIVE(sc) == 0) |
if (COM_ISALIVE(sc) == 0) |
return (EIO); |
return (EIO); |
|
|
|
#ifdef COM_HAYESP |
|
if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { |
|
int prescaler, speed; |
|
|
|
/* |
|
* Calculate UART clock prescaler. It should be in |
|
* range of 0 .. 3. |
|
*/ |
|
for (prescaler = 0, speed = t->c_ospeed; prescaler < 4; |
|
prescaler++, speed /= 2) |
|
if ((ospeed = comspeed(speed, sc->sc_frequency)) > 0) |
|
break; |
|
|
|
if (prescaler == 4) |
|
return (EINVAL); |
|
sc->sc_prescaler = prescaler; |
|
} else |
|
#endif |
|
ospeed = comspeed(t->c_ospeed, sc->sc_frequency); |
|
|
/* Check requested parameters. */ |
/* Check requested parameters. */ |
if (ospeed < 0) |
if (ospeed < 0) |
return (EINVAL); |
return (EINVAL); |
Line 1567 com_loadchannelregs(sc) |
|
Line 1597 com_loadchannelregs(sc) |
|
bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); |
bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); |
bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active = sc->sc_mcr); |
bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active = sc->sc_mcr); |
bus_space_write_1(iot, ioh, com_fifo, sc->sc_fifo); |
bus_space_write_1(iot, ioh, com_fifo, sc->sc_fifo); |
|
#ifdef COM_HAYESP |
|
if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { |
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD1, |
|
HAYESP_SETPRESCALER); |
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2, |
|
sc->sc_prescaler); |
|
} |
|
#endif |
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier); |
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier); |
} |
} |
|
|
put = sc->sc_rbput; |
put = sc->sc_rbput; |
cc = sc->sc_rbavail; |
cc = sc->sc_rbavail; |
|
|
do { |
again: do { |
u_char msr, delta; |
u_char msr, delta; |
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr); |
lsr = bus_space_read_1(iot, ioh, com_lsr); |
#if defined(DDB) || defined(KGDB) |
|
if (ISSET(lsr, LSR_BI)) { |
if (ISSET(lsr, LSR_BI)) { |
#ifndef DDB_BREAK_CHAR |
int cn_trapped = 0; |
#ifdef DDB |
cn_check_magic(sc->sc_tty->t_dev, |
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { |
CNC_BREAK, com_cnm_state); |
console_debugger(); |
if (cn_trapped) |
continue; |
continue; |
} |
#if defined(KGDB) |
#endif |
|
#ifdef KGDB |
|
if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) { |
if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) { |
kgdb_connect(1); |
kgdb_connect(1); |
continue; |
continue; |
} |
} |
#endif |
#endif |
#endif |
|
} |
} |
#endif /* DDB || KGDB */ |
|
|
|
if (ISSET(lsr, LSR_RCV_MASK) && |
if (ISSET(lsr, LSR_RCV_MASK) && |
!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) { |
!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) { |
while (cc > 0) { |
while (cc > 0) { |
|
int cn_trapped = 0; |
put[0] = bus_space_read_1(iot, ioh, com_data); |
put[0] = bus_space_read_1(iot, ioh, com_data); |
put[1] = lsr; |
put[1] = lsr; |
#if defined(DDB) && defined(DDB_BREAK_CHAR) |
cn_check_magic(sc->sc_tty->t_dev, |
if (put[0] == DDB_BREAK_CHAR && |
put[0], com_cnm_state); |
ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { |
if (cn_trapped) { |
console_debugger(); |
|
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr); |
lsr = bus_space_read_1(iot, ioh, com_lsr); |
if (!ISSET(lsr, LSR_RCV_MASK)) |
if (!ISSET(lsr, LSR_RCV_MASK)) |
break; |
break; |
|
|
continue; |
continue; |
} |
} |
#endif |
|
put += 2; |
put += 2; |
if (put >= end) |
if (put >= end) |
put = sc->sc_rbuf; |
put = sc->sc_rbuf; |
|
|
bus_space_write_1(iot, ioh, com_ier, 0); |
bus_space_write_1(iot, ioh, com_ier, 0); |
delay(10); |
delay(10); |
bus_space_write_1(iot, ioh, com_ier,sc->sc_ier); |
bus_space_write_1(iot, ioh, com_ier,sc->sc_ier); |
iir = IIR_NOPEND; |
|
continue; |
continue; |
} |
} |
} |
} |
|
|
|
|
sc->sc_st_check = 1; |
sc->sc_st_check = 1; |
} |
} |
} while (!ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND)); |
} while (ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_RXRDY) |
|
|| ((iir & IIR_IMASK) == 0)); |
|
|
/* |
/* |
* Done handling any receive interrupts. See if data can be |
* Done handling any receive interrupts. See if data can be |
|
|
} |
} |
} |
} |
} |
} |
|
|
|
if (!ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND)) |
|
goto again; |
|
|
COM_UNLOCK(sc); |
COM_UNLOCK(sc); |
|
|
/* Wake up the poller. */ |
/* Wake up the poller. */ |
|
|
/* |
/* |
* The following functions are polled getc and putc routines, shared |
* The following functions are polled getc and putc routines, shared |
* by the console and kgdb glue. |
* by the console and kgdb glue. |
|
* |
|
* The read-ahead code is so that you can detect pending in-band |
|
* cn_magic in polled mode while doing output rather than having to |
|
* wait until the kernel decides it needs input. |
*/ |
*/ |
|
|
#if defined(DDB) && defined(DDB_BREAK_CHAR) |
#define MAX_READAHEAD 20 |
#define MAX_UNGETC 20 |
static int com_readahead[MAX_READAHEAD]; |
static int com_ungetc[MAX_UNGETC]; |
static int com_readaheadcount = 0; |
static int com_ungetccount = 0; |
|
#endif |
|
|
|
int |
int |
com_common_getc(iot, ioh) |
com_common_getc(dev, iot, ioh) |
|
dev_t dev; |
bus_space_tag_t iot; |
bus_space_tag_t iot; |
bus_space_handle_t ioh; |
bus_space_handle_t ioh; |
{ |
{ |
int s = splserial(); |
int s = splserial(); |
u_char stat, c; |
u_char stat, c; |
|
|
#if defined(DDB) && defined(DDB_BREAK_CHAR) |
|
/* got a character from reading things earlier */ |
/* got a character from reading things earlier */ |
if (com_ungetccount > 0) { |
if (com_readaheadcount > 0) { |
int i; |
int i; |
|
|
c = com_ungetc[0]; |
c = com_readahead[0]; |
for (i = 1; i < com_ungetccount; i++) { |
for (i = 1; i < com_readaheadcount; i++) { |
com_ungetc[i -1] = com_ungetc[i]; |
com_readahead[i-1] = com_readahead[i]; |
} |
} |
com_ungetccount--; |
com_readaheadcount--; |
splx(s); |
splx(s); |
return (c); |
return (c); |
} |
} |
#endif |
|
|
|
/* block until a character becomes available */ |
/* block until a character becomes available */ |
while (!ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) |
while (!ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) |
Line 2260 com_common_getc(iot, ioh) |
|
Line 2296 com_common_getc(iot, ioh) |
|
|
|
c = bus_space_read_1(iot, ioh, com_data); |
c = bus_space_read_1(iot, ioh, com_data); |
stat = bus_space_read_1(iot, ioh, com_iir); |
stat = bus_space_read_1(iot, ioh, com_iir); |
#if defined(DDB) && defined(DDB_BREAK_CHAR) |
{ |
if (c == DDB_BREAK_CHAR) { |
int cn_trapped = 0; /* unused */ |
|
#ifdef DDB |
extern int db_active; |
extern int db_active; |
|
if (!db_active) |
if (db_active == 0) { |
|
console_debugger(); |
|
} |
|
} |
|
#endif |
#endif |
|
cn_check_magic(dev, c, com_cnm_state); |
|
} |
splx(s); |
splx(s); |
return (c); |
return (c); |
} |
} |
|
|
void |
void |
com_common_putc(iot, ioh, c) |
com_common_putc(dev, iot, ioh, c) |
|
dev_t dev; |
bus_space_tag_t iot; |
bus_space_tag_t iot; |
bus_space_handle_t ioh; |
bus_space_handle_t ioh; |
int c; |
int c; |
Line 2282 com_common_putc(iot, ioh, c) |
|
Line 2318 com_common_putc(iot, ioh, c) |
|
int s = splserial(); |
int s = splserial(); |
int timo; |
int timo; |
|
|
#if defined(DDB) && defined(DDB_BREAK_CHAR) |
|
int cin, stat; |
int cin, stat; |
if (com_ungetccount < MAX_UNGETC |
if (com_readaheadcount < MAX_READAHEAD |
&& ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) { |
&& ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) { |
|
int cn_trapped = 0; |
cin = bus_space_read_1(iot, ioh, com_data); |
cin = bus_space_read_1(iot, ioh, com_data); |
stat = bus_space_read_1(iot, ioh, com_iir); |
stat = bus_space_read_1(iot, ioh, com_iir); |
if (cin == DDB_BREAK_CHAR) { |
cn_check_magic(dev, cin, com_cnm_state); |
console_debugger(); |
com_readahead[com_readaheadcount++] = cin; |
} |
|
com_ungetc[com_ungetccount++] = cin; |
|
} |
} |
#endif |
|
|
|
/* wait for any pending transmission to finish */ |
/* wait for any pending transmission to finish */ |
timo = 150000; |
timo = 150000; |
Line 2366 comcnattach(iot, iobase, rate, frequency |
|
Line 2399 comcnattach(iot, iobase, rate, frequency |
|
return (res); |
return (res); |
|
|
cn_tab = &comcons; |
cn_tab = &comcons; |
|
cn_init_magic(&com_cnm_state); |
|
cn_set_magic("\047\001"); /* default magic is BREAK */ |
|
|
comconstag = iot; |
comconstag = iot; |
comconsaddr = iobase; |
comconsaddr = iobase; |
|
|
comcngetc(dev) |
comcngetc(dev) |
dev_t dev; |
dev_t dev; |
{ |
{ |
|
return (com_common_getc(dev, comconstag, comconsioh)); |
return (com_common_getc(comconstag, comconsioh)); |
|
} |
} |
|
|
/* |
/* |
Line 2391 comcnputc(dev, c) |
|
Line 2425 comcnputc(dev, c) |
|
dev_t dev; |
dev_t dev; |
int c; |
int c; |
{ |
{ |
|
com_common_putc(dev, comconstag, comconsioh, c); |
com_common_putc(comconstag, comconsioh, c); |
|
} |
} |
|
|
void |
void |
Line 2413 com_kgdb_attach(iot, iobase, rate, frequ |
|
Line 2446 com_kgdb_attach(iot, iobase, rate, frequ |
|
{ |
{ |
int res; |
int res; |
|
|
|
printf("com_kgdb_attach\n"); |
|
|
if (iot == comconstag && iobase == comconsaddr) |
if (iot == comconstag && iobase == comconsaddr) |
return (EBUSY); /* cannot share with console */ |
return (EBUSY); /* cannot share with console */ |
|
|
|
|
com_kgdb_getc(arg) |
com_kgdb_getc(arg) |
void *arg; |
void *arg; |
{ |
{ |
|
return (com_common_getc(NODEV, com_kgdb_iot, com_kgdb_ioh)); |
return (com_common_getc(com_kgdb_iot, com_kgdb_ioh)); |
|
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
Line 2444 com_kgdb_putc(arg, c) |
|
Line 2478 com_kgdb_putc(arg, c) |
|
void *arg; |
void *arg; |
int c; |
int c; |
{ |
{ |
|
com_common_putc(NODEV, com_kgdb_iot, com_kgdb_ioh, c); |
return (com_common_putc(com_kgdb_iot, com_kgdb_ioh, c)); |
|
} |
} |
#endif /* KGDB */ |
#endif /* KGDB */ |
|
|