diff options
author | bde <bde@FreeBSD.org> | 1998-12-17 17:40:13 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 1998-12-17 17:40:13 +0000 |
commit | 54f96be976a3971faa99b61949eabd13b4b341c4 (patch) | |
tree | dfcbe8c705e71c680ac8919008f3cf9259fcd888 /sys | |
parent | 7cdf796bab6a556fe3ed95398c479a01dcde9a15 (diff) | |
download | FreeBSD-src-54f96be976a3971faa99b61949eabd13b4b341c4.zip FreeBSD-src-54f96be976a3971faa99b61949eabd13b4b341c4.tar.gz |
Implemented sending of BREAKs. This is quite complicated because the
hardware is interrupt-driven to a fault and sending a BREAK requires
mode switching. Always running in the BREAK-capable mode as in PR 8318
would double the overhead for sending \0's.
Reminded by: PR 8318
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/cy/cy.c | 179 | ||||
-rw-r--r-- | sys/dev/cy/cy_isa.c | 179 | ||||
-rw-r--r-- | sys/dev/ic/cd1400.h | 7 | ||||
-rw-r--r-- | sys/i386/isa/cy.c | 179 | ||||
-rw-r--r-- | sys/i386/isa/ic/cd1400.h | 7 |
5 files changed, 507 insertions, 44 deletions
diff --git a/sys/dev/cy/cy.c b/sys/dev/cy/cy.c index d85b90c..08cf534 100644 --- a/sys/dev/cy/cy.c +++ b/sys/dev/cy/cy.c @@ -27,7 +27,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.75 1998/11/28 13:18:16 bde Exp $ + * $Id: cy.c,v 1.76 1998/11/28 15:48:09 bde Exp $ */ #include "opt_compat.h" @@ -37,7 +37,6 @@ /* * TODO: - * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. @@ -152,6 +151,16 @@ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F +/* + * ETC states. com->etc may also contain a hardware ETC command value, + * meaning that execution of that command is pending. + */ +#define ETC_NONE 0 /* we depend on bzero() setting this */ +#define ETC_BREAK_STARTING 1 +#define ETC_BREAK_STARTED 2 +#define ETC_BREAK_ENDING 3 +#define ETC_BREAK_ENDED 4 + #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 @@ -233,6 +242,7 @@ struct com_s { #if 0 u_char cfcr_image; /* copy of value written to CFCR */ #endif + u_char etc; /* pending Embedded Transmit Command */ u_char extra_state; /* more flag bits, separate for order trick */ #if 0 u_char fifo_image; /* copy of value written to FIFO */ @@ -345,6 +355,7 @@ ointhand2_t siointr; static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((struct com_s *com, int cmd)); +static void cd_etc __P((struct com_s *com, int etc)); static int cd_getreg __P((struct com_s *com, int reg)); static void cd_setreg __P((struct com_s *com, int reg, int val)); static timeout_t siodtrwakeup; @@ -871,6 +882,7 @@ sioclose(dev, flag, mode, p) com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); + cd_etc(com, CD1400_ETC_STOPBREAK); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); @@ -904,6 +916,13 @@ comhardclose(com) com->do_timestamp = 0; #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + /* XXX */ + disable_intr(); + com->etc = ETC_NONE; + cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); + enable_intr(); + cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); #endif { @@ -1339,6 +1358,77 @@ cont: & CD1400_xIVR_CHAN)); #endif + if (com->etc != ETC_NONE) { + if (com->intr_enable & CD1400_SRER_TXRDY) { + /* + * Here due to sloppy SRER_TXRDY + * enabling. Ignore. Come back when + * tx is empty. + */ + cd_outb(iobase, CD1400_SRER, cy_align, + com->intr_enable + = com->intr_enable + & ~CD1400_SRER_TXRDY + | CD1400_SRER_TXMPTY); + goto terminate_tx_service; + } + switch (com->etc) { + case CD1400_ETC_SENDBREAK: + case CD1400_ETC_STOPBREAK: + /* + * Start the command. Come back on + * next tx empty interrupt, hopefully + * after command has been executed. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] |= CD1400_COR2_ETC); + cd_outb(iobase, CD1400_TDR, cy_align, + CD1400_ETC_CMD); + cd_outb(iobase, CD1400_TDR, cy_align, + com->etc); + if (com->etc == CD1400_ETC_SENDBREAK) + com->etc = ETC_BREAK_STARTING; + else + com->etc = ETC_BREAK_ENDING; + goto terminate_tx_service; + case ETC_BREAK_STARTING: + /* + * BREAK is now on. Continue with + * SRER_TXMPTY processing, hopefully + * don't come back. + */ + com->etc = ETC_BREAK_STARTED; + break; + case ETC_BREAK_STARTED: + /* + * Came back due to sloppy SRER_TXMPTY + * enabling. Hope again. + */ + break; + case ETC_BREAK_ENDING: + /* + * BREAK is now off. Continue with + * SRER_TXMPTY processing and don't + * come back. The SWI handler will + * restart tx interrupts if necessary. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] + &= ~CD1400_COR2_ETC); + com->etc = ETC_BREAK_ENDED; + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); + } + break; + case ETC_BREAK_ENDED: + /* + * Shouldn't get here. Hope again. + */ + break; + } + } if (com->intr_enable & CD1400_SRER_TXMPTY) { if (!(com->extra_state & CSE_ODONE)) { com_events += LOTS_OF_EVENTS; @@ -1530,14 +1620,20 @@ sioioctl(dev, cmd, data, flag, p) return (error); } switch (cmd) { -#if 0 case TIOCSBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_SENDBREAK); +#endif break; case TIOCCBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_STOPBREAK); +#endif break; -#endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; @@ -1682,13 +1778,6 @@ repeat: (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } - if (com->state & CS_ODONE) { - disable_intr(); - com_events -= LOTS_OF_EVENTS; - com->state &= ~CS_ODONE; - enable_intr(); - (*linesw[tp->t_line].l_start)(tp); - } if (com->extra_state & CSE_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; @@ -1698,6 +1787,18 @@ repeat: tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); } + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup(&com->etc); + } + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; @@ -2257,26 +2358,35 @@ siostop(tp, rw) int rw; { struct com_s *com; + bool_t wakeup_etc; com = com_addr(DEV_TO_UNIT(tp->t_dev)); + wakeup_etc = FALSE; disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; - if (com->state & CS_ODONE) - com_events -= LOTS_OF_EVENTS; - com->state &= ~(CS_ODONE | CS_BUSY); if (com->extra_state & CSE_ODONE) { com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup_etc = TRUE; + } } com->tp->t_state &= ~TS_BUSY; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); + if (wakeup_etc) + wakeup(&com->etc); comstart(tp); /* XXX should clear h/w fifos too. */ @@ -2583,6 +2693,47 @@ cd1400_channel_cmd(com, cmd) cd_setreg(com, CD1400_CCR, cmd); } +static void +cd_etc(com, etc) + struct com_s *com; + int etc; +{ + /* + * We can't change the hardware's ETC state while there are any + * characters in the tx fifo, since those characters would be + * interpreted as commands! Unputting characters from the fifo + * is difficult, so we wait up to 12 character times for the fifo + * to drain. The command will be delayed for up to 2 character + * times for the tx to become empty. Unputting characters from + * the tx holding and shift registers is impossible, so we wait + * for the tx to become empty so that the command is sure to be + * executed soon after we issue it. + */ + disable_intr(); + if (com->etc == etc) { + enable_intr(); + goto wait; + } + if (etc == CD1400_ETC_SENDBREAK + && (com->etc == ETC_BREAK_STARTING + || com->etc == ETC_BREAK_STARTED) + || etc == CD1400_ETC_STOPBREAK + && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED + || com->etc == ETC_NONE)) { + enable_intr(); + return; + } + com->etc = etc; + cd_setreg(com, CD1400_SRER, + com->intr_enable + = com->intr_enable & ~CD1400_SRER_TXRDY | CD1400_SRER_TXMPTY); + enable_intr(); +wait: + while (com->etc == etc + && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) + continue; +} + static int cd_getreg(com, reg) struct com_s *com; diff --git a/sys/dev/cy/cy_isa.c b/sys/dev/cy/cy_isa.c index d85b90c..08cf534 100644 --- a/sys/dev/cy/cy_isa.c +++ b/sys/dev/cy/cy_isa.c @@ -27,7 +27,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.75 1998/11/28 13:18:16 bde Exp $ + * $Id: cy.c,v 1.76 1998/11/28 15:48:09 bde Exp $ */ #include "opt_compat.h" @@ -37,7 +37,6 @@ /* * TODO: - * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. @@ -152,6 +151,16 @@ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F +/* + * ETC states. com->etc may also contain a hardware ETC command value, + * meaning that execution of that command is pending. + */ +#define ETC_NONE 0 /* we depend on bzero() setting this */ +#define ETC_BREAK_STARTING 1 +#define ETC_BREAK_STARTED 2 +#define ETC_BREAK_ENDING 3 +#define ETC_BREAK_ENDED 4 + #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 @@ -233,6 +242,7 @@ struct com_s { #if 0 u_char cfcr_image; /* copy of value written to CFCR */ #endif + u_char etc; /* pending Embedded Transmit Command */ u_char extra_state; /* more flag bits, separate for order trick */ #if 0 u_char fifo_image; /* copy of value written to FIFO */ @@ -345,6 +355,7 @@ ointhand2_t siointr; static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((struct com_s *com, int cmd)); +static void cd_etc __P((struct com_s *com, int etc)); static int cd_getreg __P((struct com_s *com, int reg)); static void cd_setreg __P((struct com_s *com, int reg, int val)); static timeout_t siodtrwakeup; @@ -871,6 +882,7 @@ sioclose(dev, flag, mode, p) com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); + cd_etc(com, CD1400_ETC_STOPBREAK); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); @@ -904,6 +916,13 @@ comhardclose(com) com->do_timestamp = 0; #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + /* XXX */ + disable_intr(); + com->etc = ETC_NONE; + cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); + enable_intr(); + cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); #endif { @@ -1339,6 +1358,77 @@ cont: & CD1400_xIVR_CHAN)); #endif + if (com->etc != ETC_NONE) { + if (com->intr_enable & CD1400_SRER_TXRDY) { + /* + * Here due to sloppy SRER_TXRDY + * enabling. Ignore. Come back when + * tx is empty. + */ + cd_outb(iobase, CD1400_SRER, cy_align, + com->intr_enable + = com->intr_enable + & ~CD1400_SRER_TXRDY + | CD1400_SRER_TXMPTY); + goto terminate_tx_service; + } + switch (com->etc) { + case CD1400_ETC_SENDBREAK: + case CD1400_ETC_STOPBREAK: + /* + * Start the command. Come back on + * next tx empty interrupt, hopefully + * after command has been executed. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] |= CD1400_COR2_ETC); + cd_outb(iobase, CD1400_TDR, cy_align, + CD1400_ETC_CMD); + cd_outb(iobase, CD1400_TDR, cy_align, + com->etc); + if (com->etc == CD1400_ETC_SENDBREAK) + com->etc = ETC_BREAK_STARTING; + else + com->etc = ETC_BREAK_ENDING; + goto terminate_tx_service; + case ETC_BREAK_STARTING: + /* + * BREAK is now on. Continue with + * SRER_TXMPTY processing, hopefully + * don't come back. + */ + com->etc = ETC_BREAK_STARTED; + break; + case ETC_BREAK_STARTED: + /* + * Came back due to sloppy SRER_TXMPTY + * enabling. Hope again. + */ + break; + case ETC_BREAK_ENDING: + /* + * BREAK is now off. Continue with + * SRER_TXMPTY processing and don't + * come back. The SWI handler will + * restart tx interrupts if necessary. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] + &= ~CD1400_COR2_ETC); + com->etc = ETC_BREAK_ENDED; + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); + } + break; + case ETC_BREAK_ENDED: + /* + * Shouldn't get here. Hope again. + */ + break; + } + } if (com->intr_enable & CD1400_SRER_TXMPTY) { if (!(com->extra_state & CSE_ODONE)) { com_events += LOTS_OF_EVENTS; @@ -1530,14 +1620,20 @@ sioioctl(dev, cmd, data, flag, p) return (error); } switch (cmd) { -#if 0 case TIOCSBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_SENDBREAK); +#endif break; case TIOCCBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_STOPBREAK); +#endif break; -#endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; @@ -1682,13 +1778,6 @@ repeat: (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } - if (com->state & CS_ODONE) { - disable_intr(); - com_events -= LOTS_OF_EVENTS; - com->state &= ~CS_ODONE; - enable_intr(); - (*linesw[tp->t_line].l_start)(tp); - } if (com->extra_state & CSE_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; @@ -1698,6 +1787,18 @@ repeat: tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); } + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup(&com->etc); + } + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; @@ -2257,26 +2358,35 @@ siostop(tp, rw) int rw; { struct com_s *com; + bool_t wakeup_etc; com = com_addr(DEV_TO_UNIT(tp->t_dev)); + wakeup_etc = FALSE; disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; - if (com->state & CS_ODONE) - com_events -= LOTS_OF_EVENTS; - com->state &= ~(CS_ODONE | CS_BUSY); if (com->extra_state & CSE_ODONE) { com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup_etc = TRUE; + } } com->tp->t_state &= ~TS_BUSY; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); + if (wakeup_etc) + wakeup(&com->etc); comstart(tp); /* XXX should clear h/w fifos too. */ @@ -2583,6 +2693,47 @@ cd1400_channel_cmd(com, cmd) cd_setreg(com, CD1400_CCR, cmd); } +static void +cd_etc(com, etc) + struct com_s *com; + int etc; +{ + /* + * We can't change the hardware's ETC state while there are any + * characters in the tx fifo, since those characters would be + * interpreted as commands! Unputting characters from the fifo + * is difficult, so we wait up to 12 character times for the fifo + * to drain. The command will be delayed for up to 2 character + * times for the tx to become empty. Unputting characters from + * the tx holding and shift registers is impossible, so we wait + * for the tx to become empty so that the command is sure to be + * executed soon after we issue it. + */ + disable_intr(); + if (com->etc == etc) { + enable_intr(); + goto wait; + } + if (etc == CD1400_ETC_SENDBREAK + && (com->etc == ETC_BREAK_STARTING + || com->etc == ETC_BREAK_STARTED) + || etc == CD1400_ETC_STOPBREAK + && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED + || com->etc == ETC_NONE)) { + enable_intr(); + return; + } + com->etc = etc; + cd_setreg(com, CD1400_SRER, + com->intr_enable + = com->intr_enable & ~CD1400_SRER_TXRDY | CD1400_SRER_TXMPTY); + enable_intr(); +wait: + while (com->etc == etc + && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) + continue; +} + static int cd_getreg(com, reg) struct com_s *com; diff --git a/sys/dev/ic/cd1400.h b/sys/dev/ic/cd1400.h index 52081fe..ee9b9f2 100644 --- a/sys/dev/ic/cd1400.h +++ b/sys/dev/ic/cd1400.h @@ -27,7 +27,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id$ + * $Id: cd1400.h,v 1.4 1997/02/22 09:37:59 peter Exp $ */ /* @@ -99,6 +99,7 @@ #define CD1400_CCR_CMDRESET (1<<7) /* enables following: */ #define CD1400_CCR_FTF (1<<1) /* flush tx fifo */ #define CD1400_CCR_FULLRESET (1<<0) /* full reset */ +#define CD1400_CCR_CHANRESET 0 /* current channel */ #define CD1400_CCR_CMDCORCHG (1<<6) /* enables following: */ #define CD1400_CCR_COR3 (1<<3) /* COR3 changed */ #define CD1400_CCR_COR2 (1<<2) /* COR2 changed */ @@ -133,6 +134,10 @@ #define CD1400_COR2_IXANY (1<<7) /* implied XON mode */ #define CD1400_COR2_IXOFF (1<<6) /* in-band tx flow control */ #define CD1400_COR2_ETC (1<<5) /* embedded tx command */ +#define CD1400_ETC_CMD 0x00 /* start an ETC */ +#define CD1400_ETC_SENDBREAK 0x81 +#define CD1400_ETC_INSERTDELAY 0x82 +#define CD1400_ETC_STOPBREAK 0x83 #define CD1400_COR2_LLM (1<<4) /* local loopback mode */ #define CD1400_COR2_RLM (1<<3) /* remote loopback mode */ #define CD1400_COR2_RTSAO (1<<2) /* RTS auto output */ diff --git a/sys/i386/isa/cy.c b/sys/i386/isa/cy.c index d85b90c..08cf534 100644 --- a/sys/i386/isa/cy.c +++ b/sys/i386/isa/cy.c @@ -27,7 +27,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.75 1998/11/28 13:18:16 bde Exp $ + * $Id: cy.c,v 1.76 1998/11/28 15:48:09 bde Exp $ */ #include "opt_compat.h" @@ -37,7 +37,6 @@ /* * TODO: - * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. @@ -152,6 +151,16 @@ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F +/* + * ETC states. com->etc may also contain a hardware ETC command value, + * meaning that execution of that command is pending. + */ +#define ETC_NONE 0 /* we depend on bzero() setting this */ +#define ETC_BREAK_STARTING 1 +#define ETC_BREAK_STARTED 2 +#define ETC_BREAK_ENDING 3 +#define ETC_BREAK_ENDED 4 + #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 @@ -233,6 +242,7 @@ struct com_s { #if 0 u_char cfcr_image; /* copy of value written to CFCR */ #endif + u_char etc; /* pending Embedded Transmit Command */ u_char extra_state; /* more flag bits, separate for order trick */ #if 0 u_char fifo_image; /* copy of value written to FIFO */ @@ -345,6 +355,7 @@ ointhand2_t siointr; static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((struct com_s *com, int cmd)); +static void cd_etc __P((struct com_s *com, int etc)); static int cd_getreg __P((struct com_s *com, int reg)); static void cd_setreg __P((struct com_s *com, int reg, int val)); static timeout_t siodtrwakeup; @@ -871,6 +882,7 @@ sioclose(dev, flag, mode, p) com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); + cd_etc(com, CD1400_ETC_STOPBREAK); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); @@ -904,6 +916,13 @@ comhardclose(com) com->do_timestamp = 0; #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + /* XXX */ + disable_intr(); + com->etc = ETC_NONE; + cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); + enable_intr(); + cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); #endif { @@ -1339,6 +1358,77 @@ cont: & CD1400_xIVR_CHAN)); #endif + if (com->etc != ETC_NONE) { + if (com->intr_enable & CD1400_SRER_TXRDY) { + /* + * Here due to sloppy SRER_TXRDY + * enabling. Ignore. Come back when + * tx is empty. + */ + cd_outb(iobase, CD1400_SRER, cy_align, + com->intr_enable + = com->intr_enable + & ~CD1400_SRER_TXRDY + | CD1400_SRER_TXMPTY); + goto terminate_tx_service; + } + switch (com->etc) { + case CD1400_ETC_SENDBREAK: + case CD1400_ETC_STOPBREAK: + /* + * Start the command. Come back on + * next tx empty interrupt, hopefully + * after command has been executed. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] |= CD1400_COR2_ETC); + cd_outb(iobase, CD1400_TDR, cy_align, + CD1400_ETC_CMD); + cd_outb(iobase, CD1400_TDR, cy_align, + com->etc); + if (com->etc == CD1400_ETC_SENDBREAK) + com->etc = ETC_BREAK_STARTING; + else + com->etc = ETC_BREAK_ENDING; + goto terminate_tx_service; + case ETC_BREAK_STARTING: + /* + * BREAK is now on. Continue with + * SRER_TXMPTY processing, hopefully + * don't come back. + */ + com->etc = ETC_BREAK_STARTED; + break; + case ETC_BREAK_STARTED: + /* + * Came back due to sloppy SRER_TXMPTY + * enabling. Hope again. + */ + break; + case ETC_BREAK_ENDING: + /* + * BREAK is now off. Continue with + * SRER_TXMPTY processing and don't + * come back. The SWI handler will + * restart tx interrupts if necessary. + */ + cd_outb(iobase, CD1400_COR2, cy_align, + com->cor[1] + &= ~CD1400_COR2_ETC); + com->etc = ETC_BREAK_ENDED; + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); + } + break; + case ETC_BREAK_ENDED: + /* + * Shouldn't get here. Hope again. + */ + break; + } + } if (com->intr_enable & CD1400_SRER_TXMPTY) { if (!(com->extra_state & CSE_ODONE)) { com_events += LOTS_OF_EVENTS; @@ -1530,14 +1620,20 @@ sioioctl(dev, cmd, data, flag, p) return (error); } switch (cmd) { -#if 0 case TIOCSBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_SENDBREAK); +#endif break; case TIOCCBRK: +#if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#else + cd_etc(com, CD1400_ETC_STOPBREAK); +#endif break; -#endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; @@ -1682,13 +1778,6 @@ repeat: (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } - if (com->state & CS_ODONE) { - disable_intr(); - com_events -= LOTS_OF_EVENTS; - com->state &= ~CS_ODONE; - enable_intr(); - (*linesw[tp->t_line].l_start)(tp); - } if (com->extra_state & CSE_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; @@ -1698,6 +1787,18 @@ repeat: tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); } + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup(&com->etc); + } + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; @@ -2257,26 +2358,35 @@ siostop(tp, rw) int rw; { struct com_s *com; + bool_t wakeup_etc; com = com_addr(DEV_TO_UNIT(tp->t_dev)); + wakeup_etc = FALSE; disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; - if (com->state & CS_ODONE) - com_events -= LOTS_OF_EVENTS; - com->state &= ~(CS_ODONE | CS_BUSY); if (com->extra_state & CSE_ODONE) { com_events -= LOTS_OF_EVENTS; com->extra_state &= ~CSE_ODONE; + if (com->etc != ETC_NONE) { + if (com->etc == ETC_BREAK_ENDED) + com->etc = ETC_NONE; + wakeup_etc = TRUE; + } } com->tp->t_state &= ~TS_BUSY; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); + if (wakeup_etc) + wakeup(&com->etc); comstart(tp); /* XXX should clear h/w fifos too. */ @@ -2583,6 +2693,47 @@ cd1400_channel_cmd(com, cmd) cd_setreg(com, CD1400_CCR, cmd); } +static void +cd_etc(com, etc) + struct com_s *com; + int etc; +{ + /* + * We can't change the hardware's ETC state while there are any + * characters in the tx fifo, since those characters would be + * interpreted as commands! Unputting characters from the fifo + * is difficult, so we wait up to 12 character times for the fifo + * to drain. The command will be delayed for up to 2 character + * times for the tx to become empty. Unputting characters from + * the tx holding and shift registers is impossible, so we wait + * for the tx to become empty so that the command is sure to be + * executed soon after we issue it. + */ + disable_intr(); + if (com->etc == etc) { + enable_intr(); + goto wait; + } + if (etc == CD1400_ETC_SENDBREAK + && (com->etc == ETC_BREAK_STARTING + || com->etc == ETC_BREAK_STARTED) + || etc == CD1400_ETC_STOPBREAK + && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED + || com->etc == ETC_NONE)) { + enable_intr(); + return; + } + com->etc = etc; + cd_setreg(com, CD1400_SRER, + com->intr_enable + = com->intr_enable & ~CD1400_SRER_TXRDY | CD1400_SRER_TXMPTY); + enable_intr(); +wait: + while (com->etc == etc + && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) + continue; +} + static int cd_getreg(com, reg) struct com_s *com; diff --git a/sys/i386/isa/ic/cd1400.h b/sys/i386/isa/ic/cd1400.h index 52081fe..ee9b9f2 100644 --- a/sys/i386/isa/ic/cd1400.h +++ b/sys/i386/isa/ic/cd1400.h @@ -27,7 +27,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id$ + * $Id: cd1400.h,v 1.4 1997/02/22 09:37:59 peter Exp $ */ /* @@ -99,6 +99,7 @@ #define CD1400_CCR_CMDRESET (1<<7) /* enables following: */ #define CD1400_CCR_FTF (1<<1) /* flush tx fifo */ #define CD1400_CCR_FULLRESET (1<<0) /* full reset */ +#define CD1400_CCR_CHANRESET 0 /* current channel */ #define CD1400_CCR_CMDCORCHG (1<<6) /* enables following: */ #define CD1400_CCR_COR3 (1<<3) /* COR3 changed */ #define CD1400_CCR_COR2 (1<<2) /* COR2 changed */ @@ -133,6 +134,10 @@ #define CD1400_COR2_IXANY (1<<7) /* implied XON mode */ #define CD1400_COR2_IXOFF (1<<6) /* in-band tx flow control */ #define CD1400_COR2_ETC (1<<5) /* embedded tx command */ +#define CD1400_ETC_CMD 0x00 /* start an ETC */ +#define CD1400_ETC_SENDBREAK 0x81 +#define CD1400_ETC_INSERTDELAY 0x82 +#define CD1400_ETC_STOPBREAK 0x83 #define CD1400_COR2_LLM (1<<4) /* local loopback mode */ #define CD1400_COR2_RLM (1<<3) /* remote loopback mode */ #define CD1400_COR2_RTSAO (1<<2) /* RTS auto output */ |