/* * Copyright (c) 2006 Jon Sevy * All rights reserved. * * Based on ixp12x0_com.c * * Copyright (c) 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA and Naoto Shimazaki. * * This code is derived from software contributed to The NetBSD Foundation * by IWAMOTO Toshihiro. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 SUCH DAMAGE. */ /* * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 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. 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 * SUCH DAMAGE. * * @(#)com.c 7.5 (Berkeley) 5/16/91 */ #include __KERNEL_RCSID(0, "$NetBSD: vx115_com.c,v 1.19 2003/08/07 16:26:53 agc Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" #include "rnd.h" #if NRND > 0 && defined(RND_COM) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_COM //#define LOWLEVEL_DEBUG_COM #ifdef DEBUG_COM #define DPRINTF(fmt...) printf(fmt) void COM_DEBUG_PRINT_STRING(char *str); #ifdef LOWLEVEL_DEBUG_COM #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) COM_DEBUG_PRINT_STRING(str) #else #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) #endif #else #define DPRINTF(fmt...) #define COM_DEBUG_PRINT_STRING(str) #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) #endif /* Vx115 com softc struct */ struct vx115_com_softc { struct device sc_dev; bus_addr_t sc_addr; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; void *sc_softintr; struct tty *sc_tty; u_char *sc_rbuf, *sc_ebuf; u_char *sc_tba; u_int sc_tbc, sc_heldtbc; u_char *sc_rbget,*sc_rbput; u_int sc_rbavail; /* status flags */ u_int sc_hwflags, sc_swflags; u_int sc_rx_flags; int sc_tx_busy; int sc_tx_stopped; int sc_rx_ready; int sc_heldchange; /* control registers */ u_int sc_baud_divisor; u_int sc_rx_control; u_int sc_tx_control; u_int sc_char_counter_control; /* power management hooks */ int (*enable)(struct vx115_com_softc *); int (*disable)(struct vx115_com_softc *); int enabled; }; /* Vx115 com console softc struct */ struct vx115_com_cons_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_addr_t sc_addr; int sc_ospeed; tcflag_t sc_cflag; int sc_attached; }; /* Utility functions */ static void vx115_com_break(struct vx115_com_softc *sc, int onoff); static void vx115_com_shutdown(struct vx115_com_softc *sc); static void vx115_com_iflush(struct vx115_com_softc *); static void vx115_com_set_cr(struct vx115_com_softc *); static u_int cflag_to_rx_control(tcflag_t cflag); static u_int cflag_to_tx_control(tcflag_t cflag); /* tty functions */ static int vx115_com_param(struct tty *, struct termios *); static int vx115_com_hwiflow(struct tty *, int); static void vx115_com_start(struct tty *); /* Console functions */ static int vx115_com_cngetc(dev_t); static void vx115_com_cnputc(dev_t, int); static void vx115_com_cnpollc(dev_t, int); /* Soft interrupt functions */ static void vx115_com_soft(void* arg); inline static void vx115_com_tx_soft(struct vx115_com_softc *, struct tty *); inline static void vx115_com_rx_soft(struct vx115_com_softc *, struct tty *); /* Hardware interrupt functions */ static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh); static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh); static struct vx115_com_cons_softc vx115_com_cn_sc; static struct cnm_state vx115_com_cnm_state; extern struct cfdriver vx115_com_cd; dev_type_open(vx115_com_open); dev_type_close(vx115_com_close); dev_type_read(vx115_com_read); dev_type_write(vx115_com_write); dev_type_ioctl(vx115_com_ioctl); dev_type_stop(vx115_com_stop); dev_type_tty(vx115_com_tty); dev_type_poll(vx115_com_poll); /* char dev switch structure for tty */ const struct cdevsw vx115_com_cdevsw = { vx115_com_open, vx115_com_close, vx115_com_read, vx115_com_write, vx115_com_ioctl, vx115_com_stop, vx115_com_tty, vx115_com_poll, nommap, /* mmap not supported */ ttykqfilter, D_TTY }; /* console method struct */ struct consdev vx115_com_cons = { NULL, /* cn_probe: probe hardware and fill in consdev info */ NULL, /* cn_init: turn on as console */ vx115_com_cngetc, /* cn_getc: kernel getchar interface */ vx115_com_cnputc, /* cn_putc: kernel putchar interface */ vx115_com_cnpollc, /* cn_pollc: turn on and off polling */ NULL, /* cn_bell: ring bell */ NULL, /* cn_halt: stop device */ NULL, /* cn_flush: flush output */ NODEV, /* major/minor of device */ CN_NORMAL /* priority */ }; #ifndef DEFAULT_COMSPEED #define DEFAULT_COMSPEED 115200 #endif #define COMUNIT_MASK 0x7ffff #define COMDIALOUT_MASK 0x80000 #define COMUNIT(x) (minor(x) & COMUNIT_MASK) #define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK) #define COM_ISALIVE(sc) ((sc)->enabled != 0 && ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE)) #define COM_BARRIER(t,h,f) bus_space_barrier((t), (h), 0, COM_NPORTS, (f)) /* we're uniprocessor, so no need for locks */ #define COM_LOCK(sc) #define COM_UNLOCK(sc) /* macros to make code more readable */ #define REG_READ(offset) bus_space_read_4(iot, ioh, offset) #define REG_WRITE(offset,value) bus_space_write_4(iot, ioh, offset, value) #define SET(t,f) (t) |= (f) #define CLR(t,f) (t) &= ~(f) #define ISSET(t,f) ((t) & (f)) /* receive interrupts: FIFO threshold */ #define RECEIVE_INTERRUPT_MASK ASCC_RX_CTRL_FICE_TH /* character counter interrupt mask */ #define CHAR_CNT_CNTL_INTERRUPT_MASK (ASCC_CHAR_COUNTER_CONTROL_ENABLE_FREERUNNING | ASCC_CHAR_COUNTER_CONTROL_INT_ENABLE) /* transmit interrupts: FIFO threshold */ #define TRANSMIT_INTERRUPT_MASK ASCC_TX_CTRL_FICE_TH #ifdef DEBUG_COM void COM_DEBUG_PRINT_STRING(char *str) { while(*str != '\0') { bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; /* spin while tx fifo full */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ; /* write the next char */ REG_WRITE(ASCC_TX_RX_FIFO, *str++); } } #endif static int vx115_com_match(struct device *, struct cfdata *, void *); static void vx115_com_attach(struct device *, struct device *, void *); static void vx115_com_attach_subr(struct vx115_com_softc *sc); static int vx115_com_intr(void* arg); CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc), vx115_com_match, vx115_com_attach, NULL, NULL); /********************************************************************* * Autoconfig functions */ static int vx115_com_match(struct device *parent, struct cfdata *match, void *aux) { COM_DEBUG_PRINT_STRING("vx115_com_match\n"); if (strcmp(match->cf_name, "vx115_com") == 0) return 1; return 0; } static void vx115_com_attach(struct device *parent, struct device *self, void *aux) { struct vx115_com_softc *sc = (struct vx115_com_softc *)self; struct vx115_attach_args *sa = (struct vx115_attach_args *)aux; sc->sc_iot = sa->sa_iot; sc->sc_addr = sa->sa_addr; printf("\n"); COM_DEBUG_PRINT_STRING("vx115_com_attach\n"); bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh); vx115_com_attach_subr(sc); vx115_intr_establish(sa->sa_intr, IPL_SERIAL, vx115_com_intr, sc); vx115_configure_irq(sa->sa_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH); vx115_enable_irq(sa->sa_intr); } static void vx115_com_attach_subr(struct vx115_com_softc *sc) { struct tty *tp; COM_DEBUG_PRINT_STRING("vx115_com_attach_subr\n"); /* check if this is com port for console */ if ((sc->sc_iot == vx115_com_cn_sc.sc_iot) && (sc->sc_addr == vx115_com_cn_sc.sc_addr)) { vx115_com_cn_sc.sc_attached = 1; //delay(10000); /* wait for output to finish */ /* make sure the console is always "hardwired" */ SET(sc->sc_hwflags, COM_HW_CONSOLE); SET(sc->sc_swflags, TIOCFLAG_SOFTCAR); } tp = ttymalloc(); tp->t_oproc = vx115_com_start; tp->t_param = vx115_com_param; tp->t_hwiflow = vx115_com_hwiflow; sc->sc_tty = tp; sc->sc_rbuf = malloc(VX115_COM_RING_SIZE << 1, M_DEVBUF, M_NOWAIT); sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; sc->sc_rbavail = VX115_COM_RING_SIZE; if (sc->sc_rbuf == NULL) { printf("%s: unable to allocate ring buffer\n", sc->sc_dev.dv_xname); return; } sc->sc_ebuf = sc->sc_rbuf + (VX115_COM_RING_SIZE << 1); sc->sc_tbc = 0; /* initalize control registers: 8-B-1 and appropriate baud rate */ /* set FIFO thresholds to the halfway point */ sc->sc_baud_divisor = VX115_BAUD_DIVISOR(DEFAULT_COMSPEED); sc->sc_rx_control = ASCC_RX_CTRL_RXCHSIZE_8 | ASCC_RX_CTRL_RXP_NO | ASCC_RX_CTRL_RXTH32 | ASCC_RX_CTRL_BRDEN; sc->sc_tx_control = ASCC_TX_CTRL_TXCHSIZE_8 | ASCC_TX_CTRL_TXP_NO | ASCC_TX_CTRL_TXTH32; /* clear disable bit, if set */ sc->sc_rx_control &= ~ASCC_RX_CTRL_RXID; sc->sc_tx_control &= ~ASCC_TX_CTRL_TXID; /* also set the rx timeout count */ sc->sc_char_counter_control = (0x100 & ASCC_CHAR_COUNTER_CONTROL_COUNT_MASK); tty_attach(tp); if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { int maj; /* locate the major number */ maj = cdevsw_lookup_major(&vx115_com_cdevsw); cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); aprint_normal("%s: major = %i: console\n", sc->sc_dev.dv_xname, maj); } sc->sc_softintr = softintr_establish(IPL_SOFTSERIAL, vx115_com_soft, sc); #if NRND > 0 && defined(RND_COM) rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname, RND_TYPE_TTY, 0); #endif /* if there are no enable/disable functions, assume the device is always enabled */ if (!sc->enable) sc->enabled = 1; SET(sc->sc_hwflags, COM_HW_DEV_OK); } /************************************************************** * cdevsw functions */ int vx115_com_open(dev_t dev, int flag, int mode, struct proc *p) { struct vx115_com_softc *sc; struct tty *tp; int s, s2; int error; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_open\n"); sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) || sc->sc_rbuf == NULL) return (ENXIO); if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) return (ENXIO); #ifdef KGDB /* * If this is the kgdb port, no other use is permitted. */ if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) return (EBUSY); #endif tp = sc->sc_tty; if (ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); s = spltty(); /* * Do the following iff this is a first open. */ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { struct termios t; tp->t_dev = dev; s2 = splserial(); COM_LOCK(sc); if (sc->enable) { if ((*sc->enable)(sc)) { COM_UNLOCK(sc); splx(s2); splx(s); printf("%s: device enable failed\n", sc->sc_dev.dv_xname); return (EIO); } sc->enabled = 1; } /* Turn on receive interrupts. */ sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); COM_UNLOCK(sc); splx(s2); /* * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ t.c_ispeed = 0; if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { t.c_ospeed = vx115_com_cn_sc.sc_ospeed; t.c_cflag = vx115_com_cn_sc.sc_cflag; } else { t.c_ospeed = TTYDEF_SPEED; t.c_cflag = TTYDEF_CFLAG; } if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) SET(t.c_cflag, CLOCAL); if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) SET(t.c_cflag, CRTSCTS); if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) SET(t.c_cflag, MDMBUF); /* Make sure vx115_com_param() will do something. */ tp->t_ospeed = 0; (void) vx115_com_param(tp, &t); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; ttychars(tp); ttsetwater(tp); s2 = splserial(); COM_LOCK(sc); /* Clear the input ring, and unblock. */ sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; sc->sc_rbavail = VX115_COM_RING_SIZE; vx115_com_iflush(sc); CLR(sc->sc_rx_flags, RX_ANY_BLOCK); #ifdef COM_DEBUG if (vx115_com_debug) comstatus(sc, "vx115_com_open "); #endif COM_UNLOCK(sc); splx(s2); } splx(s); error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); if (error) goto bad; error = (*tp->t_linesw->l_open)(dev, tp); if (error) goto bad; return (0); bad: if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { /* * We failed to open the device, and nobody else had it opened. * Clean up the state as appropriate. */ vx115_com_shutdown(sc); } return (error); } int vx115_com_close(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_close\n"); /* XXX This is for cons.c. */ if (!ISSET(tp->t_state, TS_ISOPEN)) return (0); (*tp->t_linesw->l_close)(tp, flag); ttyclose(tp); if (COM_ISALIVE(sc) == 0) return (0); if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { /* * Although we got a last close, the device may still be in * use; e.g. if this was the dialout node, and there are still * processes waiting for carrier on the non-dialout node. */ vx115_com_shutdown(sc); } return (0); } int vx115_com_read(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_read\n"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_read)(tp, uio, flag)); } int vx115_com_write(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_write\n"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_write)(tp, uio, flag)); } int vx115_com_poll(dev, events, p) dev_t dev; int events; struct proc *p; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_poll\n"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_poll)(tp, events, p)); } /* Stop output on a line. */ void vx115_com_stop(struct tty *tp, int flag) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_stop\n"); s = splserial(); COM_LOCK(sc); if (ISSET(tp->t_state, TS_BUSY)) { /* Stop transmitting at the next chunk. */ sc->sc_tbc = 0; sc->sc_heldtbc = 0; if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } COM_UNLOCK(sc); splx(s); } struct tty * vx115_com_tty(dev_t dev) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tty\n"); return (tp); } int vx115_com_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; int error; int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_ioctl\n"); if (COM_ISALIVE(sc) == 0) return (EIO); error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); error = ttioctl(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); error = 0; s = splserial(); COM_LOCK(sc); switch (cmd) { case TIOCSBRK: vx115_com_break(sc, 1); break; case TIOCCBRK: vx115_com_break(sc, 0); break; case TIOCGFLAGS: *(int *)data = sc->sc_swflags; break; case TIOCSFLAGS: error = suser(p->p_ucred, &p->p_acflag); if (error) break; sc->sc_swflags = *(int *)data; break; default: error = EPASSTHROUGH; break; } COM_UNLOCK(sc); splx(s); return (error); } /************************************************************** * Utility functions */ /* Flush the hardware receive FIFO */ static void vx115_com_iflush(struct vx115_com_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int timo; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_iflush\n"); timo = 50000; /* flush any pending I/O */ while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) && --timo ) { REG_READ(ASCC_TX_RX_FIFO); } #ifdef DIAGNOSTIC if (!timo) printf("%s: com_iflush timeout\n", sc->sc_dev.dv_xname); #endif } /* Set or clear generation of break condition */ static void vx115_com_break(struct vx115_com_softc *sc, int onoff) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_break\n"); if (onoff) sc->sc_tx_control |= ASCC_TX_CTRL_BREAK; else sc->sc_tx_control &= ~ASCC_TX_CTRL_BREAK; if (!sc->sc_heldchange) { if (sc->sc_tx_busy) { sc->sc_heldtbc = sc->sc_tbc; sc->sc_tbc = 0; sc->sc_heldchange = 1; } else { vx115_com_set_cr(sc); } } } static void vx115_com_shutdown(struct vx115_com_softc *sc) { int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_shutdown\n"); s = splserial(); COM_LOCK(sc); /* Clear any break condition set with TIOCSBRK. */ vx115_com_break(sc, 0); /* Turn off interrupts. */ sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; /* set disable bit */ sc->sc_rx_control |= ASCC_RX_CTRL_RXID; sc->sc_tx_control |= ASCC_TX_CTRL_TXID; vx115_com_set_cr(sc); if (sc->disable) { #ifdef DIAGNOSTIC if (!sc->enabled) panic("vx115_com_shutdown: not enabled?"); #endif (*sc->disable)(sc); sc->enabled = 0; } COM_UNLOCK(sc); splx(s); } static u_int cflag_to_rx_control(tcflag_t cflag) { u_int cr = 0; COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_rx_control\n"); if (cflag & PARENB) cr |= (cflag & PARODD) ? ASCC_RX_CTRL_RXP_ODD : ASCC_RX_CTRL_RXP_EVEN; cr |= ((cflag & CSIZE) == CS8) ? ASCC_RX_CTRL_RXCHSIZE_8 : ASCC_RX_CTRL_RXCHSIZE_7; cr |= (cflag & CSTOPB) ? ASCC_RX_CTRL_RXSTOP2 : ASCC_RX_CTRL_RXSTOP1; return (cr); } static u_int cflag_to_tx_control(tcflag_t cflag) { u_int cr = 0; COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_tx_control\n"); if (cflag & PARENB) cr |= (cflag & PARODD) ? ASCC_TX_CTRL_TXP_ODD : ASCC_TX_CTRL_TXP_EVEN; cr |= ((cflag & CSIZE) == CS8) ? ASCC_TX_CTRL_TXCHSIZE_8 : ASCC_TX_CTRL_TXCHSIZE_7; cr |= (cflag & CSTOPB) ? ASCC_TX_CTRL_TXSTOP2 : ASCC_TX_CTRL_TXSTOP1; return (cr); } static void vx115_com_set_cr(struct vx115_com_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_set_cr\n"); /* set control regs and baud divisor */ REG_WRITE(ASCC_RX_CONTROL, sc->sc_rx_control); REG_WRITE(ASCC_TX_CONTROL, sc->sc_tx_control); REG_WRITE(ASCC_BAUD_DIVISOR, sc->sc_baud_divisor); REG_WRITE(ASCC_CHAR_COUNTER_CONTROL, sc->sc_char_counter_control); } /************************************************************************** * tty functions * Assigned to associated tty struct fields in vx115_com_open */ /* Assigned to standard termios line discipline t_linesw->l_start method. */ /* Called from within our vx115_txsoft method. */ static void vx115_com_start(struct tty *tp) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start\n"); if (COM_ISALIVE(sc) == 0) return; s = spltty(); if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) goto out; if (sc->sc_tx_stopped) goto out; if (tp->t_outq.c_cc <= tp->t_lowat) { if (ISSET(tp->t_state, TS_ASLEEP)) { CLR(tp->t_state, TS_ASLEEP); wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); if (tp->t_outq.c_cc == 0) goto out; } /* output the first contiguous region of buffer space. */ /* set spl to splserial to prevent race condition on sc_tbc,sc_tba, sc_tx_busy */ (void)splserial(); COM_LOCK(sc); sc->sc_tba = tp->t_outq.c_cf; sc->sc_tbc = ndqb(&tp->t_outq, 0); if (sc->sc_tbc > 0) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start: sc->sc_tbc > 0\n"); SET(tp->t_state, TS_BUSY); sc->sc_tx_busy = 1; /* Enable transmit interrupts */ sc->sc_tx_control |= TRANSMIT_INTERRUPT_MASK; vx115_com_set_cr(sc); } COM_UNLOCK(sc); out: splx(s); return; } static int vx115_com_param(struct tty *tp, struct termios *t) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_param\n"); if (COM_ISALIVE(sc) == 0) return (EIO); if (t->c_ispeed && t->c_ispeed != t->c_ospeed) return (EINVAL); sc->sc_baud_divisor = VX115_BAUD_DIVISOR(t->c_ospeed); /* * For the console, always force CLOCAL and !HUPCL, so that the port * is always active. */ if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { SET(t->c_cflag, CLOCAL); CLR(t->c_cflag, HUPCL); } /* * If there were no changes, don't do anything. This avoids dropping * input and improves performance when all we did was frob things like * VMIN and VTIME. */ if ((tp->t_ospeed == t->c_ospeed) && (tp->t_cflag == t->c_cflag)) return (0); sc->sc_rx_control |= cflag_to_rx_control(t->c_cflag); sc->sc_tx_control |= cflag_to_tx_control(t->c_cflag); s = splserial(); COM_LOCK(sc); /* don't use hardware flow control */ /* copy to tty */ tp->t_ispeed = 0; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; if (!sc->sc_heldchange) { if (sc->sc_tx_busy) { sc->sc_heldtbc = sc->sc_tbc; sc->sc_tbc = 0; sc->sc_heldchange = 1; } else vx115_com_set_cr(sc); } COM_UNLOCK(sc); splx(s); /* * Update the tty layer's idea of the carrier bit. * We tell tty the carrier is always on. */ (void) (*tp->t_linesw->l_modem)(tp, 1); #ifdef COM_DEBUG if (com_debug) comstatus(sc, "comparam "); #endif if (!ISSET(t->c_cflag, CHWFLOW)) { if (sc->sc_tx_stopped) { sc->sc_tx_stopped = 0; vx115_com_start(tp); } } return (0); } static int vx115_com_hwiflow(tp, block) struct tty *tp; int block; { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_hwiflow\n"); return (0); } /************************************************************** * Console functions */ int vx115_com_cnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh, int ospeed, tcflag_t cflag) { unsigned int baud_divisor, rx_control, tx_control; cn_tab = &vx115_com_cons; cn_init_magic(&vx115_com_cnm_state); //cn_set_magic("\047\001"); /* default magic is BREAK */ vx115_com_cn_sc.sc_iot = iot; vx115_com_cn_sc.sc_ioh = ioh; vx115_com_cn_sc.sc_addr = iobase; vx115_com_cn_sc.sc_ospeed = ospeed; vx115_com_cn_sc.sc_cflag = cflag; baud_divisor = VX115_BAUD_DIVISOR(ospeed); rx_control = cflag_to_rx_control(cflag); tx_control = cflag_to_tx_control(cflag); /* set rx and tx to interrupt on threshold achievement, */ /* and set thresholds to the halfway point */ rx_control |= ASCC_RX_CTRL_FICE_TH | ASCC_RX_CTRL_RXTH32; tx_control |= ASCC_TX_CTRL_FICE_TH | ASCC_TX_CTRL_TXTH32; /* clear disable bit, if set */ rx_control &= ~ASCC_RX_CTRL_RXID; tx_control &= ~ASCC_TX_CTRL_TXID; /* enable the UART; note that interrupts will not yet be hooked up */ /* however, the kernel printf function depends only on vx115_com_cnputc, */ /* which doesn't require that interrupts be ready, so we get output */ bus_space_write_4(iot, ioh, ASCC_BAUD_DIVISOR, baud_divisor); bus_space_write_4(iot, ioh, ASCC_RX_CONTROL, rx_control); bus_space_write_4(iot, ioh, ASCC_TX_CONTROL, tx_control); return (0); } static void vx115_com_cnpollc(dev, on) dev_t dev; int on; { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnpollc\n"); } static void vx115_com_cnputc(dev_t dev, int c) { int s; bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnputc\n"); s = splserial(); /* spin while tx fifo full */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ; /* write the char */ REG_WRITE(ASCC_TX_RX_FIFO, c); splx(s); } static int vx115_com_cngetc(dev_t dev) { int c; int s; bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cngetc\n"); s = splserial(); /* spin while rx fifo empty */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) ; /* get the char */ c = REG_READ(ASCC_TX_RX_FIFO); c &= 0xff; splx(s); return (c); } /************************************************************** * Soft interrupt functions */ inline static void vx115_com_tx_soft(struct vx115_com_softc *sc, struct tty *tp) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_soft\n"); CLR(tp->t_state, TS_BUSY); /* adjust tty buffer pointers to account for data that's been sent */ if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf)); /* for the standard tty line discipline, just calls vx115_com_start; */ /* this will check to see if any new data's available, and set transmit */ /* interrupt if so to begin transmission */ (*tp->t_linesw->l_start)(tp); } inline static void vx115_com_rx_soft(struct vx115_com_softc *sc, struct tty *tp) { int (*rint) __P((int c, struct tty *tp)) = tp->t_linesw->l_rint; u_char *get, *end; u_int cc, scc; u_char lsr; int code; int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_soft\n"); end = sc->sc_ebuf; get = sc->sc_rbget; scc = cc = VX115_COM_RING_SIZE - sc->sc_rbavail; while (cc) { code = get[0]; lsr = get[1]; if (ISSET(lsr, (ASCC_TX_RX_FIFO_ERROR_RPE_RFE | ASCC_TX_RX_FIFO_ERROR_BRE))) { if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_RPE_RFE)) { /* we have either a parity or framing error; unfortunately, */ /* the ASCC doesn't tell us which, so just call it parity */ SET(code, TTY_PE); } if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_BRE)) { /* break condition is reported through TTY_FE */ SET(code, TTY_FE); } } if ((*rint)(code, tp) != 0) { /* error; not sure what to do... */ } get += 2; if (get >= end) get = sc->sc_rbuf; cc--; } if (cc != scc) { sc->sc_rbget = get; s = splserial(); COM_LOCK(sc); sc->sc_rbavail += scc - cc; if (sc->sc_rbavail >= 1) { /* buffers should be ok again, release possible block */ if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) { CLR(sc->sc_rx_flags, RX_IBUF_FULL); /* enable receive interrupts */ sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); } } COM_UNLOCK(sc); splx(s); } } static void vx115_com_soft(void* arg) { struct vx115_com_softc *sc = arg; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_soft\n"); if (COM_ISALIVE(sc) == 0) return; if (sc->sc_rx_ready) { sc->sc_rx_ready = 0; vx115_com_rx_soft(sc, sc->sc_tty); } if (!sc->sc_tx_busy) { /* previous transmissions complete; see if have any new stuff to send */ vx115_com_tx_soft(sc, sc->sc_tty); } } /************************************************************** * Hardware interrupt functions */ static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh) { u_char *put, *end; u_int cc; u_int32_t c; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_hard\n"); if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) { /* overflow - turn off interrupts */ sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); return; } /* get receive buffer parameters */ end = sc->sc_ebuf; put = sc->sc_rbput; cc = sc->sc_rbavail; /* copy characters from ASCC FIFO to receive buffer */ /* while FIFO not empty and buffer not full; also */ /* check for magic character sequence */ while ((cc > 0) && !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE)) { c = REG_READ(ASCC_TX_RX_FIFO); put[0] = c & 0xff; put[1] = (c >> 8) & 0xff; cn_check_magic(sc->sc_tty->t_dev, put[0], vx115_com_cnm_state); put += 2; if (put >= end) put = sc->sc_rbuf; cc--; } /* * Current string of incoming characters ended because * no more data was available or we ran out of space. * Schedule a receive event if any data was received. * If we're out of space, turn off receive interrupts. */ sc->sc_rbput = put; sc->sc_rbavail = cc; sc->sc_rx_ready = 1; /* * If we're out of space, disable receive interrupts * until the queue has drained a bit. */ if (cc == 0) { SET(sc->sc_rx_flags, RX_IBUF_FULL); sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); } } static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_hard\n"); /* If we've delayed a parameter change, do it now, and restart output. */ if (sc->sc_heldchange) { vx115_com_set_cr(sc); sc->sc_heldchange = 0; sc->sc_tbc = sc->sc_heldtbc; sc->sc_heldtbc = 0; } /* output the next chunk of the contiguous buffer, if any. */ if (sc->sc_tbc > 0) { int n = 0; while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ) { if (n >= sc->sc_tbc) break; REG_WRITE(ASCC_TX_RX_FIFO, 0xff & *(sc->sc_tba + n)); n++; } sc->sc_tbc -= n; sc->sc_tba += n; } if (sc->sc_tbc == 0) { /* we've finished with transmit buffer; disable transmit completion interrupts; */ /* will be reenabled when vx115_com_start called to transmit more */ sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK; vx115_com_set_cr(sc); sc->sc_tx_busy = 0; } } static int vx115_com_intr(void* arg) { struct vx115_com_softc *sc = arg; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int status; if (COM_ISALIVE(sc) == 0) return (0); COM_LOCK(sc); /* read main status reg to see what's up */ status = REG_READ(ASCC_STATUS); COM_LOWLEVEL_DEBUG_PRINT_STRING("i\n"); do { if (status & ASCC_STATUS_ROE) { /* receive overrun */ COM_LOWLEVEL_DEBUG_PRINT_STRING("o\n"); } if (status & ASCC_STATUS_RPE) { /* parity error */ COM_LOWLEVEL_DEBUG_PRINT_STRING("p\n"); } if (status & ASCC_STATUS_RFE) { /* framing error */ COM_LOWLEVEL_DEBUG_PRINT_STRING("f\n"); } if (status & ASCC_STATUS_MISC_ANY) { /* it's a modem status interrupt */ COM_LOWLEVEL_DEBUG_PRINT_STRING("m\n"); } if (status & (ASCC_STATUS_RFT | ASCC_STATUS_CCE0)) { /* it's a receive FIFO interrupt; either hit */ /* threshold, or timed out with chars avail */ COM_LOWLEVEL_DEBUG_PRINT_STRING("r\n"); vx115_com_rx_hard(sc, iot, ioh); } if (status & ASCC_STATUS_TFT) { /* it's a transmit FIFO interrupt */ COM_LOWLEVEL_DEBUG_PRINT_STRING("t\n"); vx115_com_tx_hard(sc, iot, ioh); } /* see if we're done */ status = REG_READ(ASCC_STATUS); } while (status); COM_UNLOCK(sc); /* Wake up the poller. */ softintr_schedule(sc->sc_softintr); return (1); }