diff options
Diffstat (limited to 'sys/arch/arm32/mainbus')
-rw-r--r-- | sys/arch/arm32/mainbus/beep.c | 369 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/com.c | 1034 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/comreg.h | 133 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/cpu.c | 500 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/fd.c | 1339 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/fdreg.h | 102 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/iic.c | 403 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/iic_asm.S | 239 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/kbd.c | 1357 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/lpt.c | 1314 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/lptreg.h | 64 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/mainbus.c | 136 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/mainbus.h | 62 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/pms.c | 612 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/qmouse.c | 440 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/rtc.c | 384 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/vidcaudio.c | 870 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/waveform.h | 551 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/wd.c | 1764 | ||||
-rw-r--r-- | sys/arch/arm32/mainbus/wdreg.h | 161 |
20 files changed, 11834 insertions, 0 deletions
diff --git a/sys/arch/arm32/mainbus/beep.c b/sys/arch/arm32/mainbus/beep.c new file mode 100644 index 00000000000..762830309f5 --- /dev/null +++ b/sys/arch/arm32/mainbus/beep.c @@ -0,0 +1,369 @@ +/* $NetBSD: beep.c,v 1.4 1996/03/27 22:08:36 mark Exp $ */ + +/* + * Copyright (c) 1995 Mark Brinicombe + * + * 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 RiscBSD team. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Simple beep sounds using VIDC + */ + +/* + * To use the driver, open /dev/beep and write lines. + * Each write will generate a beep + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/kernel.h> +#include <sys/types.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <dev/cons.h> +#include <vm/vm.h> +#include <vm/vm_kern.h> + +#include <machine/irqhandler.h> +#include <machine/katelib.h> +#include <machine/iomd.h> +#include <machine/vidc.h> +#include <machine/pmap.h> +#include <machine/beep.h> +#include <arm32/mainbus/mainbus.h> +#include <arm32/mainbus/waveform.h> + +#include "beep.h" + +struct beep_softc { + struct device sc_device; + irqhandler_t sc_ih; + int sc_iobase; + int sc_open; + int sc_count; + u_int sc_sound_cur0; + u_int sc_sound_end0; + u_int sc_sound_cur1; + u_int sc_sound_end1; + vm_offset_t sc_buffer0; + vm_offset_t sc_buffer1; +}; + +int beepprobe __P((struct device *parent, void *match, void *aux)); +void beepattach __P((struct device *parent, struct device *self, void *aux)); +int beepopen __P((dev_t, int, int, struct proc *)); +int beepclose __P((dev_t, int, int, struct proc *)); +int beepintr __P((struct beep_softc *sc)); +void beepdma __P((struct beep_softc *sc, int buf)); + +struct cfattach beep_ca = { + sizeof(struct beep_softc), beepprobe, beepattach +}; + +struct cfdriver beep_cd = { + NULL, "beep", DV_TTY +}; + + +int +beepprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ +/* struct mainbus_attach_args *mb = aux;*/ + int id; + +/* Make sure we have an IOMD we understand */ + + id = ReadByte(IOMD_ID0) | (ReadByte(IOMD_ID1) << 8); + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + return(1); + break; + default: + printf("beep: Unknown IOMD id=%04x", id); + break; + } + return(0); +} + + +void +beepattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct beep_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + + sc->sc_iobase = mb->mb_iobase; + sc->sc_open = 0; + sc->sc_count = 0; + + sc->sc_buffer0 = kmem_alloc(kernel_map, NBPG); + if (sc->sc_buffer0 == 0) + panic("beep: Cannot allocate buffer memory\n"); + if ((sc->sc_buffer0 & (NBPG -1)) != 0) + panic("beep: Cannot allocate page aligned buffer\n"); + sc->sc_buffer1 = sc->sc_buffer0; + + sc->sc_sound_cur0 = pmap_extract(kernel_pmap, + (vm_offset_t)sc->sc_buffer0 & PG_FRAME); + sc->sc_sound_end0 = (sc->sc_sound_cur0 + NBPG - 16) | 0x00000000; + sc->sc_sound_cur1 = pmap_extract(kernel_pmap, + (vm_offset_t)sc->sc_buffer1 & PG_FRAME); + sc->sc_sound_end1 = (sc->sc_sound_cur1 + NBPG - 16) | 0x00000000; + + bcopy(beep_waveform, (void *)sc->sc_buffer0, sizeof(beep_waveform)); + +/* Reset the sound DMA channel */ + + WriteWord(IOMD_SD0CURA, sc->sc_sound_cur0); + WriteWord(IOMD_SD0ENDA, sc->sc_sound_end0 | 0xc0000000); + WriteWord(IOMD_SD0CURB, sc->sc_sound_cur1); + WriteWord(IOMD_SD0ENDB, sc->sc_sound_end1 | 0xc0000000); + + WriteByte(IOMD_SD0CR, 0x90); + +/* Install an IRQ handler */ + + sc->sc_ih.ih_func = beepintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_NONE; + sc->sc_ih.ih_name = "dma snd ch 0"; + + if (irq_claim(IRQ_DMASCH0, &sc->sc_ih)) + panic("Cannot claim DMASCH0 IRQ for beep%d\n", parent->dv_unit); + + disable_irq(IRQ_DMASCH0); + +/* + printf(" [ buf0=%08x:%08x->%08x buf1=%08x:%08x->%08x ]", + (u_int)sc->sc_buffer0, sc->sc_sound_cur0, sc->sc_sound_end0, + (u_int)sc->sc_buffer1, sc->sc_sound_cur1, sc->sc_sound_end1); +*/ + printf("\n"); + +/* Set sample rate to 32us */ + + WriteWord(VIDC_BASE, VIDC_SFR | 32); + +/* Set the stereo postions to centred for all channels */ + + WriteWord(VIDC_BASE, VIDC_SIR0 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR1 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR2 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR3 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR4 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR5 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR6 | SIR_CENTRE); + WriteWord(VIDC_BASE, VIDC_SIR7 | SIR_CENTRE); +} + + +int +beepopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct beep_softc *sc; + int unit = minor(dev); + int s; + + if (unit >= beep_cd.cd_ndevs) + return(ENXIO); + + sc = beep_cd.cd_devs[unit]; + if (!sc) return(ENXIO); + +/* HACK hack hack */ + + s = splhigh(); + if (sc->sc_open) { + (void)splx(s); + return(EBUSY); + } + + ++sc->sc_open; + (void)splx(s); + + return(0); +} + + +int +beepclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = minor(dev); + struct beep_softc *sc = beep_cd.cd_devs[unit]; + int s; + + if (sc->sc_open == 0) return(ENXIO); + +/* HACK hack hack */ + + s = splhigh(); + --sc->sc_open; + (void)splx(s); + + return(0); +} + + +void +beep_generate(void) +{ + struct beep_softc *sc = beep_cd.cd_devs[0]; +/* int status;*/ + + if (sc->sc_count > 0) { +/* printf("beep: active\n");*/ + return; + } +/* printf("beep: generate ");*/ + ++sc->sc_count; + +/* status = ReadByte(IOMD_SD0ST); + printf("st=%02x\n", status);*/ + WriteByte(IOMD_SD0CR, 0x90); + WriteByte(IOMD_SD0CR, 0x30); + beepdma(sc, 0); +} + + +int +beepioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct beep_softc *sc = beep_cd.cd_devs[minor(dev)]; + int rate; + struct wavebuffer *wave = (struct wavebuffer *)data; + + switch (cmd) { + case BEEP_GENERATE: + beep_generate(); + break; + + case BEEP_SETRATE: + rate = *(int *)data; + + if (rate < 3 || rate > 255) + return(EINVAL); + + WriteWord(VIDC_BASE, VIDC_SFR | rate); + break; + + case BEEP_SET: + printf("set %08x\n", (u_int)data); + printf("set %08x %08x\n", (u_int)wave->addr, wave->size); + if (wave->size < 16 || wave->size > NBPG) + return(ENXIO); + copyin(wave->addr, (char *)sc->sc_buffer0, wave->size); + sc->sc_sound_end0 = (sc->sc_sound_cur0 + wave->size - 16); + sc->sc_sound_end1 = (sc->sc_sound_cur1 + wave->size - 16); + break; + + default: + return(ENXIO); + break; + } + + return(0); +} + + +int +beepintr(sc) + struct beep_softc *sc; +{ +/* printf("beepintr: %02x,%02x,%02x,%d\n", ReadByte(IOMD_DMARQ), + ReadByte(IOMD_SD0CR), ReadByte(IOMD_SD0ST), sc->sc_count);*/ + WriteByte(IOMD_DMARQ, 0x10); + --sc->sc_count; + if (sc->sc_count <= 0) { + WriteWord(IOMD_SD0CURB, sc->sc_sound_cur1); + WriteWord(IOMD_SD0ENDB, sc->sc_sound_end1 | (1 << 30)); + disable_irq(IRQ_DMASCH0); +/* printf("stop:st=%02x\n", ReadByte(IOMD_SD0ST));*/ + return(1); + } + + beepdma(sc, sc->sc_count & 1); + return(1); +} + + +void +beepdma(sc, buf) + struct beep_softc *sc; + int buf; +{ + int status; +/* printf("beep:dma %d", buf); */ + status = ReadByte(IOMD_SD0ST); +/* printf("st=%02x\n", status);*/ + + if (buf == 0) { + WriteWord(IOMD_SD0CURA, sc->sc_sound_cur0); + WriteWord(IOMD_SD0ENDA, sc->sc_sound_end0); + WriteWord(IOMD_SD0CURB, sc->sc_sound_cur1); + WriteWord(IOMD_SD0ENDB, sc->sc_sound_end1 | (1 << 30)); + } + + if (buf == 1) { + WriteWord(IOMD_SD0CURB, sc->sc_sound_cur1); + WriteWord(IOMD_SD0ENDB, sc->sc_sound_end1 | (1 << 30)); + } + +/* status = ReadByte(IOMD_SD0ST); + printf("st=%02x\n", status);*/ + + enable_irq(IRQ_DMASCH0); +} + +/* End of beep.c */ diff --git a/sys/arch/arm32/mainbus/com.c b/sys/arch/arm32/mainbus/com.c new file mode 100644 index 00000000000..ee9b812f796 --- /dev/null +++ b/sys/arch/arm32/mainbus/com.c @@ -0,0 +1,1034 @@ +/* $NetBSD: com.c,v 1.5 1996/03/28 21:52:32 mark Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995 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 + */ + +/* + * COM 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/cpu.h> +#include <machine/katelib.h> +#include <machine/irqhandler.h> +#include <machine/io.h> +#include <arm32/mainbus/comreg.h> +#include <arm32/mainbus/mainbus.h> + +#define com_lcr com_cfcr + +#define COM_IBUFSIZE (2 * 256) +#define COM_IHIGHWATER ((3 * COM_IBUFSIZE) / 4) + +struct com_softc { + struct device sc_dev; + irqhandler_t sc_ih; + struct tty *sc_tty; + + int sc_overflows; + int sc_floods; + int sc_errors; + + int sc_iobase; + u_char sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#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; + u_char sc_dtr; + + u_char *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; + u_char sc_ibufs[2][COM_IBUFSIZE]; +}; + +int comprobe __P((struct device *, void *, void *)); +void comattach __P((struct device *, struct device *, void *)); +int comopen __P((dev_t, int, int, struct proc *)); +int comclose __P((dev_t, int, int, struct proc *)); +void comdiag __P((void *)); +int comintr __P((void *)); +void compoll __P((void *)); +int comparam __P((struct tty *, struct termios *)); +void comstart __P((struct tty *)); + +struct cfattach com_ca = { + sizeof(struct com_softc), comprobe, comattach +}; + +struct cfdriver com_cd = { + NULL, "com", DV_TTY +}; + +int comdefaultrate = TTYDEF_SPEED; +#ifdef COMCONSOLE +int comconsole = COMCONSOLE; +#else +int comconsole = -1; +#endif +int comconsinit; +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)) + +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(iobase) + int iobase; +{ + + /* force access to id reg */ + outb(iobase + com_lcr, 0); + outb(iobase + com_iir, 0); + if (inb(iobase + com_iir) & 0x38) + return 0; + + return 1; +} + +int +comprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct mainbus_attach_args *mb = aux; + int iobase = mb->mb_iobase; + + if (!comprobe1(iobase)) + return 0; + + mb->mb_iosize = COM_NPORTS; + return 1; +} + +void +comattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct com_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + int iobase = mb->mb_iobase; + struct tty *tp; + + sc->sc_iobase = iobase; + sc->sc_hwflags = ISSET(cf->cf_flags, COM_HW_NOIEN); + sc->sc_swflags = 0; + + if (sc->sc_dev.dv_unit == comconsole) + delay(1000); + + /* look for a NS 16550AF UART with FIFOs */ + outb(iobase + com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + delay(100); + if (ISSET(inb(iobase + com_iir), IIR_FIFO_MASK) == IIR_FIFO_MASK) + if (ISSET(inb(iobase + 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"); + outb(iobase + com_fifo, 0); + + /* disable interrupts */ + outb(iobase + com_ier, 0); + outb(iobase + com_mcr, 0); + + sc->sc_ih.ih_func = comintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_TTY; + sc->sc_ih.ih_name = "serial"; + if (mb->mb_irq != IRQUNK) + if (irq_claim(mb->mb_irq, &sc->sc_ih)) + panic("Cannot claim IRQ %d for com%d\n", mb->mb_irq, sc->sc_dev.dv_unit); + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (comconsole == unit) + kgdb_dev = -1; /* can't debug over console port */ + else { + (void) cominit(unit, 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 + + if (sc->sc_dev.dv_unit == comconsole) { + /* + * Need to reset baud rate, etc. of next print so reset + * comconsinit. Also make sure console is always "hardwired". + */ + comconsinit = 0; + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, COM_SW_SOFTCAR); + } +} + +int +comopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = COMUNIT(dev); + struct com_softc *sc; + int iobase; + struct tty *tp; + int s; + int error = 0; + + if (unit >= com_cd.cd_ndevs) + return ENXIO; + sc = com_cd.cd_devs[unit]; + if (!sc) + return ENXIO; + + if (!sc->sc_tty) + tp = sc->sc_tty = ttymalloc(); + 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; + 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; + + iobase = sc->sc_iobase; + /* Set the FIFO threshold based on the receive speed. */ + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) + outb(iobase + 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(inb(iobase + com_lsr), LSR_RXRDY)) + (void) inb(iobase + 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); + outb(iobase + com_mcr, sc->sc_mcr); + outb(iobase + com_ier, + IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); + + sc->sc_msr = inb(iobase + 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; + int iobase = sc->sc_iobase; + 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(); + CLR(sc->sc_lcr, LCR_SBREAK); + outb(iobase + com_lcr, sc->sc_lcr); + outb(iobase + com_ier, 0); + if (ISSET(tp->t_cflag, HUPCL) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { + /* XXX perhaps only clear DTR */ + outb(iobase + com_mcr, 0); + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (--comsopen == 0) + untimeout(compoll, NULL); + splx(s); + ttyclose(tp); +#ifdef notyet /* XXXX */ + if (unit != comconsole) { + 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; + + 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; + + 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; + int iobase = sc->sc_iobase; + 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 TIOCSBRK: + SET(sc->sc_lcr, LCR_SBREAK); + outb(iobase + com_lcr, sc->sc_lcr); + break; + case TIOCCBRK: + CLR(sc->sc_lcr, LCR_SBREAK); + outb(iobase + com_lcr, sc->sc_lcr); + break; + case TIOCSDTR: + SET(sc->sc_mcr, sc->sc_dtr); + outb(iobase + com_mcr, sc->sc_mcr); + break; + case TIOCCDTR: + CLR(sc->sc_mcr, sc->sc_dtr); + outb(iobase + 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)); + outb(iobase + com_mcr, sc->sc_mcr); + break; + case TIOCMBIC: + CLR(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data)); + outb(iobase + 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 (inb(iobase + 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)]; + int iobase = sc->sc_iobase; + int ospeed = comspeed(t->c_ospeed); + u_char lcr; + tcflag_t oldcflag; + int s; + + /* check requested parameters */ + if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + lcr = 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); + outb(iobase + com_mcr, sc->sc_mcr); + } + + /* + * Set the FIFO threshold based on the receive speed, if we are + * changing it. + */ + if (tp->t_ispeed != t->c_ispeed) { + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) + outb(iobase + com_fifo, + FIFO_ENABLE | + (t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); + } + + if (ospeed != 0) { + outb(iobase + com_lcr, lcr | LCR_DLAB); + outb(iobase + com_dlbl, ospeed); + outb(iobase + com_dlbh, ospeed >> 8); + outb(iobase + com_lcr, lcr); + SET(sc->sc_mcr, MCR_DTR); + outb(iobase + com_mcr, sc->sc_mcr); + } else + outb(iobase + 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); + outb(iobase + com_mcr, sc->sc_mcr); + } + } else { + if (ISSET(sc->sc_mcr, MCR_RTS)) { + CLR(sc->sc_mcr, MCR_RTS); + outb(iobase + 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); + outb(iobase + com_mcr, sc->sc_mcr); + } + + splx(s); + return 0; +} + +void +comstart(tp) + struct tty *tp; +{ + struct com_softc *sc = com_cd.cd_devs[COMUNIT(tp->t_dev)]; + int iobase = sc->sc_iobase; + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_TTSTOP | TS_BUSY)) + goto out; + if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, MSR_CTS)) + goto out; + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + if (tp->t_outq.c_cc == 0) + goto out; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + 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 { + outb(iobase + com_data, *cp++); + } while (--n); + } else + outb(iobase + com_data, getc(&tp->t_outq)); +out: + splx(s); +} + +/* + * Stop output on a line. + */ +void +comstop(tp, flag) + struct tty *tp; +{ + 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); +} + +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); + outb(sc->sc_iobase + 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; + int iobase = sc->sc_iobase; + struct tty *tp; + u_char lsr, data, msr, delta; + + if (ISSET(inb(iobase + com_iir), IIR_NOPEND)) + return (0); + + tp = sc->sc_tty; + + for (;;) { + lsr = inb(iobase + com_lsr); + + if (ISSET(lsr, LSR_RCV_MASK)) { + register u_char *p = sc->sc_ibufp; + + comevents = 1; + do { + data = ISSET(lsr, LSR_RXRDY) ? + inb(iobase + com_data) : 0; + if (ISSET(lsr, LSR_BI)) { +#ifdef DDB + if (sc->sc_dev.dv_unit == comconsole) { + Debugger(); + goto next; + } +#endif + data = '\0'; + } + 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); + outb(iobase + com_mcr, + sc->sc_mcr); + } + } + next: + lsr = inb(iobase + com_lsr); + } while (ISSET(lsr, LSR_RCV_MASK)); + + sc->sc_ibufp = p; + } +#if 0 + else if (ISSET(lsr, LSR_BI|LSR_FE|LSR_PE|LSR_OE)) + printf("weird lsr %02x\n", lsr); +#endif + + msr = inb(iobase + 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); + outb(iobase + 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); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else + (*linesw[tp->t_line].l_start)(tp); + } + + if (ISSET(inb(iobase + com_iir), IIR_NOPEND)) + return (1); + } +} + +/* + * Following are all routines needed for COM to act as console + */ +#include <dev/cons.h> + +void +comcnprobe(cp) + struct consdev *cp; +{ + + if (!comprobe1(CONADDR)) { + 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; +{ + + cominit(CONUNIT, comdefaultrate); + comconsole = CONUNIT; + comconsinit = 0; +} + +cominit(unit, rate) + int unit, rate; +{ + int s = splhigh(); + int iobase = CONADDR; + u_char stat; + + outb(iobase + com_lcr, LCR_DLAB); + rate = comspeed(comdefaultrate); + outb(iobase + com_dlbl, rate); + outb(iobase + com_dlbh, rate >> 8); + outb(iobase + com_lcr, LCR_8BITS); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY); + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_4); + stat = inb(iobase + com_iir); + splx(s); +} + +comcngetc(dev) + dev_t dev; +{ + int s = splhigh(); + int iobase = CONADDR; + u_char stat, c; + + while (!ISSET(stat = inb(iobase + com_lsr), LSR_RXRDY)) + ; + c = inb(iobase + com_data); + stat = inb(iobase + com_iir); + splx(s); + return c; +} + +/* + * Console kernel output character routine. + */ +void +comcnputc(dev, c) + dev_t dev; + int c; +{ + int s = splhigh(); + int iobase = CONADDR; + u_char stat; + register int timo; + +#ifdef KGDB + if (dev != kgdb_dev) +#endif + if (comconsinit == 0) { + (void) cominit(COMUNIT(dev), comdefaultrate); + comconsinit = 1; + } + /* wait for any pending transmission to finish */ + timo = 50000; + while (!ISSET(stat = inb(iobase + com_lsr), LSR_TXRDY) && --timo) + ; + outb(iobase + com_data, c); + /* wait for this transmission to complete */ + timo = 1500000; + while (!ISSET(stat = inb(iobase + com_lsr), LSR_TXRDY) && --timo) + ; + /* clear any interrupts generated by this transmission */ + stat = inb(iobase + com_iir); + splx(s); +} + +void +comcnpollc(dev, on) + dev_t dev; + int on; +{ + +} diff --git a/sys/arch/arm32/mainbus/comreg.h b/sys/arch/arm32/mainbus/comreg.h new file mode 100644 index 00000000000..7b1464d0a68 --- /dev/null +++ b/sys/arch/arm32/mainbus/comreg.h @@ -0,0 +1,133 @@ +/* $NetBSD: comreg.h,v 1.1 1996/01/31 23:24:29 mark 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. + * + * @(#)comreg.h 7.2 (Berkeley) 5/9/91 + */ + +/* + * NS16550 UART registers + */ + +#define com_data 0 /* data register (R/W) */ +#define com_dlbl 0 /* divisor latch low (W) */ +#define com_dlbh 4 /* divisor latch high (W) */ +#define com_ier 4 /* interrupt enable (W) */ +#define com_iir 8 /* interrupt identification (R) */ +#define com_fifo 8 /* FIFO control (W) */ +#define com_lctl 12 /* line control register (R/W) */ +#define com_cfcr 12 /* line control register (R/W) */ +#define com_mcr 16 /* modem control register (R/W) */ +#define com_lsr 20 /* line status register (R/W) */ +#define com_msr 24 /* modem status register (R/W) */ +#define com_scratch 28 /* scratch register (R/W) */ + +#define COM_FREQ 1843200 /* 16-bit baud rate divisor */ +#define COM_TOLERANCE 30 /* baud rate tolerance, in 0.1% units */ + +/* interrupt enable register */ +#define IER_ERXRDY 0x1 +#define IER_ETXRDY 0x2 +#define IER_ERLS 0x4 +#define IER_EMSC 0x8 + +/* interrupt identification register */ +#define IIR_IMASK 0xf +#define IIR_RXTOUT 0xc +#define IIR_RLS 0x6 +#define IIR_RXRDY 0x4 +#define IIR_TXRDY 0x2 +#define IIR_NOPEND 0x1 +#define IIR_MLSC 0x0 +#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */ + +/* fifo control register */ +#define FIFO_ENABLE 0x01 +#define FIFO_RCV_RST 0x02 +#define FIFO_XMT_RST 0x04 +#define FIFO_DMA_MODE 0x08 +#define FIFO_TRIGGER_1 0x00 +#define FIFO_TRIGGER_4 0x40 +#define FIFO_TRIGGER_8 0x80 +#define FIFO_TRIGGER_14 0xc0 + +/* line control register */ +#define LCR_DLAB 0x80 +#define LCR_SBREAK 0x40 +#define LCR_PZERO 0x30 +#define LCR_PONE 0x20 +#define LCR_PEVEN 0x10 +#define LCR_PODD 0x00 +#define LCR_PENAB 0x08 +#define LCR_STOPB 0x04 +#define LCR_8BITS 0x03 +#define LCR_7BITS 0x02 +#define LCR_6BITS 0x01 +#define LCR_5BITS 0x00 + +/* modem control register */ +#define MCR_LOOPBACK 0x10 +#define MCR_IENABLE 0x08 +#define MCR_DRS 0x04 +#define MCR_RTS 0x02 +#define MCR_DTR 0x01 + +/* line status register */ +#define LSR_RCV_FIFO 0x80 +#define LSR_TSRE 0x40 +#define LSR_TXRDY 0x20 +#define LSR_BI 0x10 +#define LSR_FE 0x08 +#define LSR_PE 0x04 +#define LSR_OE 0x02 +#define LSR_RXRDY 0x01 +#define LSR_RCV_MASK 0x1f + +/* modem status register */ +#define MSR_DCD 0x80 +#define MSR_RI 0x40 +#define MSR_DSR 0x20 +#define MSR_CTS 0x10 +#define MSR_DDCD 0x08 +#define MSR_TERI 0x04 +#define MSR_DDSR 0x02 +#define MSR_DCTS 0x01 + +#define COM_NPORTS 32 + +/* + * WARNING: Serial console is assumed to be at COM1 address + * and CONUNIT must be 0. + */ +#define CONADDR (SERIAL0_CONTROLLER_BASE) +#define CONUNIT (0) diff --git a/sys/arch/arm32/mainbus/cpu.c b/sys/arch/arm32/mainbus/cpu.c new file mode 100644 index 00000000000..b5b8551dbdd --- /dev/null +++ b/sys/arch/arm32/mainbus/cpu.c @@ -0,0 +1,500 @@ +/* $NetBSD: cpu.c,v 1.4 1996/03/18 20:50:00 mark Exp $ */ + +/* + * Copyright (c) 1995 Mark Brinicombe. + * Copyright (c) 1995 Brini. + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * cpu.c + * + * Probing and configuration for the master cpu + * + * Created : 10/10/95 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <vm/vm_kern.h> +#include <machine/io.h> +#include <machine/katelib.h> +#include <machine/cpu.h> +#include <machine/pte.h> +#include <machine/undefined.h> +#include <machine/cpus.h> + +#include "cpu.h" +#if NCPU < 1 +#error Need at least 1 CPU configured +#endif + +/* Array of cpu structures, one per possible cpu */ + +cpu_t cpus[MAX_CPUS]; + +char cpu_model[48]; +extern int cpu_ctrl; /* Control bits for boot CPU */ +volatile int undefined_test; /* Used for FPA test */ + +extern char *boot_args; + +/* Declare prototypes */ + +/* Prototypes */ + +void identify_master_cpu __P((int /*cpu_number*/)); +void identify_arm_cpu __P((int /*cpu_number*/)); +void identify_arm_fpu __P((int /*cpu_number*/)); +char *strstr __P((char */*s1*/, char */*s2*/)); + + +/* + * int cpumatch(struct device *parent, void *match, void *aux) + * + * Probe for the main cpu. Currently all this does is return 1 to + * indicate that the cpu was found. + */ + +int +cpumatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct device *dev = match; + + if (dev->dv_unit == 0) + return(1); + return(0); +} + + +/* + * void cpusattach(struct device *parent, struct device *dev, void *aux) + * + * Attach the main cpu + */ + +void +cpuattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + int loop; + + for (loop = 0; loop < MAX_CPUS; ++loop) + bzero(&cpus[loop], sizeof(cpu_t)); + + identify_master_cpu(CPU_MASTER); +} + +struct cfattach cpu_ca = { + sizeof(struct cpu_softc), cpumatch, cpuattach +}; + +struct cfdriver cpu_cd = { + NULL, "cpu", DV_DULL, 1 +}; + + +/* + * Used to test for an FPA. The following function is installed as a coproc1 handler + * on the undefined instruction vector and then we issue a FPA instruction. + * If undefined_test is non zero then the FPA did not handle the instruction so + * must be absent. + */ + +int +fpa_test(address, instruction, frame) + u_int address; + u_int instruction; + trapframe_t *frame; +{ + ++undefined_test; + return(0); +} + +/* + * If an FPA was found then this function is installed as the coproc1 handler + * on the undefined instruction vector. Currently we don't support FPA's + * so this just triggers an exception. + */ + +int +fpa_handler(address, instruction, frame) + u_int address; + u_int instruction; + trapframe_t *frame; +{ + u_int fpsr; + + __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); + + printf("FPA exception: fpsr = %08x\n", fpsr); + + return(1); +} + + +/* + * Identify the master (boot) CPU + * This also probes for an FPU and will install an FPE if necessary + */ + +void +identify_master_cpu(cpu_number) + int cpu_number; +{ + u_int fpsr; + + cpus[cpu_number].cpu_class = CPU_CLASS_ARM; + cpus[cpu_number].cpu_host = CPU_HOST_MAINBUS; + cpus[cpu_number].cpu_flags = CPU_FLAG_PRESENT; + cpus[cpu_number].cpu_ctrl = cpu_ctrl; + +/* Get the cpu ID from coprocessor 15 */ + + cpus[cpu_number].cpu_id = cpu_id(); + + identify_arm_cpu(cpu_number); + strcpy(cpu_model, cpus[cpu_number].cpu_model); + +/* + * Ok now we test for an FPA + * At this point no floating point emulator has been installed. + * This means any FP instruction will cause undefined exception. + * We install a temporay coproc 1 handler which will modify undefined_test + * if it is called. + * We then try to read the FP status register. If undefined_test has been + * decremented then the instruction was not handled by an FPA so we know + * the FPA is missing. If undefined_test is still 1 then we know the + * instruction was handled by an FPA. + * We then remove our test handler and look at the + * FP status register for identification. + */ + + install_coproc_handler(FP_COPROC, fpa_test); + + undefined_test = 0; + + __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); + + if (undefined_test == 0) { + cpus[cpu_number].fpu_type = (fpsr >> 24); + switch (fpsr >> 24) { + case 0x81 : + cpus[cpu_number].fpu_class = FPU_CLASS_FPA; + +#if 0 +/* Experimental stuff used when playing with an ARM700+FPA11 */ + printf("FPA11: FPSR=%08x\n", fpsr); + fpsr=0x00070400; + __asm __volatile("wfs %0" : "=r" (fpsr)); + __asm __volatile("rfc %0" : "=r" (fpsr)); + printf("FPA11: FPCR=%08x", fpsr); + __asm __volatile("stmfd sp!, {r0}; mov r0, #0x00000e00 ; wfc r0; ldmfd sp!, {r0}"); + __asm __volatile("rfc %0" : "=r" (fpsr)); + printf("FPA11: FPCR=%08x", fpsr); +#endif + break; + + default : + cpus[cpu_number].fpu_class = FPU_CLASS_FPU; + break; + } + cpus[cpu_number].fpu_flags = 0; + install_coproc_handler(FP_COPROC, fpa_handler); + } else { + cpus[cpu_number].fpu_class = FPU_CLASS_NONE; + cpus[cpu_number].fpu_flags = 0; + +/* Ok if ARMFPE is defined and the boot options request the ARM FPE then it will + * be installed as the FPE. If the installation fails the existing FPE is used as + * a fall back. + * If either ARMFPE is not defined or the boot args did not request it the old FPE + * is installed. + * This is just while I work on integrating the new FPE. + * It means the new FPE gets installed if compiled int (ARMFPE defined) + * and also gives me a on/off option when I boot in case the new FPE is + * causing panics. + * In all cases it falls back on the existing FPE is the ARMFPE was not successfully + * installed. + */ + +#ifdef ARMFPE + if (boot_args) { + char *ptr; + + ptr = strstr(boot_args, "noarmfpe"); + if (!ptr) { + if (initialise_arm_fpe(&cpus[cpu_number]) != 0) { + identify_arm_fpu(cpu_number); +#ifdef FPE + initialise_fpe(&cpus[cpu_number]); +#endif + } +#ifdef FPE + } else + initialise_fpe(&cpus[cpu_number]); + + } else + initialise_fpe(&cpus[cpu_number]); +#else + } + } +#endif + +#else +#ifdef FPE + initialise_fpe(&cpus[cpu_number]); +#else +#error No FPE built in +#endif +#endif + } + + identify_arm_fpu(cpu_number); +} + + + +/* + * Report the type of the specifed arm processor. This uses the generic and arm specific + * information in the cpu structure to identify the processor. The remaining fields + * in the cpu structure are filled in appropriately. + */ + +void +identify_arm_cpu(cpu_number) + int cpu_number; +{ + cpu_t *cpu; + u_int cpuid; + + cpu = &cpus[cpu_number]; + if (cpu->cpu_host == CPU_HOST_NONE || cpu->cpu_class == CPU_CLASS_NONE) { + printf("No installed processor\n"); + return; + } + if (cpu->cpu_class != CPU_CLASS_ARM) { + printf("identify_arm_cpu: Can only identify ARM CPU's\n"); + return; + } + cpuid = cpu->cpu_id; + + if (cpuid == 0) { + printf("Processor failed probe - no CPU ID\n"); + return; + } + + if ((cpuid & CPU_ID_DESIGNER_MASK) != CPU_ID_ARM_LTD) + printf("Unrecognised designer ID = %08x\n", cpuid); + + switch (cpuid & CPU_ID_CPU_MASK) { + case ID_ARM610: + cpu->cpu_type = cpuid & CPU_ID_CPU_MASK; + break; + + case ID_ARM710 : + case ID_ARM700 : + cpu->cpu_type = (cpuid & CPU_ID_CPU_MASK) >> 4; + break; + + default : + printf("Unrecognised processor ID = %08x\n", cpuid); + cpu->cpu_type = cpuid & CPU_ID_CPU_MASK; + break; + } + + sprintf(cpu->cpu_model, "ARM%x rev %d", cpu->cpu_type, cpuid & CPU_ID_REVISION_MASK); + + if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0) + strcat(cpu->cpu_model, " IDC disabled"); + else + strcat(cpu->cpu_model, " IDC enabled"); + + if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0) + strcat(cpu->cpu_model, " WB disabled"); + else + strcat(cpu->cpu_model, " WB enabled"); + + if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE) + strcat(cpu->cpu_model, " LABT"); + else + strcat(cpu->cpu_model, " EABT"); + +/* Print the info */ + + printf(": %s\n", cpu->cpu_model); +} + + +/* + * Report the type of the specifed arm fpu. This uses the generic and arm specific + * information in the cpu structure to identify the fpu. The remaining fields + * in the cpu structure are filled in appropriately. + */ + +void +identify_arm_fpu(cpu_number) + int cpu_number; +{ + cpu_t *cpu; + + cpu = &cpus[cpu_number]; + if (cpu->cpu_host == CPU_HOST_NONE || cpu->cpu_class == CPU_CLASS_NONE) { + printf("No installed processor\n"); + return; + } + + if (cpu->cpu_class != CPU_CLASS_ARM) { + printf("identify_arm_cpu: Can only identify ARM FPU's\n"); + return; + } + +/* Now for the FP info */ + + switch (cpu->fpu_class) { + case FPU_CLASS_NONE : + strcpy(cpu->fpu_model, "None"); + break; + case FPU_CLASS_FPE : + printf("fpe%d at cpu%d: %s\n", cpu_number, cpu_number, cpu->fpu_model); + printf("fpe%d: no hardware found\n", cpu_number); + break; + case FPU_CLASS_FPA : + printf("fpe%d at cpu%d: %s\n", cpu_number, cpu_number, cpu->fpu_model); + if (cpu->fpu_type == FPU_TYPE_FPA11) { + strcpy(cpu->fpu_model, "FPA11"); + printf("fpe%d: fpa11 found\n", cpu_number); + } else { + strcpy(cpu->fpu_model, "FPA"); + printf("fpe%d: fpa10 found\n", cpu_number); + } + if ((cpu->fpu_flags & 4) == 0) + strcat(cpu->fpu_model, ""); + else + strcat(cpu->fpu_model, " clk/2"); + break; + case FPU_CLASS_FPU : + sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n", cpu->fpu_type); + printf("fpu%d at cpu%d: %s\n", cpu_number, cpu_number, cpu->fpu_model); + break; + } +} + + +int +cpuopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct cpu_softc *sc; + int unit; + int s; + + unit = minor(dev); + if (unit >= cpu_cd.cd_ndevs) + return(ENXIO); + + sc = cpu_cd.cd_devs[unit]; + if (!sc) return(ENXIO); + + s = splhigh(); + if (sc->sc_open) { + (void)splx(s); + return(EBUSY); + } + + ++sc->sc_open; + (void)splx(s); + + return(0); +} + + +int +cpuclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct cpu_softc *sc; + int unit; + int s; + + unit = minor(dev); + sc = cpu_cd.cd_devs[unit]; + + if (sc->sc_open == 0) return(ENXIO); + + s = splhigh(); + --sc->sc_open; + (void)splx(s); + + return(0); +} + + +int +cpuioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct cpu_softc *sc; + int unit; + + unit = minor(dev); + sc = cpu_cd.cd_devs[unit]; + + switch (cmd) { + default: + return(ENXIO); + break; + } + + return(0); +} + +/* End of cpu.c */ diff --git a/sys/arch/arm32/mainbus/fd.c b/sys/arch/arm32/mainbus/fd.c new file mode 100644 index 00000000000..d12e73d46b9 --- /dev/null +++ b/sys/arch/arm32/mainbus/fd.c @@ -0,0 +1,1339 @@ +/* $NetBSD: fd.c,v 1.5 1996/03/28 21:52:41 mark 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 + */ + +/* The new config stuff do no use to use and need to be pulled out */ + +#undef NEWCONFIG + +#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/irqhandler.h> +#include <machine/iomd.h> +#include <machine/io.h> +#include <machine/katelib.h> + +#include <arm32/mainbus/mainbus.h> +#include <arm32/mainbus/fdreg.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 */ + irqhandler_t sc_ih; + + int sc_iobase; + int sc_drq; + + 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 *)); +#ifdef NEWCONFIG +void fdcforceintr __P((void *)); +#endif +void fdcattach __P((struct device *, struct device *, void *)); + +static fiqhandler_t fiqhandler; + +void floppy_read_fiq __P((void)); +void floppy_write_fiq __P((void)); + +struct cfattach fdc_ca = { + sizeof(struct fdc_softc), fdcprobe, fdcattach +}; + +struct cfdriver fdc_cd = { + NULL, "fdc", DV_DULL +}; + +/* + * 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 +}; + +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 mainbus_attach_args *mb = aux; + int iobase = mb->mb_iobase; + + /* 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, 6/*2*/); /* Don't remember why - mark */ + +#ifdef NEWCONFIG + if (iobase == IOBASEUNK || ia->ia_drq == DRQUNK) + return 0; + + if (ia->ia_irq == IRQUNK) { + ia->ia_irq = isa_discoverintr(fdcforceintr, aux); + if (ia->ia_irq == IRQNONE) + return 0; + + /* reset it again */ + outb(iobase + fdout, 0); + delay(100); + outb(iobase + fdout, FDO_FRST); + } +#endif + + mb->mb_iosize = FDC_NPORT; + return 1; +} + +#ifdef NEWCONFIG +void +fdcforceintr(aux) + void *aux; +{ + struct mainbus_attach_args *mb = aux; + int iobase = mb->mb_iobase; + + /* the motor is off; this should generate an error with or + without a disk drive present */ + out_fdc(iobase, NE7CMD_SEEK); + out_fdc(iobase, 0); + out_fdc(iobase, 0); +} +#endif + +/* + * 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 mainbus_attach_args *mb = aux; + struct fdc_attach_args fa; + int type; + + fdc->sc_iobase = mb->mb_iobase; + fdc->sc_drq = mb->mb_iobase + mb->mb_drq; + fdc->sc_state = DEVIDLE; + TAILQ_INIT(&fdc->sc_drives); + + printf("\n"); + +/*#ifdef NEWCONFIG + at_setup_dmachan(fdc->sc_drq, FDC_MAXIOSIZE); + isa_establish(&fdc->sc_id, &fdc->sc_dev); +#endif*/ + + fdc->sc_ih.ih_func = fdcintr; + fdc->sc_ih.ih_arg = fdc; + fdc->sc_ih.ih_level = IPL_BIO; + fdc->sc_ih.ih_name = "fdc"; + if (irq_claim(mb->mb_irq, &fdc->sc_ih)) + panic("Cannot claim IRQ %d for fdc%d\n", mb->mb_irq, parent->dv_unit); + + /* + * The NVRAM info only tells us about the first two disks on the + * `primary' floppy controller. + */ +/* if (fdc->sc_dev.dv_unit == 0) + type = mc146818_read(NULL, NVRAM_DISKETTE); + else + type = -1;*/ + + type = 0x10; + + /* physical limit: four drives per controller. */ + for (fa.fa_drive = 0; fa.fa_drive < 4; 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; + /* + * XXX + * This is to work around some odd interactions between this driver + * and SMC Ethernet cards. + */ + + /* Don't need this for arm32 port but leave for the time being (it won't hurt) */ + + if (cf->cf_loc[0] == -1 && drive >= 2) + return 0; + + /* select drive and turn on motor */ + outb(iobase + fdout, drive | FDO_FRST | FDO_MOEN(drive)); + /* wait for motor to spin up */ + delay(250000); + 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; + + /* + * Initialize and attach the disk structure. + */ + fd->sc_dk.dk_name = fd->sc_dev.dv_xname; + fd->sc_dk.dk_driver = &fddkdriver; + disk_attach(&fd->sc_dk); + +#ifdef NEWCONFIG + /* XXX Need to do some more fiddling with sc_dk. */ + dk_establish(&fd->sc_dk, &fd->sc_dev); +#endif + /* 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; + switch (type) { +/* case 0x00 : + return NULL;*/ + case 0x10 : + return &fd_types[0]; + default: + printf("%s: drive %d: unknown device type 0x%x\n", + fdc, drive, type); + return NULL; + } +} + +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; + +/* printf("fdstrategy: bp=%08x\n", bp);*/ + + /* 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 */ + /* Instrumentation. */ + disk_busy(&fd->sc_dk); + 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; + +/* printf("fdstart:\n");*/ + + /* Link into controller queue. */ + fd->sc_q.b_active = 1; + TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); + + /* Instrumentation. */ +/* disk_busy(&fd->sc_dk);*/ + + /* 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; + +/* printf("fdfinish: fd=%08x buf=%08x busy=%d\n", (u_int)fd, (u_int)bp, fd->sc_dk.dk_busy);*/ + + disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid)); + + biodone(bp); + /* turn off motor 5s from now */ + timeout(fd_motor_off, fd, 5 * 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 = 100000, + 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 .25s for motor to stabilize. */ + timeout(fd_motor_on, fd, hz / 4); + 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_CONFIGURE);/* configure command */ + out_fdc(iobase, 0); + out_fdc(iobase, 0x1a); + out_fdc(iobase, 0); + + 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 + read = bp->b_flags & B_READ; +/*#ifdef NEWCONFIG + at_dma(read, bp->b_data + fd->sc_skip, fd->sc_nbytes, + fdc->sc_drq); +#else + isa_dmastart(read, bp->b_data + fd->sc_skip, fd->sc_nbytes, + fdc->sc_drq); +#endif*/ + if (read) + fiqhandler.fh_func = floppy_read_fiq; + else + fiqhandler.fh_func = floppy_write_fiq; + fiqhandler.fh_r11 = nblks * FDC_BSIZE; + fiqhandler.fh_r12 = (int)bp->b_data + (int)fd->sc_skip; + fiqhandler.fh_r13 = fdc->sc_drq; + fiqhandler.fh_mask = 0x01; + if (fiq_claim(&fiqhandler) == -1) + panic("Cannot claim FIQ vector\n"); + + 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: +/*#ifdef NEWCONFIG + at_dma_abort(fdc->sc_drq); +#else + isa_dmaabort(fdc->sc_drq); +#endif*/ + if (fiq_release(&fiqhandler) == -1) + panic("Cannot release FIQ vector\n"); + 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) { +/*#ifdef NEWCONFIG + at_dma_abort(fdc->sc_drq); +#else + isa_dmaabort(fdc->sc_drq); +#endif*/ + if (fiq_release(&fiqhandler) == -1) + panic("Cannot release FIQ vector\n"); +#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; + } +/*#ifdef NEWCONFIG + at_dma_terminate(fdc->sc_drq); +#else + read = bp->b_flags & B_READ; + isa_dmadone(read, bp->b_data + fd->sc_skip, fd->sc_nbytes, + fdc->sc_drq); +#endif*/ + if (fiq_release(&fiqhandler) == -1) + panic("Cannot release FIQ vector\n"); + + 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; + } +/* printf("IOCOMPLETE:calling fdfinish fd=%08x bp=%08x blkno=%d\n", (u_int)fd, (u_int)bp, fd->sc_blkno);*/ + 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;*/ + fdc->sc_state = DOSEEK; + 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 +} + + +#include "rd.h" +#if NRD > 0 + +#include <dev/ramdisk.h> + +int +load_ramdisc_from_floppy(rd, dev) + struct rd_conf *rd; + dev_t dev; +{ + struct buf *bp; + int loop; + int s; + int type; + int floppysize; + + if (major(dev) != 17) + return(EINVAL); + + if (rd->rd_type == RD_UNCONFIGURED || rd->rd_addr == 0) + return(EBUSY); + + type = FDTYPE(dev) - 1; + if (type < 0) type = 0; + floppysize = fd_types[type].size << (fd_types[type].secsize + 7); + + if (rd->rd_size < floppysize) { + printf("Ramdisc not big enough for floppy image\n"); + return(EINVAL); + } + +/* We have the ramdisk ! */ + + printf("Loading ramdisc : %4dK ", 0); + +/* obtain a buffer */ + + bp = geteblk(fd_types[type].sectrac * DEV_BSIZE); + +/* request no partition relocation by driver on I/O operations */ + + bp->b_dev = dev; + + s = spl0(); + + if (fdopen(bp->b_dev, 0) != 0) { + brelse(bp); + printf("Cannot open floppy device\n"); + return(EINVAL); + } + + for (loop = 0; + loop < (floppysize / DEV_BSIZE / fd_types[type].sectrac); + ++loop) { + printf("\x08\x08\x08\x08\x08\x08%4dK ", + loop * fd_types[type].sectrac * DEV_BSIZE / 1024); + bp->b_blkno = loop * fd_types[type].sectrac; + bp->b_bcount = fd_types[type].sectrac * DEV_BSIZE; + bp->b_flags = B_BUSY | B_READ; + bp->b_error = 0; + bp->b_resid = 0; + fdstrategy(bp); + + if (biowait(bp)) + panic("Cannot load floppy image\n"); + + bcopy((caddr_t)bp->b_data, (caddr_t)rd->rd_addr + + loop * fd_types[type].sectrac * DEV_BSIZE, + fd_types[type].sectrac * DEV_BSIZE); + } + printf("\x08\x08\x08\x08\x08\x08%4dK done\n", + loop * fd_types[type].sectrac * DEV_BSIZE / 1024); + + fdclose(bp->b_dev, 0); + + brelse(bp); + + splx(s); + return(0); +} + +#endif diff --git a/sys/arch/arm32/mainbus/fdreg.h b/sys/arch/arm32/mainbus/fdreg.h new file mode 100644 index 00000000000..bdfd8fe2760 --- /dev/null +++ b/sys/arch/arm32/mainbus/fdreg.h @@ -0,0 +1,102 @@ +/* $NetBSD: fdreg.h,v 1.2 1996/03/18 20:50:02 mark 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 + */ + + +/* + * Nec 765 floppy disc controller definitions + */ + +/* Main status register */ +#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */ +#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */ +#define NE7_CB 0x10 /* Diskette Controller Busy */ +#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */ +#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */ +#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ + +/* Status register ST0 */ +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" + +/* Status register ST1 */ +#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" + +/* Status register ST2 */ +#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" + +/* Status register ST3 */ +#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" + +/* Commands */ +#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit + parameters byte */ +#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ +#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ +#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ +#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ +#define NE7CMD_RECAL 7 /* recalibrate drive - requires + unit select byte */ +#define NE7CMD_SENSEI 8 /* sense controller interrupt status */ +#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte + and new cyl byte */ +#define NE7CMD_CONFIGURE 0x13 + +/* registers */ +#define fdout 8 /* 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 16 /* NEC 765 Main Status Register (R) */ +#define fddata 20 /* NEC 765 Data Register (R/W) */ + +#define fdctl 28 /* 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 28 /* Digital Input Register (R) */ +#define FDI_DCHG 0x80 /* diskette has been changed */ + +#define FDC_BSIZE 512 +#define FDC_NPORT 32 +#define FDC_MAXIOSIZE NBPG /* XXX should be MAXBSIZE */ diff --git a/sys/arch/arm32/mainbus/iic.c b/sys/arch/arm32/mainbus/iic.c new file mode 100644 index 00000000000..334b6030443 --- /dev/null +++ b/sys/arch/arm32/mainbus/iic.c @@ -0,0 +1,403 @@ +/* $NetBSD: iic.c,v 1.1 1996/04/19 19:49:03 mark Exp $ */ + +/* + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Mark Brinicombe. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * iic.c + * + * Routines to communicate with IIC devices + * + * Created : 13/10/94 + * + * Based of kate/display/iiccontrol.c + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <machine/io.h> +#include <machine/iomd.h> +#include <machine/katelib.h> +#include <machine/cpu.h> +#include <machine/irqhandler.h> +#include <machine/iic.h> +#include <arm32/mainbus/mainbus.h> + +/* Local function prototypes */ + +static int iic_getack __P((void)); +static void iic_write_bit __P((int bit)); +static int iic_write_byte __P((u_char value)); +static u_char iic_read_byte __P((void)); +static void iic_start_bit __P((void)); +static void iic_stop_bit __P((void)); + +struct iic_softc { + struct device sc_dev; + int sc_flags; +#define IIC_BROKEN 1 +#define IIC_OPEN 2 +#define IIC_BUSY 4 +}; + +void iicattach __P((struct device *parent, struct device *self, void *aux)); +int iicmatch __P((struct device *parent, void *match, void *aux)); + +/* + * Main entry to IIC driver. + */ + +int +iic_control(address, buffer, count) + u_char address; + u_char *buffer; + int count; +{ + int loop; + +/* Send the start bit */ + + iic_start_bit(); + +/* Send the address */ + + if (!iic_write_byte(address)) { + iic_stop_bit(); + return(-1); + } + +/* Read or write the data as required */ + + if ((address & 1) == 0) { +/* Write bytes */ + for (loop = 0; loop < count; ++loop) { + if (!iic_write_byte(buffer[loop])) { + iic_stop_bit(); + return(-1); + } + } + } + else { +/* Read bytes */ + for (loop = 0; loop < count; ++loop) { + buffer[loop] = iic_read_byte(); + +/* Send final acknowledge */ + + if (loop == (count - 1)) + iic_write_bit(1); + else + iic_write_bit(0); + } + } + +/* Send stop bit */ + + iic_stop_bit(); + + return(0); +} + + +static int +iic_getack() +{ + u_int oldirqstate; + int ack; + + iic_set_state(1, 0); + oldirqstate = disable_interrupts(I32_bit); + iic_set_state_and_ack(1, 1); + ack = ReadByte(IOMD_IOCR); + iic_set_state(1, 0); + restore_interrupts(oldirqstate); + + return((ack & 1) == 0); +} + + +static void +iic_write_bit(bit) + int bit; +{ + u_int oldirqstate; + + iic_set_state(bit, 0); + oldirqstate = disable_interrupts(I32_bit); + iic_set_state_and_ack(bit, 1); + iic_set_state(bit, 0); + restore_interrupts(oldirqstate); +} + + +static int +iic_write_byte(value) + u_char value; +{ + int loop; + int bit; + + for (loop = 0x80; loop != 0; loop = loop >> 1) { + bit = ((value & loop) != 0); + iic_write_bit(bit); + } + + return(iic_getack()); +} + + +static u_char +iic_read_byte() +{ + int loop; + u_char byte; + u_int oldirqstate; + + iic_set_state(1,0); + + byte = 0; + + for (loop = 0; loop < 8; ++loop) { + oldirqstate = disable_interrupts(I32_bit); + iic_set_state_and_ack(1, 1); + byte = (byte << 1) + (ReadByte(IOMD_IOCR) & 1); + iic_set_state(1, 0); + restore_interrupts(oldirqstate); + } + + return(byte); +} + + +static void +iic_start_bit() +{ + iic_set_state(1, 1); + iic_set_state(0, 1); + iic_delay(10); + iic_set_state(0, 0); +} + + +static void +iic_stop_bit() +{ + iic_set_state(0, 1); + iic_set_state(1, 1); +} + + +struct cfattach iic_ca = { + sizeof(struct iic_softc), iicmatch, iicattach +}; + +struct cfdriver iic_cd = { + NULL, "iic", DV_DULL, 0 +}; + +int +iicmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + int id; + +/* Make sure we have an IOMD we understand */ + + id = ReadByte(IOMD_ID0) | (ReadByte(IOMD_ID1) << 8); + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + case RC7500_IOC_ID: + return(1); + break; + default: + printf("iic: Unknown IOMD id=%04x", id); + break; + } + + return(0); +} + +int +iicprint(aux, name) + void *aux; + char *name; +{ + struct iicbus_attach_args *ib = aux; + + if (!name) { + if (ib->ib_addr) + printf(" addr 0x%02x", ib->ib_addr); + } + +/* XXXX print flags */ + return (QUIET); +} + + +int +iicsubmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct iicbus_attach_args *ib = aux; + + if (cf->cf_fstate == FSTATE_STAR) + panic("eekkk, I'm stuffed"); + + ib->ib_addr = cf->cf_loc[0]; + + if (ib->ib_addr == -1) + return(0); + + return((*cf->cf_attach->ca_match)(parent, match, aux)); +} + +void +iicattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct iicbus_attach_args iaa; + + printf("\n"); + + while (config_found_sm(self, &iaa, iicprint, iicsubmatch)); +} + + +int +iicopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct iic_softc *sc; + int unit = minor(dev); + + if (unit >= iic_cd.cd_ndevs) + return(ENXIO); + + sc = iic_cd.cd_devs[unit]; + + if (!sc) return(ENXIO); + + if (sc->sc_flags & IIC_OPEN) return(EBUSY); + + sc->sc_flags |= IIC_OPEN; + + return(0); +} + + +int +iicclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = minor(dev); + struct iic_softc *sc = iic_cd.cd_devs[unit]; + + sc->sc_flags &= ~IIC_OPEN; + + return(0); +} + + +int +iicread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = minor(dev); + struct iic_softc *sc = iic_cd.cd_devs[unit]; + + return(ENXIO); +} + + +int +iicwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = minor(dev); + struct iic_softc *sc = iic_cd.cd_devs[unit]; + + return(ENXIO); +} + + +int +iicioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct iic_softc *sc = iic_cd.cd_devs[minor(dev)]; + +/* + switch (cmd) { + case IICIOC_CONTROL: + if (iiccontrol() != 0) { + return(EIO); + } + return(0); + } +*/ + + return(EINVAL); +} + +/* End of iic.c */ diff --git a/sys/arch/arm32/mainbus/iic_asm.S b/sys/arch/arm32/mainbus/iic_asm.S new file mode 100644 index 00000000000..10ceb8a0238 --- /dev/null +++ b/sys/arch/arm32/mainbus/iic_asm.S @@ -0,0 +1,239 @@ +/* $NetBSD: iic_asm.S,v 1.1 1996/04/19 19:49:04 mark Exp $ */ + +/* + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Mark Brinicombe. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * iic.s + * + * Low level routines to with IIC devices + * + * Created : 13/10/94 + * + * Based of kate/display/iic.s + */ + +#include <machine/cpu.h> +#include <machine/iomd.h> + +#define IIC_BITDELAY 10 + +sp .req r13 +lr .req r14 +pc .req r15 + +.text + + .global _iic_set_state + +_iic_set_state: +/* + * Parameters + * r0 - IIC data bit + * r1 - IIC clock bit + */ + +/* Store temporary register */ +/* stmfd sp!, {r4}*/ + +/* + * Mask the data and clock bits + * Since these routines are only called from iiccontrol.c this is not + * really needed + */ + and r0, r0, #0x00000001 + and r1, r1, #0x00000001 + +/* Get address of IOMD control register */ + + mov r2, #(IOMD_BASE) + +/* Get the current CPSR */ +/* mrs r4, cpsr_all + orr r3, r4, #(I32_bit | F32_bit) + msr cpsr_all, r3 +*/ + + IRQdisable + +/* Get current value of control register */ + + ldrb r3, [r2, #(IOMD_IOCR - IOMD_BASE)] + +/* Preserve non-IIC bits */ + + bic r3, r3, #0x00000003 + orr r3, r3, #0x000000c0 + +/* Set the IIC clock and data bits */ + + orr r3, r3, r0 + orr r3, r3, r1, lsl #1 + +/* Store the new value of control register */ + + strb r3, [r2, #(IOMD_IOCR - IOMD_BASE)] + +/* Restore CPSR state */ +/* msr cpsr_all, r4 */ + + IRQenable + +/* Restore temporary register */ +/* ldmfd sp!, {r4} */ + +/* Pause a bit */ + + mov r0, #(IIC_BITDELAY) + +/* Exit via iic_delay routine */ + b _iic_delay + + + .global _iic_set_state_and_ack + +_iic_set_state_and_ack: +/* + * Parameters + * r0 - IIC data bit + * r1 - IIC clock bit + */ +/* Store temporary register */ +/* stmfd sp!, {r4} */ + +/* + * Mask the data and clock bits + * Since these routines are only called from iiccontrol.c this is not + * really needed + */ + + and r0, r0, #0x00000001 + and r1, r1, #0x00000001 + +/* Get address of IOMD control register */ + + mov r2, #(IOMD_BASE) + +/* Get the current CPSR */ +/* mrs r4, cpsr_all + orr r3, r4, #(I32_bit | F32_bit) + msr cpsr_all, r3 +*/ + IRQdisable + +/* Get current value of control register */ + + ldrb r3, [r2, #(IOMD_IOCR - IOMD_BASE)] + +/* Preserve non-IIC bits */ + + bic r3, r3, #0x00000003 + orr r3, r3, #0x000000c0 + +/* Set the IIC clock and data bits */ + + orr r3, r3, r0 + orr r3, r3, r1, lsl #1 + +/* Store the new value of control register */ + + strb r3, [r2, #(IOMD_IOCR - IOMD_BASE)] + +iic_set_state_and_ack_loop: + ldrb r3, [r2, #(IOMD_IOCR - IOMD_BASE)] + tst r3, #0x00000002 + beq iic_set_state_and_ack_loop + +/* Restore CPSR state */ +/* msr cpsr_all, r4 */ + + IRQenable + +/* Restore temporary register */ +/* ldmfd sp!, {r4} */ + +/* Pause a bit */ + + mov r0, #(IIC_BITDELAY) + +/* Exit via iic_delay routine */ + b _iic_delay + + + .global _iic_delay + +_iic_delay: +/* + * Parameters + * r0 - time to wait + */ + +/* Load address of IOMD */ + + mov r2, #(IOMD_BASE) + +/* Latch current value of timer 1 */ + + strb r2, [r2, #(IOMD_T0LATCH - IOMD_BASE)] + +/* Get the latched value */ + + ldrb r1, [r2, #(IOMD_T0LOW - IOMD_BASE)] + +/* Loop until timer reaches end value */ + +iic_delay_loop: + +/* Latch the current value of timer1 */ + + strb r2, [r2, #(IOMD_T0LATCH - IOMD_BASE)] + +/* Get the latched value */ + + ldrb r3, [r2, #(IOMD_T0LOW - IOMD_BASE)] + +/* Loop until timer reached expected value */ + + teq r3, r1 + movne r1, r3 + beq iic_delay_loop + + subs r0, r0, #0x00000001 + bne iic_delay_loop + +/* Exit */ + mov pc, lr + +/* End of iic_asm.S */ diff --git a/sys/arch/arm32/mainbus/kbd.c b/sys/arch/arm32/mainbus/kbd.c new file mode 100644 index 00000000000..3b85bdba7e8 --- /dev/null +++ b/sys/arch/arm32/mainbus/kbd.c @@ -0,0 +1,1357 @@ +/* $NetBSD: kbd.c,v 1.7 1996/03/28 21:55:15 mark Exp $ */ + +/* + * Copyright (c) 1994 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * kbd.c + * + * Keyboard driver functions + * + * Created : 09/10/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/reboot.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/vnode.h> +#include <sys/select.h> +#include <sys/fcntl.h> +#include <sys/signalvar.h> +#include <sys/time.h> + +#include <machine/irqhandler.h> +#include <machine/iomd.h> +#include <machine/vidc.h> +#include <machine/katelib.h> +#include <machine/kbd.h> +#include <arm32/mainbus/mainbus.h> +#include "vt.h" +#include "kbd.h" + +/* Declare global variables */ + +/* Declare external variables */ + +/* Local function prototypes */ + +/* Now for the main code */ + +/* Define the key_struct structure */ + +typedef struct { + int base_code; /* Base ASCII code */ + int shift_code; /* Shifted ASCII code */ + int ctrl_code; /* CTRL code */ + int alt_code; /* Alt code */ + int flags; /* Flags field */ +} key_struct; + +/* Define mappings for each possible code */ + +key_struct keys[256] = { +/* 0x00 - 0x0f */ + { 0x00, 0x00, 0x00, 0x00, 0x80 }, + { 0x89, 0x99, 0x00, 0x489, 0x00 }, + { 0x8a, 0x9a, 0x00, 0x00, 0x00 }, + { 0x85, 0x95, 0x00, 0x485, 0x00 }, + { 0x83, 0x93, 0x00, 0x483, 0x00 }, + { 0x81, 0x91, 0x00, 0x481, 0x00 }, + { 0x82, 0x92, 0x00, 0x482, 0x00 }, + { 0x8c, 0x9c, 0x00, 0x48c, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x88, 0x98, 0x00, 0x488, 0x00 }, + { 0x86, 0x96, 0x00, 0x486, 0x00 }, + { 0x84, 0x94, 0x00, 0x484, 0x00 }, + { 0x09, 0x09, 0x09, 0x09, 0x00 }, + { 0x60, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x10 - 0x1f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x84 }, + { 0x00, 0x00, 0x00, 0x00, 0x82 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x81 }, + { 0x71, 0x51, 0x11, 0x00, 0x40 }, + { 0x31, 0x21, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x7a, 0x5a, 0x1a, 0x00, 0x40 }, + { 0x73, 0x53, 0x13, 0x00, 0x40 }, + { 0x61, 0x41, 0x01, 0x00, 0x40 }, + { 0x77, 0x57, 0x17, 0x00, 0x40 }, + { 0x32, 0x22, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x20 - 0x2f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x63, 0x43, 0x03, 0x00, 0x40 }, + { 0x78, 0x58, 0x18, 0x00, 0x40 }, + { 0x64, 0x44, 0x04, 0x00, 0x40 }, + { 0x65, 0x45, 0x05, 0x00, 0x40 }, + { 0x34, 0x24, 0x00, 0x00, 0x00 }, + { 0x33, 0x23, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x20, 0x20, 0x20, 0x20, 0x00 }, + { 0x76, 0x56, 0x16, 0x00, 0x40 }, + { 0x66, 0x46, 0x06, 0x00, 0x40 }, + { 0x74, 0x54, 0x14, 0x00, 0x40 }, + { 0x72, 0x52, 0x12, 0x00, 0x40 }, + { 0x35, 0x25, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x30 - 0x3f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x6e, 0x4e, 0x0e, 0x00, 0x40 }, + { 0x62, 0x42, 0x02, 0x00, 0x40 }, + { 0x68, 0x48, 0x08, 0x00, 0x40 }, + { 0x67, 0x47, 0x07, 0x00, 0x40 }, + { 0x79, 0x59, 0x19, 0x00, 0x40 }, + { 0x36, 0x5e, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x6d, 0x4d, 0x0d, 0x00, 0x40 }, + { 0x6a, 0x4a, 0x0a, 0x00, 0x40 }, + { 0x75, 0x55, 0x15, 0x00, 0x40 }, + { 0x37, 0x26, 0x00, 0x00, 0x00 }, + { 0x38, 0x2a, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x40 - 0x4f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x2c, 0x3c, 0x00, 0x00, 0x00 }, + { 0x6b, 0x4b, 0x0b, 0x00, 0x40 }, + { 0x69, 0x49, 0x09, 0x00, 0x40 }, + { 0x6f, 0x4f, 0x0f, 0x00, 0x40 }, + { 0x30, 0x29, 0x00, 0x00, 0x00 }, + { 0x39, 0x28, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x2e, 0x3e, 0x00, 0x00, 0x00 }, + { 0x2f, 0x3f, 0x00, 0x00, 0x00 }, + { 0x6c, 0x4c, 0x0c, 0x00, 0x40 }, + { 0x3b, 0x3a, 0x00, 0x00, 0x00 }, + { 0x70, 0x50, 0x10, 0x00, 0x40 }, + { 0x2d, 0x5f, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x50 - 0x5f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x27, 0x40, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x5b, 0x7b, 0x00, 0x00, 0x00 }, + { 0x3d, 0x2b, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0xa0 }, + { 0x00, 0x00, 0x00, 0x00, 0x82 }, + { 0x0d, 0x0d, 0x0d, 0x00, 0x00 }, + { 0x5d, 0x7d, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x23, 0x7e, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x60 - 0x6f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x5c, 0x7c, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x08, 0x7f, 0x08, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x31, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x34, 0x00, 0x00, 0x00, 0x00 }, + { 0x37, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x70 - 0x7f */ + { 0x30, 0x00, 0x00, 0x00, 0x00 }, + { 0x2e, 0x00, 0x00, 0x00, 0x00 }, + { 0x32, 0x00, 0x00, 0x00, 0x00 }, + { 0x35, 0x00, 0x00, 0x00, 0x00 }, + { 0x36, 0x00, 0x00, 0x00, 0x00 }, + { 0x38, 0x00, 0x00, 0x00, 0x00 }, + { 0x1b, 0x1b, 0x21b, 0x1b, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x90 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x2b, 0x00, 0x00, 0x22b, 0x00 }, + { 0x33, 0x00, 0x00, 0x00, 0x00 }, + { 0x2d, 0x00, 0x00, 0x22d, 0x00 }, + { 0x2a, 0x00, 0x00, 0x00, 0x00 }, + { 0x39, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x88 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x80 - 0x8f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x87, 0x97, 0x00, 0x487, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x90 - 0x9f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xa0 - 0xaf */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xb0 - 0xbf */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xc0 - 0xcf */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xd0 - 0xdf */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xe0 - 0xef */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0xf0 - 0xff */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +/* Define mappings for each possible code */ + +key_struct E0keys[128] = { + +/* 0x00 - 0x0f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x10 - 0x1f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x84 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x81 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x20 - 0x2f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x30 - 0x3f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x40 - 0x4f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x2f, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x50 - 0x5f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x0d, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x60 - 0x6f */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x10b, 0x00, 0x00, 0x20b, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x102, 0x00, 0x00, 0x202, 0x00 }, + { 0x10a, 0x00, 0x00, 0x20a, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + +/* 0x70 - 0x7f */ + { 0x108, 0x00, 0x00, 0x208, 0x00 }, + { 0x109, 0x00, 0x00, 0x209, 0x00 }, + { 0x101, 0x105, 0x00, 0x201, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x103, 0x00, 0x00, 0x203, 0x00 }, + { 0x100, 0x104, 0x00, 0x200, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x104, 0x100, 0x00, 0x204, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x105, 0x101, 0x00, 0x205, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; + +/* Special keycodes */ + +#define KEYCODE_UP 0x100 +#define KEYCODE_DOWN 0x101 +#define KEYCODE_LEFT 0x102 +#define KEYCODE_RIGHT 0x103 +#define KEYCODE_PGUP 0x104 +#define KEYCODE_PGDN 0x105 +#define KEYCODE_INSERT 0x108 +#define KEYCODE_DELETE 0x109 +#define KEYCODE_HOME 0x10a +#define KEYCODE_END 0x10b + +/* Key modifiers flags */ + +#define MODIFIER_CTRL 0x01 +#define MODIFIER_SHIFT 0x02 +#define MODIFIER_ALT 0x04 +#define MODIFIER_MASK 0x07 + +#define MODIFIER_CAPS 0x20 +#define MODIFIER_NUM 0x10 +#define MODIFIER_SCROLL 0x08 +#define MODIFIER_LOCK_MASK 0x38 + +#define MODIFIER_CAPSLOCK 0x40 +#define MODIFIER_NORETURN 0x80 + +/* Keyboard buffer variables */ + +#define BUFFER_SIZE 32 +#define RAWKBD_BSIZE 128 + +static int autorepeatkey = -1; +static struct kbd_autorepeat kbdautorepeat = { 5, 20 }; +static int rawkbd_device = 0; +int modifiers = 0; +static int kbd_ack = 0; +static int kbd_resend = 0; + +extern int pmap_debug_level; + +struct kbd_softc { + struct device sc_device; + irqhandler_t sc_ih; + + int sc_state; +#define RAWKBD_OPEN 0x01 +#define KBD_OPEN 0x02 +#define RAWKBD_ASLEEP 0x04 + int sc_iobase; + struct clist sc_q; + struct selinfo sc_rsel; + struct proc *sc_proc; +}; + +#define KBDUNIT(u) (minor(u) / 2) +#define KBDFLAG(u) (minor(u) % 2) + +#define KBDFLAG_RAWUNIT 0 +#define KBDFLAG_CONUNIT 1 + +int kbdprobe __P((struct device *, void *, void *)); +void kbdattach __P((struct device *, struct device *, void *)); + +int kbdopen __P((dev_t, int, int, struct proc *)); +int kbdclose __P((dev_t, int, int, struct proc *)); +int kbdread __P((dev_t, struct uio *, int)); +int kbdselect __P((dev_t, int, struct proc *)); +int kbdioctl __P((dev_t, int, caddr_t, int, struct proc *)); + +void kbdinit __P((struct kbd_softc *sc)); +void kbdsetleds __P((int /*leds*/)); + +int PollKeyboard __P((int)); +int kbddecodekey __P((struct kbd_softc *, int)); +int kbdintr __P((struct kbd_softc *)); + +void autorepeatstart __P((void *)); +void autorepeat __P((void *)); + +struct cfattach kbd_ca = { + sizeof(struct kbd_softc), kbdprobe, kbdattach +}; + +struct cfdriver kbd_cd = { + NULL, "kbd", DV_TTY +}; + + +int +kbdprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ +/* struct mainbus_attach_args *mb = aux;*/ + int id; + +/* Make sure we have an IOMD we understand */ + + id = ReadByte(IOMD_ID0) | (ReadByte(IOMD_ID1) << 8); + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + case RC7500_IOC_ID: + return(1); + break; + default: + printf("kbd: Unknown IOMD id=%04x", id); + break; + } + + return(0); +} + + +void +kbdattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct kbd_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + + sc->sc_iobase = mb->mb_iobase; + + kbdinit(sc); + + printf("\n"); +} + + +int +kbdopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct kbd_softc *sc; + int unit = KBDUNIT(dev); + + if (unit >= kbd_cd.cd_ndevs) + return(ENXIO); + + sc = kbd_cd.cd_devs[unit]; + + if (!sc) return(ENXIO); + + switch (KBDFLAG(dev)) { + case KBDFLAG_RAWUNIT : + if (sc->sc_state & RAWKBD_OPEN) + return(EBUSY); + sc->sc_state |= RAWKBD_OPEN; + if (clalloc(&sc->sc_q, RAWKBD_BSIZE, 0) == -1) + return(ENOMEM); + sc->sc_proc = p; + rawkbd_device = 1; + break; + case KBDFLAG_CONUNIT : + if (sc->sc_state & KBD_OPEN) + return(EBUSY); + sc->sc_state |= KBD_OPEN; + break; + } + +/* Kill any active autorepeat */ + + untimeout(autorepeatstart, &autorepeatkey); + untimeout(autorepeat, &autorepeatkey); + + return(0); +} + + +int +kbdclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = KBDUNIT(dev); + struct kbd_softc *sc = kbd_cd.cd_devs[unit]; + + switch (KBDFLAG(dev)) { + case KBDFLAG_RAWUNIT : + if (!(sc->sc_state & RAWKBD_OPEN)) + return(EINVAL); + sc->sc_state &= ~RAWKBD_OPEN; + clfree(&sc->sc_q); + sc->sc_proc = NULL; + rawkbd_device = 0; + break; + case KBDFLAG_CONUNIT : + if (!(sc->sc_state & KBD_OPEN)) + return(EINVAL); + sc->sc_state &= ~KBD_OPEN; + break; + } + + return(0); +} + + +int +kbdread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)]; + int s; + int error = 0; + size_t length; + u_char buffer[128]; + + if (KBDFLAG(dev) == KBDFLAG_CONUNIT) + return(ENXIO); + + /* Block until keyboard activity occured. */ + + s = spltty(); + while (sc->sc_q.c_cc == 0) { + if (flag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + sc->sc_state |= RAWKBD_ASLEEP; + if ((error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdread", 0))) { + sc->sc_state &= (~RAWKBD_ASLEEP); + 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 +kbdselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)]; + int s; + int ret; + + if (KBDFLAG(dev) == KBDFLAG_CONUNIT) + return(ENXIO); + + 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; +} + + +int +kbdioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ +/* struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)];*/ + struct kbd_autorepeat *kbdar = (void *)data; + int *leds = (int *)data; + int s; + + switch (cmd) { + case KBD_GETAUTOREPEAT: +/* if (KBDFLAG(dev) == KBDFLAG_RAWUNIT) + return(EINVAL);*/ + + *kbdar = kbdautorepeat; + break; + case KBD_SETAUTOREPEAT: +/* if (KBDFLAG(dev) == KBDFLAG_RAWUNIT) + return(EINVAL);*/ + s = spltty(); + kbdautorepeat = *kbdar; + if (kbdautorepeat.ka_rate < 1) + kbdautorepeat.ka_rate = 1; + if (kbdautorepeat.ka_rate > 50) + kbdautorepeat.ka_rate = 50; + if (kbdautorepeat.ka_delay > 50) + kbdautorepeat.ka_delay = 50; + if (kbdautorepeat.ka_delay < 1) + kbdautorepeat.ka_delay = 1; + (void)splx(s); + break; + case KBD_SETLEDS: + kbdsetleds(*leds); + break; + default: + return(ENXIO); + } + return(0); +} + + +void +kbdsetleds(leds) + int leds; +{ + int loop; + + if ((ReadByte(IOMD_KBDCR) & 0x80)) { + WriteByte(IOMD_KBDDAT, 0xed); + loop = 10000; + while ((ReadByte(IOMD_KBDCR) & 0x40) && loop > 0) + --loop; + if ((ReadByte(IOMD_KBDCR) & 0x80)) { + WriteByte(IOMD_KBDDAT, leds); + loop = 10000; + while ((ReadByte(IOMD_KBDCR) & 0x40) && loop > 0) + --loop; + } + } +} + + +void +kbdsetstate(state) + int state; +{ + modifiers = state & MODIFIER_LOCK_MASK; +} + + +int +kdbgetstate() +{ + return(modifiers); +} + + +void +kbdinit(sc) + struct kbd_softc *sc; +{ + sc->sc_ih.ih_func = kbdintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_TTY; + sc->sc_ih.ih_name = "kbd rx"; + if (irq_claim(IRQ_KBDRX, &sc->sc_ih)) + panic("Cannot claim IRQ for kbd%d\n", sc->sc_device.dv_unit); + + modifiers = 0; + kbdsetleds((modifiers >> 3) & 7); +} + + +int +getkey_polled() +{ + int code; + int key; + int up; + key_struct *ks; + int s; + + s = splhigh(); + + key = 0; + + do { + while ((ReadByte(IOMD_KBDCR) & (1<<5)) == 0) ; + +/* Read the IOMD keyboard register and process the key */ + + code = PollKeyboard(ReadByte(IOMD_KBDDAT)); + + if (code != 0) { + up = (code & 0x100); + key = code & 0xff; + +/* printf("code=%04x mod=%04x\n", code, modifiers);*/ + +/* By default we use the main keycode lookup table */ + + ks = keys; + +/* If we have an E0 or E1 sqeuence we use the extended table */ + + if (code > 0x1ff) + ks = E0keys; + +/* Is the key a temporary modifier ? */ + + if (ks[key].flags & MODIFIER_MASK) { + if (up) + modifiers &= ~ks[key].flags; + else + modifiers |= ks[key].flags; + key = 0; + continue; + } + +/* Is the key a locking modifier ? */ + + if (ks[key].flags & MODIFIER_LOCK_MASK) { + if (!up) { + modifiers ^= ks[key].flags; + kbdsetleds((modifiers >> 3) & 7); + } + key = 0; + continue; + } + +/* Lookup the correct key code */ + + if (modifiers & 0x01) + key = ks[key].ctrl_code; + else if (modifiers & 0x02) + key = ks[key].shift_code; + else if (modifiers & 0x04) + key = ks[key].alt_code; + else + key = ks[key].base_code; + + if (modifiers & MODIFIER_CAPS) { + if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) + key ^= 0x20; + } + + if (up) + key = 0; + if (!up && key >= 0x200) { + +#if (NVT > 0) + if ((key & ~0x0f) == 0x480) + console_switch((key & 0x0f) - 1); + else +#endif + switch (key) { +#if (NVT > 0) + case 0x201: + console_scrollforward(); + break; + case 0x200: + console_scrollback(); + break; +#endif + default: + break; + } + key = 0; + } + } + } while (key == 0); + + if (key == '\r') + key = '\n'; + + splx(s); + return(key); +} + + +/* Keyboard IRQ handler */ + +int +kbdintr(sc) + struct kbd_softc *sc; +{ + int key; + +/* Read the IOMD keyboard register and process the key */ + + key = PollKeyboard(ReadByte(IOMD_KBDDAT)); + +/* If we have a raw keycode convert it to an ASCII code */ + + if (key != 0) + kbddecodekey(sc, key); + return(1); +} + + +/* Flags used to decode the raw keys */ + +#define FLAG_KEYUP 0x01 +#define FLAG_E0 0x02 +#define FLAG_E1 0x04 + +static int flags = 0; + +/* + * This function is now misnamed. + * It processes the raw key codes from the keyboard and generates + * a unique code that can be decoded with the key translation array. + */ + +int +PollKeyboard(code) + int code; +{ +/* printf("%02x.", code);*/ + +/* + * Set the keyup flag if this is the release code. + */ + + if (code == 0xf0) { + flags |= FLAG_KEYUP; + return(0); + } + +/* If it is a special code ignore it */ + + if (code == 0xff || code == 0x00) { + flags = 0; + return(0); + } + +/* If it is a resend code note it down */ + + if (code == 0xfe) { + printf("kbd:resend\n"); + kbd_resend = 1; + return(0); + } + +/* If it is an ack code note it down */ + + if (code == 0xfa) { +/* printf("kbd:ack\n");*/ + kbd_ack = 1; + return(0); + } + +/* Flag the start of an E0 sequence. */ + + if (code == 0xe0) { + flags |= FLAG_E0; + return(0); + } + +/* Flag the start of an E1 sequence. */ + + if (code == 0xe1) { + flags |= FLAG_E1; + return(0); + } + +/* Ignore any other invalid codes */ + + if (code > 0x8f) { + flags = 0; + return(0); + } + +/* printf("%02x:%02x.", code, flags);*/ + +/* Mark the code appropriately if it is part of an E0 sequence */ + + if (flags & FLAG_E0) { + flags &= ~FLAG_E0; + if (code == 0x12) { + flags &= ~FLAG_KEYUP; + return(0); + } + code |= 0x200; + } + +/* Mark the key if it is the upcode */ + + if (flags & FLAG_KEYUP) { + flags &= ~FLAG_KEYUP; + code |= 0x100; + } + +/* Mark the code appropriately if it is part of an E1 sequence */ + + if (flags & FLAG_E1) { + if ((code & 0xff) == 0x14) { + return(0); + } + flags &= ~FLAG_E1; + code |= 0x400; + flags &= ~FLAG_KEYUP; + } + + return(code); +} + + +/* + * This routine decodes the unique keycode and generates an ASCII code + * if necessary. + */ + +int +kbddecodekey(sc, code) + struct kbd_softc *sc; + int code; +{ + key_struct *ks; + int up; + int key; + + console_unblank(); + +/* Do we have the raw kbd device open ... */ + + if (rawkbd_device == 1 && code != 0) { + struct kbd_data buffer; + int s; + + /* Add this event to the queue. */ + + buffer.keycode = code; + microtime(&buffer.event_time); + s=spltty(); + (void) b_to_q((char *)&buffer, sizeof(buffer), &sc->sc_q); + splx(s); + selwakeup(&sc->sc_rsel); + + if (sc->sc_state & RAWKBD_ASLEEP) { + sc->sc_state &= ~RAWKBD_ASLEEP; + wakeup((caddr_t)sc); + } + + psignal(sc->sc_proc, SIGIO); + return(1); + } + + up = (code & 0x100); + key = code & 0xff; + +/* By default we use the main keycode lookup table */ + + ks = keys; + +/* If we have an E0 or E1 sqeuence we use the extended table */ + + if (code > 0x1ff) + ks = E0keys; + +/* Is the key a temporary modifier ? */ + + if (ks[key].flags & MODIFIER_MASK) { + if (up) + modifiers &= ~ks[key].flags; + else + modifiers |= ks[key].flags; + return(0); + } + +/* Is the key a locking modifier ? */ + + if (ks[key].flags & MODIFIER_LOCK_MASK) { + if (!up) { + modifiers ^= ks[key].flags; + kbdsetleds((modifiers >> 3) & 7); + } + return(0); + } + +/* Lookup the correct key code */ + + if (modifiers & 0x01) + key = ks[key].ctrl_code; + else if (modifiers & 0x02) + key = ks[key].shift_code; + else if (modifiers & 0x04) + key = ks[key].alt_code; + else + key = ks[key].base_code; + + if (modifiers & MODIFIER_CAPS) { + if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) + key ^= 0x20; + } + +/* If no valid code the key is not yet mapped so report error */ + +#ifdef DEBUG_TERM +/* if (key == 0) { + char err[80]; + + sprintf(err, "\n\rUnknown keycode %04x\n\r", code); + dprintf(err); + + }*/ +#endif + +/* If we have an ASCII code insert it into the keyboard buffer */ + + if (!up && key != 0) { + if (key >= 0x200) { + + untimeout(autorepeatstart, &autorepeatkey); + untimeout(autorepeat, &autorepeatkey); + autorepeatkey = -1; +#if (NVT > 0) + if ((key & ~0x0f) == 0x480) + console_switch((key & 0x0f) - 1); + else +#endif + switch (key) { + case 0x22b: + pmap_debug(pmap_debug_level + 1); + break; + case 0x22d: + pmap_debug(pmap_debug_level - 1); + break; +#if (NVT > 0) + case 0x201: + console_scrollforward(); + break; + case 0x200: + console_scrollback(); + break; + case 0x202: + console_switchdown(); + break; + case 0x203: + console_switchup(); + break; +#endif + case 0x204: + --kbdautorepeat.ka_rate; + if (kbdautorepeat.ka_rate < 1) + kbdautorepeat.ka_rate = 1; + break; + case 0x205: + ++kbdautorepeat.ka_rate; + if (kbdautorepeat.ka_rate > 50) + kbdautorepeat.ka_rate = 50; + break; + case 0x20a: + ++kbdautorepeat.ka_delay; + if (kbdautorepeat.ka_delay > 50) + kbdautorepeat.ka_delay = 50; + break; + case 0x20b: + --kbdautorepeat.ka_delay; + if (kbdautorepeat.ka_delay < 1) + kbdautorepeat.ka_delay = 1; + break; +#ifdef DDB + case 0x208: + Debugger(); + break; +#endif + case 0x21b: + printf("Kernel interruption\n"); + boot(RB_HALT); + break; + case 0x209: + printf("Kernel interruption - nosync\n"); + boot(RB_NOSYNC | RB_HALT); + break; + + default: + printf("Special key %04x\n", key); + break; + } + } else { + if (physconkbd(key) == 0 && rawkbd_device == 0) { + if (autorepeatkey != key) { + untimeout(autorepeatstart, &autorepeatkey); + untimeout(autorepeat, &autorepeatkey); + autorepeatkey = key; + timeout(autorepeatstart, &autorepeatkey, hz/kbdautorepeat.ka_delay); + } + } + + return(1); + } + } else { + untimeout(autorepeatstart, &autorepeatkey); + untimeout(autorepeat, &autorepeatkey); + autorepeatkey = -1; + } + return(0); +} + + +void +autorepeatstart(key) + void *key; +{ + physconkbd(*((int *)key)); + timeout(autorepeat, key, hz/kbdautorepeat.ka_rate); +} + + +void +autorepeat(key) + void *key; +{ + physconkbd(*((int *)key)); + timeout(autorepeat, key, hz/kbdautorepeat.ka_rate); +} + +/* End of kbd.c */ diff --git a/sys/arch/arm32/mainbus/lpt.c b/sys/arch/arm32/mainbus/lpt.c new file mode 100644 index 00000000000..35ff77c3f0d --- /dev/null +++ b/sys/arch/arm32/mainbus/lpt.c @@ -0,0 +1,1314 @@ +/* $NetBSD: lpt.c,v 1.6 1996/03/28 21:52:47 mark Exp $ */ + +/* + * Copyright (c) 1995 Mark Brinicombe + * 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. + * + * from:$NetBSD: lpt.c,v 1.6 1996/03/28 21:52:47 mark Exp $ + */ + +/* + * Device Driver for AT parallel printer port + */ +/* + * PLIP driver code added by Mark Brinicombe + */ + +#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/syslog.h> + +#if defined(INET) && defined(PLIP) +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <sys/time.h> +#include <net/bpf.h> +#endif +#endif + +#include <machine/cpu.h> +#include <machine/katelib.h> +#include <machine/irqhandler.h> +#include <machine/io.h> +#include <arm32/mainbus/lptreg.h> +#include <arm32/mainbus/mainbus.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(INET) && defined(PLIP) +#ifndef PLIPMTU /* MTU for the plip# interfaces */ +#if defined(COMPAT_PLIP10) +#define PLIPMTU 1600 +#else +#define PLIPMTU (ETHERMTU - ifp->if_hdrlen) +#endif +#endif + +#ifndef PLIPMXSPIN1 /* DELAY factor for the plip# interfaces */ +#define PLIPMXSPIN1 2000 /* Spinning for remote intr to happen */ +#endif + +#ifndef PLIPMXSPIN2 /* DELAY factor for the plip# interfaces */ +#define PLIPMXSPIN2 6000 /* Spinning for remote handshake to happen */ +#endif + +#ifndef PLIPMXERRS /* Max errors before !RUNNING */ +#define PLIPMXERRS 100 +#endif +#ifndef PLIPMXRETRY +#define PLIPMXRETRY 10 /* Max number of retransmits */ +#endif +#ifndef PLIPRETRY +#define PLIPRETRY hz/10 /* Time between retransmits */ +#endif +#endif + +#if !defined(DEBUG) || !defined(notdef) +#define lprintf +#else +#define lprintf if (lptdebug) printf +int lptdebug = 1; +#endif + +struct lpt_softc { + struct device sc_dev; + irqhandler_t sc_ih; + + size_t sc_count; + struct buf *sc_inbuf; + u_char *sc_cp; + int sc_spinmax; + int sc_iobase; + 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 */ +#define LPT_PLIP 0x08 /* busy with PLIP */ + 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; +#if defined(INET) && defined(PLIP) + struct arpcom sc_arpcom; + u_char *sc_ifbuf; + int sc_iferrs; + int sc_ifretry; +#if defined(COMPAT_PLIP10) + u_char sc_adrcksum; +#endif +#endif +}; + +int lptprobe __P((struct device *, void *, void *)); +void lptattach __P((struct device *, struct device *, void *)); +int lptintr __P((void *)); + +#if defined(INET) && defined(PLIP) +/* Functions for the plip# interface */ +static void plipattach(struct lpt_softc *,int); +static int plipioctl(struct ifnet *, u_long, caddr_t); +static void plipstart(struct ifnet *); +static int plipintr(struct lpt_softc *); +#endif + +struct cfattach lpt_ca = { + sizeof(struct lpt_softc), lptprobe, lptattach +}; + +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() ((inb(iobase + lpt_status) ^ LPS_INVERT) & LPS_MASK) +#define NOT_READY_ERR() not_ready(inb(iobase + 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 *)); + +/* + * Internal routine to lptprobe to do port tests of one byte value. + */ +int +lpt_port_test(port, data, mask) + int port; + u_char data, mask; +{ + int timeout; + u_char temp; + + data &= mask; + outb(port, data); + timeout = 1000; + do { + delay(10); + temp = inb(port) & mask; + } while (temp != data && --timeout); + lprintf("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", port, 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 + */ +int +lptprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct mainbus_attach_args *mb = aux; + int iobase = mb->mb_iobase; + int port; + 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 + + port = iobase + lpt_data; + mask = 0xff; + + data = 0x55; /* Alternating zeros */ + if (!lpt_port_test(port, data, mask)) + ABORT; + + data = 0xaa; /* Alternating ones */ + if (!lpt_port_test(port, data, mask)) + ABORT; + + for (i = 0; i < CHAR_BIT; i++) { /* Walking zero */ + data = ~(1 << i); + if (!lpt_port_test(port, data, mask)) + ABORT; + } + + for (i = 0; i < CHAR_BIT; i++) { /* Walking one */ + data = (1 << i); + if (!lpt_port_test(port, data, mask)) + ABORT; + } + + outb(iobase + lpt_data, 0); + outb(iobase + lpt_control, 0); + + mb->mb_iosize = LPT_NPORTS; + return 1; +} + +void +lptattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct lpt_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + int iobase = mb->mb_iobase; + + if (mb->mb_irq != IRQUNK) + printf("\n"); + else + printf(": polled\n"); + + sc->sc_iobase = iobase; + sc->sc_irq = mb->mb_irq; + sc->sc_state = 0; + outb(iobase + lpt_control, LPC_NINIT); + + if (mb->mb_irq != IRQUNK) { + sc->sc_ih.ih_func = lptintr; + sc->sc_ih.ih_arg = sc; +#if defined(INET) && defined(PLIP) + sc->sc_ih.ih_level = IPL_NET; + sc->sc_ih.ih_name = "lpt/plip"; + plipattach(sc, self->dv_unit); +#else + sc->sc_ih.ih_level = IPL_NONE; + sc->sc_ih.ih_name = "lpt"; +#endif + if (irq_claim(mb->mb_irq, &sc->sc_ih)) + panic("Cannot claim IRQ %d for lpt%d\n", mb->mb_irq, sc->sc_dev.dv_unit); + + } +#if defined(INET) && defined(PLIP) + else { + printf("Warning PLIP device needs IRQ driven lpt driver\n"); + } +#endif +} + +/* + * Reset the printer, then wait until it's selected and not busy. + */ +int +lptopen(dev, flag) + dev_t dev; + int flag; +{ + int unit = LPTUNIT(dev); + u_char flags = LPTFLAGS(dev); + struct lpt_softc *sc; + int iobase; + 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); + iobase = sc->sc_iobase; + + if ((flags & LPT_NOPRIME) == 0) { + /* assert INIT for 100 usec to start up printer */ + outb(iobase + lpt_control, LPC_SELECT); + delay(100); + } + + control = LPC_SELECT | LPC_NINIT; + outb(iobase + 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 */ + if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen", + STEP) != 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; + outb(iobase + 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. + */ +lptclose(dev, flag) + dev_t dev; + int flag; +{ + int unit = LPTUNIT(dev); + struct lpt_softc *sc = lpt_cd.cd_devs[unit]; + int iobase = sc->sc_iobase; + + if (sc->sc_count) + (void) pushbytes(sc); + + if ((sc->sc_flags & LPT_NOINTR) == 0) + untimeout(lptwakeup, sc); + + outb(iobase + lpt_control, LPC_NINIT); + sc->sc_state = 0; + outb(iobase + 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; +{ + int iobase = sc->sc_iobase; + 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; + } + + outb(iobase + lpt_data, *sc->sc_cp++); + outb(iobase + lpt_control, control | LPC_STROBE); + sc->sc_count--; + outb(iobase + 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); + } + if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH, + "lptwrite2", 0)) + 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. + */ +lptwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)]; + size_t n; + int error = 0; + + while (n = min(LPT_BSIZE, uio->uio_resid)) { + 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; + int iobase = sc->sc_iobase; + +/*printf("lptintr:\n");*/ + +#if defined(INET) && defined(PLIP) + if (sc->sc_arpcom.ac_if.if_flags & IFF_UP) { + return(plipintr(sc)); + } +#endif + +#if 0 + if ((sc->sc_state & LPT_OPEN) == 0) + return 0; +#endif + + /* is printer online and ready for output */ + if (NOT_READY() && NOT_READY_ERR()) + return 0; + + if (sc->sc_count) { + u_char control = sc->sc_control; + /* send char */ + outb(iobase + lpt_data, *sc->sc_cp++); + outb(iobase + lpt_control, control | LPC_STROBE); + sc->sc_count--; + outb(iobase + 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) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; +{ + int error = 0; + + switch (cmd) { + default: + error = ENODEV; + } + + return error; +} + + +#if defined(INET) && defined(PLIP) + +#define PLIP_INTR_ENABLE (LPC_NINIT | LPC_SELECT | LPC_IENABLE) +#define PLIP_INTR_DISABLE (LPC_NINIT | LPC_SELECT) +#define PLIP_DATA (iobase + lpt_data) +#define PLIP_STATUS (iobase + lpt_status) +#define PLIP_CONTROL (iobase + lpt_control) +#define PLIP_REMOTE_TRIGGER 0x08 +#define PLIP_DELAY_UNIT 50 +#if PLIP_DELAY_UNIT > 0 +#define PLIP_DELAY DELAY(PLIP_DELAY_UNIT) +#else +#define PLIP_DELAY +#endif +#define PLIP_DEBUG_RX 0x01 +#define PLIP_DEBUG_TX 0x02 +#define PLIP_DEBUG_IF 0x04 +#define PLIP_DEBUG 0x07 +#if PLIP_DEBUG != 0 +static int plip_debug = PLIP_DEBUG; +#endif + +static void +plipattach(struct lpt_softc *sc, int unit) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + + sc->sc_ifbuf = NULL; + ifp->if_unit = unit; + ifp->if_name = "plip"; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_output = ether_output; + ifp->if_start = plipstart; + ifp->if_ioctl = plipioctl; + ifp->if_watchdog = 0; + + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + ifp->if_mtu = PLIPMTU; + + if_attach(ifp); + ether_ifattach(ifp); + + printf("plip%d at lpt%d: mtu=%d,%d,%d", unit, unit, (int) ifp->if_mtu, + ifp->if_hdrlen, ifp->if_addrlen); + if (sizeof(struct ether_header) != 14) + printf(" ethhdr super kludge mode enabled\n"); + else + printf("\n"); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +} + +/* + * Process an ioctl request. + */ +static int +plipioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct proc *p = curproc; + struct lpt_softc *sc = (struct lpt_softc *) lpt_cd.cd_devs[ifp->if_unit]; + unsigned int iobase = sc->sc_iobase; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s; + int error = 0; + +#if PLIP_DEBUG > 0 + printf("plipioctl: cmd=%08x ifp=%08x data=%08x\n", cmd, ifp, data); + printf("plipioctl: ifp->flags=%08x\n", ifp->if_flags); +#endif + + switch (cmd) { + + case SIOCSIFFLAGS: + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~IFF_RUNNING; +/* Deactive the parallel port */ +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_IF) + printf("plip: Disabling lpt irqs\n"); +#endif + outb(PLIP_DATA, 0x00); + outb(PLIP_CONTROL, PLIP_INTR_DISABLE); + sc->sc_state = 0; + + if (sc->sc_ifbuf) + free(sc->sc_ifbuf, M_DEVBUF); + + sc->sc_ifbuf = NULL; + } + if (((ifp->if_flags & IFF_UP)) && + ((ifp->if_flags & IFF_RUNNING) == 0)) { + if (sc->sc_state) { + error = EBUSY; + break; + } +/* if (!(ifp->if_flags & IFF_DEBUG)) + plip_debug = PLIP_DEBUG; + else + plip_debug = 0;*/ + sc->sc_state = LPT_OPEN | LPT_PLIP; + if (!sc->sc_ifbuf) + sc->sc_ifbuf = + malloc(ifp->if_mtu + ifp->if_hdrlen, + M_DEVBUF, M_WAITOK); + ifp->if_flags |= IFF_RUNNING; +/* This starts it running */ +/* Enable lpt interrupts */ + +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_IF) + printf("plip: Enabling lpt irqs\n"); +#endif + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); + outb(PLIP_DATA, 0x00); + } + break; + + case SIOCSIFADDR: + if (ifa->ifa_addr->sa_family == AF_INET) { + if (!sc->sc_ifbuf) + sc->sc_ifbuf = + malloc(PLIPMTU + ifp->if_hdrlen, + M_DEVBUF, M_WAITOK); + + sc->sc_arpcom.ac_enaddr[0] = 0xfc; + sc->sc_arpcom.ac_enaddr[1] = 0xfc; + bcopy((caddr_t)&IA_SIN(ifa)->sin_addr, + (caddr_t)&sc->sc_arpcom.ac_enaddr[2], 4); + sc->sc_arpcom.ac_ipaddr = IA_SIN(ifa)->sin_addr; +#if defined(COMPAT_PLIP10) + if (ifp->if_flags & IFF_LINK0) { + int i; + sc->sc_arpcom.ac_enaddr[0] = 0xfd; + sc->sc_arpcom.ac_enaddr[1] = 0xfd; + for (i = sc->sc_adrcksum = 0; i < 5; i++) + sc->sc_adrcksum += sc->sc_arpcom.ac_enaddr[i]; + sc->sc_adrcksum *= 2; + } +#endif + ifp->if_flags |= IFF_RUNNING | IFF_UP; +#if 0 + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + struct sockaddr_dl *sdl; + if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && + sdl->sdl_family == AF_LINK) { + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ifp->if_addrlen; + bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr, + LLADDR(sdl), ifp->if_addrlen); + break; + } + } +#endif +/* Looks the same as the start condition above */ +/* Enable lpt interrupts */ + +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_IF) + printf("plip: Enabling lpt irqs\n"); +#endif + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); + outb(PLIP_DATA, 0x00); + + arp_ifinit(&sc->sc_arpcom, ifa); + } else + error = EAFNOSUPPORT; + break; + + case SIOCAIFADDR: + case SIOCDIFADDR: + case SIOCSIFDSTADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + error = EAFNOSUPPORT; + break; + + case SIOCSIFMTU: + if ((error = suser(p->p_ucred, &p->p_acflag))) + return(error); + if (ifp->if_mtu != ifr->ifr_metric) { + ifp->if_mtu = ifr->ifr_metric; + if (sc->sc_ifbuf) { + s = splimp(); + + free(sc->sc_ifbuf, M_DEVBUF); + sc->sc_ifbuf = + malloc(ifp->if_mtu + ifp->if_hdrlen, + M_DEVBUF, M_WAITOK); + splx(s); + } + } + break; + + case SIOCGIFMTU: + ifr->ifr_metric = ifp->if_mtu; + break; + + default: + error = EINVAL; + } + return (error); +} + +static int +plipreceive(unsigned int iobase, u_char *buf, int len) +{ + int i; + u_char cksum = 0, c; + u_char c0, c1; + +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("Rx: "); +#endif + + while (len--) { + i = PLIPMXSPIN2; +/* Receive a byte */ + +/* Wait for a steady handshake */ + while (1) { + c0 = inb(PLIP_STATUS); + if ((c0 & LPS_NBSY) == 0) { + c1 = inb(PLIP_STATUS); + if (c0 == c1) break; +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("rx: %02x-%02x ", c0, c1); +#endif + } + --i; + if (i < 0) { +#if PLIP_DEBUG > 0 + printf("timeout rx lsn %02x\n", c0); +#endif + return(-1); + } + PLIP_DELAY; + } + c = (c0 >> 3) & 0x0f; + +/* Acknowledge */ + outb(PLIP_DATA, 0x10); + +/* Another handshake */ + i = PLIPMXSPIN2; + while (1) { + c0 = inb(PLIP_STATUS); + if (c0 & LPS_NBSY) { + c1 = inb(PLIP_STATUS); + if (c0 == c1) break; +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("rx: %02x-%02x ", c0, c1); +#endif + } + --i; + if (i < 0) { +#if PLIP_DEBUG > 0 + printf("timeout rx msn %02x\n", c0); +#endif + return(-1); + } + PLIP_DELAY; + } + c = c | ((c0 << 1) & 0xf0); +/* Acknowledge */ + outb(PLIP_DATA, 0x00); +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("%02x ", c); +#endif + + cksum += (*buf++ = c); + } +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("\n"); +#endif + return(cksum); +} + +static int +plipintr(struct lpt_softc *sc) +{ + extern struct mbuf *m_devget(char *, int, int, struct ifnet *, void (*)()); + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + unsigned int iobase = sc->sc_iobase; + struct mbuf *m; + struct ether_header *eh; + u_char *p = sc->sc_ifbuf, minibuf[4]; + int c, s, len, cksum; + u_char c0; + +printf("plipintr:\n"); + +/* Get the status */ + + c0 = inb(PLIP_STATUS); +#if PLIP_DEBUG > 0 + if ((c0 & 0xf8) != 0xc0) { + printf("st5=%02x ", c0); + } +#endif + +/* Don't want ints while receiving */ + + outb(PLIP_CONTROL, PLIP_INTR_DISABLE); + + outb(PLIP_DATA, 0x01); /* send ACK */ /* via NERR */ + +#if defined(COMPAT_PLIP10) + if (ifp->if_flags & IFF_LINK0) { + if (plipreceive(iobase, minibuf, 3) < 0) goto err; + len = (minibuf[1] << 8) | minibuf[2]; + if (len > (ifp->if_mtu + ifp->if_hdrlen)) goto err; + + switch (minibuf[0]) { + case 0xfc: + p[0] = p[ 6] = ifp->ac_enaddr[0]; + p[1] = p[ 7] = ifp->ac_enaddr[1]; + p[2] = p[ 8] = ifp->ac_enaddr[2]; + p[3] = p[ 9] = ifp->ac_enaddr[3]; + p[4] = p[10] = ifp->ac_enaddr[4]; + p += 5; + if ((cksum = plipreceive(iobase, p, 1)) < 0) goto err; + p += 6; + if ((c = plipreceive(iobase, p, len - 11)) < 0) goto err; + cksum += c + sc->sc_adrcksum; + c = p[1]; p[1] = p[2]; p[2] = c; + cksum &= 0xff; + break; + case 0xfd: + if ((cksum = plipreceive(iobase, p, len)) < 0) goto err; + break; + default: + goto err; + } + } else +#endif + { + if (plipreceive(iobase, minibuf, 2) < 0) goto err; + len = (minibuf[1] << 8) | minibuf[0]; + if (len > (ifp->if_mtu + ifp->if_hdrlen)) { + log(LOG_NOTICE, "plip%d: packet > MTU\n", ifp->if_unit); + goto err; + } +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("len=%d ", len); +#endif + if (sizeof(struct ether_header) != 14) { + if ((cksum = plipreceive(iobase, p, 14)) < 0) goto err; + if ((c = plipreceive(iobase, p+16, len-14)) < 0) goto err; + cksum += c; + len += 2; + } + else + if ((cksum = plipreceive(iobase, p, len)) < 0) goto err; + } + + if (plipreceive(iobase, minibuf, 1) < 0) goto err; + if ((cksum & 0xff) != minibuf[0]) { + printf("cksum=%d, %d, %d\n", cksum, c, minibuf[0]); + log(LOG_NOTICE, "plip%d: checksum error\n", ifp->if_unit); + goto err; + } + + outb(PLIP_DATA, 0x00); /* clear ACK */ /* via NERR */ +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_RX) + printf("done\n"); +#endif + s = splimp(); + + eh = (struct ether_header *)sc->sc_ifbuf; + + if ((m = m_devget(sc->sc_ifbuf + sizeof(struct ether_header), len - sizeof(struct ether_header), 0, ifp, NULL))) { + /* We assume that the header fit entirely in one mbuf. */ +/* eh = mtod(m, struct ether_header *);*/ +/* m->m_pkthdr.len -= sizeof(*eh);*/ +/* m->m_len -= sizeof(*eh);*/ +/* m->m_data += sizeof(*eh);*/ +/* printf("m->m_data=%08x ifbuf=%08x eh=%08x\n", m->m_data, sc->sc_ifbuf, eh);*/ + +#if NBPFILTER > 0 +/* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->sc_arpcom.ac_if.if_bpf) { + bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, m); + } +#endif + ether_input(ifp, eh, m); + } + splx(s); + sc->sc_iferrs = 0; + ifp->if_ipackets++; + +/* Allow ints again */ + + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); + return(1); + +err: + outb(PLIP_DATA, 0x00); /* clear ACK */ /* via NERR */ + + ifp->if_ierrors++; + sc->sc_iferrs++; + if (sc->sc_iferrs > PLIPMXERRS + || (sc->sc_iferrs > 5 && (inb(iobase + lpt_status) & LPS_NBSY))) { + /* We are not able to send receive anything for now, + * so stop wasting our time and leave the interrupt + * disabled. + */ + if (sc->sc_iferrs == PLIPMXERRS + 1) + log(LOG_NOTICE, "plip%d: rx hard error\n", ifp->if_unit); +/* xxx i8255->port_a |= LPA_ACTIVE;*/ + } else +; +/* xxx i8255->port_a |= LPA_ACKENABLE | LPA_ACTIVE;*/ + +/* Allow ints again */ + + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); + return(1); +} + +static int +pliptransmit(unsigned int iobase, u_char *buf, int len) +{ + int i; + u_char cksum = 0, c; + u_char c0; +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("tx: len=%d ", len); +#endif + + while (len--) { + i = PLIPMXSPIN2; + cksum += (c = *buf++); +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("%02x ", c); +#endif +/* xxx while ((i8255->port_c & LPC_NBUSY) == 0) + if (i-- < 0) return -1; + i8255->port_b = c & 0x0f; + i8255->port_b = c & 0x0f | 0x10; + c >>= 4; + while ((i8255->port_c & LPC_NBUSY) != 0) + if (i-- < 0) return -1; + i8255->port_b = c | 0x10; + i8255->port_b = c; +*/ + +/* Send the nibble + handshake */ + + outb(PLIP_DATA, 0x00 | (c & 0x0f)); + outb(PLIP_DATA, 0x10 | (c & 0x0f)); + + while (1) { + c0 = inb(PLIP_STATUS); + if ((c0 & LPS_NBSY) == 0) + break; + if (--i == 0) { /* time out */ +#if PLIP_DEBUG > 0 + printf("timeout tx lsn %02x ", c0); +#endif + return(-1); + } + PLIP_DELAY; + } + + outb(PLIP_DATA, 0x10 | (c >> 4)); + outb(PLIP_DATA, 0x00 | (c >> 4)); + i = PLIPMXSPIN2; + while (1) { + c0 = inb(PLIP_STATUS); + if ((c0 & LPS_NBSY) != 0) + break; + if (--i == 0) { /* time out */ +#if PLIP_DEBUG > 0 + printf("timeout tx msn %02x ", c0); +#endif + return(-1); + } + PLIP_DELAY; + } + } +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("done\n"); +#endif + return(cksum); +} + +/* + * Setup output on interface. + */ +static void +plipstart(struct ifnet *ifp) +{ + struct lpt_softc *sc = (struct lpt_softc *) lpt_cd.cd_devs[ifp->if_unit]; + unsigned int iobase = sc->sc_iobase; + struct mbuf *m0, *m; + u_char minibuf[4], cksum; + int len, i, s; + u_char *p; + +#if PLIP_DEBUG != 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("plipstart: "); +#endif + if (ifp->if_flags & IFF_OACTIVE) + return; + ifp->if_flags |= IFF_OACTIVE; + + if (sc->sc_ifretry) + untimeout((void (*)(void *))plipstart, ifp); + + for (;;) { + s = splimp(); + IF_DEQUEUE(&ifp->if_snd, m0); + splx(s); + if (!m0) + break; + + for (len = 0, m = m0; m; m = m->m_next) { +#if PLIP_DEBUG > 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("len=%d %d\n", m->m_len, len); +#endif + len += m->m_len; + } +#if NBPFILTER > 0 + p = sc->sc_ifbuf; + for (m = m0; m; m = m->m_next) { + if (m->m_len == 0) + continue; + bcopy(mtod(m, u_char *), p, m->m_len); + p += m->m_len; + } + if (sc->sc_arpcom.ac_if.if_bpf) + bpf_tap(sc->sc_arpcom.ac_if.if_bpf, sc->sc_ifbuf, len); +#endif + if (sizeof(struct ether_header) != 14) + len -= 2; +#if defined(COMPAT_PLIP10) + if (ifp->if_flags & IFF_LINK0) { + minibuf[0] = 3; + minibuf[1] = 0xfd; + minibuf[2] = len >> 8; + minibuf[3] = len; + } else +#endif + { + minibuf[0] = 2; + minibuf[1] = len; + minibuf[2] = len >> 8; + } + +/*yyy for (i = PLIPMXSPIN1; (inb(PLIP_STATUS) & LPS_NERR) != 0; i--) + if (i < 0) goto retry;*/ + + /* Trigger remote interrupt */ + +#if PLIP_DEBUG > 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("st=%02x ", inb(PLIP_STATUS)); +#endif + if (inb(PLIP_STATUS) & LPS_NERR) { + for (i = PLIPMXSPIN1; (inb(PLIP_STATUS) & LPS_NERR) != 0; i--) + PLIP_DELAY; +#if PLIP_DEBUG > 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("st1=%02x ", inb(PLIP_STATUS)); +#endif + } + + outb(PLIP_DATA, PLIP_REMOTE_TRIGGER); + for (i = PLIPMXSPIN1; (inb(PLIP_STATUS) & LPS_NERR) == 0; i--) { + if (i < 0 || (i > PLIPMXSPIN1/3 + && inb(PLIP_STATUS) & LPS_NACK)) { +#if PLIP_DEBUG > 0 + printf("trigger ack timeout\n"); +#endif + goto retry; + } + PLIP_DELAY; + } +#if PLIP_DEBUG > 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("st3=%02x ", inb(PLIP_STATUS)); +#endif + +/* Don't want ints while transmitting */ + + outb(PLIP_CONTROL, PLIP_INTR_DISABLE); + + if (pliptransmit(iobase, minibuf + 1, minibuf[0]) < 0) goto retry; + for (cksum = 0, m = m0; m; m = m->m_next) { + if (sizeof(struct ether_header) != 14 && m == m0) { + i = pliptransmit(iobase, mtod(m, u_char *), 14); + if (i < 0) goto retry; + cksum += i; + i = pliptransmit(iobase, mtod(m, u_char *)+16, m->m_len-16); + if (i < 0) goto retry; + } + else + i = pliptransmit(iobase, mtod(m, u_char *), m->m_len); + if (i < 0) goto retry; + cksum += i; + } + if (pliptransmit(iobase, &cksum, 1) < 0) goto retry; + i = PLIPMXSPIN2; + while ((inb(PLIP_STATUS) & LPS_NBSY) == 0) + if (i-- < 0) goto retry; + + outb(iobase + lpt_data, 0x00); +/* Re-enable ints */ + + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); + + ifp->if_opackets++; + ifp->if_obytes += len + 4; + sc->sc_ifretry = 0; + s = splimp(); + m_freem(m0); + splx(s); + } + ifp->if_flags &= ~IFF_OACTIVE; + return; + +retry: +#if PLIP_DEBUG > 0 + if (plip_debug & PLIP_DEBUG_TX) + printf("retry: %02x", inb(iobase + lpt_status)); +#endif + if (inb(PLIP_STATUS & LPS_NACK)) + ifp->if_collisions++; + else + ifp->if_oerrors++; + if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == (IFF_RUNNING | IFF_UP) + && sc->sc_ifretry < PLIPMXRETRY) { + sc->sc_ifretry++; + s = splimp(); + IF_PREPEND(&ifp->if_snd, m0); + splx(s); + timeout((void (*)(void *))plipstart, ifp, PLIPRETRY); + } else { + if (sc->sc_ifretry == PLIPMXRETRY) { + sc->sc_ifretry++; + log(LOG_NOTICE, "plip%d: tx hard error\n", ifp->if_unit); + } + s = splimp(); + m_freem(m0); + splx(s); + } + ifp->if_flags &= ~IFF_OACTIVE; + outb(PLIP_DATA, 0x00); + +/* Re-enable ints */ + + outb(PLIP_CONTROL, PLIP_INTR_ENABLE); +/*xxx if (sc->sc_iferrs > PLIPMXERRS) + i8255->port_a |= LPA_ACTIVE; + else + i8255->port_a |= LPA_ACKENABLE | LPA_ACTIVE;*/ + return; +} + +#endif diff --git a/sys/arch/arm32/mainbus/lptreg.h b/sys/arch/arm32/mainbus/lptreg.h new file mode 100644 index 00000000000..f909794337d --- /dev/null +++ b/sys/arch/arm32/mainbus/lptreg.h @@ -0,0 +1,64 @@ +/* $NetBSD: lptreg.h,v 1.2 1996/03/18 20:50:03 mark Exp $ */ + +/* + * 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. + * + * 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. + * + * @(#)lptreg.h 1.1 (Berkeley) 12/19/90 + */ + +/* + * AT Parallel Port (for lineprinter) + * Interface port and bit definitions + * Written by William Jolitz 12/18/90 + * Copyright (C) William Jolitz 1990 + */ + +#define lpt_data 0 /* Data to/from printer (R/W) */ + +#define lpt_status 4 /* Status of printer (R) */ +#define LPS_NERR 0x08 /* printer no error */ +#define LPS_SELECT 0x10 /* printer selected */ +#define LPS_NOPAPER 0x20 /* printer out of paper */ +#define LPS_NACK 0x40 /* printer no ack of data */ +#define LPS_NBSY 0x80 /* printer no ack of data */ + +#define lpt_control 8 /* Control printer (R/W) */ +#define LPC_STROBE 0x01 /* strobe data to printer */ +#define LPC_AUTOLF 0x02 /* automatic linefeed */ +#define LPC_NINIT 0x04 /* initialize printer */ +#define LPC_SELECT 0x08 /* printer selected */ +#define LPC_IENABLE 0x10 /* printer out of paper */ + +#define LPT_NPORTS 32 diff --git a/sys/arch/arm32/mainbus/mainbus.c b/sys/arch/arm32/mainbus/mainbus.c new file mode 100644 index 00000000000..7caade580dd --- /dev/null +++ b/sys/arch/arm32/mainbus/mainbus.c @@ -0,0 +1,136 @@ +/* $NetBSD: mainbus.c,v 1.3 1996/03/20 18:38:00 mark Exp $ */ + +/* + * Copyright (c) 1994,1995 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * mainbus.c + * + * mainbus configuration + * + * Created : 15/12/94 + * Last updated : 03/07/95 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <arm32/mainbus/mainbus.h> +#include <machine/io.h> + +int mainbusmatch __P((struct device *, void *, void *)); +void mainbusattach __P((struct device *, struct device *, void *)); + +struct cfattach mainbus_ca = { + sizeof(struct device), mainbusmatch, mainbusattach +}; + +struct cfdriver mainbus_cd = { + NULL, "mainbus", DV_DULL, 1 +}; + +int +mainbusmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + return (1); +} + +int +mainbusprint(aux, mainbus) + void *aux; + char *mainbus; +{ + struct mainbus_attach_args *mb = aux; + + if (mb->mb_iobase) + printf(" base 0x%x", mb->mb_iobase); + if (mb->mb_iosize > 1) + printf("-0x%x", mb->mb_iobase + mb->mb_iosize - 1); + if (mb->mb_irq != -1) + printf(" irq %d", mb->mb_irq); + if (mb->mb_drq != -1) + printf(" drq 0x%08x", mb->mb_drq); + +/* XXXX print flags */ + return (QUIET); +} + + +void +mainbusscan(parent, match) + struct device *parent; + void *match; +{ + struct device *dev = match; + struct cfdata *cf = dev->dv_cfdata; + struct mainbus_attach_args mb; + + if (cf->cf_fstate == FSTATE_STAR) + panic("eekkk, I'm stuffed"); + + if (cf->cf_loc[0] == -1) { + mb.mb_iobase = 0; + mb.mb_iosize = 0; + mb.mb_drq = -1; + mb.mb_irq = -1; + } else { + mb.mb_iobase = cf->cf_loc[0] + IO_CONF_BASE; + mb.mb_iosize = 0; + mb.mb_drq = cf->cf_loc[1]; + mb.mb_irq = cf->cf_loc[2]; + } + if ((*cf->cf_attach->ca_match)(parent, dev, &mb) > 0) + config_attach(parent, dev, &mb, mainbusprint); + else + free(dev, M_DEVBUF); +} + +void +mainbusattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + printf("\n"); + + config_scan(mainbusscan, self); +} + +/* End of mainbus.c */ diff --git a/sys/arch/arm32/mainbus/mainbus.h b/sys/arch/arm32/mainbus/mainbus.h new file mode 100644 index 00000000000..d0ae78e8c9b --- /dev/null +++ b/sys/arch/arm32/mainbus/mainbus.h @@ -0,0 +1,62 @@ +/* $NetBSD: mainbus.h,v 1.2 1996/03/18 20:50:05 mark Exp $ */ + +/* + * Copyright (c) 1994,1995 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * mainbus.c + * + * mainbus configuration + * + * Created : 15/12/94 + */ + +/* + * mainbus driver attach arguments + */ + +struct mainbus_attach_args { + u_int mb_iobase; /* base i/o address */ + int mb_iosize; /* span of ports used */ + int mb_irq; /* interrupt request */ + int mb_drq; /* DMA request */ + void *mb_aux; /* driver specific */ +}; + +#define DRQUNK -1 +#define INTUNK -1 +#define IRQUNK -1 + +/* End of mainbus.h */ + + diff --git a/sys/arch/arm32/mainbus/pms.c b/sys/arch/arm32/mainbus/pms.c new file mode 100644 index 00000000000..8339e1ab8a0 --- /dev/null +++ b/sys/arch/arm32/mainbus/pms.c @@ -0,0 +1,612 @@ +/* $NetBSD: pms.c,v 1.1 1996/03/28 21:50:19 mark Exp $ */ + +/*- + * Copyright (c) 1996 D.C. Tsen + * Copyright (c) 1994 Charles Hannum. + * Copyright (c) 1992, 1993 Erik Forsberg. + * 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 RiscBSD team. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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:pms.c,v 1.24 1995/12/24 02:30:28 mycroft Exp + */ + +/* + * Ported from 386 version of PS/2 mouse driver. + * D.C. Tsen + */ + +#include "pms.h" +#if NPMS > 1 +#error Only one PS/2 style mouse may be configured into your system. +#endif + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/malloc.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/vnode.h> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/katelib.h> +#include <machine/irqhandler.h> +#include <machine/iomd.h> +#include <machine/mouse.h> +#include <arm32/mainbus/mainbus.h> + +/* 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 (20*64) /* buffer size */ + +struct pms_softc { /* driver status information */ + struct device sc_dev; + irqhandler_t sc_ih; + + struct proc *proc; + struct clist sc_q; + struct selinfo sc_rsel; + u_int sc_state; /* mouse driver state */ +#define PMS_OPEN 0x01 /* device is open */ +#define PMS_ASLP 0x02 /* waiting for mouse data */ + u_int sc_status; /* mouse button status */ + int sc_x, sc_y; /* accumulated motion in the X,Y axis */ + int boundx, boundy, bounda, boundb; /* Bounding box. x,y is bottom left */ + int origx, origy; + int lastx, lasty, lastb; +}; + +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_DULL +}; + +#define PMSUNIT(dev) (minor(dev)) + +static inline void +pms_flush() +{ + while (inb(IOMD_MSCR) & 0x20) { + delay(6); + (void) inb(IOMD_MSDATA); + delay(6); + (void) inb(IOMD_MSDATA); + delay(6); + (void) inb(IOMD_MSDATA); + } +} + +static void +cmd_mouse(unsigned char cmd) +{ + unsigned char c; + int i = 0; + int retry = 10; + + for (i = 0; i < 1000; i++) { + if (inb(IOMD_MSCR) & 0x80) + break; + delay(2); + } + if (i == 1000) + printf("Mouse transmit not ready\n"); + +resend: + outb(IOMD_MSDATA, cmd); + delay(2); + c = inb(IOMD_MSCR) & (unsigned char) 0xff; + while (!(c & (unsigned char) 0x20)) { + delay(1); + c = inb(IOMD_MSCR); + } + + delay(10000); + + c = inb(IOMD_MSDATA) & 0xff; + if ((c == 0xFA) || (c == 0xEE)) + return; + + if (--retry) { + pms_flush(); + goto resend; + } + + printf("Mouse cmd failed, cmd = %x, status = %x\n", cmd, c); + return; +} + +int +pmsprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + /*struct mainbus_attach_args *mb = aux;*/ + int i, j; + int mid; + int id; + +/* Make sure we have an IOMD we understand */ + + id = ReadByte(IOMD_ID0) | (ReadByte(IOMD_ID1) << 8); + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + return(0); + break; + case RC7500_IOC_ID: + break; + default: + printf("pms: Unknown IOMD id=%04x", id); + return(0); + break; + } + + outb(IOMD_MSCR, 0x08); /* enable the mouse */ + + i = 0; + while ((inb(IOMD_MSCR) & 0x03) != 0x03) { + if (i++ > 10) { + printf("Mouse not found, status = <%x>.\n", inb(IOMD_MSCR)); + return(0); + } + pms_flush(); + delay(2); + outb(IOMD_MSCR, 0x08); + } + + pms_flush(); + + /* + * Disable, reset and enable the mouse. + */ + cmd_mouse(PMS_DEV_DISABLE); + cmd_mouse(PMS_RESET); + delay(300000); + j = 10; + i = 0; + while ((mid = inb(IOMD_MSDATA)) != 0xAA) { + if (++i > 500) { + if (--j < 0) { + printf("Mouse Reset failed, status = <%x>.\n", mid); + return(0); + } + pms_flush(); + cmd_mouse(PMS_RESET); + i = 0; + } + delay(100000); + } + mid = inb(IOMD_MSDATA); +#if 0 + cmd_mouse(PMS_SET_RES); + cmd_mouse(3); /* 8 counts/mm */ + cmd_mouse(PMS_SET_SCALE21); + cmd_mouse(PMS_SET_SAMPLE); + cmd_mouse(100); /* 100 samples/sec */ + cmd_mouse(PMS_SET_STREAM); +#endif + cmd_mouse(PMS_DEV_ENABLE); + return 1; +} + +void +pmsattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pms_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + + printf("\n"); + + /* Other initialization was done by pmsprobe. */ + sc->sc_state = 0; + sc->origx = 0; + sc->origy = 0; + sc->boundx = -4095; + sc->boundy = -4095; + sc->bounda = 4096; + sc->boundb = 4096; + + sc->sc_ih.ih_func = pmsintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_TTY; + sc->sc_ih.ih_name = "pms"; + if (mb->mb_irq != IRQUNK) + sc->sc_ih.ih_num = mb->mb_irq; + else +#ifdef RC7500 + sc->sc_ih.ih_num = IRQ_MSDRX; +#else + panic("pms: No IRQ specified for pms interrupt handler\n"); +#endif +} + +int +pmsopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + 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->proc = p; + sc->sc_state |= PMS_OPEN; + sc->sc_status = 0; + sc->sc_x = sc->sc_y = 0; + sc->lastx = -1; + sc->lasty = -1; + sc->lastb = -1; + + if (irq_claim(IRQ_INSTRUCT, &sc->sc_ih) == -1) + panic("Cannot claim MOUSE IRQ\n"); + + return 0; +} + +int +pmsclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + + if (irq_release(IRQ_INSTRUCT, &sc->sc_ih) != 0) + panic("Cannot release MOUSE IRQ\n"); + + sc->proc = NULL; + sc->sc_state &= ~PMS_OPEN; + sc->sc_x = sc->sc_y = 0; + + 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 = 0; + 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), "pmsread", 0)) { + sc->sc_state &= ~PMS_ASLP; + splx(s); + return error; + } + } + + /* 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; + } + splx(s); + + return error; +} + +int +pmsioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct pms_softc *sc = pms_cd.cd_devs[PMSUNIT(dev)]; + struct mouseinfo info; + int s; + int error = 0; + + s = spltty(); + + switch (cmd) { + case MOUSEIOC_SETSTATE: + printf("MOUSEIOC_SETSTATE called\n"); + break; + case MOUSEIOC_WRITEX: + sc->sc_x = *(int *) addr; + break; + case MOUSEIOC_WRITEY: + sc->sc_y = *(int *) addr; + break; + case MOUSEIOC_SETBOUNDS: + { + register struct mouse_boundingbox *bo = (void *) addr; + sc->boundx = bo->x; sc->boundy = bo->y; + sc->bounda = bo->a; sc->boundb = bo->b; + break; + } + case MOUSEIOC_SETORIGIN: + { + register struct mouse_origin *oo = (void *) addr; + sc->origx = oo->x; + sc->origy = oo->y; + break; + } + case MOUSEIOC_GETBOUNDS: + { + register struct mouse_boundingbox *bo = (void *) addr; + bo->x = sc->boundx; bo->y = sc->boundy; + bo->a = sc->bounda; bo->b = sc->boundb; + break; + } + case MOUSEIOC_GETORIGIN: + { + register struct mouse_origin *oo = (void *) addr; + oo->x = sc->origx; + oo->y = sc->origy; + break; + } + case MOUSEIOC_GETSTATE: + printf("MOUSEIOC_GETSTATE called\n"); + /* + * Fall through. + */ +/* case MOUSEIOCREAD:*/ + + info.status = sc->sc_status; + if (sc->sc_x || sc->sc_y) + info.status |= MOVEMENT; + +#if 1 + info.xmotion = sc->sc_x; + info.ymotion = sc->sc_y; +#else + if (sc->sc_x > sc->bounda) + info.xmotion = sc->bounda; + else if (sc->sc_x < sc->boundx) + info.xmotion = sc->boundx; + else + info.xmotion = sc->sc_x; + + if (sc->sc_y > sc->boundb) + info.ymotion = sc->boundb; + else if (sc->sc_y < sc->boundy) + info.ymotion = sc->boundy; + else + info.ymotion = sc->sc_y; +#endif + + /* Reset historical information. */ + sc->sc_x = sc->sc_y = 0; + sc->sc_status &= ~BUTCHNGMASK; + ndflush(&sc->sc_q, sc->sc_q.c_cc); + + error = copyout(&info, addr, sizeof(struct mouseinfo)); + break; + + default: + error = EINVAL; + break; + } + splx(s); + + return error; +} + +/* Masks for the first byte of a packet */ +#define PS2LBUTMASK 0x01 +#define PS2RBUTMASK 0x02 +#define PS2MBUTMASK 0x04 + +#define XNEG_MASK 0x10 +#define YNEG_MASK 0x20 + +int +pmsintr(arg) + void *arg; +{ + struct pms_softc *sc = arg; + static int state = 0; + static u_char buttons; + u_char changed; + static int dx, dy; + int dosignal = 0; + int s; + u_char b; + struct mousebufrec mbuffer; + + if ((sc->sc_state & PMS_OPEN) == 0) { + /* Interrupts are not expected. Discard the byte. */ + pms_flush(); + return(-1); + } + + switch (state) { + + case 0: + buttons = inb(IOMD_MSDATA); + if ((buttons & 0xc0) == 0) + ++state; + break; + + case 1: + dx = inb(IOMD_MSDATA); + dx = (buttons & XNEG_MASK) ? -dx : dx; + ++state; + break; + + case 2: + dy = inb(IOMD_MSDATA); + dy = (buttons & YNEG_MASK) ? -dy : dy; + state = 0; + + buttons = ((buttons & PS2LBUTMASK) << 2) | + ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1); + /* + * Ahhh, the Xarm server interrupt button bits reversed. + * If bit is set to 1, it means button is not pressed which is + * not what PS/2 mouse button bits said. Since we are simulating + * the quadmouse, that's not too picky about it. + */ + buttons ^= BUTSTATMASK; + changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3; + sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; + + if (dx || dy || changed) { + if (dx < 0) + dx = -(256 + dx); + if (dy < 0) + dy = -(256 + dy); + + /* Update accumulated movements. */ + sc->sc_x += dx; + sc->sc_y += dy; + + if (sc->sc_x > sc->bounda) + sc->sc_x = sc->bounda; + else if (sc->sc_x < sc->boundx) + sc->sc_x = sc->boundx; + + if (sc->sc_y > sc->boundb) + sc->sc_y = sc->boundb; + else if (sc->sc_y < sc->boundy) + sc->sc_y = sc->boundy; + + if (sc->sc_q.c_cc == 0) { + dosignal = 1; + } + + /* Add this event to the queue. */ + b = buttons & BUTSTATMASK; + mbuffer.status = b | (b ^ sc->lastb) << 3 + | (((sc->sc_x==sc->lastx) && (sc->sc_y==sc->lasty))?0:0x40); + mbuffer.x = sc->sc_x * 2; + mbuffer.y = sc->sc_y * 2; + microtime(&mbuffer.event_time); + s = spltty(); + (void) b_to_q((char *)&mbuffer, sizeof(mbuffer), &sc->sc_q); + (void) splx(s); + sc->lastx = sc->sc_x; + sc->lasty = sc->sc_y; + sc->lastb = b; + + selwakeup(&sc->sc_rsel); + if (sc->sc_state & PMS_ASLP) { + sc->sc_state &= ~PMS_ASLP; + wakeup((caddr_t)sc); + } + if (dosignal) + psignal(sc->proc, SIGIO); + } + + break; + } + + return(0); +} + +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/arm32/mainbus/qmouse.c b/sys/arch/arm32/mainbus/qmouse.c new file mode 100644 index 00000000000..b2a6447a073 --- /dev/null +++ b/sys/arch/arm32/mainbus/qmouse.c @@ -0,0 +1,440 @@ +/* $NetBSD: qmouse.c,v 1.5 1996/03/28 21:56:40 mark Exp $ */ + +/* + * Copyright (c) Scott Stevens 1995 All rights reserved + * Copyright (c) Melvin Tang-Richardson 1995 All rights reserved + * Copyright (c) Mark Brinicombe 1995 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 RiscBSD team. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Quadrature mouse driver + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/kernel.h> +#include <sys/types.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <dev/cons.h> +#include <sys/fcntl.h> +#include <sys/signalvar.h> +#include <sys/vnode.h> +#include <sys/time.h> + +#include <arm32/mainbus/mainbus.h> +#include <machine/irqhandler.h> +#include <machine/katelib.h> +#include <machine/iomd.h> +#include <machine/mouse.h> + +#include "quadmouse.h" + +#define TIMER1_COUNT 40000 /* 50Hz */ + +#define QMOUSE_BSIZE 12*64 + +struct quadmouse_softc { + struct device sc_device; + irqhandler_t sc_ih; + int sc_iobase; + struct selinfo sc_rsel; +#define QMOUSE_OPEN 0x01 +#define QMOUSE_ASLEEP 0x02 + int sc_state; + int boundx, boundy, bounda, boundb; /* Bounding box. x,y is bottom left */ + int origx, origy; + int xmult, ymult; /* Multipliers */ + int lastx, lasty, lastb; + struct proc *proc; + struct clist buffer; +}; + +int quadmouseprobe __P((struct device *, void *, void *)); +void quadmouseattach __P((struct device *, struct device *, void *)); +int quadmouseopen __P((dev_t, int, int, struct proc *)); +int quadmouseclose __P((dev_t, int, int, struct proc *)); + +int strncmp __P((const char *, const char *, size_t)); + +int quadmouseintr (struct quadmouse_softc *); + +struct cfattach quadmouse_ca = { + sizeof(struct quadmouse_softc), quadmouseprobe, quadmouseattach +}; + +struct cfdriver quadmouse_cd = { + NULL, "quadmouse", DV_DULL +}; + + +int +quadmouseprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ +/* struct mainbus_attach_args *mb = aux;*/ + int id; + +/* Make sure we have an IOMD we understand */ + + id = ReadByte(IOMD_ID0) | (ReadByte(IOMD_ID1) << 8); + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + return(1); + break; + default: + printf("quadmouse: Unknown IOMD id=%04x", id); + break; + } + + return(0); +} + + +void +quadmouseattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct quadmouse_softc *sc = (void *)self; + struct mainbus_attach_args *mb = aux; + + sc->sc_iobase = mb->mb_iobase; + +/* Check for a known IOMD chip */ + + sc->sc_ih.ih_func = quadmouseintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_level = IPL_TTY; + sc->sc_ih.ih_name = "T1 quadmouse"; + +/* Set up origin and multipliers */ + + sc->origx = 0; + sc->origy = 0; + sc->xmult = 2; + sc->ymult = 2; + +/* Set up bounding box */ + + sc->boundx = -4095; + sc->boundy = -4095; + sc->bounda = 4096; + sc->boundb = 4096; + + sc->sc_state = 0; + + WriteWord(IOMD_MOUSEX, sc->origx); + WriteWord(IOMD_MOUSEY, sc->origy); + + printf("\n"); +} + + +int +quadmouseopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct quadmouse_softc *sc; + int unit = minor(dev); + + if (unit >= quadmouse_cd.cd_ndevs) + return(ENXIO); + + sc = quadmouse_cd.cd_devs[unit]; + + if (!sc) return(ENXIO); + + if (sc->sc_state & QMOUSE_OPEN) return(EBUSY); + + sc->proc = p; + + sc->lastx = -1; + sc->lasty = -1; + sc->lastb = -1; + + /* initialise buffer */ + if (clalloc(&sc->buffer, QMOUSE_BSIZE, 0) == -1) + return(ENOMEM); + + sc->sc_state |= QMOUSE_OPEN; + + WriteByte(IOMD_T1LOW, (TIMER1_COUNT >> 0) & 0xff); + WriteByte(IOMD_T1HIGH, (TIMER1_COUNT >> 8) & 0xff); + WriteByte(IOMD_T1GO, 0); + + if (irq_claim(IRQ_TIMER1, &sc->sc_ih)) + panic("Cannot claim TIMER1 IRQ for quadmouse%d\n", sc->sc_device.dv_unit); + + return(0); +} + + +int +quadmouseclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = minor (dev); + struct quadmouse_softc *sc = quadmouse_cd.cd_devs[unit]; + + if (irq_release(IRQ_TIMER1, &sc->sc_ih) != 0) + panic("Cannot release IRA\n"); + + sc->proc = NULL; + sc->sc_state = 0; + + clfree(&sc->buffer); + + return(0); +} + +int +quadmouseread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = minor(dev); + struct quadmouse_softc *sc = quadmouse_cd.cd_devs[unit]; + int error; + int s; + int length; + u_char buffer[128]; + + s=spltty(); + while(sc->buffer.c_cc==0) { + if(flag & IO_NDELAY) { + (void)splx(s); + return(EWOULDBLOCK); + } + sc->sc_state |= QMOUSE_ASLEEP; + if((error = tsleep((caddr_t)sc, PZERO | PCATCH, "quadmouseread", 0))) { + sc->sc_state &= ~QMOUSE_ASLEEP; + (void)splx(s); + return(error); + } + } + + while(sc->buffer.c_cc>0 && uio->uio_resid>0) { + length=min(sc->buffer.c_cc, uio->uio_resid); + if(length>sizeof(buffer)) + length=sizeof(buffer); + + (void) q_to_b(&sc->buffer, buffer, length); + + if(error = (uiomove(buffer, length, uio))) + break; + } + (void)splx(s); + return(error); +} + + +#define FMT_START int x = ReadWord(IOMD_MOUSEX)&0xffff; \ + int y = ReadWord(IOMD_MOUSEY)&0xffff; \ + int b = ReadByte(IO_MOUSE_BUTTONS)&0x70; \ + if(x&0x8000) x|=0xffff0000; \ + if(y&0x8000) y|=0xffff0000; \ + x = (x - sc->origx); \ + y = (y - sc->origy); \ + if (x<(sc->boundx)) x = sc->boundx; \ + WriteWord(IOMD_MOUSEX, x + sc->origx); \ + if (x>(sc->bounda)) x = sc->bounda; \ + WriteWord(IOMD_MOUSEX, x + sc->origx); \ + if (y<(sc->boundy)) y = sc->boundy; \ + WriteWord(IOMD_MOUSEY, y + sc->origy); \ + if (y>(sc->boundb)) y = sc->boundb; \ + WriteWord(IOMD_MOUSEY, y + sc->origy); \ + x=x*sc->xmult; \ + y=y*sc->ymult; + +#define FMT_END + + +int +quadmouseioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct quadmouse_softc *sc = quadmouse_cd.cd_devs[minor(dev)]; + + switch (cmd) { + case MOUSEIOC_WRITEX: + WriteWord(IOMD_MOUSEX, *(int *)data+sc->origx); + return 0; + case MOUSEIOC_WRITEY: + WriteWord(IOMD_MOUSEY, *(int *)data+sc->origy); + return 0; + case MOUSEIOC_SETSTATE: + { + register struct mouse_state *co = (void *)data; + WriteWord(IOMD_MOUSEX, co->x); + WriteWord(IOMD_MOUSEY, co->y); + + /* Silly, but here for completeness, just incase */ + /* the hardware supports it *giggle* */ + +/* This is not writable -- mark -- technically this should fault */ + +/* WriteWord ( IO_MOUSE_BUTTONS, co->buttons );*/ + return 0; + } + case MOUSEIOC_SETBOUNDS: + { + register struct mouse_boundingbox *bo = (void *)data; + sc->boundx = bo->x; sc->boundy = bo->y; + sc->bounda = bo->a; sc->boundb = bo->b; + return 0; + } + case MOUSEIOC_SETORIGIN: + { + register struct mouse_origin *oo = (void *)data; +/* int oldx, oldy;*/ + /* Need to fix up! */ + sc->origx = oo->x; + sc->origy = oo->y; + return 0; + } + case MOUSEIOC_GETSTATE: + { + register struct mouse_state *co = (void *)data; + FMT_START + co->x = x; + co->y = y; + co->buttons = b ^ 0x70; + FMT_END + return 0; + } + case MOUSEIOC_GETBOUNDS: + { + register struct mouse_boundingbox *bo = (void *)data; + bo->x = sc->boundx; bo->y = sc->boundy; + bo->a = sc->bounda; bo->b = sc->boundb; + return 0; + } + case MOUSEIOC_GETORIGIN: + { + register struct mouse_origin *oo = (void *)data; + oo->x = sc->origx; + oo->y = sc->origy; + return 0; + } + } + + return (EINVAL); +} + + +int +quadmouseintr(sc) + struct quadmouse_softc *sc; +{ + int s; + struct mousebufrec buffer; + int dosignal=0; + + FMT_START + + b &= 0x70; + b >>= 4; + if (x != sc->lastx || y != sc->lasty || b != sc->lastb) { + /* Mouse state changed */ + buffer.status = b | ( b ^ sc->lastb) << 3 | (((x==sc->lastx) && (y==sc->lasty))?0:0x40); + buffer.x = x; + buffer.y = y; + microtime(&buffer.event_time); + + if(sc->buffer.c_cc==0) + dosignal=1; + + s=spltty(); + (void) b_to_q((char *)&buffer, sizeof(buffer), &sc->buffer); + (void)splx(s); + selwakeup(&sc->sc_rsel); + + if(sc->sc_state & QMOUSE_ASLEEP) { + sc->sc_state &= ~QMOUSE_ASLEEP; + wakeup((caddr_t)sc); + } + + if(dosignal) + psignal(sc->proc, SIGIO); + + sc->lastx = x; + sc->lasty = y; + sc->lastb = b; + } + + FMT_END + return(0); +} + +int +quadmouseselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + int unit = minor(dev); + struct quadmouse_softc *sc = quadmouse_cd.cd_devs[unit]; + int s; + + if(rw == FWRITE) + return 0; + + s=spltty(); + if(sc->buffer.c_cc > 0) { + selrecord(p, &sc->sc_rsel); + (void)splx(s); + return 0; + } + (void)splx(s); + return 1; +} + +/* End of quadmouse.c */ diff --git a/sys/arch/arm32/mainbus/rtc.c b/sys/arch/arm32/mainbus/rtc.c new file mode 100644 index 00000000000..d759294324c --- /dev/null +++ b/sys/arch/arm32/mainbus/rtc.c @@ -0,0 +1,384 @@ +/* $NetBSD: rtc.c,v 1.1 1996/04/19 19:49:06 mark Exp $ */ + +/* + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * rtc.c + * + * Routines to read and write the RTC and CMOS RAM + * + * Created : 13/10/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <machine/iic.h> +#include <machine/rtc.h> + +struct rtc_softc { + struct device sc_dev; + int sc_flags; +#define RTC_BROKEN 1 +#define RTC_OPEN 2 +}; + +void rtcattach __P((struct device *parent, struct device *self, void *aux)); +int rtcmatch __P((struct device *parent, void *match, void *aux)); + +/* Read a byte from CMOS RAM */ + +int +cmos_read(location) + int location; +{ + u_char buff; + +/* + * This commented code dates from when I was translating CMOS address + * from the RISCOS addresses. Now all addresses are specifed as + * actual addresses in the CMOS RAM + */ + +/* + if (location > 0xF0) + return(-1); + + if (location < 0xC0) + buff = location + 0x40; + else + buff = location - 0xB0; +*/ + buff = location; + + if (iic_control(RTC_Write, &buff, 1)) + return(-1); + if (iic_control(RTC_Read, &buff, 1)) + return(-1); + + return(buff); +} + + +/* Write a byte to CMOS RAM */ + +int +cmos_write(location, value) + int location; + int value; +{ + u_char buff[2]; + +/* + * This commented code dates from when I was translating CMOS address + * from the RISCOS addresses. Now all addresses are specifed as + * actual addresses in the CMOS RAM + */ + +/* if (location > 0xF0) + return(-1); + + if (location < 0xC0) + buff = location + 0x40; + else + buff = location - 0xB0; +*/ + buff[0] = location; + buff[1] = value; + + if (iic_control(RTC_Write, buff, 2)) + return(-1); + + return(0); +} + + +/* Hex to BCD and BCD to hex conversion routines */ + +static inline int +hexdectodec(n) + u_char n; +{ + return(((n >> 4) & 0x0F) * 10 + (n & 0x0F)); +} + +static inline int +dectohexdec(n) + u_char n; +{ + return(((n / 10) << 4) + (n % 10)); +} + + +/* Write the RTC data from an 8 byte buffer */ + +int +rtc_write(rtc) + rtc_t *rtc; +{ + u_char buff[8]; + + buff[0] = 1; + + buff[1] = dectohexdec(rtc->rtc_centi); + buff[2] = dectohexdec(rtc->rtc_sec); + buff[3] = dectohexdec(rtc->rtc_min); + buff[4] = dectohexdec(rtc->rtc_hour) & 0x3f; + buff[5] = dectohexdec(rtc->rtc_day); + buff[6] = dectohexdec(rtc->rtc_mon); + + if (iic_control(RTC_Write, buff, 7)) + return(0); + + cmos_write(RTC_ADDR_YEAR, rtc->rtc_year); + cmos_write(RTC_ADDR_CENT, rtc->rtc_cen); + + return(1); +} + + +/* Read the RTC data into a 8 byte buffer */ + +int +rtc_read(rtc) + rtc_t *rtc; +{ + u_char buff[8]; + int byte; + + buff[0] = 0; + + if (iic_control(RTC_Write, buff, 1)) + return(0); + + if (iic_control(RTC_Read, buff, 8)) + return(0); + + rtc->rtc_micro = 0; + rtc->rtc_centi = hexdectodec(buff[1] & 0xff); + rtc->rtc_sec = hexdectodec(buff[2] & 0x7f); + rtc->rtc_min = hexdectodec(buff[3] & 0x7f); + rtc->rtc_hour = hexdectodec(buff[4] & 0x3f); + + /* If in 12 hour mode need to look at the AM/PM flag */ + + if (buff[4] & 0x80) + rtc->rtc_hour += (buff[4] & 0x40) ? 12 : 0; + + rtc->rtc_day = hexdectodec(buff[5] & 0x3f); + rtc->rtc_mon = hexdectodec(buff[6] & 0x1f); + + byte = cmos_read(RTC_ADDR_YEAR); + if (byte == -1) + return(0); + rtc->rtc_year = byte; + + byte = cmos_read(RTC_ADDR_CENT); + if (byte == -1) + return(0); + rtc->rtc_cen = byte; + + return(1); +} + + +struct cfattach rtc_ca = { + sizeof(struct rtc_softc), rtcmatch, rtcattach +}; + +struct cfdriver rtc_cd = { + NULL, "rtc", DV_DULL, 0 +}; + +int +rtcmatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ +/* struct iicbus_attach_args *ib = aux;*/ + + return(1); +} + + +void +rtcattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct rtc_softc *sc = (struct rtc_softc *)self; + struct iicbus_attach_args *ib = aux; + u_char buff[1]; + + sc->sc_flags |= RTC_BROKEN; + if ((ib->ib_addr & IIC_PCF8583_MASK) == IIC_PCF8583_ADDR) { + printf(": PCF8583"); + + buff[0] = 0; + + if (iic_control(RTC_Write, buff, 1)) + return; + + if (iic_control(RTC_Read, buff, 1)) + return; + + printf(" clock base "); + switch (buff[0] & 0x30) { + case 0x00: + printf("32.768KHz"); + break; + case 0x10: + printf("50Hz"); + break; + case 0x20: + printf("event"); + break; + case 0x30: + printf("test mode"); + break; + } + + if (buff[0] & 0x80) + printf(" stopped"); + if (buff[0] & 0x04) + printf(" alarm enabled"); + sc->sc_flags &= ~RTC_BROKEN; + } + + +/* + * Initialise the time of day register. + * This is normally left to the filing system to do but not all + * filing systems call it e.g. cd9660 + */ + + inittodr(0); + + printf("\n"); +} + + +int +rtcopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct rtc_softc *sc; + int unit = minor(dev); + + if (unit >= rtc_cd.cd_ndevs) + return(ENXIO); + + sc = rtc_cd.cd_devs[unit]; + + if (!sc) return(ENXIO); + + if (sc->sc_flags & RTC_OPEN) return(EBUSY); + + sc->sc_flags |= RTC_OPEN; + + return(0); +} + + +int +rtcclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit = minor(dev); + struct rtc_softc *sc = rtc_cd.cd_devs[unit]; + + sc->sc_flags &= ~RTC_OPEN; + + return(0); +} + + +int +rtcread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = minor(dev); + struct rtc_softc *sc = rtc_cd.cd_devs[unit]; + + return(ENXIO); +} + + +int +rtcwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = minor(dev); + struct rtc_softc *sc = rtc_cd.cd_devs[unit]; + + return(ENXIO); +} + + +int +rtcioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct rtc_softc *sc = rtc_cd.cd_devs[minor(dev)]; + +/* switch (cmd) { + case RTCIOC_READ: + return(0); + } */ + + return(EINVAL); +} + +/* End of rtc.c */ diff --git a/sys/arch/arm32/mainbus/vidcaudio.c b/sys/arch/arm32/mainbus/vidcaudio.c new file mode 100644 index 00000000000..0529519a9b4 --- /dev/null +++ b/sys/arch/arm32/mainbus/vidcaudio.c @@ -0,0 +1,870 @@ +/* $NetBSD: vidcaudio.c,v 1.3 1996/03/17 01:24:37 thorpej Exp $ */ + +/* + * Copyright (c) 1995 Melvin Tang-Richardson + * + * 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 RiscBSD team. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Yikes just had a look at this and realised that it does not conform + * to Kernel Normal Form at all. I have fixed the first few functions + * but there are a lot more still to do - mark + */ + +/* + * vidcaudio driver for RiscBSD by Melvin Tang-Richardson (c) 1995 + * + * Interfaces with the NetBSD generic audio driver to provide SUN + * /dev/audio (partial) compatibility. + * + * Do not include me in conf.c. My interface is done by the generic + * audio driver. Put me in config file and files.arm32. Do not put + * the audio.c generic driver in them. I do the autoconfig for it. + * + * The following files need to be altered + * + * [files.arm32] + * device vidcaudio at mainbus :audio + * file arch/arm32/mainbus/vidcaudio.c vidcaudio needs-flag + * major { audio=36 } + * + * [conf.c] + * #include "audio.h" + * cdev_decl(audio) + * + * cdev_audio_init(NAUDIO,audio) /* 36: generic audio I/O */ + * + * [GENERIC] + * vidcaudio0 at mainbus? base 0x00000000 + * + */ + +#include <sys/param.h> /* proc.h */ +#include <sys/types.h> /* dunno */ +#include <sys/conf.h> /* autoconfig functions */ +#include <sys/device.h> /* device calls */ +#include <sys/proc.h> /* device calls */ +#include <sys/audioio.h> +#include <sys/errno.h> +#include <sys/systm.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> + +#include <dev/audio_if.h> + +#include <machine/irqhandler.h> +#include <machine/iomd.h> +#include <machine/vidc.h> +#include <machine/katelib.h> /* ReadByte etc. */ + +#include <arm32/mainbus/mainbus.h> +#include "waveform.h" +#include "vidcaudio.h" + +#undef DEBUG + +struct audio_general { + int in_sr; + int out_sr; + vm_offset_t silence; + irqhandler_t ih; + + void (*intr) (); + void *arg; + + vm_offset_t next_cur; + vm_offset_t next_end; + void (*next_intr) (); + void *next_arg; + + int buffer; + int in_progress; + + int open; +} ag; + +struct vidcaudio_softc { + struct device device; + int iobase; + + int open; + + u_int encoding; + int inport; + int outport; +}; + +int vidcaudio_probe __P((struct device *parent, struct device *match, void *aux)); +void vidcaudio_attach __P((struct device *parent, struct device *self, void *aux)); +int vidcaudio_open __P((dev_t dev, int flags)); +void vidcaudio_close __P((void *addr)); + +int vidcaudio_intr __P((void *arg)); +int vidcaudio_dma_program __P((vm_offset_t cur, vm_offset_t end, void (*intr)(), void *arg)); +void vidcaudio_dummy_routine __P((void *arg)); +int vidcaudio_stereo __P((int channel, int position)); +int vidcaudio_rate __P((int rate)); +void vidcaudio_shutdown __P((void)); +int vidcaudio_hw_attach __P((struct vidcaudio_softc *sc)); + +struct cfattach vidcaudio_ca = { + sizeof(struct vidcaudio_softc), vidcaudio_probe, vidcaudio_attach +}; + +struct cfdriver vidcaudio_cd = { + NULL, "vidcaudio", DV_DULL +}; + + +void +vidcaudio_beep_generate() +{ + vidcaudio_dma_program ( ag.silence, ag.silence+sizeof(beep_waveform)-16, + vidcaudio_dummy_routine, NULL ); +} + + +int +vidcaudio_probe(parent, match, aux) + struct device *parent; + struct device *match; + void *aux; +{ + int id; + + id = ReadByte(IOMD_ID0) | ReadByte(IOMD_ID1) << 8; + +/* So far I only know about this IOMD */ + + switch (id) { + case RPC600_IOMD_ID: + return(1); + break; + default: + printf("vidcaudio: Unknown IOMD id=%04x", id); + break; + } + + return (0); +} + + +void +vidcaudio_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct mainbus_attach_args *mb = aux; + struct vidcaudio_softc *sc = (void *)self; + + sc->iobase = mb->mb_iobase; + + sc->open = 0; + sc->encoding = 0; + sc->inport = 0; + sc->outport = 0; + ag.in_sr = 24*1024; + ag.out_sr = 24*1024; + ag.in_progress = 0; + + ag.next_cur = 0; + ag.next_end = 0; + ag.next_intr = NULL; + ag.next_arg = NULL; + + vidcaudio_rate(32); + +/* Program the silence buffer and reset the DMA channel */ + + ag.silence = kmem_alloc(kernel_map, NBPG); + if (ag.silence == NULL) + panic("vidcaudio: Cannot allocate memory\n"); + + bzero((char *)ag.silence, NBPG); + bcopy((char *)beep_waveform, (char *)ag.silence, sizeof(beep_waveform)); + + ag.buffer = 0; + + /* Install the irq handler for the DMA interrupt */ + ag.ih.ih_func = vidcaudio_intr; + ag.ih.ih_arg = NULL; + ag.ih.ih_level = IPL_NONE; + + ag.intr = NULL; + ag.nextintr = NULL; + + disable_irq(IRQ_DMASCH0); + + if (irq_claim(IRQ_DMASCH0, &(ag.ih))) + panic("vidcaudio: couldn't claim IRQ_DMASCH0"); + + disable_irq(IRQ_DMASCH0); + + vidcaudio_dma_program(ag.silence, ag.silence+NBPG-16, + vidcaudio_dummy_routine, NULL); + + vidcaudio_hw_attach(sc); + +#ifdef DEBUG + printf(" UNDER DEVELOPMENT (nuts)\n"); +#endif +} + +int +vidcaudio_open(dev, flags) + dev_t dev; + int flags; +{ + struct vidcaudio_softc *sc; + int unit = AUDIOUNIT (dev); + int s; + +#ifdef DEBUG + printf("DEBUG: vidcaudio_open called\n"); +#endif + + if (unit >= vidcaudio_cd.cd_ndevs) + return ENODEV; + + sc = vidcaudio_cd.cd_devs[unit]; + + if (!sc) + return ENXIO; + + s = splhigh(); + + if (sc->open != 0) { + (void)splx(s); + return EBUSY; + } + + sc->open=1; + ag.open=1; + + (void)splx(s); + return 0; +} + +void +vidcaudio_close(addr) + void *addr; +{ + struct vidcaudio_softc *sc = addr; + + vidcaudio_shutdown (); + +#ifdef DEBUG + printf("DEBUG: vidcaudio_close called\n"); +#endif + + sc->open = 0; + ag.open = 0; +} + +/* ************************************************************************* * + | Interface to the generic audio driver | + * ************************************************************************* */ + +int vidcaudio_set_in_sr __P((void *, u_long)); +u_long vidcaudio_get_in_sr __P((void *)); +int vidcaudio_set_out_sr __P((void *, u_long)); +u_long vidcaudio_get_out_sr __P((void *)); +int vidcaudio_query_encoding __P((void *, struct audio_encoding *)); +int vidcaudio_set_encoding __P((void *, u_int)); +int vidcaudio_get_encoding __P((void *)); +int vidcaudio_set_precision __P((void *, u_int)); +int vidcaudio_getprecision __P((void *)); +int vidcaudio_set_channels __P((void *, int)); +int vidcaudio_get_channels __P((void *)); +int vidcaudio_round_blocksize __P((void *, int)); +int vidcaudio_set_out_port __P((void *, int)); +int vidcaudio_get_out_port __P((void *)); +int vidcaudio_set_in_port __P((void *, int)); +int vidcaudio_get_in_port __P((void *)); +int vidcaudio_commit_settings __P((void *)); +u_int vidcaudio_get_silence __P((int)); +void vidcaudio_sw_encode __P((int, u_char *, int)); +void vidcaudio_sw_decode __P((int, u_char *, int)); +int vidcaudio_start_output __P((void *, void *, int, void (*)(), void *)); +int vidcaudio_start_input __P((void *, void *, int, void (*)(), void *)); +int vidcaudio_halt_output __P((void *)); +int vidcaudio_halt_input __P((void *)); +int vidcaudio_cont_output __P((void *)); +int vidcaudio_cont_input __P((void *)); +int vidcaudio_speaker_ctl __P((void *, int)); +int vidcaudio_getdev __P((void *, struct audio_device *)); +int vidcaudio_setfd __P((void *, int)); +int vidcaudio_set_port __P((void *, mixer_ctrl_t *)); +int vidcaudio_get_port __P((void *, mixer_ctrl_t *)); +int vidcaudio_query_devinfo __P((void *, mixer_devinfo_t *)); + +struct audio_device vidcaudio_device = { + "VidcAudio 8-bit", + "x", + "vidcaudio" +}; + +int +vidcaudio_set_in_sr(addr, sr) + void *addr; + u_long sr; +{ + ag.in_sr = sr; + return 0; +} + +u_long +vidcaudio_get_in_sr(addr) + void *addr; +{ + return ag.in_sr; +} + +int +vidcaudio_set_out_sr(addr, sr) + void *addr; + u_long sr; +{ + ag.out_sr = sr; + return 0; +} + +u_long +vidcaudio_get_out_sr(addr) + void *addr; +{ + return(ag.out_sr); +} + +int vidcaudio_query_encoding ( void *addr, struct audio_encoding *fp ) +{ + switch ( fp->index ) + { + case 0: + strcpy (fp->name, "vidc" ); + fp->format_id = AUDIO_ENCODING_ULAW; + break; + + default: + return (EINVAL); + } + return 0; +} + +int vidcaudio_set_encoding ( void *addr, u_int enc ) +{ + struct vidcaudio_softc *sc = addr; + + switch (enc) + { + case AUDIO_ENCODING_ULAW: +#ifdef DEBUG + printf ( "DEBUG: Set ulaw encoding\n" ); +#endif + sc->encoding = enc; + break; + + default: + return (EINVAL); + } + return 0; +} + +int vidcaudio_get_encoding ( void *addr ) +{ + struct vidcaudio_softc *sc = addr; + return sc->encoding; +} + +int vidcaudio_set_precision ( void *addr, u_int prec ) +{ + if (prec != 8) + return EINVAL; + return 0; +} + +int vidcaudio_get_precision ( void *addr ) +{ + return (8); +} + +int vidcaudio_set_channels ( void *addr, int chans ) +{ + if ( chans!=1 ) + return EINVAL; + + return 0; +} + +int vidcaudio_get_channels ( void *addr ) +{ + return 1; +} + +int vidcaudio_round_blocksize ( void *addr, int blk ) +{ + if (blk>NBPG) blk=NBPG; + return blk; +} + +int vidcaudio_set_out_port ( void *addr, int port ) +{ + struct vidcaudio_softc *sc = addr; + sc->outport = port; + return 0; +} + +int vidcaudio_get_out_port ( void *addr ) +{ + struct vidcaudio_softc *sc = addr; + return sc->outport; +} + +int vidcaudio_set_in_port ( void *addr, int port ) +{ + struct vidcaudio_softc *sc = addr; + sc->inport = port; + return 0; +} + +int vidcaudio_get_in_port ( void *addr ) +{ + struct vidcaudio_softc *sc = addr; + return sc->inport; +} + +int vidcaudio_commit_settings ( void *addr ) +{ +#ifdef DEBUG +printf ( "DEBUG: committ_settings\n" ); +#endif + return 0; +} + +u_int vidcaudio_get_silence ( int enc ) +{ + switch (enc) + { + case AUDIO_ENCODING_ULAW: + return 0x7f; + + default: + return 0; + } + return 0; +} + +void vidcaudio_sw_encode ( int e, u_char *p, int cc ) +{ +#ifdef DEBUG + printf ( "DEBUG: sw_encode\n" ); +#endif + return; +} + +void vidcaudio_sw_decode ( int e, u_char *p, int cc ) +{ +#ifdef DEBUG + printf ( "DEBUG: sw_decode\n" ); +#endif +} + +#define ROUND(s) ( ((int)s) & (~(NBPG-1)) ) + +int vidcaudio_start_output ( void *addr, void *p, int cc, + void (*intr)(), void *arg ) +{ + /* I can only DMA inside 1 page */ + +#ifdef DEBUG + printf ( "vidcaudio_start_output (%d) %08x %08x\n", cc, intr, arg ); +#endif + + if ( ROUND(p) != ROUND(p+cc) ) + { + /* If it's over a page I can fix it up by copying it into my buffer */ + +#ifdef DEBUG + printf ( "vidcaudio: DMA over page boundary requested. Fixing up\n" ); +#endif + bcopy ( (char *)ag.silence, p, cc>NBPG ? NBPG : cc ); + p=(void *)ag.silence; + + /* I can't DMA any more than that, but it is possible to fix it up */ + /* by handling multiple buffers and only interrupting the audio */ + /* driver after sending out all the stuff it gave me. That it more */ + /* than I can be bothered to do right now and it probablly wont */ + /* happen so I'll just truncate the buffer and tell the user */ + + if ( cc>NBPG ) + { + printf ( "vidcaudio: DMA buffer truncated. I could fix this up\n" ); + cc=NBPG; + } + } + vidcaudio_dma_program ( (vm_offset_t)p, (vm_offset_t)(p+cc), intr, arg ); + return 0; +} + +int vidcaudio_start_input ( void *addr, void *p, int cc, + void (*intr)(), void *arg ) +{ + return EIO; +} + +int vidcaudio_halt_output ( void *addr ) +{ +#ifdef DEBUG + printf ( "DEBUG: vidcaudio_halt_output\n" ); +#endif + return EIO; +} + +int vidcaudio_halt_input ( void *addr ) +{ +#ifdef DEBUG + printf ( "DEBUG: vidcaudio_halt_output\n" ); +#endif + return EIO; +} + +int vidcaudio_cont_output ( void *addr ) +{ +#ifdef DEBUG + printf ( "DEBUG: vidcaudio_halt_output\n" ); +#endif + return EIO; +} + +int vidcaudio_cont_input ( void *addr ) +{ +#ifdef DEBUG + printf ( "DEBUG: vidcaudio_halt_output\n" ); +#endif + return EIO; +} + +int vidcaudio_speaker_ctl ( void *addr, int newstate ) +{ +#ifdef DEBUG + printf ( "DEBUG: vidcaudio_speaker_ctl\n" ); +#endif + return 0; +} + +int vidcaudio_getdev ( void *addr, struct audio_device *retp ) +{ + *retp = vidcaudio_device; + return 0; +} + + +int vidcaudio_setfd ( void *addr, int flag ) +{ + return ENOTTY; +} + +int vidcaudio_set_port ( void *addr, mixer_ctrl_t *cp ) +{ + return EINVAL; +} + +int vidcaudio_get_port ( void *addr, mixer_ctrl_t *cp ) +{ + return EINVAL; +} + +int vidcaudio_query_devinfo ( void *addr, mixer_devinfo_t *dip ) +{ + return 0; +} + +struct audio_hw_if vidcaudio_hw_if = { + vidcaudio_open, + vidcaudio_close, + NULL, + vidcaudio_set_in_sr, + vidcaudio_get_in_sr, + vidcaudio_set_out_sr, + vidcaudio_get_out_sr, + vidcaudio_query_encoding, + vidcaudio_set_encoding, + vidcaudio_get_encoding, + vidcaudio_set_precision, + vidcaudio_get_precision, + vidcaudio_set_channels, + vidcaudio_get_channels, + vidcaudio_round_blocksize, + vidcaudio_set_out_port, + vidcaudio_get_out_port, + vidcaudio_set_in_port, + vidcaudio_get_in_port, + vidcaudio_commit_settings, + vidcaudio_get_silence, + vidcaudio_sw_encode, + vidcaudio_sw_decode, + vidcaudio_start_output, + vidcaudio_start_input, + vidcaudio_halt_output, + vidcaudio_halt_input, + vidcaudio_cont_output, + vidcaudio_cont_input, + vidcaudio_speaker_ctl, + vidcaudio_getdev, + vidcaudio_setfd, + vidcaudio_set_port, + vidcaudio_get_port, + vidcaudio_query_devinfo, + 0, /* not full duplex */ + 0 +}; + +void vidcaudio_dummy_routine ( void *arg ) +{ +#ifdef DEBUG + printf ( "vidcaudio_dummy_routine\n" ); +#endif +} + +int vidcaudio_hw_attach ( struct vidcaudio_softc *sc ) +{ + return ( audio_hardware_attach ( &vidcaudio_hw_if, sc ) ); +} + +int vidcaudio_rate ( int rate ) +{ + WriteWord ( VIDC_BASE, VIDC_SFR | rate ); + return 0; +} + +int vidcaudio_stereo ( int channel, int position ) +{ + if ( channel<0 ) return EINVAL; + if ( channel>7 ) return EINVAL; + channel = channel<<24 | VIDC_SIR0; + WriteWord ( VIDC_BASE, channel|position ); + return 0; +} + +#define PHYS(x) (pmap_extract( kernel_pmap, ((x)&PG_FRAME) )) + +/* + * Program the next buffer to be used + * This function must be re-entrant, maximum re-entrancy of 2 + */ + +#define FLAGS (0) + +int vidcaudio_dma_program ( vm_offset_t cur, vm_offset_t end, + void (*intr)(), void *arg ) +{ + /* If there isn't a transfer in progress then start a new one */ + if ( ag.in_progress==0 ) + { + ag.buffer = 0; + WriteWord ( IOMD_SD0CR, 0x90 ); /* Reset State Machine */ + WriteWord ( IOMD_SD0CR, 0x30 ); /* Reset State Machine */ + + WriteWord ( IOMD_SD0CURB, PHYS(cur) ); + WriteWord ( IOMD_SD0ENDB, PHYS(end-16)|FLAGS ); + WriteWord ( IOMD_SD0CURA, PHYS(cur) ); + WriteWord ( IOMD_SD0ENDA, PHYS(end-16)|FLAGS ); + + ag.in_progress = 1; + + ag.next_cur = ag.next_end = 0; + ag.next_intr = ag.next_arg = 0; + + ag.intr = intr; + ag.arg = arg; + + /* The driver 'clicks' between buffer swaps, leading me to think */ + /* that the fifo is much small than on other sound cards. So */ + /* so I'm going to have to do some tricks here */ + + (*ag.intr)(ag.arg); /* Schedule the next buffer */ + ag.intr = vidcaudio_dummy_routine; /* Already done this */ + ag.arg = NULL; + +#ifdef PRINT +printf ( "vidcaudio: start output\n" ); +#endif +#ifdef DEBUG + printf ( "SE" ); +#endif + enable_irq ( IRQ_DMASCH0 ); + } + else + { + /* Otherwise schedule the next one */ + if ( ag.next_cur!=0 ) + { + /* If there's one scheduled then complain */ + printf ( "vidcaudio: Buffer already Q'ed\n" ); + return EIO; + } + else + { + /* We're OK to schedule it now */ + ag.buffer = (++ag.buffer) & 1; + ag.next_cur = PHYS(cur); + ag.next_end = PHYS(end-16); + ag.next_intr = intr; + ag.next_arg = arg; +#ifdef DEBUG +printf ( "s" ); +#endif + } + } + return 0; +} + +void vidcaudio_shutdown ( void ) +{ + /* Shut down the channel */ + ag.intr = NULL; + ag.in_progress = 0; +#ifdef PRINT +printf ( "vidcaudio: stop output\n" ); +#endif + WriteWord ( IOMD_SD0CURB, ag.silence ); + WriteWord ( IOMD_SD0ENDB, (ag.silence + NBPG - 16) | (1<<30) ); + disable_irq ( IRQ_DMASCH0 ); +} + +int vidcaudio_intr ( void *arg ) +{ + int status = ReadByte(IOMD_SD0ST); + void (*nintr)(); + void *narg; + void (*xintr)(); + void *xarg; + int xcur, xend; + WriteWord ( IOMD_DMARQ, 0x10 ); + +#ifdef PRINT + printf ( "I" ); +#endif + + if ( ag.open==0 ) + { + vidcaudio_shutdown (); + return 0; + } + + /* Have I got the generic audio device attached */ + +#ifdef DEBUG + printf ( "[B%01x]", status ); +#endif + + nintr=ag.intr; + narg=ag.arg; + ag.intr = NULL; + + xintr=ag.next_intr; + xarg=ag.next_arg; + xcur=ag.next_cur; + xend=ag.next_end; + ag.next_cur = 0; + ag.intr = xintr; + ag.arg = xarg; + + if ( nintr ) + { +#ifdef DEBUG + printf ( "i" ); +#endif + (*nintr)(narg); + } + + if ( xcur==0 ) + { + vidcaudio_shutdown (); + } + else + { +#define OVERRUN (0x04) +#define INTERRUPT (0x02) +#define BANK_A (0x00) +#define BANK_B (0x01) + switch ( status&0x7 ) + { + case (INTERRUPT|BANK_A): +#ifdef PRINT +printf( "B" ); +#endif + WriteWord ( IOMD_SD0CURB, xcur ); + WriteWord ( IOMD_SD0ENDB, xend|FLAGS ); + break; + + case (INTERRUPT|BANK_B): +#ifdef PRINT +printf( "A" ); +#endif + WriteWord ( IOMD_SD0CURA, xcur ); + WriteWord ( IOMD_SD0ENDA, xend|FLAGS ); + break; + + case (OVERRUN|INTERRUPT|BANK_A): +#ifdef PRINT +printf( "A" ); +#endif + WriteWord ( IOMD_SD0CURA, xcur ); + WriteWord ( IOMD_SD0ENDA, xend|FLAGS ); + break; + + case (OVERRUN|INTERRUPT|BANK_B): +#ifdef PRINT +printf( "B" ); +#endif + WriteWord ( IOMD_SD0CURB, xcur ); + WriteWord ( IOMD_SD0ENDB, xend|FLAGS ); + break; + } +/* + ag.next_cur = 0; + ag.intr = xintr; + ag.arg = xarg; +*/ + } +#ifdef PRINT +printf ( "i" ); +#endif + + if ( ag.next_cur==0 ) + { + (*ag.intr)(ag.arg); /* Schedule the next buffer */ + ag.intr = vidcaudio_dummy_routine; /* Already done this */ + ag.arg = NULL; + } + return 0; +} + + diff --git a/sys/arch/arm32/mainbus/waveform.h b/sys/arch/arm32/mainbus/waveform.h new file mode 100644 index 00000000000..1508a71d2e3 --- /dev/null +++ b/sys/arch/arm32/mainbus/waveform.h @@ -0,0 +1,551 @@ +/* $NetBSD: waveform.h,v 1.2 1996/03/18 20:50:06 mark Exp $ */ + +/* + * Copyright (c) 1994,1995 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + */ + +static unsigned char beep_waveform[] = { + 0x00, 0x06, 0x18, 0x2a, 0x3e, 0x4a, 0x54, 0x60, + 0x64, 0x68, 0x6a, 0x6a, 0x66, 0x62, 0x54, 0x3e, + 0x00, 0x45, 0x61, 0x6f, 0x7f, 0x87, 0x8d, 0x91, + 0x93, 0x95, 0x93, 0x91, 0x8b, 0x85, 0x73, 0x59, + 0x00, 0x5c, 0x78, 0x8a, 0x96, 0xa0, 0xa6, 0xa8, + 0xaa, 0xaa, 0xa8, 0xa6, 0xa2, 0x94, 0x86, 0x6a, + 0x00, 0x6b, 0x8b, 0x9d, 0xa7, 0xaf, 0xb5, 0xb9, + 0xbb, 0xbb, 0xb9, 0xb3, 0xad, 0xa5, 0x93, 0x77, + 0x00, 0x78, 0x96, 0xa8, 0xb2, 0xbc, 0xc2, 0xc4, + 0xc6, 0xc6, 0xc4, 0xc0, 0xb8, 0xac, 0x9e, 0x82, + 0x00, 0x83, 0xa3, 0xb1, 0xbf, 0xc5, 0xcb, 0xcd, + 0xcf, 0xcf, 0xcd, 0xc9, 0xc3, 0xb7, 0xa7, 0x89, + 0x00, 0x88, 0xa8, 0xb8, 0xc4, 0xcc, 0xd0, 0xd4, + 0xd6, 0xd4, 0xd2, 0xce, 0xc8, 0xbe, 0xac, 0x8e, + 0x00, 0x8f, 0xaf, 0xc3, 0xcb, 0xd3, 0xd9, 0xdd, + 0xdf, 0xdd, 0xdb, 0xd5, 0xcf, 0xc5, 0xb3, 0x95, + 0x00, 0x96, 0xb4, 0xc6, 0xd0, 0xd8, 0xe0, 0xe2, + 0xe4, 0xe2, 0xe2, 0xdc, 0xd2, 0xc8, 0xb8, 0x9a, + 0x00, 0x9d, 0xbb, 0xcb, 0xd7, 0xe1, 0xe5, 0xe7, + 0xe9, 0xe7, 0xe5, 0xe3, 0xd9, 0xcf, 0xbf, 0xa1, + 0x00, 0xa2, 0xc0, 0xce, 0xdc, 0xe4, 0xe8, 0xea, + 0xec, 0xea, 0xe8, 0xe4, 0xde, 0xd2, 0xc2, 0xa4, + 0x00, 0xa5, 0xc5, 0xd5, 0xe3, 0xe7, 0xed, 0xef, + 0xf1, 0xef, 0xed, 0xe9, 0xe3, 0xd7, 0xc7, 0xa9, + 0x00, 0xa8, 0xc6, 0xd8, 0xe4, 0xea, 0xee, 0xf2, + 0xf4, 0xf2, 0xf0, 0xec, 0xe6, 0xda, 0xc8, 0xaa, + 0x00, 0xab, 0xcb, 0xdd, 0xe7, 0xef, 0xf3, 0xf7, + 0xf9, 0xf7, 0xf5, 0xef, 0xe9, 0xdf, 0xcd, 0xaf, + 0x00, 0xae, 0xcc, 0xe0, 0xea, 0xf0, 0xf6, 0xfa, + 0xfc, 0xfa, 0xf8, 0xf2, 0xea, 0xe2, 0xd0, 0xb0, + 0x00, 0xb1, 0xd1, 0xe3, 0xed, 0xf5, 0xfb, 0xff, + 0xff, 0xff, 0xfb, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb4, + 0x00, 0xb5, 0xd3, 0xe5, 0xef, 0xf7, 0xfd, 0xff, + 0xff, 0xff, 0xfd, 0xf7, 0xef, 0xe5, 0xd3, 0xb5, + 0x00, 0xb4, 0xd2, 0xe4, 0xee, 0xf6, 0xfc, 0xfe, + 0xfe, 0xfe, 0xfc, 0xf6, 0xee, 0xe4, 0xd2, 0xb2, + 0x00, 0xb3, 0xd3, 0xe5, 0xef, 0xf7, 0xfb, 0xff, + 0xff, 0xff, 0xfb, 0xf5, 0xef, 0xe5, 0xd1, 0xb3, + 0x00, 0xb2, 0xd0, 0xe4, 0xec, 0xf4, 0xfa, 0xfe, + 0xfe, 0xfe, 0xfa, 0xf4, 0xec, 0xe4, 0xd0, 0xb2, + 0x00, 0xb3, 0xd1, 0xe5, 0xed, 0xf5, 0xfb, 0xff, + 0xff, 0xff, 0xfb, 0xf5, 0xed, 0xe5, 0xd1, 0xb3, + 0x00, 0xb2, 0xd0, 0xe4, 0xec, 0xf4, 0xfa, 0xfc, + 0xfe, 0xfc, 0xfa, 0xf4, 0xec, 0xe2, 0xd0, 0xb2, + 0x00, 0xb3, 0xd1, 0xe3, 0xed, 0xf5, 0xf9, 0xfd, + 0xff, 0xfd, 0xf9, 0xf5, 0xed, 0xe3, 0xd1, 0xb1, + 0x00, 0xb0, 0xd0, 0xe2, 0xec, 0xf2, 0xf8, 0xfc, + 0xfc, 0xfc, 0xf8, 0xf2, 0xec, 0xe2, 0xd0, 0xb0, + 0x00, 0xb1, 0xcf, 0xe3, 0xed, 0xf3, 0xf9, 0xfd, + 0xfd, 0xfd, 0xf9, 0xf3, 0xed, 0xe3, 0xcf, 0xb1, + 0x00, 0xb0, 0xce, 0xe2, 0xea, 0xf2, 0xf8, 0xfa, + 0xfc, 0xfa, 0xf8, 0xf2, 0xea, 0xe2, 0xce, 0xb0, + 0x00, 0xb1, 0xcf, 0xe3, 0xeb, 0xf3, 0xf9, 0xfb, + 0xfd, 0xfb, 0xf7, 0xf3, 0xeb, 0xe3, 0xcf, 0xb1, + 0x00, 0xb0, 0xce, 0xe2, 0xea, 0xf2, 0xf6, 0xfa, + 0xfa, 0xfa, 0xf6, 0xf2, 0xea, 0xe2, 0xce, 0xae, + 0x00, 0xaf, 0xcf, 0xe3, 0xeb, 0xf1, 0xf7, 0xfb, + 0xfb, 0xfb, 0xf7, 0xf1, 0xeb, 0xe1, 0xcf, 0xaf, + 0x00, 0xae, 0xce, 0xe0, 0xea, 0xf0, 0xf6, 0xf8, + 0xfa, 0xf8, 0xf6, 0xf0, 0xea, 0xe0, 0xcc, 0xae, + 0x00, 0xaf, 0xcd, 0xe1, 0xeb, 0xf1, 0xf7, 0xf9, + 0xfb, 0xf9, 0xf7, 0xf1, 0xe9, 0xe1, 0xcd, 0xaf, + 0x00, 0xae, 0xcc, 0xe0, 0xe8, 0xf0, 0xf4, 0xf8, + 0xf8, 0xf8, 0xf4, 0xf0, 0xe8, 0xe0, 0xcc, 0xae, + 0x00, 0xaf, 0xcd, 0xe1, 0xe9, 0xf1, 0xf5, 0xf9, + 0xf9, 0xf9, 0xf5, 0xf1, 0xe9, 0xdf, 0xcd, 0xad, + 0x00, 0xac, 0xcc, 0xde, 0xe8, 0xee, 0xf4, 0xf6, + 0xf8, 0xf6, 0xf4, 0xee, 0xe8, 0xde, 0xcc, 0xac, + 0x00, 0xad, 0xcd, 0xdf, 0xe9, 0xef, 0xf5, 0xf7, + 0xf9, 0xf7, 0xf5, 0xef, 0xe9, 0xdf, 0xcb, 0xad, + 0x00, 0xac, 0xca, 0xde, 0xe8, 0xee, 0xf2, 0xf6, + 0xf6, 0xf6, 0xf2, 0xee, 0xe8, 0xde, 0xca, 0xac, + 0x00, 0xad, 0xcb, 0xdd, 0xe7, 0xef, 0xf3, 0xf7, + 0xf7, 0xf7, 0xf3, 0xef, 0xe7, 0xdd, 0xcb, 0xad, + 0x00, 0xac, 0xca, 0xdc, 0xe6, 0xee, 0xf2, 0xf4, + 0xf6, 0xf4, 0xf2, 0xee, 0xe6, 0xdc, 0xca, 0xaa, + 0x00, 0xab, 0xcb, 0xdd, 0xe7, 0xed, 0xf3, 0xf5, + 0xf7, 0xf5, 0xf3, 0xed, 0xe7, 0xdd, 0xcb, 0xab, + 0x00, 0xaa, 0xca, 0xdc, 0xe6, 0xec, 0xf2, 0xf4, + 0xf4, 0xf4, 0xf0, 0xec, 0xe6, 0xda, 0xc8, 0xaa, + 0x00, 0xab, 0xc9, 0xdb, 0xe7, 0xed, 0xf1, 0xf5, + 0xf5, 0xf5, 0xf1, 0xed, 0xe7, 0xdb, 0xc9, 0xab, + 0x00, 0xaa, 0xc8, 0xda, 0xe6, 0xec, 0xf0, 0xf4, + 0xf4, 0xf2, 0xf0, 0xec, 0xe6, 0xda, 0xc8, 0xaa, + 0x00, 0xab, 0xc9, 0xdb, 0xe5, 0xed, 0xf1, 0xf3, + 0xf5, 0xf3, 0xf1, 0xeb, 0xe5, 0xdb, 0xc9, 0xa9, + 0x00, 0xa8, 0xc8, 0xd8, 0xe4, 0xea, 0xf0, 0xf2, + 0xf2, 0xf2, 0xf0, 0xea, 0xe4, 0xd8, 0xc8, 0xa8, + 0x00, 0xa9, 0xc9, 0xd9, 0xe5, 0xeb, 0xef, 0xf3, + 0xf3, 0xf3, 0xef, 0xeb, 0xe5, 0xd9, 0xc9, 0xa9, + 0x00, 0xa8, 0xc6, 0xd8, 0xe4, 0xea, 0xee, 0xf2, + 0xf2, 0xf2, 0xee, 0xea, 0xe4, 0xd8, 0xc6, 0xa8, + 0x00, 0xa9, 0xc7, 0xd9, 0xe5, 0xeb, 0xef, 0xf1, + 0xf3, 0xf1, 0xef, 0xeb, 0xe5, 0xd7, 0xc7, 0xa9, + 0x00, 0xa8, 0xc6, 0xd6, 0xe4, 0xea, 0xee, 0xf0, + 0xf0, 0xf0, 0xee, 0xe8, 0xe2, 0xd6, 0xc6, 0xa8, + 0x00, 0xa7, 0xc7, 0xd7, 0xe3, 0xe9, 0xed, 0xf1, + 0xf1, 0xf1, 0xed, 0xe9, 0xe3, 0xd7, 0xc7, 0xa7, + 0x00, 0xa6, 0xc6, 0xd6, 0xe2, 0xe8, 0xec, 0xf0, + 0xf0, 0xf0, 0xec, 0xe8, 0xe2, 0xd6, 0xc6, 0xa6, + 0x00, 0xa7, 0xc5, 0xd7, 0xe3, 0xe9, 0xed, 0xef, + 0xf1, 0xef, 0xed, 0xe9, 0xe3, 0xd5, 0xc5, 0xa7, + 0x00, 0xa6, 0xc4, 0xd4, 0xe2, 0xe8, 0xec, 0xee, + 0xee, 0xee, 0xec, 0xe8, 0xe2, 0xd4, 0xc4, 0xa6, + 0x00, 0xa7, 0xc5, 0xd5, 0xe3, 0xe7, 0xed, 0xef, + 0xef, 0xef, 0xeb, 0xe7, 0xe3, 0xd5, 0xc5, 0xa7, + 0x00, 0xa6, 0xc4, 0xd4, 0xe0, 0xe6, 0xea, 0xee, + 0xee, 0xee, 0xea, 0xe6, 0xe0, 0xd4, 0xc4, 0xa4, + 0x00, 0xa5, 0xc5, 0xd3, 0xe1, 0xe7, 0xeb, 0xed, + 0xef, 0xed, 0xeb, 0xe7, 0xe1, 0xd3, 0xc5, 0xa5, + 0x00, 0xa4, 0xc4, 0xd2, 0xe0, 0xe6, 0xea, 0xec, + 0xec, 0xec, 0xea, 0xe6, 0xe0, 0xd2, 0xc2, 0xa4, + 0x00, 0xa5, 0xc3, 0xd3, 0xdf, 0xe7, 0xeb, 0xed, + 0xed, 0xed, 0xeb, 0xe7, 0xdf, 0xd3, 0xc3, 0xa5, + 0x00, 0xa4, 0xc2, 0xd2, 0xde, 0xe4, 0xe8, 0xec, + 0xec, 0xec, 0xe8, 0xe4, 0xde, 0xd0, 0xc2, 0xa4, + 0x00, 0xa5, 0xc3, 0xd1, 0xdf, 0xe5, 0xe9, 0xeb, + 0xed, 0xeb, 0xe9, 0xe5, 0xdf, 0xd1, 0xc3, 0xa3, + 0x00, 0xa2, 0xc2, 0xd0, 0xdc, 0xe4, 0xe8, 0xea, + 0xea, 0xea, 0xe8, 0xe4, 0xdc, 0xd0, 0xc2, 0xa2, + 0x00, 0xa3, 0xc3, 0xd1, 0xdd, 0xe5, 0xe9, 0xeb, + 0xeb, 0xeb, 0xe9, 0xe5, 0xdd, 0xd1, 0xc1, 0xa3, + 0x00, 0xa2, 0xc0, 0xce, 0xdc, 0xe4, 0xe6, 0xea, + 0xea, 0xea, 0xe6, 0xe4, 0xda, 0xce, 0xc0, 0xa2, + 0x00, 0xa3, 0xc1, 0xcf, 0xdb, 0xe3, 0xe7, 0xe9, + 0xeb, 0xe9, 0xe7, 0xe3, 0xdb, 0xcf, 0xc1, 0xa3, + 0x00, 0xa2, 0xc0, 0xce, 0xda, 0xe2, 0xe6, 0xe8, + 0xe8, 0xe8, 0xe6, 0xe2, 0xda, 0xce, 0xbe, 0xa0, + 0x00, 0xa1, 0xbf, 0xcf, 0xdb, 0xe3, 0xe7, 0xe9, + 0xe9, 0xe9, 0xe7, 0xe3, 0xd9, 0xcd, 0xbf, 0xa1, + 0x00, 0xa0, 0xbe, 0xcc, 0xd8, 0xe2, 0xe6, 0xe8, + 0xe8, 0xe8, 0xe4, 0xe2, 0xd8, 0xcc, 0xbc, 0x9e, + 0x00, 0x9f, 0xbd, 0xcd, 0xd9, 0xe3, 0xe5, 0xe7, + 0xe9, 0xe7, 0xe5, 0xe1, 0xd9, 0xcd, 0xbd, 0x9f, + 0x00, 0x9e, 0xbc, 0xcc, 0xd8, 0xe0, 0xe4, 0xe6, + 0xe6, 0xe6, 0xe4, 0xe0, 0xd6, 0xcc, 0xbc, 0x9e, + 0x00, 0x9f, 0xbd, 0xcd, 0xd7, 0xe1, 0xe5, 0xe7, + 0xe7, 0xe7, 0xe5, 0xe1, 0xd7, 0xcb, 0xbb, 0x9d, + 0x00, 0x9c, 0xba, 0xca, 0xd6, 0xde, 0xe4, 0xe6, + 0xe6, 0xe6, 0xe4, 0xde, 0xd6, 0xca, 0xba, 0x9c, + 0x00, 0x9d, 0xbb, 0xcb, 0xd5, 0xdf, 0xe3, 0xe5, + 0xe7, 0xe5, 0xe3, 0xdf, 0xd5, 0xcb, 0xbb, 0x9d, + 0x00, 0x9a, 0xb8, 0xca, 0xd4, 0xde, 0xe2, 0xe4, + 0xe4, 0xe4, 0xe2, 0xdc, 0xd4, 0xca, 0xb8, 0x9a, + 0x00, 0x9b, 0xb9, 0xc9, 0xd5, 0xdd, 0xe3, 0xe5, + 0xe5, 0xe5, 0xe3, 0xdd, 0xd5, 0xc9, 0xb9, 0x9b, + 0x00, 0x9a, 0xb8, 0xc8, 0xd2, 0xdc, 0xe2, 0xe4, + 0xe4, 0xe4, 0xe2, 0xdc, 0xd2, 0xc8, 0xb6, 0x98, + 0x00, 0x99, 0xb7, 0xc9, 0xd3, 0xdb, 0xe1, 0xe3, + 0xe5, 0xe3, 0xe1, 0xdb, 0xd3, 0xc9, 0xb7, 0x99, + 0x00, 0x98, 0xb6, 0xc8, 0xd2, 0xda, 0xe0, 0xe2, + 0xe2, 0xe2, 0xe0, 0xda, 0xd2, 0xc6, 0xb6, 0x98, + 0x00, 0x99, 0xb7, 0xc7, 0xd1, 0xd9, 0xdf, 0xe3, + 0xe3, 0xe3, 0xdf, 0xd9, 0xd1, 0xc7, 0xb5, 0x97, + 0x00, 0x96, 0xb4, 0xc6, 0xd0, 0xd8, 0xde, 0xe2, + 0xe2, 0xe2, 0xde, 0xd8, 0xd0, 0xc6, 0xb4, 0x96, + 0x00, 0x97, 0xb5, 0xc7, 0xd1, 0xd9, 0xdf, 0xe1, + 0xe3, 0xe1, 0xdd, 0xd7, 0xcf, 0xc7, 0xb3, 0x95, + 0x00, 0x94, 0xb2, 0xc4, 0xce, 0xd6, 0xdc, 0xe0, + 0xe0, 0xe0, 0xdc, 0xd6, 0xce, 0xc4, 0xb2, 0x94, + 0x00, 0x95, 0xb3, 0xc5, 0xcf, 0xd7, 0xdd, 0xdf, + 0xe1, 0xdf, 0xdd, 0xd7, 0xcf, 0xc5, 0xb3, 0x95, + 0x00, 0x94, 0xb2, 0xc4, 0xce, 0xd4, 0xda, 0xde, + 0xde, 0xde, 0xda, 0xd4, 0xcc, 0xc4, 0xb0, 0x92, + 0x00, 0x93, 0xb1, 0xc5, 0xcd, 0xd5, 0xdb, 0xdd, + 0xdf, 0xdd, 0xdb, 0xd5, 0xcd, 0xc3, 0xb1, 0x93, + 0x00, 0x92, 0xb0, 0xc2, 0xcc, 0xd4, 0xd8, 0xdc, + 0xdc, 0xdc, 0xd8, 0xd2, 0xcc, 0xc2, 0xb0, 0x90, + 0x00, 0x91, 0xaf, 0xc3, 0xcb, 0xd3, 0xd9, 0xdb, + 0xdd, 0xdb, 0xd9, 0xd3, 0xcb, 0xc3, 0xaf, 0x91, + 0x00, 0x90, 0xae, 0xc2, 0xca, 0xd2, 0xd6, 0xda, + 0xda, 0xda, 0xd6, 0xd2, 0xca, 0xc2, 0xae, 0x90, + 0x00, 0x91, 0xaf, 0xc3, 0xcb, 0xd1, 0xd7, 0xd9, + 0xdb, 0xd9, 0xd7, 0xd1, 0xcb, 0xc1, 0xad, 0x8f, + 0x00, 0x8e, 0xac, 0xc0, 0xc8, 0xd0, 0xd4, 0xd8, + 0xd8, 0xd8, 0xd4, 0xd0, 0xc8, 0xc0, 0xac, 0x8e, + 0x00, 0x8f, 0xad, 0xbf, 0xc9, 0xcf, 0xd5, 0xd7, + 0xd9, 0xd7, 0xd5, 0xcf, 0xc9, 0xbf, 0xad, 0x8f, + 0x00, 0x8c, 0xac, 0xbe, 0xc8, 0xce, 0xd2, 0xd6, + 0xd6, 0xd6, 0xd2, 0xce, 0xc8, 0xbe, 0xaa, 0x8c, + 0x00, 0x8d, 0xab, 0xbd, 0xc7, 0xcf, 0xd3, 0xd5, + 0xd7, 0xd5, 0xd3, 0xcd, 0xc7, 0xbd, 0xab, 0x8d, + 0x00, 0x8c, 0xaa, 0xbc, 0xc6, 0xcc, 0xd2, 0xd4, + 0xd4, 0xd4, 0xd0, 0xcc, 0xc6, 0xba, 0xaa, 0x8a, + 0x00, 0x8b, 0xa9, 0xbb, 0xc7, 0xcd, 0xd1, 0xd5, + 0xd5, 0xd3, 0xd1, 0xcd, 0xc5, 0xbb, 0xa9, 0x8b, + 0x00, 0x8a, 0xa8, 0xba, 0xc4, 0xca, 0xd0, 0xd2, + 0xd2, 0xd2, 0xd0, 0xca, 0xc4, 0xb8, 0xa8, 0x8a, + 0x00, 0x8b, 0xa9, 0xb9, 0xc5, 0xcb, 0xcf, 0xd3, + 0xd3, 0xd1, 0xcf, 0xcb, 0xc5, 0xb9, 0xa7, 0x89, + 0x00, 0x88, 0xa6, 0xb8, 0xc4, 0xca, 0xce, 0xd0, + 0xd0, 0xd0, 0xce, 0xc8, 0xc2, 0xb6, 0xa6, 0x88, + 0x00, 0x89, 0xa7, 0xb7, 0xc3, 0xc9, 0xcd, 0xd1, + 0xd1, 0xcf, 0xcd, 0xc9, 0xc3, 0xb7, 0xa7, 0x87, + 0x00, 0x86, 0xa6, 0xb4, 0xc2, 0xc8, 0xcc, 0xce, + 0xce, 0xce, 0xcc, 0xc8, 0xc2, 0xb4, 0xa4, 0x86, + 0x00, 0x87, 0xa5, 0xb5, 0xc3, 0xc7, 0xcb, 0xcf, + 0xcf, 0xcf, 0xcb, 0xc7, 0xc1, 0xb5, 0xa5, 0x87, + 0x00, 0x86, 0xa4, 0xb2, 0xc0, 0xc6, 0xca, 0xcc, + 0xcc, 0xcc, 0xca, 0xc6, 0xc0, 0xb2, 0xa2, 0x84, + 0x00, 0x85, 0xa3, 0xb3, 0xbf, 0xc5, 0xc9, 0xcd, + 0xcd, 0xcd, 0xc9, 0xc5, 0xbf, 0xb1, 0xa3, 0x85, + 0x00, 0x84, 0xa2, 0xb0, 0xbc, 0xc4, 0xc8, 0xca, + 0xca, 0xca, 0xc8, 0xc4, 0xbc, 0xb0, 0xa2, 0x82, + 0x00, 0x83, 0xa3, 0xb1, 0xbd, 0xc5, 0xc7, 0xcb, + 0xcb, 0xcb, 0xc7, 0xc3, 0xbb, 0xaf, 0xa1, 0x83, + 0x00, 0x82, 0xa0, 0xae, 0xba, 0xc2, 0xc6, 0xc8, + 0xc8, 0xc8, 0xc6, 0xc2, 0xba, 0xae, 0x9e, 0x82, + 0x00, 0x83, 0x9f, 0xaf, 0xb9, 0xc3, 0xc7, 0xc9, + 0xc9, 0xc9, 0xc5, 0xc3, 0xb9, 0xad, 0x9f, 0x81, + 0x00, 0x80, 0x9c, 0xac, 0xb8, 0xc0, 0xc4, 0xc6, + 0xc6, 0xc6, 0xc4, 0xc0, 0xb6, 0xac, 0x9c, 0x7e, + 0x00, 0x7f, 0x9d, 0xab, 0xb7, 0xbf, 0xc5, 0xc7, + 0xc7, 0xc7, 0xc5, 0xbf, 0xb7, 0xab, 0x9b, 0x7d, + 0x00, 0x7c, 0x9a, 0xaa, 0xb4, 0xbe, 0xc2, 0xc4, + 0xc4, 0xc4, 0xc2, 0xbc, 0xb4, 0xaa, 0x98, 0x7c, + 0x00, 0x7d, 0x99, 0xa9, 0xb5, 0xbd, 0xc3, 0xc5, + 0xc5, 0xc5, 0xc3, 0xbb, 0xb3, 0xa9, 0x99, 0x7b, + 0x00, 0x7a, 0x96, 0xa8, 0xb2, 0xba, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc0, 0xba, 0xb0, 0xa6, 0x96, 0x78, + 0x00, 0x79, 0x97, 0xa7, 0xb1, 0xb9, 0xbf, 0xc3, + 0xc3, 0xc3, 0xbf, 0xb9, 0xb1, 0xa7, 0x95, 0x77, + 0x00, 0x76, 0x94, 0xa6, 0xae, 0xb6, 0xbc, 0xc0, + 0xc0, 0xc0, 0xbc, 0xb6, 0xae, 0xa4, 0x92, 0x76, + 0x00, 0x75, 0x93, 0xa5, 0xaf, 0xb5, 0xbb, 0xbf, + 0xbf, 0xbf, 0xbb, 0xb5, 0xad, 0xa5, 0x91, 0x75, + 0x00, 0x74, 0x90, 0xa4, 0xac, 0xb4, 0xb8, 0xbc, + 0xbc, 0xbc, 0xb8, 0xb2, 0xac, 0xa2, 0x90, 0x72, + 0x00, 0x73, 0x91, 0xa3, 0xab, 0xb3, 0xb7, 0xbb, + 0xbb, 0xbb, 0xb7, 0xb1, 0xab, 0xa3, 0x8f, 0x71, + 0x00, 0x70, 0x8e, 0xa0, 0xaa, 0xb0, 0xb4, 0xb8, + 0xb8, 0xb8, 0xb4, 0xb0, 0xa8, 0xa0, 0x8c, 0x6e, + 0x00, 0x6f, 0x8d, 0x9f, 0xa9, 0xaf, 0xb5, 0xb7, + 0xb7, 0xb7, 0xb3, 0xaf, 0xa9, 0x9f, 0x8b, 0x6f, + 0x00, 0x6e, 0x8a, 0x9c, 0xa6, 0xac, 0xb2, 0xb4, + 0xb4, 0xb4, 0xb0, 0xac, 0xa6, 0x9a, 0x8a, 0x6c, + 0x00, 0x6d, 0x89, 0x9b, 0xa5, 0xab, 0xb1, 0xb3, + 0xb3, 0xb3, 0xaf, 0xab, 0xa5, 0x99, 0x89, 0x6b, + 0x00, 0x6a, 0x88, 0x98, 0xa4, 0xaa, 0xae, 0xb0, + 0xb0, 0xb0, 0xae, 0xa8, 0xa2, 0x96, 0x86, 0x68, + 0x00, 0x69, 0x87, 0x97, 0xa3, 0xa9, 0xad, 0xaf, + 0xaf, 0xaf, 0xad, 0xa7, 0xa3, 0x95, 0x85, 0x69, + 0x00, 0x66, 0x84, 0x94, 0xa0, 0xa6, 0xaa, 0xac, + 0xac, 0xac, 0xaa, 0xa6, 0x9e, 0x92, 0x84, 0x66, + 0x00, 0x67, 0x83, 0x93, 0x9f, 0xa5, 0xa9, 0xab, + 0xab, 0xab, 0xa9, 0xa5, 0x9d, 0x91, 0x83, 0x65, + 0x00, 0x64, 0x82, 0x8e, 0x9a, 0xa2, 0xa6, 0xa8, + 0xa8, 0xa8, 0xa6, 0xa2, 0x9a, 0x8e, 0x80, 0x62, + 0x00, 0x63, 0x7f, 0x8d, 0x99, 0xa1, 0xa5, 0xa7, + 0xa7, 0xa7, 0xa5, 0xa1, 0x97, 0x8d, 0x7d, 0x61, + 0x00, 0x60, 0x7c, 0x8a, 0x94, 0x9e, 0xa2, 0xa4, + 0xa4, 0xa4, 0xa2, 0x9c, 0x94, 0x8a, 0x7a, 0x5e, + 0x00, 0x5f, 0x79, 0x89, 0x93, 0x9b, 0xa1, 0xa3, + 0xa3, 0xa3, 0xa1, 0x9b, 0x91, 0x87, 0x77, 0x5b, + 0x00, 0x5a, 0x76, 0x86, 0x90, 0x96, 0x9c, 0xa0, + 0xa0, 0xa0, 0x9c, 0x96, 0x8e, 0x84, 0x72, 0x58, + 0x00, 0x59, 0x73, 0x85, 0x8d, 0x95, 0x99, 0x9d, + 0x9d, 0x9d, 0x99, 0x93, 0x8d, 0x83, 0x71, 0x55, + 0x00, 0x54, 0x70, 0x82, 0x8a, 0x90, 0x96, 0x98, + 0x98, 0x98, 0x94, 0x90, 0x88, 0x80, 0x6c, 0x52, + 0x00, 0x51, 0x6d, 0x7f, 0x87, 0x8d, 0x93, 0x95, + 0x95, 0x95, 0x91, 0x8d, 0x87, 0x7b, 0x6b, 0x4f, + 0x00, 0x4e, 0x6a, 0x7a, 0x84, 0x8a, 0x8e, 0x90, + 0x90, 0x90, 0x8c, 0x88, 0x82, 0x76, 0x66, 0x4a, + 0x00, 0x4b, 0x67, 0x75, 0x83, 0x87, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8b, 0x87, 0x7f, 0x73, 0x65, 0x49, + 0x00, 0x48, 0x62, 0x70, 0x7c, 0x82, 0x86, 0x88, + 0x88, 0x88, 0x86, 0x82, 0x78, 0x6e, 0x60, 0x44, + 0x00, 0x45, 0x5f, 0x6d, 0x77, 0x7f, 0x83, 0x85, + 0x85, 0x85, 0x83, 0x7d, 0x75, 0x6b, 0x5b, 0x43, + 0x00, 0x42, 0x58, 0x68, 0x70, 0x78, 0x7c, 0x80, + 0x80, 0x7e, 0x7c, 0x76, 0x6e, 0x64, 0x54, 0x3c, + 0x00, 0x3d, 0x53, 0x63, 0x6b, 0x73, 0x77, 0x79, + 0x79, 0x79, 0x75, 0x6f, 0x69, 0x61, 0x4f, 0x37, + 0x00, 0x34, 0x4c, 0x5c, 0x66, 0x6a, 0x6e, 0x70, + 0x70, 0x70, 0x6c, 0x68, 0x62, 0x56, 0x48, 0x30, + 0x00, 0x2f, 0x47, 0x55, 0x5f, 0x65, 0x67, 0x69, + 0x69, 0x69, 0x65, 0x63, 0x59, 0x4f, 0x43, 0x2b, + 0x00, 0x28, 0x40, 0x4a, 0x52, 0x5a, 0x5e, 0x60, + 0x60, 0x5e, 0x5a, 0x54, 0x4c, 0x44, 0x36, 0x22, + 0x00, 0x23, 0x33, 0x43, 0x49, 0x4d, 0x4f, 0x51, + 0x51, 0x4f, 0x4d, 0x47, 0x43, 0x37, 0x2b, 0x19, + 0x00, 0x16, 0x26, 0x30, 0x36, 0x3c, 0x40, 0x40, + 0x40, 0x3c, 0x38, 0x32, 0x2c, 0x24, 0x1a, 0x0c, + 0x00, 0x0b, 0x15, 0x1b, 0x21, 0x23, 0x23, 0x21, + 0x1f, 0x19, 0x15, 0x0f, 0x09, 0x05, 0x03, 0x00, +}; + +/* End of waveform.h */ diff --git a/sys/arch/arm32/mainbus/wd.c b/sys/arch/arm32/mainbus/wd.c new file mode 100644 index 00000000000..e54e2e2a127 --- /dev/null +++ b/sys/arch/arm32/mainbus/wd.c @@ -0,0 +1,1764 @@ +/* $NetBSD: wd.c,v 1.6 1996/03/28 21:52:52 mark Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. + * + * DMA and multi-sector PIO handling are derived from code contributed by + * Onno van der Linden. + * + * 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 Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> +#include <sys/syslog.h> + +#include <vm/vm.h> + +#include <machine/cpu.h> + +#include <arm32/mainbus/wdreg.h> +#include <arm32/mainbus/mainbus.h> +#include <machine/irqhandler.h> +#include <machine/io.h> +#include <machine/katelib.h> + +extern int wdresethack; + +#define WAITTIME (4 * hz) /* time to wait for a completion */ +#define RECOVERYTIME (hz / 2) /* time to recover from an error */ + +#define WDCDELAY 100 +#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */ +#if 0 +/* If you enable this, it will report any delays more than 100us * N long. */ +#define WDCNDELAY_DEBUG 10 +#endif + +#define WDIORETRIES 5 /* number of retries before giving up */ + +#define WDUNIT(dev) DISKUNIT(dev) +#define WDPART(dev) DISKPART(dev) +#define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) + +#define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) + +struct wd_softc { + struct device sc_dev; + struct disk sc_dk; + + /* Information about the current transfer: */ + 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 transferring */ + int sc_nbytes; /* number of bytes currently transferring */ + + /* Long-term state: */ + int sc_drive; /* physical unit number */ + int sc_state; /* control state */ +#define RECAL 0 /* recalibrate */ +#define RECAL_WAIT 1 /* done recalibrating */ +#define GEOMETRY 2 /* upload geometry */ +#define GEOMETRY_WAIT 3 /* done uploading geometry */ +#define MULTIMODE 4 /* set multiple mode */ +#define MULTIMODE_WAIT 5 /* done setting multiple mode */ +#define OPEN 6 /* done with open */ + int sc_mode; /* transfer mode */ +#define WDM_PIOSINGLE 0 /* single-sector PIO */ +#define WDM_PIOMULTI 1 /* multi-sector PIO */ +#define WDM_DMA 2 /* DMA */ + int sc_multiple; /* multiple for WDM_PIOMULTI */ + int sc_flags; /* drive characteistics found */ +#define WDF_LOCKED 0x01 +#define WDF_WANTED 0x02 +#define WDF_WLABEL 0x04 /* label is writable */ +#define WDF_LABELLING 0x08 /* writing label */ +/* XXX Nothing resets this yet, but disk change sensing will when ATAPI is + implemented. */ +#define WDF_LOADED 0x10 /* parameters loaded */ +#define WDF_32BIT 0x20 /* can do 32-bit transfer */ + + struct wdparams sc_params; /* ESDI/ATA drive parameters */ + daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */ + + TAILQ_ENTRY(wd_softc) sc_drivechain; + struct buf sc_q; +}; + +struct wdc_softc { + struct device sc_dev; + irqhandler_t sc_ih; + + int sc_iobase; /* I/O port base */ + int sc_drq; /* DMA channel */ + + TAILQ_HEAD(drivehead, wd_softc) sc_drives; + int sc_flags; +#define WDCF_ACTIVE 0x01 /* controller is active */ +#define WDCF_SINGLE 0x02 /* sector at a time mode */ +#define WDCF_ERROR 0x04 /* processing a disk error */ +#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */ + int sc_errors; /* errors during current transfer */ + u_char sc_status; /* copy of status register */ + u_char sc_error; /* copy of error register */ +}; + +int wdcprobe __P((struct device *, void *, void *)); +void wdcattach __P((struct device *, struct device *, void *)); + +struct cfattach wdc_ca = { + sizeof(struct wdc_softc), wdcprobe, wdcattach +}; + +struct cfdriver wdc_cd = { + NULL, "wdc", DV_DULL +}; + +int wdprobe __P((struct device *, void *, void *)); +void wdattach __P((struct device *, struct device *, void *)); + +struct cfattach wd_ca = { + sizeof(struct wd_softc), wdprobe, wdattach +}; + +struct cfdriver wd_cd = { + NULL, "wd", DV_DISK +}; + +void wdgetdisklabel __P((struct wd_softc *)); +int wd_get_parms __P((struct wd_softc *)); +void wdstrategy __P((struct buf *)); +void wdstart __P((struct wd_softc *)); + +struct dkdriver wddkdriver = { wdstrategy }; + +void wdfinish __P((struct wd_softc *, struct buf *)); +int wdcintr __P((void *)); +void wdcstart __P((struct wdc_softc *)); +int wdcommand __P((struct wd_softc *, int, int, int, int, int)); +int wdcommandshort __P((struct wdc_softc *, int, int)); +int wdcontrol __P((struct wd_softc *)); +int wdsetctlr __P((struct wd_softc *)); +static void bad144intern __P((struct wd_softc *)); +int wdcreset __P((struct wdc_softc *)); +void wdcrestart __P((void *arg)); +void wdcunwedge __P((struct wdc_softc *)); +void wdctimeout __P((void *arg)); +void wderror __P((void *, struct buf *, char *)); +int wdcwait __P((struct wdc_softc *, int)); +/* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write + command is aborted. */ +#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ) +#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC) +#define wait_for_unbusy(d) wdcwait(d, 0) + +int +wdcprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct wdc_softc *wdc = match; + struct mainbus_attach_args *mb = aux; + int iobase; + + wdc->sc_iobase = iobase = mb->mb_iobase; + + /* Check if we have registers that work. */ + outb(iobase+wd_error, 0x5a); /* Error register not writable, */ + outb(iobase+wd_cyl_lo, 0xa5); /* but all of cyllo are. */ + if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo) != 0xa5) + return 0; + + if (wdcreset(wdc) != 0) { + delay(500000); + if (wdcreset(wdc) != 0) + return 0; + } + + /* Select drive 0. */ + outb(iobase+wd_sdh, WDSD_IBM | 0); + + /* Wait for controller to become ready. */ + if (wait_for_unbusy(wdc) < 0) + return 0; + + /* Start drive diagnostics. */ + outb(iobase+wd_command, WDCC_DIAGNOSE); + + /* Wait for command to complete. */ + if (wait_for_unbusy(wdc) < 0) + return 0; + + mb->mb_iosize = 32; + return 1; +} + +struct wdc_attach_args { + int wa_drive; +}; + +int +wdprint(aux, wdc) + void *aux; + char *wdc; +{ + struct wdc_attach_args *wa = aux; + + if (!wdc) + printf(" drive %d", wa->wa_drive); + return QUIET; +} + +void +wdcattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wdc_softc *wdc = (void *)self; + struct mainbus_attach_args *mb = aux; + struct wdc_attach_args wa; + + TAILQ_INIT(&wdc->sc_drives); + wdc->sc_drq = mb->mb_drq; + + printf("\n"); + + wdc->sc_ih.ih_func = wdcintr; + wdc->sc_ih.ih_arg = wdc; + wdc->sc_ih.ih_level = IPL_BIO; + wdc->sc_ih.ih_name = "wdc"; + if (irq_claim(mb->mb_irq, &wdc->sc_ih)) + panic("Cannot claim IRQ %d for wdc%d\n", mb->mb_irq, parent->dv_unit); + + for (wa.wa_drive = 0; wa.wa_drive < 2; wa.wa_drive++) + (void)config_found(self, (void *)&wa, wdprint); +} + +int +wdprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct wdc_softc *wdc = (void *)parent; + struct cfdata *cf = match; + struct wdc_attach_args *wa = aux; + int drive = wa->wa_drive; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive) + return 0; + + if (wdcommandshort(wdc, drive, WDCC_RECAL) != 0 || + wait_for_ready(wdc) != 0) + return 0; + + return 1; +} + +void +wdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wd_softc *wd = (void *)self; + struct wdc_softc *wdc = (void *)parent; + struct wdc_attach_args *wa = aux; + int i, blank; + char buf[41], c, *p, *q; + + wd->sc_drive = wa->wa_drive; + + /* + * Initialize and attach the disk structure. + */ + wd->sc_dk.dk_driver = &wddkdriver; + wd->sc_dk.dk_name = wd->sc_dev.dv_xname; + disk_attach(&wd->sc_dk); + + wd_get_parms(wd); + for (blank = 0, p = wd->sc_params.wdp_model, q = buf, i = 0; + i < sizeof(wd->sc_params.wdp_model); i++) { + c = *p++; + if (c == '\0') + break; + if (c != ' ') { + if (blank) { + *q++ = ' '; + blank = 0; + } + *q++ = c; + } else + blank = 1; + } + *q++ = '\0'; + + printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec <%s>\n", + wd->sc_params.wdp_cylinders * + (wd->sc_params.wdp_heads * wd->sc_params.wdp_sectors) / + (1048576 / DEV_BSIZE), + wd->sc_params.wdp_cylinders, + wd->sc_params.wdp_heads, + wd->sc_params.wdp_sectors, + DEV_BSIZE, + buf); + + if ((wd->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && + wdc->sc_drq != DRQUNK) { + wd->sc_mode = WDM_DMA; + } else if (wd->sc_params.wdp_maxmulti > 1) { + wd->sc_mode = WDM_PIOMULTI; + wd->sc_multiple = min(wd->sc_params.wdp_maxmulti, 16); + } else { + wd->sc_mode = WDM_PIOSINGLE; + wd->sc_multiple = 1; + } + + printf("%s: using", wd->sc_dev.dv_xname); + if (wd->sc_mode == WDM_DMA) + printf(" dma transfers,"); + else + printf(" %d-sector %d-bit pio transfers,", + wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32); + if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) + printf(" lba addressing\n"); + else + printf(" chs addressing\n"); +} + +/* + * Read/write routine for a buffer. Validates the arguments and schedules the + * transfer. Does not wait for the transfer to complete. + */ +void +wdstrategy(bp) + struct buf *bp; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; + int s; + + /* Valid request? */ + if (bp->b_blkno < 0 || + (bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 || + (bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { + bp->b_error = EINVAL; + goto bad; + } + + /* If device invalidated (e.g. media change, door open), error. */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + bp->b_error = EIO; + goto bad; + } + + /* If it's a null transfer, return immediately. */ + if (bp->b_bcount == 0) + goto done; + + /* + * Do bounds checking, adjust transfer. if error, process. + * If end of partition, just return. + */ + if (WDPART(bp->b_dev) != RAW_PART && + bounds_check_with_label(bp, wd->sc_dk.dk_label, + (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + goto done; + + /* Queue transfer on drive, activate drive and controller if idle. */ + s = splbio(); + disksort(&wd->sc_q, bp); + if (!wd->sc_q.b_active) + wdstart(wd); +#if 0 + else { + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + if ((wdc->sc_flags & (WDCF_ACTIVE|WDCF_ERROR)) == 0) { + printf("wdstrategy: controller inactive\n"); + wdcstart(wdc); + } + } +#endif + splx(s); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + /* Toss transfer; we're done early. */ + bp->b_resid = bp->b_bcount; + biodone(bp); +} + +/* + * Queue a drive for I/O. + */ +void +wdstart(wd) + struct wd_softc *wd; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + int active = wdc->sc_drives.tqh_first != 0; + + /* Link onto controller queue. */ + wd->sc_q.b_active = 1; + TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); + + disk_busy(&wd->sc_dk); + + /* If controller not already active, start it. */ + if (!active) + wdcstart(wdc); +} + +/* + * Finish an I/O operation. Clean up the drive and controller state, set the + * residual count, and inform the upper layers that the operation is complete. + */ +void +wdfinish(wd, bp) + struct wd_softc *wd; + struct buf *bp; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + + wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR); + wdc->sc_errors = 0; + /* + * Move this drive to the end of the queue to give others a `fair' + * chance. + */ + if (wd->sc_drivechain.tqe_next) { + TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); + if (bp->b_actf) { + TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); + } else + wd->sc_q.b_active = 0; + } + bp->b_resid = wd->sc_bcount; + wd->sc_skip = 0; + wd->sc_q.b_actf = bp->b_actf; + + disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); + + if (!wd->sc_q.b_actf) { + TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); + wd->sc_q.b_active = 0; + } else + disk_busy(&wd->sc_dk); + + biodone(bp); +} + +int +wdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +wdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +/* + * Start I/O on a controller. This does the calculation, and starts a read or + * write operation. Called to from wdstart() to start a transfer, from + * wdcintr() to continue a multi-sector transfer or start the next transfer, or + * wdcrestart() after recovering from an error. + */ +void +wdcstart(wdc) + struct wdc_softc *wdc; +{ + struct wd_softc *wd; + struct buf *bp; + struct disklabel *lp; + int nblks; + +#ifdef DIAGNOSTIC + if ((wdc->sc_flags & WDCF_ACTIVE) != 0) + panic("wdcstart: controller still active"); +#endif + + /* + * XXX + * This is a kluge. See comments in wd_get_parms(). + */ + if ((wdc->sc_flags & WDCF_WANTED) != 0) { + wdc->sc_flags &= ~WDCF_WANTED; + wakeup(wdc); + return; + } + +loop: + /* Is there a drive for the controller to do a transfer with? */ + wd = wdc->sc_drives.tqh_first; + if (wd == NULL) + return; + + /* Is there a transfer to this drive? If not, deactivate drive. */ + bp = wd->sc_q.b_actf; + + if (wdc->sc_errors >= WDIORETRIES) { + wderror(wd, bp, "hard error"); + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + wdfinish(wd, bp); + goto loop; + } + + /* Do control operations specially. */ + if (wd->sc_state < OPEN) { + /* + * Actually, we want to be careful not to mess with the control + * state if the device is currently busy, but we can assume + * that we never get to this point if that's the case. + */ + if (wdcontrol(wd) == 0) { + /* The drive is busy. Wait. */ + return; + } + } + + /* + * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is + * encountered. If we are in multi-sector mode, then we switch to + * single-sector mode and retry the operation from the start. + */ + if (wdc->sc_flags & WDCF_ERROR) { + wdc->sc_flags &= ~WDCF_ERROR; + if ((wdc->sc_flags & WDCF_SINGLE) == 0) { + wdc->sc_flags |= WDCF_SINGLE; + wd->sc_skip = 0; + } + } + + lp = wd->sc_dk.dk_label; + + /* When starting a transfer... */ + if (wd->sc_skip == 0) { + int part = WDPART(bp->b_dev); + daddr_t blkno; + +#ifdef WDDEBUG + printf("\n%s: wdcstart %s %d@%d; map ", wd->sc_dev.dv_xname, + (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, + bp->b_blkno); +#endif + wd->sc_bcount = bp->b_bcount; + blkno = bp->b_blkno; + if (part != RAW_PART) + blkno += lp->d_partitions[part].p_offset; + wd->sc_blkno = blkno / (lp->d_secsize / DEV_BSIZE); + } else { +#ifdef WDDEBUG + printf(" %d)%x", wd->sc_skip, inb(wd->sc_iobase+wd_altsts)); +#endif + } + + /* When starting a multi-sector transfer, or doing single-sector + transfers... */ + if (wd->sc_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 || + wd->sc_mode == WDM_DMA) { + daddr_t blkno = wd->sc_blkno; + long cylin, head, sector; + int command; + + if ((wdc->sc_flags & WDCF_SINGLE) != 0) + nblks = 1; + else if (wd->sc_mode != WDM_DMA) + nblks = wd->sc_bcount / lp->d_secsize; + else + nblks = min(wd->sc_bcount / lp->d_secsize, 8); + + /* Check for bad sectors and adjust transfer, if necessary. */ + if ((lp->d_flags & D_BADSECT) != 0 +#ifdef B_FORMAT + && (bp->b_flags & B_FORMAT) == 0 +#endif + ) { + long blkdiff; + int i; + + for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { + blkdiff -= blkno; + if (blkdiff < 0) + continue; + if (blkdiff == 0) { + /* Replace current block of transfer. */ + blkno = + lp->d_secperunit - lp->d_nsectors - i - 1; + } + if (blkdiff < nblks) { + /* Bad block inside transfer. */ + wdc->sc_flags |= WDCF_SINGLE; + nblks = 1; + } + break; + } + /* Tranfer is okay now. */ + } + + if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { + sector = (blkno >> 0) & 0xff; + cylin = (blkno >> 8) & 0xffff; + head = (blkno >> 24) & 0xf; + head |= WDSD_LBA; + } else { + sector = blkno % lp->d_nsectors; + sector++; /* Sectors begin with 1, not 0. */ + blkno /= lp->d_nsectors; + head = blkno % lp->d_ntracks; + blkno /= lp->d_ntracks; + cylin = blkno; + head |= WDSD_CHS; + } + + if (wd->sc_mode == WDM_PIOSINGLE || + (wdc->sc_flags & WDCF_SINGLE) != 0) + wd->sc_nblks = 1; + else if (wd->sc_mode == WDM_PIOMULTI) + wd->sc_nblks = min(nblks, wd->sc_multiple); + else + wd->sc_nblks = nblks; + wd->sc_nbytes = wd->sc_nblks * lp->d_secsize; + +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) { + sector = lp->d_gap3; + nblks = lp->d_nsectors; + command = WDCC_FORMAT; + } else +#endif + switch (wd->sc_mode) { + case WDM_DMA: + command = (bp->b_flags & B_READ) ? + WDCC_READDMA : WDCC_WRITEDMA; + /* Start the DMA channel and bounce the buffer if + necessary. */ +/* isa_dmastart(bp->b_flags & B_READ, + bp->b_data + wd->sc_skip, + wd->sc_nbytes, wdc->sc_drq);*/ + panic("wd cannot do DMA yet\n"); + break; + case WDM_PIOMULTI: + command = (bp->b_flags & B_READ) ? + WDCC_READMULTI : WDCC_WRITEMULTI; + break; + case WDM_PIOSINGLE: + command = (bp->b_flags & B_READ) ? + WDCC_READ : WDCC_WRITE; + break; + } + + /* Initiate command! */ + if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) { + wderror(wd, NULL, + "wdcstart: timeout waiting for unbusy"); + wdcunwedge(wdc); + return; + } + +#ifdef WDDEBUG + printf("sector %d cylin %d head %d addr %x sts %x\n", sector, + cylin, head, bp->b_data, 0/*inb(wd->sc_iobase+wd_altsts)*/); +#endif + } else if (wd->sc_nblks > 1) { + /* The number of blocks in the last stretch may be smaller. */ + nblks = wd->sc_bcount / lp->d_secsize; + if (wd->sc_nblks > nblks) { + wd->sc_nblks = nblks; + wd->sc_nbytes = wd->sc_bcount; + } + } + + /* If this was a write and not using DMA, push the data. */ + if (wd->sc_mode != WDM_DMA && + (bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) { + if (wait_for_drq(wdc) < 0) { + wderror(wd, NULL, "wdcstart: timeout waiting for drq"); + wdcunwedge(wdc); + return; + } + + /* Push out data. */ + if ((wd->sc_flags & WDF_32BIT) == 0) + outsw(wdc->sc_iobase+wd_data, (u_int) bp->b_data + wd->sc_skip, + wd->sc_nbytes >> 1); + else + panic("wd cannot do 32 bit transfers\n"); +/* outsl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, + wd->sc_nbytes >> 2);*/ + } + + wdc->sc_flags |= WDCF_ACTIVE; + timeout(wdctimeout, wdc, WAITTIME); +} + +/* + * Interrupt routine for the controller. Acknowledge the interrupt, check for + * errors on the current operation, mark it done if necessary, and start the + * next request. Also check for a partially done transfer, and continue with + * the next chunk if so. + */ +int +wdcintr(arg) + void *arg; +{ + struct wdc_softc *wdc = arg; + struct wd_softc *wd; + struct buf *bp; + + if ((wdc->sc_flags & WDCF_ACTIVE) == 0) { + /* Clear the pending interrupt and abort. */ + (void) inb(wdc->sc_iobase+wd_status); + return 0; + } + + wdc->sc_flags &= ~WDCF_ACTIVE; + untimeout(wdctimeout, wdc); + + wd = wdc->sc_drives.tqh_first; + bp = wd->sc_q.b_actf; + +#ifdef WDDEBUG + printf("I%d ", ctrlr); +#endif + + if (wait_for_unbusy(wdc) < 0) { + wderror(wd, NULL, "wdcintr: timeout waiting for unbusy"); + wdc->sc_status |= WDCS_ERR; /* XXX */ + } + + /* Is it not a transfer, but a control operation? */ + if (wd->sc_state < OPEN) { + if (wdcontrol(wd) == 0) { + /* The drive is busy. Wait. */ + return 1; + } + wdcstart(wdc); + return 1; + } + + /* Turn off the DMA channel and unbounce the buffer. */ + if (wd->sc_mode == WDM_DMA) + panic("wd cannot do DMA\n"); +/* isa_dmadone(bp->b_flags & B_READ, bp->b_data + wd->sc_skip, + wd->sc_nbytes, wdc->sc_drq);*/ + + /* Have we an error? */ + if (wdc->sc_status & WDCS_ERR) { + lose: +#ifdef WDDEBUG + wderror(wd, NULL, "wdcintr"); +#endif + if ((wdc->sc_flags & WDCF_SINGLE) == 0) { + wdc->sc_flags |= WDCF_ERROR; + goto restart; + } + +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) + goto bad; +#endif + + if (++wdc->sc_errors < WDIORETRIES) + goto restart; + wderror(wd, bp, "hard error"); + + bad: + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + goto done; + } + + /* If this was a read and not using DMA, fetch the data. */ + if (wd->sc_mode != WDM_DMA && + (bp->b_flags & (B_READ|B_WRITE)) == B_READ) { + if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) + != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) { + wderror(wd, NULL, "wdcintr: read intr before drq"); + wdcunwedge(wdc); + return 1; + } + + /* Pull in data. */ + if ((wd->sc_flags & WDF_32BIT) == 0) + insw(wdc->sc_iobase+wd_data, (u_int) bp->b_data + wd->sc_skip, + wd->sc_nbytes >> 1); + else + panic("wd cannot do 32 bit transfers\n"); +/* insl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, + wd->sc_nbytes >> 2);*/ + } + + /* If we encountered any abnormalities, flag it as a soft error. */ + if (wdc->sc_errors > 0 || + (wdc->sc_status & WDCS_CORR) != 0) { + wderror(wd, bp, "soft error (corrected)"); + wdc->sc_errors = 0; + } + + /* Adjust pointers for the next block, if any. */ + wd->sc_blkno += wd->sc_nblks; + wd->sc_skip += wd->sc_nbytes; + wd->sc_bcount -= wd->sc_nbytes; + + /* See if this transfer is complete. */ + if (wd->sc_bcount > 0) + goto restart; + +done: + /* Done with this transfer, with or without error. */ + wdfinish(wd, bp); + +restart: + /* Start the next operation, if any. */ + wdcstart(wdc); + + return 1; +} + +/* + * Wait interruptibly for an exclusive lock. + * + * XXX + * Several drivers do this; it should be abstracted and made MP-safe. + */ +int +wdlock(wd) + struct wd_softc *wd; +{ + int error; + + while ((wd->sc_flags & WDF_LOCKED) != 0) { + wd->sc_flags |= WDF_WANTED; + if ((error = tsleep(wd, PRIBIO | PCATCH, "wdlck", 0)) != 0) + return error; + } + wd->sc_flags |= WDF_LOCKED; + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +void +wdunlock(wd) + struct wd_softc *wd; +{ + + wd->sc_flags &= ~WDF_LOCKED; + if ((wd->sc_flags & WDF_WANTED) != 0) { + wd->sc_flags &= ~WDF_WANTED; + wakeup(wd); + } +} + +int +wdopen(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct wd_softc *wd; + int unit, part; + int error; + + unit = WDUNIT(dev); + if (unit >= wd_cd.cd_ndevs) + return ENXIO; + wd = wd_cd.cd_devs[unit]; + if (wd == 0) + return ENXIO; + + if (error = wdlock(wd)) + return error; + + if (wd->sc_dk.dk_openmask != 0) { + /* + * If any partition is open, but the disk has been invalidated, + * disallow further opens. + */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + error = EIO; + goto bad3; + } + } else { + if ((wd->sc_flags & WDF_LOADED) == 0) { + wd->sc_flags |= WDF_LOADED; + + /* Load the physical device parameters. */ + if (wd_get_parms(wd) != 0) { + error = ENXIO; + goto bad2; + } + + /* Load the partition info if not already loaded. */ + wdgetdisklabel(wd); + } + } + + part = WDPART(dev); + + /* Check that the partition exists. */ + if (part != RAW_PART && + (part >= wd->sc_dk.dk_label->d_npartitions || + wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { + error = ENXIO; + goto bad; + } + + /* Insure only one open at a time. */ + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask |= (1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask |= (1 << part); + break; + } + wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + wdunlock(wd); + return 0; + +bad2: + wd->sc_flags &= ~WDF_LOADED; + +bad: + if (wd->sc_dk.dk_openmask == 0) { + } + +bad3: + wdunlock(wd); + return error; +} + +int +wdclose(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + int part = WDPART(dev); + int error; + + if (error = wdlock(wd)) + return error; + + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask &= ~(1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask &= ~(1 << part); + break; + } + wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + if (wd->sc_dk.dk_openmask == 0) { + /* XXXX Must wait for I/O to complete! */ + } + + wdunlock(wd); + return 0; +} + +/* + * Fabricate a default disk label, and try to read the correct one. + */ +void +wdgetdisklabel(wd) + struct wd_softc *wd; +{ + struct disklabel *lp = wd->sc_dk.dk_label; + char *errstring; + + bzero(lp, sizeof(struct disklabel)); + bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); + + lp->d_secsize = DEV_BSIZE; + lp->d_ntracks = wd->sc_params.wdp_heads; + lp->d_nsectors = wd->sc_params.wdp_sectors; + lp->d_ncylinders = wd->sc_params.wdp_cylinders; + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + +#if 0 + strncpy(lp->d_typename, "ST506 disk", 16); + lp->d_type = DTYPE_ST506; +#endif + strncpy(lp->d_packname, wd->sc_params.wdp_model, 16); + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + lp->d_rpm = 3600; + lp->d_interleave = 1; + lp->d_flags = 0; + + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; + lp->d_npartitions = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); + + wd->sc_badsect[0] = -1; + + if (wd->sc_state > RECAL) + wd->sc_state = RECAL; + errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), + wdstrategy, lp, wd->sc_dk.dk_cpulabel); + if (errstring) { + /* + * This probably happened because the drive's default + * geometry doesn't match the DOS geometry. We + * assume the DOS geometry is now in the label and try + * again. XXX This is a kluge. + */ + if (wd->sc_state > GEOMETRY) + wd->sc_state = GEOMETRY; + errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), + wdstrategy, lp, wd->sc_dk.dk_cpulabel); + } + if (errstring) { + printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); + return; + } + + if (wd->sc_state > GEOMETRY) + wd->sc_state = GEOMETRY; + if ((lp->d_flags & D_BADSECT) != 0) + bad144intern(wd); +} + +/* + * Implement operations needed before read/write. + * Returns 0 if operation still in progress, 1 if completed. + */ +int +wdcontrol(wd) + struct wd_softc *wd; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + + switch (wd->sc_state) { + case RECAL: /* Set SDH, step rate, do recal. */ + if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) { + wderror(wd, NULL, "wdcontrol: recal failed (1)"); + goto bad; + } + wd->sc_state = RECAL_WAIT; + break; + + case RECAL_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(wd, NULL, "wdcontrol: recal failed (2)"); + goto bad; + } + /* fall through */ + case GEOMETRY: + if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) + goto multimode; + if (wdsetctlr(wd) != 0) { + /* Already printed a message. */ + goto bad; + } + wd->sc_state = GEOMETRY_WAIT; + break; + + case GEOMETRY_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(wd, NULL, "wdcontrol: geometry failed"); + goto bad; + } + /* fall through */ + case MULTIMODE: + multimode: + if (wd->sc_mode != WDM_PIOMULTI) + goto open; + outb(wdc->sc_iobase+wd_seccnt, wd->sc_multiple); + if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) { + wderror(wd, NULL, "wdcontrol: setmulti failed (1)"); + goto bad; + } + wd->sc_state = MULTIMODE_WAIT; + break; + + case MULTIMODE_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(wd, NULL, "wdcontrol: setmulti failed (2)"); + goto bad; + } + /* fall through */ + case OPEN: + open: + wdc->sc_errors = 0; + wd->sc_state = OPEN; + /* + * The rest of the initialization can be done by normal means. + */ + return 1; + + bad: + wdcunwedge(wdc); + return 0; + } + + wdc->sc_flags |= WDCF_ACTIVE; + timeout(wdctimeout, wdc, WAITTIME); + return 0; +} + +/* + * Wait for the drive to become ready and send a command. + * Return -1 if busy for too long or 0 otherwise. + * Assumes interrupts are blocked. + */ +int +wdcommand(wd, command, cylin, head, sector, count) + struct wd_softc *wd; + int command; + int cylin, head, sector, count; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + int iobase = wdc->sc_iobase; + int stat; + + /* Select drive, head, and addressing mode. */ + outb(iobase+wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head); + + /* Wait for it to become ready to accept a command. */ + if (command == WDCC_IDP) + stat = wait_for_unbusy(wdc); + else + stat = wdcwait(wdc, WDCS_DRDY); + if (stat < 0) + return -1; + + /* Load parameters. */ + if (wd->sc_dk.dk_label->d_type == DTYPE_ST506) + outb(iobase+wd_precomp, wd->sc_dk.dk_label->d_precompcyl / 4); + else + outb(iobase+wd_features, 0); + outb(iobase+wd_cyl_lo, cylin); + outb(iobase+wd_cyl_hi, cylin >> 8); + outb(iobase+wd_sector, sector); + outb(iobase+wd_seccnt, count); + + /* Send command. */ + outb(iobase+wd_command, command); + + return 0; +} + +/* + * Simplified version of wdcommand(). + */ +int +wdcommandshort(wdc, drive, command) + struct wdc_softc *wdc; + int drive; + int command; +{ + int iobase = wdc->sc_iobase; + + /* Select drive. */ + outb(iobase+wd_sdh, WDSD_IBM | (drive << 4)); + + if (wdcwait(wdc, WDCS_DRDY) < 0) + return -1; + + outb(iobase+wd_command, command); + + return 0; +} + +/* + * Tell the drive what geometry to use. + */ +int +wdsetctlr(wd) + struct wd_softc *wd; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + +#ifdef WDDEBUG + printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive, + wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks, + wd->sc_dk.dk_label->d_nsectors); +#endif + + if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label->d_ncylinders, + wd->sc_dk.dk_label->d_ntracks - 1, 0, + wd->sc_dk.dk_label->d_nsectors) != 0) { + wderror(wd, NULL, "wdsetctlr: geometry upload failed"); + return -1; + } + + return 0; +} + +/* + * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506. + */ +int +wd_get_parms(wd) + struct wd_softc *wd; +{ + struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + int i; + char tb[DEV_BSIZE]; + int s, error; + + /* + * XXX + * The locking done here, and the length of time this may keep the rest + * of the system suspended, is a kluge. This should be rewritten to + * set up a transfer and queue it through wdstart(), but it's called + * infrequently enough that this isn't a pressing matter. + */ + + s = splbio(); + + while ((wdc->sc_flags & WDCF_ACTIVE) != 0) { + wdc->sc_flags |= WDCF_WANTED; + if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) { + splx(s); + return error; + } + } + + if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 || + wait_for_drq(wdc) != 0) { + /* + * We `know' there's a drive here; just assume it's old. + * This geometry is only used to read the MBR and print a + * (false) attach message. + */ + strncpy(wd->sc_dk.dk_label->d_typename, "ST506", + sizeof wd->sc_dk.dk_label->d_typename); + wd->sc_dk.dk_label->d_type = DTYPE_ST506; + + strncpy(wd->sc_params.wdp_model, "unknown", + sizeof wd->sc_params.wdp_model); + wd->sc_params.wdp_config = WD_CFG_FIXED; + wd->sc_params.wdp_cylinders = 1024; + wd->sc_params.wdp_heads = 8; + wd->sc_params.wdp_sectors = 17; + wd->sc_params.wdp_maxmulti = 0; + wd->sc_params.wdp_usedmovsd = 0; + wd->sc_params.wdp_capabilities = 0; + } else { + strncpy(wd->sc_dk.dk_label->d_typename, "ESDI/IDE", + sizeof wd->sc_dk.dk_label->d_typename); + wd->sc_dk.dk_label->d_type = DTYPE_ESDI; + + /* Read in parameter block. */ + insw(wdc->sc_iobase+wd_data, (u_int)tb, sizeof(tb) / sizeof(short)); + bcopy(tb, &wd->sc_params, sizeof(struct wdparams)); + + /* Shuffle string byte order. */ + for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) { + u_short *p; + p = (u_short *)(wd->sc_params.wdp_model + i); + *p = ntohs(*p); + } + } + + /* Clear any leftover interrupt. */ + (void) inb(wdc->sc_iobase+wd_status); + + /* Restart the queue. */ + wdcstart(wdc); + + splx(s); + return 0; +} + +int +wdioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + int error; + + if ((wd->sc_flags & WDF_LOADED) == 0) + return EIO; + + switch (cmd) { + case DIOCSBAD: + if ((flag & FWRITE) == 0) + return EBADF; + wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; + wd->sc_dk.dk_label->d_flags |= D_BADSECT; + bad144intern(wd); + return 0; + + case DIOCGDINFO: + *(struct disklabel *)addr = *(wd->sc_dk.dk_label); + return 0; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; + ((struct partinfo *)addr)->part = + &wd->sc_dk.dk_label->d_partitions[WDPART(dev)]; + return 0; + + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + return EBADF; + + if (error = wdlock(wd)) + return error; + wd->sc_flags |= WDF_LABELLING; + + error = setdisklabel(wd->sc_dk.dk_label, + (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0, + wd->sc_dk.dk_cpulabel); + if (error == 0) { + if (wd->sc_state > GEOMETRY) + wd->sc_state = GEOMETRY; + if (cmd == DIOCWDINFO) + error = writedisklabel(WDLABELDEV(dev), + wdstrategy, wd->sc_dk.dk_label, + wd->sc_dk.dk_cpulabel); + } + + wd->sc_flags &= ~WDF_LABELLING; + wdunlock(wd); + return error; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + if (*(int *)addr) + wd->sc_flags |= WDF_WLABEL; + else + wd->sc_flags &= ~WDF_WLABEL; + return 0; + +#ifdef notyet + case DIOCWFORMAT: + if ((flag & FWRITE) == 0) + return EBADF; + { + register struct format_op *fop; + struct iovec aiov; + struct uio auio; + + fop = (struct format_op *)addr; + aiov.iov_base = fop->df_buf; + aiov.iov_len = fop->df_count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = fop->df_count; + auio.uio_segflg = 0; + auio.uio_offset = + fop->df_startblk * wd->sc_dk.dk_label->d_secsize; + auio.uio_procp = p; + error = physio(wdformat, NULL, dev, B_WRITE, minphys, + &auio); + fop->df_count -= auio.uio_resid; + fop->df_reg[0] = wdc->sc_status; + fop->df_reg[1] = wdc->sc_error; + return error; + } +#endif + + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("wdioctl: impossible"); +#endif +} + +#ifdef B_FORMAT +int +wdformat(struct buf *bp) +{ + + bp->b_flags |= B_FORMAT; + return wdstrategy(bp); +} +#endif + +int +wdsize(dev) + dev_t dev; +{ + struct wd_softc *wd; + int part; + int size; + + if (wdopen(dev, 0, S_IFBLK) != 0) + return -1; + wd = wd_cd.cd_devs[WDUNIT(dev)]; + part = WDPART(dev); + if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) + size = -1; + else + size = wd->sc_dk.dk_label->d_partitions[part].p_size; + if (wdclose(dev, 0, S_IFBLK) != 0) + return -1; + return size; +} + + +#ifndef __BDEVSW_DUMP_OLD_TYPE +/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ +static int wddoingadump; +static int wddumprecalibrated; + +/* + * Dump core after a system crash. + */ +int +wddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + struct wd_softc *wd; /* disk unit to do the I/O */ + struct wdc_softc *wdc; /* disk controller to do the I/O */ + struct disklabel *lp; /* disk's disklabel */ + int unit, part; + int nblks; /* total number of sectors left to write */ + + /* Check if recursive dump; if so, punt. */ + if (wddoingadump) + return EFAULT; + wddoingadump = 1; + + unit = WDUNIT(dev); + if (unit >= wd_cd.cd_ndevs) + return ENXIO; + wd = wd_cd.cd_devs[unit]; + if (wd == 0) + return ENXIO; + + part = WDPART(dev); + + /* Make sure it was initialized. */ + if (wd->sc_state < OPEN) + return ENXIO; + + wdc = (void *)wd->sc_dev.dv_parent; + + /* Convert to disk sectors. Request must be a multiple of size. */ + lp = wd->sc_dk.dk_label; + if ((size % lp->d_secsize) != 0) + return EFAULT; + nblks = size / lp->d_secsize; + blkno = blkno / (lp->d_secsize / DEV_BSIZE); + + /* Check transfer bounds against partition size. */ + if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size)) + return EINVAL; + + /* Offset block number to start of partition. */ + blkno += lp->d_partitions[part].p_offset; + + /* Recalibrate, if first dump transfer. */ + if (wddumprecalibrated == 0) { + wddumprecalibrated = 1; + if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 || + wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 || + wait_for_ready(wdc) != 0) { + wderror(wd, NULL, "wddump: recal failed"); + return EIO; + } + } + + while (nblks > 0) { + daddr_t xlt_blkno = blkno; + long cylin, head, sector; + + if ((lp->d_flags & D_BADSECT) != 0) { + long blkdiff; + int i; + + for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { + blkdiff -= xlt_blkno; + if (blkdiff < 0) + continue; + if (blkdiff == 0) { + /* Replace current block of transfer. */ + xlt_blkno = lp->d_secperunit - + lp->d_nsectors - i - 1; + } + break; + } + /* Tranfer is okay now. */ + } + + if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { + sector = (xlt_blkno >> 0) & 0xff; + cylin = (xlt_blkno >> 8) & 0xffff; + head = (xlt_blkno >> 24) & 0xf; + head |= WDSD_LBA; + } else { + sector = xlt_blkno % lp->d_nsectors; + sector++; /* Sectors begin with 1, not 0. */ + xlt_blkno /= lp->d_nsectors; + head = xlt_blkno % lp->d_ntracks; + xlt_blkno /= lp->d_ntracks; + cylin = xlt_blkno; + head |= WDSD_CHS; + } + +#ifndef WD_DUMP_NOT_TRUSTED + if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 || + wait_for_drq(wdc) != 0) { + wderror(wd, NULL, "wddump: write failed"); + return EIO; + } + + outsw(wdc->sc_iobase+wd_data, (u_int)va, lp->d_secsize >> 1); + + /* Check data request (should be done). */ + if (wait_for_ready(wdc) != 0) { + wderror(wd, NULL, "wddump: timeout waiting for ready"); + return EIO; + } +#else /* WD_DUMP_NOT_TRUSTED */ + /* Let's just talk about this first... */ + printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", + unit, va, cylin, head, sector); + delay(500 * 1000); /* half a second */ +#endif + + /* update block count */ + nblks -= 1; + blkno += 1; + va += lp->d_secsize; + } + + wddoingadump = 0; + return 0; +} +#else /* __BDEVSW_DUMP_NEW_TYPE */ +int +wddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} +#endif /* __BDEVSW_DUMP_NEW_TYPE */ + +/* + * Internalize the bad sector table. + */ +void +bad144intern(wd) + struct wd_softc *wd; +{ + struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; + struct disklabel *lp = wd->sc_dk.dk_label; + int i = 0; + + for (; i < 126; i++) { + if (bt->bt_bad[i].bt_cyl == 0xffff) + break; + wd->sc_badsect[i] = + bt->bt_bad[i].bt_cyl * lp->d_secpercyl + + (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + + (bt->bt_bad[i].bt_trksec & 0xff); + } + for (; i < 127; i++) + wd->sc_badsect[i] = -1; +} + +int +wdcreset(wdc) + struct wdc_softc *wdc; +{ + int iobase = wdc->sc_iobase; + + /* Reset the device. */ + if (wdresethack) { + outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS); + delay(1000); + outb(iobase+wd_ctlr, WDCTL_IDS); + delay(1000); + (void) inb(iobase+wd_error); + outb(iobase+wd_ctlr, WDCTL_4BIT); + } + + if (wait_for_unbusy(wdc) < 0) { + printf("%s: reset failed\n", wdc->sc_dev.dv_xname); + return 1; + } + + return 0; +} + +void +wdcrestart(arg) + void *arg; +{ + struct wdc_softc *wdc = arg; + int s; + + s = splbio(); + wdcstart(wdc); + splx(s); +} + +/* + * Unwedge the controller after an unexpected error. We do this by resetting + * it, marking all drives for recalibration, and stalling the queue for a short + * period to give the reset time to finish. + * NOTE: We use a timeout here, so this routine must not be called during + * autoconfig or dump. + */ +void +wdcunwedge(wdc) + struct wdc_softc *wdc; +{ + int unit; + + untimeout(wdctimeout, wdc); + (void) wdcreset(wdc); + + /* Schedule recalibrate for all drives on this controller. */ + for (unit = 0; unit < wd_cd.cd_ndevs; unit++) { + struct wd_softc *wd = wd_cd.cd_devs[unit]; + if (!wd || (void *)wd->sc_dev.dv_parent != wdc) + continue; + if (wd->sc_state > RECAL) + wd->sc_state = RECAL; + } + + wdc->sc_flags |= WDCF_ERROR; + ++wdc->sc_errors; + + /* Wake up in a little bit and restart the operation. */ + timeout(wdcrestart, wdc, RECOVERYTIME); +} + +int +wdcwait(wdc, mask) + struct wdc_softc *wdc; + int mask; +{ + int iobase = wdc->sc_iobase; + int timeout = 0; + u_char status; + extern int cold; + + for (;;) { + wdc->sc_status = status = inb(iobase+wd_status); + if ((status & WDCS_BSY) == 0 && (status & mask) == mask) + break; + if (++timeout > WDCNDELAY) + return -1; + delay(WDCDELAY); + } + if (status & WDCS_ERR) { + wdc->sc_error = inb(iobase+wd_error); + return WDCS_ERR; + } +#ifdef WDCNDELAY_DEBUG + /* After autoconfig, there should be no long delays. */ + if (!cold && timeout > WDCNDELAY_DEBUG) + printf("%s: warning: busy-wait took %dus\n", + wdc->sc_dev.dv_xname, WDCDELAY * timeout); +#endif + return 0; +} + +void +wdctimeout(arg) + void *arg; +{ + struct wdc_softc *wdc = (struct wdc_softc *)arg; + int s; + + s = splbio(); + if ((wdc->sc_flags & WDCF_ACTIVE) != 0) { + struct wd_softc *wd = wdc->sc_drives.tqh_first; + struct buf *bp = wd->sc_q.b_actf; + + wdc->sc_flags &= ~WDCF_ACTIVE; + wderror(wdc, NULL, "lost interrupt"); + printf("%s: lost interrupt: %sing %d@%s:%d\n", + wdc->sc_dev.dv_xname, + (bp->b_flags & B_READ) ? "read" : "writ", + wd->sc_nblks, wd->sc_dev.dv_xname, wd->sc_blkno); + wdcunwedge(wdc); + } else + wderror(wdc, NULL, "missing untimeout"); + splx(s); +} + +void +wderror(dev, bp, msg) + void *dev; + struct buf *bp; + char *msg; +{ + struct wd_softc *wd = dev; + struct wdc_softc *wdc = dev; + + if (bp) { + diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip / DEV_BSIZE, + wd->sc_dk.dk_label); + printf("\n"); + } else + printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname, + msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS); +} diff --git a/sys/arch/arm32/mainbus/wdreg.h b/sys/arch/arm32/mainbus/wdreg.h new file mode 100644 index 00000000000..12311b76e60 --- /dev/null +++ b/sys/arch/arm32/mainbus/wdreg.h @@ -0,0 +1,161 @@ +/* $NetBSD: wdreg.h,v 1.1 1996/01/31 23:25:18 mark Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * 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. + * + * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 + */ + +/* + * Disk Controller register definitions. + */ +#define wd_data 0x000 /* data register (R/W - 16 bits) */ +#define wd_error 0x004 /* error register (R) */ +#define wd_precomp 0x004 /* write precompensation (W) */ +#define wd_features 0x004 /* features (W) */ +#define wd_seccnt 0x008 /* sector count (R/W) */ +#define wd_sector 0x00c /* first sector number (R/W) */ +#define wd_cyl_lo 0x010 /* cylinder address, low byte (R/W) */ +#define wd_cyl_hi 0x014 /* cylinder address, high byte (R/W) */ +#define wd_sdh 0x018 /* sector size/drive/head (R/W) */ +#define wd_command 0x01c /* command register (W) */ +#define wd_status 0x01c /* immediate status (R) */ + +#define wd_altsts 0x818 /* alternate fixed disk status (via 1015) (R) */ +#define wd_ctlr 0x818 /* fixed disk controller control (via 1015) (W) */ +#define WDCTL_4BIT 0x08 /* use four head bits (wd1003) */ +#define WDCTL_RST 0x04 /* reset the controller */ +#define WDCTL_IDS 0x02 /* disable controller interrupts */ +#define wd_digin 0x81c /* disk controller input (via 1015) (R) */ + +/* + * Status bits. + */ +#define WDCS_BSY 0x80 /* busy */ +#define WDCS_DRDY 0x40 /* drive ready */ +#define WDCS_DWF 0x20 /* drive write fault */ +#define WDCS_DSC 0x10 /* drive seek complete */ +#define WDCS_DRQ 0x08 /* data request */ +#define WDCS_CORR 0x04 /* corrected data */ +#define WDCS_IDX 0x02 /* index */ +#define WDCS_ERR 0x01 /* error */ +#define WDCS_BITS "\020\010bsy\007drdy\006dwf\005dsc\004drq\003corr\002idx\001err" + +/* + * Error bits. + */ +#define WDCE_BBK 0x80 /* bad block detected */ +#define WDCE_UNC 0x40 /* uncorrectable data error */ +#define WDCE_MC 0x20 /* media changed */ +#define WDCE_IDNF 0x10 /* id not found */ +#define WDCE_ABRT 0x08 /* aborted command */ +#define WDCE_MCR 0x04 /* media change requested */ +#define WDCE_TK0NF 0x02 /* track 0 not found */ +#define WDCE_AMNF 0x01 /* address mark not found */ +#define WDERR_BITS "\020\010bbk\007unc\006mc\005idnf\004mcr\003abrt\002tk0nf\001amnf" + +/* + * Commands for Disk Controller. + */ +#define WDCC_RECAL 0x10 /* disk restore code -- resets cntlr */ + +#define WDCC_READ 0x20 /* disk read code */ +#define WDCC_WRITE 0x30 /* disk write code */ +#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */ +#define WDCC__NORETRY 0x01 /* modifier -- no retrys */ + +#define WDCC_FORMAT 0x50 /* disk format code */ +#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */ +#define WDCC_IDP 0x91 /* initialize drive parameters */ + +#define WDCC_READMULTI 0xc4 /* read multiple */ +#define WDCC_WRITEMULTI 0xc5 /* write multiple */ +#define WDCC_SETMULTI 0xc6 /* set multiple mode */ + +#define WDCC_READDMA 0xc8 /* read with DMA */ +#define WDCC_WRITEDMA 0xca /* write with DMA */ + +#define WDCC_ACKMC 0xdb /* acknowledge media change */ +#define WDCC_LOCK 0xde /* lock drawer */ +#define WDCC_UNLOCK 0xdf /* unlock drawer */ + +#define WDCC_IDENTIFY 0xec /* read parameters from controller */ +#define WDCC_CACHEC 0xef /* cache control */ + +#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */ +#define WDSD_CHS 0x00 /* cylinder/head/sector addressing */ +#define WDSD_LBA 0x40 /* logical block addressing */ + + +#ifdef _KERNEL +/* + * read parameters command returns this: + */ +struct wdparams { + /* drive info */ + short wdp_config; /* general configuration */ +#define WD_CFG_REMOVABLE 0x0080 +#define WD_CFG_FIXED 0x0040 + short wdp_cylinders; /* number of non-removable cylinders */ + char __reserved1[2]; + short wdp_heads; /* number of heads */ + short wdp_unfbytespertrk; /* number of unformatted bytes/track */ + short wdp_unfbytespersec; /* number of unformatted bytes/sector */ + short wdp_sectors; /* number of sectors */ + char wdp_vendor1[6]; + /* controller info */ + char wdp_serial[20]; /* serial number */ + short wdp_buftype; /* buffer type */ +#define WD_BUF_SINGLEPORTSECTOR 1 /* single port, single sector buffer */ +#define WD_BUF_DUALPORTMULTI 2 /* dual port, multiple sector buffer */ +#define WD_BUF_DUALPORTMULTICACHE 3 /* above plus track cache */ + short wdp_bufsize; /* buffer size, in 512-byte units */ + short wdp_eccbytes; /* ecc bytes appended */ + char wdp_revision[8]; /* firmware revision */ + char wdp_model[40]; /* model name */ + u_char wdp_maxmulti; /* maximum sectors per interrupt */ + char wdp_vendor2[1]; + short wdp_usedmovsd; /* can use double word read/write? */ + char wdp_vendor3[1]; + char wdp_capabilities; /* capability flags */ +#define WD_CAP_LBA 0x02 +#define WD_CAP_DMA 0x01 + char __reserved2[2]; + char wdp_vendor4[1]; + char wdp_piotiming; /* PIO timing mode */ + char wdp_vendor5[1]; + char wdp_dmatiming; /* DMA timing mode */ +}; +#endif /* _KERNEL */ |