diff options
Diffstat (limited to 'sys/arch/arc/dev')
-rw-r--r-- | sys/arch/arc/dev/ace.c | 1783 | ||||
-rw-r--r-- | sys/arch/arc/dev/asc.c | 2074 | ||||
-rw-r--r-- | sys/arch/arc/dev/ascreg.h | 322 | ||||
-rw-r--r-- | sys/arch/arc/dev/dma.c | 395 | ||||
-rw-r--r-- | sys/arch/arc/dev/dma.h | 151 | ||||
-rw-r--r-- | sys/arch/arc/dev/fd.c | 1139 | ||||
-rw-r--r-- | sys/arch/arc/dev/fdreg.h | 67 | ||||
-rw-r--r-- | sys/arch/arc/dev/if_sn.c | 1300 | ||||
-rw-r--r-- | sys/arch/arc/dev/if_sn.h | 347 | ||||
-rw-r--r-- | sys/arch/arc/dev/lpt.c | 676 | ||||
-rw-r--r-- | sys/arch/arc/dev/pccons.c | 2094 | ||||
-rw-r--r-- | sys/arch/arc/dev/scsi.h | 559 |
12 files changed, 10907 insertions, 0 deletions
diff --git a/sys/arch/arc/dev/ace.c b/sys/arch/arc/dev/ace.c new file mode 100644 index 00000000000..a285e748ff7 --- /dev/null +++ b/sys/arch/arc/dev/ace.c @@ -0,0 +1,1783 @@ +/* $OpenBSD: ace.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: com.c,v 1.82.4.1 1996/06/02 09:08:00 mrg Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996 + * Charles M. Hannum. All rights reserved. + * 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. 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 + * SUCH DAMAGE. + * + * @(#)com.c 7.5 (Berkeley) 5/16/91 + */ + +/* + * ACE driver, based on HP dca driver + * uses National Semiconductor NS16450/NS16550AF UART + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/tty.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/uio.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/types.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> +#include <dev/isa/comreg.h> +#include <dev/isa/comvar.h> +#include <dev/ic/ns16550reg.h> +#ifdef COM_HAYESP +#include <dev/ic/hayespreg.h> +#endif +#define com_lcr com_cfcr + +#include "com.h" + + +#define COM_IBUFSIZE (2 * 512) +#define COM_IHIGHWATER ((3 * COM_IBUFSIZE) / 4) + +struct com_softc { + struct device sc_dev; + void *sc_ih; + bus_chipset_tag_t sc_bc; + struct tty *sc_tty; + + int sc_overflows; + int sc_floods; + int sc_errors; + + int sc_halt; + + int sc_iobase; +#ifdef COM_HAYESP + int sc_hayespbase; +#endif + + bus_io_handle_t sc_ioh; + bus_io_handle_t sc_hayespioh; + + u_char sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#define COM_HW_HAYESP 0x04 +#define COM_HW_ABSENT_PENDING 0x08 /* reattached, awaiting close/reopen */ +#define COM_HW_ABSENT 0x10 /* configure actually failed, or removed */ +#define COM_HW_REATTACH 0x20 /* reattaching */ +#define COM_HW_CONSOLE 0x40 + u_char sc_swflags; +#define COM_SW_SOFTCAR 0x01 +#define COM_SW_CLOCAL 0x02 +#define COM_SW_CRTSCTS 0x04 +#define COM_SW_MDMBUF 0x08 + u_char sc_msr, sc_mcr, sc_lcr, sc_ier; + u_char sc_dtr; + + u_char *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; + u_char sc_ibufs[2][COM_IBUFSIZE]; +}; + +#ifdef COM_HAYESP +int comprobeHAYESP __P((bus_io_handle_t hayespioh, struct com_softc *sc)); +#endif +void comdiag __P((void *)); +int comspeed __P((long)); +int comparam __P((struct tty *, struct termios *)); +void comstart __P((struct tty *)); +void compoll __P((void *)); + +/* XXX: These belong elsewhere */ +cdev_decl(com); +bdev_decl(com); + +struct consdev; +void comcnprobe __P((struct consdev *)); +void comcninit __P((struct consdev *)); +int comcngetc __P((dev_t)); +void comcnputc __P((dev_t, int)); +void comcnpollc __P((dev_t, int)); + +static u_char tiocm_xxx2mcr __P((int)); + +/* + * XXX the following two cfattach structs should be different, and possibly + * XXX elsewhere. + */ +int comprobe __P((struct device *, void *, void *)); +void comattach __P((struct device *, struct device *, void *)); +void com_absent_notify __P((struct com_softc *sc)); +void comstart_pending __P((void *)); + +#if NACE_ISA +struct cfattach ace_isa_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + +#if NACE_COMMULTI +struct cfattach ace_commulti_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + +#if NACE_PICA +#undef CONADDR /* This is stupid but using devs before config .. */ +#define CONADDR 0xe0006000 + +struct cfattach ace_pica_ca = { + sizeof(struct com_softc), comprobe, comattach +}; +#endif + + +struct cfdriver ace_cd = { + NULL, "com", DV_TTY +}; + +void cominit __P((bus_chipset_tag_t, bus_io_handle_t, int)); + +#ifndef CONSPEED +#define CONSPEED B9600 +#endif + +#ifdef COMCONSOLE +int comdefaultrate = CONSPEED; /* XXX why set default? */ +#else +int comdefaultrate = TTYDEF_SPEED; +#endif +int comconsaddr; +int comconsinit; +int comconsattached; +bus_chipset_tag_t comconsbc; +bus_io_handle_t comconsioh; +tcflag_t comconscflag = TTYDEF_CFLAG; + +int commajor; +int comsopen = 0; +int comevents = 0; + +#ifdef KGDB +#include <machine/remote-sl.h> +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif + +#define COMUNIT(x) (minor(x)) + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +#if NACE_PCMCIA +#include <dev/pcmcia/pcmciavar.h> + +int com_pcmcia_match __P((struct device *, void *, void *)); +void com_pcmcia_attach __P((struct device *, struct device *, void *)); +int com_pcmcia_detach __P((struct device *)); + +struct cfattach ace_pcmcia_ca = { + sizeof(struct com_softc), com_pcmcia_match, comattach, + com_pcmcia_detach +}; + +int com_pcmcia_mod __P((struct pcmcia_link *pc_link, struct device *self, + struct pcmcia_conf *pc_cf, struct cfdata *cf)); + +/* additional setup needed for pcmcia devices */ +/* modify config entry */ +int +com_pcmcia_mod(pc_link, self, pc_cf, cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + struct pcmciadevs *dev = pc_link->device; + struct ed_softc *sc = (void *)self; + if (!(err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, + pc_cf, cf))) { + pc_cf->memwin = 0; + if (pc_cf->cfgtype == 0) + pc_cf->cfgtype = CFGENTRYID; /* determine from ioaddr */ + } + return err; +} + +int com_pcmcia_isa_attach __P((struct device *, void *, void *, + struct pcmcia_link *)); +int com_pcmcia_remove __P((struct pcmcia_link *, struct device *)); + +static struct pcmcia_com { + struct pcmcia_device pcd; +} pcmcia_com = { + {"PCMCIA Modem card", com_pcmcia_mod, com_pcmcia_isa_attach, + NULL, com_pcmcia_remove} +}; + + +struct pcmciadevs pcmcia_com_devs[] = { + { "com", 0, + NULL, "*MODEM*", NULL, NULL, + NULL, (void *)&pcmcia_com + }, + { "com", 0, + NULL, NULL, "*MODEM*", NULL, + NULL, (void *)&pcmcia_com + }, + { "com", 0, + NULL, NULL, NULL, "*MODEM*", + NULL, (void *)&pcmcia_com + }, + {NULL} +}; +#define ncom_pcmcia_devs sizeof(pcmcia_com_devs)/sizeof(pcmcia_com_devs[0]) + +int +com_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_com_devs, + ncom_pcmcia_devs); +} + +int +com_pcmcia_isa_attach(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct isa_attach_args *ia = aux; + struct com_softc *sc = match; + + int rval; + if (rval = comprobe(parent, sc->sc_dev.dv_cfdata, ia)) { + if (ISSET(pc_link->flags, PCMCIA_REATTACH)) { +#ifdef COM_DEBUG + printf("comreattach, hwflags=%x\n", sc->sc_hwflags); +#endif + sc->sc_hwflags = COM_HW_REATTACH | + (sc->sc_hwflags & (COM_HW_ABSENT_PENDING|COM_HW_CONSOLE)); + } else + sc->sc_hwflags = 0; + } + return rval; +} + + +/* + * Called by config_detach attempts, shortly after com_pcmcia_remove + * was called. + */ +int +com_pcmcia_detach(self) + struct device *self; +{ + struct com_softc *sc = (void *)self; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + /* don't let it really be detached, it is still open */ + return EBUSY; + } + return 0; /* OK! */ +} + +/* + * called by pcmcia framework to accept/reject remove attempts. + * If we return 0, then the detach will proceed. + */ +int +com_pcmcia_remove(pc_link, self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct com_softc *sc = (void *)self; + struct tty *tp; + int s; + + if (!sc->sc_tty) + goto ok; + tp = sc->sc_tty; + + /* not in use ? if so, return "OK" */ + if (!ISSET(tp->t_state, TS_ISOPEN) && + !ISSET(tp->t_state, TS_WOPEN)) { + ttyfree(sc->sc_tty); + sc->sc_tty = NULL; + ok: + isa_intr_disestablish(sc->sc_bc, sc->sc_ih); + sc->sc_ih = NULL; + SET(sc->sc_hwflags, COM_HW_ABSENT); + return 0; /* OK! */ + } + /* + * Not easily removed. Put device into a dead state, clean state + * as best we can. notify all waiters. + */ + SET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING); +#ifdef COM_DEBUG + printf("pending detach flags %x\n", sc->sc_hwflags); +#endif + + s = spltty(); + com_absent_notify(sc); + splx(s); + + return 0; +} + +#if 0 +void +com_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("com_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct com_softc *sc = (void *)self; + sc->sc_hwflags |= COM_HW_ABSENT; + printf(": not attached\n"); + } +} +#endif +#endif + +/* + * must be called at spltty() or higher. + */ +void +com_absent_notify(sc) + struct com_softc *sc; +{ + struct tty *tp = sc->sc_tty; + + if (tp) { + CLR(tp->t_state, TS_CARR_ON|TS_BUSY); + ttyflush(tp, FREAD|FWRITE); + } +} + +int +comspeed(speed) + long speed; +{ +#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ + + int x, err; + + if (speed == 0) + return 0; + if (speed < 0) + return -1; + x = divrnd((COM_FREQ / 16), speed); + if (x <= 0) + return -1; + err = divrnd((COM_FREQ / 16) * 1000, speed * x) - 1000; + if (err < 0) + err = -err; + if (err > COM_TOLERANCE) + return -1; + return x; + +#undef divrnd(n, q) +} + +int +comprobe1(bc, ioh, iobase) + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int iobase; +{ + int i, k; + + /* force access to id reg */ + bus_io_write_1(bc, ioh, com_lcr, 0); + bus_io_write_1(bc, ioh, com_iir, 0); + for (i = 0; i < 32; i++) { + k = bus_io_read_1(bc, ioh, com_iir); + if (k & 0x38) { + bus_io_read_1(bc, ioh, com_data); /* cleanup */ + } else + break; + } + if (i >= 32) + return 0; + + return 1; +} + +#ifdef COM_HAYESP +int +comprobeHAYESP(hayespioh, sc) + bus_io_handle_t hayespioh; + struct com_softc *sc; +{ + char val, dips; + int combaselist[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + bus_chipset_tag_t bc = sc->sc_bc; + + /* + * Hayes ESP cards have two iobases. One is for compatibility with + * 16550 serial chips, and at the same ISA PC base addresses. The + * other is for ESP-specific enhanced features, and lies at a + * different addressing range entirely (0x140, 0x180, 0x280, or 0x300). + */ + + /* Test for ESP signature */ + if ((bus_io_read_1(bc, hayespioh, 0) & 0xf3) == 0) + return 0; + + /* + * ESP is present at ESP enhanced base address; unknown com port + */ + + /* Get the dip-switch configurations */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_GETDIPS); + dips = bus_io_read_1(bc, hayespioh, HAYESP_STATUS1); + + /* Determine which com port this ESP card services: bits 0,1 of */ + /* dips is the port # (0-3); combaselist[val] is the com_iobase */ + if (sc->sc_iobase != combaselist[dips & 0x03]) + return 0; + + printf(": ESP"); + + /* Check ESP Self Test bits. */ + /* Check for ESP version 2.0: bits 4,5,6 == 010 */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_GETTEST); + val = bus_io_read_1(bc, hayespioh, HAYESP_STATUS1); /* Clear reg 1 */ + val = bus_io_read_1(bc, hayespioh, HAYESP_STATUS2); + if ((val & 0x70) < 0x20) { + printf("-old (%o)", val & 0x70); + /* we do not support the necessary features */ + return 0; + } + + /* Check for ability to emulate 16550: bit 8 == 1 */ + if ((dips & 0x80) == 0) { + printf(" slave"); + /* XXX Does slave really mean no 16550 support?? */ + return 0; + } + + /* + * If we made it this far, we are a full-featured ESP v2.0 (or + * better), at the correct com port address. + */ + + SET(sc->sc_hwflags, COM_HW_HAYESP); + printf(", 1024 byte fifo\n"); + return 1; +} +#endif + +int +comprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int iobase, needioh; + int rv = 1; + +#if NACE_ISA || NACE_PCMCIA +#define IS_ISA(parent) \ + (!strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") || \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "pcmcia")) +#elif NACE_ISA +#define IS_ISA(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") +#endif +#if NACE_PICA +#define IS_PICA(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "pica") +#endif + /* + * XXX should be broken out into functions for isa probe and + * XXX for commulti probe, with a helper function that contains + * XXX most of the interesting stuff. + */ +#if NACE_ISA || NACE_PCMCIA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + bc = ia->ia_bc; + iobase = ia->ia_iobase; + needioh = 1; + } else +#endif +#if NACE_PICA + if(IS_PICA(parent)) { + struct confargs *ca = aux; + if(!BUS_MATCHNAME(ca, "com")) + return(0); + iobase = (long)BUS_CVTADDR(ca); + bc = 0; + needioh = 1; + } else +#endif +#if NACE_COMMULTI + if (1) { + struct cfdata *cf = match; + struct commulti_attach_args *ca = aux; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != ca->ca_slave) + return (0); + + bc = ca->ca_bc; + iobase = ca->ca_iobase; + ioh = ca->ca_ioh; + needioh = 0; + } else +#endif + return(0); /* This cannot happen */ + + /* if it's in use as console, it's there. */ + if (iobase == comconsaddr && !comconsattached) + goto out; + + if (needioh && bus_io_map(bc, iobase, COM_NPORTS, &ioh)) { + rv = 0; + goto out; + } + rv = comprobe1(bc, ioh, iobase); + if (needioh) + bus_io_unmap(bc, ioh, COM_NPORTS); + +out: +#if NACE_ISA || NACE_PCMCIA + if (rv && IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + ia->ia_iosize = COM_NPORTS; + ia->ia_msize = 0; + } +#endif + return (rv); +} + +void +comattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct com_softc *sc = (void *)self; + int iobase, irq; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; +#ifdef COM_HAYESP + int hayesp_ports[] = { 0x140, 0x180, 0x280, 0x300, 0 }; + int *hayespp; +#endif + + /* + * XXX should be broken out into functions for isa attach and + * XXX for commulti attach, with a helper function that contains + * XXX most of the interesting stuff. + */ + if (ISSET(sc->sc_hwflags, COM_HW_REATTACH)) { + int s; + s = spltty(); + com_absent_notify(sc); + splx(s); + } else + sc->sc_hwflags = 0; + sc->sc_swflags = 0; +#if NACE_ISA || NACE_PCMCIA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + /* + * We're living on an isa. + */ + iobase = ia->ia_iobase; + bc = ia->ia_bc; + if (iobase != comconsaddr) { + if (bus_io_map(bc, iobase, COM_NPORTS, &ioh)) + panic("comattach: io mapping failed"); + } else + ioh = comconsioh; + irq = ia->ia_irq; + } else +#endif +#if NACE_PICA + if(IS_PICA(parent)) { + struct confargs *ca = aux; + iobase = (long)BUS_CVTADDR(ca); + bc = 0; + irq = 0; + ioh = iobase; + } else +#endif +#if NACE_COMMULTI + if (1) { + struct commulti_attach_args *ca = aux; + + /* + * We're living on a commulti. + */ + iobase = ca->ca_iobase; + bc = ca->ca_bc; + ioh = ca->ca_ioh; + irq = IRQUNK; + + if (ca->ca_noien) + SET(sc->sc_hwflags, COM_HW_NOIEN); + } else +#endif + panic("comattach: impossible"); + + sc->sc_bc = bc; + sc->sc_ioh = ioh; + sc->sc_iobase = iobase; + + if (iobase == comconsaddr) { + comconsattached = 1; + + /* + * Need to reset baud rate, etc. of next print so reset + * comconsinit. Also make sure console is always "hardwired". + */ + delay(1000); /* wait for output to finish */ + comconsinit = 0; + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, COM_SW_SOFTCAR); + } + +#ifdef COM_HAYESP + /* Look for a Hayes ESP board. */ + for (hayespp = hayesp_ports; *hayespp != 0; hayespp++) { + bus_io_handle_t hayespioh; + +#define HAYESP_NPORTS 8 /* XXX XXX XXX ??? ??? ??? */ + if (bus_io_map(bc, *hayespp, HAYESP_NPORTS, &hayespioh)) + continue; + if (comprobeHAYESP(hayespioh, sc)) { + sc->sc_hayespbase = *hayespp; + sc->sc_hayespioh = hayespioh; + break; + } + bus_io_unmap(bc, hayespioh, HAYESP_NPORTS); + } + /* No ESP; look for other things. */ + if (*hayespp == 0) { +#endif + + /* look for a NS 16550AF UART with FIFOs */ + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + delay(100); + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_FIFO_MASK) == + IIR_FIFO_MASK) + if (ISSET(bus_io_read_1(bc, ioh, com_fifo), FIFO_TRIGGER_14) == + FIFO_TRIGGER_14) { + SET(sc->sc_hwflags, COM_HW_FIFO); + printf(": ns16550a, working fifo\n"); + } else + printf(": ns16550, broken fifo\n"); + else + printf(": ns8250 or ns16450, no fifo\n"); + bus_io_write_1(bc, ioh, com_fifo, 0); +#ifdef COM_HAYESP + } +#endif + + /* disable interrupts */ + bus_io_write_1(bc, ioh, com_ier, 0); + bus_io_write_1(bc, ioh, com_mcr, 0); + + if (irq != IRQUNK) { +#if NACE_ISA || NACE_PCMCIA + if (IS_ISA(parent)) { + struct isa_attach_args *ia = aux; + + sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, + IST_EDGE, IPL_TTY, comintr, sc, + sc->sc_dev.dv_xname); + } else +#endif +#if NACE_PICA + if (IS_PICA(parent)) { + struct confargs *ca = aux; + BUS_INTR_ESTABLISH(ca, comintr, (void *)(long)sc); + } else +#endif + panic("comattach: IRQ but can't have one"); + } + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + kgdb_dev = -1; /* can't debug over console port */ + else { + cominit(bc, ioh, kgdb_rate); + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("%s: ", sc->sc_dev.dv_xname); + kgdb_connect(1); + } else + printf("%s: kgdb enabled\n", + sc->sc_dev.dv_xname); + } + } +#endif + + /* XXX maybe move up some? */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + printf("%s: console\n", sc->sc_dev.dv_xname); +} + +int +comopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = COMUNIT(dev); + struct com_softc *sc; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + struct tty *tp; + int s; + int error = 0; + + if (unit >= com_cd.cd_ndevs) + return ENXIO; + sc = com_cd.cd_devs[unit]; + if (!sc || ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) + return ENXIO; + + if (!sc->sc_tty) { + tp = sc->sc_tty = ttymalloc(); + tty_attach(tp); + } else + tp = sc->sc_tty; + + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + tp->t_cflag = comconscflag; + else + tp->t_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, COM_SW_CLOCAL)) + SET(tp->t_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS)) + SET(tp->t_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, COM_SW_MDMBUF)) + SET(tp->t_cflag, MDMBUF); + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = comdefaultrate; + + s = spltty(); + + comparam(tp, &tp->t_termios); + ttsetwater(tp); + + if (comsopen++ == 0) + timeout(compoll, NULL, 1); + + sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE; + + bc = sc->sc_bc; + ioh = sc->sc_ioh; +#ifdef COM_HAYESP + /* Setup the ESP board */ + if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { + bus_io_handle_t hayespioh = sc->sc_hayespioh; + + bus_io_write_1(bc, ioh, com_fifo, + FIFO_DMA_MODE|FIFO_ENABLE| + FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_8); + + /* Set 16550 compatibility mode */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETMODE); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_MODE_FIFO|HAYESP_MODE_RTS| + HAYESP_MODE_SCALE); + + /* Set RTS/CTS flow control */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETFLOWTYPE); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, HAYESP_FLOW_RTS); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, HAYESP_FLOW_CTS); + + /* Set flow control levels */ + bus_io_write_1(bc, hayespioh, HAYESP_CMD1, HAYESP_SETRXFLOW); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_HIBYTE(HAYESP_RXHIWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_LOBYTE(HAYESP_RXHIWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_HIBYTE(HAYESP_RXLOWMARK)); + bus_io_write_1(bc, hayespioh, HAYESP_CMD2, + HAYESP_LOBYTE(HAYESP_RXLOWMARK)); + } else +#endif + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) + /* Set the FIFO threshold based on the receive speed. */ + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | + (tp->t_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); + /* flush any pending I/O */ + while (ISSET(bus_io_read_1(bc, ioh, com_lsr), LSR_RXRDY)) + (void) bus_io_read_1(bc, ioh, com_data); + /* you turn me on, baby */ + sc->sc_mcr = MCR_DTR | MCR_RTS; + if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN)) + SET(sc->sc_mcr, MCR_IENABLE); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + sc->sc_ier = IER_ERXRDY | IER_ERLS | IER_EMSC; + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + + sc->sc_msr = bus_io_read_1(bc, ioh, com_msr); + if (ISSET(sc->sc_swflags, COM_SW_SOFTCAR) || + ISSET(sc->sc_msr, MSR_DCD) || ISSET(tp->t_cflag, MDMBUF)) + SET(tp->t_state, TS_CARR_ON); + else + CLR(tp->t_state, TS_CARR_ON); + } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) + return EBUSY; + else + s = spltty(); + + /* wait for carrier if necessary */ + if (!ISSET(flag, O_NONBLOCK)) + while (!ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON)) { + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, + ttopen, 0); + if (error) { + /* XXX should turn off chip if we're the + only waiter */ + splx(s); + return error; + } + } + splx(s); + + return (*linesw[tp->t_line].l_open)(dev, tp); +} + +int +comclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = COMUNIT(dev); + struct com_softc *sc = com_cd.cd_devs[unit]; + struct tty *tp = sc->sc_tty; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flag); + s = spltty(); + if (!ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* can't do any of this stuff .... */ + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + bus_io_write_1(bc, ioh, com_ier, 0); + if (ISSET(tp->t_cflag, HUPCL) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { + /* XXX perhaps only clear DTR */ + bus_io_write_1(bc, ioh, com_mcr, 0); + } + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (--comsopen == 0) + untimeout(compoll, NULL); + splx(s); + ttyclose(tp); +#ifdef COM_DEBUG + /* mark it ready for more use if reattached earlier */ + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + printf("comclose pending cleared\n"); + } +#endif + CLR(sc->sc_hwflags, COM_HW_ABSENT_PENDING); + +#ifdef notyet /* XXXX */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { + ttyfree(tp); + sc->sc_tty = 0; + } +#endif + return 0; +} + +int +comread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +comwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +struct tty * +comtty(dev) + dev_t dev; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + return (tp); +} + +static u_char +tiocm_xxx2mcr(data) + int data; +{ + u_char m = 0; + + if (ISSET(data, TIOCM_DTR)) + SET(m, MCR_DTR); + if (ISSET(data, TIOCM_RTS)) + SET(m, MCR_RTS); + return m; +} + +int +comioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int unit = COMUNIT(dev); + struct com_softc *sc = com_cd.cd_devs[unit]; + struct tty *tp = sc->sc_tty; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int error; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return error; + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return error; + + switch (cmd) { + case TIOCSBRK: + SET(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + break; + case TIOCCBRK: + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + break; + case TIOCSDTR: + SET(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCCDTR: + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMSET: + CLR(sc->sc_mcr, MCR_DTR | MCR_RTS); + case TIOCMBIS: + SET(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data)); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMBIC: + CLR(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data)); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + break; + case TIOCMGET: { + u_char m; + int bits = 0; + + m = sc->sc_mcr; + if (ISSET(m, MCR_DTR)) + SET(bits, TIOCM_DTR); + if (ISSET(m, MCR_RTS)) + SET(bits, TIOCM_RTS); + m = sc->sc_msr; + if (ISSET(m, MSR_DCD)) + SET(bits, TIOCM_CD); + if (ISSET(m, MSR_CTS)) + SET(bits, TIOCM_CTS); + if (ISSET(m, MSR_DSR)) + SET(bits, TIOCM_DSR); + if (ISSET(m, MSR_RI | MSR_TERI)) + SET(bits, TIOCM_RI); + if (bus_io_read_1(bc, ioh, com_ier)) + SET(bits, TIOCM_LE); + *(int *)data = bits; + break; + } + case TIOCGFLAGS: { + int driverbits, userbits = 0; + + driverbits = sc->sc_swflags; + if (ISSET(driverbits, COM_SW_SOFTCAR)) + SET(userbits, TIOCFLAG_SOFTCAR); + if (ISSET(driverbits, COM_SW_CLOCAL)) + SET(userbits, TIOCFLAG_CLOCAL); + if (ISSET(driverbits, COM_SW_CRTSCTS)) + SET(userbits, TIOCFLAG_CRTSCTS); + if (ISSET(driverbits, COM_SW_MDMBUF)) + SET(userbits, TIOCFLAG_MDMBUF); + + *(int *)data = userbits; + break; + } + case TIOCSFLAGS: { + int userbits, driverbits = 0; + + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return(EPERM); + + userbits = *(int *)data; + if (ISSET(userbits, TIOCFLAG_SOFTCAR) || + ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + SET(driverbits, COM_SW_SOFTCAR); + if (ISSET(userbits, TIOCFLAG_CLOCAL)) + SET(driverbits, COM_SW_CLOCAL); + if (ISSET(userbits, TIOCFLAG_CRTSCTS)) + SET(driverbits, COM_SW_CRTSCTS); + if (ISSET(userbits, TIOCFLAG_MDMBUF)) + SET(driverbits, COM_SW_MDMBUF); + + sc->sc_swflags = driverbits; + break; + } + default: + return ENOTTY; + } + + return 0; +} + +int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(tp->t_dev)]; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int ospeed = comspeed(t->c_ospeed); + u_char lcr; + tcflag_t oldcflag; + int s; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + + /* check requested parameters */ + if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + lcr = ISSET(sc->sc_lcr, LCR_SBREAK); + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + SET(lcr, LCR_5BITS); + break; + case CS6: + SET(lcr, LCR_6BITS); + break; + case CS7: + SET(lcr, LCR_7BITS); + break; + case CS8: + SET(lcr, LCR_8BITS); + break; + } + if (ISSET(t->c_cflag, PARENB)) { + SET(lcr, LCR_PENAB); + if (!ISSET(t->c_cflag, PARODD)) + SET(lcr, LCR_PEVEN); + } + if (ISSET(t->c_cflag, CSTOPB)) + SET(lcr, LCR_STOPB); + + sc->sc_lcr = lcr; + + s = spltty(); + + if (ospeed == 0) { + CLR(sc->sc_mcr, MCR_DTR); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + + /* + * Set the FIFO threshold based on the receive speed, if we are + * changing it. + */ +#if 1 + if (tp->t_ispeed != t->c_ispeed) { +#else + if (1) { +#endif + if (ospeed != 0) { + /* + * Make sure the transmit FIFO is empty before + * proceeding. If we don't do this, some revisions + * of the UART will hang. Interestingly enough, + * even if we do this will the last character is + * still being pushed out, they don't hang. This + * seems good enough. + */ + while (ISSET(tp->t_state, TS_BUSY)) { + int error; + + ++sc->sc_halt; + error = ttysleep(tp, &tp->t_outq, + TTOPRI | PCATCH, "comprm", 0); + --sc->sc_halt; + if (error) { + splx(s); + comstart(tp); + return (error); + } + } + + bus_io_write_1(bc, ioh, com_lcr, lcr | LCR_DLAB); + bus_io_write_1(bc, ioh, com_dlbl, ospeed); + bus_io_write_1(bc, ioh, com_dlbh, ospeed >> 8); + bus_io_write_1(bc, ioh, com_lcr, lcr); + SET(sc->sc_mcr, MCR_DTR); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } else + bus_io_write_1(bc, ioh, com_lcr, lcr); + + if (!ISSET(sc->sc_hwflags, COM_HW_HAYESP) && + ISSET(sc->sc_hwflags, COM_HW_FIFO)) + bus_io_write_1(bc, ioh, com_fifo, + FIFO_ENABLE | + (t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); + } else + bus_io_write_1(bc, ioh, com_lcr, lcr); + + /* When not using CRTSCTS, RTS follows DTR. */ + if (!ISSET(t->c_cflag, CRTSCTS)) { + if (ISSET(sc->sc_mcr, MCR_DTR)) { + if (!ISSET(sc->sc_mcr, MCR_RTS)) { + SET(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + } else { + if (ISSET(sc->sc_mcr, MCR_RTS)) { + CLR(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + } + sc->sc_dtr = MCR_DTR | MCR_RTS; + } else + sc->sc_dtr = MCR_DTR; + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + oldcflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + + /* + * If DCD is off and MDMBUF is changed, ask the tty layer if we should + * stop the device. + */ + if (!ISSET(sc->sc_msr, MSR_DCD) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR) && + ISSET(oldcflag, MDMBUF) != ISSET(tp->t_cflag, MDMBUF) && + (*linesw[tp->t_line].l_modem)(tp, 0) == 0) { + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + + /* Just to be sure... */ + splx(s); + comstart(tp); + return 0; +} + +void +comstart_pending(arg) + void *arg; +{ + struct com_softc *sc = arg; + int s; + + s = spltty(); + com_absent_notify(sc); + splx(s); +} + +void +comstart(tp) + struct tty *tp; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(tp->t_dev)]; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int s; + + s = spltty(); + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* + * not quite good enough: if caller is ttywait() it will + * go to sleep immediately, so hang out a bit and then + * prod caller again. + */ + com_absent_notify(sc); + timeout(comstart_pending, sc, 1); + goto out; + } + if (ISSET(tp->t_state, TS_BUSY)) + goto out; + if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || + sc->sc_halt > 0) + goto stopped; + if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, MSR_CTS)) + goto stopped; + 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); + } + if (tp->t_outq.c_cc == 0) + goto stopped; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + + if (!ISSET(sc->sc_ier, IER_ETXRDY)) { + SET(sc->sc_ier, IER_ETXRDY); + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + } +#ifdef COM_HAYESP + if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) { + u_char buffer[1024], *cp = buffer; + int n = q_to_b(&tp->t_outq, cp, sizeof buffer); + do + bus_io_write_1(bc, ioh, com_data, *cp++); + while (--n); + } + else +#endif + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { + u_char buffer[16], *cp = buffer; + int n = q_to_b(&tp->t_outq, cp, sizeof buffer); + do { + bus_io_write_1(bc, ioh, com_data, *cp++); + } while (--n); + } else + bus_io_write_1(bc, ioh, com_data, getc(&tp->t_outq)); +out: + splx(s); + return; +stopped: + if (ISSET(sc->sc_ier, IER_ETXRDY)) { + CLR(sc->sc_ier, IER_ETXRDY); + bus_io_write_1(bc, ioh, com_ier, sc->sc_ier); + } + splx(s); +} + +/* + * Stop output on a line. + */ +int +comstop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY)) + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + splx(s); + return 0; +} + +void +comdiag(arg) + void *arg; +{ + struct com_softc *sc = arg; + int overflows, floods; + int s; + + s = spltty(); + sc->sc_errors = 0; + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + floods = sc->sc_floods; + sc->sc_floods = 0; + splx(s); + + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", + sc->sc_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +void +compoll(arg) + void *arg; +{ + int unit; + struct com_softc *sc; + struct tty *tp; + register u_char *ibufp; + u_char *ibufend; + register int c; + int s; + static int lsrmap[8] = { + 0, TTY_PE, + TTY_FE, TTY_PE|TTY_FE, + TTY_FE, TTY_PE|TTY_FE, + TTY_FE, TTY_PE|TTY_FE + }; + + s = spltty(); + if (comevents == 0) { + splx(s); + goto out; + } + comevents = 0; + splx(s); + + for (unit = 0; unit < com_cd.cd_ndevs; unit++) { + sc = com_cd.cd_devs[unit]; + if (sc == 0 || sc->sc_ibufp == sc->sc_ibuf) + continue; + + tp = sc->sc_tty; + + s = spltty(); + + ibufp = sc->sc_ibuf; + ibufend = sc->sc_ibufp; + + if (ibufp == ibufend) { + splx(s); + continue; + } + + sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? + sc->sc_ibufs[1] : sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE; + + if (tp == 0 || !ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + continue; + } + + if (ISSET(tp->t_cflag, CRTSCTS) && + !ISSET(sc->sc_mcr, MCR_RTS)) { + /* XXX */ + SET(sc->sc_mcr, MCR_RTS); + bus_io_write_1(sc->sc_bc, sc->sc_ioh, com_mcr, + sc->sc_mcr); + } + + splx(s); + + while (ibufp < ibufend) { + c = *ibufp++; + if (*ibufp & LSR_OE) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + timeout(comdiag, sc, 60 * hz); + } + /* This is ugly, but fast. */ + c |= lsrmap[(*ibufp++ & (LSR_BI|LSR_FE|LSR_PE)) >> 2]; + (*linesw[tp->t_line].l_rint)(c, tp); + } + } + +out: + timeout(compoll, NULL, 1); +} + +int +comintr(arg) + void *arg; +{ + struct com_softc *sc = arg; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + struct tty *tp; + u_char lsr, data, msr, delta; +#ifdef COM_DEBUG + int n; + struct { + u_char iir, lsr, msr; + } iter[32]; +#endif + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT) || !sc->sc_tty) + return 0; /* can't do squat. */ + +#ifdef COM_DEBUG + n = 0; + if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (0); +#else + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (0); +#endif + + tp = sc->sc_tty; + + for (;;) { +#ifdef COM_DEBUG + iter[n].lsr = +#endif + lsr = bus_io_read_1(bc, ioh, com_lsr); + + if (ISSET(lsr, LSR_RXRDY)) { + register u_char *p = sc->sc_ibufp; + + comevents = 1; + do { + data = bus_io_read_1(bc, ioh, com_data); + if (ISSET(lsr, LSR_BI)) { +#ifdef notdef + printf("break %02x %02x %02x %02x\n", + sc->sc_msr, sc->sc_mcr, sc->sc_lcr, + sc->sc_dtr); +#endif +#ifdef DDB + if (ISSET(sc->sc_hwflags, + COM_HW_CONSOLE)) { + Debugger(); + goto next; + } +#endif + } + if (p >= sc->sc_ibufend) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + timeout(comdiag, sc, 60 * hz); + } else { + *p++ = data; + *p++ = lsr; + if (p == sc->sc_ibufhigh && + ISSET(tp->t_cflag, CRTSCTS)) { + /* XXX */ + CLR(sc->sc_mcr, MCR_RTS); + bus_io_write_1(bc, ioh, com_mcr, + sc->sc_mcr); + } + } + next: +#ifdef COM_DEBUG + if (++n >= 32) + goto ohfudge; + iter[n].lsr = +#endif + lsr = bus_io_read_1(bc, ioh, com_lsr); + } while (ISSET(lsr, LSR_RXRDY)); + + sc->sc_ibufp = p; + } +#ifdef COM_DEBUG + else if (ISSET(lsr, LSR_BI|LSR_FE|LSR_PE|LSR_OE)) + printf("weird lsr %02x\n", lsr); +#endif + +#ifdef COM_DEBUG + iter[n].msr = +#endif + msr = bus_io_read_1(bc, ioh, com_msr); + + if (msr != sc->sc_msr) { + delta = msr ^ sc->sc_msr; + sc->sc_msr = msr; + if (ISSET(delta, MSR_DCD) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR) && + (*linesw[tp->t_line].l_modem)(tp, ISSET(msr, MSR_DCD)) == 0) { + CLR(sc->sc_mcr, sc->sc_dtr); + bus_io_write_1(bc, ioh, com_mcr, sc->sc_mcr); + } + if (ISSET(delta & msr, MSR_CTS) && + ISSET(tp->t_cflag, CRTSCTS)) { + /* the line is up and we want to do rts/cts flow control */ + (*linesw[tp->t_line].l_start)(tp); + } + } + + if (ISSET(lsr, LSR_TXRDY) && ISSET(tp->t_state, TS_BUSY)) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (sc->sc_halt > 0) + wakeup(&tp->t_outq); + (*linesw[tp->t_line].l_start)(tp); + } + +#ifdef COM_DEBUG + if (++n >= 32) + goto ohfudge; + if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (1); +#else + if (ISSET(bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) + return (1); +#endif + } +#ifdef COM_DEBUG +ohfudge: + printf("comintr: too many iterations"); + for (n = 0; n < 32; n++) { + if ((n % 4) == 0) + printf("\ncomintr: iter[%02d]", n); + printf(" %02x %02x %02x", iter[n].iir, iter[n].lsr, iter[n].msr); + } + printf("\n"); + printf("comintr: msr %02x mcr %02x lcr %02x ier %02x\n", + sc->sc_msr, sc->sc_mcr, sc->sc_lcr, sc->sc_ier); + printf("comintr: state %08x cc %d\n", sc->sc_tty->t_state, + sc->sc_tty->t_outq.c_cc); +#endif +} + +/* + * Following are all routines needed for COM to act as console + */ +#include <dev/cons.h> + +void +comcnprobe(cp) + struct consdev *cp; +{ + /* XXX NEEDS TO BE FIXED XXX */ + bus_chipset_tag_t bc = 0; + bus_io_handle_t ioh; + int found; + +#if 0 + if (bus_io_map(bc, CONADDR, COM_NPORTS, &ioh)) { + cp->cn_pri = CN_DEAD; + return; + } +#else + ioh = CONADDR; +#endif + found = comprobe1(bc, ioh, CONADDR); + bus_io_unmap(bc, ioh, COM_NPORTS); + if (!found) { + cp->cn_pri = CN_DEAD; + return; + } + + /* locate the major number */ + for (commajor = 0; commajor < nchrdev; commajor++) + if (cdevsw[commajor].d_open == comopen) + break; + + /* initialize required fields */ + cp->cn_dev = makedev(commajor, CONUNIT); +#ifdef COMCONSOLE + cp->cn_pri = CN_REMOTE; /* Force a serial port console */ +#else + cp->cn_pri = CN_NORMAL; +#endif +} + +void +comcninit(cp) + struct consdev *cp; +{ + +#if 0 + XXX NEEDS TO BE FIXED XXX + comconsbc = ???; +#endif + if (bus_io_map(comconsbc, CONADDR, COM_NPORTS, &comconsioh)) + panic("comcninit: mapping failed"); + + cominit(comconsbc, comconsioh, comdefaultrate); + comconsaddr = CONADDR; + comconsinit = 0; +} + +void +cominit(bc, ioh, rate) + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + int rate; +{ + int s = splhigh(); + u_char stat; + + bus_io_write_1(bc, ioh, com_lcr, LCR_DLAB); + rate = comspeed(comdefaultrate); + bus_io_write_1(bc, ioh, com_dlbl, rate); + bus_io_write_1(bc, ioh, com_dlbh, rate >> 8); + bus_io_write_1(bc, ioh, com_lcr, LCR_8BITS); + bus_io_write_1(bc, ioh, com_ier, IER_ERXRDY | IER_ETXRDY); + bus_io_write_1(bc, ioh, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_4); + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); +} + +int +comcngetc(dev) + dev_t dev; +{ + int s = splhigh(); + bus_chipset_tag_t bc = comconsbc; + bus_io_handle_t ioh = comconsioh; + u_char stat, c; + + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_RXRDY)) + ; + c = bus_io_read_1(bc, ioh, com_data); + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); + return c; +} + +/* + * Console kernel output character routine. + */ +void +comcnputc(dev, c) + dev_t dev; + int c; +{ + int s = splhigh(); + bus_chipset_tag_t bc = comconsbc; + bus_io_handle_t ioh = comconsioh; + u_char stat; + register int timo; + +#ifdef KGDB + if (dev != kgdb_dev) +#endif + if (comconsinit == 0) { + cominit(bc, ioh, comdefaultrate); + comconsinit = 1; + } + /* wait for any pending transmission to finish */ + timo = 50000; + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_TXRDY) && --timo) + ; + bus_io_write_1(bc, ioh, com_data, c); + /* wait for this transmission to complete */ + timo = 1500000; + while (!ISSET(stat = bus_io_read_1(bc, ioh, com_lsr), LSR_TXRDY) && --timo) + ; + /* clear any interrupts generated by this transmission */ + stat = bus_io_read_1(bc, ioh, com_iir); + splx(s); +} + +void +comcnpollc(dev, on) + dev_t dev; + int on; +{ + +} diff --git a/sys/arch/arc/dev/asc.c b/sys/arch/arc/dev/asc.c new file mode 100644 index 00000000000..0a8af5464b9 --- /dev/null +++ b/sys/arch/arc/dev/asc.c @@ -0,0 +1,2074 @@ +/* $OpenBSD: asc.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: asc.c,v 1.10 1994/12/05 19:11:12 dean Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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 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 + * SUCH DAMAGE. + * + * @(#)asc.c 8.3 (Berkeley) 7/3/94 + */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * HISTORY + * Log: scsi_53C94_hdw.c,v + * Revision 2.5 91/02/05 17:45:07 mrt + * Added author notices + * [91/02/04 11:18:43 mrt] + * + * Changed to use new Mach copyright + * [91/02/02 12:17:20 mrt] + * + * Revision 2.4 91/01/08 15:48:24 rpd + * Added continuation argument to thread_block. + * [90/12/27 rpd] + * + * Revision 2.3 90/12/05 23:34:48 af + * Recovered from pmax merge.. and from the destruction of a disk. + * [90/12/03 23:40:40 af] + * + * Revision 2.1.1.1 90/11/01 03:39:09 af + * Created, from the DEC specs: + * "PMAZ-AA TURBOchannel SCSI Module Functional Specification" + * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990. + * And from the NCR data sheets + * "NCR 53C94, 53C95, 53C96 Advances SCSI Controller" + * [90/09/03 af] + */ + +/* + * File: scsi_53C94_hdw.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 53C94 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <asc.h> +#if NASC > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/dkstat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> + +#include <arc/dev/dma.h> +#include <arc/dev/scsi.h> +#include <arc/dev/ascreg.h> + +#include <arc/pica/pica.h> +#include <arc/arc/arctype.h> + + +#define readback(a) { register int foo; foo = (a); } +extern int cputype; + +/* + * In 4ns ticks. + */ +int asc_to_scsi_period[] = { + 32, + 33, + 34, + 35, + 5, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, +}; + +/* + * Internal forward declarations. + */ +static void asc_reset(); +static void asc_startcmd(); + +#ifdef DEBUG +int asc_debug = 1; +int asc_debug_cmd; +int asc_debug_bn; +int asc_debug_sz; +#define NLOG 16 +struct asc_log { + u_int status; + u_char state; + u_char msg; + int target; + int resid; +} asc_log[NLOG], *asc_logp = asc_log; +#define PACK(unit, status, ss, ir) \ + ((unit << 24) | (status << 16) | (ss << 8) | ir) +#endif + +/* + * Scripts are entries in a state machine table. + * A script has four parts: a pre-condition, an action, a command to the chip, + * and an index into asc_scripts for the next state. The first triggers error + * handling if not satisfied and in our case it is formed by the + * values of the interrupt register and status register, this + * basically captures the phase of the bus and the TC and BS + * bits. The action part is just a function pointer, and the + * command is what the 53C94 should be told to do at the end + * of the action processing. This command is only issued and the + * script proceeds if the action routine returns TRUE. + * See asc_intr() for how and where this is all done. + */ +typedef struct script { + int condition; /* expected state at interrupt time */ + int (*action)(); /* extra operations */ + int command; /* command to the chip */ + struct script *next; /* index into asc_scripts for next state */ +} script_t; + +/* Matching on the condition value */ +#define SCRIPT_MATCH(ir, csr) ((ir) | (((csr) & 0x67) << 8)) + +/* forward decls of script actions */ +static int script_nop(); /* when nothing needed */ +static int asc_end(); /* all come to an end */ +static int asc_get_status(); /* get status from target */ +static int asc_dma_in(); /* start reading data from target */ +static int asc_last_dma_in(); /* cleanup after all data is read */ +static int asc_resume_in(); /* resume data in after a message */ +static int asc_resume_dma_in(); /* resume DMA after a disconnect */ +static int asc_dma_out(); /* send data to target via dma */ +static int asc_last_dma_out(); /* cleanup after all data is written */ +static int asc_resume_out(); /* resume data out after a message */ +static int asc_resume_dma_out(); /* resume DMA after a disconnect */ +static int asc_sendsync(); /* negotiate sync xfer */ +static int asc_replysync(); /* negotiate sync xfer */ +static int asc_msg_in(); /* process a message byte */ +static int asc_disconnect(); /* process an expected disconnect */ + +/* Define the index into asc_scripts for various state transitions */ +#define SCRIPT_DATA_IN 0 +#define SCRIPT_CONTINUE_IN 2 +#define SCRIPT_DATA_OUT 3 +#define SCRIPT_CONTINUE_OUT 5 +#define SCRIPT_SIMPLE 6 +#define SCRIPT_GET_STATUS 7 +#define SCRIPT_DONE 8 +#define SCRIPT_MSG_IN 9 +#define SCRIPT_REPLY_SYNC 11 +#define SCRIPT_TRY_SYNC 12 +#define SCRIPT_DISCONNECT 15 +#define SCRIPT_RESEL 16 +#define SCRIPT_RESUME_IN 17 +#define SCRIPT_RESUME_DMA_IN 18 +#define SCRIPT_RESUME_OUT 19 +#define SCRIPT_RESUME_DMA_OUT 20 +#define SCRIPT_RESUME_NO_DATA 21 + +/* + * Scripts + */ +script_t asc_scripts[] = { + /* start data in */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI), /* 0 */ + asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 1 */ + asc_last_dma_in, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* continue data in after a chunk is finished */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 2 */ + asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* start data out */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO), /* 3 */ + asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 4 */ + asc_last_dma_out, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* continue data out after a chunk is finished */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 5 */ + asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* simple command with no data transfer */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS), /* 6 */ + script_nop, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, + + /* get status and finish command */ + {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 7 */ + asc_get_status, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_DONE]}, + {SCRIPT_MATCH(ASC_INT_DISC, 0), /* 8 */ + asc_end, ASC_CMD_NOP, + &asc_scripts[SCRIPT_DONE]}, + + /* message in */ + {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 9 */ + asc_msg_in, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_MSG_IN + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 10 */ + script_nop, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_MSG_IN]}, + + /* send synchonous negotiation reply */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 11 */ + asc_replysync, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_REPLY_SYNC]}, + + /* try to negotiate synchonous transfer parameters */ + {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 12 */ + asc_sendsync, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_TRY_SYNC + 1]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 13 */ + script_nop, ASC_CMD_XFER_INFO, + &asc_scripts[SCRIPT_MSG_IN]}, + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND), /* 14 */ + script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_RESUME_NO_DATA]}, + + /* handle a disconnect */ + {SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO), /* 15 */ + asc_disconnect, ASC_CMD_ENABLE_SEL, + &asc_scripts[SCRIPT_RESEL]}, + + /* reselect sequence: this is just a placeholder so match fails */ + {SCRIPT_MATCH(0, ASC_PHASE_MSG_IN), /* 16 */ + script_nop, ASC_CMD_MSG_ACPT, + &asc_scripts[SCRIPT_RESEL]}, + + /* resume data in after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 17 */ + asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* resume partial DMA data in after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 18 */ + asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_IN + 1]}, + + /* resume data out after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 19 */ + asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* resume partial DMA data out after a message */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 20 */ + asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, + &asc_scripts[SCRIPT_DATA_OUT + 1]}, + + /* resume after a message when there is no more data */ + {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 21 */ + script_nop, ASC_CMD_I_COMPLETE, + &asc_scripts[SCRIPT_GET_STATUS]}, +}; + +/* + * State kept for each active SCSI device. + */ +typedef struct scsi_state { + script_t *script; /* saved script while processing error */ + struct scsi_generic cmd;/* storage for scsi command */ + int statusByte; /* status byte returned during STATUS_PHASE */ + u_int dmaBufSize; /* DMA buffer size */ + int dmalen; /* amount to transfer in this chunk */ + int dmaresid; /* amount not transfered if chunk suspended */ + int cmdlen; /* length of command in cmd */ + int buflen; /* total remaining amount of data to transfer */ + vm_offset_t buf; /* current pointer within scsicmd->buf */ + int flags; /* see below */ + int msglen; /* number of message bytes to read */ + int msgcnt; /* number of message bytes received */ + u_char sync_period; /* DMA synchronous period */ + u_char sync_offset; /* DMA synchronous xfer offset or 0 if async */ + u_char msg_out; /* next MSG_OUT byte to send */ + u_char msg_in[16]; /* buffer for multibyte messages */ +} State; + +/* state flags */ +#define DISCONN 0x001 /* true if currently disconnected from bus */ +#define DMA_IN_PROGRESS 0x002 /* true if data DMA started */ +#define DMA_IN 0x004 /* true if reading from SCSI device */ +#define DMA_OUT 0x010 /* true if writing to SCSI device */ +#define DID_SYNC 0x020 /* true if synchronous offset was negotiated */ +#define TRY_SYNC 0x040 /* true if try neg. synchronous offset */ +#define PARITY_ERR 0x080 /* true if parity error seen */ +#define CHECK_SENSE 0x100 /* true if doing sense command */ + +/* + * State kept for each active SCSI host interface (53C94). + */ +struct asc_softc { + struct device sc_dev; /* use as a device */ + asc_regmap_t *regs; /* chip address */ + dma_softc_t __dma; /* stupid macro..... */ + dma_softc_t *dma; /* dma control structure */ + int sc_id; /* SCSI ID of this interface */ + int myidmask; /* ~(1 << myid) */ + int state; /* current SCSI connection state */ + int target; /* target SCSI ID if busy */ + script_t *script; /* next expected interrupt & action */ + struct scsi_xfer *cmdq[ASC_NCMD];/* Pointer to queued commands */ + struct scsi_xfer *cmd[ASC_NCMD];/* Pointer to current active command */ + State st[ASC_NCMD]; /* state info for each active command */ + int min_period; /* Min transfer period clk/byte */ + int max_period; /* Max transfer period clk/byte */ + int ccf; /* CCF, whatever that really is? */ + int timeout_250; /* 250ms timeout */ + int tb_ticks; /* 4ns. ticks/tb channel ticks */ + struct scsi_link sc_link; /* scsi link struct */ +}; + +#define ASC_STATE_IDLE 0 /* idle state */ +#define ASC_STATE_BUSY 1 /* selecting or currently connected */ +#define ASC_STATE_TARGET 2 /* currently selected as target */ +#define ASC_STATE_RESEL 3 /* currently waiting for reselect */ + +typedef struct asc_softc *asc_softc_t; + +/* + * Autoconfiguration data for config. + */ +int ascmatch __P((struct device *, void *, void *)); +void ascattach __P((struct device *, struct device *, void *)); +int ascprint(void *, char *); + +int asc_doprobe __P((void *, int, int, struct device *)); + +struct cfattach asc_ca = { + sizeof(struct asc_softc), ascmatch, ascattach +}; +struct cfdriver asc_cd = { + NULL, "asc", DV_DULL, NULL, 0 +}; + +/* + * Glue to the machine dependent scsi + */ +int asc_scsi_cmd __P((struct scsi_xfer *)); +void asc_minphys __P((struct buf *)); + +struct scsi_adapter asc_switch = { + asc_scsi_cmd, +/*XXX*/ asc_minphys, /* no max transfer size, DMA driver negotiates */ + NULL, + NULL, +}; + +struct scsi_device asc_dev = { +/*XXX*/ NULL, /* Use default error handler */ +/*XXX*/ NULL, /* have a queue, served by this */ +/*XXX*/ NULL, /* have no async handler */ +/*XXX*/ NULL, /* Use default 'done' routine */ +}; + +static int asc_probe(); +static void asc_start(); +static int asc_intr(); + +/* + * Match driver based on name + */ +int +ascmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct confargs *ca = aux; + + if(!BUS_MATCHNAME(ca, "asc")) + return(0); + return(1); +} + +void +ascattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + register struct confargs *ca = aux; + register asc_softc_t asc = (void *)self; + register asc_regmap_t *regs; + int id, s, i; + int bufsiz; + + /* + * Initialize hw descriptor, cache some pointers + */ + asc->regs = (asc_regmap_t *)BUS_CVTADDR(ca); + + /* + * Set up machine dependencies. + * 1) how to do dma + * 2) timing based on chip clock frequency + */ + switch (cputype) { + case ACER_PICA_61: + bufsiz = 63 * 1024; /*XXX check if code handles 0 as 64k */ + asc->dma = &asc->__dma; + asc_dma_init(asc->dma); + break; + default: + bufsiz = 64 * 1024; + }; + /* + * Now for timing. The pica has a 25Mhz + */ + switch (cputype) { + case ACER_PICA_61: + asc->min_period = ASC_MIN_PERIOD25; + asc->max_period = ASC_MAX_PERIOD25; + asc->ccf = ASC_CCF(25); + asc->timeout_250 = ASC_TIMEOUT_250(25, asc->ccf); + asc->tb_ticks = 10; + break; + default: + asc->min_period = ASC_MIN_PERIOD12; + asc->max_period = ASC_MAX_PERIOD12; + asc->ccf = ASC_CCF(13); + asc->timeout_250 = ASC_TIMEOUT_250(13, asc->ccf); + asc->tb_ticks = 20; + break; + }; + + asc->state = ASC_STATE_IDLE; + asc->target = -1; + + regs = asc->regs; + + /* + * Reset chip, fully. Note that interrupts are already enabled. + */ + s = splbio(); + + /* preserve our ID for now */ + asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; + asc->myidmask = ~(1 << asc->sc_id); + + asc_reset(asc, regs); + + /* + * Our SCSI id on the bus. + * The user can set this via the prom on 3maxen/picaen. + * If this changes it is easy to fix: make a default that + * can be changed as boot arg. + */ +#ifdef unneeded + regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) | + (scsi_initiator_id[unit] & 0x7); + asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; +#endif + id = asc->sc_id; + splx(s); + + /* + * Give each target its DMA buffer region. + * The buffer address is the same for all targets, + * the allocated dma viritual scatter/gather space. + */ + for (i = 0; i < ASC_NCMD; i++) { + asc->st[i].dmaBufSize = bufsiz; + } + + /* + * Set up interrupt handler. + */ + BUS_INTR_ESTABLISH(ca, asc_intr, (void *)asc); + + printf(": NCR53C94, target %d\n", id); + + /* + * Fill in the prototype scsi link. + */ + asc->sc_link.adapter_softc = asc; + asc->sc_link.adapter_target = asc->sc_id; + asc->sc_link.adapter = &asc_switch; + asc->sc_link.device = &asc_dev; + asc->sc_link.openings = 2; + + /* + * Now try to attach all the sub devices. + */ + config_found(self, &asc->sc_link, ascprint); +} + +int +ascprint(aux, name) + void *aux; + char *name; +{ + return -1; +} + +/* + * Driver breaks down request transfer size. + */ +void +asc_minphys(bp) + struct buf *bp; +{ +} + +/* + * Start activity on a SCSI device. + * We maintain information on each device separately since devices can + * connect/disconnect during an operation. + */ +int +asc_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct asc_softc *asc = sc_link->adapter_softc; + State *state = &asc->st[sc_link->target]; + + int flags, s; + + flags = xs->flags; + + /* + * Flush caches for any data buffer + */ + if(xs->datalen != 0) { + MachHitFlushDCache(xs->data, xs->datalen); + } + /* + * The hack on the next few lines are to avoid buffers + * mapped to UADDR. Realloc to the kva uarea address. + */ + if((u_int)(xs->data) >= UADDR) { + xs->data = ((u_int)(xs->data) & ~UADDR) + (u_char *)(curproc->p_addr); + } + + /* + * Check if another command is already in progress. + * We may have to change this if we allow SCSI devices with + * separate LUNs. + */ + s = splbio(); + if (asc->cmd[sc_link->target]) { + if (asc->cmdq[sc_link->target]) { + splx(s); + printf("asc_scsi_cmd: called when target busy"); + xs->error = XS_DRIVER_STUFFUP; + return TRY_AGAIN_LATER; + } + asc->cmdq[sc_link->target] = xs; + splx(s); + return SUCCESSFULLY_QUEUED; + } + asc->cmd[sc_link->target] = xs; + + /* + * Going to launch. + * Make a local copy of the command and some pointers. + */ + asc_startcmd(asc, sc_link->target); + + /* + * If in startup, interrupts not usable yet. + */ + if(flags & SCSI_POLL) { + return(asc_poll(asc,sc_link->target)); + } + splx(s); + return SUCCESSFULLY_QUEUED; +} + +int +asc_poll(asc, target) + struct asc_softc *asc; + int target; +{ + struct scsi_xfer *scsicmd = asc->cmd[target]; + int count = scsicmd->timeout * 10; + + while(count) { + if(asc->regs->asc_status &ASC_CSR_INT) { + asc_intr(asc); + } + if(scsicmd->flags & ITSDONE) + break; + DELAY(5); + count--; + } + if(count == 0) { + scsicmd->error = XS_TIMEOUT; + asc_end(asc, 0, 0, 0); + } + return COMPLETE; +} + +static void +asc_reset(asc, regs) + asc_softc_t asc; + asc_regmap_t *regs; +{ + + /* + * Reset chip and wait till done + */ + regs->asc_cmd = ASC_CMD_RESET; + wbflush(); DELAY(25); + + /* spec says this is needed after reset */ + regs->asc_cmd = ASC_CMD_NOP; + wbflush(); DELAY(25); + + /* + * Set up various chip parameters + */ + regs->asc_ccf = asc->ccf; + wbflush(); DELAY(25); + regs->asc_sel_timo = asc->timeout_250; + /* restore our ID */ + regs->asc_cnfg1 = asc->sc_id | ASC_CNFG1_P_CHECK; + /* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */ + regs->asc_cnfg2 = /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL; + regs->asc_cnfg3 = 0; + /* zero anything else */ + ASC_TC_PUT(regs, 0); + regs->asc_syn_p = asc->min_period; + regs->asc_syn_o = 0; /* async for now */ + wbflush(); +} + +/* + * Start a SCSI command on a target. + */ +static void +asc_startcmd(asc, target) + asc_softc_t asc; + int target; +{ + asc_regmap_t *regs; + State *state; + struct scsi_xfer *scsicmd; + int i, len; + + /* + * See if another target is currently selected on this SCSI bus. + */ + if (asc->target >= 0) + return; + + regs = asc->regs; + + /* + * If a reselection is in progress, it is Ok to ignore it since + * the ASC will automatically cancel the command and flush + * the FIFO if the ASC is reselected before the command starts. + * If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if + * a reselect occurs before starting the command. + */ + + asc->state = ASC_STATE_BUSY; + asc->target = target; + + /* cache some pointers */ + scsicmd = asc->cmd[target]; + state = &asc->st[target]; + + /* + * Init the chip and target state. + */ + state->flags = state->flags & (DID_SYNC | CHECK_SENSE); + state->script = (script_t *)0; + state->msg_out = SCSI_NO_OP; + + /* + * Set up for DMA of command output. Also need to flush cache. + */ + if(!(state->flags & CHECK_SENSE)) { + bcopy(scsicmd->cmd, &state->cmd, scsicmd->cmdlen); + state->cmdlen = scsicmd->cmdlen; + state->buf = (vm_offset_t)scsicmd->data; + state->buflen = scsicmd->datalen; + } + len = state->cmdlen; + state->dmalen = len; + +#ifdef DEBUG + if (asc_debug > 1) { + printf("asc_startcmd: %s target %d cmd %x len %d\n", + asc->sc_dev.dv_xname, target, + state->cmd.opcode, state->buflen); + } +#endif + + /* check for simple SCSI command with no data transfer */ + if(state->flags & CHECK_SENSE) { + asc->script = &asc_scripts[SCRIPT_DATA_IN]; + state->flags |= DMA_IN; + } + else if (scsicmd->flags & SCSI_DATA_OUT) { + asc->script = &asc_scripts[SCRIPT_DATA_OUT]; + state->flags |= DMA_OUT; + } + else if (scsicmd->flags & SCSI_DATA_IN) { + asc->script = &asc_scripts[SCRIPT_DATA_IN]; + state->flags |= DMA_IN; + } + else if (state->buflen == 0) { + /* check for sync negotiation */ + if ((scsicmd->flags & /* SCSICMD_USE_SYNC */ 0) && + !(state->flags & DID_SYNC)) { + asc->script = &asc_scripts[SCRIPT_TRY_SYNC]; + state->flags |= TRY_SYNC; + } else + asc->script = &asc_scripts[SCRIPT_SIMPLE]; + state->buf = (vm_offset_t)0; + } + +#ifdef DEBUG + asc_debug_cmd = state->cmd.opcode; + if (state->cmd.opcode == SCSI_READ_EXT) { + asc_debug_bn = (state->cmd.bytes[1] << 24) | + (state->cmd.bytes[2] << 16) | + (state->cmd.bytes[3] << 8) | + state->cmd.bytes[4]; + asc_debug_sz = (state->cmd.bytes[6] << 8) | state->cmd.bytes[7]; + } + asc_logp->status = PACK(asc->sc_dev.dv_unit, 0, 0, asc_debug_cmd); + asc_logp->target = asc->target; + asc_logp->state = asc->script - asc_scripts; + asc_logp->msg = SCSI_DIS_REC_IDENTIFY; + asc_logp->resid = scsicmd->datalen; + if (++asc_logp >= &asc_log[NLOG]) + asc_logp = asc_log; +#endif + + /* preload the FIFO with the message and command to be sent */ + regs->asc_fifo = SCSI_DIS_REC_IDENTIFY | (scsicmd->sc_link->lun & 0x07); + + for( i = 0; i < len; i++ ) { + regs->asc_fifo = ((caddr_t)&state->cmd)[i]; + } + ASC_TC_PUT(regs, 0); + readback(regs->asc_cmd); + regs->asc_cmd = ASC_CMD_DMA; + readback(regs->asc_cmd); + + regs->asc_dbus_id = target; + readback(regs->asc_dbus_id); + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + +/*XXX PEFO */ +/* we are not using sync transfer now, need to check this if we will */ + + if (state->flags & TRY_SYNC) + regs->asc_cmd = ASC_CMD_SEL_ATN_STOP; + else + regs->asc_cmd = ASC_CMD_SEL_ATN; + readback(regs->asc_cmd); +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +int +asc_intr(sc) + void *sc; +{ + asc_softc_t asc = sc; + asc_regmap_t *regs = asc->regs; + State *state; + script_t *scpt; + int ss, ir, status; + + /* collect ephemeral information */ + status = regs->asc_status; + ss = regs->asc_ss; + + if ((status & ASC_CSR_INT) == 0) /* Make shure it's a real interrupt */ + return; + + ir = regs->asc_intr; /* this resets the previous two */ + scpt = asc->script; + +#ifdef DEBUG + asc_logp->status = PACK(asc->sc_dev.dv_unit, status, ss, ir); + asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1; + asc_logp->state = scpt - asc_scripts; + asc_logp->msg = -1; + asc_logp->resid = 0; + if (++asc_logp >= &asc_log[NLOG]) + asc_logp = asc_log; + if (asc_debug > 2) + printf("asc_intr: status %x ss %x ir %x cond %d:%x\n", + status, ss, ir, scpt - asc_scripts, scpt->condition); +#endif + + /* check the expected state */ + if (SCRIPT_MATCH(ir, status) == scpt->condition) { + /* + * Perform the appropriate operation, then proceed. + */ + if ((*scpt->action)(asc, status, ss, ir)) { + regs->asc_cmd = scpt->command; + readback(regs->asc_cmd); + asc->script = scpt->next; + } + goto done; + } + + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (status & ASC_CSR_PE) { + printf("%s: SCSI device %d: incomming parity error seen\n", + asc->sc_dev.dv_xname, asc->target); + asc->st[asc->target].flags |= PARITY_ERR; + } + + /* + * Check for gross error. + * Probably a bug in a device driver. + */ + if (status & ASC_CSR_GE) { + printf("%s: SCSI device %d: gross error\n", + asc->sc_dev.dv_xname, asc->target); + goto abort; + } + + /* check for message in or out */ + if ((ir & ~ASC_INT_FC) == ASC_INT_BS) { + register int len, fifo; + + state = &asc->st[asc->target]; + switch (ASC_PHASE(status)) { + case ASC_PHASE_DATAI: + case ASC_PHASE_DATAO: + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); + goto abort; + + case ASC_PHASE_MSG_IN: + break; + + case ASC_PHASE_MSG_OUT: + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (state->flags & PARITY_ERR) { + state->flags &= ~PARITY_ERR; + state->msg_out = SCSI_MESSAGE_PARITY_ERROR; + /* reset message in counter */ + state->msglen = 0; + } else + state->msg_out = SCSI_NO_OP; + regs->asc_fifo = state->msg_out; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + goto done; + + case ASC_PHASE_STATUS: + /* probably an error in the SCSI command */ + asc->script = &asc_scripts[SCRIPT_GET_STATUS]; + regs->asc_cmd = ASC_CMD_I_COMPLETE; + readback(regs->asc_cmd); + goto done; + + default: + goto abort; + } + + if (state->script) + goto abort; + + /* + * OK, message coming in clean up whatever is going on. + * Get number of bytes left to transfered from byte counter + * counter decrements when data is trf on the SCSI bus + */ + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + /* flush any data in the FIFO */ + if (fifo && !(state->flags & DMA_IN_PROGRESS)) { +printf("asc_intr: fifo flush %d len %d fifo %x\n", fifo, len, regs->asc_fifo); + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + } + else if (fifo && state->flags & DMA_IN_PROGRESS) { + if (state->flags & DMA_OUT) { + len += fifo; /* Bytes dma'ed but not sent */ + } + else if (state->flags & DMA_IN) { + u_char *cp; + + printf("asc_intr: IN: dmalen %d len %d fifo %d\n", + state->dmalen, len, fifo); /* XXX */ + } + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + } + if (len && (state->flags & DMA_IN_PROGRESS)) { + /* save number of bytes still to be sent or received */ + state->dmaresid = len; + state->flags &= ~DMA_IN_PROGRESS; + ASC_TC_PUT(regs, 0); +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + /* setup state to resume to */ + if (state->flags & DMA_IN) { + /* + * Since the ASC_CNFG3_SRB bit of the + * cnfg3 register bit is not set, + * we just transferred an extra byte. + * Since we can't resume on an odd byte + * boundary, we copy the valid data out + * and resume DMA at the start address. + */ + if (len & 1) { + printf("asc_intr: msg in len %d (fifo %d)\n", + len, fifo); /* XXX */ + len = state->dmalen - len; + goto do_in; + } + state->script = + &asc_scripts[SCRIPT_RESUME_DMA_IN]; + } else if (state->flags & DMA_OUT) + state->script = + &asc_scripts[SCRIPT_RESUME_DMA_OUT]; + else + state->script = asc->script; + } else if (state->flags & DMA_IN) { + if (len) { +#ifdef DEBUG + printf("asc_intr: 1: bn %d len %d (fifo %d)\n", + asc_debug_bn, len, fifo); /* XXX */ +#endif + goto abort; + } + /* setup state to resume to */ + if (state->flags & DMA_IN_PROGRESS) { + len = state->dmalen; + state->flags &= ~DMA_IN_PROGRESS; + do_in: + DMA_END(asc->dma); + state->buf += len; + state->buflen -= len; + } + if (state->buflen) + state->script = + &asc_scripts[SCRIPT_RESUME_IN]; + else + state->script = + &asc_scripts[SCRIPT_RESUME_NO_DATA]; + } else if (state->flags & DMA_OUT) { + if (len) { + printf("asc_intr: 2: len %d (fifo %d)\n", len, + fifo); /* XXX */ +/* XXX THEO */ +#if 1 + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + DELAY(2); + len = 0; +#else + goto abort; +#endif + } + /* + * If this is the last chunk, the next expected + * state is to get status. + */ + if (state->flags & DMA_IN_PROGRESS) { + state->flags &= ~DMA_IN_PROGRESS; + DMA_END(asc->dma); + len = state->dmalen; + state->buf += len; + state->buflen -= len; + } + if (state->buflen) + state->script = + &asc_scripts[SCRIPT_RESUME_OUT]; + else + state->script = + &asc_scripts[SCRIPT_RESUME_NO_DATA]; + } else if (asc->script == &asc_scripts[SCRIPT_SIMPLE]) + state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA]; + else + state->script = asc->script; + + /* setup to receive a message */ + asc->script = &asc_scripts[SCRIPT_MSG_IN]; + state->msglen = 0; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + goto done; + } + + /* check for SCSI bus reset */ + if (ir & ASC_INT_RESET) { + register int i; + + printf("%s: SCSI bus reset!!\n", asc->sc_dev.dv_xname); + /* need to flush any pending commands */ + for (i = 0; i < ASC_NCMD; i++) { + if (!asc->cmd[i]) + continue; + asc->cmd[i]->error = XS_DRIVER_STUFFUP; + asc_end(asc, 0, 0, 0); + } + /* rearbitrate synchronous offset */ + for (i = 0; i < ASC_NCMD; i++) { + asc->st[i].sync_offset = 0; + asc->st[i].flags = 0; + } + asc->target = -1; + return; + } + + /* check for command errors */ + if (ir & ASC_INT_ILL) + goto abort; + + /* check for disconnect */ + if (ir & ASC_INT_DISC) { + state = &asc->st[asc->target]; + switch (asc->script - asc_scripts) { + case SCRIPT_DONE: + case SCRIPT_DISCONNECT: + /* + * Disconnects can happen normally when the + * command is complete with the phase being + * either ASC_PHASE_DATAO or ASC_PHASE_MSG_IN. + * The SCRIPT_MATCH() only checks for one phase + * so we can wind up here. + * Perform the appropriate operation, then proceed. + */ + if ((*scpt->action)(asc, status, ss, ir)) { + regs->asc_cmd = scpt->command; + readback(regs->asc_cmd); + asc->script = scpt->next; + } + goto done; + + case SCRIPT_TRY_SYNC: + case SCRIPT_SIMPLE: + case SCRIPT_DATA_IN: + case SCRIPT_DATA_OUT: /* one of the starting scripts */ + if (ASC_SS(ss) == 0) { + /* device did not respond */ + if (regs->asc_flags & ASC_FLAGS_FIFO_CNT) { + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + asc->cmd[asc->target]->error = XS_DRIVER_STUFFUP; + asc_end(asc, status, ss, ir); + return; + } + /* FALLTHROUGH */ + + default: + printf("%s: SCSI device %d: unexpected disconnect\n", + asc->sc_dev.dv_xname, asc->target); +#ifdef DEBUG + asc_DumpLog("asc_disc"); +#endif + /* + * On rare occasions my RZ24 does a disconnect during + * data in phase and the following seems to keep it + * happy. + * XXX Should a scsi disk ever do this?? + */ + asc->script = &asc_scripts[SCRIPT_RESEL]; + asc->state = ASC_STATE_RESEL; + state->flags |= DISCONN; + regs->asc_cmd = ASC_CMD_ENABLE_SEL; + readback(regs->asc_cmd); + return; + } + } + + /* check for reselect */ + if (ir & ASC_INT_RESEL) { + unsigned fifo, id, msg; + + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + if (fifo < 2) + goto abort; + /* read unencoded SCSI ID and convert to binary */ + msg = regs->asc_fifo & asc->myidmask; + for (id = 0; (msg & 1) == 0; id++) + msg >>= 1; + /* read identify message */ + msg = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = msg; + else + asc_logp[-1].msg = msg; +#endif + asc->state = ASC_STATE_BUSY; + asc->target = id; + state = &asc->st[id]; + asc->script = state->script; + state->script = (script_t *)0; + if (!(state->flags & DISCONN)) + goto abort; + state->flags &= ~DISCONN; + regs->asc_syn_p = state->sync_period; + regs->asc_syn_o = state->sync_offset; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + goto done; + } + + /* check if we are being selected as a target */ + if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN)) + goto abort; + + /* + * 'ir' must be just ASC_INT_FC. + * This is normal if canceling an ASC_ENABLE_SEL. + */ + +done: + wbflush(); + /* + * If the next interrupt comes in immediatly the interrupt + * dispatcher (which we are returning to) will catch it + * before returning to the interrupted code. + */ + return; + +abort: +#ifdef DEBUG + asc_DumpLog("asc_intr"); +#endif +#if 0 + panic("asc_intr"); +#else + boot(4); /* XXX */ +#endif +} + +/* + * All the many little things that the interrupt + * routine might switch to. + */ + +/* ARGSUSED */ +static int +script_nop(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + return (1); +} + +/* ARGSUSED */ +static int +asc_get_status(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register int data; + + /* + * Get the last two bytes in the FIFO. + */ + if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) { + printf("asc_get_status: cmdreg %x, fifo cnt %d\n", + regs->asc_cmd, data); /* XXX */ +#ifdef DEBUG + asc_DumpLog("get_status"); /* XXX */ +#endif + if (data < 2) { + asc->regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(asc->regs->asc_cmd); + return (0); + } + do { + data = regs->asc_fifo; + } while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2); + } + + /* save the status byte */ + asc->st[asc->target].statusByte = data = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = data; + else + asc_logp[-1].msg = data; +#endif + + /* get the (presumed) command_complete message */ + if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE) + return (1); + +#ifdef DEBUG + printf("asc_get_status: status %x cmd %x\n", + asc->st[asc->target].statusByte, data); + asc_DumpLog("asc_get_status"); +#endif + return (0); +} + +/* ARGSUSED */ +static int +asc_end(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + struct scsi_xfer *scsicmd; + struct scsi_link *sc_link; + State *state; + int i, target; + + asc->state = ASC_STATE_IDLE; + target = asc->target; + asc->target = -1; + scsicmd = asc->cmd[target]; + sc_link = scsicmd->sc_link; + asc->cmd[target] = (struct scsi_xfer *)0; + state = &asc->st[target]; + +#ifdef DEBUG + if (asc_debug > 1) { + printf("asc_end: %s target %d cmd %x err %d resid %d\n", + asc->sc_dev.dv_xname, target, + state->cmd.opcode, scsicmd->error, state->buflen); + } +#endif +#ifdef DIAGNOSTIC + if (target < 0 || !scsicmd) + panic("asc_end"); +#endif + + /* look for disconnected devices */ + for (i = 0; i < ASC_NCMD; i++) { + if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN)) + continue; + asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL; + readback(asc->regs->asc_cmd); + asc->state = ASC_STATE_RESEL; + asc->script = &asc_scripts[SCRIPT_RESEL]; + break; + } + + if(scsicmd->error == XS_NOERROR && !(state->flags & CHECK_SENSE)) { + if((state->statusByte & ST_MASK) == SCSI_CHECK) { + struct scsi_sense *ss = (void *)&state->cmd; + /* Save return values */ + scsicmd->resid = state->buflen; + scsicmd->status = state->statusByte; + /* Set up sense request command */ + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = sc_link->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + state->cmdlen = sizeof(*ss); + state->buf = (vm_offset_t)&scsicmd->sense; + state->buflen = sizeof(struct scsi_sense_data); + state->flags |= CHECK_SENSE; + MachHitFlushDCache(state->buf, state->buflen); + asc->cmd[target] = scsicmd; + asc_startcmd(asc, target); + return(0); + } + } + + if(scsicmd->error == XS_NOERROR && (state->flags & CHECK_SENSE)) { + scsicmd->error = XS_SENSE; + } + else { + scsicmd->resid = state->buflen; + } + state->flags &= ~CHECK_SENSE; + + /* + * Look for another device that is ready. + * May want to keep last one started and increment for fairness + * rather than always starting at zero. + */ + for (i = 0; i < ASC_NCMD; i++) { + if (asc->cmd[i] == 0 && asc->cmdq[i] != 0) { + asc->cmd[i] = asc->cmdq[i]; + asc->cmdq[i] = 0; + } + } + for (i = 0; i < ASC_NCMD; i++) { + /* don't restart a disconnected command */ + if (!asc->cmd[i] || (asc->st[i].flags & DISCONN)) + continue; + asc_startcmd(asc, i); + break; + } + + /* signal device driver that the command is done */ + scsicmd->flags |= ITSDONE; + scsi_done(scsicmd); + + return (0); +} + +/* ARGSUSED */ +static int +asc_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* check for previous chunk in buffer */ + if (state->flags & DMA_IN_PROGRESS) { + /* + * Only count bytes that have been copied to memory. + * There may be some bytes in the FIFO if synchonous transfers + * are in progress. + */ + DMA_END(asc->dma); + ASC_TC_GET(regs, len); + len = state->dmalen - len; + state->buf += len; + state->buflen -= len; + } + + /* setup to start reading the next chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; + DMA_START(asc->dma, (caddr_t)state->buf, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_last_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + DMA_END(asc->dma); + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); +#endif + if (fifo) { + /* device must be trying to send more than we expect */ + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + state->flags &= ~DMA_IN_PROGRESS; + len = state->dmalen - len; + state->buflen -= len; + + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* setup to start reading the next chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + DMA_START(asc->dma, (caddr_t)state->buf, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_in: buflen %d, len %d\n", state->buflen, + len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_dma_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, off; + + /* setup to finish reading the current chunk */ + len = state->dmaresid; + off = state->dmalen - len; + if ((off & 1) && state->sync_offset) { + printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n", + state->dmalen, len, off); /* XXX */ + regs->asc_res_fifo = ((u_char *)state->buf)[off]; +/*XXX Need to flush cache ? */ + } + DMA_START(asc->dma, (caddr_t)state->buf + off, len, DMA_FROM_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n", + state->dmalen, state->buflen, len, off); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (state->dmalen != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + if (state->flags & DMA_IN_PROGRESS) { + /* check to be sure previous chunk was finished */ + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; + if (len || fifo) + printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); /* XXX */ + len += fifo; + len = state->dmalen - len; + state->buf += len; + state->buflen -= len; + } + + /* setup for this chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; + DMA_START(asc->dma, (caddr_t)state->buf, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_last_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, fifo; + + ASC_TC_GET(regs, len); + fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n", + state->buflen, state->dmalen, len, fifo); +#endif + if (fifo) { + len += fifo; + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + state->flags &= ~DMA_IN_PROGRESS; + len = state->dmalen - len; + state->buflen -= len; + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len; + + /* setup for this chunk */ + len = state->buflen; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + if (len > state->dmaBufSize) + len = state->dmaBufSize; + state->dmalen = len; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].resid = len; + else + asc_logp[-1].resid = len; +#endif + DMA_START(asc->dma, (caddr_t)state->buf, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_out: buflen %d, len %d\n", state->buflen, + len); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (len != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_resume_dma_out(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int len, off; + + /* setup to finish writing this chunk */ + len = state->dmaresid; + off = state->dmalen - len; + if (off & 1) { + printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n", + state->dmalen, len, off); /* XXX */ + regs->asc_fifo = ((u_char *)state->buf)[off]; +/*XXX Need to flush Cache ? */ + off++; + len--; + } + DMA_START(asc->dma, (caddr_t)state->buf + off, len, DMA_TO_DEV); + ASC_TC_PUT(regs, len); +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n", + state->dmalen, state->buflen, len, off); +#endif + + /* check for next chunk */ + state->flags |= DMA_IN_PROGRESS; + if (state->dmalen != state->buflen) { + regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; + return (0); + } + return (1); +} + +/* ARGSUSED */ +static int +asc_sendsync(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + + /* send the extended synchronous negotiation message */ + regs->asc_fifo = SCSI_EXTENDED_MSG; + wbflush(); + regs->asc_fifo = 3; + wbflush(); + regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; + wbflush(); + regs->asc_fifo = SCSI_MIN_PERIOD; + wbflush(); + regs->asc_fifo = ASC_MAX_OFFSET; + /* state to resume after we see the sync reply message */ + state->script = asc->script + 2; + state->msglen = 0; + return (1); +} + +/* ARGSUSED */ +static int +asc_replysync(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_replysync: %x %x\n", + asc_to_scsi_period[state->sync_period] * asc->tb_ticks, + state->sync_offset); +#endif + /* send synchronous transfer in response to a request */ + regs->asc_fifo = SCSI_EXTENDED_MSG; + wbflush(); + regs->asc_fifo = 3; + wbflush(); + regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; + wbflush(); + regs->asc_fifo = asc_to_scsi_period[state->sync_period] * asc->tb_ticks; + wbflush(); + regs->asc_fifo = state->sync_offset; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + + /* return to the appropriate script */ + if (!state->script) { +#ifdef DEBUG + asc_DumpLog("asc_replsync"); +#endif + panic("asc_replysync"); + } + asc->script = state->script; + state->script = (script_t *)0; + return (0); +} + +/* ARGSUSED */ +static int +asc_msg_in(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register asc_regmap_t *regs = asc->regs; + register State *state = &asc->st[asc->target]; + register int msg; + int i; + + /* read one message byte */ + msg = regs->asc_fifo; +#ifdef DEBUG + if (asc_logp == asc_log) + asc_log[NLOG - 1].msg = msg; + else + asc_logp[-1].msg = msg; +#endif + + /* check for multi-byte message */ + if (state->msglen != 0) { + /* first byte is the message length */ + if (state->msglen < 0) { + state->msglen = msg; + return (1); + } + if (state->msgcnt >= state->msglen) + goto abort; + state->msg_in[state->msgcnt++] = msg; + + /* did we just read the last byte of the message? */ + if (state->msgcnt != state->msglen) + return (1); + + /* process an extended message */ +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_msg_in: msg %x %x %x\n", + state->msg_in[0], + state->msg_in[1], + state->msg_in[2]); +#endif + switch (state->msg_in[0]) { + case SCSI_SYNCHRONOUS_XFER: + state->flags |= DID_SYNC; + state->sync_offset = state->msg_in[2]; + + /* convert SCSI period to ASC period */ + i = state->msg_in[1] / asc->tb_ticks; + if (i < asc->min_period) + i = asc->min_period; + else if (i >= asc->max_period) { + /* can't do sync transfer, period too long */ + printf("%s: SCSI device %d: sync xfer period too long (%d)\n", + asc->sc_dev.dv_xname, asc->target, i); + i = asc->max_period; + state->sync_offset = 0; + } + if ((i * asc->tb_ticks) != state->msg_in[1]) + i++; + state->sync_period = i & 0x1F; + + /* + * If this is a request, check minimums and + * send back an acknowledge. + */ + if (!(state->flags & TRY_SYNC)) { + regs->asc_cmd = ASC_CMD_SET_ATN; + readback(regs->asc_cmd); + + if (state->sync_period < asc->min_period) + state->sync_period = + asc->min_period; + if (state->sync_offset > ASC_MAX_OFFSET) + state->sync_offset = + ASC_MAX_OFFSET; + asc->script = &asc_scripts[SCRIPT_REPLY_SYNC]; + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + return (0); + } + + regs->asc_syn_p = state->sync_period; + readback(regs->asc_syn_p); + regs->asc_syn_o = state->sync_offset; + readback(regs->asc_syn_o); + goto done; + + default: + printf("%s: SCSI device %d: rejecting extended message 0x%x\n", + asc->sc_dev.dv_xname, asc->target, + state->msg_in[0]); + goto reject; + } + } + + /* process first byte of a message */ +#ifdef DEBUG + if (asc_debug > 2) + printf("asc_msg_in: msg %x\n", msg); +#endif + switch (msg) { +#if 0 + case SCSI_MESSAGE_REJECT: + printf(" did not like SYNCH xfer "); /* XXX */ + state->flags |= DID_SYNC; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + status = asc_wait(regs, ASC_CSR_INT); + ir = regs->asc_intr; + /* some just break out here, some dont */ + if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) { + regs->asc_fifo = SCSI_ABORT; + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + status = asc_wait(regs, ASC_CSR_INT); + ir = regs->asc_intr; + } + if (ir & ASC_INT_DISC) { + asc_end(asc, status, 0, ir); + return (0); + } + goto status; +#endif /* 0 */ + + case SCSI_EXTENDED_MSG: /* read an extended message */ + /* setup to read message length next */ + state->msglen = -1; + state->msgcnt = 0; + return (1); + + case SCSI_NO_OP: + break; + + case SCSI_SAVE_DATA_POINTER: + /* expect another message */ + return (1); + + case SCSI_RESTORE_POINTERS: + /* + * Need to do the following if resuming synchonous data in + * on an odd byte boundary. + regs->asc_cnfg2 |= ASC_CNFG2_RFB; + */ + break; + + case SCSI_DISCONNECT: + if (state->flags & DISCONN) + goto abort; + state->flags |= DISCONN; + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + asc->script = &asc_scripts[SCRIPT_DISCONNECT]; + return (0); + + default: + printf("%s: SCSI device %d: rejecting message 0x%x\n", + asc->sc_dev.dv_xname, asc->target, msg); + reject: + /* request a message out before acknowledging this message */ + state->msg_out = SCSI_MESSAGE_REJECT; + regs->asc_cmd = ASC_CMD_SET_ATN; + readback(regs->asc_cmd); + } + +done: + /* return to original script */ + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + if (!state->script) { + abort: +#ifdef DEBUG + asc_DumpLog("asc_msg_in"); +#endif + panic("asc_msg_in"); + } + asc->script = state->script; + state->script = (script_t *)0; + return (0); +} + +/* ARGSUSED */ +static int +asc_disconnect(asc, status, ss, ir) + register asc_softc_t asc; + register int status, ss, ir; +{ + register State *state = &asc->st[asc->target]; + +#ifdef DIAGNOSTIC + if (!(state->flags & DISCONN)) { + printf("asc_disconnect: device %d: DISCONN not set!\n", + asc->target); + } +#endif /* DIAGNOSTIC */ + asc->target = -1; + asc->state = ASC_STATE_RESEL; + return (1); +} + +#ifdef DEBUG +/* + * Dump the log buffer. + */ +asc_DumpLog(str) + char *str; +{ + register struct asc_log *lp; + register u_int status; + + printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd, + asc_debug_bn, asc_debug_sz); + lp = asc_logp; + do { + status = lp->status; + printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x resid %d\n", + status >> 24, + lp->target, + (status >> 16) & 0xFF, + (status >> 8) & 0xFF, + status & 0XFF, + lp->state, + asc_scripts[lp->state].condition, + lp->msg, lp->resid); + if (++lp >= &asc_log[NLOG]) + lp = asc_log; + } while (lp != asc_logp); +} +#endif /* DEBUG */ + +#endif /* NASC > 0 */ diff --git a/sys/arch/arc/dev/ascreg.h b/sys/arch/arc/dev/ascreg.h new file mode 100644 index 00000000000..c6ff2fb236b --- /dev/null +++ b/sys/arch/arc/dev/ascreg.h @@ -0,0 +1,322 @@ +/* $OpenBSD: ascreg.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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 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 + * SUCH DAMAGE. + * + * from: @(#)ascreg.h 8.1 (Berkeley) 6/10/93 + * $Id: ascreg.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ + */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * HISTORY + * Log: scsi_53C94.h,v + * Revision 2.4 91/02/05 17:44:59 mrt + * Added author notices + * [91/02/04 11:18:32 mrt] + * + * Changed to use new Mach copyright + * [91/02/02 12:17:11 mrt] + * + * Revision 2.3 90/12/05 23:34:46 af + * Documented max DMA xfer size. + * [90/12/03 23:39:36 af] + * + * Revision 2.1.1.1 90/11/01 03:38:54 af + * Created, from the DEC specs: + * "PMAZ-AA TURBOchannel SCSI Module Functional Specification" + * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990. + * And from the NCR data sheets + * "NCR 53C94, 53C95, 53C96 Advanced SCSI Controller" + * [90/09/03 af] + */ + +/* + * File: scsi_53C94.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Defines for the NCR 53C94 ASC (SCSI interface) + * Some gotcha came from the "86C01/53C94 DMA lab work" written + * by Ken Stewart (NCR MED Logic Products Applications Engineer) + * courtesy of NCR. Thanks Ken ! + */ + +#define ASC_OFFSET_53C94 0x0 /* from module base */ + +#define ASC_NCMD 7 /* Number of simultaneous cmds */ + +/* + * Synch xfer parameters, and timing conversions + */ +#define SCSI_MIN_PERIOD 50 /* in 4 nsecs units */ +#define ASC_MIN_PERIOD40 8 /* in CLKS/BYTE, 1 CLK = 25nsecs */ +#define ASC_MIN_PERIOD25 5 /* in CLKS/BYTE, 1 CLK = 40nsecs */ +#define ASC_MIN_PERIOD12 3 /* in CLKS/BYTE, 1 CLK = 80nsecs */ +#define ASC_MAX_PERIOD40 56 /* in CLKS/BYTE, 1 CLK = 25nsecs */ +#define ASC_MAX_PERIOD25 35 /* in CLKS/BYTE, 1 CLK = 40nsecs */ +#define ASC_MAX_PERIOD12 18 /* in CLKS/BYTE, 1 CLK = 80nsecs */ +#define ASC_MAX_OFFSET 15 /* pure number */ +/* + * Register map, padded as needed + */ + +typedef volatile struct { + u_char asc_tc_lsb; /* rw: Transfer Counter LSB */ + u_char asc_tc_msb; /* rw: Transfer Counter MSB */ + u_char asc_fifo; /* rw: FIFO top */ + u_char asc_cmd; /* rw: Command */ + u_char asc_status; /* r: Status */ +#define asc_dbus_id asc_status /* w: Destination Bus ID */ + u_char asc_intr; /* r: Interrupt */ +#define asc_sel_timo asc_intr /* w: (re)select timeout */ + u_char asc_ss; /* r: Sequence Step */ +#define asc_syn_p asc_ss /* w: synchronous period */ + u_char asc_flags; /* r: FIFO flags + seq step */ +#define asc_syn_o asc_flags /* w: synchronous offset */ + u_char asc_cnfg1; /* rw: Configuration 1 */ + u_char asc_ccf; /* w: Clock Conv. Factor */ + u_char asc_test; /* w: Test Mode */ + u_char asc_cnfg2; /* rw: Configuration 2 */ + u_char asc_cnfg3; /* rw: Configuration 3 */ + u_char asc_res_fifo; /* w: Reserve FIFO byte */ +} asc_regmap_t; + +/* + * Transfer Count: access macros + * That a NOP is required after loading the dma counter + * I learned on the NCR test code. Sic. + */ + +#define ASC_TC_MAX 0x10000 + +#define ASC_TC_GET(ptr, val) \ + val = (ptr)->asc_tc_lsb | ((ptr)->asc_tc_msb << 8) +#define ASC_TC_PUT(ptr, val) \ + (ptr)->asc_tc_lsb = (val); \ + (ptr)->asc_tc_msb = (val) >> 8; \ + (ptr)->asc_cmd = ASC_CMD_NOP | ASC_CMD_DMA; + +/* + * Command register (command codes) + */ + +#define ASC_CMD_DMA 0x80 + /* Miscellaneous */ +#define ASC_CMD_NOP 0x00 +#define ASC_CMD_FLUSH 0x01 +#define ASC_CMD_RESET 0x02 +#define ASC_CMD_BUS_RESET 0x03 + /* Initiator state */ +#define ASC_CMD_XFER_INFO 0x10 +#define ASC_CMD_I_COMPLETE 0x11 +#define ASC_CMD_MSG_ACPT 0x12 +#define ASC_CMD_XFER_PAD 0x18 +#define ASC_CMD_SET_ATN 0x1a +#define ASC_CMD_CLR_ATN 0x1b + /* Target state */ +#define ASC_CMD_SND_MSG 0x20 +#define ASC_CMD_SND_STATUS 0x21 +#define ASC_CMD_SND_DATA 0x22 +#define ASC_CMD_DISC_SEQ 0x23 +#define ASC_CMD_TERM 0x24 +#define ASC_CMD_T_COMPLETE 0x25 +#define ASC_CMD_DISC 0x27 +#define ASC_CMD_RCV_MSG 0x28 +#define ASC_CMD_RCV_CDB 0x29 +#define ASC_CMD_RCV_DATA 0x2a +#define ASC_CMD_RCV_CMD 0x2b +#define ASC_CMD_ABRT_DMA 0x04 + /* Disconnected state */ +#define ASC_CMD_RESELECT 0x40 +#define ASC_CMD_SEL 0x41 +#define ASC_CMD_SEL_ATN 0x42 +#define ASC_CMD_SEL_ATN_STOP 0x43 +#define ASC_CMD_ENABLE_SEL 0x44 +#define ASC_CMD_DISABLE_SEL 0x45 +#define ASC_CMD_SEL_ATN3 0x46 + +/* + * Status register, and phase encoding + */ + +#define ASC_CSR_INT 0x80 +#define ASC_CSR_GE 0x40 +#define ASC_CSR_PE 0x20 +#define ASC_CSR_TC 0x10 +#define ASC_CSR_VGC 0x08 +#define ASC_CSR_MSG 0x04 +#define ASC_CSR_CD 0x02 +#define ASC_CSR_IO 0x01 + +#define ASC_PHASE(csr) ((csr) & 0x7) +#define ASC_PHASE_DATAO 0x0 +#define ASC_PHASE_DATAI 0x1 +#define ASC_PHASE_COMMAND 0x2 +#define ASC_PHASE_STATUS 0x3 + /* 4..5 ANSI reserved */ +#define ASC_PHASE_MSG_OUT 0x6 +#define ASC_PHASE_MSG_IN 0x7 + +/* + * Destination Bus ID + */ + +#define ASC_DEST_ID_MASK 0x07 + +/* + * Interrupt register + */ + +#define ASC_INT_RESET 0x80 +#define ASC_INT_ILL 0x40 +#define ASC_INT_DISC 0x20 +#define ASC_INT_BS 0x10 +#define ASC_INT_FC 0x08 +#define ASC_INT_RESEL 0x04 +#define ASC_INT_SEL_ATN 0x02 +#define ASC_INT_SEL 0x01 + +/* + * Timeout register: + * + * val = (timeout * CLK_freq) / (8192 * CCF); + */ + +#define ASC_TIMEOUT_250(clk, ccf) (((clk) * 31) / (ccf)) + +/* + * Sequence Step register + */ + +#define ASC_SS_RESERVED 0xf0 +#define ASC_SS_SOM 0x08 +#define ASC_SS_MASK 0x07 +#define ASC_SS(ss) ((ss) & ASC_SS_MASK) + +/* + * Synchronous Transfer Period + */ + +#define ASC_STP_MASK 0x1f +#define ASC_STP_MIN 0x05 /* 5 clk per byte */ +#define ASC_STP_MAX 0x04 /* after ovfl, 35 clk/byte */ + +/* + * FIFO flags + */ + +#define ASC_FLAGS_SEQ_STEP 0xe0 +#define ASC_FLAGS_FIFO_CNT 0x1f + +/* + * Synchronous offset + */ + +#define ASC_SYNO_MASK 0x0f /* 0 -> asyn */ + +/* + * Configuration 1 + */ + +#define ASC_CNFG1_SLOW 0x80 +#define ASC_CNFG1_SRD 0x40 +#define ASC_CNFG1_P_TEST 0x20 +#define ASC_CNFG1_P_CHECK 0x10 +#define ASC_CNFG1_TEST 0x08 +#define ASC_CNFG1_MY_BUS_ID 0x07 + +/* + * CCF register + */ + +#define ASC_CCF(clk) ((((clk) - 1) / 5) + 1) + +/* + * Test register + */ + +#define ASC_TEST_XXXX 0xf8 +#define ASC_TEST_HI_Z 0x04 +#define ASC_TEST_I 0x02 +#define ASC_TEST_T 0x01 + +/* + * Configuration 2 + */ + +#define ASC_CNFG2_RFB 0x80 +#define ASC_CNFG2_EPL 0x40 +#define ASC_CNFG2_EBC 0x20 +#define ASC_CNFG2_DREQ_HIZ 0x10 +#define ASC_CNFG2_SCSI2 0x08 +#define ASC_CNFG2_BPA 0x04 +#define ASC_CNFG2_RPE 0x02 +#define ASC_CNFG2_DPE 0x01 + +/* + * Configuration 3 + */ + +#define ASC_CNFG3_RESERVED 0xf8 +#define ASC_CNFG3_SRB 0x04 +#define ASC_CNFG3_ALT_DMA 0x02 +#define ASC_CNFG3_T8 0x01 + +#define ST_MASK 0x3e diff --git a/sys/arch/arc/dev/dma.c b/sys/arch/arc/dev/dma.c new file mode 100644 index 00000000000..20d068480e5 --- /dev/null +++ b/sys/arch/arc/dev/dma.c @@ -0,0 +1,395 @@ +/* $OpenBSD: dma.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* + * Copyright (c) 1992, 1993 + * 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. 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 + * SUCH DAMAGE. + * + * from: @(#)rz.c 8.1 (Berkeley) 7/29/93 + * $Id: dma.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ + */ + +/* + * PICA system dma driver. Handles resource allocation and + * logical (viritual) address remaping. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/syslog.h> +#include <sys/device.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <machine/pte.h> +#include <machine/pio.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <arc/pica/pica.h> +#include <arc/dev/dma.h> + + +extern vm_map_t phys_map; + +#define dma_pte_to_pa(x) (((x) - first_dma_pte) * PICA_DMA_PAGE_SIZE) + +dma_pte_t *free_dma_pte; /* Pointer to free dma pte list */ +dma_pte_t *first_dma_pte; /* Pointer to first dma pte */ + +/* + * Initialize the dma mapping register area and pool. + */ +void +picaDmaInit() +{ + int map = PICA_TL_BASE; + + MachFlushCache(); /* Make shure no map entries are cached */ + + bzero((char *)map, PICA_TL_SIZE); + free_dma_pte = (dma_pte_t *)map; + first_dma_pte = (dma_pte_t *)map; + free_dma_pte->queue.next = NULL; + free_dma_pte->queue.size = PICA_TL_SIZE / sizeof(dma_pte_t); + + out32(PICA_SYS_TL_BASE, UNCACHED_TO_PHYS(map)); + out32(PICA_SYS_TL_LIMIT, PICA_TL_SIZE); + out32(PICA_SYS_TL_IVALID, 0); +} + +/* + * Allocate an array of 'size' dma pte entrys. + * Return address to first pte. + */ +void +picaDmaTLBAlloc(dma_softc_t *dma) +{ + dma_pte_t *list; + dma_pte_t *found; + int size; + int s; + + found = NULL; + size = dma->pte_size; + do { + list = (dma_pte_t *)&free_dma_pte; + s = splhigh(); + while(list) { + if(list->queue.next->queue.size >= size) { + found = list->queue.next; + break; + } + } +/*XXX Wait for release wakeup */ + } while(found == NULL); + if(found->queue.size == size) { + list->queue.next = found->queue.next; + } + else { + list->queue.next = found + size; + list = found + size; + list->queue.next = found->queue.next; + list->queue.size = found->queue.size - size; + } + splx(s); + dma->pte_base = found; + dma->dma_va = dma_pte_to_pa(found); +} + +/* + * Free an array of dma pte entrys. + */ +void +picaDmaTLBFree(dma_softc_t *dma) +{ + dma_pte_t *list; + dma_pte_t *entry; + int size; + int s; + + s = splhigh(); + entry = dma->pte_base; + size = dma->pte_size; + entry->queue.next = NULL; + entry->queue.size = size; + if(free_dma_pte == NULL || entry < free_dma_pte) { + list = entry; + list->queue.next = free_dma_pte; + free_dma_pte = entry; + } + else { + list = free_dma_pte; + while(list < entry && list->queue.next != NULL) { + if(list + list->queue.size == entry) { + list->queue.size += size; + break; + } + else if(list->queue.next == NULL) { + list->queue.next = entry; + break; + } + else + list = list->queue.next; + } + } + if(list->queue.next != NULL) { + if(list + list->queue.size == list->queue.next) { + list->queue.size += list->queue.next->queue.size; + list->queue.next = list->queue.next->queue.next; + } + } + splx(s); +/*XXX Wakeup waiting */ +} + +/* + * Map up a viritual address space in dma space given by + * the dma control structure and invalidate dma TLB cache. + */ + +picaDmaTLBMap(dma_softc_t *sc) +{ + vm_offset_t pa; + vm_offset_t va; + dma_pte_t *dma_pte; + int nbytes; + + va = sc->next_va - sc->dma_va; + dma_pte = sc->pte_base + (va / PICA_DMA_PAGE_SIZE); + nbytes = dma_page_round(sc->next_size + dma_page_offs(va)); + va = sc->req_va; + while(nbytes > 0) { + if(va < VM_MIN_KERNEL_ADDRESS) { + pa = CACHED_TO_PHYS(va); + } + else { + pa = pmap_extract(vm_map_pmap(phys_map), va); + } + pa &= PICA_DMA_PAGE_NUM; + if(pa == 0) + panic("picaDmaTLBMap: null page frame"); + dma_pte->entry.lo_addr = pa; + dma_pte->entry.hi_addr = 0; + dma_pte++; + va += PICA_DMA_PAGE_SIZE; + nbytes -= PICA_DMA_PAGE_SIZE; + } +} + +/* + * Start local dma channel. + */ +void +picaDmaStart(sc, addr, size, datain) + struct dma_softc *sc; + char *addr; + size_t size; + int datain; +{ + int mode; + pDmaReg regs = sc->dma_reg; + + /* Halt DMA */ + regs->dma_enab = 0; + regs->dma_mode = 0; + + /* Remap request space va into dma space va */ + + sc->req_va = (int)addr; + sc->next_va = sc->dma_va + dma_page_offs(addr); + sc->next_size = size; + + /* Map up the request viritual dma space */ + picaDmaTLBMap(sc); + out32(PICA_SYS_TL_IVALID, 0); /* Flush dma map cache */ + + /* Load new transfer parameters */ + regs->dma_addr = sc->next_va; + regs->dma_count = sc->next_size; + regs->dma_mode = sc->mode & PICA_DMA_MODE; + + sc->sc_active = 1; + if(datain == DMA_FROM_DEV) { + sc->mode &= ~DMA_DIR_WRITE; + regs->dma_enab = PICA_DMA_ENAB_RUN | PICA_DMA_ENAB_READ; + } + else { + sc->mode |= DMA_DIR_WRITE; + regs->dma_enab = PICA_DMA_ENAB_RUN | PICA_DMA_ENAB_WRITE; + } + wbflush(); +} + +/* + * Set up DMA mapper for external dma. + * Used by ISA dma and SONIC + */ +void +picaDmaMap(sc, addr, size, offset) + struct dma_softc *sc; + char *addr; + size_t size; + int offset; +{ + /* Remap request space va into dma space va */ + + sc->req_va = (int)addr; + sc->next_va = sc->dma_va + dma_page_offs(addr) + offset; + sc->next_size = size; + + /* Map up the request viritual dma space */ + picaDmaTLBMap(sc); +} + +/* + * Prepare for new dma by flushing + */ +void +picaDmaFlush(sc, addr, size, datain) + struct dma_softc *sc; + char *addr; + size_t size; + int datain; +{ + out32(PICA_SYS_TL_IVALID, 0); /* Flush dma map cache */ +} + +/* + * Stop/Reset a DMA channel + */ +void +picaDmaReset(dma_softc_t *sc) +{ + pDmaReg regs = sc->dma_reg; + + /* Halt DMA */ + regs->dma_enab = 0; + regs->dma_mode = 0; + sc->sc_active = 0; +} + +/* + * End dma operation, return byte count left. + */ +int +picaDmaEnd(dma_softc_t *sc) +{ + pDmaReg regs = sc->dma_reg; + int res; + + res = regs->dma_count = sc->next_size; + + /* Halt DMA */ + regs->dma_enab = 0; + regs->dma_mode = 0; + sc->sc_active = 0; + + return res; +} + +/* + * Null call rathole! + */ +void +picaDmaNull(dma_softc_t *sc) +{ + pDmaReg regs = sc->dma_reg; + + printf("picaDmaNull called\n"); +} + +/* + * dma_init.. + * Called from asc to set up dma + */ +void +asc_dma_init(dma_softc_t *sc) +{ + sc->reset = picaDmaReset; + sc->enintr = picaDmaNull; + sc->start = picaDmaStart; + sc->map = picaDmaMap; + sc->isintr = (int(*)())picaDmaNull; + sc->intr = (int(*)())picaDmaNull; + sc->end = picaDmaEnd; + + sc->dma_reg = (pDmaReg)PICA_SYS_DMA0_REGS; + sc->pte_size = 32; + sc->mode = PICA_DMA_MODE_160NS | PICA_DMA_MODE_16; + picaDmaTLBAlloc(sc); +} +/* + * dma_init.. + * Called from fdc to set up dma + */ +void +fdc_dma_init(dma_softc_t *sc) +{ + sc->reset = picaDmaReset; + sc->enintr = picaDmaNull; + sc->start = picaDmaStart; + sc->map = picaDmaMap; + sc->isintr = (int(*)())picaDmaNull; + sc->intr = (int(*)())picaDmaNull; + sc->end = picaDmaEnd; + + sc->dma_reg = (pDmaReg)PICA_SYS_DMA1_REGS; + sc->pte_size = 32; + sc->mode = PICA_DMA_MODE_160NS | PICA_DMA_MODE_8; + picaDmaTLBAlloc(sc); +} +/* + * dma_init.. + * Called from sonic to set up dma + */ +void +sn_dma_init(dma_softc_t *sc, int pages) +{ + sc->reset = picaDmaNull; + sc->enintr = picaDmaNull; + sc->start = picaDmaFlush; + sc->map = picaDmaMap; + sc->isintr = (int(*)())picaDmaNull; + sc->intr = (int(*)())picaDmaNull; + sc->end = (int(*)())picaDmaNull; + + sc->dma_reg = (pDmaReg)NULL; + sc->pte_size = pages; + sc->mode = 0; + picaDmaTLBAlloc(sc); +} diff --git a/sys/arch/arc/dev/dma.h b/sys/arch/arc/dev/dma.h new file mode 100644 index 00000000000..949154ca697 --- /dev/null +++ b/sys/arch/arc/dev/dma.h @@ -0,0 +1,151 @@ +/* $OpenBSD: dma.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/*- + * Copyright (c) 1992, 1993 + * 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. 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 + * SUCH DAMAGE. + * + * from: @(#)dma.h 8.1 (Berkeley) 6/10/93 + * $Id: dma.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ + */ + +/* + * The PICA system has four dma channels capable of scatter/gather + * and full memory addressing. The maximum transfer length is 1Mb. + * Dma snopes the L2 cache so no precaution is required. However + * if L1 cache is cached 'write back' the processor is responible + * for flushing/invalidating it. + * + * The dma mapper has up to 4096 page descriptors. + */ + +#define PICA_TL_BASE 0xa0008000 /* Base of tl register area */ +#define PICA_TL_SIZE 0x00008000 /* Size of tl register area */ + +/* + * Hardware dma registers. + */ +typedef volatile struct { + int dma_mode; + int pad1; + int dma_enab; + int pad2; + int dma_count; + int pad3; + vm_offset_t dma_addr; + int pad4; +} DmaReg, *pDmaReg; + +#define PICA_DMA_MODE_40NS 0x00 /* Device dma timing */ +#define PICA_DMA_MODE_80NS 0x01 /* Device dma timing */ +#define PICA_DMA_MODE_120NS 0x02 /* Device dma timing */ +#define PICA_DMA_MODE_160NS 0x03 /* Device dma timing */ +#define PICA_DMA_MODE_200NS 0x04 /* Device dma timing */ +#define PICA_DMA_MODE_240NS 0x05 /* Device dma timing */ +#define PICA_DMA_MODE_280NS 0x06 /* Device dma timing */ +#define PICA_DMA_MODE_320NS 0x07 /* Device dma timing */ +#define PICA_DMA_MODE_8 0x08 /* Device 8 bit */ +#define PICA_DMA_MODE_16 0x10 /* Device 16 bit */ +#define PICA_DMA_MODE_32 0x18 /* Device 32 bit */ +#define PICA_DMA_MODE_INT 0x20 /* Interrupt when done */ +#define PICA_DMA_MODE_BURST 0x40 /* Burst mode (Rev 2 only) */ +#define PICA_DMA_MODE_FAST 0x80 /* Fast dma cycle (Rev 2 only) */ +#define PICA_DMA_MODE 0xff /* Mode register bits */ +#define DMA_DIR_WRITE 0x100 /* Software direction status */ +#define DMA_DIR_READ 0x000 /* Software direction status */ + +#define PICA_DMA_ENAB_RUN 0x01 /* Enable dma */ +#define PICA_DMA_ENAB_READ 0x00 /* Read from device */ +#define PICA_DMA_ENAB_WRITE 0x02 /* Write to device */ +#define PICA_DMA_ENAB_TC_IE 0x100 /* Terminal count int enable */ +#define PICA_DMA_ENAB_ME_IE 0x200 /* Memory error int enable */ +#define PICA_DMA_ENAB_TL_IE 0x400 /* Translation limit int enable */ + +#define PICA_DMA_COUNT_MASK 0x00fffff /* Byte count mask */ +#define PICA_DMA_PAGE_NUM 0xffff000 /* Address page number */ +#define PICA_DMA_PAGE_OFFS 0x0000fff /* Address page offset */ +#define PICA_DMA_PAGE_SIZE 0x0001000 /* Address page size */ + + +/* + * Dma TLB entry + */ + +typedef union dma_pte { + struct { + vm_offset_t lo_addr; /* Low part of translation addr */ + vm_offset_t hi_addr; /* High part of translation addr */ + } entry; + struct bbb { + union dma_pte *next; /* Next free translation entry */ + int size; /* Number of consecutive free entrys */ + } queue; +} dma_pte_t; + +/* + * Structure used to control dma. + */ + +typedef struct dma_softc { + struct device sc_dev; /* use as a device */ + struct esp_softc *sc_esp; + vm_offset_t dma_va; /* Viritual address for transfer */ + int req_va; /* Original request va */ + vm_offset_t next_va; /* Value to program into dma regs */ + int next_size; /* Value to program into dma regs */ + int mode; /* Mode register value and direction */ + dma_pte_t *pte_base; /* Pointer to dma tlb array */ + int pte_size; /* Size of pte allocated pte array */ + pDmaReg dma_reg; /* Pointer to dma registers */ + int sc_active; /* Active flag */ + char **sc_dmaaddr; /* Pointer to dma address in dev */ + int *sc_dmalen; /* Pointer to len counter in dev */ + void (*reset)(struct dma_softc *); /* Reset routine pointer */ + void (*enintr)(struct dma_softc *); /* Int enab routine pointer */ + void (*map)(struct dma_softc *, char *, size_t, int); + /* Map a dma viritual area */ + void (*start)(struct dma_softc *, caddr_t, size_t, int); + /* Start routine pointer */ + int (*isintr)(struct dma_softc *); /* Int check routine pointer */ + int (*intr)(struct dma_softc *); /* Interrupt routine pointer */ + int (*end)(struct dma_softc *); /* Interrupt routine pointer */ +} dma_softc_t; + +#define DMA_TO_DEV 0 +#define DMA_FROM_DEV 1 + +#define dma_page_offs(x) ((int)(x) & PICA_DMA_PAGE_OFFS) +#define dma_page_round(x) (((int)(x) + PICA_DMA_PAGE_OFFS) & PICA_DMA_PAGE_NUM) + +#define DMA_RESET(r) ((r->reset)(r)) +#define DMA_START(a, b, c, d) ((a->start)(a, b, c, d)) +#define DMA_MAP(a, b, c, d) ((a->map)(a, b, c, d)) +#define DMA_INTR(r) ((r->intr)(r)) +#define DMA_DRAIN(r) +#define DMA_END(r) ((r->end)(r)) diff --git a/sys/arch/arc/dev/fd.c b/sys/arch/arc/dev/fd.c new file mode 100644 index 00000000000..aee3df4d52d --- /dev/null +++ b/sys/arch/arc/dev/fd.c @@ -0,0 +1,1139 @@ +/* $OpenBSD: fd.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: fd.c,v 1.78 1995/07/04 07:23:09 mycroft Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995 Charles Hannum. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Don Ahn. + * + * 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 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 + * SUCH DAMAGE. + * + * @(#)fd.c 7.4 (Berkeley) 5/25/91 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/dkstat.h> +#include <sys/disk.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/syslog.h> +#include <sys/queue.h> + +#include <machine/cpu.h> +#include <machine/pio.h> +#include <machine/autoconf.h> + +#include <arc/dev/fdreg.h> +#include <arc/dev/dma.h> + + +#define FDUNIT(dev) (minor(dev) / 8) +#define FDTYPE(dev) (minor(dev) % 8) + +#define b_cylin b_resid + +enum fdc_state { + DEVIDLE = 0, + MOTORWAIT, + DOSEEK, + SEEKWAIT, + SEEKTIMEDOUT, + SEEKCOMPLETE, + DOIO, + IOCOMPLETE, + IOTIMEDOUT, + DORESET, + RESETCOMPLETE, + RESETTIMEDOUT, + DORECAL, + RECALWAIT, + RECALTIMEDOUT, + RECALCOMPLETE, +}; + +/* software state, per controller */ +struct fdc_softc { + struct device sc_dev; /* boilerplate */ + + struct dma_softc __dma; + struct dma_softc *dma; + + int sc_iobase; + + struct fd_softc *sc_fd[4]; /* pointers to children */ + TAILQ_HEAD(drivehead, fd_softc) sc_drives; + enum fdc_state sc_state; + int sc_errors; /* number of retries so far */ + u_char sc_status[7]; /* copy of registers */ +}; + +/* controller driver configuration */ +int fdcprobe __P((struct device *, void *, void *)); +void fdcattach __P((struct device *, struct device *, void *)); + +struct cfattach fdc_ca = { + sizeof(struct fdc_softc), fdcprobe, fdcattach +}; +struct cfdriver fdc_cd = { + NULL, "fdc", DV_DULL, NULL, 0 +}; + +/* + * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how + * we tell them apart. + */ +struct fd_type { + int sectrac; /* sectors per track */ + int heads; /* number of heads */ + int seccyl; /* sectors per cylinder */ + int secsize; /* size code for sectors */ + int datalen; /* data len when secsize = 0 */ + int steprate; /* step rate and head unload time */ + int gap1; /* gap len between sectors */ + int gap2; /* formatting gap */ + int tracks; /* total num of tracks */ + int size; /* size of disk in sectors */ + int step; /* steps per cylinder */ + int rate; /* transfer speed code */ + char *name; +}; + +/* The order of entries in the following table is important -- BEWARE! */ +struct fd_type fd_types[] = { + { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB" }, /* 1.44MB diskette */ + { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB" }, /* 1.2 MB AT-diskettes */ + { 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */ + { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */ + { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */ + { 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x" }, /* 720kB in 1.2MB drive */ + { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */ +}; + +/* software state, per disk (with up to 4 disks per ctlr) */ +struct fd_softc { + struct device sc_dev; + struct disk sc_dk; + + struct fd_type *sc_deftype; /* default type descriptor */ + struct fd_type *sc_type; /* current type descriptor */ + + daddr_t sc_blkno; /* starting block number */ + int sc_bcount; /* byte count left */ + int sc_skip; /* bytes already transferred */ + int sc_nblks; /* number of blocks currently tranferring */ + int sc_nbytes; /* number of bytes currently tranferring */ + + int sc_drive; /* physical unit number */ + int sc_flags; +#define FD_OPEN 0x01 /* it's open */ +#define FD_MOTOR 0x02 /* motor should be on */ +#define FD_MOTOR_WAIT 0x04 /* motor coming up */ + int sc_cylin; /* where we think the head is */ + + void *sc_sdhook; /* saved shutdown hook for drive. */ + + TAILQ_ENTRY(fd_softc) sc_drivechain; + int sc_ops; /* I/O ops since last switch */ + struct buf sc_q; /* head of buf chain */ +}; + +/* floppy driver configuration */ +int fdprobe __P((struct device *, void *, void *)); +void fdattach __P((struct device *, struct device *, void *)); + +struct cfattach fd_ca = { + sizeof(struct fd_softc), fdprobe, fdattach +}; +struct cfdriver fd_cd = { + NULL, "fd", DV_DISK, NULL, 0 +}; + +void fdgetdisklabel __P((struct fd_softc *)); +int fd_get_parms __P((struct fd_softc *)); +void fdstrategy __P((struct buf *)); +void fdstart __P((struct fd_softc *)); + +struct dkdriver fddkdriver = { fdstrategy }; + +struct fd_type *fd_nvtotype __P((char *, int, int)); +void fd_set_motor __P((struct fdc_softc *fdc, int reset)); +void fd_motor_off __P((void *arg)); +void fd_motor_on __P((void *arg)); +int fdcresult __P((struct fdc_softc *fdc)); +int out_fdc __P((int iobase, u_char x)); +void fdcstart __P((struct fdc_softc *fdc)); +void fdcstatus __P((struct device *dv, int n, char *s)); +void fdctimeout __P((void *arg)); +void fdcpseudointr __P((void *arg)); +int fdcintr __P((void *)); +void fdcretry __P((struct fdc_softc *fdc)); +void fdfinish __P((struct fd_softc *fd, struct buf *bp)); + +int +fdcprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + register struct confargs *ca = aux; + int iobase = (long)BUS_CVTADDR(ca); + + if (!BUS_MATCHNAME(ca, "fdc")) + return (0); + + /* reset */ + outb(iobase + fdout, 0); + delay(100); + outb(iobase + fdout, FDO_FRST); + + /* see if it can handle a command */ + if (out_fdc(iobase, NE7CMD_SPECIFY) < 0) + return 0; + out_fdc(iobase, 0xdf); + out_fdc(iobase, 2); + + return 1; +} + +/* + * Arguments passed between fdcattach and fdprobe. + */ +struct fdc_attach_args { + int fa_drive; + struct fd_type *fa_deftype; +}; + +/* + * Print the location of a disk drive (called just before attaching the + * the drive). If `fdc' is not NULL, the drive was found but was not + * in the system config file; print the drive name as well. + * Return QUIET (config_find ignores this if the device was configured) to + * avoid printing `fdN not configured' messages. + */ +int +fdprint(aux, fdc) + void *aux; + char *fdc; +{ + register struct fdc_attach_args *fa = aux; + + if (!fdc) + printf(" drive %d", fa->fa_drive); + return QUIET; +} + +void +fdcattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct fdc_softc *fdc = (void *)self; + struct confargs *ca = aux; + struct fdc_attach_args fa; + int type; + + fdc->sc_iobase = (long)BUS_CVTADDR(ca); + fdc->sc_state = DEVIDLE; + TAILQ_INIT(&fdc->sc_drives); + + fdc->dma = &fdc->__dma; + fdc_dma_init(fdc->dma); + + printf("\n"); + + BUS_INTR_ESTABLISH(ca, fdcintr, fdc); + + /* + * No way yet to determine default disk types. + * we assume 1.44 3.5" type for the moment. + */ + type = 0; + + /* physical limit: two drives per controller. */ + for (fa.fa_drive = 0; fa.fa_drive < 2; fa.fa_drive++) { + if (type >= 0 && fa.fa_drive < 2) + fa.fa_deftype = fd_nvtotype(fdc->sc_dev.dv_xname, + type, fa.fa_drive); + else + fa.fa_deftype = NULL; /* unknown */ + (void)config_found(self, (void *)&fa, fdprint); + } +} + +int +fdprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct fdc_softc *fdc = (void *)parent; + struct cfdata *cf = match; + struct fdc_attach_args *fa = aux; + int drive = fa->fa_drive; + int iobase = fdc->sc_iobase; + int n; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive) + return 0; + + /* select drive and turn on motor */ + outb(iobase + fdout, drive | FDO_FRST | FDO_MOEN(drive)); + /* wait for motor to spin up */ + delay(500000); + out_fdc(iobase, NE7CMD_RECAL); + out_fdc(iobase, drive); + /* wait for recalibrate */ + delay(2000000); + out_fdc(iobase, NE7CMD_SENSEI); + n = fdcresult(fdc); +#ifdef FD_DEBUG + { + int i; + printf("fdprobe: status"); + for (i = 0; i < n; i++) + printf(" %x", fdc->sc_status[i]); + printf("\n"); + } +#endif + if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20) + return 0; + /* turn off motor */ + outb(iobase + fdout, FDO_FRST); + + return 1; +} + +/* + * Controller is working, and drive responded. Attach it. + */ +void +fdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct fdc_softc *fdc = (void *)parent; + struct fd_softc *fd = (void *)self; + struct fdc_attach_args *fa = aux; + struct fd_type *type = fa->fa_deftype; + int drive = fa->fa_drive; + + /* XXX Allow `flags' to override device type? */ + + if (type) + printf(": %s %d cyl, %d head, %d sec\n", type->name, + type->tracks, type->heads, type->sectrac); + else + printf(": density unknown\n"); + + fd->sc_cylin = -1; + fd->sc_drive = drive; + fd->sc_deftype = type; + fdc->sc_fd[drive] = fd; + fd->sc_dk.dk_name = fd->sc_dev.dv_xname; + fd->sc_dk.dk_driver = &fddkdriver; + /* XXX Need to do some more fiddling with sc_dk. */ + dk_establish(&fd->sc_dk, &fd->sc_dev); + + /* Needed to power off if the motor is on when we halt. */ + fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd); +} + +/* + * Translate nvram type into internal data structure. Return NULL for + * none/unknown/unusable. + */ +struct fd_type * +fd_nvtotype(fdc, nvraminfo, drive) + char *fdc; + int nvraminfo, drive; +{ + int type; + + type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0; +#if 0 + switch (type) { + case NVRAM_DISKETTE_NONE: + return NULL; + case NVRAM_DISKETTE_12M: + return &fd_types[1]; + case NVRAM_DISKETTE_TYPE5: + case NVRAM_DISKETTE_TYPE6: + /* XXX We really ought to handle 2.88MB format. */ + case NVRAM_DISKETTE_144M: + return &fd_types[0]; + case NVRAM_DISKETTE_360K: + return &fd_types[3]; + case NVRAM_DISKETTE_720K: + return &fd_types[4]; + default: + printf("%s: drive %d: unknown device type 0x%x\n", + fdc, drive, type); + return NULL; + } +#else + return &fd_types[0]; /* Use only 1.44 for now */ +#endif +} + +inline struct fd_type * +fd_dev_to_type(fd, dev) + struct fd_softc *fd; + dev_t dev; +{ + int type = FDTYPE(dev); + + if (type > (sizeof(fd_types) / sizeof(fd_types[0]))) + return NULL; + return type ? &fd_types[type - 1] : fd->sc_deftype; +} + +void +fdstrategy(bp) + register struct buf *bp; /* IO operation to perform */ +{ + struct fd_softc *fd; + int unit = FDUNIT(bp->b_dev); + int sz; + int s; + + /* Valid unit, controller, and request? */ + if (unit >= fd_cd.cd_ndevs || + (fd = fd_cd.cd_devs[unit]) == 0 || + bp->b_blkno < 0 || + (bp->b_bcount % FDC_BSIZE) != 0) { + bp->b_error = EINVAL; + goto bad; + } + + /* If it's a null transfer, return immediately. */ + if (bp->b_bcount == 0) + goto done; + + sz = howmany(bp->b_bcount, FDC_BSIZE); + + if (bp->b_blkno + sz > fd->sc_type->size) { + sz = fd->sc_type->size - bp->b_blkno; + if (sz == 0) { + /* If exactly at end of disk, return EOF. */ + bp->b_resid = bp->b_bcount; + goto done; + } + if (sz < 0) { + /* If past end of disk, return EINVAL. */ + bp->b_error = EINVAL; + goto bad; + } + /* Otherwise, truncate request. */ + bp->b_bcount = sz << DEV_BSHIFT; + } + + bp->b_cylin = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE) / fd->sc_type->seccyl; + +#ifdef FD_DEBUG + printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n", + bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylin, sz); +#endif + + /* Queue transfer on drive, activate drive and controller if idle. */ + s = splbio(); + disksort(&fd->sc_q, bp); + untimeout(fd_motor_off, fd); /* a good idea */ + if (!fd->sc_q.b_active) + fdstart(fd); +#ifdef DIAGNOSTIC + else { + struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; + if (fdc->sc_state == DEVIDLE) { + printf("fdstrategy: controller inactive\n"); + fdcstart(fdc); + } + } +#endif + splx(s); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + /* Toss transfer; we're done early. */ + biodone(bp); +} + +void +fdstart(fd) + struct fd_softc *fd; +{ + struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; + int active = fdc->sc_drives.tqh_first != 0; + + /* Link into controller queue. */ + fd->sc_q.b_active = 1; + TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); + + /* If controller not already active, start it. */ + if (!active) + fdcstart(fdc); +} + +void +fdfinish(fd, bp) + struct fd_softc *fd; + struct buf *bp; +{ + struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; + + /* + * Move this drive to the end of the queue to give others a `fair' + * chance. We only force a switch if N operations are completed while + * another drive is waiting to be serviced, since there is a long motor + * startup delay whenever we switch. + */ + if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) { + fd->sc_ops = 0; + TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); + if (bp->b_actf) { + TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); + } else + fd->sc_q.b_active = 0; + } + bp->b_resid = fd->sc_bcount; + fd->sc_skip = 0; + fd->sc_q.b_actf = bp->b_actf; + biodone(bp); + /* turn off motor 5s from now */ + timeout(fd_motor_off, fd, 10 * hz); + fdc->sc_state = DEVIDLE; +} + +int +fdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +fdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +void +fd_set_motor(fdc, reset) + struct fdc_softc *fdc; + int reset; +{ + struct fd_softc *fd; + u_char status; + int n; + + if (fd = fdc->sc_drives.tqh_first) + status = fd->sc_drive; + else + status = 0; + if (!reset) + status |= FDO_FRST | FDO_FDMAEN; + for (n = 0; n < 4; n++) + if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR)) + status |= FDO_MOEN(n); + outb(fdc->sc_iobase + fdout, status); +} + +void +fd_motor_off(arg) + void *arg; +{ + struct fd_softc *fd = arg; + int s; + + s = splbio(); + fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); + fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0); + splx(s); +} + +void +fd_motor_on(arg) + void *arg; +{ + struct fd_softc *fd = arg; + struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; + int s; + + s = splbio(); + fd->sc_flags &= ~FD_MOTOR_WAIT; + if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT)) + (void) fdcintr(fdc); + splx(s); +} + +int +fdcresult(fdc) + struct fdc_softc *fdc; +{ + int iobase = fdc->sc_iobase; + u_char i; + int j = 400000, /* Empirical, should do at 150 Mhz to */ + n = 0; + + for (; j; --j) { + i = inb(iobase + fdsts) & (NE7_DIO | NE7_RQM | NE7_CB); + if (i == NE7_RQM) { + return n; + } + if (i == (NE7_DIO | NE7_RQM | NE7_CB)) { + if (n >= sizeof(fdc->sc_status)) { + log(LOG_ERR, "fdcresult: overrun\n"); + return -1; + } + fdc->sc_status[n++] = inb(iobase + fddata); + } + } + log(LOG_ERR, "fdcresult: timeout\n"); + return -1; +} + +int +out_fdc(iobase, x) + int iobase; + u_char x; +{ + int i = 100000; + + while ((inb(iobase + fdsts) & NE7_DIO) && i-- > 0); + if (i <= 0) + return -1; + while ((inb(iobase + fdsts) & NE7_RQM) == 0 && i-- > 0); + if (i <= 0) + return -1; + outb(iobase + fddata, x); + return 0; +} + +int +fdopen(dev, flags) + dev_t dev; + int flags; +{ + int unit; + struct fd_softc *fd; + struct fd_type *type; + + unit = FDUNIT(dev); + if (unit >= fd_cd.cd_ndevs) + return ENXIO; + fd = fd_cd.cd_devs[unit]; + if (fd == 0) + return ENXIO; + type = fd_dev_to_type(fd, dev); + if (type == NULL) + return ENXIO; + + if ((fd->sc_flags & FD_OPEN) != 0 && + fd->sc_type != type) + return EBUSY; + + fd->sc_type = type; + fd->sc_cylin = -1; + fd->sc_flags |= FD_OPEN; + + return 0; +} + +int +fdclose(dev, flags) + dev_t dev; + int flags; +{ + struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; + + fd->sc_flags &= ~FD_OPEN; + return 0; +} + +void +fdcstart(fdc) + struct fdc_softc *fdc; +{ + +#ifdef DIAGNOSTIC + /* only got here if controller's drive queue was inactive; should + be in idle state */ + if (fdc->sc_state != DEVIDLE) { + printf("fdcstart: not idle\n"); + return; + } +#endif + (void) fdcintr(fdc); +} + +void +fdcstatus(dv, n, s) + struct device *dv; + int n; + char *s; +{ + struct fdc_softc *fdc = (void *)dv->dv_parent; + int iobase = fdc->sc_iobase; + + if (n == 0) { + out_fdc(fdc->sc_iobase, NE7CMD_SENSEI); + (void) fdcresult(fdc); + n = 2; + } + + printf("%s: %s", dv->dv_xname, s); + + switch (n) { + case 0: + printf("\n"); + break; + case 2: + printf(" (st0 %b cyl %d)\n", + fdc->sc_status[0], NE7_ST0BITS, + fdc->sc_status[1]); + break; + case 7: + printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", + fdc->sc_status[0], NE7_ST0BITS, + fdc->sc_status[1], NE7_ST1BITS, + fdc->sc_status[2], NE7_ST2BITS, + fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); + break; +#ifdef DIAGNOSTIC + default: + printf("\nfdcstatus: weird size"); + break; +#endif + } +} + +void +fdctimeout(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; + struct fd_softc *fd = fdc->sc_drives.tqh_first; + int s; + + s = splbio(); + fdcstatus(&fd->sc_dev, 0, "timeout"); + + if (fd->sc_q.b_actf) + fdc->sc_state++; + else + fdc->sc_state = DEVIDLE; + + (void) fdcintr(fdc); + splx(s); +} + +void +fdcpseudointr(arg) + void *arg; +{ + int s; + + /* Just ensure it has the right spl. */ + s = splbio(); + (void) fdcintr(arg); + splx(s); +} + +int +fdcintr(arg) + void *arg; +{ + struct fdc_softc *fdc = arg; +#define st0 fdc->sc_status[0] +#define cyl fdc->sc_status[1] + struct fd_softc *fd; + struct buf *bp; + int iobase = fdc->sc_iobase; + int read, head, trac, sec, i, s, nblks; + struct fd_type *type; + +loop: + /* Is there a drive for the controller to do a transfer with? */ + fd = fdc->sc_drives.tqh_first; + if (fd == NULL) { + fdc->sc_state = DEVIDLE; + return 1; + } + + /* Is there a transfer to this drive? If not, deactivate drive. */ + bp = fd->sc_q.b_actf; + if (bp == NULL) { + fd->sc_ops = 0; + TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); + fd->sc_q.b_active = 0; + goto loop; + } + + switch (fdc->sc_state) { + case DEVIDLE: + fdc->sc_errors = 0; + fd->sc_skip = 0; + fd->sc_bcount = bp->b_bcount; + fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE); + untimeout(fd_motor_off, fd); + if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { + fdc->sc_state = MOTORWAIT; + return 1; + } + if ((fd->sc_flags & FD_MOTOR) == 0) { + /* Turn on the motor, being careful about pairing. */ + struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1]; + if (ofd && ofd->sc_flags & FD_MOTOR) { + untimeout(fd_motor_off, ofd); + ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); + } + fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; + fd_set_motor(fdc, 0); + fdc->sc_state = MOTORWAIT; + /* Allow .5s for motor to stabilize. */ + timeout(fd_motor_on, fd, hz / 2); + return 1; + } + /* Make sure the right drive is selected. */ + fd_set_motor(fdc, 0); + + /* fall through */ + case DOSEEK: + doseek: + if (fd->sc_cylin == bp->b_cylin) + goto doio; + + out_fdc(iobase, NE7CMD_SPECIFY);/* specify command */ + out_fdc(iobase, fd->sc_type->steprate); + out_fdc(iobase, 6); /* XXX head load time == 6ms */ + + out_fdc(iobase, NE7CMD_SEEK); /* seek function */ + out_fdc(iobase, fd->sc_drive); /* drive number */ + out_fdc(iobase, bp->b_cylin * fd->sc_type->step); + + fd->sc_cylin = -1; + fdc->sc_state = SEEKWAIT; + timeout(fdctimeout, fdc, 4 * hz); + return 1; + + case DOIO: + doio: + type = fd->sc_type; + sec = fd->sc_blkno % type->seccyl; + nblks = type->seccyl - sec; + nblks = min(nblks, fd->sc_bcount / FDC_BSIZE); + nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE); + fd->sc_nblks = nblks; + fd->sc_nbytes = nblks * FDC_BSIZE; + head = sec / type->sectrac; + sec -= head * type->sectrac; +#ifdef DIAGNOSTIC + {int block; + block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; + if (block != fd->sc_blkno) { + printf("fdcintr: block %d != blkno %d\n", block, fd->sc_blkno); +#ifdef DDB + Debugger(); +#endif + }} +#endif + MachFlushDCache(bp->b_data + fd->sc_skip, fd->sc_nbytes); + read = bp->b_flags & B_READ ? DMA_FROM_DEV : DMA_TO_DEV; + DMA_START(fdc->dma, bp->b_data + fd->sc_skip, fd->sc_nbytes, read); + outb(iobase + fdctl, type->rate); +#ifdef FD_DEBUG + printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n", + read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head, + sec, nblks); +#endif + if (read) + out_fdc(iobase, NE7CMD_READ); /* READ */ + else + out_fdc(iobase, NE7CMD_WRITE); /* WRITE */ + out_fdc(iobase, (head << 2) | fd->sc_drive); + out_fdc(iobase, fd->sc_cylin); /* track */ + out_fdc(iobase, head); + out_fdc(iobase, sec + 1); /* sector +1 */ + out_fdc(iobase, type->secsize); /* sector size */ + out_fdc(iobase, type->sectrac); /* sectors/track */ + out_fdc(iobase, type->gap1); /* gap1 size */ + out_fdc(iobase, type->datalen); /* data length */ + fdc->sc_state = IOCOMPLETE; + /* allow 2 seconds for operation */ + timeout(fdctimeout, fdc, 2 * hz); + return 1; /* will return later */ + + case SEEKWAIT: + untimeout(fdctimeout, fdc); + fdc->sc_state = SEEKCOMPLETE; + /* allow 1/50 second for heads to settle */ + timeout(fdcpseudointr, fdc, hz / 50); + return 1; + + case SEEKCOMPLETE: + /* Make sure seek really happened. */ + out_fdc(iobase, NE7CMD_SENSEI); + if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || + cyl != bp->b_cylin * fd->sc_type->step) { +#ifdef FD_DEBUG + fdcstatus(&fd->sc_dev, 2, "seek failed"); +#endif + fdcretry(fdc); + goto loop; + } + fd->sc_cylin = bp->b_cylin; + goto doio; + + case IOTIMEDOUT: + DMA_RESET(fdc->dma); + + case SEEKTIMEDOUT: + case RECALTIMEDOUT: + case RESETTIMEDOUT: + fdcretry(fdc); + goto loop; + + case IOCOMPLETE: /* IO DONE, post-analyze */ + untimeout(fdctimeout, fdc); + if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { + DMA_RESET(fdc->dma); +#ifdef FD_DEBUG + fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ? + "read failed" : "write failed"); + printf("blkno %d nblks %d\n", + fd->sc_blkno, fd->sc_nblks); +#endif + fdcretry(fdc); + goto loop; + } + DMA_END(fdc->dma); + read = bp->b_flags & B_READ; + if (fdc->sc_errors) { + diskerr(bp, "fd", "soft error", LOG_PRINTF, + fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL); + printf("\n"); + fdc->sc_errors = 0; + } + fd->sc_blkno += fd->sc_nblks; + fd->sc_skip += fd->sc_nbytes; + fd->sc_bcount -= fd->sc_nbytes; + if (fd->sc_bcount > 0) { + bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl; + goto doseek; + } + fdfinish(fd, bp); + goto loop; + + case DORESET: + /* try a reset, keep motor on */ + fd_set_motor(fdc, 1); + delay(100); + fd_set_motor(fdc, 0); + fdc->sc_state = RESETCOMPLETE; + timeout(fdctimeout, fdc, hz / 2); + return 1; /* will return later */ + + case RESETCOMPLETE: + untimeout(fdctimeout, fdc); + /* clear the controller output buffer */ + for (i = 0; i < 4; i++) { + out_fdc(iobase, NE7CMD_SENSEI); + (void) fdcresult(fdc); + } + + /* fall through */ + case DORECAL: + out_fdc(iobase, NE7CMD_RECAL); /* recalibrate function */ + out_fdc(iobase, fd->sc_drive); + fdc->sc_state = RECALWAIT; + timeout(fdctimeout, fdc, 5 * hz); + return 1; /* will return later */ + + case RECALWAIT: + untimeout(fdctimeout, fdc); + fdc->sc_state = RECALCOMPLETE; + /* allow 1/30 second for heads to settle */ + timeout(fdcpseudointr, fdc, hz / 30); + return 1; /* will return later */ + + case RECALCOMPLETE: + out_fdc(iobase, NE7CMD_SENSEI); + if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { +#ifdef FD_DEBUG + fdcstatus(&fd->sc_dev, 2, "recalibrate failed"); +#endif + fdcretry(fdc); + goto loop; + } + fd->sc_cylin = 0; + goto doseek; + + case MOTORWAIT: + if (fd->sc_flags & FD_MOTOR_WAIT) + return 1; /* time's not up yet */ + goto doseek; + + default: + fdcstatus(&fd->sc_dev, 0, "stray interrupt"); + return 1; + } +#ifdef DIAGNOSTIC + panic("fdcintr: impossible"); +#endif +#undef st0 +#undef cyl +} + +void +fdcretry(fdc) + struct fdc_softc *fdc; +{ + struct fd_softc *fd; + struct buf *bp; + + fd = fdc->sc_drives.tqh_first; + bp = fd->sc_q.b_actf; + + switch (fdc->sc_errors) { + case 0: + /* try again */ + fdc->sc_state = SEEKCOMPLETE; + break; + + case 1: case 2: case 3: + /* didn't work; try recalibrating */ + fdc->sc_state = DORECAL; + break; + + case 4: + /* still no go; reset the bastard */ + fdc->sc_state = DORESET; + break; + + default: + diskerr(bp, "fd", "hard error", LOG_PRINTF, + fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL); + printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", + fdc->sc_status[0], NE7_ST0BITS, + fdc->sc_status[1], NE7_ST1BITS, + fdc->sc_status[2], NE7_ST2BITS, + fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); + + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + fdfinish(fd, bp); + } + fdc->sc_errors++; +} + +int +fdsize(dev) + dev_t dev; +{ + + /* Swapping to floppies would not make sense. */ + return -1; +} + +int +fddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} + +int +fdioctl(dev, cmd, addr, flag) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; +{ + struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; + struct disklabel buffer; + int error; + + switch (cmd) { + case DIOCGDINFO: + bzero(&buffer, sizeof(buffer)); + + buffer.d_secpercyl = fd->sc_type->seccyl; + buffer.d_type = DTYPE_FLOPPY; + buffer.d_secsize = FDC_BSIZE; + + if (readdisklabel(dev, fdstrategy, &buffer, NULL) != NULL) + return EINVAL; + + *(struct disklabel *)addr = buffer; + return 0; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + /* XXX do something */ + return 0; + + case DIOCWDINFO: + if ((flag & FWRITE) == 0) + return EBADF; + + error = setdisklabel(&buffer, (struct disklabel *)addr, 0, NULL); + if (error) + return error; + + error = writedisklabel(dev, fdstrategy, &buffer, NULL); + return error; + + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("fdioctl: impossible"); +#endif +} diff --git a/sys/arch/arc/dev/fdreg.h b/sys/arch/arc/dev/fdreg.h new file mode 100644 index 00000000000..633ddf816de --- /dev/null +++ b/sys/arch/arc/dev/fdreg.h @@ -0,0 +1,67 @@ +/* $OpenBSD: fdreg.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: fdreg.h,v 1.8 1995/06/28 04:30:57 cgd Exp $ */ + +/*- + * 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. 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 + * SUCH DAMAGE. + * + * @(#)fdreg.h 7.1 (Berkeley) 5/9/91 + */ + +/* + * AT floppy controller registers and bitfields + */ + +/* uses NEC765 controller */ +#include <dev/ic/nec765reg.h> + +/* registers */ +#define fdout 2 /* Digital Output Register (W) */ +#define FDO_FDSEL 0x03 /* floppy device select */ +#define FDO_FRST 0x04 /* floppy controller reset */ +#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ +#define FDO_MOEN(n) ((1 << n) * 0x10) /* motor enable */ + +#define fdsts 4 /* NEC 765 Main Status Register (R) */ +#define fddata 5 /* NEC 765 Data Register (R/W) */ + +#define fdctl 7 /* Control Register (W) */ +#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */ +#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */ +#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */ +#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */ + +#define fdin 7 /* Digital Input Register (R) */ +#define FDI_DCHG 0x80 /* diskette has been changed */ + +#define FDC_BSIZE 512 +#define FDC_NPORT 8 +#define FDC_MAXIOSIZE NBPG /* XXX should be MAXBSIZE */ diff --git a/sys/arch/arc/dev/if_sn.c b/sys/arch/arc/dev/if_sn.c new file mode 100644 index 00000000000..dfe411a6551 --- /dev/null +++ b/sys/arch/arc/dev/if_sn.c @@ -0,0 +1,1300 @@ +/* $OpenBSD: if_sn.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* + * National Semiconductor SONIC Driver + * Copyright (c) 1991 Algorithmics Ltd (http://www.algor.co.uk) + * You may use, copy, and modify this program so long as you retain the + * copyright line. + * + * This driver has been substantially modified since Algorithmics donated + * it. + */ + +#include "sn.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/buf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <machine/autoconf.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/route.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <vm/vm.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <arc/dev/dma.h> + +#define SONICDW 32 +typedef unsigned char uchar; + +#include <arc/dev/if_sn.h> +#define SWR(a, x) (a) = (x) +#define SRD(a) ((a) & 0xffff) + +#include <machine/cpu.h> + +/* + * Statistics collected over time + */ +struct sn_stats { + int ls_opacks; /* packets transmitted */ + int ls_ipacks; /* packets received */ + int ls_tdr; /* contents of tdr after collision */ + int ls_tdef; /* packets where had to wait */ + int ls_tone; /* packets with one retry */ + int ls_tmore; /* packets with more than one retry */ + int ls_tbuff; /* transmit buff errors */ + int ls_tuflo; /* " uflo " */ + int ls_tlcol; + int ls_tlcar; + int ls_trtry; + int ls_rbuff; /* receive buff errors */ + int ls_rfram; /* framing */ + int ls_roflo; /* overflow */ + int ls_rcrc; + int ls_rrng; /* rx ring sequence error */ + int ls_babl; /* chip babl error */ + int ls_cerr; /* collision error */ + int ls_miss; /* missed packet */ + int ls_merr; /* memory error */ + int ls_copies; /* copies due to out of range mbufs */ + int ls_maxmbufs; /* max mbufs on transmit */ + int ls_maxslots; /* max ring slots on transmit */ +}; + +struct sn_softc { + struct device sc_dev; + struct arpcom sc_ac; +#define sc_if sc_ac.ac_if /* network visible interface */ +#define sc_enaddr sc_ac.ac_enaddr /* hardware ethernet address */ + + struct sonic_reg *sc_csr; /* hardware pointer */ + dma_softc_t __dma; /* stupid macro ... */ + dma_softc_t *dma; /* dma mapper control */ + int sc_rxmark; /* position in rx ring for reading buffs */ + + int sc_rramark; /* index into rra of wp */ + + int sc_txhead; /* index of first TDA passed to chip */ + int sc_missed; /* missed packet counter */ + struct RXpkt *sc_lrxp; /* last RDA available to chip */ + struct sn_stats sc_sum; + short sc_iflags; +} sn_softc; + +int snmatch __P((struct device *, void *, void *)); +void snattach __P((struct device *, struct device *, void *)); + +struct cfattach sn_ca = { + sizeof(struct sn_softc), snmatch, snattach +}; +struct cfdriver sn_cd = { + NULL, "sn", DV_IFNET, NULL, 0 +}; + +#include <assert.h> +void +__assert(file, line, failedexpr) + const char *file, *failedexpr; + int line; +{ + (void)printf( + "assertion \"%s\" failed: file \"%s\", line %d\n", + failedexpr, file, line); +} + +void +m_check(m) + struct mbuf *m; +{ + if (m->m_flags & M_EXT) { + assert(m->m_len >= 0); + assert(m->m_len <= m->m_ext.ext_size); + assert(m->m_data >= &m->m_ext.ext_buf[0]); + assert(m->m_data <= &m->m_ext.ext_buf[m->m_ext.ext_size]); + assert(m->m_data + m->m_len <= &m->m_ext.ext_buf[m->m_ext.ext_size]); + } else if (m->m_flags & M_PKTHDR) { + assert(m->m_len >= 0); + assert(m->m_len <= MHLEN); + assert(m->m_data >= m->m_pktdat); + assert(m->m_data <= &m->m_pktdat[MHLEN]); + assert(m->m_data + m->m_len <= &m->m_pktdat[MHLEN]); + } else { + assert(m->m_len >= 0); + assert(m->m_len <= MLEN); + assert(m->m_data >= m->m_dat); + assert(m->m_data <= &m->m_dat[MLEN]); + assert(m->m_data + m->m_len <= &m->m_dat[MLEN]); + } +} + +void +m_checkm(m) + struct mbuf *m; +{ + while (m) { + m_check(m); + m = m->m_next; + } +} + +int ethdebug = 0; + +int snintr __P((struct sn_softc *)); +int snioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +void snstart __P((struct ifnet *ifp)); +void snwatchdog __P((struct ifnet *ifp)); +void snreset __P((struct sn_softc *sc)); + +/* + * SONIC buffers need to be aligned 16 or 32 bit aligned. + * These macros calculate and verify alignment. + */ +#if SONICDW == 32 +#define SONICALIGN 4 +#else +#define SONICALIGN 2 +#endif +#define SOALIGN(array) (((int)array+SONICALIGN-1) & ~(SONICALIGN-1)) +#define SOALIGNED(p) (!(((uint)p)&(SONICALIGN-1))) + +#define UPPER(x) ((unsigned)(x) >> 16) +#define LOWER(x) ((unsigned)(x) & 0xffff) + +#define NRRA 32 /* # receive resource descriptors */ +#define RRAMASK 0x1f /* why it must be poer of two */ + +#define NRBA 16 /* # receive buffers < NRRA */ +#define NRDA NRBA /* # receive descriptors */ +#define NTDA 4 /* # transmit descriptors */ + +#define CDASIZE sizeof(struct CDA) +#define RRASIZE (NRRA*sizeof(struct RXrsrc)) +#define RDASIZE (NRDA*sizeof(struct RXpkt)) +#define TDASIZE (NTDA*sizeof(struct TXpkt)) + +#define FCSSIZE 4 /* size of FCS append te received packets */ + +/* + * maximum recieve packet size plus 2 byte pad to make each + * one aligned. 4 byte slop (required for eobc) + */ +#define RBASIZE (sizeof(struct ether_header) + ETHERMTU + FCSSIZE + 2 + 4) + +/* + * space requiered for descriptors + */ +#define DESC_SIZE (RRASIZE + CDASIZE + RDASIZE + TDASIZE + SONICALIGN - 1) + +/* + * This should really be 'allocated' but for now we + * 'hardwire' it. + */ +#define SONICBUF 0xa0010000 + +/* + * Nicely aligned pointers into the sonicbuffers + * p_ points at physical (K1_SEG) addresses. + * v_ is dma viritual address used by sonic. + */ +struct RXrsrc *p_rra; /* receiver resource descriptors */ +struct RXrsrc *v_rra; +struct RXpkt *p_rda; /* receiver desriptors */ +struct RXpkt *v_rda; +struct TXpkt *p_tda; /* transmitter descriptors */ +struct TXpkt *v_tda; +struct CDA *p_cda; /* CAM descriptors */ +struct CDA *v_cda; +char *p_rba; /* receive buffer area base */ +char *v_rba; + +/* Meta transmit descriptors */ +struct mtd { + struct mtd *mtd_link; + struct TXpkt *mtd_txp; + struct mbuf *mtd_mbuf; +} mtda[NTDA]; + +struct mtd *mtdfree; /* list of free meta transmit descriptors */ +struct mtd *mtdhead; /* head of descriptors assigned to chip */ +struct mtd *mtdtail; /* tail of descriptors assigned to chip */ +struct mtd *mtdnext; /* next descriptor to give to chip */ + +void mtd_free __P((struct mtd *)); +struct mtd *mtd_alloc __P((void)); + +int sngetaddr __P((struct sn_softc *sc)); +int sninit __P((struct sn_softc *sc)); +int snstop __P((struct sn_softc *sc)); +int sonicput __P((struct sn_softc *sc, struct mbuf *m0)); + +void camdump __P((struct sn_softc *sc)); + +int +snmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct confargs *ca = aux; + + /* XXX CHECK BUS */ + /* make sure that we're looking for this type of device. */ + if (!BUS_MATCHNAME(ca, "sonic")) + return (0); + + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +void +snattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sn_softc *sc = (void *)self; + struct confargs *ca = aux; + struct ifnet *ifp = &sc->sc_if; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + int p, pp; + + sc->sc_csr = (struct sonic_reg *)BUS_CVTADDR(ca); + + sc->dma = &sc->__dma; + sn_dma_init(sc->dma, FRAGMAX * NTDA + + (NRBA * RBASIZE / PICA_DMA_PAGE_SIZE) + 1 + + (DESC_SIZE * 2 / PICA_DMA_PAGE_SIZE) + 1); + +/* + * because the sonic is basicly 16bit device it 'concatenates' + * a higher buffer address to a 16 bit offset this will cause wrap + * around problems near the end of 64k !! + */ + p = SONICBUF; + pp = SONICBUF - (FRAGMAX * NTDA * PICA_DMA_PAGE_SIZE); + + if ((p ^ (p + TDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_tda = (struct TXpkt *) p; + v_tda = (struct TXpkt *)(p - pp + sc->dma->dma_va); + p += TDASIZE; + + if ((p ^ (p + RRASIZE + CDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_rra = (struct RXrsrc *) p; + v_rra = (struct RXrsrc *)(p - pp + sc->dma->dma_va); + p += RRASIZE; + + if ((p ^ (p + RDASIZE)) & 0x10000) + p = (p + 0x10000) & ~0xffff; + p_rda = (struct RXpkt *) p; + v_rda = (struct RXpkt *)(p - pp + sc->dma->dma_va); + p += RDASIZE; + + p_cda = (struct CDA *) p; + v_cda = (struct CDA *)(p - pp + sc->dma->dma_va); + p += CDASIZE; + + p += PICA_DMA_PAGE_SIZE - (p & (PICA_DMA_PAGE_SIZE -1)); + p_rba = (char *)p; + v_rba = (char *)(p - pp + sc->dma->dma_va); + p += NRBA * RBASIZE; + + DMA_MAP(sc->dma, (caddr_t)SONICBUF, p - SONICBUF, SONICBUF - pp); + +#if 0 + camdump(sc); +#endif + sngetaddr(sc); + printf(" address %s\n", ether_sprintf(sc->sc_enaddr)); + +#if 0 +printf("\nsonic buffers: rra=0x%x cda=0x%x rda=0x%x tda=0x%x rba=0x%x\n", + p_rra, p_cda, p_rda, p_tda, p_rba); +printf("sonic buffers: rra=0x%x cda=0x%x rda=0x%x tda=0x%x rba=0x%x\n", + v_rra, v_cda, v_rda, v_tda, v_rba); +printf("mapped to offset 0x%x size 0x%x\n", SONICBUF - pp, p - SONICBUF); +#endif + + BUS_INTR_ESTABLISH(ca, (intr_handler_t)snintr, (void *)sc); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_ioctl = snioctl; + ifp->if_start = snstart; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_watchdog = snwatchdog; +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + if_attach(ifp); + ether_ifattach(ifp); +} + +int +snioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ifaddr *ifa; + struct sn_softc *sc = ifp->if_softc; + int s = splnet(), err = 0; + int temp; + int error; + + if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) { + splx(s); + return error; + } + + switch (cmd) { + + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + (void)sninit(ifp->if_softc); + arp_ifinit(&sc->sc_ac, ifa); + break; +#endif + default: + (void)sninit(ifp->if_softc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING) { + snstop(ifp->if_softc); + ifp->if_flags &= ~IFF_RUNNING; + } else if (ifp->if_flags & IFF_UP && + (ifp->if_flags & IFF_RUNNING) == 0) + (void)sninit(ifp->if_softc); + /* + * If the state of the promiscuous bit changes, the interface + * must be reset to effect the change. + */ + if (((ifp->if_flags ^ sc->sc_iflags) & IFF_PROMISC) && + (ifp->if_flags & IFF_RUNNING)) { + sc->sc_iflags = ifp->if_flags; + printf("change in flags\n"); + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + snstart(ifp); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if(cmd == SIOCADDMULTI) + err = ether_addmulti((struct ifreq *)data, &sc->sc_ac); + else + err = ether_delmulti((struct ifreq *)data, &sc->sc_ac); + + if (err == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. But remember UP flag! + */ + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + err = 0; + } + break; + default: + err = EINVAL; + } + splx(s); + return (err); +} + +/* + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + */ +void +snstart(ifp) + struct ifnet *ifp; +{ + struct sn_softc *sc = ifp->if_softc; + struct mbuf *m; + int len; + + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) + return; + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) + return; + + /* + * If there is nothing in the o/p queue, and there is room in + * the Tx ring, then send the packet directly. Otherwise append + * it to the o/p queue. + */ + if (!sonicput(sc, m)) { /* not enough space */ + IF_PREPEND(&sc->sc_if.if_snd, m); + } +#if NBPFILTER > 0 + /* + * If bpf is listening on this interface, let it + * see the packet before we commit it to the wire. + */ + if (sc->sc_if.if_bpf) + bpf_mtap(sc->sc_if.if_bpf, m); +#endif + + sc->sc_if.if_opackets++; /* # of pkts */ + sc->sc_sum.ls_opacks++; /* # of pkts */ +} + +/* + * This is called from sonicioctl() when /etc/ifconfig is run to set + * the address or switch the i/f on. + */ +void caminitialise __P((void)); +void camentry __P((int, u_char *ea)); +void camprogram __P((struct sn_softc *)); +void initialise_tda __P((struct sn_softc *)); +void initialise_rda __P((struct sn_softc *)); +void initialise_rra __P((struct sn_softc *)); + +/* + * reset and restart the SONIC. Called in case of fatal + * hardware/software errors. + */ +void +snreset(sc) + struct sn_softc *sc; +{ + snstop(sc); + sninit(sc); +} + +int +sninit(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int s, error; + + if (sc->sc_if.if_flags & IFF_RUNNING) + /* already running */ + return (0); + + s = splnet(); + + csr->s_cr = CR_RST; /* s_dcr only accessable reset mode! */ + + /* config it */ + csr->s_dcr = DCR_LBR | DCR_SYNC | DCR_WAIT0 | DCR_DW32 | DCR_DMABLOCK | + DCR_RFT16 | DCR_TFT16; + csr->s_rcr = RCR_BRD | RCR_LBNONE; + csr->s_imr = IMR_PRXEN | IMR_PTXEN | IMR_TXEREN | IMR_HBLEN | IMR_LCDEN; + + /* clear pending interrupts */ + csr->s_isr = 0x7fff; + + /* clear tally counters */ + csr->s_crct = -1; + csr->s_faet = -1; + csr->s_mpt = -1; + + initialise_tda(sc); + initialise_rda(sc); + initialise_rra(sc); + + /* enable the chip */ + csr->s_cr = 0; + wbflush(); + + /* program the CAM with our address */ + caminitialise(); + camentry(0, sc->sc_enaddr); + camprogram(sc); + + /* get it to read resource descriptors */ + csr->s_cr = CR_RRRA; + wbflush(); + while (csr->s_cr & CR_RRRA) + continue; + + /* enable rx */ + csr->s_cr = CR_RXEN; + wbflush(); + + /* flag interface as "running" */ + sc->sc_if.if_flags |= IFF_RUNNING; + + splx(s); + return (0); + +bad: + snstop(sc); + return (error); +} + +/* + * close down an interface and free its buffers + * Called on final close of device, or if sninit() fails + * part way through. + */ +int +snstop(sc) + struct sn_softc *sc; +{ + struct mtd *mtd; + int s = splnet(); + + /* stick chip in reset */ + sc->sc_csr->s_cr = CR_RST; + wbflush(); + + /* free all receive buffers (currently static so nothing to do) */ + + /* free all pending transmit mbufs */ + while (mtd = mtdhead) { + mtdhead = mtdhead->mtd_link; + if (mtd->mtd_mbuf) + m_freem(mtd->mtd_mbuf); + mtd->mtd_mbuf = 0; + mtd_free(mtd); + } + mtdnext = mtd_alloc(); + + sc->sc_if.if_timer = 0; + sc->sc_if.if_flags &= ~(IFF_RUNNING | IFF_UP); + + splx(s); + return (0); +} + +/* + * Called if any Tx packets remain unsent after 5 seconds, + * In all cases we just reset the chip, and any retransmission + * will be handled by higher level protocol timeouts. + */ +void +snwatchdog(ifp) + struct ifnet *ifp; +{ + struct sn_softc *sc = ifp->if_softc; + int temp; + + if (mtdhead && mtdhead->mtd_mbuf) { + /* something still pending for transmit */ + if (mtdhead->mtd_txp->status == 0) + log(LOG_ERR, "%s%d: Tx - timeout\n", + sc->sc_if.if_xname, sc->sc_if.if_softc); + else + log(LOG_ERR, "%s%d: Tx - lost interrupt\n", + sc->sc_if.if_xname, sc->sc_if.if_softc); + temp = sc->sc_if.if_flags & IFF_UP; + snreset(sc); + sc->sc_if.if_flags |= temp; + } +} +/* + * stuff packet into sonic (at splnet) +*/ +int +sonicput(sc, m0) + struct sn_softc *sc; + struct mbuf *m0; +{ + struct sonic_reg *csr = sc->sc_csr; + struct TXpkt *txp; + struct mtd *mtdnew; + struct mbuf *m; + int len = 0, fr = 0; + int i; + int fragoffset; /* Offset in viritual dma space for fragment */ + + /* grab the replacement mtd */ + if ((mtdnew = mtd_alloc()) == 0) + return (0); + + /* this packet goes to mdtnext fill in the TDA */ + mtdnext->mtd_mbuf = m0; + txp = mtdnext->mtd_txp; + SWR(txp->config, 0); + fragoffset = (txp - p_tda) * FRAGMAX * PICA_DMA_PAGE_SIZE; + + /* + * Now fill in the fragments. Each fragment maps to it's + * own dma page. Fragments crossing a dma page boundary + * are split up in two fragments. This is somewhat stupid + * because the dma mapper can do the work, but it helps + * keeping the fragments in order. (read lazy programmer). + */ + for (m = m0; m; m = m->m_next) { + unsigned va = (unsigned) mtod(m, caddr_t); + int resid = m->m_len; + + if(resid != 0) { + MachHitFlushDCache(va, resid); + DMA_MAP(sc->dma, (caddr_t)va, resid, fragoffset); + } + len += resid; + + while (resid) { + unsigned pa; + unsigned n; + + pa = sc->dma->dma_va + (va & PGOFSET) + fragoffset; + n = resid; + if (n > NBPG - (va & PGOFSET)) { + n = NBPG - (va & PGOFSET); + } + if (fr < FRAGMAX) { + SWR(txp->u[fr].frag_ptrlo, LOWER(pa)); + SWR(txp->u[fr].frag_ptrhi, UPPER(pa)); + SWR(txp->u[fr].frag_size, n); + } + fr++; + va += n; + resid -= n; + fragoffset += PICA_DMA_PAGE_SIZE; + } + } + /* + * pad out last fragment for minimum size + */ + if (len < ETHERMIN + sizeof(struct ether_header) && fr < FRAGMAX) { + int pad = ETHERMIN + sizeof(struct ether_header) - len; + static char zeros[64]; + unsigned pa; + + DMA_MAP(sc->dma, (caddr_t)zeros, pad, fragoffset); + pa = sc->dma->dma_va + ((unsigned)zeros & PGOFSET) + fragoffset; + SWR(txp->u[fr].frag_ptrlo, LOWER(pa)); + SWR(txp->u[fr].frag_ptrhi, UPPER(pa)); + SWR(txp->u[fr].frag_size, pad); + fr++; + len = ETHERMIN + sizeof(struct ether_header); + } + + DMA_START(sc->dma, (caddr_t)0, 0, 0); /* Flush dma tlb */ + + if (fr > FRAGMAX) { + mtd_free(mtdnew); + m_freem(m0); + log(LOG_ERR, "%s%d: tx too many fragments %d\n", + sc->sc_if.if_xname, sc->sc_if.if_softc, fr); + sc->sc_if.if_oerrors++; + return (len); + } + + SWR(txp->frag_count, fr); + SWR(txp->pkt_size, len); + + /* link onto the next mtd that will be used */ + SWR(txp->u[fr].tlink, LOWER(v_tda + (mtdnew->mtd_txp - p_tda)) | EOL); + + if (mtdhead == 0) { + /* no current transmit list start with this one */ + mtdtail = mtdhead = mtdnext; + csr->s_ctda = LOWER(v_tda + (txp - p_tda)); + } else { + /* + * have a transmit list append it to end note + * mtdnext is already physicaly linked to mtdtail in + * mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink + */ + SWR(mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink, + SRD(mtdtail->mtd_txp->u[mtdtail->mtd_txp->frag_count].tlink) & ~EOL); + mtdtail = mtdnext; + } + mtdnext->mtd_link = mtdnew; + mtdnext = mtdnew; + + /* make sure chip is running */ + wbflush(); + csr->s_cr = CR_TXP; + wbflush(); + sc->sc_if.if_timer = 5; /* 5 seconds to watch for failing to transmit */ + return (len); +} + +/* + * Read out the ethernet address from the cam. It is stored + * there by the boot when doing a loopback test. Thus we don't + * have to fetch it from nv ram. + */ +int +sngetaddr(sc) + struct sn_softc *sc; +{ + unsigned i, x, y; + char *cp, *ea; + +#if 0 + sc->sc_csr->s_cr = CR_RST; + wbflush(); + sc->sc_csr->s_cep = 0; + i = sc->sc_csr->s_cap2; + wbflush(); + sc->sc_enaddr[5] = i >> 8; + sc->sc_enaddr[4] = i; + i = sc->sc_csr->s_cap1; + wbflush(); + sc->sc_enaddr[3] = i >> 8; + sc->sc_enaddr[2] = i; + i = sc->sc_csr->s_cap0; + wbflush(); + sc->sc_enaddr[1] = i >> 8; + sc->sc_enaddr[0] = i; + + sc->sc_csr->s_cr = 0; + wbflush(); +#else + sc->sc_enaddr[0] = 0x08; + sc->sc_enaddr[1] = 0x00; + sc->sc_enaddr[2] = 0x20; + sc->sc_enaddr[3] = 0xa0; + sc->sc_enaddr[4] = 0x66; + sc->sc_enaddr[5] = 0x54; +#endif + return (0); +} + +void sonictxint __P((struct sn_softc *)); +void sonicrxint __P((struct sn_softc *)); + +int sonic_read __P((struct sn_softc *, struct RXpkt *)); +struct mbuf *sonic_get __P((struct sn_softc *, struct ether_header *, int)); + +void +mtd_free(mtd) + struct mtd *mtd; +{ + mtd->mtd_link = mtdfree; + mtdfree = mtd; +} + +struct mtd * +mtd_alloc() +{ + struct mtd *mtd = mtdfree; + + if (mtd) { + mtdfree = mtd->mtd_link; + mtd->mtd_link = 0; + } + return (mtd); +} + +/* + * CAM support + */ +void +caminitialise() +{ + int i; + + for (i = 0; i < MAXCAM; i++) + SWR(p_cda->desc[i].cam_ep, i); + SWR(p_cda->enable, 0); +} + +void +camentry(entry, ea) + int entry; + u_char *ea; +{ + SWR(p_cda->desc[entry].cam_ep, entry); + SWR(p_cda->desc[entry].cam_ap2, (ea[5] << 8) | ea[4]); + SWR(p_cda->desc[entry].cam_ap1, (ea[3] << 8) | ea[2]); + SWR(p_cda->desc[entry].cam_ap0, (ea[1] << 8) | ea[0]); + SWR(p_cda->enable, SRD(p_cda->enable) | (1 << entry)); +} + +void +camprogram(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int timeout; + int i; + + csr = sc->sc_csr; + csr->s_cdp = LOWER(v_cda); + csr->s_cdc = MAXCAM; + csr->s_cr = CR_LCAM; + wbflush(); + + timeout = 10000; + while (csr->s_cr & CR_LCAM && timeout--) + continue; + if (timeout == 0) { + /* XXX */ + panic("sonic: CAM initialisation failed\n"); + } + timeout = 10000; + while ((csr->s_isr & ISR_LCD) == 0 && timeout--) + continue; + + if (csr->s_isr & ISR_LCD) + csr->s_isr = ISR_LCD; + else + printf("sonic: CAM initialisation without interrupt\n"); +} + +#if 0 +void +camdump(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int i; + + printf("CAM entries:\n"); + csr->s_cr = CR_RST; + wbflush(); + + for (i = 0; i < 16; i++) { + ushort ap2, ap1, ap0; + csr->s_cep = i; + wbflush(); + ap2 = csr->s_cap2; + ap1 = csr->s_cap1; + ap0 = csr->s_cap0; + printf("%d: ap2=0x%x ap1=0x%x ap0=0x%x\n", i, ap2, ap1, ap0); + } + printf("CAM enable 0x%x\n", csr->s_cep); + + csr->s_cr = 0; + wbflush(); +} +#endif + +void +initialise_tda(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + struct mtd *mtd; + int i; + + csr = sc->sc_csr; + + mtdfree = mtdhead = mtdtail = (struct mtd *) 0; + + for (i = 0; i < NTDA; i++) { + mtd = &mtda[i]; + mtd->mtd_txp = &p_tda[i]; + mtd->mtd_mbuf = (struct mbuf *) 0; + mtd_free(mtd); + } + mtdnext = mtd_alloc(); + + csr->s_utda = UPPER(v_tda); +} + +void +initialise_rda(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int i; + + csr = sc->sc_csr; + + /* link the RDA's together into a circular list */ + for (i = 0; i < (NRDA - 1); i++) { + SWR(p_rda[i].rlink, LOWER(&v_rda[i + 1])); + SWR(p_rda[i].in_use, 1); + } + SWR(p_rda[NRDA - 1].rlink, LOWER(&v_rda[0]) | EOL); + SWR(p_rda[NRDA - 1].in_use, 1); + + /* mark end of receive descriptor list */ + sc->sc_lrxp = &p_rda[NRDA - 1]; + + sc->sc_rxmark = 0; + + csr->s_urda = UPPER(&v_rda[0]); + csr->s_crda = LOWER(&v_rda[0]); + wbflush(); +} + +void +initialise_rra(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr; + int i; + + csr = sc->sc_csr; + + csr->s_eobc = RBASIZE / 2 - 2; /* must be >= MAXETHERPKT */ + csr->s_urra = UPPER(v_rra); + csr->s_rsa = LOWER(v_rra); + csr->s_rea = LOWER(&v_rra[NRRA]); + csr->s_rrp = LOWER(v_rra); + csr->s_rsc = 0; + + /* fill up SOME of the rra with buffers */ + for (i = 0; i < NRBA; i++) { + SWR(p_rra[i].buff_ptrhi, UPPER(&v_rba[i * RBASIZE])); + SWR(p_rra[i].buff_ptrlo, LOWER(&v_rba[i * RBASIZE])); + SWR(p_rra[i].buff_wchi, UPPER(RBASIZE / 2)); + SWR(p_rra[i].buff_wclo, LOWER(RBASIZE / 2)); + } + sc->sc_rramark = NRBA; + csr->s_rwp = LOWER(&v_rra[sc->sc_rramark]); + wbflush(); +} + +int +snintr(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + int isr; + + while (isr = (csr->s_isr & ISR_ALL)) { + /* scrub the interrupts that we are going to service */ + csr->s_isr = isr; + wbflush(); + + if (isr & (ISR_BR | ISR_LCD | ISR_PINT | ISR_TC)) + printf("sonic: unexpected interrupt status 0x%x\n", isr); + + if (isr & (ISR_TXDN | ISR_TXER)) + sonictxint(sc); + + if (isr & ISR_PKTRX) + sonicrxint(sc); + + if (isr & (ISR_HBL | ISR_RDE | ISR_RBE | ISR_RBAE | ISR_RFO)) { + if (isr & ISR_HBL) + printf("sonic: no heartbeat\n"); + if (isr & ISR_RDE) + printf("sonic: receive descriptors exhausted\n"); + if (isr & ISR_RBE) + printf("sonic: receive buffers exhausted\n"); + if (isr & ISR_RBAE) + printf("sonic: receive buffer area exhausted\n"); + if (isr & ISR_RFO) + printf("sonic: receive FIFO overrun\n"); + } + if (isr & (ISR_CRC | ISR_FAE | ISR_MP)) { +#ifdef notdef + if (isr & ISR_CRC) + sc->sc_crctally++; + if (isr & ISR_FAE) + sc->sc_faetally++; + if (isr & ISR_MP) + sc->sc_mptally++; +#endif + } + } + return (1); +} + +/* + * Transmit interrupt routine + */ +void +sonictxint(sc) + struct sn_softc *sc; +{ + struct TXpkt *txp; + struct sonic_reg *csr; + struct mtd *mtd; + + if (mtdhead == (struct mtd *) 0) + return; + + csr = sc->sc_csr; + + while (mtd = mtdhead) { + struct mbuf *m = mtd->mtd_mbuf; + + if (m == 0) + break; + + txp = mtd->mtd_txp; + + if (SRD(txp->status) == 0) /* it hasn't really gone yet */ + return; + + if (ethdebug) { + struct ether_header *eh = mtod(m, struct ether_header *); + printf("xmit status=0x%x len=%d type=0x%x from %s", + txp->status, + txp->pkt_size, + htons(eh->ether_type), + ether_sprintf(eh->ether_shost)); + printf(" (to %s)\n", ether_sprintf(eh->ether_dhost)); + } + m_freem(m); + mtd->mtd_mbuf = 0; + mtdhead = mtd->mtd_link; + + mtd_free(mtd); + + if ((SRD(txp->status) & TCR_PTX) == 0) { + printf("sonic: Tx packet status=0x%x\n", txp->status); + + if (mtdhead != mtdnext) { + printf("resubmitting remaining packets\n"); + csr->s_ctda = LOWER(mtdhead->mtd_txp); + csr->s_cr = CR_TXP; + wbflush(); + return; + } + } + } + /* mtdhead should be at mtdnext (go) */ + assert(mtdhead == mtdnext); + assert(mtdhead->mtd_link == 0); + mtdhead = 0; + + /* and start feeding any queued packets to chip */ + while (1) { + struct mbuf *m; + + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) /* nothing left to send */ + break; + if (!sonicput(sc, m)) { /* not enough space */ + IF_PREPEND(&sc->sc_if.if_snd, m); + break; + } + } +} + +/* + * Receive interrupt routine + */ +void +sonicrxint(sc) + struct sn_softc *sc; +{ + struct sonic_reg *csr = sc->sc_csr; + struct RXpkt *rxp; + u_long addr; + int orra; + + rxp = &p_rda[sc->sc_rxmark]; + + while (SRD(rxp->in_use) == 0) { + unsigned status = SRD(rxp->status); + if ((status & RCR_LPKT) == 0) + printf("sonic: more than one packet in RBA!\n"); + assert(PSNSEQ(SRD(rxp->seq_no)) == 0); + + if (status & RCR_PRX) { + if (sonic_read(sc, rxp)) { + sc->sc_if.if_ipackets++; + sc->sc_sum.ls_ipacks++; + sc->sc_missed = 0; + } + } else + sc->sc_if.if_ierrors++; + + /* + * give receive buffer area back to chip XXX what buffer + * did the sonic use for this descriptor answer look at + * the rba sequence number !! + */ + orra = RBASEQ(SRD(rxp->seq_no)) & RRAMASK; + + assert(SRD(rxp->pkt_ptrhi) == SRD(p_rra[orra].buff_ptrhi)); + assert(SRD(rxp->pkt_ptrlo) == SRD(p_rra[orra].buff_ptrlo)); +if(SRD(rxp->pkt_ptrlo) != SRD(p_rra[orra].buff_ptrlo)) +printf("%x,%x\n",SRD(rxp->pkt_ptrlo),SRD(p_rra[orra].buff_ptrlo)); + assert(SRD(p_rra[orra].buff_wclo)); + + /* + * orra is now empty of packets and can be freed if + * sonic read didnt copy it out then we would have to + * wait !! + * (dont bother add it back in again straight away) + */ + p_rra[sc->sc_rramark] = p_rra[orra]; + + /* zap old rra for fun */ + p_rra[orra].buff_wchi = 0; + p_rra[orra].buff_wclo = 0; + + sc->sc_rramark = (sc->sc_rramark + 1) & RRAMASK; + csr->s_rwp = LOWER(&v_rra[sc->sc_rramark]); + wbflush(); + + /* + * give recieve descriptor back to chip simple + * list is circular + */ + SWR(rxp->in_use, 1); + SWR(rxp->rlink, SRD(rxp->rlink) | EOL); + SWR(sc->sc_lrxp->rlink, SRD(sc->sc_lrxp->rlink) & ~EOL); + sc->sc_lrxp = rxp; + + if (++sc->sc_rxmark >= NRDA) + sc->sc_rxmark = 0; + rxp = &p_rda[sc->sc_rxmark]; + } +} + +/* + * sonic_read -- pull packet off interface and forward to + * appropriate protocol handler + */ +int +sonic_read(sc, rxp) + struct sn_softc *sc; + struct RXpkt *rxp; +{ + struct ifnet *ifp = &sc->sc_if; + /*extern char *ether_sprintf();*/ + struct ether_header *et; + struct mbuf *m; + int len, off, i; + caddr_t pkt; + + /* + * Get input data length. + * Get pointer to ethernet header (in input buffer). + * Deal with trailer protocol: if type is PUP trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + + len = SRD(rxp->byte_count) - sizeof(struct ether_header) - FCSSIZE; + pkt = (caddr_t)((SRD(rxp->pkt_ptrhi) << 16) | SRD(rxp->pkt_ptrlo)); + pkt = pkt - v_rba + p_rba; + et = (struct ether_header *)pkt; + + if (ethdebug) { + printf("rcvd 0x%x status=0x%x, len=%d type=0x%x from %s", + et, rxp->status, len, htons(et->ether_type), + ether_sprintf(et->ether_shost)); + printf(" (to %s)\n", ether_sprintf(et->ether_dhost)); + } + if (len < ETHERMIN || len > ETHERMTU) { + printf("sonic: invalid packet length %d bytes\n", len); + return (0); + } + +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to enet, then discard things + * not destined for us (but be sure to keep broadcast/multicast). + */ + if (sc->sc_if.if_bpf) { + bpf_tap(sc->sc_if.if_bpf, pkt, + len + sizeof(struct ether_header)); + if ((ifp->if_flags & IFF_PROMISC) != 0 && + (et->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ + bcmp(et->ether_dhost, sc->sc_enaddr, + sizeof(et->ether_dhost)) != 0) + return; + } +#endif + m = sonic_get(sc, et, len); + if (m == NULL) + return (0); + ether_input(ifp, et, m); + return(1); +} + +#define sonicdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) + +/* + * munge the recieved packet into an mbuf chain + * because we are using stupif buffer management this + * is slow. +*/ +struct mbuf * +sonic_get(sc, eh, datalen) + struct sn_softc *sc; + struct ether_header *eh; + int datalen; +{ + struct mbuf *m; + struct mbuf *top = 0, **mp = ⊤ + int len; + char *spkt = sonicdataaddr(eh, 0, caddr_t); + char *epkt = spkt + datalen; + char *cp = spkt; + + epkt = cp + datalen; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = datalen; + m->m_len = MHLEN; + + while (datalen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(datalen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned) len); + cp += len; + *mp = m; + mp = &m->m_next; + datalen -= len; + if (cp == epkt) + cp = spkt; + } + return (top); +} diff --git a/sys/arch/arc/dev/if_sn.h b/sys/arch/arc/dev/if_sn.h new file mode 100644 index 00000000000..c2018fb43e5 --- /dev/null +++ b/sys/arch/arc/dev/if_sn.h @@ -0,0 +1,347 @@ +/* $OpenBSD: if_sn.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* + * Copyright (c) 1991 Algorithmics Ltd (http://www.algor.co.uk) + * You may use, copy, and modify this program so long as you retain the + * copyright line. + */ + +/* + * if_sonic.h -- National Semiconductor DP83932BVF (SONIC) + */ + +/* + * Accessing SONIC data structures and registers as 32 bit values + * makes code endianess independent. The SONIC is however always in + * bigendian mode so it is necessary to ensure that data structures shared + * between the CPU and the SONIC are always in bigendian order. + */ + +/* + * Receive Resource Descriptor + * This structure describes the buffers into which packets + * will be received. Note that more than one packet may be + * packed into a single buffer if constraints permit. + */ +#if SONICDW == 32 +struct RXrsrc { + u_long buff_ptrlo; /* buffer address LO */ + u_long buff_ptrhi; /* buffer address HI */ + u_long buff_wclo; /* buffer size (16bit words) LO */ + u_long buff_wchi; /* buffer size (16bit words) HI */ +}; +#endif + +/* + * Receive Descriptor + * This structure holds information about packets received. + */ +#if SONICDW == 32 +struct RXpkt { + u_long status; /* + receive status */ + u_long byte_count; /* + packet byte count (including FCS) */ + u_long pkt_ptrlo; /* + packet data LO (in RBA) */ + u_long pkt_ptrhi; /* + packet data HI (in RBA) */ + u_long seq_no; /* + RBA sequence numbers */ + u_long rlink; /* link to next receive descriptor */ + u_long in_use; /* + packet available to SONIC */ + u_long pad; /* pad to multiple of 16 bytes */ +}; +#endif +#define RBASEQ(x) (((x)>>8)&0xff) +#define PSNSEQ(x) ((x) & 0xff) + +/* + * Transmit Descriptor + * This structure holds information about packets to be transmitted. + */ +#define FRAGMAX 31 /* maximum number of fragments in a packet */ +#if SONICDW == 32 +struct TXpkt { + u_long status; /* + transmitted packet status */ + u_long config; /* transmission configuration */ + u_long pkt_size; /* entire packet size in bytes */ + u_long frag_count; /* # fragments in packet */ + union { + struct { + u_long _frag_ptrlo; /* pointer to packet fragment LO */ + u_long _frag_ptrhi; /* pointer to packet fragment HI */ + u_long _frag_size; /* fragment size */ + } u_frag; + struct { + u_long _tlink; /* link to next transmit descriptor */ + } u_link; + } u[FRAGMAX+1]; /* +1 makes tcp->u[FRAGMAX].u_link.link valid! */ +}; +#endif + +#define frag_ptrlo u_frag._frag_ptrlo +#define frag_ptrhi u_frag._frag_ptrhi +#define frag_size u_frag._frag_size +#define tlink u_link._tlink + +#define EOL 0x0001 /* end of list marker for link fields */ + +#define MAXCAM 16 /* number of user entries in CAM */ +#if SONICDW == 32 +struct CDA { + struct { + u_long cam_ep; /* CAM Entry Pointer */ + u_long cam_ap0; /* CAM Address Port 0 xx-xx-xx-xx-YY-YY */ + u_long cam_ap1; /* CAM Address Port 1 xx-xx-YY-YY-xxxx */ + u_long cam_ap2; /* CAM Address Port 2 YY-YY-xx-xx-xx-xx */ + } desc[MAXCAM]; + u_long enable; /* mask enabling CAM entries */ +}; +#endif + +/* + * SONIC registers as seen by the processor + */ +struct sonic_reg { + volatile u_long s_cr; /* 00: Command */ + volatile u_long s_dcr; /* 01: Data Configuration */ + volatile u_long s_rcr; /* 02: Receive Control */ + volatile u_long s_tcr; /* 03: Transmit Control */ + volatile u_long s_imr; /* 04: Interrupt Mask */ + volatile u_long s_isr; /* 05: Interrupt Status */ + volatile u_long s_utda; /* 06: Upper Transmit Descriptor Address */ + volatile u_long s_ctda; /* 07: Current Transmit Descriptor Address */ + volatile u_long _s_tps; /* 08* Transmit Packet Size */ + volatile u_long _s_tfc; /* 09* Transmit Fragment Count */ + volatile u_long _s_tsa0; /* 0a* Transmit Start Address 0 */ + volatile u_long _s_tsa1; /* 0b* Transmit Start Address 1 */ + volatile u_long _s_tfs; /* 0c* Transmit Fragment Size */ + volatile u_long s_urda; /* 0d: Upper Receive Descriptor Address */ + volatile u_long s_crda; /* 0e: Current Receive Descriptor Address */ + volatile u_long _s_crba0; /* 0f* Current Receive Buffer Address 0 */ + volatile u_long _s_crba1; /* 10* Current Receive Buffer Address 1 */ + volatile u_long _s_rbwc0; /* 11* Remaining Buffer Word Count 0 */ + volatile u_long _s_rbwc1; /* 12* Remaining Buffer Word Count 1 */ + volatile u_long s_eobc; /* 13: End Of Buffer Word Count */ + volatile u_long s_urra; /* 14: Upper Receive Resource Address */ + volatile u_long s_rsa; /* 15: Resource Start Address */ + volatile u_long s_rea; /* 16: Resource End Address */ + volatile u_long s_rrp; /* 17: Resource Read Pointer */ + volatile u_long s_rwp; /* 18: Resource Write Pointer */ + volatile u_long _s_trba0; /* 19* Temporary Receive Buffer Address 0 */ + volatile u_long _s_trba1; /* 1a* Temporary Receive Buffer Address 1 */ + volatile u_long _s_tbwc0; /* 1b* Temporary Buffer Word Count 0 */ + volatile u_long _s_tbwc1; /* 1c* Temporary Buffer Word Count 1 */ + volatile u_long _s_addr0; /* 1d* Address Generator 0 */ + volatile u_long _s_addr1; /* 1e* Address Generator 1 */ + volatile u_long _s_llfa; /* 1f* Last Link Field Address */ + volatile u_long _s_ttda; /* 20* Temp Transmit Descriptor Address */ + volatile u_long s_cep; /* 21: CAM Entry Pointer */ + volatile u_long s_cap2; /* 22: CAM Address Port 2 */ + volatile u_long s_cap1; /* 23: CAM Address Port 1 */ + volatile u_long s_cap0; /* 24: CAM Address Port 0 */ + volatile u_long s_ce; /* 25: CAM Enable */ + volatile u_long s_cdp; /* 26: CAM Descriptor Pointer */ + volatile u_long s_cdc; /* 27: CAM Descriptor Count */ + volatile u_long s_sr; /* 28: Silicon Revision */ + volatile u_long s_wt0; /* 29: Watchdog Timer 0 */ + volatile u_long s_wt1; /* 2a: Watchdog Timer 1 */ + volatile u_long s_rsc; /* 2b: Receive Sequence Counter */ + volatile u_long s_crct; /* 2c: CRC Error Tally */ + volatile u_long s_faet; /* 2d: FAE Tally */ + volatile u_long s_mpt; /* 2e: Missed Packet Tally */ + volatile u_long _s_mdt; /* 2f* Maximum Deferral Timer */ + volatile u_long _s_rtc; /* 30* Receive Test Control */ + volatile u_long _s_ttc; /* 31* Transmit Test Control */ + volatile u_long _s_dtc; /* 32* DMA Test Control */ + volatile u_long _s_cc0; /* 33* CAM Comparison 0 */ + volatile u_long _s_cc1; /* 34* CAM Comparison 1 */ + volatile u_long _s_cc2; /* 35* CAM Comparison 2 */ + volatile u_long _s_cm; /* 36* CAM Match */ + volatile u_long :32; /* 37* reserved */ + volatile u_long :32; /* 38* reserved */ + volatile u_long _s_rbc; /* 39* Receiver Byte Count */ + volatile u_long :32; /* 3a* reserved */ + volatile u_long _s_tbo; /* 3b* Transmitter Backoff Counter */ + volatile u_long _s_trc; /* 3c* Transmitter Random Counter */ + volatile u_long _s_tbm; /* 3d* Transmitter Backoff Mask */ + volatile u_long :32; /* 3e* Reserved */ + volatile u_long s_dcr2; /* 3f Data Configuration 2 (AVF) */ +}; + +/* + * Register Interpretations + */ + +/* + * The command register is used for issuing commands to the SONIC. + * With the exception of CR_RST, the bit is reset when the operation + * completes. + */ +#define CR_LCAM 0x0200 /* load CAM with descriptor at s_cdp */ +#define CR_RRRA 0x0100 /* read next RRA descriptor at s_rrp */ +#define CR_RST 0x0080 /* software reset */ +#define CR_ST 0x0020 /* start timer */ +#define CR_STP 0x0010 /* stop timer */ +#define CR_RXEN 0x0008 /* receiver enable */ +#define CR_RXDIS 0x0004 /* receiver disable */ +#define CR_TXP 0x0002 /* transmit packets */ +#define CR_HTX 0x0001 /* halt transmission */ + +/* + * The data configuration register establishes the SONIC's bus cycle + * operation. This register can only be accessed when the SONIC is in + * reset mode (s_cr.CR_RST is set.) + */ +#define DCR_EXBUS 0x8000 /* extended bus mode (AVF) */ +#define DCR_LBR 0x2000 /* latched bus retry */ +#define DCR_PO1 0x1000 /* programmable output 1 */ +#define DCR_PO0 0x0800 /* programmable output 0 */ +#define DCR_STERM 0x0400 /* synchronous termination */ +#define DCR_USR1 0x0200 /* reflects USR1 input pin */ +#define DCR_USR0 0x0100 /* reflects USR0 input pin */ +#define DCR_WC1 0x0080 /* wait state control 1 */ +#define DCR_WC0 0x0040 /* wait state control 0 */ +#define DCR_DW 0x0020 /* data width select */ +#define DCR_BMS 0x0010 /* DMA block mode select */ +#define DCR_RFT1 0x0008 /* receive FIFO threshold control 1 */ +#define DCR_RFT0 0x0004 /* receive FIFO threshold control 0 */ +#define DCR_TFT1 0x0002 /* transmit FIFO threshold control 1 */ +#define DCR_TFT0 0x0001 /* transmit FIFO threshold control 0 */ + +/* data configuration register aliases */ +#define DCR_SYNC DCR_STERM /* synchronous (memory cycle 2 clocks) */ +#define DCR_ASYNC 0 /* asynchronous (memory cycle 3 clocks) */ + +#define DCR_WAIT0 0 /* 0 wait states added */ +#define DCR_WAIT1 DCR_WC0 /* 1 wait state added */ +#define DCR_WAIT2 DCR_WC1 /* 2 wait states added */ +#define DCR_WAIT3 (DCR_WC1|DCR_WC0) /* 3 wait states added */ + +#define DCR_DW16 0 /* use 16-bit DMA accesses */ +#define DCR_DW32 DCR_DW /* use 32-bit DMA accesses */ + +#define DCR_DMAEF 0 /* DMA until TX/RX FIFO has emptied/filled */ +#define DCR_DMABLOCK DCR_BMS /* DMA until RX/TX threshold crossed */ + +#define DCR_RFT4 0 /* receive threshold 4 bytes */ +#define DCR_RFT8 DCR_RFT0 /* receive threshold 8 bytes */ +#define DCR_RFT16 DCR_RFT1 /* receive threshold 16 bytes */ +#define DCR_RFT24 (DCR_RFT1|DCR_RFT0) /* receive threshold 24 bytes */ + +#define DCR_TFT8 0 /* transmit threshold 8 bytes */ +#define DCR_TFT16 DCR_TFT0 /* transmit threshold 16 bytes */ +#define DCR_TFT24 DCR_TFT1 /* transmit threshold 24 bytes */ +#define DCR_TFT28 (DCR_TFT1|DCR_TFT0) /* transmit threshold 28 bytes */ + +/* + * The receive control register is used to filter incoming packets and + * provides status information on packets received. + * The contents of the register are copied into the RXpkt.status field + * when a packet is received. RCR_MC - RCR_PRX are then reset. + */ +#define RCR_ERR 0x8000 /* accept packets with CRC errors */ +#define RCR_RNT 0x4000 /* accept runt (length < 64) packets */ +#define RCR_BRD 0x2000 /* accept broadcast packets */ +#define RCR_PRO 0x1000 /* accept all physical address packets */ +#define RCR_AMC 0x0800 /* accept all multicast packets */ +#define RCR_LB1 0x0400 /* loopback control 1 */ +#define RCR_LB0 0x0200 /* loopback control 0 */ +#define RCR_MC 0x0100 /* multicast packet received */ +#define RCR_BC 0x0080 /* broadcast packet received */ +#define RCR_LPKT 0x0040 /* last packet in RBA (RBWC < EOBC) */ +#define RCR_CRS 0x0020 /* carrier sense activity */ +#define RCR_COL 0x0010 /* collision activity */ +#define RCR_CRC 0x0008 /* CRC error */ +#define RCR_FAE 0x0004 /* frame alignment error */ +#define RCR_LBK 0x0002 /* loopback packet received */ +#define RCR_PRX 0x0001 /* packet received without errors */ + +/* receiver control register aliases */ +/* the loopback control bits provide the following options */ +#define RCR_LBNONE 0 /* no loopback - normal operation */ +#define RCR_LBMAC RCR_LB0 /* MAC loopback */ +#define RCR_LBENDEC RCR_LB1 /* ENDEC loopback */ +#define RCR_LBTRANS (RCR_LB1|RCR_LB0) /* transceiver loopback */ + +/* + * The transmit control register controls the SONIC's transmit operations. + * TCR_PINT - TCR_EXDIS are loaded from the TXpkt.config field at the + * start of transmission. TCR_EXD-TCR_PTX are cleared at the beginning + * of transmission and updated when the transmission is completed. + */ +#define TCR_PINT 0x8000 /* interrupt when transmission starts */ +#define TCR_POWC 0x4000 /* program out of window collision timer */ +#define TCR_CRCI 0x2000 /* transmit packet without 4 byte FCS */ +#define TCR_EXDIS 0x1000 /* disable excessive deferral timer */ +#define TCR_EXD 0x0400 /* excessive deferrals occurred (>3.2ms) */ +#define TCR_DEF 0x0200 /* deferred transmissions occurred */ +#define TCR_NCRS 0x0100 /* carrier not present during transmission */ +#define TCR_CRSL 0x0080 /* carrier lost during transmission */ +#define TCR_EXC 0x0040 /* excessive collisions (>16) detected */ +#define TCR_OWC 0x0020 /* out of window (bad) collision occurred */ +#define TCR_PMB 0x0008 /* packet monitored bad - the tansmitted + * packet had a bad source address or CRC */ +#define TCR_FU 0x0004 /* FIFO underrun (memory access failed) */ +#define TCR_BCM 0x0002 /* byte count mismatch (TXpkt.pkt_size + * != sum(TXpkt.frag_size) */ +#define TCR_PTX 0x0001 /* packet transmitted without errors */ + +/* transmit control register aliases */ +#define TCR_OWCSFD 0 /* start after start of frame delimiter */ +#define TCR_OWCPRE TCR_POWC /* start after first bit of preamble */ + + +/* + * The interrupt mask register masks the interrupts that + * are generated from the interrupt status register. + * All reserved bits should be written with 0. + */ +#define IMR_BREN 0x4000 /* bus retry occurred enable */ +#define IMR_HBLEN 0x2000 /* heartbeat lost enable */ +#define IMR_LCDEN 0x1000 /* load CAM done interrupt enable */ +#define IMR_PINTEN 0x0800 /* programmable interrupt enable */ +#define IMR_PRXEN 0x0400 /* packet received enable */ +#define IMR_PTXEN 0x0200 /* packet transmitted enable */ +#define IMR_TXEREN 0x0100 /* transmit error enable */ +#define IMR_TCEN 0x0080 /* timer complete enable */ +#define IMR_RDEEN 0x0040 /* receive descriptors exhausted enable */ +#define IMR_RBEEN 0x0020 /* receive buffers exhausted enable */ +#define IMR_RBAEEN 0x0010 /* receive buffer area exceeded enable */ +#define IMR_CRCEN 0x0008 /* CRC tally counter rollover enable */ +#define IMR_FAEEN 0x0004 /* FAE tally counter rollover enable */ +#define IMR_MPEN 0x0002 /* MP tally counter rollover enable */ +#define IMR_RFOEN 0x0001 /* receive FIFO overrun enable */ + + +/* + * The interrupt status register indicates the source of an interrupt when + * the INT pin goes active. The interrupt is acknowledged by writing + * the appropriate bit(s) in this register. + */ +#define ISR_ALL 0xffff /* all interrupts */ +#define ISR_BR 0x4000 /* bus retry occurred */ +#define ISR_HBL 0x2000 /* CD heartbeat lost */ +#define ISR_LCD 0x1000 /* load CAM command has completed */ +#define ISR_PINT 0x0800 /* programmed interrupt from TXpkt.config */ +#define ISR_PKTRX 0x0400 /* packet received */ +#define ISR_TXDN 0x0200 /* no remaining packets to be transmitted */ +#define ISR_TXER 0x0100 /* packet transmission caused error */ +#define ISR_TC 0x0080 /* timer complete */ +#define ISR_RDE 0x0040 /* receive descriptors exhausted */ +#define ISR_RBE 0x0020 /* receive buffers exhausted */ +#define ISR_RBAE 0x0010 /* receive buffer area exceeded */ +#define ISR_CRC 0x0008 /* CRC tally counter rollover */ +#define ISR_FAE 0x0004 /* FAE tally counter rollover */ +#define ISR_MP 0x0002 /* MP tally counter rollover */ +#define ISR_RFO 0x0001 /* receive FIFO overrun */ + +/* + * The second data configuration register allows additional user defined + * pins to be controlled. These bits are only available if s_dcr.DCR_EXBUS + * is set. + */ +#define DCR2_EXPO3 0x8000 /* EXUSR3 output */ +#define DCR2_EXPO2 0x4000 /* EXUSR2 output */ +#define DCR2_EXPO1 0x2000 /* EXUSR1 output */ +#define DCR2_EXPO0 0x1000 /* EXUSR0 output */ +#define DCR2_PHL 0x0010 /* extend HOLD signal by 1/2 clock */ +#define DCR2_LRDY 0x0008 /* set latched ready mode */ +#define DCR2_PCM 0x0004 /* packet compress on match */ +#define DCR2_PCNM 0x0002 /* packet compress on mismatch */ +#define DCR2_RJM 0x0001 /* reject on match */ diff --git a/sys/arch/arc/dev/lpt.c b/sys/arch/arc/dev/lpt.c new file mode 100644 index 00000000000..da3e62733ba --- /dev/null +++ b/sys/arch/arc/dev/lpt.c @@ -0,0 +1,676 @@ +/* $OpenBSD: lpt.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: lpt.c,v 1.39 1996/05/12 23:53:06 mycroft Exp $ */ + +/* + * Copyright (c) 1993, 1994 Charles Hannum. + * Copyright (c) 1990 William F. Jolitz, TeleMuse + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This software is a component of "386BSD" developed by + * William F. Jolitz, TeleMuse. + * 4. Neither the name of the developer nor the name "386BSD" + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ + * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS + * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. + * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT + * NOT MAKE USE OF THIS WORK. + * + * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED + * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN + * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES + * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING + * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND + * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE + * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS + * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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. + */ + +/* + * Device Driver for AT parallel printer port + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/syslog.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> +#include <dev/isa/lptreg.h> + +#include <lpt.h> + +#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ +#define STEP hz/4 + +#define LPTPRI (PZERO+8) +#define LPT_BSIZE 1024 + +#if !defined(DEBUG) || !defined(notdef) +#define LPRINTF(a) +#else +#define LPRINTF if (lptdebug) printf a +int lptdebug = 1; +#endif + +struct lpt_softc { + struct device sc_dev; + void *sc_ih; + + size_t sc_count; + struct buf *sc_inbuf; + u_char *sc_cp; + int sc_spinmax; + int sc_iobase; + bus_chipset_tag_t sc_bc; + bus_io_handle_t sc_ioh; + int sc_irq; + u_char sc_state; +#define LPT_OPEN 0x01 /* device is open */ +#define LPT_OBUSY 0x02 /* printer is busy doing output */ +#define LPT_INIT 0x04 /* waiting to initialize for open */ + u_char sc_flags; +#define LPT_AUTOLF 0x20 /* automatic LF on CR */ +#define LPT_NOPRIME 0x40 /* don't prime on open */ +#define LPT_NOINTR 0x80 /* do not use interrupt */ + u_char sc_control; + u_char sc_laststatus; +}; + +/* XXX does not belong here */ +cdev_decl(lpt); + +int lptintr __P((void *)); + +#if NLPT_ISA +int lpt_isa_probe __P((struct device *, void *, void *)); +void lpt_isa_attach __P((struct device *, struct device *, void *)); +struct cfattach lpt_ca = { + sizeof(struct lpt_softc), lpt_isa_probe, lpt_isa_attach +}; +#endif + +#if NLPT_PICA +int lpt_pica_probe __P((struct device *, void *, void *)); +void lpt_pica_attach __P((struct device *, struct device *, void *)); +struct cfattach lpt_pica_ca = { + sizeof(struct lpt_softc), lpt_pica_probe, lpt_pica_attach +}; +#endif + +struct cfdriver lpt_cd = { + NULL, "lpt", DV_TTY +}; + +#define LPTUNIT(s) (minor(s) & 0x1f) +#define LPTFLAGS(s) (minor(s) & 0xe0) + +#define LPS_INVERT (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK) +#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER) +#define NOT_READY() ((bus_io_read_1(bc, ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK) +#define NOT_READY_ERR() not_ready(bus_io_read_1(bc, ioh, lpt_status), sc) +static int not_ready __P((u_char, struct lpt_softc *)); + +static void lptwakeup __P((void *arg)); +static int pushbytes __P((struct lpt_softc *)); + +int lpt_port_test __P((bus_chipset_tag_t, bus_io_handle_t, bus_io_addr_t, + bus_io_size_t, u_char, u_char)); + +/* + * Internal routine to lptprobe to do port tests of one byte value. + */ +int +lpt_port_test(bc, ioh, base, off, data, mask) + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + bus_io_addr_t base; + bus_io_size_t off; + u_char data, mask; +{ + int timeout; + u_char temp; + + data &= mask; + bus_io_write_1(bc, ioh, off, data); + timeout = 1000; + do { + delay(10); + temp = bus_io_read_1(bc, ioh, off) & mask; + } while (temp != data && --timeout); + LPRINTF(("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off, + data, temp, timeout)); + return (temp == data); +} + +/* + * Logic: + * 1) You should be able to write to and read back the same value + * to the data port. Do an alternating zeros, alternating ones, + * walking zero, and walking one test to check for stuck bits. + * + * 2) You should be able to write to and read back the same value + * to the control port lower 5 bits, the upper 3 bits are reserved + * per the IBM PC technical reference manauls and different boards + * do different things with them. Do an alternating zeros, alternating + * ones, walking zero, and walking one test to check for stuck bits. + * + * Some printers drag the strobe line down when the are powered off + * so this bit has been masked out of the control port test. + * + * XXX Some printers may not like a fast pulse on init or strobe, I + * don't know at this point, if that becomes a problem these bits + * should be turned off in the mask byte for the control port test. + * + * 3) Set the data and control ports to a value of 0 + */ +#if NLPT_ISA +int +lpt_isa_probe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct isa_attach_args *ia = aux; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + u_long base; + u_char mask, data; + int i, rv; + +#ifdef DEBUG +#define ABORT do {printf("lptprobe: mask %x data %x failed\n", mask, data); \ + goto out;} while (0) +#else +#define ABORT goto out +#endif + + bc = ia->ia_bc; + base = ia->ia_iobase; + if (bus_io_map(bc, base, LPT_NPORTS, &ioh)) + return 0; + + rv = 0; + mask = 0xff; + + data = 0x55; /* Alternating zeros */ + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + + data = 0xaa; /* Alternating ones */ + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + + for (i = 0; i < CHAR_BIT; i++) { /* Walking zero */ + data = ~(1 << i); + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + } + + for (i = 0; i < CHAR_BIT; i++) { /* Walking one */ + data = (1 << i); + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + } + + bus_io_write_1(bc, ioh, lpt_data, 0); + bus_io_write_1(bc, ioh, lpt_control, 0); + + ia->ia_iosize = LPT_NPORTS; + ia->ia_msize = 0; + + rv = 1; + +out: + bus_io_unmap(bc, ioh, LPT_NPORTS); + return rv; +} +#endif + +#if NLPT_PICA +int +lpt_pica_probe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct confargs *ca = aux; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + u_long base; + u_char mask, data; + int i; + +#ifdef DEBUG +#define ABORT do {printf("lptprobe: mask %x data %x failed\n", mask, data); \ + return 0;} while (0) +#else +#define ABORT return 0 +#endif + + if(!BUS_MATCHNAME(ca, "lpt")) + return(0); + + bc = 0; + base = (int)BUS_CVTADDR(ca); + ioh = base; + + mask = 0xff; + + data = 0x55; /* Alternating zeros */ + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + + data = 0xaa; /* Alternating ones */ + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + + for (i = 0; i < CHAR_BIT; i++) { /* Walking zero */ + data = ~(1 << i); + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + } + + for (i = 0; i < CHAR_BIT; i++) { /* Walking one */ + data = (1 << i); + if (!lpt_port_test(bc, ioh, base, lpt_data, data, mask)) + ABORT; + } + + bus_io_write_1(bc, ioh, lpt_data, 0); + bus_io_write_1(bc, ioh, lpt_control, 0); + + return 1; +} +#endif + +#if NLPT_ISA +void +lpt_isa_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct lpt_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + + if (ia->ia_irq != IRQUNK) + printf("\n"); + else + printf(": polled\n"); + + sc->sc_iobase = ia->ia_iobase; + sc->sc_irq = ia->ia_irq; + sc->sc_state = 0; + + bc = sc->sc_bc = ia->ia_bc; + if (bus_io_map(bc, sc->sc_iobase, LPT_NPORTS, &ioh)) + panic("lptattach: couldn't map I/O ports"); + sc->sc_ioh = ioh; + + bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT); + + if (ia->ia_irq != IRQUNK) + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_TTY, lptintr, sc, sc->sc_dev.dv_xname); +} +#endif + +#if NLPT_PICA +void +lpt_pica_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct lpt_softc *sc = (void *)self; + struct confargs *ca = aux; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + + printf("\n"); + + sc->sc_iobase = (int)BUS_CVTADDR(ca); + sc->sc_irq = 0; + sc->sc_state = 0; + + bc = sc->sc_bc = 0; + sc->sc_ioh = sc->sc_iobase; + + bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT); + + BUS_INTR_ESTABLISH(ca, lptintr, sc); +} +#endif + +/* + * Reset the printer, then wait until it's selected and not busy. + */ +int +lptopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = LPTUNIT(dev); + u_char flags = LPTFLAGS(dev); + struct lpt_softc *sc; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + u_char control; + int error; + int spin; + + if (unit >= lpt_cd.cd_ndevs) + return ENXIO; + sc = lpt_cd.cd_devs[unit]; + if (!sc) + return ENXIO; + + if (sc->sc_irq == IRQUNK && (flags & LPT_NOINTR) == 0) + return ENXIO; + +#ifdef DIAGNOSTIC + if (sc->sc_state) + printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname, + sc->sc_state); +#endif + + if (sc->sc_state) + return EBUSY; + + sc->sc_state = LPT_INIT; + sc->sc_flags = flags; + LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags)); + bc = sc->sc_bc; + ioh = sc->sc_ioh; + + if ((flags & LPT_NOPRIME) == 0) { + /* assert INIT for 100 usec to start up printer */ + bus_io_write_1(bc, ioh, lpt_control, LPC_SELECT); + delay(100); + } + + control = LPC_SELECT | LPC_NINIT; + bus_io_write_1(bc, ioh, lpt_control, control); + + /* wait till ready (printer running diagnostics) */ + for (spin = 0; NOT_READY_ERR(); spin += STEP) { + if (spin >= TIMEOUT) { + sc->sc_state = 0; + return EBUSY; + } + + /* wait 1/4 second, give up if we get a signal */ + error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen", STEP); + if (error != EWOULDBLOCK) { + sc->sc_state = 0; + return error; + } + } + + if ((flags & LPT_NOINTR) == 0) + control |= LPC_IENABLE; + if (flags & LPT_AUTOLF) + control |= LPC_AUTOLF; + sc->sc_control = control; + bus_io_write_1(bc, ioh, lpt_control, control); + + sc->sc_inbuf = geteblk(LPT_BSIZE); + sc->sc_count = 0; + sc->sc_state = LPT_OPEN; + + if ((sc->sc_flags & LPT_NOINTR) == 0) + lptwakeup(sc); + + LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname)); + return 0; +} + +int +not_ready(status, sc) + u_char status; + struct lpt_softc *sc; +{ + u_char new; + + status = (status ^ LPS_INVERT) & LPS_MASK; + new = status & ~sc->sc_laststatus; + sc->sc_laststatus = status; + + if (new & LPS_SELECT) + log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname); + else if (new & LPS_NOPAPER) + log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname); + else if (new & LPS_NERR) + log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname); + + return status; +} + +void +lptwakeup(arg) + void *arg; +{ + struct lpt_softc *sc = arg; + int s; + + s = spltty(); + lptintr(sc); + splx(s); + + timeout(lptwakeup, sc, STEP); +} + +/* + * Close the device, and free the local line buffer. + */ +int +lptclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = LPTUNIT(dev); + struct lpt_softc *sc = lpt_cd.cd_devs[unit]; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + + if (sc->sc_count) + (void) pushbytes(sc); + + if ((sc->sc_flags & LPT_NOINTR) == 0) + untimeout(lptwakeup, sc); + + bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT); + sc->sc_state = 0; + bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT); + brelse(sc->sc_inbuf); + + LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname)); + return 0; +} + +int +pushbytes(sc) + struct lpt_softc *sc; +{ + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + int error; + + if (sc->sc_flags & LPT_NOINTR) { + int spin, tic; + u_char control = sc->sc_control; + + while (sc->sc_count > 0) { + spin = 0; + while (NOT_READY()) { + if (++spin < sc->sc_spinmax) + continue; + tic = 0; + /* adapt busy-wait algorithm */ + sc->sc_spinmax++; + while (NOT_READY_ERR()) { + /* exponential backoff */ + tic = tic + tic + 1; + if (tic > TIMEOUT) + tic = TIMEOUT; + error = tsleep((caddr_t)sc, + LPTPRI | PCATCH, "lptpsh", tic); + if (error != EWOULDBLOCK) + return error; + } + break; + } + + bus_io_write_1(bc, ioh, lpt_data, *sc->sc_cp++); + bus_io_write_1(bc, ioh, lpt_control, control | LPC_STROBE); + sc->sc_count--; + bus_io_write_1(bc, ioh, lpt_control, control); + + /* adapt busy-wait algorithm */ + if (spin*2 + 16 < sc->sc_spinmax) + sc->sc_spinmax--; + } + } else { + int s; + + while (sc->sc_count > 0) { + /* if the printer is ready for a char, give it one */ + if ((sc->sc_state & LPT_OBUSY) == 0) { + LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname, + sc->sc_count)); + s = spltty(); + (void) lptintr(sc); + splx(s); + } + error = tsleep((caddr_t)sc, LPTPRI | PCATCH, + "lptwrite2", 0); + if (error) + return error; + } + } + return 0; +} + +/* + * Copy a line from user space to a local buffer, then call putc to get the + * chars moved to the output queue. + */ +int +lptwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)]; + size_t n; + int error = 0; + + while ((n = min(LPT_BSIZE, uio->uio_resid)) != 0) { + uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio); + sc->sc_count = n; + error = pushbytes(sc); + if (error) { + /* + * Return accurate residual if interrupted or timed + * out. + */ + uio->uio_resid += sc->sc_count; + sc->sc_count = 0; + return error; + } + } + return 0; +} + +/* + * Handle printer interrupts which occur when the printer is ready to accept + * another char. + */ +int +lptintr(arg) + void *arg; +{ + struct lpt_softc *sc = arg; + bus_chipset_tag_t bc = sc->sc_bc; + bus_io_handle_t ioh = sc->sc_ioh; + + if (((sc->sc_state & LPT_OPEN) == 0 && sc->sc_count == 0) || (sc->sc_flags & LPT_NOINTR)) + return 0; + + /* is printer online and ready for output */ + if (NOT_READY() && NOT_READY_ERR()) + return -1; + + if (sc->sc_count) { + u_char control = sc->sc_control; + /* send char */ + bus_io_write_1(bc, ioh, lpt_data, *sc->sc_cp++); + bus_io_write_1(bc, ioh, lpt_control, control | LPC_STROBE); + sc->sc_count--; + bus_io_write_1(bc, ioh, lpt_control, control); + sc->sc_state |= LPT_OBUSY; + } else + sc->sc_state &= ~LPT_OBUSY; + + if (sc->sc_count == 0) { + /* none, wake up the top half to get more */ + wakeup((caddr_t)sc); + } + + return 1; +} + +int +lptioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int error = 0; + + switch (cmd) { + default: + error = ENODEV; + } + + return error; +} diff --git a/sys/arch/arc/dev/pccons.c b/sys/arch/arc/dev/pccons.c new file mode 100644 index 00000000000..edc39878791 --- /dev/null +++ b/sys/arch/arc/dev/pccons.c @@ -0,0 +1,2094 @@ +/* $OpenBSD: pccons.c,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* $NetBSD: pccons.c,v 1.89 1995/05/04 19:35:20 cgd Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * Copyright (c) 1994 Charles Hannum. + * Copyright (c) 1992, 1993 Erik Forsberg. + * + * 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 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 + * SUCH DAMAGE. + * + * @(#)pccons.c 5.11 (Berkeley) 5/21/91 + */ + +/* + * code to work keyboard & display for PC-style console + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/syslog.h> +#include <sys/vnode.h> +#include <sys/device.h> +#include <sys/file.h> + +#include <dev/cons.h> + +#include <machine/cpu.h> +#include <machine/pio.h> +#include <machine/autoconf.h> +#include <machine/display.h> +#include <machine/pccons.h> +#include <arc/pica/pica.h> + +#include <dev/isa/isavar.h> +#include <machine/kbdreg.h> + +#define XFREE86_BUG_COMPAT + +#ifndef BEEP_FREQ +#define BEEP_FREQ 1600 +#endif +#ifndef BEEP_TIME +#define BEEP_TIME (hz/5) +#endif + +#define PCBURST 128 + +static u_short *Crtat; /* pointer to backing store */ +static u_short *crtat; /* pointer to current char */ +static u_char async, kernel, polling; /* Really, you don't want to know. */ +static u_char lock_state = 0x00, /* all off */ + old_lock_state = 0xff, + typematic_rate = 0xff, /* don't update until set by user */ + old_typematic_rate = 0xff; +static u_short cursor_shape = 0xffff, /* don't update until set by user */ + old_cursor_shape = 0xffff; +static keymap_t scan_codes[KB_NUM_KEYS];/* keyboard translation table */ +int pc_xmode = 0; + +/* + * Keyboard output queue. + */ +int kb_oq_put = 0; +int kb_oq_get = 0; +u_char kb_oq[8]; + +#define PCUNIT(x) (minor(x)) + +static struct video_state { + int cx, cy; /* escape parameters */ + int row, col; /* current cursor position */ + int nrow, ncol, nchr; /* current screen geometry */ + u_char state; /* parser state */ +#define VSS_ESCAPE 1 +#define VSS_EBRACE 2 +#define VSS_EPARAM 3 + char so; /* in standout mode? */ + char color; /* color or mono display */ + char at; /* normal attributes */ + char so_at; /* standout attributes */ +} vs; + +struct pc_softc { + struct device sc_dev; + struct tty *sc_tty; +}; + +struct pms_softc { /* driver status information */ + struct device sc_dev; + + struct clist sc_q; + struct selinfo sc_rsel; + u_char sc_state; /* mouse driver state */ +#define PMS_OPEN 0x01 /* device is open */ +#define PMS_ASLP 0x02 /* waiting for mouse data */ + u_char sc_status; /* mouse button status */ + int sc_x, sc_y; /* accumulated motion in the X,Y axis */ +}; + +int pcprobe __P((struct device *, void *, void *)); +void pcattach __P((struct device *, struct device *, void *)); +int pcintr __P((void *)); + +struct cfattach pc_ca = { + sizeof(struct pc_softc), pcprobe, pcattach +}; +struct cfdriver pc_cd = { + NULL, "pc", DV_TTY, NULL, 0 +}; + +int pmsprobe __P((struct device *, void *, void *)); +void pmsattach __P((struct device *, struct device *, void *)); +int pmsintr __P((void *)); + +struct cfattach pms_ca = { + sizeof(struct pms_softc), pmsprobe, pmsattach +}; +struct cfdriver pms_cd = { + NULL, "pms", DV_TTY, NULL, 0 +}; + +#define PMSUNIT(dev) (minor(dev)) + +#define COL 80 +#define ROW 25 +#define CHR 2 + +static unsigned int addr_6845 = MONO_BASE; + +char *sget __P((void)); +void sput __P((u_char *, int)); + +void pcstart(); +int pcparam(); +char partab[]; + +extern pcopen(dev_t, int, int, struct proc *); + +#define KBD_DELAY \ + DELAY(10); + +static inline int +kbd_wait_output() +{ + u_int i; + + for (i = 100000; i; i--) + if ((inb(KBSTATP) & KBS_IBF) == 0) { + KBD_DELAY; + return 1; + } + return 0; +} + +static inline int +kbd_wait_input() +{ + u_int i; + + for (i = 100000; i; i--) + if ((inb(KBSTATP) & KBS_DIB) != 0) { + KBD_DELAY; + return 1; + } + return 0; +} + +static inline void +kbd_flush_input() +{ + u_char c; + + while (c = inb(KBSTATP) & 0x03) + if ((c & KBS_DIB) == KBS_DIB) { + /* XXX - delay is needed to prevent some keyboards from + wedging when the system boots */ + delay(6); + (void) inb(KBDATAP); + } +} + + +#if 1 +/* + * Get the current command byte. + */ +static u_char +kbc_get8042cmd() +{ + + if (!kbd_wait_output()) + return -1; + outb(KBCMDP, K_RDCMDBYTE); + if (!kbd_wait_input()) + return -1; + return inb(KBDATAP); +} +#endif + +/* + * Pass command byte to keyboard controller (8042). + */ +static int +kbc_put8042cmd(val) + u_char val; +{ + + if (!kbd_wait_output()) + return 0; + outb(KBCMDP, K_LDCMDBYTE); + if (!kbd_wait_output()) + return 0; + outb(KBOUTP, val); + return 1; +} + +/* + * Pass command to keyboard itself + */ +int +kbd_cmd(val, polling) + u_char val; + u_char polling; +{ + u_int retries = 3; + register u_int i; + + if(!polling) { + i = spltty(); + if(kb_oq_get == kb_oq_put) { + outb(KBOUTP, val); + } + kb_oq[kb_oq_put] = val; + kb_oq_put = (kb_oq_put + 1) & 7; + splx(i); + return(1); + } + else do { + if (!kbd_wait_output()) + return 0; + outb(KBOUTP, val); + for (i = 100000; i; i--) { + if (inb(KBSTATP) & KBS_DIB) { + register u_char c; + + KBD_DELAY; + c = inb(KBDATAP); + if (c == KBR_ACK || c == KBR_ECHO) { + return 1; + } + if (c == KBR_RESEND) { + break; + } +#ifdef DIAGNOSTIC + printf("kbd_cmd: input char %x lost\n", c); +#endif + } + } + } while (--retries); + return 0; +} + +void +set_cursor_shape() +{ + register int iobase = addr_6845; + + outb(iobase, 10); + outb(iobase+1, cursor_shape >> 8); + outb(iobase, 11); + outb(iobase+1, cursor_shape); + old_cursor_shape = cursor_shape; +} + +void +get_cursor_shape() +{ + register int iobase = addr_6845; + + outb(iobase, 10); + cursor_shape = inb(iobase+1) << 8; + outb(iobase, 11); + cursor_shape |= inb(iobase+1); + + /* + * real 6845's, as found on, MDA, Hercules or CGA cards, do + * not support reading the cursor shape registers. the 6845 + * tri-states it's data bus. This is _normally_ read by the + * cpu as either 0x00 or 0xff.. in which case we just use + * a line cursor. + */ + if (cursor_shape == 0x0000 || cursor_shape == 0xffff) + cursor_shape = 0x0b10; + else + cursor_shape &= 0x1f1f; +} + +void +do_async_update(poll) + u_char poll; +{ + int pos; + static int old_pos = -1; + + async = 0; + + if (lock_state != old_lock_state) { + old_lock_state = lock_state; + if (!kbd_cmd(KBC_MODEIND, poll) || + !kbd_cmd(lock_state, poll)) { + printf("pc: timeout updating leds\n"); + (void) kbd_cmd(KBC_ENABLE, poll); + } + } + if (typematic_rate != old_typematic_rate) { + old_typematic_rate = typematic_rate; + if (!kbd_cmd(KBC_TYPEMATIC, poll) || + !kbd_cmd(typematic_rate, poll)) { + printf("pc: timeout updating typematic rate\n"); + (void) kbd_cmd(KBC_ENABLE, poll); + } + } + + if (pc_xmode > 0) + return; + + pos = crtat - Crtat; + if (pos != old_pos) { + register int iobase = addr_6845; + outb(iobase, 14); + outb(iobase+1, pos >> 8); + outb(iobase, 15); + outb(iobase+1, pos); + old_pos = pos; + } + if (cursor_shape != old_cursor_shape) + set_cursor_shape(); +} + +void +async_update() +{ + + if (kernel || polling) { + if (async) + untimeout(do_async_update, NULL); + do_async_update(1); + } else { + if (async) + return; + async = 1; + timeout(do_async_update, NULL, 1); + } +} + +/* + * these are both bad jokes + */ +int +pcprobe(parent, cfdata, aux) + struct device *parent; + void *cfdata, *aux; +{ + struct confargs *ca = aux; + u_int i; + + /* Make shure we're looking for this type of device */ + if(!BUS_MATCHNAME(ca, "pc")) + return(0); + + /* Enable interrupts and keyboard, etc. */ + if (!kbc_put8042cmd(CMDBYTE)) { + printf("pcprobe: command error\n"); + return 0; + } + +#if 1 + /* Flush any garbage. */ + kbd_flush_input(); + /* Reset the keyboard. */ + if (!kbd_cmd(KBC_RESET, 1)) { + printf("pcprobe: reset error %d\n", 1); + goto lose; + } + for (i = 600000; i; i--) + if ((inb(KBSTATP) & KBS_DIB) != 0) { + KBD_DELAY; + break; + } + if (i == 0 || inb(KBDATAP) != KBR_RSTDONE) { + printf("pcprobe: reset error %d\n", 2); + goto lose; + } + /* + * Some keyboards seem to leave a second ack byte after the reset. + * This is kind of stupid, but we account for them anyway by just + * flushing the buffer. + */ + kbd_flush_input(); + /* Just to be sure. */ + if (!kbd_cmd(KBC_ENABLE, 1)) { + printf("pcprobe: reset error %d\n", 3); + goto lose; + } + + /* + * Some keyboard/8042 combinations do not seem to work if the keyboard + * is set to table 1; in fact, it would appear that some keyboards just + * ignore the command altogether. So by default, we use the AT scan + * codes and have the 8042 translate them. Unfortunately, this is + * known to not work on some PS/2 machines. We try desparately to deal + * with this by checking the (lack of a) translate bit in the 8042 and + * attempting to set the keyboard to XT mode. If this all fails, well, + * tough luck. + * + * XXX It would perhaps be a better choice to just use AT scan codes + * and not bother with this. + */ + if (kbc_get8042cmd() & KC8_TRANS) { + /* The 8042 is translating for us; use AT codes. */ + if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(2, 1)) { + printf("pcprobe: reset error %d\n", 4); + goto lose; + } + } else { + /* Stupid 8042; set keyboard to XT codes. */ + if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(1, 1)) { + printf("pcprobe: reset error %d\n", 5); + goto lose; + } + } + +lose: + /* + * Technically, we should probably fail the probe. But we'll be nice + * and allow keyboard-less machines to boot with the console. + */ +#endif + + return 1; +} + +void +pcattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + struct pc_softc *sc = (void *)self; + + printf(": %s\n", vs.color ? "color" : "mono"); + do_async_update(1); + + BUS_INTR_ESTABLISH(ca, pcintr, (void *)(long)sc); +} + +int +pcopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct pc_softc *sc; + int unit = PCUNIT(dev); + struct tty *tp; + + if (unit >= pc_cd.cd_ndevs) + return ENXIO; + sc = pc_cd.cd_devs[unit]; + if (sc == 0) + return ENXIO; + + if (!sc->sc_tty) { + tp = sc->sc_tty = ttymalloc(); + } + else { + tp = sc->sc_tty; + } + + tp->t_oproc = pcstart; + tp->t_param = pcparam; + tp->t_dev = dev; + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + pcparam(tp, &tp->t_termios); + ttsetwater(tp); + } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) + return EBUSY; + tp->t_state |= TS_CARR_ON; + + return ((*linesw[tp->t_line].l_open)(dev, tp)); +} + +int +pcclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct pc_softc *sc = pc_cd.cd_devs[PCUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + (*linesw[tp->t_line].l_close)(tp, flag); + ttyclose(tp); +#ifdef notyet /* XXX */ + ttyfree(tp); +#endif + return(0); +} + +int +pcread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct pc_softc *sc = pc_cd.cd_devs[PCUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +pcwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct pc_softc *sc = pc_cd.cd_devs[PCUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +struct tty * +pctty(dev) + dev_t dev; +{ + struct pc_softc *sc = pc_cd.cd_devs[PCUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + return (tp); +} + +/* + * Got a console receive interrupt - + * the console processor wants to give us a character. + * Catch the character, and see who it goes to. + */ +int +pcintr(arg) + void *arg; +{ + struct pc_softc *sc = arg; + register struct tty *tp = sc->sc_tty; + u_char *cp; + + if ((inb(KBSTATP) & KBS_DIB) == 0) + return 0; + if (polling) + return 1; + do { + cp = sget(); + if (!tp || (tp->t_state & TS_ISOPEN) == 0) + return 1; + if (cp) + do + (*linesw[tp->t_line].l_rint)(*cp++, tp); + while (*cp); + } while (inb(KBSTATP) & KBS_DIB); + return 1; +} + +int +pcioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct pc_softc *sc = pc_cd.cd_devs[PCUNIT(dev)]; + struct tty *tp = sc->sc_tty; + int error; + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return error; + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return error; + + switch (cmd) { + case CONSOLE_X_MODE_ON: + pc_xmode_on(); + return 0; + case CONSOLE_X_MODE_OFF: + pc_xmode_off(); + return 0; + case CONSOLE_X_BELL: + /* + * If set, data is a pointer to a length 2 array of + * integers. data[0] is the pitch in Hz and data[1] + * is the duration in msec. + */ + if (data) + sysbeep(((int*)data)[0], + (((int*)data)[1] * hz) / 1000); + else + sysbeep(BEEP_FREQ, BEEP_TIME); + return 0; + case CONSOLE_SET_TYPEMATIC_RATE: { + u_char rate; + + if (!data) + return EINVAL; + rate = *((u_char *)data); + /* + * Check that it isn't too big (which would cause it to be + * confused with a command). + */ + if (rate & 0x80) + return EINVAL; + typematic_rate = rate; + async_update(); + return 0; + } + case CONSOLE_SET_KEYMAP: { + keymap_t *map = (keymap_t *) data; + int i; + + if (!data) + return EINVAL; + for (i = 0; i < KB_NUM_KEYS; i++) + if (map[i].unshift[KB_CODE_SIZE-1] || + map[i].shift[KB_CODE_SIZE-1] || + map[i].ctl[KB_CODE_SIZE-1] || + map[i].altgr[KB_CODE_SIZE-1] || + map[i].shift_altgr[KB_CODE_SIZE-1]) + return EINVAL; + + bcopy(data,scan_codes,sizeof(keymap_t[KB_NUM_KEYS])); + return 0; + } + case CONSOLE_GET_KEYMAP: + if (!data) + return EINVAL; + bcopy(scan_codes,data,sizeof(keymap_t[KB_NUM_KEYS])); + return 0; + + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("pcioctl: impossible"); +#endif +} + +void +pcstart(tp) + struct tty *tp; +{ + struct clist *cl; + int s, len, n; + u_char buf[PCBURST]; + + s = spltty(); + if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) + goto out; + tp->t_state |= TS_BUSY; + splx(s); + /* + * We need to do this outside spl since it could be fairly + * expensive and we don't want our serial ports to overflow. + */ + cl = &tp->t_outq; + len = q_to_b(cl, buf, PCBURST); + sput(buf, len); + s = spltty(); + tp->t_state &= ~TS_BUSY; + if (cl->c_cc) { + tp->t_state |= TS_TIMEOUT; + timeout(ttrstrt, tp, 1); + } + if (cl->c_cc <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup(cl); + } + selwakeup(&tp->t_wsel); + } +out: + splx(s); +} + +void +pcstop(tp, flag) + struct tty *tp; + int flag; +{ + +} + +void +pccnprobe(cp) + struct consdev *cp; +{ + int maj; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == pcopen) + break; + + /* initialize required fields */ + cp->cn_dev = makedev(maj, 0); + cp->cn_pri = CN_INTERNAL; +} + +/* ARGSUSED */ +void +pccninit(cp) + struct consdev *cp; +{ + + /* + * For now, don't screw with it. + */ + /* crtat = 0; */ +} + +/* ARGSUSED */ +void +pccnputc(dev, c) + dev_t dev; + char c; +{ + u_char oldkernel = kernel; + + kernel = 1; + if (c == '\n') + sput("\r\n", 2); + else + sput(&c, 1); + kernel = oldkernel; +} + +/* ARGSUSED */ +pccngetc(dev) + dev_t dev; +{ + register char *cp; + + if (pc_xmode > 0) + return 0; + + do { + /* wait for byte */ + while ((inb(KBSTATP) & KBS_DIB) == 0); + /* see if it's worthwhile */ + cp = sget(); + } while (!cp); + if (*cp == '\r') + return '\n'; + return *cp; +} + +void +pccnpollc(dev, on) + dev_t dev; + int on; +{ + + polling = on; + if (!on) { + int unit; + struct pc_softc *sc; + int s; + + /* + * If disabling polling on a device that's been configured, + * make sure there are no bytes left in the FIFO, holding up + * the interrupt line. Otherwise we won't get any further + * interrupts. + */ + unit = PCUNIT(dev); + if (pc_cd.cd_ndevs > unit) { + sc = pc_cd.cd_devs[unit]; + if (sc != 0) { + s = spltty(); + pcintr(sc); + splx(s); + } + } + } +} + +/* + * Set line parameters. + */ +int +pcparam(tp, t) + struct tty *tp; + struct termios *t; +{ + + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + return 0; +} + +#define wrtchar(c, at) do {\ + char *cp = (char *)crtat; *cp++ = (c); *cp = (at); crtat++; vs.col++; \ +} while (0) + +/* translate ANSI color codes to standard pc ones */ +static char fgansitopc[] = { + FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, + FG_MAGENTA, FG_CYAN, FG_LIGHTGREY +}; + +static char bgansitopc[] = { + BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, + BG_MAGENTA, BG_CYAN, BG_LIGHTGREY +}; + +static u_char iso2ibm437[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xff, 0xad, 0x9b, 0x9c, 0, 0x9d, 0, 0x40, + 0x6f, 0x63, 0x61, 0xae, 0, 0, 0, 0, + 0xf8, 0xf1, 0xfd, 0x33, 0, 0xe6, 0, 0xfa, + 0, 0x31, 0x6f, 0xaf, 0xac, 0xab, 0, 0xa8, + 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80, + 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + 0x81, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x4f, + 0x4f, 0x55, 0x55, 0x55, 0x9a, 0x59, 0, 0xe1, + 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87, + 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, + 0, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0x6f, + 0x6f, 0x97, 0xa3, 0x96, 0x81, 0x98, 0, 0 +}; + +/* + * `pc3' termcap emulation. + */ +void +sput(cp, n) + u_char *cp; + int n; +{ + u_char c, scroll = 0; + + if (pc_xmode > 0) + return; + + if (crtat == 0) { + u_short volatile *cp; + u_short was; + unsigned cursorat; + + cp = (u_short *)CGA_BUF; + was = *cp; + *cp = (u_short) 0xA55A; + if (*cp != 0xA55A) { + cp = (u_short *)MONO_BUF; + addr_6845 = MONO_BASE; + vs.color = 0; + } else { + *cp = was; + addr_6845 = CGA_BASE; + vs.color = 1; + } + + /* Extract cursor location */ + outb(addr_6845, 14); + cursorat = inb(addr_6845+1) << 8; + outb(addr_6845, 15); + cursorat |= inb(addr_6845+1); + +#ifdef FAT_CURSOR + cursor_shape = 0x0012; +#else + get_cursor_shape(); +#endif + + Crtat = (u_short *)cp; + crtat = (u_short *)(cp + cursorat); + + vs.ncol = COL; + vs.nrow = ROW; + vs.nchr = COL * ROW; + vs.at = FG_LIGHTGREY | BG_BLACK; + + if (vs.color == 0) + vs.so_at = FG_BLACK | BG_LIGHTGREY; + else + vs.so_at = FG_YELLOW | BG_BLACK; + + fillw((vs.at << 8) | ' ', crtat, vs.nchr - cursorat); + } + + while (n--) { + if (!(c = *cp++)) + continue; + + switch (c) { + case 0x1B: + if (vs.state >= VSS_ESCAPE) { + wrtchar(c, vs.so_at); + vs.state = 0; + goto maybe_scroll; + } else + vs.state = VSS_ESCAPE; + break; + + case '\t': { + int inccol = 8 - (vs.col & 7); + crtat += inccol; + vs.col += inccol; + } + maybe_scroll: + if (vs.col >= COL) { + vs.col -= COL; + scroll = 1; + } + break; + + case '\010': + if (crtat <= Crtat) + break; + --crtat; + if (--vs.col < 0) + vs.col += COL; /* non-destructive backspace */ + break; + + case '\r': + crtat -= vs.col; + vs.col = 0; + break; + + case '\n': + crtat += vs.ncol; + scroll = 1; + break; + + default: + bypass: + switch (vs.state) { + case 0: + if (c == '\a') + sysbeep(BEEP_FREQ, BEEP_TIME); + else { + /* + * If we're outputting multiple printed + * characters, just blast them to the + * screen until we reach the end of the + * buffer or a control character. This + * saves time by short-circuiting the + * switch. + * If we reach the end of the line, we + * break to do a scroll check. + */ + for (;;) { + if (c & 0x80) + c = iso2ibm437[c&0x7f]; + + if (vs.so) + wrtchar(c, vs.so_at); + else + wrtchar(c, vs.at); + if (vs.col >= vs.ncol) { + vs.col = 0; + scroll = 1; + break; + } + if (!n || (c = *cp) < ' ') + break; + n--, cp++; + } + } + break; + case VSS_ESCAPE: + if (c == '[') { /* Start ESC [ sequence */ + vs.cx = vs.cy = 0; + vs.state = VSS_EBRACE; + } else if (c == 'c') { /* Clear screen & home */ + fillw((vs.at << 8) | ' ', Crtat, + vs.nchr); + crtat = Crtat; + vs.col = 0; + vs.state = 0; + } else { /* Invalid, clear state */ + wrtchar(c, vs.so_at); + vs.state = 0; + goto maybe_scroll; + } + break; + default: /* VSS_EBRACE or VSS_EPARAM */ + switch (c) { + int pos; + case 'm': + if (!vs.cx) + vs.so = 0; + else + vs.so = 1; + vs.state = 0; + break; + case 'A': { /* back cx rows */ + int cx = vs.cx; + if (cx <= 0) + cx = 1; + else + cx %= vs.nrow; + pos = crtat - Crtat; + pos -= vs.ncol * cx; + if (pos < 0) + pos += vs.nchr; + crtat = Crtat + pos; + vs.state = 0; + break; + } + case 'B': { /* down cx rows */ + int cx = vs.cx; + if (cx <= 0) + cx = 1; + else + cx %= vs.nrow; + pos = crtat - Crtat; + pos += vs.ncol * cx; + if (pos >= vs.nchr) + pos -= vs.nchr; + crtat = Crtat + pos; + vs.state = 0; + break; + } + case 'C': { /* right cursor */ + int cx = vs.cx, + col = vs.col; + if (cx <= 0) + cx = 1; + else + cx %= vs.ncol; + pos = crtat - Crtat; + pos += cx; + col += cx; + if (col >= vs.ncol) { + pos -= vs.ncol; + col -= vs.ncol; + } + vs.col = col; + crtat = Crtat + pos; + vs.state = 0; + break; + } + case 'D': { /* left cursor */ + int cx = vs.cx, + col = vs.col; + if (cx <= 0) + cx = 1; + else + cx %= vs.ncol; + pos = crtat - Crtat; + pos -= cx; + col -= cx; + if (col < 0) { + pos += vs.ncol; + col += vs.ncol; + } + vs.col = col; + crtat = Crtat + pos; + vs.state = 0; + break; + } + case 'J': /* Clear ... */ + switch (vs.cx) { + case 0: + /* ... to end of display */ + fillw((vs.at << 8) | ' ', crtat, + Crtat + vs.nchr - crtat); + break; + case 1: + /* ... to next location */ + fillw((vs.at << 8) | ' ', Crtat, + crtat - Crtat + 1); + break; + case 2: + /* ... whole display */ + fillw((vs.at << 8) | ' ', Crtat, + vs.nchr); + break; + } + vs.state = 0; + break; + case 'K': /* Clear line ... */ + switch (vs.cx) { + case 0: + /* ... current to EOL */ + fillw((vs.at << 8) | ' ', crtat, + vs.ncol - vs.col); + break; + case 1: + /* ... beginning to next */ + fillw((vs.at << 8) | ' ', + crtat - vs.col, + vs.col + 1); + break; + case 2: + /* ... entire line */ + fillw((vs.at << 8) | ' ', + crtat - vs.col, vs.ncol); + break; + } + vs.state = 0; + break; + case 'f': /* in system V consoles */ + case 'H': { /* Cursor move */ + int cx = vs.cx, + cy = vs.cy; + if (!cx || !cy) { + crtat = Crtat; + vs.col = 0; + } else { + if (cx > vs.nrow) + cx = vs.nrow; + if (cy > vs.ncol) + cy = vs.ncol; + crtat = Crtat + + (cx - 1) * vs.ncol + cy - 1; + vs.col = cy - 1; + } + vs.state = 0; + break; + } + case 'M': { /* delete cx rows */ + u_short *crtAt = crtat - vs.col; + int cx = vs.cx, + row = (crtAt - Crtat) / vs.ncol, + nrow = vs.nrow - row; + if (cx <= 0) + cx = 1; + else if (cx > nrow) + cx = nrow; + if (cx < nrow) + bcopy(crtAt + vs.ncol * cx, + crtAt, vs.ncol * (nrow - + cx) * CHR); + fillw((vs.at << 8) | ' ', + crtAt + vs.ncol * (nrow - cx), + vs.ncol * cx); + vs.state = 0; + break; + } + case 'S': { /* scroll up cx lines */ + int cx = vs.cx; + if (cx <= 0) + cx = 1; + else if (cx > vs.nrow) + cx = vs.nrow; + if (cx < vs.nrow) + bcopy(Crtat + vs.ncol * cx, + Crtat, vs.ncol * (vs.nrow - + cx) * CHR); + fillw((vs.at << 8) | ' ', + Crtat + vs.ncol * (vs.nrow - cx), + vs.ncol * cx); + /* crtat -= vs.ncol * cx; /* XXX */ + vs.state = 0; + break; + } + case 'L': { /* insert cx rows */ + u_short *crtAt = crtat - vs.col; + int cx = vs.cx, + row = (crtAt - Crtat) / vs.ncol, + nrow = vs.nrow - row; + if (cx <= 0) + cx = 1; + else if (cx > nrow) + cx = nrow; + if (cx < nrow) + bcopy(crtAt, + crtAt + vs.ncol * cx, + vs.ncol * (nrow - cx) * + CHR); + fillw((vs.at << 8) | ' ', crtAt, + vs.ncol * cx); + vs.state = 0; + break; + } + case 'T': { /* scroll down cx lines */ + int cx = vs.cx; + if (cx <= 0) + cx = 1; + else if (cx > vs.nrow) + cx = vs.nrow; + if (cx < vs.nrow) + bcopy(Crtat, + Crtat + vs.ncol * cx, + vs.ncol * (vs.nrow - cx) * + CHR); + fillw((vs.at << 8) | ' ', Crtat, + vs.ncol * cx); + /* crtat += vs.ncol * cx; /* XXX */ + vs.state = 0; + break; + } + case ';': /* Switch params in cursor def */ + vs.state = VSS_EPARAM; + break; + case 'r': + vs.so_at = (vs.cx & FG_MASK) | + ((vs.cy << 4) & BG_MASK); + vs.state = 0; + break; + case 'x': /* set attributes */ + switch (vs.cx) { + case 0: + vs.at = FG_LIGHTGREY | BG_BLACK; + break; + case 1: + /* ansi background */ + if (!vs.color) + break; + vs.at &= FG_MASK; + vs.at |= bgansitopc[vs.cy & 7]; + break; + case 2: + /* ansi foreground */ + if (!vs.color) + break; + vs.at &= BG_MASK; + vs.at |= fgansitopc[vs.cy & 7]; + break; + case 3: + /* pc text attribute */ + if (vs.state >= VSS_EPARAM) + vs.at = vs.cy; + break; + } + vs.state = 0; + break; + + default: /* Only numbers valid here */ + if ((c >= '0') && (c <= '9')) { + if (vs.state >= VSS_EPARAM) { + vs.cy *= 10; + vs.cy += c - '0'; + } else { + vs.cx *= 10; + vs.cx += c - '0'; + } + } else + vs.state = 0; + break; + } + break; + } + } + if (scroll) { + scroll = 0; + /* scroll check */ + if (crtat >= Crtat + vs.nchr) { + if (!kernel) { + int s = spltty(); + if (lock_state & KB_SCROLL) + tsleep((caddr_t)&lock_state, + PUSER, "pcputc", 0); + splx(s); + } + bcopy(Crtat + vs.ncol, Crtat, + (vs.nchr - vs.ncol) * CHR); + fillw((vs.at << 8) | ' ', + Crtat + vs.nchr - vs.ncol, vs.ncol); + crtat -= vs.ncol; + } + } + } + async_update(); +} + +static keymap_t scan_codes[KB_NUM_KEYS] = { +/* type unshift shift control altgr shift_altgr scancode */ + KB_NONE, "", "", "", "", "", /* 0 unused */ + KB_ASCII, "\033", "\033", "\033", "", "", /* 1 ESCape */ + KB_ASCII, "1", "!", "!", "", "", /* 2 1 */ + KB_ASCII, "2", "@", "\000", "", "", /* 3 2 */ + KB_ASCII, "3", "#", "#", "", "", /* 4 3 */ + KB_ASCII, "4", "$", "$", "", "", /* 5 4 */ + KB_ASCII, "5", "%", "%", "", "", /* 6 5 */ + KB_ASCII, "6", "^", "\036", "", "", /* 7 6 */ + KB_ASCII, "7", "&", "&", "", "", /* 8 7 */ + KB_ASCII, "8", "*", "\010", "", "", /* 9 8 */ + KB_ASCII, "9", "(", "(", "", "", /* 10 9 */ + KB_ASCII, "0", ")", ")", "", "", /* 11 0 */ + KB_ASCII, "-", "_", "\037", "", "", /* 12 - */ + KB_ASCII, "=", "+", "+", "", "", /* 13 = */ + KB_ASCII, "\177", "\177", "\010", "", "", /* 14 backspace */ + KB_ASCII, "\t", "\t", "\t", "", "", /* 15 tab */ + KB_ASCII, "q", "Q", "\021", "", "", /* 16 q */ + KB_ASCII, "w", "W", "\027", "", "", /* 17 w */ + KB_ASCII, "e", "E", "\005", "", "", /* 18 e */ + KB_ASCII, "r", "R", "\022", "", "", /* 19 r */ + KB_ASCII, "t", "T", "\024", "", "", /* 20 t */ + KB_ASCII, "y", "Y", "\031", "", "", /* 21 y */ + KB_ASCII, "u", "U", "\025", "", "", /* 22 u */ + KB_ASCII, "i", "I", "\011", "", "", /* 23 i */ + KB_ASCII, "o", "O", "\017", "", "", /* 24 o */ + KB_ASCII, "p", "P", "\020", "", "", /* 25 p */ + KB_ASCII, "[", "{", "\033", "", "", /* 26 [ */ + KB_ASCII, "]", "}", "\035", "", "", /* 27 ] */ + KB_ASCII, "\r", "\r", "\n", "", "", /* 28 return */ + KB_CTL, "", "", "", "", "", /* 29 control */ + KB_ASCII, "a", "A", "\001", "", "", /* 30 a */ + KB_ASCII, "s", "S", "\023", "", "", /* 31 s */ + KB_ASCII, "d", "D", "\004", "", "", /* 32 d */ + KB_ASCII, "f", "F", "\006", "", "", /* 33 f */ + KB_ASCII, "g", "G", "\007", "", "", /* 34 g */ + KB_ASCII, "h", "H", "\010", "", "", /* 35 h */ + KB_ASCII, "j", "J", "\n", "", "", /* 36 j */ + KB_ASCII, "k", "K", "\013", "", "", /* 37 k */ + KB_ASCII, "l", "L", "\014", "", "", /* 38 l */ + KB_ASCII, ";", ":", ";", "", "", /* 39 ; */ + KB_ASCII, "'", "\"", "'", "", "", /* 40 ' */ + KB_ASCII, "`", "~", "`", "", "", /* 41 ` */ + KB_SHIFT, "", "", "", "", "", /* 42 shift */ + KB_ASCII, "\\", "|", "\034", "", "", /* 43 \ */ + KB_ASCII, "z", "Z", "\032", "", "", /* 44 z */ + KB_ASCII, "x", "X", "\030", "", "", /* 45 x */ + KB_ASCII, "c", "C", "\003", "", "", /* 46 c */ + KB_ASCII, "v", "V", "\026", "", "", /* 47 v */ + KB_ASCII, "b", "B", "\002", "", "", /* 48 b */ + KB_ASCII, "n", "N", "\016", "", "", /* 49 n */ + KB_ASCII, "m", "M", "\r", "", "", /* 50 m */ + KB_ASCII, ",", "<", "<", "", "", /* 51 , */ + KB_ASCII, ".", ">", ">", "", "", /* 52 . */ + KB_ASCII, "/", "?", "\037", "", "", /* 53 / */ + KB_SHIFT, "", "", "", "", "", /* 54 shift */ + KB_KP, "*", "*", "*", "", "", /* 55 kp * */ + KB_ALT, "", "", "", "", "", /* 56 alt */ + KB_ASCII, " ", " ", "\000", "", "", /* 57 space */ + KB_CAPS, "", "", "", "", "", /* 58 caps */ + KB_FUNC, "\033[M", "\033[Y", "\033[k", "", "", /* 59 f1 */ + KB_FUNC, "\033[N", "\033[Z", "\033[l", "", "", /* 60 f2 */ + KB_FUNC, "\033[O", "\033[a", "\033[m", "", "", /* 61 f3 */ + KB_FUNC, "\033[P", "\033[b", "\033[n", "", "", /* 62 f4 */ + KB_FUNC, "\033[Q", "\033[c", "\033[o", "", "", /* 63 f5 */ + KB_FUNC, "\033[R", "\033[d", "\033[p", "", "", /* 64 f6 */ + KB_FUNC, "\033[S", "\033[e", "\033[q", "", "", /* 65 f7 */ + KB_FUNC, "\033[T", "\033[f", "\033[r", "", "", /* 66 f8 */ + KB_FUNC, "\033[U", "\033[g", "\033[s", "", "", /* 67 f9 */ + KB_FUNC, "\033[V", "\033[h", "\033[t", "", "", /* 68 f10 */ + KB_NUM, "", "", "", "", "", /* 69 num lock */ + KB_SCROLL, "", "", "", "", "", /* 70 scroll lock */ + KB_KP, "7", "\033[H", "7", "", "", /* 71 kp 7 */ + KB_KP, "8", "\033[A", "8", "", "", /* 72 kp 8 */ + KB_KP, "9", "\033[I", "9", "", "", /* 73 kp 9 */ + KB_KP, "-", "-", "-", "", "", /* 74 kp - */ + KB_KP, "4", "\033[D", "4", "", "", /* 75 kp 4 */ + KB_KP, "5", "\033[E", "5", "", "", /* 76 kp 5 */ + KB_KP, "6", "\033[C", "6", "", "", /* 77 kp 6 */ + KB_KP, "+", "+", "+", "", "", /* 78 kp + */ + KB_KP, "1", "\033[F", "1", "", "", /* 79 kp 1 */ + KB_KP, "2", "\033[B", "2", "", "", /* 80 kp 2 */ + KB_KP, "3", "\033[G", "3", "", "", /* 81 kp 3 */ + KB_KP, "0", "\033[L", "0", "", "", /* 82 kp 0 */ + KB_KP, ",", "\177", ",", "", "", /* 83 kp , */ + KB_NONE, "", "", "", "", "", /* 84 0 */ + KB_NONE, "", "", "", "", "", /* 85 0 */ + KB_NONE, "", "", "", "", "", /* 86 0 */ + KB_FUNC, "\033[W", "\033[i", "\033[u", "", "", /* 87 f11 */ + KB_FUNC, "\033[X", "\033[j", "\033[v", "", "", /* 88 f12 */ + KB_NONE, "", "", "", "", "", /* 89 0 */ + KB_NONE, "", "", "", "", "", /* 90 0 */ + KB_NONE, "", "", "", "", "", /* 91 0 */ + KB_NONE, "", "", "", "", "", /* 92 0 */ + KB_NONE, "", "", "", "", "", /* 93 0 */ + KB_NONE, "", "", "", "", "", /* 94 0 */ + KB_NONE, "", "", "", "", "", /* 95 0 */ + KB_NONE, "", "", "", "", "", /* 96 0 */ + KB_NONE, "", "", "", "", "", /* 97 0 */ + KB_NONE, "", "", "", "", "", /* 98 0 */ + KB_NONE, "", "", "", "", "", /* 99 0 */ + KB_NONE, "", "", "", "", "", /* 100 */ + KB_NONE, "", "", "", "", "", /* 101 */ + KB_NONE, "", "", "", "", "", /* 102 */ + KB_NONE, "", "", "", "", "", /* 103 */ + KB_NONE, "", "", "", "", "", /* 104 */ + KB_NONE, "", "", "", "", "", /* 105 */ + KB_NONE, "", "", "", "", "", /* 106 */ + KB_NONE, "", "", "", "", "", /* 107 */ + KB_NONE, "", "", "", "", "", /* 108 */ + KB_NONE, "", "", "", "", "", /* 109 */ + KB_NONE, "", "", "", "", "", /* 110 */ + KB_NONE, "", "", "", "", "", /* 111 */ + KB_NONE, "", "", "", "", "", /* 112 */ + KB_NONE, "", "", "", "", "", /* 113 */ + KB_NONE, "", "", "", "", "", /* 114 */ + KB_NONE, "", "", "", "", "", /* 115 */ + KB_NONE, "", "", "", "", "", /* 116 */ + KB_NONE, "", "", "", "", "", /* 117 */ + KB_NONE, "", "", "", "", "", /* 118 */ + KB_NONE, "", "", "", "", "", /* 119 */ + KB_NONE, "", "", "", "", "", /* 120 */ + KB_NONE, "", "", "", "", "", /* 121 */ + KB_NONE, "", "", "", "", "", /* 122 */ + KB_NONE, "", "", "", "", "", /* 123 */ + KB_NONE, "", "", "", "", "", /* 124 */ + KB_NONE, "", "", "", "", "", /* 125 */ + KB_NONE, "", "", "", "", "", /* 126 */ + KB_NONE, "", "", "", "", "" /* 127 */ +}; + +/* + * Get characters from the keyboard. If none are present, return NULL. + */ +char * +sget() +{ + u_char dt; + static u_char extended = 0, shift_state = 0; + static u_char capchar[2]; + +top: + KBD_DELAY; + dt = inb(KBDATAP); + + switch (dt) { + case KBR_ACK: case KBR_ECHO: + kb_oq_get = (kb_oq_get + 1) & 7; + if(kb_oq_get != kb_oq_put) { + outb(KBOUTP, kb_oq[kb_oq_get]); + } + goto loop; + case KBR_RESEND: + outb(KBOUTP, kb_oq[kb_oq_get]); + goto loop; + } + + if (pc_xmode > 0) { +#if defined(DDB) && defined(XSERVER_DDB) + /* F12 enters the debugger while in X mode */ + if (dt == 88) + Debugger(); +#endif + capchar[0] = dt; + capchar[1] = 0; + /* + * Check for locking keys. + * + * XXX Setting the LEDs this way is a bit bogus. What if the + * keyboard has been remapped in X? + */ + switch (scan_codes[dt & 0x7f].type) { + case KB_NUM: + if (dt & 0x80) { + shift_state &= ~KB_NUM; + break; + } + if (shift_state & KB_NUM) + break; + shift_state |= KB_NUM; + lock_state ^= KB_NUM; + async_update(); + break; + case KB_CAPS: + if (dt & 0x80) { + shift_state &= ~KB_CAPS; + break; + } + if (shift_state & KB_CAPS) + break; + shift_state |= KB_CAPS; + lock_state ^= KB_CAPS; + async_update(); + break; + case KB_SCROLL: + if (dt & 0x80) { + shift_state &= ~KB_SCROLL; + break; + } + if (shift_state & KB_SCROLL) + break; + shift_state |= KB_SCROLL; + lock_state ^= KB_SCROLL; + if ((lock_state & KB_SCROLL) == 0) + wakeup((caddr_t)&lock_state); + async_update(); + break; + } + return capchar; + } + + switch (dt) { + case KBR_EXTENDED: + extended = 1; + goto loop; + } + +#ifdef DEBUG + /* + * Check for cntl-alt-esc. + */ + if ((dt == 1) && (shift_state & (KB_CTL | KB_ALT)) == (KB_CTL | KB_ALT)) { + mdbpanic(); + dt |= 0x80; /* discard esc (ddb discarded ctl-alt) */ + } +#endif + + /* + * Check for make/break. + */ + if (dt & 0x80) { + /* + * break + */ + dt &= 0x7f; + switch (scan_codes[dt].type) { + case KB_NUM: + shift_state &= ~KB_NUM; + break; + case KB_CAPS: + shift_state &= ~KB_CAPS; + break; + case KB_SCROLL: + shift_state &= ~KB_SCROLL; + break; + case KB_SHIFT: + shift_state &= ~KB_SHIFT; + break; + case KB_ALT: + if (extended) + shift_state &= ~KB_ALTGR; + else + shift_state &= ~KB_ALT; + break; + case KB_CTL: + shift_state &= ~KB_CTL; + break; + } + } else { + /* + * make + */ + switch (scan_codes[dt].type) { + /* + * locking keys + */ + case KB_NUM: + if (shift_state & KB_NUM) + break; + shift_state |= KB_NUM; + lock_state ^= KB_NUM; + async_update(); + break; + case KB_CAPS: + if (shift_state & KB_CAPS) + break; + shift_state |= KB_CAPS; + lock_state ^= KB_CAPS; + async_update(); + break; + case KB_SCROLL: + if (shift_state & KB_SCROLL) + break; + shift_state |= KB_SCROLL; + lock_state ^= KB_SCROLL; + if ((lock_state & KB_SCROLL) == 0) + wakeup((caddr_t)&lock_state); + async_update(); + break; + /* + * non-locking keys + */ + case KB_SHIFT: + shift_state |= KB_SHIFT; + break; + case KB_ALT: + if (extended) + shift_state |= KB_ALTGR; + else + shift_state |= KB_ALT; + break; + case KB_CTL: + shift_state |= KB_CTL; + break; + case KB_ASCII: + /* control has highest priority */ + if (shift_state & KB_CTL) + capchar[0] = scan_codes[dt].ctl[0]; + else if (shift_state & KB_ALTGR) { + if (shift_state & KB_SHIFT) + capchar[0] = scan_codes[dt].shift_altgr[0]; + else + capchar[0] = scan_codes[dt].altgr[0]; + } + else { + if (shift_state & KB_SHIFT) + capchar[0] = scan_codes[dt].shift[0]; + else + capchar[0] = scan_codes[dt].unshift[0]; + } + if ((lock_state & KB_CAPS) && capchar[0] >= 'a' && + capchar[0] <= 'z') { + capchar[0] -= ('a' - 'A'); + } + capchar[0] |= (shift_state & KB_ALT); + extended = 0; + return capchar; + case KB_NONE: +printf("keycode %d\n",dt); + break; + case KB_FUNC: { + char *more_chars; + if (shift_state & KB_SHIFT) + more_chars = scan_codes[dt].shift; + else if (shift_state & KB_CTL) + more_chars = scan_codes[dt].ctl; + else + more_chars = scan_codes[dt].unshift; + extended = 0; + return more_chars; + } + case KB_KP: { + char *more_chars; + if (shift_state & (KB_SHIFT | KB_CTL) || + (lock_state & KB_NUM) == 0 || extended) + more_chars = scan_codes[dt].shift; + else + more_chars = scan_codes[dt].unshift; + extended = 0; + return more_chars; + } + } + } + + extended = 0; +loop: + if ((inb(KBSTATP) & KBS_DIB) == 0) + return 0; + goto top; +} + +int +pcmmap(dev, offset, nprot) + dev_t dev; + int offset; + int nprot; +{ + + if (offset >= 0xa0000 && offset < 0xc0000) + return pica_btop(PICA_P_LOCAL_VIDEO + offset); + if (offset >= 0x0000 && offset < 0x10000) + return pica_btop(PICA_P_LOCAL_VIDEO_CTRL + offset); + if (offset >= 0x40000000 && offset < 0x40800000) + return pica_btop(PICA_P_LOCAL_VIDEO + offset - 0x40000000); + return -1; +} + +pc_xmode_on() +{ + if (pc_xmode) + return; + pc_xmode = 1; + +#ifdef XFREE86_BUG_COMPAT + /* If still unchanged, get current shape. */ + if (cursor_shape == 0xffff) + get_cursor_shape(); +#endif +} + +pc_xmode_off() +{ + if (pc_xmode == 0) + return; + pc_xmode = 0; + +#ifdef XFREE86_BUG_COMPAT + /* XXX It would be hard to justify why the X server doesn't do this. */ + set_cursor_shape(); +#endif + async_update(); +} +/* $NetBSD: pms.c,v 1.21 1995/04/18 02:25:18 mycroft Exp $ */ + +#include <machine/mouse.h> + +/* status bits */ +#define PMS_OBUF_FULL 0x01 +#define PMS_IBUF_FULL 0x02 + +/* controller commands */ +#define PMS_INT_ENABLE 0x47 /* enable controller interrupts */ +#define PMS_INT_DISABLE 0x65 /* disable controller interrupts */ +#define PMS_AUX_ENABLE 0xa7 /* enable auxiliary port */ +#define PMS_AUX_DISABLE 0xa8 /* disable auxiliary port */ +#define PMS_MAGIC_1 0xa9 /* XXX */ + +#define PMS_8042_CMD 0x65 + +/* mouse commands */ +#define PMS_SET_SCALE11 0xe6 /* set scaling 1:1 */ +#define PMS_SET_SCALE21 0xe7 /* set scaling 2:1 */ +#define PMS_SET_RES 0xe8 /* set resolution */ +#define PMS_GET_SCALE 0xe9 /* get scaling factor */ +#define PMS_SET_STREAM 0xea /* set streaming mode */ +#define PMS_SET_SAMPLE 0xf3 /* set sampling rate */ +#define PMS_DEV_ENABLE 0xf4 /* mouse on */ +#define PMS_DEV_DISABLE 0xf5 /* mouse off */ +#define PMS_RESET 0xff /* reset */ + +#define PMS_CHUNK 128 /* chunk size for read */ +#define PMS_BSIZE 1020 /* buffer size */ + + +static inline void +pms_dev_cmd(value) + u_char value; +{ + kbd_flush_input(); + outb(KBCMDP, 0xd4); + kbd_flush_input(); + outb(KBDATAP, value); +} + +static inline void +pms_aux_cmd(value) + u_char value; +{ + kbd_flush_input(); + outb(KBCMDP, value); +} + +static inline void +pms_pit_cmd(value) + u_char value; +{ + kbd_flush_input(); + outb(KBCMDP, 0x60); + kbd_flush_input(); + outb(KBDATAP, value); +} + +int +pmsprobe(parent, probe, aux) + struct device *parent; + void *probe, *aux; +{ + struct confargs *ca = aux; + u_char x; + + /* Make shure we're looking for this type of device */ + if(!BUS_MATCHNAME(ca, "pms")) + return(0); + + pms_dev_cmd(KBC_RESET); + pms_aux_cmd(PMS_MAGIC_1); + delay(10000); + x = inb(KBDATAP); + pms_pit_cmd(PMS_INT_DISABLE); + if (x & 0x04) + return 0; + + return 1; +} + +void +pmsattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pms_softc *sc = (void *)self; + struct confargs *ca = aux; + + printf("\n"); + + /* Other initialization was done by pmsprobe. */ + sc->sc_state = 0; + + BUS_INTR_ESTABLISH(ca, pmsintr, (void *)(long)sc); +} + +int +pmsopen(dev, flag) + dev_t dev; + int flag; +{ + int unit = PMSUNIT(dev); + struct pms_softc *sc; + + if (unit >= pms_cd.cd_ndevs) + return ENXIO; + sc = pms_cd.cd_devs[unit]; + if (!sc) + return ENXIO; + + if (sc->sc_state & PMS_OPEN) + return EBUSY; + + if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1) + return ENOMEM; + + sc->sc_state |= PMS_OPEN; + sc->sc_status = 0; + sc->sc_x = sc->sc_y = 0; + + /* Enable interrupts. */ + pms_dev_cmd(PMS_DEV_ENABLE); + pms_aux_cmd(PMS_AUX_ENABLE); + pms_dev_cmd(PMS_SET_RES); + pms_dev_cmd(3); /* 8 counts/mm */ + pms_dev_cmd(PMS_SET_SCALE21); +#if 0 + pms_dev_cmd(PMS_SET_SAMPLE); + pms_dev_cmd(100); /* 100 samples/sec */ + pms_dev_cmd(PMS_SET_STREAM); +#endif + pms_pit_cmd(PMS_INT_ENABLE); + + return 0; +} + +int +pmsclose(dev, flag) + dev_t dev; + int flag; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + + /* Disable interrupts. */ + pms_dev_cmd(PMS_DEV_DISABLE); + pms_pit_cmd(PMS_INT_DISABLE); + pms_aux_cmd(PMS_AUX_DISABLE); + + sc->sc_state &= ~PMS_OPEN; + + clfree(&sc->sc_q); + + return 0; +} + +int +pmsread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + int s; + int error; + size_t length; + u_char buffer[PMS_CHUNK]; + + /* Block until mouse activity occured. */ + + s = spltty(); + while (sc->sc_q.c_cc == 0) { + if (flag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + sc->sc_state |= PMS_ASLP; + if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "pmsrea", 0)) { + sc->sc_state &= ~PMS_ASLP; + splx(s); + return error; + } + } + splx(s); + + /* Transfer as many chunks as possible. */ + + while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { + length = min(sc->sc_q.c_cc, uio->uio_resid); + if (length > sizeof(buffer)) + length = sizeof(buffer); + + /* Remove a small chunk from the input queue. */ + (void) q_to_b(&sc->sc_q, buffer, length); + + /* Copy the data to the user process. */ + if (error = uiomove(buffer, length, uio)) + break; + } + + return error; +} + +int +pmsioctl(dev, cmd, addr, flag) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + struct mouseinfo info; + int s; + int error; + + switch (cmd) { + case MOUSEIOCREAD: + s = spltty(); + + info.status = sc->sc_status; + if (sc->sc_x || sc->sc_y) + info.status |= MOVEMENT; + + if (sc->sc_x > 127) + info.xmotion = 127; + else if (sc->sc_x < -127) + /* Bounding at -127 avoids a bug in XFree86. */ + info.xmotion = -127; + else + info.xmotion = sc->sc_x; + + if (sc->sc_y > 127) + info.ymotion = 127; + else if (sc->sc_y < -127) + info.ymotion = -127; + else + info.ymotion = sc->sc_y; + + /* Reset historical information. */ + sc->sc_x = sc->sc_y = 0; + sc->sc_status &= ~BUTCHNGMASK; + ndflush(&sc->sc_q, sc->sc_q.c_cc); + + splx(s); + error = copyout(&info, addr, sizeof(struct mouseinfo)); + break; + + default: + error = EINVAL; + break; + } + + return error; +} + +/* Masks for the first byte of a packet */ +#define PS2LBUTMASK 0x01 +#define PS2RBUTMASK 0x02 +#define PS2MBUTMASK 0x04 + +int +pmsintr(arg) + void *arg; +{ + struct pms_softc *sc = arg; + static int state = 0; + static u_char buttons; + u_char changed; + u_char mbutt; + static char dx, dy; + u_char buffer[5]; + + if ((sc->sc_state & PMS_OPEN) == 0) { + /* Interrupts are not expected. Discard the byte. */ + kbd_flush_input(); + return 0; + } + + switch (state) { + + case 0: + buttons = inb(KBDATAP); + if ((buttons & 0xc0) == 0) + ++state; + break; + + case 1: + dx = inb(KBDATAP); + /* Bounding at -127 avoids a bug in XFree86. */ + dx = (dx == -128) ? -127 : dx; + ++state; + break; + + case 2: + dy = inb(KBDATAP); + dy = (dy == -128) ? -127 : dy; + state = 0; + + mbutt = buttons; + buttons = ((buttons & PS2LBUTMASK) << 2) | + ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1); + changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3; + sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; + + if (dx || dy || changed) { + /* Update accumulated movements. */ + sc->sc_x += dx; + sc->sc_y += dy; + + /* Add this event to the queue. */ + buffer[0] = 0x80 | (mbutt & BUTSTATMASK); + if(dx < 0) + buffer[0] |= 0x10; + buffer[1] = dx & 0x7f; + if(dy < 0) + buffer[0] |= 0x20; + buffer[2] = dy & 0x7f; + buffer[3] = buffer[4] = 0; + (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); + + if (sc->sc_state & PMS_ASLP) { + sc->sc_state &= ~PMS_ASLP; + wakeup((caddr_t)sc); + } + selwakeup(&sc->sc_rsel); + } + + break; + } + + return -1; +} + +int +pmsselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + int s; + int ret; + + if (rw == FWRITE) + return 0; + + s = spltty(); + if (!sc->sc_q.c_cc) { + selrecord(p, &sc->sc_rsel); + ret = 0; + } else + ret = 1; + splx(s); + + return ret; +} diff --git a/sys/arch/arc/dev/scsi.h b/sys/arch/arc/dev/scsi.h new file mode 100644 index 00000000000..963617add65 --- /dev/null +++ b/sys/arch/arc/dev/scsi.h @@ -0,0 +1,559 @@ +/* $OpenBSD: scsi.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ */ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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 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 + * SUCH DAMAGE. + * + * from: @(#)scsi.h 8.1 (Berkeley) 6/10/93 + * $Id: scsi.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ + * + * scsi.h -- + * + * Common declarations for SCSI command formaters. This file only covers + * definitions pertaining to the SCSI common command set that are + * common to all SCSI device types (ie disk, tapes, WORM, printers, etc). + * Some of the references from the proceedings of the + * 1984 Mini/Micro Northeast conference might help in understanding SCSI. + * + * from: Header: /sprite/src/kernel/dev/RCS/scsi.h, + * v 9.1 90/02/13 23:11:24 jhh Exp SPRITE (Berkeley) + * $Id: scsi.h,v 1.1 1996/06/24 09:07:19 pefo Exp $ + */ + +#ifndef _SCSI_H +#define _SCSI_H + +/* + * "Standard" SCSI Commands. + * SCSI commands are divided into 8 groups as follows: + * Group0 (0x00 - 0x1f). Basic commands. 6 bytes long + * Group1 (0x20 - 0x3f). Extended command. 10 bytes. + * Group2 (0x40 - 0x5f). Reserved. + * Group2 (0x60 - 0x7f). Reserved. + * Group2 (0x80 - 0x9f). Reserved. + * Group2 (0xa0 - 0xbf). Reserved. + * Group6 (0xc0 - 0xdf). Vendor Unique + * Group7 (0xe0 - 0xff). Vendor Unique + */ + +/* + * Scsi Group0 commands all are 6 bytes and have a format according to + * struct ScsiGroup0Cmd. + */ +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_REZERO_UNIT 0x01 +#define SCSI_REWIND 0x01 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_FORMAT_UNIT 0x04 +#define SCSI_READ_BLOCK_LIMITS 0x05 +#define SCSI_REASSIGN_BLOCKS 0x07 +#define SCSI_READ 0x08 +#define SCSI_WRITE 0x0a +#define SCSI_SEEK 0x0b +#define SCSI_TRACK_SELECT 0x0b +#define SCSI_READ_REVERSE 0x0f +#define SCSI_WRITE_EOF 0x10 +#define SCSI_SPACE 0x11 +#define SCSI_INQUIRY 0x12 +#define SCSI_VERIFY 0x13 +#define SCSI_READ_BUFFER 0x14 +#define SCSI_MODE_SELECT 0x15 +#define SCSI_RESERVE_UNIT 0x16 +#define SCSI_RELEASE_UNIT 0x17 +#define SCSI_COPY 0x18 +#define SCSI_ERASE_TAPE 0x19 +#define SCSI_MODE_SENSE 0x1a +#define SCSI_START_STOP 0x1b +#define SCSI_LOAD_UNLOAD 0x1b +#define SCSI_RECV_DIAG_RESULTS 0x1c +#define SCSI_SEND_DIAGNOSTIC 0x1d +#define SCSI_PREVENT_ALLOW 0x1e + +/* + * Group1 commands are all 10 bytes and have a format according to + * struct ScsiGroup1Cmd. + */ +#define SCSI_READ_CAPACITY 0x25 +#define SCSI_READ_EXT 0x28 +#define SCSI_WRITE_EXT 0x2a +#define SCSI_SEEK_EXT 0x2b +#define SCSI_WRITE_VERIFY 0x2e +#define SCSI_VERIFY_EXT 0x2f +#define SCSI_SEARCH_HIGH 0x30 +#define SCSI_SEARCH_EQUAL 0x31 +#define SCSI_SEARCH_LOW 0x32 +#define SCSI_SET_LIMITS 0x33 +#define SCSI_COMPARE 0x39 +#define SCSI_COPY_VERIFY 0x3a + +/* + * Control byte flags for Group0 and Group1 commands. + * + * SCSI_CTRL_LINK - This is used to prevent a bus free phase between commands. + * If the command terminates successfully, a SCSI_LINKED_CMD_COMPLETE + * message is returned instead of the normal SCSI_COMMAND_COMPLETE message. * The last command in a chain should not have this bit set + * (and consequently gets a normal SCSI_COMMAND_COMPLETE message). + * SCSI_CTRL_LINK_FLAG - This bit should only set when SCSI_CTRL_LINK is set and + * causes a SCSI_LINKED_FLAGED_CMD_COMPLETE to be returned instead of + * a SCSI_LINKED_CMD_COMPLETE. + */ +#define SCSI_CTRL_LINK 0x01 /* Link commands (no bus free phase) */ +#define SCSI_CTRL_LINK_INTR 0x02 /* Interrupt after linked command */ + +/* + * The standard group0 6-byte SCSI control block. Note that the + * fields between highAddr and blockCount inclusive are command dependent. + * The definitions Addr and BlockCount cover most of the commands we will + * use. + */ +typedef struct ScsiGroup0Cmd { + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ +#if BYTE_ORDER == BIG_ENDIAN + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char highAddr :5; /* High bits of address */ +#else + u_char highAddr :5; /* High bits of address */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ +#endif + u_char midAddr; /* Middle bits of address */ + u_char lowAddr; /* Low bits of address */ + u_char blockCount; /* Blocks to transfer */ + u_char control; /* See flags for common bits */ +} ScsiGroup0Cmd; + +/* + * Format of a SCSI_START_STOP command. This is a group 0 command, but + * the command contents are different. + */ +typedef struct ScsiStartStopCmd { +#if BYTE_ORDER == BIG_ENDIAN + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad1 :4; /* Reserved */ + u_char immed :1; /* Immediate status bit */ + u_char pad2; /* Reserved */ + u_char pad3; /* Reserved */ + u_char pad4 :6; /* Reserved */ + u_char loadEject :1; /* Load or eject medium */ + u_char start :1; /* Start or stop medium */ + u_char control; /* See flags for common bits */ +#else + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ + u_char immed :1; /* Immediate status bit */ + u_char pad1 :4; /* Reserved */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad2; /* Reserved */ + u_char pad3; /* Reserved */ + u_char start :1; /* Start or stop medium */ + u_char loadEject :1; /* Load or eject medium */ + u_char pad4 :6; /* Reserved */ + u_char control; /* See flags for common bits */ +#endif +} ScsiStartStopCmd; + +/* + * The standard group1 10-byte SCSI control block. Note that the + * fields between highAddr and blockCount inclusive are command dependent. + * The definitions Addr and BlockCount cover most of the commands we will + * use. + */ +typedef struct ScsiGroup1Cmd { + u_char command; /* command code, defined below. The + * upper three bits of this are zero + * to indicate the control block is + * only 6 bytes long */ +#if BYTE_ORDER == BIG_ENDIAN + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ + u_char pad1 :5; /* Reserved */ +#else + u_char pad1 :5; /* Reserved */ + u_char unitNumber :3; /* Logical Unit (LUN) to which to + * pass the command. The device + * has already been selected using + * the "targetID" bit. */ +#endif + u_char highAddr; /* High bits of address */ + u_char midHighAddr; /* Middle high bits of address */ + u_char midLowAddr; /* Middle low bits of address */ + u_char lowAddr; /* Low bits of address */ + u_char pad2; /* Reserved */ + u_char highBlockCount; /* High bits of blocks to transfer */ + u_char lowBlockCount; /* Low bits of blocks to transfer */ + u_char control; /* See flags for common bits */ +} ScsiGroup1Cmd; + +/* + * SCSI status completion information. + * This is returned by the device when a command completes. + */ +#define SCSI_STATUS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */ +#define SCSI_STATUS_CONDMET 0x04 /* Condition Met (ie., search worked) */ +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_INTERMED 0x10 /* Intermediate status sent */ +#define SCSI_STATUS_EXT 0x80 /* Extended status valid */ + +/* + * Sense information provided after some errors. This is divided into + * two kinds, classes 0-6, and class 7. This is 30 bytes big to allow + * for the drive specific sense bytes that follow the standard 4 byte header. + * + * For extended sense, this buffer may be cast into another type. Also + * The actual size of the sense data returned is used to detect what + * kind of tape drive is out there. Kludgy, but true. + */ +typedef struct ScsiClass0Sense { +#if BYTE_ORDER == BIG_ENDIAN + u_char valid :1; /* Sense data is valid */ + u_char error :7; /* 3 bits class and 4 bits code */ +#else + u_char error :7; /* 3 bits class and 4 bits code */ + u_char valid :1; /* Sense data is valid */ +#endif + u_char highAddr; /* High byte of block address */ + u_char midAddr; /* Middle byte of block address */ + u_char lowAddr; /* Low byte of block address */ + u_char sense[26]; /* Target specific sense data */ +} ScsiClass0Sense; + +/* + * Definitions for errors in the sense data. The error field is specified + * as a 3 bit class and 4 bit code, but it is easier to treat it as a + * single 7 bit field. + */ +#define SCSI_NO_SENSE_DATA 0x00 +#define SCSI_NOT_READY 0x04 +#define SCSI_NOT_LOADED 0x09 +#define SCSI_INSUF_CAPACITY 0x0a +#define SCSI_HARD_DATA_ERROR 0x11 +#define SCSI_WRITE_PROTECT 0x17 +#define SCSI_CORRECTABLE_ERROR 0x18 +#define SCSI_FILE_MARK 0x1c +#define SCSI_INVALID_COMMAND 0x20 +#define SCSI_UNIT_ATTENTION 0x30 +#define SCSI_END_OF_MEDIA 0x34 + +/* + * The standard "extended" sense data returned by SCSI devices. This + * has an error field of 0x70, for a "class 7" error. + */ +typedef struct ScsiClass7Sense { +#if BYTE_ORDER == BIG_ENDIAN + u_char valid :1; /* Sense data is valid */ + u_char error7 :7; /* == 0x70 */ + u_char pad1; /* Also "segment number" for copy */ + u_char fileMark :1; /* File mark on device */ + u_char endOfMedia :1; /* End of media reached */ + u_char badBlockLen :1; /* Block length mis-match (Exabyte) */ + u_char pad2 :1; + u_char key :4; /* Sense keys defined below */ + u_char info1; /* Information byte 1 */ + u_char info2; /* Information byte 2 */ + u_char info3; /* Information byte 3 */ + u_char info4; /* Information byte 4 */ + u_char length; /* Number of additional info bytes */ +#else + u_char error7 :7; /* == 0x70 */ + u_char valid :1; /* Sense data is valid */ + u_char pad1; /* Also "segment number" for copy */ + u_char key :4; /* Sense keys defined below */ + u_char pad2 :1; + u_char badBlockLen :1; /* Block length mis-match (Exabyte) */ + u_char endOfMedia :1; /* End of media reached */ + u_char fileMark :1; /* File mark on device */ + u_char info1; /* Information byte 1 */ + u_char info2; /* Information byte 2 */ + u_char info3; /* Information byte 3 */ + u_char info4; /* Information byte 4 */ + u_char length; /* Number of additional info bytes */ +#endif +} ScsiClass7Sense; /* 8 Bytes */ + +/* + * Key values for standardized sense class 7. + */ +#define SCSI_CLASS7_NO_SENSE 0 +#define SCSI_CLASS7_RECOVERABLE 1 +#define SCSI_CLASS7_NOT_READY 2 +#define SCSI_CLASS7_MEDIA_ERROR 3 +#define SCSI_CLASS7_HARDWARE_ERROR 4 +#define SCSI_CLASS7_ILLEGAL_REQUEST 5 + +/* + * These seem to have different meanings to different vendors.... + */ +#define SCSI_CLASS7_MEDIA_CHANGE 6 +#define SCSI_CLASS7_UNIT_ATTN 6 + +#define SCSI_CLASS7_WRITE_PROTECT 7 +#define SCSI_CLASS7_BLANK_CHECK 8 +#define SCSI_CLASS7_VENDOR 9 +#define SCSI_CLASS7_POWER_UP_FAILURE 10 +#define SCSI_CLASS7_ABORT 11 +#define SCSI_CLASS7_EQUAL 12 +#define SCSI_CLASS7_OVERFLOW 13 +#define SCSI_CLASS7_RESERVED_14 14 +#define SCSI_CLASS7_RESERVED_15 15 + +/* + * Data return by the SCSI inquiry command. + */ +typedef struct ScsiInquiryData { +#if BYTE_ORDER == BIG_ENDIAN + u_char type; /* Peripheral Device type. See below. */ + u_char rmb:1; /* Removable Medium bit. */ + u_char qualifier:7; /* Device type qualifier. */ + u_char version; /* Version info. */ + u_char reserved:4; /* reserved. */ + u_char format:4; /* Response format. */ + u_char length; /* length of data returned. */ + u_char reserved2[2]; /* Reserved */ + u_char flags; /* SCSI II flags (see below) */ + u_char vendorID[8]; /* Vendor ID (ASCII) */ + u_char productID[16]; /* Product ID (ASCII) */ + u_char revLevel[4]; /* Revision level (ASCII) */ + u_char revData[8]; /* Revision data (ASCII) */ +#else + u_char type; /* Peripheral Device type. See below. */ + u_char qualifier:7; /* Device type qualifier. */ + u_char rmb:1; /* Removable Medium bit. */ + u_char version; /* Version info. */ + u_char format:4; /* Response format. */ + u_char reserved:4; /* reserved. */ + u_char length; /* length of data returned. */ + u_char reserved2[2]; /* Reserved */ + u_char flags; /* SCSI II flags (see below) */ + u_char vendorID[8]; /* Vendor ID (ASCII) */ + u_char productID[16]; /* Product ID (ASCII) */ + u_char revLevel[4]; /* Revision level (ASCII) */ + u_char revData[8]; /* Revision data (ASCII) */ +#endif +} ScsiInquiryData; + +/* + * The SCSI Peripheral type ID codes as return by the SCSI_INQUIRY command. + * + * SCSI_DISK_TYPE - Direct Access Device. + * SCSI_TAPE_TYPE - Sequential Access Device. + * SCSI_PRINTER_TYPE - Printer Device. + * SCSI_HOST_TYPE - Processor Device. + * SCSI_WORM_TYPE - Write-Once Read-Multiple Device. + * SCSI_ROM_TYPE - Read-Only Direct Access Device. + * SCSI_SCANNER_TYPE - Scanner device. + * SCSI_OPTICAL_MEM_TYPE - Optical memory device. + * SCSI_MEDIUM_CHANGER_TYPE - Medium changer device. + * SCSI_COMMUNICATIONS_TYPE - Communications device. + * SCSI_NODEVICE_TYPE - Logical Unit not present or implemented. + * + * Note that codes 0xa-0x7e are reserved and 0x80-0xff are vendor unique. + */ +#define SCSI_DISK_TYPE 0 +#define SCSI_TAPE_TYPE 1 +#define SCSI_PRINTER_TYPE 2 +#define SCSI_HOST_TYPE 3 +#define SCSI_WORM_TYPE 4 +#define SCSI_ROM_TYPE 5 +#define SCSI_SCANNER_TYPE 6 +#define SCSI_OPTICAL_MEM_TYPE 7 +#define SCSI_MEDIUM_CHANGER_TYPE 8 +#define SCSI_COMMUNICATIONS_TYPE 9 +#define SCSI_NODEVICE_TYPE 0x7f + +/* + * The SCSI I & II inquiry flags. + * + * SCSI_REL_ADR - Relative addressing supported. + * SCSI_WIDE_32 - 32 bit wide SCSI bus transfers supported. + * SCSI_WIDE_16 - 16 bit wide SCSI bus transfers supported. + * SCSI_SYNC - Synchronous data transfers supported. + * SCSI_LINKED - Linked commands supported. + * SCSI_CMD_QUEUE - Tagged command queuing supported. + * SCSI_SOFT_RESET - Soft RESET alternative suported. + */ +#define SCSI_REL_ADR 0x80 +#define SCSI_WIDE_32 0x40 +#define SCSI_WIDE_16 0x20 +#define SCSI_SYNC 0x10 +#define SCSI_LINKED 0x08 +#define SCSI_CMD_QUEUE 0x02 +#define SCSI_SOFT_RESET 0x01 + +/* + * Standard header for SCSI_MODE_SENSE and SCSI_MODE_SELECT commands for tapes. + */ +typedef struct ScsiTapeModeSelectHdr { + u_char len; /* length */ + u_char media; /* media type */ +#if BYTE_ORDER == BIG_ENDIAN + u_char writeprot:1; /* Write protected media */ + u_char bufferedMode:3; /* Type of buffer to be done. */ + u_char speed:4; /* Drive speed. */ +#else + u_char speed:4; /* Drive speed. */ + u_char bufferedMode:3; /* Type of buffer to be done. */ + u_char writeprot:1; /* Write protected media */ +#endif + u_char length; /* Block descriptor length. */ + u_char density; /* tape density code */ + u_char blocks_2; /* number of blocks (MSB) */ + u_char blocks_1; /* number of blocks */ + u_char blocks_0; /* number of blocks (LSB) */ + u_char reserved; /* */ + u_char block_size2; /* Tape block size (MSB) */ + u_char block_size1; /* Tape block size */ + u_char block_size0; /* Tape block size (LSB) */ + u_char vendor[6]; /* vendor specific data */ +} ScsiTapeModeSelectHdr; + +/* + * Definitions of SCSI messages. + * + * SCSI_COMMAND_COMPLETE - After a command has completed, successfully + * or not, this is returned to the host from the target. + * + * SCSI_EXTENDED_MSG - Indicates that a multi-byte message is being sent. + * + * The following messages are used with connect/disconnect: + * SCSI_SAVE_DATA_POINTER - Sent from target to host to request saving + * of current DMA address and count. Indicates a pending dis-connect. + * SCSI_RESTORE_POINTER - Sent from the target to the host to request + * restoring pointers saved before a disconnect + * SCSI_DISCONNECT - Sent from the target to the host to disconnect. + * SCSI_ABORT - Sent from the host to the target to abort current request. + * SCSI_MESSAGE_REJECT - Indicates receipt, by either host or target, of + * an unimplemented message. + * SCSI_NO_OP - Sent from host to target if it has no real message to send. + * SCSI_MESSAGE_PARITY_ERROR - Sent from host to target on message parity error + * SCSI_BUS_RESET - Sent from host to target to reset all current I/O + * + * SCSI_IDENTIFY - The low order two bits of this message type indicate + * the Logical Unit of the Target which is requesting a reconnect. + * SCSI_DIS_REC_IDENTIFY - Sent from the host to a target to indicate + * is supports connect/dis-connect + * + */ +#define SCSI_COMMAND_COMPLETE 0x00 +#define SCSI_EXTENDED_MSG 0x01 +#define SCSI_SAVE_DATA_POINTER 0x02 +#define SCSI_RESTORE_POINTERS 0x03 +#define SCSI_DISCONNECT 0x04 +#define SCSI_ABORT 0x06 +#define SCSI_MESSAGE_REJECT 0x07 +#define SCSI_NO_OP 0x08 +#define SCSI_MESSAGE_PARITY_ERROR 0x09 +#define SCSI_LINKED_CMD_COMPLETE 0x0A +#define SCSI_LINKED_FLAGED_CMD_COMPLETE 0x0B +#define SCSI_BUS_RESET 0x0C + +#define SCSI_IDENTIFY 0x80 +#define SCSI_DIS_REC_IDENTIFY 0xc0 + +/* + * Extended message types (2nd byte of SCSI_EXTENDED_MSG). + */ +#define SCSI_MODIFY_DATA_PTR 0x00 +#define SCSI_SYNCHRONOUS_XFER 0x01 +#define SCSI_EXTENDED_IDENTIFY 0x02 /* only in SCSI I */ +#define SCSI_WIDE_XFER 0x03 + +/* + * Driver ioctl's for various scsi operations. + */ +#ifndef _IOCTL_ +#include <sys/ioctl.h> +#endif + +/* + * Control for SCSI "format" mode. + * + * "Format" mode allows a privileged process to issue direct SCSI + * commands to a drive (it is intended primarily to allow on-line + * formatting). SDIOCSFORMAT with a non-zero arg will put the drive + * into format mode; a zero arg will take it out. When in format + * mode, only the process that issued the SDIOCFORMAT can read or + * write the drive. + * + * In format mode, process is expected to + * - do SDIOCSCSICOMMAND to supply cdb for next SCSI op + * - do read or write as appropriate for cdb + * - if i/o error, optionally do SDIOCSENSE to get completion + * status and sense data from last scsi operation. + */ + +struct scsi_fmt_cdb { + int len; /* cdb length (in bytes) */ + u_char cdb[28]; /* cdb to use on next read/write */ +}; + +struct scsi_fmt_sense { + u_int status; /* completion status of last op */ + u_char sense[32]; /* sense data (if any) from last op */ +}; + +#define SDIOCSFORMAT _IOW('S', 0x1, int) +#define SDIOCGFORMAT _IOR('S', 0x2, int) +#define SDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb) +#define SDIOCSENSE _IOR('S', 0x4, struct scsi_fmt_sense) + +#ifdef _KERNEL +/* + * Routines. + */ +extern void scsiGroup0Cmd(); +extern void scsiGroup1Cmd(); +#endif /* _KERNEL */ + +#endif /* _SCSI_H */ |