diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ed/if_ed.c | 2487 | ||||
-rw-r--r-- | sys/dev/ed/if_edreg.h | 962 | ||||
-rw-r--r-- | sys/dev/ep/if_ep.c | 993 | ||||
-rw-r--r-- | sys/dev/ep/if_epreg.h | 295 | ||||
-rw-r--r-- | sys/dev/fdc/fdc.c | 1255 | ||||
-rw-r--r-- | sys/dev/fdc/fdcreg.h | 65 | ||||
-rw-r--r-- | sys/dev/ic/i8237.h | 11 | ||||
-rw-r--r-- | sys/dev/ic/i82586.h | 325 | ||||
-rw-r--r-- | sys/dev/ic/nec765.h | 72 | ||||
-rw-r--r-- | sys/dev/ic/ns16550.h | 51 | ||||
-rw-r--r-- | sys/dev/ie/if_ie.c | 1801 | ||||
-rw-r--r-- | sys/dev/ie/if_iereg.h | 24 | ||||
-rw-r--r-- | sys/dev/kbd/kbdtables.h | 859 | ||||
-rw-r--r-- | sys/dev/mcd/mcd.c | 1335 | ||||
-rw-r--r-- | sys/dev/mcd/mcdreg.h | 159 | ||||
-rw-r--r-- | sys/dev/mse/mse.c | 499 | ||||
-rw-r--r-- | sys/dev/ppbus/lptio.h | 24 | ||||
-rw-r--r-- | sys/dev/sio/sio.c | 1920 | ||||
-rw-r--r-- | sys/dev/sio/sioreg.h | 114 | ||||
-rw-r--r-- | sys/dev/speaker/speaker.h | 30 | ||||
-rw-r--r-- | sys/dev/speaker/spkr.c | 541 | ||||
-rw-r--r-- | sys/dev/syscons/syscons.c | 2660 |
22 files changed, 16482 insertions, 0 deletions
diff --git a/sys/dev/ed/if_ed.c b/sys/dev/ed/if_ed.c new file mode 100644 index 0000000..26e3ebd --- /dev/null +++ b/sys/dev/ed/if_ed.c @@ -0,0 +1,2487 @@ +/* + * Device driver for National Semiconductor DS8390/WD83C690 based ethernet + * adapters. By David Greenman, 29-April-1993 + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + * + * Currently supports the Western Digital/SMC 8003 and 8013 series, + * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000, + * and a variety of similar clones. + * + */ + +/* + * $Id: if_ed.c,v 1.36 1994/04/10 20:06:26 davidg Exp $ + */ + +#include "ed.h" +#if NED > 0 +/* bpfilter included here in case it is needed in future net includes */ +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_edreg.h" + +#include "i386/include/pio.h" + +/* For backwards compatibility */ +#ifndef IFF_ALTPHYS +#define IFF_ALTPHYS IFF_LINK0 +#endif + +/* + * ed_softc: per line info and status + */ +struct ed_softc { + struct arpcom arpcom; /* ethernet common */ + + char *type_str; /* pointer to type string */ + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ + + u_short asic_addr; /* ASIC I/O bus address */ + u_short nic_addr; /* NIC (DS8390) I/O bus address */ + +/* + * The following 'proto' variable is part of a work-around for 8013EBT asics + * being write-only. It's sort of a prototype/shadow of the real thing. + */ + u_char wd_laar_proto; + u_char isa16bit; /* width of access to card 0=8 or 1=16 */ + int is790; /* set by the probe code if the card is 790 based */ + + caddr_t bpf; /* BPF "magic cookie" */ + caddr_t mem_start; /* NIC memory start address */ + caddr_t mem_end; /* NIC memory end address */ + u_long mem_size; /* total NIC memory size */ + caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ + + u_char mem_shared; /* NIC memory is shared with host */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* number of transmit buffers */ + u_char txb_inuse; /* number of TX buffers currently in-use*/ + + u_char txb_new; /* pointer to where new buffer will be added */ + u_char txb_next_tx; /* pointer to next buffer ready to xmit */ + u_short txb_len[8]; /* buffered xmit buffer lengths */ + u_char tx_page_start; /* first page of TX buffer area */ + u_char rec_page_start; /* first page of RX ring-buffer */ + u_char rec_page_stop; /* last page of RX ring-buffer */ + u_char next_packet; /* pointer to next unread RX packet */ +} ed_softc[NED]; + +int ed_attach(struct isa_device *); +void ed_init(int); +void edintr(int); +int ed_ioctl(struct ifnet *, int, caddr_t); +int ed_probe(struct isa_device *); +void ed_start(struct ifnet *); +void ed_reset(int); +void ed_watchdog(int); + +static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/); +static void ed_stop(int); + +static inline void ed_rint(); +static inline void ed_xmit(); +static inline char *ed_ring_copy(); + +void ed_pio_readmem(), ed_pio_writemem(); +u_short ed_pio_write_mbufs(); + +extern int ether_output(); + +struct trailer_header { + u_short ether_type; + u_short ether_residual; +}; + +struct isa_driver eddriver = { + ed_probe, + ed_attach, + "ed" +}; +/* + * Interrupt conversion table for WD/SMC ASIC + * (IRQ* are defined in icu.h) + */ +static unsigned short ed_intr_mask[] = { + IRQ9, + IRQ3, + IRQ5, + IRQ7, + IRQ10, + IRQ11, + IRQ15, + IRQ4 +}; + +/* + * Interrupt conversion table for 585/790 Combo + */ +static unsigned short ed_790_intr_mask[] = { + 0, + IRQ9, + IRQ3, + IRQ5, + IRQ7, + IRQ10, + IRQ11, + IRQ15 +}; +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 +#define ETHER_HDR_SIZE 14 + +/* + * Determine if the device is present + * + * on entry: + * a pointer to an isa_device struct + * on exit: + * NULL if device not found + * or # of i/o addresses used (if found) + */ +int +ed_probe(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int nports; + + if (nports = ed_probe_WD80x3(isa_dev)) + return (nports); + + if (nports = ed_probe_3Com(isa_dev)) + return (nports); + + if (nports = ed_probe_Novell(isa_dev)) + return (nports); + + return(0); +} + +/* + * Generic probe routine for testing for the existance of a DS8390. + * Must be called after the NIC has just been reset. This routine + * works by looking at certain register values that are gauranteed + * to be initialized a certain way after power-up or reset. Seems + * not to currently work on the 83C690. + * + * Specifically: + * + * Register reset bits set bits + * Command Register (CR) TXP, STA RD2, STP + * Interrupt Status (ISR) RST + * Interrupt Mask (IMR) All bits + * Data Control (DCR) LAS + * Transmit Config. (TCR) LB1, LB0 + * + * We only look at the CR and ISR registers, however, because looking at + * the others would require changing register pages (which would be + * intrusive if this isn't an 8390). + * + * Return 1 if 8390 was found, 0 if not. + */ + +int +ed_probe_generic8390(sc) + struct ed_softc *sc; +{ + if ((inb(sc->nic_addr + ED_P0_CR) & + (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) != + (ED_CR_RD2|ED_CR_STP)) + return (0); + if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) + return (0); + + return(1); +} + +/* + * Probe and vendor-specific initialization routine for SMC/WD80x3 boards + */ +int +ed_probe_WD80x3(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int i; + u_int memsize; + u_char iptr, isa16bit, sum; + + sc->asic_addr = isa_dev->id_iobase; + sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; + sc->is790 = 0; + +#ifdef TOSH_ETHER + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); + DELAY(10000); +#endif + /* + * Attempt to do a checksum over the station address PROM. + * If it fails, it's probably not a SMC/WD board. There + * is a problem with this, though: some clone WD boards + * don't pass the checksum test. Danpex boards for one. + */ + for (sum = 0, i = 0; i < 8; ++i) + sum += inb(sc->asic_addr + ED_WD_PROM + i); + + if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { + /* + * Checksum is invalid. This often happens with cheap + * WD8003E clones. In this case, the checksum byte + * (the eighth byte) seems to always be zero. + */ + if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || + inb(sc->asic_addr + ED_WD_PROM + 7) != 0) + return(0); + } + + /* reset card to force it into a known state. */ +#ifdef TOSH_ETHER + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); +#else + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST); +#endif + DELAY(100); + outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST); + /* wait in the case this card is reading it's EEROM */ + DELAY(5000); + + sc->vendor = ED_VENDOR_WD_SMC; + sc->type = inb(sc->asic_addr + ED_WD_CARD_ID); + + /* + * Set initial values for width/size. + */ + memsize = 8192; + isa16bit = 0; + switch (sc->type) { + case ED_TYPE_WD8003S: + sc->type_str = "WD8003S"; + break; + case ED_TYPE_WD8003E: + sc->type_str = "WD8003E"; + break; + case ED_TYPE_WD8003EB: + sc->type_str = "WD8003EB"; + break; + case ED_TYPE_WD8003W: + sc->type_str = "WD8003W"; + break; + case ED_TYPE_WD8013EBT: + sc->type_str = "WD8013EBT"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013W: + sc->type_str = "WD8013W"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EP: /* also WD8003EP */ + if (inb(sc->asic_addr + ED_WD_ICR) + & ED_WD_ICR_16BIT) { + isa16bit = 1; + memsize = 16384; + sc->type_str = "WD8013EP"; + } else { + sc->type_str = "WD8003EP"; + } + break; + case ED_TYPE_WD8013WC: + sc->type_str = "WD8013WC"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EBP: + sc->type_str = "WD8013EBP"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EPC: + sc->type_str = "WD8013EPC"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_SMC8216C: + sc->type_str = "SMC8216/SMC8216C"; + memsize = 16384; + isa16bit = 1; + sc->is790 = 1; + break; + case ED_TYPE_SMC8216T: + sc->type_str = "SMC8216T"; + memsize = 16384; + isa16bit = 1; + sc->is790 = 1; + break; +#ifdef TOSH_ETHER + case ED_TYPE_TOSHIBA1: + sc->type_str = "Toshiba1"; + memsize = 32768; + isa16bit = 1; + break; + case ED_TYPE_TOSHIBA4: + sc->type_str = "Toshiba4"; + memsize = 32768; + isa16bit = 1; + break; +#endif + default: + sc->type_str = ""; + break; + } + /* + * Make some adjustments to initial values depending on what is + * found in the ICR. + */ + if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) +#ifdef TOSH_ETHER + && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) +#endif + && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { + isa16bit = 0; + memsize = 8192; + } + +#if ED_DEBUG + printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", + sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize); + for (i=0; i<8; i++) + printf("%x -> %x\n", i, inb(sc->asic_addr + i)); +#endif + /* + * Allow the user to override the autoconfiguration + */ + if (isa_dev->id_msize) + memsize = isa_dev->id_msize; + /* + * (note that if the user specifies both of the following flags + * that '8bit' mode intentionally has precedence) + */ + if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) + isa16bit = 1; + if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE) + isa16bit = 0; + + /* + * Check 83C584 interrupt configuration register if this board has one + * XXX - we could also check the IO address register. But why + * bother...if we get past this, it *has* to be correct. + */ + if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { + /* + * Assemble together the encoded interrupt number. + */ + iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | + ((inb(isa_dev->id_iobase + ED_WD_IRR) & + (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + /* + * Translate it using translation table, and check for correctness. + */ + if (ed_intr_mask[iptr] != isa_dev->id_irq) { + printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n", + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_intr_mask[iptr]) - 1); + return(0); + } + /* + * Enable the interrupt. + */ + outb(isa_dev->id_iobase + ED_WD_IRR, + inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); + } + if (sc->is790) { + outb(isa_dev->id_iobase + ED_WD790_HWR, + inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); + iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | + (inb(isa_dev->id_iobase + ED_WD790_GCR) & + (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2); + outb(isa_dev->id_iobase + ED_WD790_HWR, + inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); + + if (ed_790_intr_mask[iptr] != isa_dev->id_irq) { + printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n", + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_790_intr_mask[iptr]) - 1, iptr); + return 0; + } + /* + * Enable interrupts. + */ + outb(isa_dev->id_iobase + ED_WD790_ICR, + inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); + } + + sc->isa16bit = isa16bit; + +#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ + /* + * The following allows the WD/SMC boards to be used in Programmed I/O + * mode - without mapping the NIC memory shared. ...Not the prefered + * way, but it might be the only way. + */ + if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) { + sc->mem_shared = 0; + isa_dev->id_maddr = 0; + } else { + sc->mem_shared = 1; + } +#else + sc->mem_shared = 1; +#endif + isa_dev->id_msize = memsize; + + sc->mem_start = (caddr_t)isa_dev->id_maddr; + + /* + * allocate one xmit buffer if < 16k, two buffers otherwise + */ + if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) { + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); + sc->txb_cnt = 1; + sc->rec_page_start = ED_TXBUF_SIZE; + } else { + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2); + sc->txb_cnt = 2; + sc->rec_page_start = ED_TXBUF_SIZE * 2; + } + sc->mem_size = memsize; + sc->mem_end = sc->mem_start + memsize; + sc->rec_page_stop = memsize / ED_PAGE_SIZE; + sc->tx_page_start = ED_WD_PAGE_OFFSET; + + /* + * Get station address from on-board ROM + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); + + if (sc->mem_shared) { + /* + * Set address and enable interface shared memory. + */ + if(!sc->is790) { +#ifdef TOSH_ETHER + outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); + outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); + +#else + outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & + ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); +#endif + } else { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); + outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | + ((kvtop(sc->mem_start) >> 11) & 0x40) | + (inb(sc->asic_addr + 0x0b) & 0xb0)); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); + } + + /* + * Set upper address bits and 8/16 bit access to shared memory + */ + if (isa16bit) { + if (sc->is790) { + sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR); + outb(sc->asic_addr + ED_WD_LAAR, ED_WD_LAAR_M16EN); + (void) inb(0x84); + } else { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = + ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + } + } else { + if ((sc->type & ED_WD_SOFTCONFIG) || +#ifdef TOSH_ETHER + (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || +#endif + (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + } + } + + /* + * Now zero memory and verify that it is clear + */ + bzero(sc->mem_start, memsize); + + for (i = 0; i < memsize; ++i) + if (sc->mem_start[i]) { + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + + /* + * Disable 16 bit access to shared memory + */ + if (isa16bit) { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + (void) inb(0x84); + } + + return(0); + } + + /* + * Disable 16bit access to shared memory - we leave it disabled so + * that 1) machines reboot properly when the board is set + * 16 bit mode and there are conflicting 8bit devices/ROMS + * in the same 128k address space as this boards shared + * memory. and 2) so that other 8 bit devices with shared + * memory can be used in this 128k region, too. + */ + if (isa16bit) { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + (void) inb(0x84); + } + } + + return (ED_WD_IO_PORTS); +} + +/* + * Probe and vendor-specific initialization routine for 3Com 3c503 boards + */ +int +ed_probe_3Com(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int i; + u_int memsize; + u_char isa16bit, sum; + + sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; + + /* + * Verify that the kernel configured I/O address matches the board + * configured address + */ + switch (inb(sc->asic_addr + ED_3COM_BCFR)) { + case ED_3COM_BCFR_300: + if (isa_dev->id_iobase != 0x300) + return(0); + break; + case ED_3COM_BCFR_310: + if (isa_dev->id_iobase != 0x310) + return(0); + break; + case ED_3COM_BCFR_330: + if (isa_dev->id_iobase != 0x330) + return(0); + break; + case ED_3COM_BCFR_350: + if (isa_dev->id_iobase != 0x350) + return(0); + break; + case ED_3COM_BCFR_250: + if (isa_dev->id_iobase != 0x250) + return(0); + break; + case ED_3COM_BCFR_280: + if (isa_dev->id_iobase != 0x280) + return(0); + break; + case ED_3COM_BCFR_2A0: + if (isa_dev->id_iobase != 0x2a0) + return(0); + break; + case ED_3COM_BCFR_2E0: + if (isa_dev->id_iobase != 0x2e0) + return(0); + break; + default: + return(0); + } + + /* + * Verify that the kernel shared memory address matches the + * board configured address. + */ + switch (inb(sc->asic_addr + ED_3COM_PCFR)) { + case ED_3COM_PCFR_DC000: + if (kvtop(isa_dev->id_maddr) != 0xdc000) + return(0); + break; + case ED_3COM_PCFR_D8000: + if (kvtop(isa_dev->id_maddr) != 0xd8000) + return(0); + break; + case ED_3COM_PCFR_CC000: + if (kvtop(isa_dev->id_maddr) != 0xcc000) + return(0); + break; + case ED_3COM_PCFR_C8000: + if (kvtop(isa_dev->id_maddr) != 0xc8000) + return(0); + break; + default: + return(0); + } + + + /* + * Reset NIC and ASIC. Enable on-board transceiver throughout reset + * sequence because it'll lock up if the cable isn't connected + * if we don't. + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); + + /* + * Wait for a while, then un-reset it + */ + DELAY(50); + /* + * The 3Com ASIC defaults to rather strange settings for the CR after + * a reset - it's important to set it again after the following + * outb (this is done when we map the PROM below). + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + + /* + * Wait a bit for the NIC to recover from the reset + */ + DELAY(5000); + + sc->vendor = ED_VENDOR_3COM; + sc->type_str = "3c503"; + + sc->mem_shared = 1; + + /* + * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k + * window to it. + */ + memsize = 8192; + + /* + * Get station address from on-board ROM + */ + /* + * First, map ethernet address PROM over the top of where the NIC + * registers normally appear. + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); + + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i); + + /* + * Unmap PROM - select NIC registers. The proper setting of the + * tranceiver is set in ed_init so that the attach code + * is given a chance to set the default based on a compile-time + * config option + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + + /* + * Determine if this is an 8bit or 16bit board + */ + + /* + * select page 0 registers + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + /* + * Attempt to clear WTS bit. If it doesn't clear, then this is a + * 16bit board. + */ + outb(sc->nic_addr + ED_P0_DCR, 0); + + /* + * select page 2 registers + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP); + + /* + * The 3c503 forces the WTS bit to a one if this is a 16bit board + */ + if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS) + isa16bit = 1; + else + isa16bit = 0; + + /* + * select page 0 registers + */ + outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP); + + sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_size = memsize; + sc->mem_end = sc->mem_start + memsize; + + /* + * We have an entire 8k window to put the transmit buffers on the + * 16bit boards. But since the 16bit 3c503's shared memory + * is only fast enough to overlap the loading of one full-size + * packet, trying to load more than 2 buffers can actually + * leave the transmitter idle during the load. So 2 seems + * the best value. (Although a mix of variable-sized packets + * might change this assumption. Nonetheless, we optimize for + * linear transfers of same-size packets.) + */ + if (isa16bit) { + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; + sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; + sc->rec_page_stop = memsize / ED_PAGE_SIZE + + ED_3COM_RX_PAGE_OFFSET_16BIT; + sc->mem_ring = sc->mem_start; + } else { + sc->txb_cnt = 1; + sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->rec_page_stop = memsize / ED_PAGE_SIZE + + ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); + } + + sc->isa16bit = isa16bit; + + /* + * Initialize GA page start/stop registers. Probably only needed + * if doing DMA, but what the hell. + */ + outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); + outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); + + /* + * Set IRQ. 3c503 only allows a choice of irq 2-5. + */ + switch (isa_dev->id_irq) { + case IRQ2: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2); + break; + case IRQ3: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3); + break; + case IRQ4: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4); + break; + case IRQ5: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); + break; + default: + printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n", + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); + return(0); + } + + /* + * Initialize GA configuration register. Set bank and enable shared mem. + */ + outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | + ED_3COM_GACFR_MBS0); + + /* + * Initialize "Vector Pointer" registers. These gawd-awful things + * are compared to 20 bits of the address on ISA, and if they + * match, the shared memory is disabled. We set them to + * 0xffff0...allegedly the reset vector. + */ + outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); + outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); + outb(sc->asic_addr + ED_3COM_VPTR0, 0x00); + + /* + * Zero memory and verify that it is clear + */ + bzero(sc->mem_start, memsize); + + for (i = 0; i < memsize; ++i) + if (sc->mem_start[i]) { + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + return(0); + } + + isa_dev->id_msize = memsize; + return(ED_3COM_IO_PORTS); +} + +/* + * Probe and vendor-specific initialization routine for NE1000/2000 boards + */ +int +ed_probe_Novell(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + u_int memsize, n; + u_char romdata[16], isa16bit = 0, tmp; + static char test_pattern[32] = "THIS is A memory TEST pattern"; + char test_buffer[32]; + + sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET; + + /* XXX - do Novell-specific probe here */ + + /* Reset the board */ + tmp = inb(sc->asic_addr + ED_NOVELL_RESET); + + /* + * I don't know if this is necessary; probably cruft leftover from + * Clarkson packet driver code. Doesn't do a thing on the boards + * I've tested. -DG [note that a outb(0x84, 0) seems to work + * here, and is non-invasive...but some boards don't seem to reset + * and I don't have complete documentation on what the 'right' + * thing to do is...so we do the invasive thing for now. Yuck.] + */ + outb(sc->asic_addr + ED_NOVELL_RESET, tmp); + DELAY(5000); + + /* + * This is needed because some NE clones apparently don't reset the + * NIC properly (or the NIC chip doesn't reset fully on power-up) + * XXX - this makes the probe invasive! ...Done against my better + * judgement. -DLG + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + DELAY(5000); + + /* Make sure that we really have an 8390 based board */ + if (!ed_probe_generic8390(sc)) + return(0); + + sc->vendor = ED_VENDOR_NOVELL; + sc->mem_shared = 0; + isa_dev->id_maddr = 0; + + /* + * Test the ability to read and write to the NIC memory. This has + * the side affect of determining if this is an NE1000 or an NE2000. + */ + + /* + * This prevents packets from being stored in the NIC memory when + * the readmem routine turns on the start bit in the CR. + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); + + /* Temporarily initialize DCR for byte operations */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + + outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); + outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); + + sc->isa16bit = 0; + + /* + * Write a test pattern in byte mode. If this fails, then there + * probably isn't any memory at 8k - which likely means + * that the board is an NE2000. + */ + ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); + ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { + /* not an NE1000 - try NE2000 */ + + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); + outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); + + sc->isa16bit = 1; + /* + * Write a test pattern in word mode. If this also fails, then + * we don't know what this board is. + */ + ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); + ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) + return(0); /* not an NE2000 either */ + + sc->type = ED_TYPE_NE2000; + sc->type_str = "NE2000"; + } else { + sc->type = ED_TYPE_NE1000; + sc->type_str = "NE1000"; + } + + /* 8k of memory plus an additional 8k if 16bit */ + memsize = 8192 + sc->isa16bit * 8192; + +#if 0 /* probably not useful - NE boards only come two ways */ + /* allow kernel config file overrides */ + if (isa_dev->id_msize) + memsize = isa_dev->id_msize; +#endif + + sc->mem_size = memsize; + + /* NIC memory doesn't start at zero on an NE board */ + /* The start address is tied to the bus width */ + sc->mem_start = (char *) 8192 + sc->isa16bit * 8192; + sc->mem_end = sc->mem_start + memsize; + sc->tx_page_start = memsize / ED_PAGE_SIZE; + + /* + * Use one xmit buffer if < 16k, two buffers otherwise (if not told + * otherwise). + */ + if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE; + sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE; + + sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; + + ed_pio_readmem(sc, 0, romdata, 16); + for (n = 0; n < ETHER_ADDR_LEN; n++) + sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)]; + + /* clear any pending interrupts that might have occurred above */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); + + return(ED_NOVELL_IO_PORTS); +} + +/* + * Install interface into kernel networking data structures + */ +int +ed_attach(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* + * Set interface to stopped condition (reset) + */ + ed_stop(isa_dev->id_unit); + + /* + * Initialize ifnet structure + */ + ifp->if_unit = isa_dev->id_unit; + ifp->if_name = "ed" ; + ifp->if_mtu = ETHERMTU; + ifp->if_init = ed_init; + ifp->if_output = ether_output; + ifp->if_start = ed_start; + ifp->if_ioctl = ed_ioctl; + ifp->if_reset = ed_reset; + ifp->if_watchdog = ed_watchdog; + + /* + * Set default state for ALTPHYS flag (used to disable the tranceiver + * for AUI operation), based on compile-time config option. + */ + if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER) + ifp->if_flags = + (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS); + else + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* + * Attach the interface + */ + if_attach(ifp); + + /* + * Search down the ifa address list looking for the AF_LINK type entry + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + /* + * If we find an AF_LINK type entry we fill in the hardware address. + * This is useful for netstat(1) to keep track of which interface + * is which. + */ + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* + * Fill in the link-level address for this interface + */ + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + + /* + * Print additional info when attached + */ + printf("ed%d: address %s, ", isa_dev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr)); + + if (sc->type_str && (*sc->type_str != 0)) + printf("type %s ", sc->type_str); + else + printf("type unknown (0x%x) ", sc->type); + + printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)"); + + printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && + (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); + + /* + * If BPF is in the kernel, call the attach for it + */ +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; +} + +/* + * Reset interface. + */ +void +ed_reset(unit) + int unit; +{ + int s; + + s = splimp(); + + /* + * Stop interface and re-initialize. + */ + ed_stop(unit); + ed_init(unit); + + (void) splx(s); +} + +/* + * Take interface offline. + */ +void +ed_stop(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + int n = 5000; + + /* + * Stop everything on the interface, and select page 0 registers. + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + } + /* + * Wait for interface to enter stopped state, but limit # of checks + * to 'n' (about 5ms). It shouldn't even take 5us on modern + * DS8390's, but just in case it's an old one. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); + +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +void +ed_watchdog(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + + log(LOG_ERR, "ed%d: device timeout\n", unit); + ++sc->arpcom.ac_if.if_oerrors; + + ed_reset(unit); +} + +/* + * Initialize device. + */ +void +ed_init(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i, s; + u_char command; + + + /* address not known */ + if (ifp->if_addrlist == (struct ifaddr *)0) return; + + /* + * Initialize the NIC in the exact order outlined in the NS manual. + * This init procedure is "mandatory"...don't change what or when + * things happen. + */ + s = splimp(); + + /* reset transmitter flags */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_timer = 0; + + sc->txb_inuse = 0; + sc->txb_new = 0; + sc->txb_next_tx = 0; + + /* This variable is used below - don't move this assignment */ + sc->next_packet = sc->rec_page_start + 1; + + /* + * Set interface for page 0, Remote DMA complete, Stopped + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + } + if (sc->isa16bit) { + /* + * Set FIFO threshold to 8, No auto-init Remote DMA, + * byte order=80x86, word-wide DMA xfers, + */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS); + } else { + /* + * Same as above, but byte-wide DMA xfers + */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + } + + /* + * Clear Remote Byte Count Registers + */ + outb(sc->nic_addr + ED_P0_RBCR0, 0); + outb(sc->nic_addr + ED_P0_RBCR1, 0); + + /* + * Enable reception of broadcast packets + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); + + /* + * Place NIC in internal loopback mode + */ + outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0); + + /* + * Initialize transmit/receive (ring-buffer) Page Start + */ + outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start); + outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start); + /* Set lower bits of byte addressable framing to 0 */ + if (sc->is790) + outb(sc->nic_addr + 0x09, 0); + + /* + * Initialize Receiver (ring-buffer) Page Stop and Boundry + */ + outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop); + outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start); + + /* + * Clear all interrupts. A '1' in each bit position clears the + * corresponding flag. + */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); + + /* + * Enable the following interrupts: receive/transmit complete, + * receive/transmit error, and Receiver OverWrite. + * + * Counter overflow and Remote DMA complete are *not* enabled. + */ + outb(sc->nic_addr + ED_P0_IMR, + ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE); + + /* + * Program Command Register for page 1 + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP); + } + /* + * Copy out our station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); + +#if NBPFILTER > 0 + /* + * Initialize multicast address hashing registers to accept + * all multicasts (only used when in promiscuous mode) + */ + for (i = 0; i < 8; ++i) + outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); +#endif + + /* + * Set Current Page pointer to next_packet (initialized above) + */ + outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); + + /* + * Set Command Register for page 0, Remote DMA complete, + * and interface Start. + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P1_CR, ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA); + } + /* + * Take interface out of loopback + */ + outb(sc->nic_addr + ED_P0_TCR, 0); + + /* + * If this is a 3Com board, the tranceiver must be software enabled + * (there is no settable hardware default). + */ + if (sc->vendor == ED_VENDOR_3COM) { + if (ifp->if_flags & IFF_ALTPHYS) { + outb(sc->asic_addr + ED_3COM_CR, 0); + } else { + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + } + } + + /* + * Set 'running' flag, and clear output active flag. + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * ...and attempt to start output + */ + ed_start(ifp); + + (void) splx(s); +} + +/* + * This routine actually starts the transmission on the interface + */ +static inline void ed_xmit(ifp) + struct ifnet *ifp; +{ + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + unsigned short len; + + len = sc->txb_len[sc->txb_next_tx]; + + /* + * Set NIC for page 0 register access + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + } + /* + * Set TX buffer start page + */ + outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + + sc->txb_next_tx * ED_TXBUF_SIZE); + + /* + * Set TX length + */ + outb(sc->nic_addr + ED_P0_TBCR0, len); + outb(sc->nic_addr + ED_P0_TBCR1, len >> 8); + + /* + * Set page 0, Remote DMA complete, Transmit Packet, and *Start* + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA); + } + sc->xmit_busy = 1; + + /* + * Point to next transmit buffer slot and wrap if necessary. + */ + sc->txb_next_tx++; + if (sc->txb_next_tx == sc->txb_cnt) + sc->txb_next_tx = 0; + + /* + * Set a timer just in case we never hear from the board again + */ + ifp->if_timer = 2; +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splimp _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +void +ed_start(ifp) + struct ifnet *ifp; +{ + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + struct mbuf *m0, *m; + caddr_t buffer; + int len; + +outloop: + /* + * First, see if there are buffered packets and an idle + * transmitter - should never happen at this point. + */ + if (sc->txb_inuse && (sc->xmit_busy == 0)) { + printf("ed: packets buffers, but transmitter idle\n"); + ed_xmit(ifp); + } + + /* + * See if there is room to put another packet in the buffer. + */ + if (sc->txb_inuse == sc->txb_cnt) { + /* + * No room. Indicate this to the outside world + * and exit. + */ + ifp->if_flags |= IFF_OACTIVE; + return; + } + + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == 0) { + /* + * We are using the !OACTIVE flag to indicate to the outside + * world that we can accept an additional packet rather than + * that the transmitter is _actually_ active. Indeed, the + * transmitter may be active, but if we haven't filled all + * the buffers with data then we still want to accept more. + */ + ifp->if_flags &= ~IFF_OACTIVE; + return; + } + + /* + * Copy the mbuf chain into the transmit buffer + */ + + m0 = m; + + /* txb_new points to next open buffer slot */ + buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); + + if (sc->mem_shared) { + /* + * Special case setup for 16 bit boards... + */ + if (sc->isa16bit) { + switch (sc->vendor) { + /* + * For 16bit 3Com boards (which have 16k of memory), + * we have the xmit buffers in a different page + * of memory ('page 0') - so change pages. + */ + case ED_VENDOR_3COM: + outb(sc->asic_addr + ED_3COM_GACFR, + ED_3COM_GACFR_RSEL); + break; + /* + * Enable 16bit access to shared memory on WD/SMC boards + * Don't update wd_laar_proto because we want to restore the + * previous state (because an arp reply in the input code + * may cause a call-back to ed_start) + * XXX - the call-back to 'start' is a bug, IMHO. + */ + case ED_VENDOR_WD_SMC: { + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); + (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + (void) inb(0x84); + } + (void) inb(0x84); + break; + } + } + } + + for (len = 0; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + + /* + * Restore previous shared memory access + */ + if (sc->isa16bit) { + switch (sc->vendor) { + case ED_VENDOR_3COM: + outb(sc->asic_addr + ED_3COM_GACFR, + ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); + break; + case ED_VENDOR_WD_SMC: { + outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); + (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } + break; + } + } + } + } else { + len = ed_pio_write_mbufs(sc, m, buffer); + } + + sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN); + + sc->txb_inuse++; + + /* + * Point to next buffer slot and wrap if necessary. + */ + sc->txb_new++; + if (sc->txb_new == sc->txb_cnt) + sc->txb_new = 0; + + if (sc->xmit_busy == 0) + ed_xmit(ifp); + /* + * If there is BPF support in the configuration, tap off here. + * The following has support for converting trailer packets + * back to normal. + * XXX - support for trailer packets in BPF should be moved into + * the bpf code proper to avoid code duplication in all of + * the drivers. + */ +#if NBPFILTER > 0 + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(m0, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(m0, off, sizeof(struct trailer_header), + (caddr_t)&trailer_header.ether_type); + + /* copy residual data */ + m_copydata(m0, off+sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - + sizeof(struct trailer_header), ep); + ep += resid; + + /* copy data */ + m_copydata(m0, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, m0); + } +#endif + + m_freem(m0); + + /* + * Loop back to the top to possibly buffer more packets + */ + goto outloop; +} + +/* + * Ethernet interface receiver interrupt. + */ +static inline void +ed_rint(unit) + int unit; +{ + register struct ed_softc *sc = &ed_softc[unit]; + u_char boundry, current; + u_short len; + struct ed_ring packet_hdr; + char *packet_ptr; + + /* + * Set NIC to page 1 registers to get 'current' pointer + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + } + /* + * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. + * it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer + * - i.e. it points to where additional new data will be added. + * We loop here until the logical beginning equals the logical + * end (or in other words, until the ring-buffer is empty). + */ + while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { + + /* get pointer to this buffer's header structure */ + packet_ptr = sc->mem_ring + + (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE; + + /* + * The byte count includes the FCS - Frame Check Sequence (a + * 32 bit CRC). + */ + if (sc->mem_shared) + packet_hdr = *(struct ed_ring *)packet_ptr; + else + ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr, + sizeof(packet_hdr)); + len = packet_hdr.count; + if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* + * Go get packet. len - 4 removes CRC from length. + */ + ed_get_packet(sc, packet_ptr + 4, len - 4); + ++sc->arpcom.ac_if.if_ipackets; + } else { + /* + * Really BAD...probably indicates that the ring pointers + * are corrupted. Also seen on early rev chips under + * high load - the byte order of the length gets switched. + */ + log(LOG_ERR, + "ed%d: NIC memory corrupt - invalid packet length %d\n", + unit, len); + ++sc->arpcom.ac_if.if_ierrors; + ed_reset(unit); + return; + } + + /* + * Update next packet pointer + */ + sc->next_packet = packet_hdr.next_packet; + + /* + * Update NIC boundry pointer - being careful to keep it + * one buffer behind. (as recommended by NS databook) + */ + boundry = sc->next_packet - 1; + if (boundry < sc->rec_page_start) + boundry = sc->rec_page_stop - 1; + + /* + * Set NIC to page 0 registers to update boundry register + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + } + outb(sc->nic_addr + ED_P0_BNRY, boundry); + + /* + * Set NIC to page 1 registers before looping to top (prepare to + * get 'CURR' current pointer) + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + } + } +} + +/* + * Ethernet interface interrupt processor + */ +void +edintr(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + u_char isr; + + /* + * Set NIC to page 0 registers + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + } + /* + * loop until there are no more new interrupts + */ + while (isr = inb(sc->nic_addr + ED_P0_ISR)) { + + /* + * reset all the bits that we are 'acknowledging' + * by writing a '1' to each bit position that was set + * (writing a '1' *clears* the bit) + */ + outb(sc->nic_addr + ED_P0_ISR, isr); + + /* + * Handle transmitter interrupts. Handle these first + * because the receiver will reset the board under + * some conditions. + */ + if (isr & (ED_ISR_PTX|ED_ISR_TXE)) { + u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; + + /* + * Check for transmit error. If a TX completed with an + * error, we end up throwing the packet away. Really + * the only error that is possible is excessive + * collisions, and in this case it is best to allow the + * automatic mechanisms of TCP to backoff the flow. Of + * course, with UDP we're screwed, but this is expected + * when a network is heavily loaded. + */ + (void) inb(sc->nic_addr + ED_P0_TSR); + if (isr & ED_ISR_TXE) { + + /* + * Excessive collisions (16) + */ + if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT) + && (collisions == 0)) { + /* + * When collisions total 16, the + * P0_NCR will indicate 0, and the + * TSR_ABT is set. + */ + collisions = 16; + } + + /* + * update output errors counter + */ + ++sc->arpcom.ac_if.if_oerrors; + } else { + /* + * Update total number of successfully + * transmitted packets. + */ + ++sc->arpcom.ac_if.if_opackets; + } + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + + /* + * Add in total number of collisions on last + * transmission. + */ + sc->arpcom.ac_if.if_collisions += collisions; + + /* + * Decrement buffer in-use count if not zero (can only + * be zero if a transmitter interrupt occured while + * not actually transmitting). + * If data is ready to transmit, start it transmitting, + * otherwise defer until after handling receiver + */ + if (sc->txb_inuse && --sc->txb_inuse) + ed_xmit(&sc->arpcom.ac_if); + } + + /* + * Handle receiver interrupts + */ + if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) { + /* + * Overwrite warning. In order to make sure that a lockup + * of the local DMA hasn't occurred, we reset and + * re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some + * chips seem to want more. The DMA lockup has been + * seen only with early rev chips - Methinks this + * bug was fixed in later revs. -DG + */ + if (isr & ED_ISR_OVW) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef DIAGNOSTIC + log(LOG_WARNING, + "ed%d: warning - receiver ring buffer overrun\n", + unit); +#endif + /* + * Stop/reset/re-init NIC + */ + ed_reset(unit); + } else { + + /* + * Receiver Error. One or more of: CRC error, frame + * alignment error FIFO overrun, or missed packet. + */ + if (isr & ED_ISR_RXE) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef ED_DEBUG + printf("ed%d: receive error %x\n", unit, + inb(sc->nic_addr + ED_P0_RSR)); +#endif + } + + /* + * Go get the packet(s) + * XXX - Doing this on an error is dubious + * because there shouldn't be any data to + * get (we've configured the interface to + * not accept packets with errors). + */ + + /* + * Enable 16bit access to shared memory first + * on WD/SMC boards. + */ + if (sc->isa16bit && + (sc->vendor == ED_VENDOR_WD_SMC)) { + + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto |= + ED_WD_LAAR_M16EN)); + (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, + ED_WD_MSR_MENB); + (void) inb(0x84); + } + } + + ed_rint (unit); + + /* disable 16bit access */ + if (sc->isa16bit && + (sc->vendor == ED_VENDOR_WD_SMC)) { + + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } + } + } + } + + /* + * If it looks like the transmitter can take more data, + * attempt to start output on the interface. + * This is done after handling the receiver to + * give the receiver priority. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) + ed_start(&sc->arpcom.ac_if); + + /* + * return NIC CR to standard state: page 0, remote DMA complete, + * start (toggling the TXP bit off, even if was just set + * in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) + */ + if (sc->is790) { + outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); + } else { + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + } + /* + * If the Network Talley Counters overflow, read them to + * reset them. It appears that old 8390's won't + * clear the ISR flag otherwise - resulting in an + * infinite loop. + */ + if (isr & ED_ISR_CNT) { + (void) inb(sc->nic_addr + ED_P0_CNTR0); + (void) inb(sc->nic_addr + ED_P0_CNTR1); + (void) inb(sc->nic_addr + ED_P0_CNTR2); + } + } +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +ed_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ed_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + ed_init(ifp->if_unit); + break; + } +#endif + default: + ed_init(ifp->if_unit); + break; + } + break; + + case SIOCGIFADDR: + { + struct sockaddr *sa; + sa = (struct sockaddr *)&ifr->ifr_data; + bcopy((caddr_t)sc->arpcom.ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ed_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + ed_init(ifp->if_unit); + } +#if NBPFILTER > 0 + if (ifp->if_flags & IFF_PROMISC) { + /* + * Set promiscuous mode on interface. + * XXX - for multicasts to work, we would need to + * write 1's in all bits of multicast + * hashing array. For now we assume that + * this was done in ed_init(). + */ + outb(sc->nic_addr + ED_P0_RCR, + ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB); + } else { + /* + * XXX - for multicasts to work, we would need to + * rewrite the multicast hashing array with the + * proper hash (would have been destroyed above). + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); + } +#endif + /* + * An unfortunate hack to provide the (required) software control + * of the tranceiver for 3Com boards. The ALTPHYS flag disables + * the tranceiver if set. + */ + if (sc->vendor == ED_VENDOR_3COM) { + if (ifp->if_flags & IFF_ALTPHYS) { + outb(sc->asic_addr + ED_3COM_CR, 0); + } else { + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + } + } + + break; + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* + * Macro to calculate a new address within shared memory when given an offset + * from an address, taking into account ring-wrap. + */ +#define ringoffset(sc, start, off, type) \ + ((type)( ((caddr_t)(start)+(off) >= (sc)->mem_end) ? \ + (((caddr_t)(start)+(off))) - (sc)->mem_end \ + + (sc)->mem_ring: \ + ((caddr_t)(start)+(off)) )) + +/* + * Retreive packet from shared memory and send to the next level up via + * ether_input(). If there is a BPF listener, give a copy to BPF, too. + */ +static void +ed_get_packet(sc, buf, len) + struct ed_softc *sc; + char *buf; + u_short len; +{ + struct ether_header *eh; + struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); + u_short off; + int resid; + u_short etype; + struct trailer_header trailer_header; + + /* Allocate a header mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto bad; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = len; + m->m_len = 0; + head = m; + + /* The following sillines is to make NFS happy */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + + /* + * The following assumes there is room for + * the ether header in the header mbuf + */ + head->m_data += EOFF; + eh = mtod(head, struct ether_header *); + + if (sc->mem_shared) + bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); + else + ed_pio_readmem(sc, buf, mtod(head, caddr_t), + sizeof(struct ether_header)); + buf += sizeof(struct ether_header); + head->m_len += sizeof(struct ether_header); + len -= sizeof(struct ether_header); + + etype = ntohs((u_short)eh->ether_type); + + /* + * Deal with trailer protocol: + * If trailer protocol, calculate the datasize as 'off', + * which is also the offset to the trailer header. + * Set resid to the amount of packet data following the + * trailer header. + * Finally, copy residual data into mbuf chain. + */ + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + + off = (etype - ETHERTYPE_TRAIL) << 9; + if ((off + sizeof(struct trailer_header)) > len) + goto bad; /* insanity */ + + /* + * If we have shared memory, we can get info directly from the + * stored packet, otherwise we must get a local copy + * of the trailer header using PIO. + */ + if (sc->mem_shared) { + eh->ether_type = *ringoffset(sc, buf, off, u_short *); + resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + } else { + struct trailer_header trailer_header; + ed_pio_readmem(sc, + ringoffset(sc, buf, off, caddr_t), + (char *) &trailer_header, + sizeof(trailer_header)); + eh->ether_type = trailer_header.ether_type; + resid = trailer_header.ether_residual; + } + + if ((off + resid) > len) goto bad; /* insanity */ + + resid -= sizeof(struct trailer_header); + if (resid < 0) goto bad; /* insanity */ + + m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), + head, resid); + if (m == 0) goto bad; + + len = off; + head->m_pkthdr.len -= 4; /* subtract trailer header */ + } + + /* + * Pull packet off interface. Or if this was a trailer packet, + * the data portion is appended. + */ + m = ed_ring_to_mbuf(sc, buf, m, len); + if (m == 0) goto bad; + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->bpf) { + bpf_mtap(sc->bpf, head); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + * + * XXX This test does not support multicasts. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + + m_freem(head); + return; + } + } +#endif + + /* + * Fix up data start offset in mbuf to point past ether header + */ + m_adj(head, sizeof(struct ether_header)); + + /* + * silly ether_input routine needs 'type' in host byte order + */ + eh->ether_type = ntohs(eh->ether_type); + + ether_input(&sc->arpcom.ac_if, eh, head); + return; + +bad: if (head) + m_freem(head); + return; +} + +/* + * Supporting routines + */ + +/* + * Given a NIC memory source address and a host memory destination + * address, copy 'amount' from NIC to host using Programmed I/O. + * The 'amount' is rounded up to a word - okay as long as mbufs + * are word sized. + * This routine is currently Novell-specific. + */ +void +ed_pio_readmem(sc,src,dst,amount) + struct ed_softc *sc; + unsigned short src; + unsigned char *dst; + unsigned short amount; +{ + unsigned short tmp_amount; + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* round up to a word */ + tmp_amount = amount; + if (amount & 1) ++amount; + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, amount); + outb(sc->nic_addr + ED_P0_RBCR1, amount>>8); + + /* set up source address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, src); + outb(sc->nic_addr + ED_P0_RSAR1, src>>8); + + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); + + if (sc->isa16bit) { + insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2); + } else + insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); + +} + +/* + * Stripped down routine for writing a linear buffer to NIC memory. + * Only used in the probe routine to test the memory. 'len' must + * be even. + */ +void +ed_pio_writemem(sc,src,dst,len) + struct ed_softc *sc; + char *src; + unsigned short dst; + unsigned short len; +{ + int maxwait=100; /* about 120us */ + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + + if (sc->isa16bit) + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2); + else + outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts + * and we can't start another remote DMA until this one completes. + * Not waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); +} + +/* + * Write an mbuf chain to the destination NIC memory address using + * programmed I/O. + */ +u_short +ed_pio_write_mbufs(sc,m,dst) + struct ed_softc *sc; + struct mbuf *m; + unsigned short dst; +{ + unsigned short len, mb_offset; + struct mbuf *mp; + unsigned char residual[2]; + int maxwait=100; /* about 120us */ + + /* First, count up the total number of bytes to copy */ + for (len = 0, mp = m; mp; mp = mp->m_next) + len += mp->m_len; + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + + mb_offset = 0; + /* + * Transfer the mbuf chain to the NIC memory. + * The following code isn't too pretty. The problem is that we can only + * transfer words to the board, and if an mbuf has an odd number + * of bytes in it, this is a problem. It's not a simple matter of + * just removing a byte from the next mbuf (adjusting data++ and + * len--) because this will hose-over the mbuf chain which might + * be needed later for BPF. Instead, we maintain an offset + * (mb_offset) which let's us skip over the first byte in the + * following mbuf. + */ + while (m) { + if (m->m_len - mb_offset) { + if (sc->isa16bit) { + if ((m->m_len - mb_offset) > 1) + outsw(sc->asic_addr + ED_NOVELL_DATA, + mtod(m, caddr_t) + mb_offset, + (m->m_len - mb_offset) / 2); + + /* + * if odd number of bytes, get the odd byte from + * the next mbuf with data + */ + if ((m->m_len - mb_offset) & 1) { + /* first the last byte in current mbuf */ + residual[0] = *(mtod(m, caddr_t) + + m->m_len - 1); + + /* advance past any empty mbufs */ + while (m->m_next && (m->m_next->m_len == 0)) + m = m->m_next; + + if (m->m_next) { + /* remove first byte in next mbuf */ + residual[1] = *(mtod(m->m_next, caddr_t)); + mb_offset = 1; + } + + outw(sc->asic_addr + ED_NOVELL_DATA, + *((unsigned short *) residual)); + } else + mb_offset = 0; + } else + outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len); + + } + m = m->m_next; + } + + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts + * and we can't start another remote DMA until this one completes. + * Not waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + + if (!maxwait) { + log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n", + sc->arpcom.ac_if.if_unit); + ed_reset(sc->arpcom.ac_if.if_unit); + } + + return(len); +} + +/* + * Given a source and destination address, copy 'amount' of a packet from + * the ring buffer into a linear destination buffer. Takes into account + * ring-wrap. + */ +static inline char * +ed_ring_copy(sc,src,dst,amount) + struct ed_softc *sc; + char *src; + char *dst; + u_short amount; +{ + u_short tmp_amount; + + /* does copy wrap to lower addr in ring buffer? */ + if (src + amount > sc->mem_end) { + tmp_amount = sc->mem_end - src; + + /* copy amount up to end of NIC memory */ + if (sc->mem_shared) + bcopy(src,dst,tmp_amount); + else + ed_pio_readmem(sc,src,dst,tmp_amount); + + amount -= tmp_amount; + src = sc->mem_ring; + dst += tmp_amount; + } + + if (sc->mem_shared) + bcopy(src, dst, amount); + else + ed_pio_readmem(sc, src, dst, amount); + + return(src + amount); +} + +/* + * Copy data from receive buffer to end of mbuf chain + * allocate additional mbufs as needed. return pointer + * to last mbuf in chain. + * sc = ed info (softc) + * src = pointer in ed ring buffer + * dst = pointer to last mbuf in mbuf chain to copy to + * amount = amount of data to copy + */ +struct mbuf * +ed_ring_to_mbuf(sc,src,dst,total_len) + struct ed_softc *sc; + char *src; + struct mbuf *dst; + u_short total_len; +{ + register struct mbuf *m = dst; + + while (total_len) { + register u_short amount = min(total_len, M_TRAILINGSPACE(m)); + + if (amount == 0) { /* no more data in this mbuf, alloc another */ + /* + * If there is enough data for an mbuf cluster, attempt + * to allocate one of those, otherwise, a regular + * mbuf will do. + * Note that a regular mbuf is always required, even if + * we get a cluster - getting a cluster does not + * allocate any mbufs, and one is needed to assign + * the cluster to. The mbuf that has a cluster + * extension can not be used to contain data - only + * the cluster can contain data. + */ + dst = m; + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + + if (total_len >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + + m->m_len = 0; + dst->m_next = m; + amount = min(total_len, M_TRAILINGSPACE(m)); + } + + src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); + + m->m_len += amount; + total_len -= amount; + + } + return (m); +} +#endif diff --git a/sys/dev/ed/if_edreg.h b/sys/dev/ed/if_edreg.h new file mode 100644 index 0000000..f75e261 --- /dev/null +++ b/sys/dev/ed/if_edreg.h @@ -0,0 +1,962 @@ +/* + * National Semiconductor DS8390 NIC register definitions + * + * $Id: if_edreg.h,v 1.13 1994/02/02 14:05:58 davidg Exp $ + * + * Modification history + * + * Revision 2.2 1993/11/29 16:33:39 davidg + * From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk> + * Add support for the 8013W board type + * + * Revision 2.1 1993/11/22 10:52:33 davidg + * patch to add support for SMC8216 (Elite-Ultra) boards + * from Glen H. Lowe + * + * Revision 2.0 93/09/29 00:37:15 davidg + * changed double buffering flag to multi buffering + * made changes/additions for 3c503 multi-buffering + * ...companion to Rev. 2.0 of 'ed' driver. + * + * Revision 1.1 93/06/23 03:01:07 davidg + * Initial revision + * + */ + +/* + * Page 0 register offsets + */ +#define ED_P0_CR 0x00 /* Command Register */ + +#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */ +#define ED_P0_PSTART 0x01 /* Page Start register (write) */ + +#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */ +#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */ + +#define ED_P0_BNRY 0x03 /* Boundary Pointer */ + +#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */ +#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */ + +#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */ +#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */ + +#define ED_P0_FIFO 0x06 /* FIFO register (read) */ +#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */ + +#define ED_P0_ISR 0x07 /* Interrupt Status Register */ + +#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */ +#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */ + +#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */ +#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */ + +#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */ + +#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */ + +#define ED_P0_RSR 0x0c /* Receive Status (read) */ +#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */ + +#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */ +#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */ + +#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */ +#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */ + +#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */ +#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */ + +/* + * Page 1 register offsets + */ +#define ED_P1_CR 0x00 /* Command Register */ +#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */ +#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */ +#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */ +#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */ +#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */ +#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */ +#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */ +#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */ +#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */ +#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */ +#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */ +#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */ +#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */ +#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */ +#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */ + +/* + * Page 2 register offsets + */ +#define ED_P2_CR 0x00 /* Command Register */ +#define ED_P2_PSTART 0x01 /* Page Start (read) */ +#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */ +#define ED_P2_PSTOP 0x02 /* Page Stop (read) */ +#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */ +#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */ +#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */ +#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */ +#define ED_P2_ACU 0x06 /* Address Counter Upper */ +#define ED_P2_ACL 0x07 /* Address Counter Lower */ +#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */ +#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */ +#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */ +#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */ + +/* + * Command Register (CR) definitions + */ + +/* + * STP: SToP. Software reset command. Takes the controller offline. No + * packets will be received or transmitted. Any reception or + * transmission in progress will continue to completion before + * entering reset state. To exit this state, the STP bit must + * reset and the STA bit must be set. The software reset has + * executed only when indicated by the RST bit in the ISR being + * set. + */ +#define ED_CR_STP 0x01 + +/* + * STA: STArt. This bit is used to activate the NIC after either power-up, + * or when the NIC has been put in reset mode by software command + * or error. + */ +#define ED_CR_STA 0x02 + +/* + * TXP: Transmit Packet. This bit must be set to indicate transmission of + * a packet. TXP is internally reset either after the transmission is + * completed or aborted. This bit should be set only after the Transmit + * Byte Count and Transmit Page Start register have been programmed. + */ +#define ED_CR_TXP 0x04 + +/* + * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation + * of the remote DMA channel. RD2 can be set to abort any remote DMA + * command in progress. The Remote Byte Count registers should be cleared + * when a remote DMA has been aborted. The Remote Start Addresses are not + * restored to the starting address if the remote DMA is aborted. + * + * RD2 RD1 RD0 function + * 0 0 0 not allowed + * 0 0 1 remote read + * 0 1 0 remote write + * 0 1 1 send packet + * 1 X X abort + */ +#define ED_CR_RD0 0x08 +#define ED_CR_RD1 0x10 +#define ED_CR_RD2 0x20 + +/* + * PS0, PS1: Page Select. The two bits select which register set or 'page' to + * access. + * + * PS1 PS0 page + * 0 0 0 + * 0 1 1 + * 1 0 2 + * 1 1 reserved + */ +#define ED_CR_PS0 0x40 +#define ED_CR_PS1 0x80 +/* bit encoded aliases */ +#define ED_CR_PAGE_0 0x00 /* (for consistency) */ +#define ED_CR_PAGE_1 0x40 +#define ED_CR_PAGE_2 0x80 + +/* + * Interrupt Status Register (ISR) definitions + */ + +/* + * PRX: Packet Received. Indicates packet received with no errors. + */ +#define ED_ISR_PRX 0x01 + +/* + * PTX: Packet Transmitted. Indicates packet transmitted with no errors. + */ +#define ED_ISR_PTX 0x02 + +/* + * RXE: Receive Error. Indicates that a packet was received with one or more + * the following errors: CRC error, frame alignment error, FIFO overrun, + * missed packet. + */ +#define ED_ISR_RXE 0x04 + +/* + * TXE: Transmission Error. Indicates that an attempt to transmit a packet + * resulted in one or more of the following errors: excessive + * collisions, FIFO underrun. + */ +#define ED_ISR_TXE 0x08 + +/* + * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network + * would exceed (has exceeded?) the boundry pointer, resulting in data + * that was previously received and not yet read from the buffer to be + * overwritten. + */ +#define ED_ISR_OVW 0x10 + +/* + * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley + * Counters has been set. + */ +#define ED_ISR_CNT 0x20 + +/* + * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed. + */ +#define ED_ISR_RDC 0x40 + +/* + * RST: Reset status. Set when the NIC enters the reset state and cleared when a + * Start Command is issued to the CR. This bit is also set when a receive + * ring-buffer overrun (OverWrite) occurs and is cleared when one or more + * packets have been removed from the ring. This is a read-only bit. + */ +#define ED_ISR_RST 0x80 + +/* + * Interrupt Mask Register (IMR) definitions + */ + +/* + * PRXE: Packet Received interrupt Enable. If set, a received packet will cause + * an interrupt. + */ +#define ED_IMR_PRXE 0x01 + +/* + * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when + * a packet transmission completes. + */ +#define ED_IMR_PTXE 0x02 + +/* + * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a + * packet is received with an error. + */ +#define ED_IMR_RXEE 0x04 + +/* + * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever + * a transmission results in an error. + */ +#define ED_IMR_TXEE 0x08 + +/* + * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever + * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded. + */ +#define ED_IMR_OVWE 0x10 + +/* + * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever + * the MSB of one or more of the Network Statistics counters has been set. + */ +#define ED_IMR_CNTE 0x20 + +/* + * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated + * when a remote DMA transfer has completed. + */ +#define ED_IMR_RDCE 0x40 + +/* + * bit 7 is unused/reserved + */ + +/* + * Data Configuration Register (DCR) definitions + */ + +/* + * WTS: Word Transfer Select. WTS establishes byte or word transfers for + * both remote and local DMA transfers + */ +#define ED_DCR_WTS 0x01 + +/* + * BOS: Byte Order Select. BOS sets the byte order for the host. + * Should be 0 for 80x86, and 1 for 68000 series processors + */ +#define ED_DCR_BOS 0x02 + +/* + * LAS: Long Address Select. When LAS is 1, the contents of the remote + * DMA registers RSAR0 and RSAR1 are used to provide A16-A31 + */ +#define ED_DCR_LAS 0x04 + +/* + * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2 + * of the TCR must also be programmed for loopback operation. + * When 1, normal operation is selected. + */ +#define ED_DCR_LS 0x08 + +/* + * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer + * under program control. When 1, remote DMA is automatically initiated + * and the boundry pointer is automatically updated + */ +#define ED_DCR_AR 0x10 + +/* + * FT0, FT1: Fifo Threshold select. + * FT1 FT0 Word-width Byte-width + * 0 0 1 word 2 bytes + * 0 1 2 words 4 bytes + * 1 0 4 words 8 bytes + * 1 1 8 words 12 bytes + * + * During transmission, the FIFO threshold indicates the number of bytes + * or words that the FIFO has filled from the local DMA before BREQ is + * asserted. The transmission threshold is 16 bytes minus the receiver + * threshold. + */ +#define ED_DCR_FT0 0x20 +#define ED_DCR_FT1 0x40 + +/* + * bit 7 (0x80) is unused/reserved + */ + +/* + * Transmit Configuration Register (TCR) definitions + */ + +/* + * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC + * is not appended by the transmitter. + */ +#define ED_TCR_CRC 0x01 + +/* + * LB0, LB1: Loopback control. These two bits set the type of loopback that is + * to be performed. + * + * LB1 LB0 mode + * 0 0 0 - normal operation (DCR_LS = 0) + * 0 1 1 - internal loopback (DCR_LS = 0) + * 1 0 2 - external loopback (DCR_LS = 1) + * 1 1 3 - external loopback (DCR_LS = 0) + */ +#define ED_TCR_LB0 0x02 +#define ED_TCR_LB1 0x04 + +/* + * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows + * another station to disable the NIC's transmitter by transmitting to + * a multicast address hashing to bit 62. Reception of a multicast address + * hashing to bit 63 enables the transmitter. + */ +#define ED_TCR_ATD 0x08 + +/* + * OFST: Collision Offset enable. This bit when set modifies the backoff + * algorithm to allow prioritization of nodes. + */ +#define ED_TCR_OFST 0x10 + +/* + * bits 5, 6, and 7 are unused/reserved + */ + +/* + * Transmit Status Register (TSR) definitions + */ + +/* + * PTX: Packet Transmitted. Indicates successful transmission of packet. + */ +#define ED_TSR_PTX 0x01 + +/* + * bit 1 (0x02) is unused/reserved + */ + +/* + * COL: Transmit Collided. Indicates that the transmission collided at least + * once with another station on the network. + */ +#define ED_TSR_COL 0x04 + +/* + * ABT: Transmit aborted. Indicates that the transmission was aborted due to + * excessive collisions. + */ +#define ED_TSR_ABT 0x08 + +/* + * CRS: Carrier Sense Lost. Indicates that carrier was lost during the + * transmission of the packet. (Transmission is not aborted because + * of a loss of carrier) + */ +#define ED_TSR_CRS 0x10 + +/* + * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/ + * transmission memory before the FIFO emptied. Transmission of the + * packet was aborted. + */ +#define ED_TSR_FU 0x20 + +/* + * CDH: CD Heartbeat. Indicates that the collision detection circuitry + * isn't working correctly during a collision heartbeat test. + */ +#define ED_TSR_CDH 0x40 + +/* + * OWC: Out of Window Collision: Indicates that a collision occurred after + * a slot time (51.2us). The transmission is rescheduled just as in + * normal collisions. + */ +#define ED_TSR_OWC 0x80 + +/* + * Receiver Configuration Register (RCR) definitions + */ + +/* + * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1, + * packets with CRC and frame errors are not discarded. + */ +#define ED_RCR_SEP 0x01 + +/* + * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded. + * If set to 1, packets with less than 64 byte are not discarded. + */ +#define ED_RCR_AR 0x02 + +/* + * AB: Accept Broadcast. If set, packets sent to the broadcast address will be + * accepted. + */ +#define ED_RCR_AB 0x04 + +/* + * AM: Accept Multicast. If set, packets sent to a multicast address are checked + * for a match in the hashing array. If clear, multicast packets are ignored. + */ +#define ED_RCR_AM 0x08 + +/* + * PRO: Promiscuous Physical. If set, all packets with a physical addresses are + * accepted. If clear, a physical destination address must match this + * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM + * must also be set. In addition, the multicast hashing array must be set + * to all 1's so that all multicast addresses are accepted. + */ +#define ED_RCR_PRO 0x10 + +/* + * MON: Monitor Mode. If set, packets will be checked for good CRC and framing, + * but are not stored in the ring-buffer. If clear, packets are stored (normal + * operation). + */ +#define ED_RCR_MON 0x20 + +/* + * bits 6 and 7 are unused/reserved. + */ + +/* + * Receiver Status Register (RSR) definitions + */ + +/* + * PRX: Packet Received without error. + */ +#define ED_RSR_PRX 0x01 + +/* + * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame + * alignment errors. + */ +#define ED_RSR_CRC 0x02 + +/* + * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on + * a byte boundry and the CRC did not match at the last byte boundry. + */ +#define ED_RSR_FAE 0x04 + +/* + * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA) + * causing it to overrun. Reception of the packet is aborted. + */ +#define ED_RSR_FO 0x08 + +/* + * MPA: Missed Packet. Indicates that the received packet couldn't be stored in + * the ring-buffer because of insufficient buffer space (exceeding the + * boundry pointer), or because the transfer to the ring-buffer was inhibited + * by RCR_MON - monitor mode. + */ +#define ED_RSR_MPA 0x10 + +/* + * PHY: Physical address. If 0, the packet received was sent to a physical address. + * If 1, the packet was accepted because of a multicast/broadcast address + * match. + */ +#define ED_RSR_PHY 0x20 + +/* + * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor + * mode. Cleared when the receiver exits monitor mode. + */ +#define ED_RSR_DIS 0x40 + +/* + * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs + * are active, and the transceiver has set the CD line as a result of the + * jabber. + */ +#define ED_RSR_DFR 0x80 + +/* + * receive ring discriptor + * + * The National Semiconductor DS8390 Network interface controller uses + * the following receive ring headers. The way this works is that the + * memory on the interface card is chopped up into 256 bytes blocks. + * A contiguous portion of those blocks are marked for receive packets + * by setting start and end block #'s in the NIC. For each packet that + * is put into the receive ring, one of these headers (4 bytes each) is + * tacked onto the front. + */ +struct ed_ring { + struct edr_status { /* received packet status */ + u_char rs_prx:1, /* packet received intack */ + rs_crc:1, /* crc error */ + rs_fae:1, /* frame alignment error */ + rs_fo:1, /* fifo overrun */ + rs_mpa:1, /* packet received intack */ + rs_phy:1, /* packet received intack */ + rs_dis:1, /* packet received intack */ + rs_dfr:1; /* packet received intack */ + } ed_rcv_status; /* received packet status */ + u_char next_packet; /* pointer to next packet */ + u_short count; /* bytes in packet (length + 4) */ +}; + +/* + * Common constants + */ +#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */ +#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */ + +/* + * Vendor types + */ +#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */ +#define ED_VENDOR_3COM 0x01 /* 3Com */ +#define ED_VENDOR_NOVELL 0x02 /* Novell */ + +/* + * Compile-time config flags + */ +/* + * this sets the default for enabling/disablng the tranceiver + */ +#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001 + +/* + * This forces the board to be used in 8/16bit mode even if it + * autoconfigs differently + */ +#define ED_FLAGS_FORCE_8BIT_MODE 0x0002 +#define ED_FLAGS_FORCE_16BIT_MODE 0x0004 + +/* + * This disables the use of double transmit buffers. + */ +#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008 + +/* + * This forces all operations with the NIC memory to use Programmed + * I/O (i.e. not via shared memory) + */ +#define ED_FLAGS_FORCE_PIO 0x0010 + +/* + * Definitions for Western digital/SMC WD80x3 series ASIC + */ +/* + * Memory Select Register (MSR) + */ +#define ED_WD_MSR 0 + +/* next three definitions for Toshiba */ +#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */ +#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */ +#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits, + 1 = 8 bits (R/W) */ +#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */ +#define ED_WD_MSR_MENB 0x40 /* Memory enable */ +#define ED_WD_MSR_RST 0x80 /* Reset board */ + +/* + * Interface Configuration Register (ICR) + */ +#define ED_WD_ICR 1 + +#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */ +#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */ +#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */ +#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */ +#define ED_WD_ICR_RLA 0x10 /* recall LAN address */ +#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */ +#define ED_WD_ICR_RIO 0x40 /* recall i/o address */ +#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */ +#ifdef TOSH_ETHER +#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */ +#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K, + 0x02 = 16K, 0x01 = 8K */ + /* 64K can only be used if mem address + above 1Mb */ + /* IAR holds address A23-A16 (R/W) */ +#endif + +/* + * IO Address Register (IAR) + */ +#define ED_WD_IAR 2 + +/* + * EEROM Address Register + */ +#define ED_WD_EAR 3 + +/* + * Interrupt Request Register (IRR) + */ +#define ED_WD_IRR 4 + +#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */ +#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */ +#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */ +#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */ +#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */ + +/* + * The three bits of the encoded IRQ are decoded as follows: + * + * IR2 IR1 IR0 IRQ + * 0 0 0 2/9 + * 0 0 1 3 + * 0 1 0 5 + * 0 1 1 7 + * 1 0 0 10 + * 1 0 1 11 + * 1 1 0 15 + * 1 1 1 4 + */ +#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */ +#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */ +#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */ + +/* + * LA Address Register (LAAR) + */ +#define ED_WD_LAAR 5 + +#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */ +#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */ +#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */ +#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */ + +/* i/o base offset to station address/card-ID PROM */ +#define ED_WD_PROM 8 + +/* + * 83C790 specific registers + */ +/* + * Hardware Support Register (HWR) ('790) + */ +#define ED_WD790_HWR 4 + +#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */ +#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */ +#define ED_WD790_HWR_SWH 0x80 /* switch register set */ + +/* + * ICR790 Interrupt Control Register for the 83C790 + */ +#define ED_WD790_ICR 6 + +#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */ + +/* + * General Control Register (GCR) + * Enabled with SWH bit=1 in HWR register + */ +#define ED_WD790_GCR 0x0d + +#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */ +#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */ +#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */ +#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */ +/* + * The three bits of the encoded IRQ are decoded as follows: + * + * IR2 IR1 IR0 IRQ + * 0 0 0 none + * 0 0 1 9 + * 0 1 0 3 + * 0 1 1 5 + * 1 0 0 7 + * 1 0 1 10 + * 1 1 0 11 + * 1 1 1 15 + */ + +/* i/o base offset to CARD ID */ +#define ED_WD_CARD_ID ED_WD_PROM+6 + +/* Board type codes in card ID */ +#define ED_TYPE_WD8003S 0x02 +#define ED_TYPE_WD8003E 0x03 +#define ED_TYPE_WD8013EBT 0x05 +#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */ +#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */ +#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */ +#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */ +#define ED_TYPE_WD8003W 0x24 +#define ED_TYPE_WD8003EB 0x25 +#define ED_TYPE_WD8013W 0x26 +#define ED_TYPE_WD8013EP 0x27 +#define ED_TYPE_WD8013WC 0x28 +#define ED_TYPE_WD8013EPC 0x29 +#define ED_TYPE_SMC8216T 0x2a +#define ED_TYPE_SMC8216C 0x2b +#define ED_TYPE_WD8013EBP 0x2c + +/* Bit definitions in card ID */ +#define ED_WD_REV_MASK 0x1f /* Revision mask */ +#define ED_WD_SOFTCONFIG 0x20 /* Soft config */ +#define ED_WD_LARGERAM 0x40 /* Large RAM */ +#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */ + +/* + * Checksum total. All 8 bytes in station address PROM will add up to this + */ +#ifdef TOSH_ETHER +#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5 +#else +#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF +#endif + +#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */ +#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */ +#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */ + +#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */ + +/* + * Definitions for 3Com 3c503 + */ +#define ED_3COM_NIC_OFFSET 0 +#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */ + +/* + * XXX - The I/O address range is fragmented in the 3c503; this is the + * number of regs at iobase. + */ +#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */ + +/* tx memory starts in second bank on 8bit cards */ +#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20 + +/* tx memory starts in first bank on 16bit cards */ +#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0 + +/* ...and rx memory starts in second bank */ +#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20 + + +/* + * Page Start Register. Must match PSTART in NIC + */ +#define ED_3COM_PSTR 0 + +/* + * Page Stop Register. Must match PSTOP in NIC + */ +#define ED_3COM_PSPR 1 + +/* + * Drq Timer Register. Determines number of bytes to be transfered during + * a DMA burst. + */ +#define ED_3COM_DQTR 2 + +/* + * Base Configuration Register. Read-only register which contains the + * board-configured I/O base address of the adapter. Bit encoded. + */ +#define ED_3COM_BCFR 3 + +#define ED_3COM_BCFR_2E0 0x01 +#define ED_3COM_BCFR_2A0 0x02 +#define ED_3COM_BCFR_280 0x04 +#define ED_3COM_BCFR_250 0x08 +#define ED_3COM_BCFR_350 0x10 +#define ED_3COM_BCFR_330 0x20 +#define ED_3COM_BCFR_310 0x40 +#define ED_3COM_BCFR_300 0x80 + +/* + * EPROM Configuration Register. Read-only register which contains the + * board-configured memory base address. Bit encoded. + */ +#define ED_3COM_PCFR 4 + +#define ED_3COM_PCFR_C8000 0x10 +#define ED_3COM_PCFR_CC000 0x20 +#define ED_3COM_PCFR_D8000 0x40 +#define ED_3COM_PCFR_DC000 0x80 + +/* + * GA Configuration Register. Gate-Array Configuration Register. + */ +#define ED_3COM_GACFR 5 + +/* + * mbs2 mbs1 mbs0 start address + * 0 0 0 0x0000 + * 0 0 1 0x2000 + * 0 1 0 0x4000 + * 0 1 1 0x6000 + * + * Note that with adapters with only 8K, the setting for 0x2000 must + * always be used. + */ +#define ED_3COM_GACFR_MBS0 0x01 +#define ED_3COM_GACFR_MBS1 0x02 +#define ED_3COM_GACFR_MBS2 0x04 + +#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */ +#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */ +#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */ +#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */ +#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */ + +/* + * Control Register. Miscellaneous control functions. + */ +#define ED_3COM_CR 6 + +#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */ +#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */ +#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */ +#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */ +#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */ +#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */ +#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */ +#define ED_3COM_CR_START 0x80 /* Start DMA controller */ + +/* + * Status Register. Miscellaneous status information. + */ +#define ED_3COM_STREG 7 + +#define ED_3COM_STREG_REV 0x07 /* GA revision */ +#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */ +#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */ +#define ED_3COM_STREG_OFLW 0x20 /* Overflow */ +#define ED_3COM_STREG_UFLW 0x40 /* Underflow */ +#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */ + +/* + * Interrupt/DMA Configuration Register + */ +#define ED_3COM_IDCFR 8 + +#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */ +#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */ +#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */ +#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */ +#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */ +#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */ +#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */ +#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */ + +/* + * DMA Address Register MSB + */ +#define ED_3COM_DAMSB 9 + +/* + * DMA Address Register LSB + */ +#define ED_3COM_DALSB 0x0a + +/* + * Vector Pointer Register 2 + */ +#define ED_3COM_VPTR2 0x0b + +/* + * Vector Pointer Register 1 + */ +#define ED_3COM_VPTR1 0x0c + +/* + * Vector Pointer Register 0 + */ +#define ED_3COM_VPTR0 0x0d + +/* + * Register File Access MSB + */ +#define ED_3COM_RFMSB 0x0e + +/* + * Register File Access LSB + */ +#define ED_3COM_RFLSB 0x0f + +/* + * Definitions for Novell NE1000/2000 boards + */ + +/* + * Board type codes + */ +#define ED_TYPE_NE1000 0x01 +#define ED_TYPE_NE2000 0x02 + +/* + * Register offsets/total + */ +#define ED_NOVELL_NIC_OFFSET 0x00 +#define ED_NOVELL_ASIC_OFFSET 0x10 +#define ED_NOVELL_IO_PORTS 32 + +/* + * Remote DMA data register; for reading or writing to the NIC mem + * via programmed I/O (offset from ASIC base) + */ +#define ED_NOVELL_DATA 0x00 + +/* + * Reset register; reading from this register causes a board reset + */ +#define ED_NOVELL_RESET 0x0f diff --git a/sys/dev/ep/if_ep.c b/sys/dev/ep/if_ep.c new file mode 100644 index 0000000..b47f829 --- /dev/null +++ b/sys/dev/ep/if_ep.c @@ -0,0 +1,993 @@ +/* + * Copyright (c) 1993 Herb Peyerl <hpeyerl@novatel.ca> + * 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. 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. + * + * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ + * $Id: if_ep.c,v 1.8 1994/03/15 01:58:22 wollman Exp $ + */ + +#include "ep.h" +#if NEP > 0 + +#include "bpfilter.h" + +#include <sys/param.h> +#if defined(__FreeBSD__) +#include <sys/systm.h> +#include <sys/kernel.h> +#endif +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#if defined(__NetBSD__) +#include <sys/select.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/pio.h> + +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> +#include <i386/isa/if_epreg.h> + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + +/* + * Ethernet software status per interface. + */ +struct ep_softc { + struct arpcom arpcom; /* Ethernet common part */ + short ep_io_addr; /* i/o bus address */ + char ep_connectors; /* Connectors on this card. */ +#define MAX_MBS 8 /* # of mbufs we keep around */ + struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ + int next_mb; /* Which mbuf to use next. */ + int last_mb; /* Last mbuf. */ + int tx_start_thresh; /* Current TX_start_thresh. */ + caddr_t bpf; /* BPF "magic cookie" */ + char bus32bit; /* 32bit access possible */ +} ep_softc[NEP]; + +static int epprobe __P((struct isa_device *)); +static int epattach __P((struct isa_device *)); +static int epioctl __P((struct ifnet * ifp, int, caddr_t)); + +void epinit __P((int)); +void epintr __P((int)); +void epmbuffill __P((caddr_t)); +void epmbufempty __P((struct ep_softc *)); +void epread __P((struct ep_softc *)); +void epreset __P((int)); +void epstart __P((struct ifnet *)); +void epstop __P((int)); +void epwatchdog __P((int)); + +struct isa_driver epdriver = { + epprobe, + epattach, + "ep" +}; + +static int send_ID_sequence __P((u_short)); +static u_short get_eeprom_data __P((int, int)); +static int is_eeprom_busy __P((struct isa_device *)); + +/* + * Rudimentary support for multiple cards is here but is not + * currently handled. In the future we will have to add code + * for tagging the cards for later activation. We wanna do something + * about the id_port. We're limited due to current config procedure. + * Magnum config holds promise of a fix but we'll have to wait a bit. + */ +int +epprobe(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + u_short k; + int id_port = 0x100; /* XXX */ + + outw(BASE + EP_COMMAND, GLOBAL_RESET); + DELAY(1000); + outb(id_port, 0xc0); /* Global reset to id_port. */ + DELAY(1000); + send_ID_sequence(id_port); + DELAY(1000); + + /* + * MFG_ID should have 0x6d50. + * PROD_ID should be 0x9[0-f]50 + */ + k = get_eeprom_data(id_port, EEPROM_MFG_ID); + if (k != MFG_ID) + return (0); + k = get_eeprom_data(id_port, EEPROM_PROD_ID); + if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) + return (0); + + k = get_eeprom_data(id_port, EEPROM_ADDR_CFG); /* get addr cfg */ + k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */ + if (k != (u_short)is->id_iobase) + return (0); + + k = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG); + k >>= 12; + if (is->id_irq != (1 << ((k == 2) ? 9 : k))) + return (0); + + outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); + + return (0x10); /* 16 bytes of I/O space used. */ +} + +static int +epattach(is) + struct isa_device *is; +{ + struct ep_softc *sc = &ep_softc[is->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + u_short i; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + sc->ep_io_addr = is->id_iobase; + + printf("ep%d: ", is->id_unit); + + sc->ep_connectors = 0; + i = inw(is->id_iobase + EP_W0_CONFIG_CTRL); + if (i & IS_AUI) { + printf("aui"); + sc->ep_connectors |= AUI; + } + if (i & IS_BNC) { + if (sc->ep_connectors) + printf("/"); + printf("bnc"); + sc->ep_connectors |= BNC; + } + if (i & IS_UTP) { + if (sc->ep_connectors) + printf("/"); + printf("utp"); + sc->ep_connectors |= UTP; + } + if (!sc->ep_connectors) + printf("no connectors!"); + + /* + * Read the station address from the eeprom + */ + for (i = 0; i < 3; i++) { + u_short *p; + GO_WINDOW(0); + if (is_eeprom_busy(is)) + return(0); + outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i); + if (is_eeprom_busy(is)) + return(0); + p =(u_short *)&sc->arpcom.ac_enaddr[i*2]; + *p = htons(inw(BASE + EP_W0_EEPROM_DATA)); + GO_WINDOW(2); + outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(*p)); + } + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); + + ifp->if_unit = is->id_unit; + ifp->if_name = "ep"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_init = epinit; + ifp->if_output = ether_output; + ifp->if_start = epstart; + ifp->if_ioctl = epioctl; + ifp->if_watchdog = epwatchdog; + + if_attach(ifp); + + /* + * Fill the hardware address into ifa_addr if we find an + * AF_LINK entry. We need to do this so bpf's can get the hardware + * addr of this card. netstat likes this too! + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; +} + + +/* + * The order in here seems important. Otherwise we may not receive + * interrupts. ?! + */ +void +epinit(unit) + int unit; +{ + register struct ep_softc *sc = &ep_softc[unit]; + register struct ifnet *ifp = &sc->arpcom.ac_if; + int s, i; + + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; + + s = splimp(); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + + GO_WINDOW(0); + + /* Disable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, 0); + + /* Enable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + + GO_WINDOW(2); + + /* Reload the ether_addr. */ + for (i = 0; i < 6; i++) + outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); + + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(BASE + EP_COMMAND, ACK_INTR | 0xff); + + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL); + + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); + + /* + * you can `ifconfig (link0|-link0) ep0' to get the following + * behaviour: + * -link0 disable AUI/UTP. enable BNC. + * link0 disable BNC. enable AUI. if the card has a UTP + * connector, that is enabled too. not sure, but it + * seems you have to be careful to not plug things + * into both AUI & UTP. + */ +#if defined(__NetBSD__) + if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { +#else + if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { +#endif + outw(BASE + EP_COMMAND, START_TRANSCEIVER); + DELAY(1000); + } +#if defined(__NetBSD__) + if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { +#else + if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { +#endif + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); + GO_WINDOW(1); + } + + outw(BASE + EP_COMMAND, RX_ENABLE); + outw(BASE + EP_COMMAND, TX_ENABLE); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ + sc->tx_start_thresh = 20; /* probably a good starting point. */ + /* + * Store up a bunch of mbuf's for use later. (MAX_MBS). First we + * free up any that we had in case we're being called from intr or + * somewhere else. + */ + sc->last_mb = 0; + sc->next_mb = 0; + epmbuffill((caddr_t)sc, 0); + + epstart(ifp); + + splx(s); +} + +static const char padmap[] = {0, 3, 2, 1}; + +void +epstart(ifp) + struct ifnet *ifp; +{ + register struct ep_softc *sc = &ep_softc[ifp->if_unit]; + struct mbuf *m, *top; + int s, len, pad; + + s = splimp(); + if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { + splx(s); + return; + } + +startagain: + /* Sneak a peek at the next packet */ + m = sc->arpcom.ac_if.if_snd.ifq_head; + if (m == 0) { + splx(s); + return; + } +#if 0 + len = m->m_pkthdr.len; +#else + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; +#endif + + pad = padmap[len & 3]; + + /* + * The 3c509 automatically pads short packets to minimum ethernet + * length, but we drop packets that are too large. Perhaps we should + * truncate them instead? + */ + if (len + pad > ETHER_MAX_LEN) { + /* packet is obviously too large: toss it */ + ++sc->arpcom.ac_if.if_oerrors; + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + m_freem(m); + goto readcheck; + } + + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + splx(s); + return; + } + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == 0) { /* not really needed */ + splx(s); + return; + } + outw(BASE + EP_COMMAND, SET_TX_START_THRESH | + (len / 4 + sc->tx_start_thresh)); + + outw(BASE + EP_W1_TX_PIO_WR_1, len); + outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ + + for (top = m; m != 0; m = m->m_next) { + if (sc->bus32bit) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len/4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len/4, + m->m_len & 3); + } else { + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); + } + } + while (pad--) + outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ + +#if NBPFILTER > 0 + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(top, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + eh->ether_type = etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(top, off, sizeof(struct trailer_header), + (caddr_t)&trailer_header.ether_type); + + /* copy residual data */ + resid = trailer_header.ether_residual - + sizeof(struct trailer_header); + resid = ntohs(resid); + m_copydata(top, off + sizeof(struct trailer_header), + resid, ep); + ep += resid; + + /* copy data */ + m_copydata(top, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, top); + } +#endif + + m_freem(top); + ++sc->arpcom.ac_if.if_opackets; + + /* + * Is another packet coming in? We don't want to overflow the + * tiny RX fifo. + */ +readcheck: + if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { + splx(s); + return; + } + goto startagain; +} + +void +epintr(unit) + int unit; +{ + int status, i; + register struct ep_softc *sc = &ep_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + + status = 0; +checkintr: + status = inw(BASE + EP_STATUS) & + (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); + if (status == 0) { + /* No interrupts. */ + outw(BASE + EP_COMMAND, C_INTR_LATCH); + return; + } + /* important that we do this first. */ + outw(BASE + EP_COMMAND, ACK_INTR | status); + + if (status & S_TX_AVAIL) { + status &= ~S_TX_AVAIL; + inw(BASE + EP_W1_FREE_TX); + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + epstart(&sc->arpcom.ac_if); + } + if (status & S_RX_COMPLETE) { + status &= ~S_RX_COMPLETE; + epread(sc); + } + if (status & S_CARD_FAILURE) { + printf("ep%d: reset (status: %x)\n", unit, status); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + epinit(unit); + return; + } + if (status & S_TX_COMPLETE) { + status &= ~S_TX_COMPLETE; + /* + * We need to read TX_STATUS until we get a 0 status in + * order to turn off the interrupt flag. + */ + while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { + outw(BASE + EP_W1_TX_STATUS, 0x0); + if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) { + if (i & TXS_MAX_COLLISION) + ++sc->arpcom.ac_if.if_collisions; + if (i & (TXS_JABBER | TXS_UNDERRUN)) { + outw(BASE + EP_COMMAND, TX_RESET); + if (i & TXS_UNDERRUN) { + if (sc->tx_start_thresh < ETHER_MAX_LEN) { + sc->tx_start_thresh += 20; + outw(BASE + EP_COMMAND, + SET_TX_START_THRESH | + sc->tx_start_thresh); + } + } + } + outw(BASE + EP_COMMAND, TX_ENABLE); + ++sc->arpcom.ac_if.if_oerrors; + } + } + epstart(ifp); + } + goto checkintr; +} + +void +epread(sc) + register struct ep_softc *sc; +{ + struct ether_header *eh; + struct mbuf *mcur, *m, *m0, *top; + int totlen, lenthisone; + int save_totlen; + u_short etype; + int off, resid; + int count, spinwait; + int i; + + totlen = inw(BASE + EP_W1_RX_STATUS); + off = 0; + top = 0; + + if (totlen & ERR_RX) { + ++sc->arpcom.ac_if.if_ierrors; + goto out; + } + save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ + + m = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + + if (m == 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto out; + } else { + /* Convert one of our saved mbuf's */ + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } + + top = m0 = m; /* We assign top so we can "goto out" */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + m0->m_data += EOFF; + /* Read what should be the header. */ + insw(BASE + EP_W1_RX_PIO_RD_1, + mtod(m0, caddr_t), sizeof(struct ether_header) / 2); + m->m_len = sizeof(struct ether_header); + totlen -= sizeof(struct ether_header); + /* + * mostly deal with trailer here. (untested) + * We do this in a couple of parts. First we check for a trailer, if + * we have one we convert the mbuf back to a regular mbuf and set the offset and + * subtract sizeof(struct ether_header) from the pktlen. + * After we've read the packet off the interface (all except for the trailer + * header, we then get a header mbuf, read the trailer into it, and fix up + * the mbuf pointer chain. + */ + eh = mtod(m, struct ether_header *); + eh->ether_type = etype = ntohs((u_short) eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + m->m_data = m->m_dat; /* Convert back to regular mbuf. */ + m->m_flags = 0; /* This sucks but non-trailers are the norm */ + off = (etype - ETHERTYPE_TRAIL) * 512; + if (off >= ETHERMTU) { + m_freem(m); + return; /* sanity */ + } + totlen -= sizeof(struct ether_header); /* We don't read the trailer */ + m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + } + while (totlen > 0) { + lenthisone = min(totlen, M_TRAILINGSPACE(m)); + if (lenthisone == 0) { /* no room in this one */ + mcur = m; + m = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + if (!m) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto out; + } else { + timeout(epmbuffill, (caddr_t)sc, 0); + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + } + if (totlen >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + m->m_len = 0; + mcur->m_next = m; + lenthisone = min(totlen, M_TRAILINGSPACE(m)); + } + if (sc->bus32bit) { + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); + } else { + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + } + totlen -= lenthisone; + } + if (off) { + top = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + if (top == 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (top == 0) { + top = m0; + goto out; + } + } else { + /* Convert one of our saved mbuf's */ + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + top->m_data = top->m_pktdat; + top->m_flags = M_PKTHDR; + } + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), + sizeof(struct ether_header)); + top->m_next = m0; + top->m_len = sizeof(struct ether_header); + /* XXX Accomodate for type and len from beginning of trailer */ + top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short)); + } else { + top = m0; + top->m_pkthdr.len = save_totlen; + } + + top->m_pkthdr.rcvif = &sc->arpcom.ac_if; + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + ++sc->arpcom.ac_if.if_ipackets; +#if NBPFILTER > 0 + if (sc->bpf) { + bpf_mtap(sc->bpf, top); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + (eh->ether_dhost[0] & 1) == 0 && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + m_freem(top); + return; + } + } +#endif + m_adj(top, sizeof(struct ether_header)); + ether_input(&sc->arpcom.ac_if, eh, top); + return; + +out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + if (top) + m_freem(top); + +} + + +/* + * Look familiar? + */ +static int +epioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *) data; + struct ep_softc *sc = &ep_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + epinit(ifp->if_unit); /* before arpwhohas */ + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + epinit(ifp->if_unit); + break; + } +#endif + default: + epinit(ifp->if_unit); + break; + } + break; + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + epstop(ifp->if_unit); + epmbufempty(sc); + break; + } + if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) + epinit(ifp->if_unit); + break; +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + default: + error = EINVAL; + } + return (error); +} + +void +epreset(unit) + int unit; +{ + int s = splimp(); + + epstop(unit); + epinit(unit); + splx(s); +} + +void +epwatchdog(unit) + int unit; +{ + struct ep_softc *sc = &ep_softc[unit]; + + log(LOG_ERR, "ep%d: watchdog\n", unit); + ++sc->arpcom.ac_if.if_oerrors; + + epreset(unit); +} + +void +epstop(unit) + int unit; +{ + struct ep_softc *sc = &ep_softc[unit]; + + outw(BASE + EP_COMMAND, RX_DISABLE); + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + outw(BASE + EP_COMMAND, TX_DISABLE); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + outw(BASE + EP_COMMAND, SET_RD_0_MASK); + outw(BASE + EP_COMMAND, SET_INTR_MASK); + outw(BASE + EP_COMMAND, SET_RX_FILTER); +} + + +/* + * This is adapted straight from the book. There's probably a better way. + */ +static int +send_ID_sequence(port) + u_short port; +{ + char cx, al; + + cx = 0x0ff; + al = 0x0ff; + + outb(port, 0x0); + DELAY(1000); + outb(port, 0x0); + DELAY(1000); + +loop1: cx--; + outb(port, al); + if (!(al & 0x80)) { + al = al << 1; + goto loop1; + } + al = al << 1; + al ^= 0xcf; + if (cx) + goto loop1; + + return(1); +} + + +/* + * We get eeprom data from the id_port given an offset into the + * eeprom. Basically; after the ID_sequence is sent to all of + * the cards; they enter the ID_CMD state where they will accept + * command requests. 0x80-0xbf loads the eeprom data. We then + * read the port 16 times and with every read; the cards check + * for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; + * each card compares the data on the bus; if there is a difference + * then that card goes into ID_WAIT state again). In the meantime; + * one bit of data is returned in the AX register which is conveniently + * returned to us by inb(). Hence; we read 16 times getting one + * bit of data with each read. + */ +static u_short +get_eeprom_data(id_port, offset) + int id_port; + int offset; +{ + int i, data = 0; + outb(id_port, 0x80 + offset); + DELAY(1000); + for (i = 0; i < 16; i++) + data = (data << 1) | (inw(id_port) & 1); + return (data); +} + +static int +is_eeprom_busy(is) + struct isa_device *is; +{ + int i = 0, j; + register struct ep_softc *sc = &ep_softc[is->id_unit]; + + while (i++ < 100) { + j = inw(BASE + EP_W0_EEPROM_COMMAND); + if (j & EEPROM_BUSY) + DELAY(100); + else + break; + } + if (i >= 100) { + printf("\nep%d: eeprom failed to come ready.\n", is->id_unit); + return (1); + } + if (j & EEPROM_TST_MODE) { + printf("\nep%d: 3c509 in test mode. Erase pencil mark!\n", is->id_unit); + return (1); + } + return (0); +} + +void +epmbuffill(sp) + caddr_t sp; +{ + struct ep_softc *sc = (struct ep_softc *)sp; + int s, i; + + s = splimp(); + i = sc->last_mb; + do { + if(sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if(sc->mb[i] == NULL) + break; + i = (i + 1) % MAX_MBS; + } while (i != sc->next_mb); + sc->last_mb = i; + splx(s); +} + +static void +epmbufempty(sc) + struct ep_softc *sc; +{ + int s, i; + + s = splimp(); + for (i = 0; i<MAX_MBS; i++) { + if (sc->mb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; + } + } + sc->last_mb = sc->next_mb = 0; + untimeout(epmbuffill, sc); + splx(s); +} + +#endif /* NEP > 0 */ diff --git a/sys/dev/ep/if_epreg.h b/sys/dev/ep/if_epreg.h new file mode 100644 index 0000000..f0b4cd9 --- /dev/null +++ b/sys/dev/ep/if_epreg.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + * + * $Id: if_epreg.h,v 1.1 1993/12/14 04:26:47 hpeyerl Exp $ + */ +/************************************************************************** + * * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existance of the adapter after having sent + * the ID_Sequence. + * + * There are others but only the ones we use are defined here. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ + +/************************************************************************** + * * + * These are the registers for the 3Com 3c509 and their bit patterns when * + * applicable. They have been taken out the the "EtherLink III Parallel * + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * + * from 3com. * + * * + **************************************************************************/ + +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */ +/* + * Window 0 registers. Setup. + */ + /* Write */ +#define EP_W0_EEPROM_DATA 0x0c +#define EP_W0_EEPROM_COMMAND 0x0a +#define EP_W0_RESOURCE_CFG 0x08 +#define EP_W0_ADDRESS_CFG 0x06 +#define EP_W0_CONFIG_CTRL 0x04 + /* Read */ +#define EP_W0_PRODUCT_ID 0x02 +#define EP_W0_MFG_ID 0x00 + +/* + * Window 1 registers. Operating Set. + */ + /* Write */ +#define EP_W1_TX_PIO_WR_2 0x02 +#define EP_W1_TX_PIO_WR_1 0x00 + /* Read */ +#define EP_W1_FREE_TX 0x0c +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_RX_STATUS 0x08 +#define EP_W1_RX_PIO_RD_2 0x02 +#define EP_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ + /* Read/Write */ +#define EP_W2_ADDR_5 0x05 +#define EP_W2_ADDR_4 0x04 +#define EP_W2_ADDR_3 0x03 +#define EP_W2_ADDR_2 0x02 +#define EP_W2_ADDR_1 0x01 +#define EP_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ + /* Read */ +#define EP_W3_FREE_TX 0x0c +#define EP_W3_FREE_RX 0x0a + +/* + * Window 4 registers. Diagnostics. + */ + /* Read/Write */ +#define EP_W4_MEDIA_TYPE 0x0a +#define EP_W4_CTRLR_STATUS 0x08 +#define EP_W4_NET_DIAG 0x06 +#define EP_W4_FIFO_DIAG 0x04 +#define EP_W4_HOST_DIAG 0x02 +#define EP_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ + /* Read */ +#define EP_W5_READ_0_MASK 0x0c +#define EP_W5_INTR_MASK 0x0a +#define EP_W5_RX_FILTER 0x08 +#define EP_W5_RX_EARLY_THRESH 0x06 +#define EP_W5_TX_AVAIL_THRESH 0x02 +#define EP_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ + /* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */ +#define WINDOW_SELECT (u_short) (0x1<<11) +#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine + whether this is needed. If so; + wait 800 uSec before using trans- + ceiver. */ +#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */ +#define RX_ENABLE (u_short) (0x4<<11) +#define RX_RESET (u_short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) +#define TX_ENABLE (u_short) (0x9<<11) +#define TX_DISABLE (u_short) (0xa<<11) +#define TX_RESET (u_short) (0xb<<11) +#define REQ_INTR (u_short) (0xc<<11) + /* + * The following C_* acknowledge the various interrupts. + * Some of them don't do anything. See the manual. + */ +#define ACK_INTR (u_short) (0x6800) +# define C_INTR_LATCH (u_short) (ACK_INTR|0x1) +# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) +# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) +# define C_TX_AVAIL (u_short) (ACK_INTR|0x8) +# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) +# define C_RX_EARLY (u_short) (ACK_INTR|0x20) +# define C_INT_RQD (u_short) (ACK_INTR|0x40) +# define C_UPD_STATS (u_short) (ACK_INTR|0x80) +#define SET_INTR_MASK (u_short) (0xe<<11) +#define SET_RD_0_MASK (u_short) (0xf<<11) +#define SET_RX_FILTER (u_short) (0x10<<11) +# define FIL_INDIVIDUAL (u_short) (0x1) +# define FIL_GROUP (u_short) (0x2) +# define FIL_BRDCST (u_short) (0x4) +# define FIL_ALL (u_short) (0x8) +#define SET_RX_EARLY_THRESH (u_short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) +#define SET_TX_START_THRESH (u_short) (0x13<<11) +#define STATS_ENABLE (u_short) (0x15<<11) +#define STATS_DISABLE (u_short) (0x16<<11) +#define STOP_TRANSCEIVER (u_short) (0x17<<11) + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (u_short) (0x1) +#define S_CARD_FAILURE (u_short) (0x2) +#define S_TX_COMPLETE (u_short) (0x4) +#define S_TX_AVAIL (u_short) (0x8) +#define S_RX_COMPLETE (u_short) (0x10) +#define S_RX_EARLY (u_short) (0x20) +#define S_INT_RQD (u_short) (0x40) +#define S_UPD_STATS (u_short) (0x80) +#define S_COMMAND_IN_PROGRESS (u_short) (0x1000) + +/* + * FIFO Registers. RX Status. + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_INCOMPLETE (u_short) (0x8000) +#define ERR_RX (u_short) (0x4000) +#define ERR_RX_PACKET (u_short) (0x2000) +#define ERR_OVERRUN (u_short) (0x1000) +#define ERR_RUNT (u_short) (0x1300) +#define ERR_ALIGNMENT (u_short) (0x1400) +#define ERR_CRC (u_short) (0x1500) +#define ERR_OVERSIZE (u_short) (0x1100) +#define ERR_DRIBBLE (u_short) (0x200) + +/* + * TX Status + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +/* + * Misc defines for various things. + */ +#define TAG_ADAPTER_0 0xd0 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff +#define ENABLE_DRQ_IRQ 0x0001 +#define MFG_ID 0x6d50 +#define PROD_ID 0x9150 +#define BASE sc->ep_io_addr +#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x) +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 +#define IS_AUI (1<<13) +#define IS_BNC (1<<12) +#define IS_UTP (1<<9) +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) +#define READ_EEPROM (1<<7) +#define ETHER_ADDR_LEN 6 +#define ETHER_MAX 1536 +#define ENABLE_UTP 0xc0 +#define DISABLE_UTP 0x0 +#define RX_BYTES_MASK (u_short) (0x07ff) diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c new file mode 100644 index 0000000..259d451 --- /dev/null +++ b/sys/dev/fdc/fdc.c @@ -0,0 +1,1255 @@ +/*#define DEBUG 1*/ +/*- + * 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. + * + * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 + * $Id: fd.c,v 1.24 1994/03/08 16:25:29 nate Exp $ + * + */ + +#include "ft.h" +#if NFT < 1 +#undef NFDC +#endif +#include "fd.h" + +#if NFDC > 0 + +#include <sys/param.h> +#include <sys/dkbad.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <machine/ioctl_fd.h> +#include <sys/disklabel.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/syslog.h> +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/fdreg.h" +#include "i386/isa/fdc.h" +#include "i386/isa/icu.h" +#include "i386/isa/rtc.h" + +#if NFT > 0 +extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); +#endif + +#define b_cylin b_resid +#define FDBLK 512 + +/* misuse a flag to identify format operation */ +#define B_FORMAT B_XXX + +#define NUMTYPES 14 +#define NUMDENS (NUMTYPES - 6) + +/* This defines (-1) must match index for fd_types */ +#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ +#define NO_TYPE 0 /* must match NO_TYPE in ft.c */ +#define FD_1720 1 +#define FD_1480 2 +#define FD_1440 3 +#define FD_1200 4 +#define FD_820 5 +#define FD_800 6 +#define FD_720 7 +#define FD_360 8 + +#define FD_1480in5_25 9 +#define FD_1440in5_25 10 +#define FD_820in5_25 11 +#define FD_800in5_25 12 +#define FD_720in5_25 13 +#define FD_360in5_25 14 + + +struct fd_type fd_types[NUMTYPES] = +{ +{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ +{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ +{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ +{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ +{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ +{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ +{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ +{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ + +{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ +{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ +{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ +{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ +{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ +{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ +}; + +#define DRVS_PER_CTLR 2 /* 2 floppies */ +/***********************************************************************\ +* Per controller structure. * +\***********************************************************************/ +struct fdc_data fdc_data[NFDC]; + +/***********************************************************************\ +* Per drive structure. * +* N per controller (DRVS_PER_CTLR) * +\***********************************************************************/ +struct fd_data { + struct fdc_data *fdc; /* pointer to controller structure */ + int fdsu; /* this units number on this controller */ + int type; /* Drive type (HD, DD */ + struct fd_type *ft; /* pointer to the type descriptor */ + int flags; +#define FD_OPEN 0x01 /* it's open */ +#define FD_ACTIVE 0x02 /* it's active */ +#define FD_MOTOR 0x04 /* motor should be on */ +#define FD_MOTOR_WAIT 0x08 /* motor coming up */ + int skip; + int hddrv; + int track; /* where we think the head is */ +} fd_data[NFD]; + +/***********************************************************************\ +* Throughout this file the following conventions will be used: * +* fd is a pointer to the fd_data struct for the drive in question * +* fdc is a pointer to the fdc_data struct for the controller * +* fdu is the floppy drive unit number * +* fdcu is the floppy controller unit number * +* fdsu is the floppy drive unit number on that controller. (sub-unit) * +\***********************************************************************/ + +#define id_physid id_scsiid /* this biotab field doubles as a field */ + /* for the physical unit number on the controller */ + +static int retrier(fdcu_t); + +#define DEVIDLE 0 +#define FINDWORK 1 +#define DOSEEK 2 +#define SEEKCOMPLETE 3 +#define IOCOMPLETE 4 +#define RECALCOMPLETE 5 +#define STARTRECAL 6 +#define RESETCTLR 7 +#define SEEKWAIT 8 +#define RECALWAIT 9 +#define MOTORWAIT 10 +#define IOTIMEDOUT 11 + +#ifdef DEBUG +char *fdstates[] = +{ +"DEVIDLE", +"FINDWORK", +"DOSEEK", +"SEEKCOMPLETE", +"IOCOMPLETE", +"RECALCOMPLETE", +"STARTRECAL", +"RESETCTLR", +"SEEKWAIT", +"RECALWAIT", +"MOTORWAIT", +"IOTIMEDOUT" +}; + + +int fd_debug = 1; +#define TRACE0(arg) if(fd_debug) printf(arg) +#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#else /* DEBUG */ +#define TRACE0(arg) +#define TRACE1(arg1,arg2) +#endif /* DEBUG */ + +static void fdstart(fdcu_t); +void fdintr(fdcu_t); +static void fd_turnoff(caddr_t); + +/****************************************************************************/ +/* autoconfiguration stuff */ +/****************************************************************************/ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +struct isa_driver fdcdriver = { + fdprobe, fdattach, "fdc", +}; + +/* + * probe for existance of controller + */ +int +fdprobe(dev) + struct isa_device *dev; +{ + fdcu_t fdcu = dev->id_unit; + if(fdc_data[fdcu].flags & FDC_ATTACHED) + { + printf("fdc: same unit (%d) used multiple times\n",fdcu); + return 0; + } + + fdc_data[fdcu].baseport = dev->id_iobase; + + /* First - lets reset the floppy controller */ + + outb(dev->id_iobase+fdout,0); + DELAY(100); + outb(dev->id_iobase+fdout,FDO_FRST); + + /* see if it can handle a command */ + if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + { + return(0); + } + out_fdc(fdcu,0xDF); + out_fdc(fdcu,2); + return (IO_FDCSIZE); +} + +/* + * wire controller into system, look for floppy units + */ +int +fdattach(dev) + struct isa_device *dev; +{ + unsigned fdt,st0, cyl; + int hdr; + fdu_t fdu; + fdcu_t fdcu = dev->id_unit; + fdc_p fdc = fdc_data + fdcu; + fd_p fd; + int fdsu; + struct isa_device *fdup; + + fdc->fdcu = fdcu; + fdc->flags |= FDC_ATTACHED; + fdc->dmachan = dev->id_drq; + fdc->state = DEVIDLE; + hdr = 0; + printf("fdc%d:", fdcu); + + /* check for each floppy drive */ + for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { + if (fdup->id_iobase != dev->id_iobase) + continue; + fdu = fdup->id_unit; + fd = &fd_data[fdu]; + if (fdu >= (NFD+NFT)) + continue; + fdsu = fdup->id_physid; + /* look up what bios thinks we have */ + switch (fdu) { + case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0); + break; + case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); + break; + default: fdt = RTCFDT_NONE; + break; + } + /* is there a unit? */ + if ((fdt == RTCFDT_NONE) +#if NFT > 0 + || (fdsu >= DRVS_PER_CTLR)) { +#else + ) { + fd->type = NO_TYPE; +#endif +#if NFT > 0 + /* If BIOS says no floppy, or > 2nd device */ + /* Probe for and attach a floppy tape. */ + if (ftattach(dev, fdup)) + continue; + if (fdsu < DRVS_PER_CTLR) + fd->type = NO_TYPE; +#endif + continue; + } + +#ifdef notyet + /* select it */ + fd_turnon1(fdu); + spinwait(1000); /* 1 sec */ + out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu,fdsu); + spinwait(1000); /* 1 sec */ + + /* anything responding */ + out_fdc(fdcu,NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + if (st0 & 0xd0) + continue; + +#endif + fd->track = -2; + fd->fdc = fdc; + fd->fdsu = fdsu; + printf(" [%d: fd%d: ", fdsu, fdu); + + switch (fdt) { + case RTCFDT_12M: + printf("1.2MB 5.25in]"); + fd->type = FD_1200; + break; + case RTCFDT_144M: + printf("1.44MB 3.5in]"); + fd->type = FD_1440; + break; + case RTCFDT_360K: + printf("360KB 5.25in]"); + fd->type = FD_360; + break; + case RTCFDT_720K: + printf("720KB 3.5in]"); + fd->type = FD_720; + break; + default: + printf("unknown]"); + fd->type = NO_TYPE; + break; + } + + fd_turnoff((caddr_t)fdu); + hdr = 1; + } + printf("\n"); + + /* Set transfer to 500kbps */ + outb(fdc->baseport+fdctl,0); /*XXX*/ + return 1; +} + +int +fdsize(dev) + dev_t dev; +{ + return(0); +} + +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void fdstrategy(struct buf *bp) +{ + register struct buf *dp,*dp0,*dp1; + long nblocks,blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + +#if NFT > 0 + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + return; + } +#endif + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + pg("fd:error in fdstrategy"); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / FDBLK) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + bp->b_pblkno = bp->b_blkno; + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + +/****************************************************************************/ +/* motor control stuff */ +/* remember to not deselect the drive we're working on */ +/****************************************************************************/ +void +set_motor(fdcu, fdu, reset) + fdcu_t fdcu; + fdu_t fdu; + int reset; +{ + int m0,m1; + int selunit; + fd_p fd; + if(fd = fdc_data[fdcu].fd)/* yes an assign! */ + { + selunit = fd->fdsu; + } + else + { + selunit = 0; + } + m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; + m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; + outb(fdc_data[fdcu].baseport+fdout, + selunit + | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) + | (m0 ? FDO_MOEN0 : 0) + | (m1 ? FDO_MOEN1 : 0)); + TRACE1("[0x%x->fdout]",( + selunit + | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) + | (m0 ? FDO_MOEN0 : 0) + | (m1 ? FDO_MOEN1 : 0))); +} + +static void +fd_turnoff(caddr_t arg1) +{ + fdu_t fdu = (fdu_t)arg1; + int s; + + fd_p fd = fd_data + fdu; + s = splbio(); + fd->flags &= ~FD_MOTOR; + set_motor(fd->fdc->fdcu,fd->fdsu,0); + splx(s); +} + +void +fd_motor_on(caddr_t arg1) +{ + fdu_t fdu = (fdu_t)arg1; + int s; + + fd_p fd = fd_data + fdu; + s = splbio(); + fd->flags &= ~FD_MOTOR_WAIT; + if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) + { + fdintr(fd->fdc->fdcu); + } + splx(s); +} + +static void fd_turnon1(fdu_t); + +void +fd_turnon(fdu) + fdu_t fdu; +{ + fd_p fd = fd_data + fdu; + if(!(fd->flags & FD_MOTOR)) + { + fd_turnon1(fdu); + fd->flags |= FD_MOTOR_WAIT; + timeout((timeout_func_t)fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ + } +} + +static void +fd_turnon1(fdu_t fdu) +{ + fd_p fd = fd_data + fdu; + fd->flags |= FD_MOTOR; + set_motor(fd->fdc->fdcu,fd->fdsu,0); +} + +/****************************************************************************/ +/* fdc in/out */ +/****************************************************************************/ +int +in_fdc(fdcu) + fdcu_t fdcu; +{ + int baseport = fdc_data[fdcu].baseport; + int i, j = 100000; + while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + != (NE7_DIO|NE7_RQM) && j-- > 0) + if (i == NE7_RQM) return -1; + if (j <= 0) + return(-1); +#ifdef DEBUG + i = inb(baseport+fddata); + TRACE1("[fddata->0x%x]",(unsigned char)i); + return(i); +#else + return inb(baseport+fddata); +#endif +} + +int +out_fdc(fdcu, x) + fdcu_t fdcu; + int x; +{ + int baseport = fdc_data[fdcu].baseport; + int i; + + /* Check that the direction bit is set */ + i = 100000; + while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + if (i <= 0) return (-1); /* Floppy timed out */ + + /* Check that the floppy controller is ready for a command */ + i = 100000; + while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + if (i <= 0) return (-1); /* Floppy timed out */ + + /* Send the command and return */ + outb(baseport+fddata,x); + TRACE1("[0x%x->fddata]",x); + return (0); +} + +/****************************************************************************/ +/* fdopen/fdclose */ +/****************************************************************************/ +int +Fdopen(dev, flags) + dev_t dev; + int flags; +{ + fdu_t fdu = FDUNIT(minor(dev)); + int type = FDTYPE(minor(dev)); + fdc_p fdc; + +#if NFT > 0 + /* check for a tape open */ + if (type & F_TAPE_TYPE) + return(ftopen(dev, flags)); +#endif + /* check bounds */ + if (fdu >= NFD) + return(ENXIO); + fdc = fd_data[fdu].fdc; + if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) + return(ENXIO); + if (type > NUMDENS) + return(ENXIO); + if (type == 0) + type = fd_data[fdu].type; + else { + if (type != fd_data[fdu].type) { + switch (fd_data[fdu].type) { + case FD_360: + return(ENXIO); + case FD_720: + if ( type != FD_820 + && type != FD_800 + ) + return(ENXIO); + break; + case FD_1200: + switch (type) { + case FD_1480: + type = FD_1480in5_25; + break; + case FD_1440: + type = FD_1440in5_25; + break; + case FD_820: + type = FD_820in5_25; + break; + case FD_800: + type = FD_800in5_25; + break; + case FD_720: + type = FD_720in5_25; + break; + case FD_360: + type = FD_360in5_25; + break; + default: + return(ENXIO); + } + break; + case FD_1440: + if ( type != FD_1720 + && type != FD_1480 + && type != FD_1200 + && type != FD_820 + && type != FD_800 + && type != FD_720 + ) + return(ENXIO); + break; + } + } + } + fd_data[fdu].ft = fd_types + type - 1; + fd_data[fdu].flags |= FD_OPEN; + + return 0; +} + +int +fdclose(dev, flags) + dev_t dev; + int flags; +{ + fdu_t fdu = FDUNIT(minor(dev)); + int type = FDTYPE(minor(dev)); + +#if NFT > 0 + if (type & F_TAPE_TYPE) + return ftclose(0); +#endif + fd_data[fdu].flags &= ~FD_OPEN; + return(0); +} + + +/***************************************************************\ +* fdstart * +* We have just queued something.. if the controller is not busy * +* then simulate the case where it has just finished a command * +* So that it (the interrupt routine) looks on the queue for more* +* work to do and picks up what we just added. * +* If the controller is already busy, we need do nothing, as it * +* will pick up our work when the present work completes * +\***************************************************************/ +static void +fdstart(fdcu) + fdcu_t fdcu; +{ + register struct buf *dp,*bp; + int s; + fdu_t fdu; + + s = splbio(); + if(fdc_data[fdcu].state == DEVIDLE) + { + fdintr(fdcu); + } + splx(s); +} + +static void +fd_timeout(caddr_t arg1) +{ + fdcu_t fdcu = (fdcu_t)arg1; + fdu_t fdu = fdc_data[fdcu].fdu; + int st0, st3, cyl; + struct buf *dp,*bp; + int s; + + dp = &fdc_data[fdcu].head; + s = splbio(); + bp = dp->b_actf; + + out_fdc(fdcu,NE7CMD_SENSED); + out_fdc(fdcu,fd_data[fdu].hddrv); + st3 = in_fdc(fdcu); + + out_fdc(fdcu,NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", + fdu, + st0, + NE7_ST0BITS, + cyl, + st3, + NE7_ST3BITS); + + if (bp) + { + retrier(fdcu); + fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].state = IOTIMEDOUT; + if( fdc_data[fdcu].retry < 6) + fdc_data[fdcu].retry = 6; + } + else + { + fdc_data[fdcu].fd = (fd_p) 0; + fdc_data[fdcu].fdu = -1; + fdc_data[fdcu].state = DEVIDLE; + } + fdintr(fdcu); + splx(s); +} + +/* just ensure it has the right spl */ +static void +fd_pseudointr(caddr_t arg1, int arg2) +{ + fdcu_t fdcu = (fdcu_t)arg1; + int s; + s = splbio(); + fdintr(fdcu); + splx(s); +} + +/***********************************************************************\ +* fdintr * +* keep calling the state machine until it returns a 0 * +* ALWAYS called at SPLBIO * +\***********************************************************************/ +void +fdintr(fdcu_t fdcu) +{ + fdc_p fdc = fdc_data + fdcu; +#if NFT > 0 + fdu_t fdu = fdc->fdu; + + if (fdc->flags & FDC_TAPE_BUSY) + (ftintr(fdu)); + else +#endif + while(fdstate(fdcu, fdc)) + ; +} + +/***********************************************************************\ +* The controller state machine. * +* if it returns a non zero value, it should be called again immediatly * +\***********************************************************************/ +int +fdstate(fdcu, fdc) + fdcu_t fdcu; + fdc_p fdc; +{ + int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + unsigned long blknum; + fdu_t fdu = fdc->fdu; + fd_p fd; + register struct buf *dp,*bp; + struct fd_formb *finfo = NULL; + + dp = &(fdc->head); + bp = dp->b_actf; + if(!bp) + { + /***********************************************\ + * nothing left for this controller to do * + * Force into the IDLE state, * + \***********************************************/ + fdc->state = DEVIDLE; + if(fdc->fd) + { + printf("unexpected valid fd pointer (fdu = %d)\n" + ,fdc->fdu); + fdc->fd = (fd_p) 0; + fdc->fdu = -1; + } + TRACE1("[fdc%d IDLE]",fdcu); + return(0); + } + fdu = FDUNIT(minor(bp->b_dev)); + fd = fd_data + fdu; + if (fdc->fd && (fd != fdc->fd)) + { + printf("confused fd pointers\n"); + } + read = bp->b_flags & B_READ; + format = bp->b_flags & B_FORMAT; + if(format) + finfo = (struct fd_formb *)bp->b_un.b_addr; + TRACE1("fd%d",fdu); + TRACE1("[%s]",fdstates[fdc->state]); + TRACE1("(0x%x)",fd->flags); + untimeout((timeout_func_t)fd_turnoff, (caddr_t)fdu); + timeout((timeout_func_t)fd_turnoff, (caddr_t)fdu, 4 * hz); + switch (fdc->state) + { + case DEVIDLE: + case FINDWORK: /* we have found new work */ + fdc->retry = 0; + fd->skip = 0; + fdc->fd = fd; + fdc->fdu = fdu; + outb(fdc->baseport+fdctl, fd->ft->trans); + /*******************************************************\ + * If the next drive has a motor startup pending, then * + * it will start up in it's own good time * + \*******************************************************/ + if(fd->flags & FD_MOTOR_WAIT) + { + fdc->state = MOTORWAIT; + return(0); /* come back later */ + } + /*******************************************************\ + * Maybe if it's not starting, it SHOULD be starting * + \*******************************************************/ + if (!(fd->flags & FD_MOTOR)) + { + fdc->state = MOTORWAIT; + fd_turnon(fdu); + return(0); + } + else /* at least make sure we are selected */ + { + set_motor(fdcu,fd->fdsu,0); + } + fdc->state = DOSEEK; + break; + case DOSEEK: + if (bp->b_cylin == fd->track) + { + fdc->state = SEEKCOMPLETE; + break; + } + out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu,fd->fdsu); /* Drive number */ + out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + fd->track = -2; + fdc->state = SEEKWAIT; + timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz); + return(0); /* will return later */ + case SEEKWAIT: + untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu); + /* allow heads to settle */ + timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 50); + fdc->state = SEEKCOMPLETE; + return(0); /* will return later */ + break; + + case SEEKCOMPLETE : /* SEEK DONE, START DMA */ + /* Make sure seek really happened*/ + if(fd->track == -2) + { + int descyl = bp->b_cylin * fd->ft->steptrac; + out_fdc(fdcu,NE7CMD_SENSEI); + i = in_fdc(fdcu); + cyl = in_fdc(fdcu); + if (cyl != descyl) + { + printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, i, NE7_ST0BITS); + return(retrier(fdcu)); + } + } + + fd->track = bp->b_cylin; + if(format) + fd->skip = (char *)&(finfo->fd_formb_cylno(0)) + - (char *)finfo; + isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, + format ? bp->b_bcount : FDBLK, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK + + fd->skip/FDBLK; + sectrac = fd->ft->sectrac; + sec = blknum % (sectrac * fd->ft->heads); + head = sec / sectrac; + sec = sec % sectrac + 1; +/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + + if(format) + { + /* formatting */ + out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); + out_fdc(fdcu,head << 2 | fdu); + out_fdc(fdcu,finfo->fd_formb_secshift); + out_fdc(fdcu,finfo->fd_formb_nsecs); + out_fdc(fdcu,finfo->fd_formb_gaplen); + out_fdc(fdcu,finfo->fd_formb_fillbyte); + } + else + { + if (read) + { + out_fdc(fdcu,NE7CMD_READ); /* READ */ + } + else + { + out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + } + out_fdc(fdcu,head << 2 | fdu); /* head & unit */ + out_fdc(fdcu,fd->track); /* track */ + out_fdc(fdcu,head); + out_fdc(fdcu,sec); /* sector XXX +1? */ + out_fdc(fdcu,fd->ft->secsize); /* sector size */ + out_fdc(fdcu,sectrac); /* sectors/track */ + out_fdc(fdcu,fd->ft->gap); /* gap size */ + out_fdc(fdcu,fd->ft->datalen); /* data length */ + } + fdc->state = IOCOMPLETE; + timeout((timeout_func_t)fd_timeout, (caddr_t)fdcu, 2 * hz); + return(0); /* will return later */ + case IOCOMPLETE: /* IO DONE, post-analyze */ + untimeout((timeout_func_t)fd_timeout, (caddr_t)fdcu); + for(i=0;i<7;i++) + { + fdc->status[i] = in_fdc(fdcu); + } + case IOTIMEDOUT: /*XXX*/ + isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, + format ? bp->b_bcount : FDBLK, fdc->dmachan); + if (fdc->status[0]&0xF8) + { + if (fdc->status[1] & 0x10) { + /* + * Operation not completed in reasonable time. + * Just restart it, don't increment retry count. + * (vak) + */ + fdc->state = SEEKCOMPLETE; + return (1); + } + return(retrier(fdcu)); + } + /* All OK */ + fd->skip += FDBLK; + if (!format && fd->skip < bp->b_bcount) + { + /* set up next transfer */ + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK + + fd->skip/FDBLK; + bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + fdc->state = DOSEEK; + } + else + { + /* ALL DONE */ + fd->skip = 0; + bp->b_resid = 0; + dp->b_actf = bp->b_actf; + biodone(bp); + fdc->fd = (fd_p) 0; + fdc->fdu = -1; + fdc->state = FINDWORK; + } + return(1); + case RESETCTLR: + /* Try a reset, keep motor on */ + set_motor(fdcu,fd->fdsu,1); + DELAY(100); + set_motor(fdcu,fd->fdsu,0); + outb(fdc->baseport+fdctl,fd->ft->trans); + TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc->retry++; + fdc->state = STARTRECAL; + break; + case STARTRECAL: + out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ + out_fdc(fdcu,0xDF); + out_fdc(fdcu,2); + out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu,fdu); + fdc->state = RECALWAIT; + return(0); /* will return later */ + case RECALWAIT: + /* allow heads to settle */ + timeout((timeout_func_t)fd_pseudointr, (caddr_t)fdcu, hz / 30); + fdc->state = RECALCOMPLETE; + return(0); /* will return later */ + case RECALCOMPLETE: + out_fdc(fdcu,NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + if (cyl != 0) + { + printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, + st0, NE7_ST0BITS, cyl); + return(retrier(fdcu)); + } + fd->track = 0; + /* Seek (probably) necessary */ + fdc->state = DOSEEK; + return(1); /* will return immediatly */ + case MOTORWAIT: + if(fd->flags & FD_MOTOR_WAIT) + { + return(0); /* time's not up yet */ + } + fdc->state = DOSEEK; + return(1); /* will return immediatly */ + default: + printf("Unexpected FD int->"); + out_fdc(fdcu,NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + printf("ST0 = %lx, PCN = %lx\n",i,sec); + out_fdc(fdcu,0x4A); + out_fdc(fdcu,fd->fdsu); + for(i=0;i<7;i++) { + fdc->status[i] = in_fdc(fdcu); + } + printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + return(0); + } + return(1); /* Come back immediatly to new state */ +} + +static int +retrier(fdcu) + fdcu_t fdcu; +{ + fdc_p fdc = fdc_data + fdcu; + register struct buf *dp,*bp; + + dp = &(fdc->head); + bp = dp->b_actf; + + switch(fdc->retry) + { + case 0: case 1: case 2: + fdc->state = SEEKCOMPLETE; + break; + case 3: case 4: case 5: + fdc->state = STARTRECAL; + break; + case 6: + fdc->state = RESETCTLR; + break; + case 7: + break; + default: + { + dev_t sav_b_dev = bp->b_dev; + /* Trick diskerr */ + bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3); + diskerr(bp, "fd", "hard error", LOG_PRINTF, + fdc->fd->skip, (struct disklabel *)NULL); + bp->b_dev = sav_b_dev; + printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); + printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); + printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS); + printf("cyl %d hd %d sec %d)\n", + fdc->status[3], fdc->status[4], fdc->status[5]); + } + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + bp->b_resid = bp->b_bcount - fdc->fd->skip; + dp->b_actf = bp->b_actf; + fdc->fd->skip = 0; + biodone(bp); + fdc->state = FINDWORK; + fdc->fd = (fd_p) 0; + fdc->fdu = -1; + /* XXX abort current command, if any. */ + return(1); + } + fdc->retry++; + return(1); +} + +static int +fdformat(dev, finfo, p) + dev_t dev; + struct fd_formb *finfo; + struct proc *p; +{ + fdu_t fdu; + fd_p fd; + + struct buf *bp; + int rv = 0, s; + + fdu = FDUNIT(minor(dev)); + fd = &fd_data[fdu]; + + /* set up a buffer header for fdstrategy() */ + bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); + if(bp == 0) + return ENOBUFS; + bzero((void *)bp, sizeof(struct buf)); + bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; + bp->b_proc = p; + bp->b_dev = dev; + + /* + * calculate a fake blkno, so fdstrategy() would initiate a + * seek to the requested cylinder + */ + bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; + bp->b_un.b_addr = (caddr_t)finfo; + + /* now do the format */ + fdstrategy(bp); + + /* ...and wait for it to complete */ + s = splbio(); + while(!(bp->b_flags & B_DONE)) + { + rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); + if(rv == EWOULDBLOCK) + break; + } + splx(s); + + if(rv == EWOULDBLOCK) + { + /* timed out */ + biodone(bp); + rv = EIO; + } + free(bp, M_TEMP); + return rv; +} + +/* + * fdioctl() from jc@irbs.UUCP (John Capo) + * i386/i386/conf.c needs to have fdioctl() declared and remove the line that + * defines fdioctl to be enxio. + * + * TODO: Reformat. + * Think about allocating buffer off stack. + * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). + * Watch out for NetBSD's different *disklabel() interface. + * + * Added functionality for floppy formatting + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + */ + +int +fdioctl (dev, cmd, addr, flag, p) + dev_t dev; + int cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct fd_type *fdt; + struct disklabel *dl; + char buffer[DEV_BSIZE]; + int error; + +#if NFT > 0 + int type = FDTYPE(minor(dev)); + + /* check for a tape ioctl */ + if (type & F_TAPE_TYPE) + return ftioctl(dev, cmd, addr, flag, p); +#endif + + error = 0; + + switch (cmd) + { + case DIOCGDINFO: + bzero(buffer, sizeof (buffer)); + dl = (struct disklabel *)buffer; + dl->d_secsize = FDBLK; + fdt = fd_data[FDUNIT(minor(dev))].ft; + dl->d_secpercyl = fdt->size / fdt->tracks; + dl->d_type = DTYPE_FLOPPY; + + if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL) + error = 0; + else + error = EINVAL; + + *(struct disklabel *)addr = *dl; + break; + + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + break; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + error = EBADF; + break; + + case DIOCWDINFO: + if ((flag & FWRITE) == 0) + { + error = EBADF; + break; + } + + dl = (struct disklabel *)addr; + + if (error = setdisklabel ((struct disklabel *)buffer, + dl, 0, NULL)) + break; + + error = writedisklabel(dev, fdstrategy, + (struct disklabel *)buffer, NULL); + break; + + case FD_FORM: + if((flag & FWRITE) == 0) + error = EBADF; /* must be opened for writing */ + else if(((struct fd_formb *)addr)->format_version != + FD_FORMAT_VERSION) + error = EINVAL; /* wrong version of formatting prog */ + else + error = fdformat(dev, (struct fd_formb *)addr, p); + break; + + case FD_GTYPE: /* get drive type */ + *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +#endif diff --git a/sys/dev/fdc/fdcreg.h b/sys/dev/fdc/fdcreg.h new file mode 100644 index 0000000..5deb02c --- /dev/null +++ b/sys/dev/fdc/fdcreg.h @@ -0,0 +1,65 @@ +/*- + * 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. + * + * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 + * $Id: fdreg.h,v 1.3 1994/02/07 04:27:10 alm Exp $ + */ + +/* + * AT floppy controller registers and bitfields + */ + +/* uses NEC765 controller */ +#include "../i386/isa/ic/nec765.h" + +/* registers */ +#define fdout 2 /* Digital Output Register (W) */ +#define FDO_FDSEL 0x03 /* floppy device select */ +#define FDO_FRST 0x04 /* floppy controller reset */ +#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ +#define FDO_MOEN0 0x10 /* motor enable drive 0 */ +#define FDO_MOEN1 0x20 /* motor enable drive 1 */ +#define FDO_MOEN2 0x30 /* motor enable drive 2 */ +#define FDO_MOEN3 0x40 /* motor enable drive 3 */ + +#define fdsts 4 /* NEC 765 Main Status Register (R) */ +#define fddata 5 /* NEC 765 Data Register (R/W) */ + +#define fdctl 7 /* Control Register (W) */ +#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */ +#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */ +#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */ +#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */ + +#define fdin 7 /* Digital Input Register (R) */ +#define FDI_DCHG 0x80 /* diskette has been changed */ + diff --git a/sys/dev/ic/i8237.h b/sys/dev/ic/i8237.h new file mode 100644 index 0000000..2199e73 --- /dev/null +++ b/sys/dev/ic/i8237.h @@ -0,0 +1,11 @@ +/* + * Intel 8237 DMA Controller + * + * $Id$ + */ + +#define DMA37MD_SINGLE 0x40 /* single pass mode */ +#define DMA37MD_CASCADE 0xc0 /* cascade mode */ +#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */ +#define DMA37MD_READ 0x08 /* write the device, read memory operation */ + diff --git a/sys/dev/ic/i82586.h b/sys/dev/ic/i82586.h new file mode 100644 index 0000000..577313d --- /dev/null +++ b/sys/dev/ic/i82586.h @@ -0,0 +1,325 @@ +/*- + * Copyright (c) 1992, University of Vermont and State Agricultural College. + * Copyright (c) 1992, Garrett A. Wollman. + * 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 + * Vermont and State Agricultural College and Garrett A. Wollman. + * 4. Neither the name of the University 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 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 UNIVERSITY OR 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. + * + * $Id$ + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + */ + +struct ie_en_addr { + u_char data[6]; +}; + +/* + * This is the master configuration block. It tells the hardware where all + * the rest of the stuff is. + */ +struct ie_sys_conf_ptr { + u_short mbz; /* must be zero */ + u_char ie_bus_use; /* true if 8-bit only */ + u_char mbz2[5]; /* must be zero */ + caddr_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */ +}; + +/* + * Note that this is wired in hardware; the SCP is always located here, no + * matter what. + */ +#define IE_SCP_ADDR 0xfffff4 + +/* + * The tells the hardware where all the rest of the stuff is, too. + * FIXME: some of these should be re-commented after we figure out their + * REAL function. + */ +struct ie_int_sys_conf_ptr { + u_char ie_busy; /* zeroed after init */ + u_char mbz; + u_short ie_scb_offset; /* 16-bit physaddr of next struct */ + caddr_t ie_base; /* 24-bit physaddr for all 16-bit vars */ +}; + +/* + * This FINALLY tells the hardware what to do and where to put it. + */ +struct ie_sys_ctl_block { + u_short ie_status; /* status word */ + u_short ie_command; /* command word */ + u_short ie_command_list; /* 16-pointer to command block list */ + u_short ie_recv_list; /* 16-pointer to receive frame list */ + u_short ie_err_crc; /* CRC errors */ + u_short ie_err_align; /* Alignment errors */ + u_short ie_err_resource; /* Resource errors */ + u_short ie_err_overrun; /* Overrun errors */ +}; + +/* Command values */ +#define IE_RU_COMMAND 0x0070 /* mask for RU command */ +#define IE_RU_NOP 0 /* for completeness */ +#define IE_RU_START 0x0010 /* start receive unit command */ +#define IE_RU_ENABLE 0x0020 /* enable receiver command */ +#define IE_RU_DISABLE 0x0030 /* disable receiver command */ +#define IE_RU_ABORT 0x0040 /* abort current receive operation */ + +#define IE_CU_COMMAND 0x0700 /* mask for CU command */ +#define IE_CU_NOP 0 /* included for completeness */ +#define IE_CU_START 0x0100 /* do-command command */ +#define IE_CU_RESUME 0x0200 /* resume a suspended cmd list */ +#define IE_CU_STOP 0x0300 /* SUSPEND was already taken */ +#define IE_CU_ABORT 0x0400 /* abort current command */ + +#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */ +#define IE_ACK_CX 0x8000 /* ack IE_ST_DONE */ +#define IE_ACK_FR 0x4000 /* ack IE_ST_RECV */ +#define IE_ACK_CNA 0x2000 /* ack IE_ST_ALLDONE */ +#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */ + +#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START) + /* is this command an action command? */ + +/* Status values */ +#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */ +#define IE_ST_DONE 0x8000 /* command with I bit completed */ +#define IE_ST_RECV 0x4000 /* frame received */ +#define IE_ST_ALLDONE 0x2000 /* all commands completed */ +#define IE_ST_RNR 0x1000 /* receive not ready */ + +#define IE_CU_STATUS 0x700 /* mask for command unit status */ +#define IE_CU_ACTIVE 0x200 /* command unit is active */ +#define IE_CU_SUSPEND 0x100 /* command unit is suspended */ + +#define IE_RU_STATUS 0x70 /* mask for receiver unit status */ +#define IE_RU_SUSPEND 0x10 /* receiver is suspended */ +#define IE_RU_NOSPACE 0x20 /* receiver has no resources */ +#define IE_RU_READY 0x40 /* reveiver is ready */ + +/* + * This is filled in partially by the chip, partially by us. + */ +struct ie_recv_frame_desc { + u_short ie_fd_status; /* status for this frame */ + u_short ie_fd_last; /* end of frame list flag */ + u_short ie_fd_next; /* 16-pointer to next RFD */ + u_short ie_fd_buf_desc; /* 16-pointer to list of buffer desc's */ + struct ie_en_addr dest; /* destination ether */ + struct ie_en_addr src; /* source ether */ + u_short ie_length; /* 802 length/Ether type */ + u_short mbz; /* must be zero */ +}; + +#define IE_FD_LAST 0x8000 /* last rfd in list */ +#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */ + +#define IE_FD_COMPLETE 0x8000 /* frame is complete */ +#define IE_FD_BUSY 0x4000 /* frame is busy */ +#define IE_FD_OK 0x2000 /* frame is bad */ +#define IE_FD_RNR 0x0200 /* receiver out of resources here */ + +/* + * linked list of buffers... + */ +struct ie_recv_buf_desc { + u_short ie_rbd_actual; /* status for this buffer */ + u_short ie_rbd_next; /* 16-pointer to next RBD */ + caddr_t ie_rbd_buffer; /* 24-pointer to buffer for this RBD */ + u_short ie_rbd_length; /* length of the buffer */ + u_short mbz; /* must be zero */ +}; + +#define IE_RBD_LAST 0x8000 /* last buffer */ +#define IE_RBD_USED 0x4000 /* this buffer has data */ +/* + * All commands share this in common. + */ +struct ie_cmd_common { + u_short ie_cmd_status; /* status of this command */ + u_short ie_cmd_cmd; /* command word */ + u_short ie_cmd_link; /* link to next command */ +}; + +#define IE_STAT_COMPL 0x8000 /* command is completed */ +#define IE_STAT_BUSY 0x4000 /* command is running now */ +#define IE_STAT_OK 0x2000 /* command completed successfully */ + +#define IE_CMD_NOP 0x0000 /* NOP */ +#define IE_CMD_IASETUP 0x0001 /* initial address setup */ +#define IE_CMD_CONFIG 0x0002 /* configure command */ +#define IE_CMD_MCAST 0x0003 /* multicast setup command */ +#define IE_CMD_XMIT 0x0004 /* transmit command */ +#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */ +#define IE_CMD_DUMP 0x0006 /* dump command */ +#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */ + +#define IE_CMD_LAST 0x8000 /* this is the last command in the list */ +#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */ +#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */ + +/* + * This is the command to transmit a frame. + */ +struct ie_xmit_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_xmit_status com.ie_cmd_status + + u_short ie_xmit_desc; /* 16-pointer to buffer descriptor */ + struct ie_en_addr ie_xmit_addr; /* destination address */ + + u_short ie_xmit_length; /* 802.3 length/Ether type field */ +}; + +#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */ +#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */ +#define IE_XS_SQE 0x0040 /* SQE positive */ +#define IE_XS_DEFERRED 0x0080 /* transmission deferred */ +#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */ +#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */ +#define IE_XS_NOCARRIER 0x0400 /* No Carrier */ +#define IE_XS_LATECOLL 0x0800 /* Late collision */ + +/* + * This is a buffer descriptor for a frame to be transmitted. + */ + +struct ie_xmit_buf { + u_short ie_xmit_flags; /* see below */ + u_short ie_xmit_next; /* 16-pointer to next desc. */ + caddr_t ie_xmit_buf; /* 24-pointer to the actual buffer */ +}; + +#define IE_XMIT_LAST 0x8000 /* this TBD is the last one */ +/* The rest of the `flags' word is actually the length. */ + +/* + * Multicast setup command. + */ + +#define MAXMCAST 50 /* must fit in transmit buffer */ + +struct ie_mcast_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_mcast_status com.ie_cmd_status + + u_short ie_mcast_bytes; /* size (in bytes) of multicast addresses */ + struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1]; /* space for them */ +}; + +/* + * Time Domain Reflectometer command. + */ + +struct ie_tdr_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_tdr_status com.ie_cmd_status + + u_short ie_tdr_time; /* error bits and time */ +}; + +#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */ +#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */ +#define IE_TDR_OPEN 0x2000 /* detected an open */ +#define IE_TDR_SHORT 0x1000 /* TDR detected a short */ +#define IE_TDR_TIME 0x07ff /* mask for reflection time */ + +/* + * Initial Address Setup command + */ +struct ie_iasetup_cmd { + struct ie_cmd_common com; +#define ie_iasetup_status com.ie_cmd_status + + struct ie_en_addr ie_address; +}; + +/* + * Configuration command + */ +struct ie_config_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_config_status com.ie_cmd_status + + u_char ie_config_count; /* byte count (0x0c) */ + u_char ie_fifo; /* fifo (8) */ + u_char ie_save_bad; /* save bad frames (0x40) */ + u_char ie_addr_len; /* address length (0x2e) (AL-LOC == 1) */ + u_char ie_priority; /* priority and backoff (0x0) */ + u_char ie_ifs; /* inter-frame spacing (0x60) */ + u_char ie_slot_low; /* slot time, LSB (0x0) */ + u_char ie_slot_high; /* slot time, MSN, and retries (0xf2) */ + u_char ie_promisc; /* 1 if promiscuous, else 0 */ + u_char ie_crs_cdt; /* CSMA/CD parameters (0x0) */ + u_char ie_min_len; /* min frame length (0x40) */ + u_char ie_junk; /* stuff for 82596 (0xff) */ +}; + +/* + * Here are a few useful functions. We could have done these as macros, + * but since we have the inline facility, it makes sense to use that + * instead. + */ +inline void +ie_setup_config(volatile struct ie_config_cmd *cmd, + int promiscuous, int manchester) { + cmd->ie_config_count = 0x0c; + cmd->ie_fifo = 8; + cmd->ie_save_bad = 0x40; + cmd->ie_addr_len = 0x2e; + cmd->ie_priority = 0; + cmd->ie_ifs = 0x60; + cmd->ie_slot_low = 0; + cmd->ie_slot_high = 0xf2; + cmd->ie_promisc = !!promiscuous | manchester << 2; + cmd->ie_crs_cdt = 0; + cmd->ie_min_len = 64; + cmd->ie_junk = 0xff; +} + +inline caddr_t +Align(caddr_t ptr) { + unsigned long l = (unsigned long)ptr; + l = (l + 3) & ~3L; + return (caddr_t)l; +} + +inline void +ie_ack(volatile struct ie_sys_ctl_block *scb, + u_int mask, int unit, + void (*ca)(int)) { + scb->ie_command = scb->ie_status & mask; + (*ca)(unit); +} diff --git a/sys/dev/ic/nec765.h b/sys/dev/ic/nec765.h new file mode 100644 index 0000000..1895db7 --- /dev/null +++ b/sys/dev/ic/nec765.h @@ -0,0 +1,72 @@ +/*- + * 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. + * + * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 + * $Id$ + */ + +/* + * 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 */ diff --git a/sys/dev/ic/ns16550.h b/sys/dev/ic/ns16550.h new file mode 100644 index 0000000..ff59757 --- /dev/null +++ b/sys/dev/ic/ns16550.h @@ -0,0 +1,51 @@ +/*- + * 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. + * + * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 + * $Id$ + */ + +/* + * NS16550 UART registers + */ + +#define com_data 0 /* data register (R/W) */ +#define com_dlbl 0 /* divisor latch low (W) */ +#define com_dlbh 1 /* divisor latch high (W) */ +#define com_ier 1 /* interrupt enable (W) */ +#define com_iir 2 /* interrupt identification (R) */ +#define com_fifo 2 /* FIFO control (W) */ +#define com_lctl 3 /* line control register (R/W) */ +#define com_cfcr 3 /* line control register (R/W) */ +#define com_mcr 4 /* modem control register (R/W) */ +#define com_lsr 5 /* line status register (R/W) */ +#define com_msr 6 /* modem status register (R/W) */ diff --git a/sys/dev/ie/if_ie.c b/sys/dev/ie/if_ie.c new file mode 100644 index 0000000..fbb1d5e --- /dev/null +++ b/sys/dev/ie/if_ie.c @@ -0,0 +1,1801 @@ +/*- + * Copyright (c) 1992, 1993, University of Vermont and State + * Agricultural College. + * Copyright (c) 1992, 1993, Garrett A. Wollman. + * + * Portions: + * Copyright (c) 1990, 1991, William F. Jolitz + * Copyright (c) 1990, 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 + * Vermont and State Agricultural College and Garrett A. Wollman, + * by William F. Jolitz, by the University of California, + * Berkeley, by Larwence Berkeley Laboratory, and its contributors. + * 4. Neither the names of the Universities nor the names of the authors + * 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 UNIVERSITY OR 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. + * + * $Id: if_ie.c,v 1.2 1993/11/25 01:31:36 wollman Exp $ + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + * + * BPF support code stolen directly from hpdev/if_le.c, supplied with + * tcpdump. + */ + +/* + * The i82586 is a very versatile chip, found in many implementations. + * Programming this chip is mostly the same, but certain details differ + * from card to card. This driver is written so that different cards + * can be automatically detected at run-time. Currently, only the + * AT&T EN100/StarLAN 10 series are supported. + */ + +/* +Mode of operation: + +We run the 82586 in a standard Ethernet mode. We keep NFRAMES received +frame descriptors around for the receiver to use, and NBUFFS associated +receive buffer descriptors, both in a circular list. Whenever a frame is +received, we rotate both lists as necessary. (The 586 treats both lists +as a simple queue.) We also keep a transmit command around so that packets +can be sent off quickly. + +We configure the adapter in AL-LOC = 1 mode, which means that the +Ethernet/802.3 MAC header is placed at the beginning of the receive buffer +rather than being split off into various fields in the RFD. This also +means that we must include this header in the transmit buffer as well. + +By convention, all transmit commands, and only transmit commands, shall +have the I (IE_CMD_INTR) bit set in the command. This way, when an +interrupt arrives at ieintr(), it is immediately possible to tell +what precisely caused it. ANY OTHER command-sending routines should +run at splimp(), and should post an acknowledgement to every interrupt +they generate. + +The 82586 has a 24-bit address space internally, and the adaptor's +memory is located at the top of this region. However, the value we are +given in configuration is normally the *bottom* of the adaptor RAM. So, +we must go through a few gyrations to come up with a kernel virtual address +which represents the actual beginning of the 586 address space. First, +we autosize the RAM by running through several possible sizes and trying +to initialize the adapter under the assumption that the selected size +is correct. Then, knowing the correct RAM size, we set up our pointers +in ie_softc[unit]. `iomem' represents the computed base of the 586 +address space. `iomembot' represents the actual configured base +of adapter RAM. Finally, `iosize' represents the calculated size +of 586 RAM. Then, when laying out commands, we use the interval +[iomembot, iomembot + iosize); to make 24-pointers, we subtract +iomem, and to make 16-pointers, we subtract iomem and and with 0xffff. + +*/ + +#include "ie.h" +#if NIE > 0 + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "ioctl.h" +#include "errno.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_types.h" +#include "net/if_dl.h" +#include "net/route.h" + +#include "bpfilter.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/ic/i82586.h" +#include "i386/isa/if_iereg.h" +#include "i386/isa/icu.h" + +#include "vm/vm.h" + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#if (NBPFILTER > 0) || defined(MULTICAST) +#define FILTER +static struct mbuf *last_not_for_us; +#endif + +#ifdef DEBUG +#define IED_RINT 1 +#define IED_TINT 2 +#define IED_RNR 4 +#define IED_CNA 8 +#define IED_READFRAME 16 +int ie_debug = IED_RNR; +#endif + +#ifndef ETHERMINLEN +#define ETHERMINLEN 60 +#endif + +#define IE_BUF_LEN 1512 /* length of transmit buffer */ + +/* Forward declaration */ +struct ie_softc; + +static int ieprobe(struct isa_device *dvp); +static int ieattach(struct isa_device *dvp); +static void ieinit(int unit); +static int ieioctl(struct ifnet *ifp, int command, caddr_t data); +static void iestart(struct ifnet *ifp); +static void sl_reset_586(int unit); +static void sl_chan_attn(int unit); +static void iereset(int unit); +static void ie_readframe(int unit, struct ie_softc *ie, int bufno); +static void ie_drop_packet_buffer(int unit, struct ie_softc *ie); +static void sl_read_ether(int unit, unsigned char addr[6]); +static void find_ie_mem_size(int unit); +static int command_and_wait(int unit, int command, void volatile *pcmd, int); +static int ierint(int unit, struct ie_softc *ie); +static int ietint(int unit, struct ie_softc *ie); +static int iernr(int unit, struct ie_softc *ie); +static void start_receiver(int unit); +static int ieget(int, struct ie_softc *, struct mbuf **, + struct ether_header *, int *); +static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie); +static int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *); +#ifdef MULTICAST +static void ie_mc_reset(int unit); +#endif + +#ifdef DEBUG +void print_rbd(volatile struct ie_recv_buf_desc *rbd); + +int in_ierint = 0; +int in_ietint = 0; +#endif + +/* + * This tells the autoconf code how to set us up. + */ +struct isa_driver iedriver = { + ieprobe, ieattach, "ie", +}; + +enum ie_hardware { + IE_STARLAN10, + IE_EN100, + IE_SLFIBER, + IE_UNKNOWN +}; + +const char *ie_hardware_names[] = { + "StarLAN 10", + "EN100", + "StarLAN Fiber", + "Unknown" +}; + +/* +sizeof(iscp) == 1+1+2+4 == 8 +sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 +NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 +sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 +sizeof(transmit buffer) == 1512 +sizeof(transmit buffer desc) == 8 +----- +1946 + +NBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12 +NBUFFS * IE_RBUF_SIZE == NBUFFS*256 + +NBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 + +With NBUFFS == 48, this leaves us 1574 bytes for another command or +more buffers. Another transmit command would be 18+8+1512 == 1538 +---just barely fits! + +Obviously all these would have to be reduced for smaller memory sizes. +With a larger memory, it would be possible to roughly double the number of +both transmit and receive buffers. +*/ + +#define NFRAMES 16 /* number of frames to allow for receive */ +#define NBUFFS 48 /* number of buffers to allocate */ +#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ + +/* + * Ethernet status, per interface. + */ +struct ie_softc { + struct arpcom arpcom; + void (*ie_reset_586)(int); + void (*ie_chan_attn)(int); + enum ie_hardware hard_type; + int hard_vers; + + u_short port; + caddr_t iomem; + caddr_t iomembot; + unsigned iosize; + + int want_mcsetup; + int promisc; + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + volatile struct ie_recv_frame_desc *rframes[NFRAMES]; + volatile struct ie_recv_buf_desc *rbuffs[NBUFFS]; + volatile char *cbuffs[NBUFFS]; + int rfhead, rftail, rbhead, rbtail; + + volatile struct ie_xmit_cmd *xmit_cmds[2]; + volatile struct ie_xmit_buf *xmit_buffs[2]; + int xmit_count; + u_char *xmit_cbuffs[2]; + + struct ie_en_addr mcast_addrs[MAXMCAST + 1]; + int mcast_count; + +#if NBPFILTER > 0 + caddr_t ie_bpf; +#endif + +} ie_softc[NIE]; + +#define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base)) +#define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr)) + +#define PORT ie_softc[unit].port +#define MEM ie_softc[unit].iomem + + +int ieprobe(dvp) + struct isa_device *dvp; +{ + int unit = dvp->id_unit; + u_char c; + + ie_softc[unit].port = dvp->id_iobase; + ie_softc[unit].iomembot = dvp->id_maddr; + ie_softc[unit].iomem = 0; + + c = inb(PORT + IEATT_REVISION); + switch(SL_BOARD(c)) { + case SL10_BOARD: + ie_softc[unit].hard_type = IE_STARLAN10; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + case EN100_BOARD: + ie_softc[unit].hard_type = IE_EN100; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + case SLFIBER_BOARD: + ie_softc[unit].hard_type = IE_SLFIBER; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + + /* + * Anything else is not recognized or cannot be used. + */ + default: + return 0; + } + + ie_softc[unit].hard_vers = SL_REV(c); + + /* + * Divine memory size on-board the card. Ususally 16k. + */ + find_ie_mem_size(unit); + + if(!ie_softc[unit].iosize) { + return 0; + } + + dvp->id_msize = ie_softc[unit].iosize; + + switch(ie_softc[unit].hard_type) { + case IE_EN100: + case IE_STARLAN10: + case IE_SLFIBER: + sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr); + break; + + default: + printf("ie%d: unknown AT&T board type code %d\n", unit, + ie_softc[unit].hard_type); + return 0; + } + + return 1; +} + +/* + * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. + */ +int +ieattach(dvp) + struct isa_device *dvp; +{ + int unit = dvp->id_unit; + struct ie_softc *ie = &ie_softc[unit]; + struct ifnet *ifp = &ie->arpcom.ac_if; + + ifp->if_unit = unit; + ifp->if_name = iedriver.name; + ifp->if_mtu = ETHERMTU; + printf("<%s R%d> ethernet address %s", + ie_hardware_names[ie_softc[unit].hard_type], + ie_softc[unit].hard_vers + 1, + ether_sprintf(ie->arpcom.ac_enaddr)); + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; +#ifdef MULTICAST + ifp->if_flags |= IFF_MULTICAST; +#endif /* MULTICAST */ + + ifp->if_init = ieinit; + ifp->if_output = ether_output; + ifp->if_start = iestart; + ifp->if_ioctl = ieioctl; + ifp->if_reset = iereset; + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + +#if NBPFILTER > 0 + printf("\n"); + bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); +#endif + + if_attach(ifp); + { + struct ifaddr *ifa = ifp->if_addrlist; + struct sockaddr_dl *sdl; + while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK) + ifa = ifa->ifa_next; + + if(!ifa || !ifa->ifa_addr) return 1; + + /* Provide our ether address to the higher layers */ + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = 6; + sdl->sdl_slen = 0; + bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6); + return 1; + } +} + +/* + * What to do upon receipt of an interrupt. + */ +int ieintr(unit) + int unit; +{ + register struct ie_softc *ie = &ie_softc[unit]; + register u_short status; + + status = ie->scb->ie_status; + +loop: + if(status & (IE_ST_RECV | IE_ST_RNR)) { +#ifdef DEBUG + in_ierint++; + if(ie_debug & IED_RINT) + printf("ie%d: rint\n", unit); +#endif + ierint(unit, ie); +#ifdef DEBUG + in_ierint--; +#endif + } + + if(status & IE_ST_DONE) { +#ifdef DEBUG + in_ietint++; + if(ie_debug & IED_TINT) + printf("ie%d: tint\n", unit); +#endif + ietint(unit, ie); +#ifdef DEBUG + in_ietint--; +#endif + } + + if(status & IE_ST_RNR) { +#ifdef DEBUG + if(ie_debug & IED_RNR) + printf("ie%d: rnr\n", unit); +#endif + iernr(unit, ie); + } + +#ifdef DEBUG + if((status & IE_ST_ALLDONE) + && (ie_debug & IED_CNA)) + printf("ie%d: cna\n", unit); +#endif + + /* Don't ack interrupts which we didn't receive */ + ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn); + + if((status = ie->scb->ie_status) & IE_ST_WHENCE) + goto loop; + + return unit; +} + +/* + * Process a received-frame interrupt. + */ +static int ierint(unit, ie) + int unit; + struct ie_softc *ie; +{ + int i, status; + static int timesthru = 1024; + + i = ie->rfhead; + while(1) { + status = ie->rframes[i]->ie_fd_status; + + if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { + ie->arpcom.ac_if.if_ipackets++; + if(!--timesthru) { + ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align + + ie->scb->ie_err_resource + ie->scb->ie_err_overrun; + ie->scb->ie_err_crc = 0; + ie->scb->ie_err_align = 0; + ie->scb->ie_err_resource = 0; + ie->scb->ie_err_overrun = 0; + timesthru = 1024; + } + ie_readframe(unit, ie, i); + } else { + if(status & IE_FD_RNR) { + if(!(ie->scb->ie_status & IE_RU_READY)) { + ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]); + ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); + } + } + break; + } + i = (i + 1) % NFRAMES; + } + return 0; +} + +/* + * Process a command-complete interrupt. These are only generated by + * the transmission of frames. This routine is deceptively simple, since + * most of the real work is done by iestart(). + */ +static int ietint(unit, ie) + int unit; + struct ie_softc *ie; +{ + int status; + int i; + + ie->arpcom.ac_if.if_timer = 0; + ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + for(i = 0; i < ie->xmit_count; i++) { + status = ie->xmit_cmds[i]->ie_xmit_status; + + if(status & IE_XS_LATECOLL) { + printf("ie%d: late collision\n", unit); + ie->arpcom.ac_if.if_collisions++; + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_NOCARRIER) { + printf("ie%d: no carrier\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_LOSTCTS) { + printf("ie%d: lost CTS\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_UNDERRUN) { + printf("ie%d: DMA underrun\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_EXCMAX) { + printf("ie%d: too many collisions\n", unit); + ie->arpcom.ac_if.if_collisions += 16; + ie->arpcom.ac_if.if_oerrors++; + } else { + ie->arpcom.ac_if.if_opackets++; + ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL; + } + } + ie->xmit_count = 0; + + /* + * If multicast addresses were added or deleted while we were transmitting, + * ie_mc_reset() set the want_mcsetup flag indicating that we should do it. + */ + if(ie->want_mcsetup) { + mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb); + ie->want_mcsetup = 0; + } + + /* Wish I knew why this seems to be necessary... */ + ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; + + iestart(&ie->arpcom.ac_if); + return 0; /* shouldn't be necessary */ +} + +/* + * Process a receiver-not-ready interrupt. I believe that we get these + * when there aren't enough buffers to go around. For now (FIXME), we + * just restart the receiver, and hope everything's ok. + */ +static int iernr(unit, ie) + int unit; + struct ie_softc *ie; +{ +#ifdef doesnt_work + setup_rfa((caddr_t)ie->rframes[0], ie); + + ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); +#else + /* This doesn't work either, but it doesn't hang either. */ + command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */ + setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */ + + ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */ + +#endif + ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); + + ie->arpcom.ac_if.if_ierrors++; + return 0; +} + +#ifdef FILTER +/* + * Compare two Ether/802 addresses for equality, inlined and + * unrolled for speed. I'd love to have an inline assembler + * version of this... + */ +static inline int ether_equal(u_char *one, u_char *two) { + if(one[0] != two[0]) return 0; + if(one[1] != two[1]) return 0; + if(one[2] != two[2]) return 0; + if(one[3] != two[3]) return 0; + if(one[4] != two[4]) return 0; + if(one[5] != two[5]) return 0; + return 1; +} + +/* + * Check for a valid address. to_bpf is filled in with one of the following: + * 0 -> BPF doesn't get this packet + * 1 -> BPF does get this packet + * 2 -> BPF does get this packet, but we don't + * Return value is true if the packet is for us, and false otherwise. + * + * This routine is a mess, but it's also critical that it be as fast + * as possible. It could be made cleaner if we can assume that the + * only client which will fiddle with IFF_PROMISC is BPF. This is + * probably a good assumption, but we do not make it here. (Yet.) + */ +static inline int check_eh(struct ie_softc *ie, + struct ether_header *eh, + int *to_bpf) { + int i; + + switch(ie->promisc) { + case IFF_ALLMULTI: + /* + * Receiving all multicasts, but no unicasts except those destined for us. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */ +#endif + if(eh->ether_dhost[0] & 1) { + return 1; + } + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + return 0; + + case IFF_PROMISC: + /* + * Receiving all packets. These need to be passed on to BPF. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + /* If for us, accept and hand up to BPF */ + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 2; /* we don't need to see it */ +#endif + +#ifdef MULTICAST + /* + * Not a multicast, so BPF wants to see it but we don't. + */ + if(!(eh->ether_dhost[0] & 1)) return 1; + + /* + * If it's one of our multicast groups, accept it and pass it + * up. + */ + for(i = 0; i < ie->mcast_count; i++) { + if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) { +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 1; +#endif + return 1; + } + } +#endif /* MULTICAST */ + return 1; + + case IFF_ALLMULTI | IFF_PROMISC: + /* + * Acting as a multicast router, and BPF running at the same time. + * Whew! (Hope this is a fast machine...) + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + /* We want to see multicasts. */ + if(eh->ether_dhost[0] & 1) return 1; + + /* We want to see our own packets */ + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + + /* Anything else goes to BPF but nothing else. */ +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 2; +#endif + return 1; + + default: + /* + * Only accept unicast packets destined for us, or multicasts + * for groups that we belong to. For now, we assume that the + * '586 will only return packets that we asked it for. This + * isn't strictly true (it uses hashing for the multicast filter), + * but it will do in this case, and we want to get out of here + * as quickly as possible. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + return 1; + } + return 0; +} +#endif /* FILTER */ + +/* + * We want to isolate the bits that have meaning... This assumes that + * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds + * the size of the buffer, then we are screwed anyway. + */ +static inline int ie_buflen(struct ie_softc *ie, int head) { + return (ie->rbuffs[head]->ie_rbd_actual + & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); +} + +static inline int ie_packet_len(int unit, struct ie_softc *ie) { + int i; + int head = ie->rbhead; + int acc = 0; + + do { + if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef DEBUG + print_rbd(ie->rbuffs[ie->rbhead]); +#endif + log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", + unit, ie->rbhead); + iereset(unit); + return -1; + } + + i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; + + acc += ie_buflen(ie, head); + head = (head + 1) % NBUFFS; + } while(!i); + + return acc; +} + +/* + * Read data off the interface, and turn it into an mbuf chain. + * + * This code is DRAMATICALLY different from the previous version; this + * version tries to allocate the entire mbuf chain up front, given the + * length of the data available. This enables us to allocate mbuf + * clusters in many situations where before we would have had a long + * chain of partially-full mbufs. This should help to speed up the + * operation considerably. (Provided that it works, of course.) + */ +static inline int ieget(unit, ie, mp, ehp, to_bpf) + int unit; + struct ie_softc *ie; + struct mbuf **mp; + struct ether_header *ehp; + int *to_bpf; +{ + struct mbuf *m, *top, **mymp; + int i; + int offset; + int totlen, resid; + int thismboff; + int head; + + totlen = ie_packet_len(unit, ie); + if(totlen <= 0) return -1; + + i = ie->rbhead; + + /* + * Snarf the Ethernet header. + */ + bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp); + /* ignore cast-qual warning here */ + + /* + * As quickly as possible, check if this packet is for us. + * If not, don't waste a single cycle copying the rest of the + * packet in. + * This is only a consideration when FILTER is defined; i.e., when + * we are either running BPF or doing multicasting. + */ +#ifdef FILTER + if(!check_eh(ie, ehp, to_bpf)) { + ie_drop_packet_buffer(unit, ie); + ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */ + return -1; + } +#endif + totlen -= (offset = sizeof *ehp); + + MGETHDR(*mp, M_DONTWAIT, MT_DATA); + if(!*mp) { + ie_drop_packet_buffer(unit, ie); + return -1; + } + + m = *mp; + m->m_pkthdr.rcvif = &ie->arpcom.ac_if; + m->m_len = MHLEN; + resid = m->m_pkthdr.len = totlen; + top = 0; + mymp = ⊤ + + /* + * This loop goes through and allocates mbufs for all the data we will + * be copying in. It does not actually do the copying yet. + */ + do { /* while(resid > 0) */ + /* + * Try to allocate an mbuf to hold the data that we have. If we + * already allocated one, just get another one and stick it on the + * end (eventually). If we don't already have one, try to allocate + * an mbuf cluster big enough to hold the whole packet, if we think it's + * reasonable, or a single mbuf which may or may not be big enough. + * Got that? + */ + if(top) { + MGET(m, M_DONTWAIT, MT_DATA); + if(!m) { + m_freem(top); + ie_drop_packet_buffer(unit, ie); + return -1; + } + m->m_len = MLEN; + } + + if(resid >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if(m->m_flags & M_EXT) + m->m_len = min(resid, MCLBYTES); + } else { + if(resid < m->m_len) { + if(!top && resid + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = resid; + } + } + resid -= m->m_len; + *mymp = m; + mymp = &m->m_next; + } while(resid > 0); + + resid = totlen; + m = top; + thismboff = 0; + head = ie->rbhead; + + /* + * Now we take the mbuf chain (hopefully only one mbuf most of the + * time) and stuff the data into it. There are no possible failures + * at or after this point. + */ + while(resid > 0) { /* while there's stuff left */ + int thislen = ie_buflen(ie, head) - offset; + + /* + * If too much data for the current mbuf, then fill the current one + * up, go to the next one, and try again. + */ + if(thislen > m->m_len - thismboff) { + int newlen = m->m_len - thismboff; + bcopy((caddr_t)(ie->cbuffs[head] + offset), + mtod(m, caddr_t) + thismboff, (unsigned)newlen); + /* ignore cast-qual warning */ + m = m->m_next; + thismboff = 0; /* new mbuf, so no offset */ + offset += newlen; /* we are now this far into the packet */ + resid -= newlen; /* so there is this much left to get */ + continue; + } + + /* + * If there is more than enough space in the mbuf to hold the + * contents of this buffer, copy everything in, advance pointers, + * and so on. + */ + if(thislen < m->m_len - thismboff) { + bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ + mtod(m, caddr_t) + thismboff, (unsigned)thislen); + thismboff += thislen; /* we are this far into the mbuf */ + resid -= thislen; /* and this much is left */ + goto nextbuf; + } + + /* + * Otherwise, there is exactly enough space to put this buffer's + * contents into the current mbuf. Do the combination of the above + * actions. + */ + bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ + mtod(m, caddr_t) + thismboff, (unsigned)thislen); + m = m->m_next; + thismboff = 0; /* new mbuf, start at the beginning */ + resid -= thislen; /* and we are this far through */ + + /* + * Advance all the pointers. We can get here from either of the + * last two cases, but never the first. + */ +nextbuf: + offset = 0; + ie->rbuffs[head]->ie_rbd_actual = 0; + ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; + ie->rbhead = head = (head + 1) % NBUFFS; + ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + ie->rbtail = (ie->rbtail + 1) % NBUFFS; + } + + /* + * Unless something changed strangely while we were doing the copy, + * we have now copied everything in from the shared memory. + * This means that we are done. + */ + return 0; +} + +/* + * Read frame NUM from unit UNIT (pre-cached as IE). + * + * This routine reads the RFD at NUM, and copies in the buffers from + * the list of RBD, then rotates the RBD and RFD lists so that the receiver + * doesn't start complaining. Trailers are DROPPED---there's no point + * in wasting time on confusing code to deal with them. Hopefully, + * this machine will never ARP for trailers anyway. + */ +static void ie_readframe(unit, ie, num) + int unit; + struct ie_softc *ie; + int num; /* frame number to read */ +{ + struct ie_recv_frame_desc rfd; + struct mbuf *m = 0; + struct ether_header eh; +#if NBPFILTER > 0 + int bpf_gets_it = 0; +#endif + + bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc)); + + /* Immediately advance the RFD list, since we we have copied ours now. */ + ie->rframes[num]->ie_fd_status = 0; + ie->rframes[num]->ie_fd_last |= IE_FD_LAST; + ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST; + ie->rftail = (ie->rftail + 1) % NFRAMES; + ie->rfhead = (ie->rfhead + 1) % NFRAMES; + + if(rfd.ie_fd_status & IE_FD_OK) { + if( +#if NBPFILTER > 0 + ieget(unit, ie, &m, &eh, &bpf_gets_it) +#else + ieget(unit, ie, &m, &eh, (int *)0) +#endif + ) { + ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */ + return; + } + } + +#ifdef DEBUG + if(ie_debug & IED_READFRAME) { + printf("ie%d: frame from ether %s type %x\n", unit, + ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type); + } + if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL + && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) + printf("received trailer!\n"); +#endif + + if(!m) return; + +#ifdef FILTER + if(last_not_for_us) { + m_freem(last_not_for_us); + last_not_for_us = 0; + } + +#if NBPFILTER > 0 + /* + * Check for a BPF filter; if so, hand it up. + * Note that we have to stick an extra mbuf up front, because + * bpf_mtap expects to have the ether header at the front. + * It doesn't matter that this results in an ill-formatted mbuf chain, + * since BPF just looks at the data. (It doesn't try to free the mbuf, + * tho' it will make a copy for tcpdump.) + */ + if(bpf_gets_it) { + struct mbuf m0; + m0.m_len = sizeof eh; + m0.m_data = (caddr_t)&eh; + m0.m_next = m; + + /* Pass it up */ + bpf_mtap(ie->ie_bpf, &m0); + } + /* + * A signal passed up from the filtering code indicating that the + * packet is intended for BPF but not for the protocol machinery. + * We can save a few cycles by not handing it off to them. + */ + if(bpf_gets_it == 2) { + last_not_for_us = m; + return; + } +#endif /* NBPFILTER > 0 */ + /* + * In here there used to be code to check destination addresses upon + * receipt of a packet. We have deleted that code, and replaced it + * with code to check the address much earlier in the cycle, before + * copying the data in; this saves us valuable cycles when operating + * as a multicast router or when using BPF. + */ +#endif /* FILTER */ + + eh.ether_type = ntohs(eh.ether_type); + + /* + * Finally pass this packet up to higher layers. + */ + ether_input(&ie->arpcom.ac_if, &eh, m); +} + +static void ie_drop_packet_buffer(int unit, struct ie_softc *ie) { + int i; + + do { + /* + * This means we are somehow out of sync. So, we reset the + * adapter. + */ + if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef DEBUG + print_rbd(ie->rbuffs[ie->rbhead]); +#endif + log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", + unit, ie->rbhead); + iereset(unit); + return; + } + + i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST; + + ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST; + ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0; + ie->rbhead = (ie->rbhead + 1) % NBUFFS; + ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + ie->rbtail = (ie->rbtail + 1) % NBUFFS; + } while(!i); +} + + +/* + * Start transmission on an interface. + */ +static void +iestart(ifp) + struct ifnet *ifp; +{ + struct ie_softc *ie = &ie_softc[ifp->if_unit]; + struct mbuf *m0, *m; + unsigned char *buffer; + u_short len; + /* This is not really volatile, in this routine, but it makes gcc happy. */ + volatile u_short *bptr = &ie->scb->ie_command_list; + + if(!(ifp->if_flags & IFF_RUNNING)) + return; + if(ifp->if_flags & IFF_OACTIVE) + return; + + do { + IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m); + if(!m) + break; + + buffer = ie->xmit_cbuffs[ie->xmit_count]; + len = 0; + + for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + + m_freem(m0); + len = max(len, ETHERMINLEN); + +#if NBPFILTER > 0 + /* + * See if bpf is listening on this interface, let it see the packet + * before we commit it to the wire. + */ + if(ie->ie_bpf) + bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len); +#endif + + ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len; + ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff; + ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf = + MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]); + + ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; + ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0; + ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc = + MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]); + + *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]); + bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link; + ie->xmit_count++; + } while(ie->xmit_count < 2); + + /* + * If we queued up anything for transmission, send it. + */ + if(ie->xmit_count) { + ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |= + IE_CMD_LAST | IE_CMD_INTR; + + /* + * By passing the command pointer as a null, we tell + * command_and_wait() to pretend that this isn't an action + * command. I wish I understood what was happening here. + */ + command_and_wait(ifp->if_unit, IE_CU_START, 0, 0); + ifp->if_flags |= IFF_OACTIVE; + } + + return; +} + +/* + * Check to see if there's an 82586 out there. + */ +int check_ie_present(unit, where, size) + int unit; + caddr_t where; + unsigned size; +{ + volatile struct ie_sys_conf_ptr *scp; + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + u_long realbase; + int s; + + s = splimp(); + + realbase = (u_long)where + size - (1 << 24); + + scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR); + bzero((char *)scp, sizeof *scp); /* ignore cast-qual */ + + /* + * First we put the ISCP at the bottom of memory; this tests to make + * sure that our idea of the size of memory is the same as the controller's. + * This is NOT where the ISCP will be in normal operation. + */ + iscp = (volatile struct ie_int_sys_conf_ptr *)where; + bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ + + scb = (volatile struct ie_sys_ctl_block *)where; + bzero((char *)scb, sizeof *scb); /* ignore cast-qual */ + + scp->ie_bus_use = 0; /* 16-bit */ + scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */ + (volatile caddr_t)realbase); + + iscp->ie_busy = 1; + iscp->ie_scb_offset = MK_16(realbase, scb) + 256; + + (*ie_softc[unit].ie_reset_586)(unit); + (*ie_softc[unit].ie_chan_attn)(unit); + + DELAY(100); /* wait a while... */ + + if(iscp->ie_busy) { + splx(s); + return 0; + } + + /* + * Now relocate the ISCP to its real home, and reset the controller + * again. + */ + iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR - + sizeof(struct ie_int_sys_conf_ptr))); + bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ + + scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase); + /* ignore cast-qual */ + + iscp->ie_busy = 1; + iscp->ie_scb_offset = MK_16(realbase, scb); + + (*ie_softc[unit].ie_reset_586)(unit); + (*ie_softc[unit].ie_chan_attn)(unit); + + DELAY(100); + + if(iscp->ie_busy) { + splx(s); + return 0; + } + + ie_softc[unit].iosize = size; + ie_softc[unit].iomem = (caddr_t)realbase; + + ie_softc[unit].iscp = iscp; + ie_softc[unit].scb = scb; + + /* + * Acknowledge any interrupts we may have caused... + */ + ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); + splx(s); + + return 1; +} + +/* + * Divine the memory size of ie board UNIT. + * Better hope there's nothing important hiding just below the ie card... + */ +static void find_ie_mem_size(unit) + int unit; +{ + unsigned size; + + ie_softc[unit].iosize = 0; + + for(size = 65536; size >= 16384; size -= 16384) { + if(check_ie_present(unit, ie_softc[unit].iomembot, size)) { + return; + } + } + + return; +} + +void sl_reset_586(unit) + int unit; +{ + outb(PORT + IEATT_RESET, 0); +} + +void sl_chan_attn(unit) + int unit; +{ + outb(PORT + IEATT_ATTN, 0); +} + +void sl_read_ether(unit, addr) + int unit; + unsigned char addr[6]; +{ + int i; + + for(i = 0; i < 6; i++) + addr[i] = inb(PORT + i); +} + + +static void +iereset(unit) + int unit; +{ + int s = splimp(); + + if(unit >= NIE) { + splx(s); + return; + } + + printf("ie%d: reset\n", unit); + ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP; + ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); + + /* + * Stop i82586 dead in its tracks. + */ + if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) + printf("ie%d: abort commands timed out\n", unit); + + if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) + printf("ie%d: disable commands timed out\n", unit); + +#ifdef notdef + if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize)) + panic("ie disappeared!\n"); +#endif + + ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP; + ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); + + splx(s); + return; +} + +/* + * This is called if we time out. + */ +static void +chan_attn_timeout(rock) + caddr_t rock; +{ + *(int *)rock = 1; +} + +/* + * Send a command to the controller and wait for it to either + * complete or be accepted, depending on the command. If the + * command pointer is null, then pretend that the command is + * not an action command. If the command pointer is not null, + * and the command is an action command, wait for + * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK + * to become true. + */ +static int command_and_wait(unit, cmd, pcmd, mask) + int unit; + int cmd; + volatile void *pcmd; + int mask; +{ + volatile struct ie_cmd_common *cc = pcmd; + volatile int timedout = 0; + extern int hz; + + ie_softc[unit].scb->ie_command = (u_short)cmd; + + if(IE_ACTION_COMMAND(cmd) && pcmd) { + (*ie_softc[unit].ie_chan_attn)(unit); + + /* + * According to the packet driver, the minimum timeout should be + * .369 seconds, which we round up to .37. + */ + timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100); + /* ignore cast-qual */ + + /* + * Now spin-lock waiting for status. This is not a very nice + * thing to do, but I haven't figured out how, or indeed if, we + * can put the process waiting for action to sleep. (We may + * be getting called through some other timeout running in the + * kernel.) + */ + while(1) { + if((cc->ie_cmd_status & mask) || timedout) + break; + } + + untimeout(chan_attn_timeout, (caddr_t)&timedout); + /* ignore cast-qual */ + + return timedout; + } else { + + /* + * Otherwise, just wait for the command to be accepted. + */ + (*ie_softc[unit].ie_chan_attn)(unit); + + while(ie_softc[unit].scb->ie_command) + ; /* spin lock */ + + return 0; + } +} + +/* + * Run the time-domain reflectometer... + */ +static void run_tdr(unit, cmd) + int unit; + struct ie_tdr_cmd *cmd; +{ + int result; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + cmd->ie_tdr_time = 0; + + ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd); + cmd->ie_tdr_time = 0; + + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)) + result = 0x2000; + else + result = cmd->ie_tdr_time; + + ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, + ie_softc[unit].ie_chan_attn); + + if(result & IE_TDR_SUCCESS) + return; + + if(result & IE_TDR_XCVR) { + printf("ie%d: transceiver problem\n", unit); + } else if(result & IE_TDR_OPEN) { + printf("ie%d: TDR detected an open %d clocks away\n", unit, + result & IE_TDR_TIME); + } else if(result & IE_TDR_SHORT) { + printf("ie%d: TDR detected a short %d clocks away\n", unit, + result & IE_TDR_TIME); + } else { + printf("ie%d: TDR returned unknown status %x\n", result); + } +} + +static void start_receiver(unit) + int unit; +{ + int s = splimp(); + + ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); + + ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); + + splx(s); +} + +/* + * Here is a helper routine for iernr() and ieinit(). This sets up + * the RFA. + */ +static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) { + volatile struct ie_recv_frame_desc *rfd = (void *)ptr; + volatile struct ie_recv_buf_desc *rbd; + int i; + int unit = ie - &ie_softc[0]; + + /* First lay them out */ + for(i = 0; i < NFRAMES; i++) { + ie->rframes[i] = rfd; + bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */ + rfd++; + } + + ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */ + + /* Now link them together */ + for(i = 0; i < NFRAMES; i++) { + ie->rframes[i]->ie_fd_next = + MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]); + } + + /* Finally, set the EOL bit on the last one. */ + ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST; + + /* + * Now lay out some buffers for the incoming frames. Note that + * we set aside a bit of slop in each buffer, to make sure that + * we have enough space to hold a single frame in every buffer. + */ + rbd = (void *)ptr; + + for(i = 0; i < NBUFFS; i++) { + ie->rbuffs[i] = rbd; + bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */ + ptr = (caddr_t)Align(ptr + sizeof *rbd); + rbd->ie_rbd_length = IE_RBUF_SIZE; + rbd->ie_rbd_buffer = MK_24(MEM, ptr); + ie->cbuffs[i] = (void *)ptr; + ptr += IE_RBUF_SIZE; + rbd = (void *)ptr; + } + + /* Now link them together */ + for(i = 0; i < NBUFFS; i++) { + ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]); + } + + /* Tag EOF on the last one */ + ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST; + + /* We use the head and tail pointers on receive to keep track of + * the order in which RFDs and RBDs are used. */ + ie->rfhead = 0; + ie->rftail = NFRAMES - 1; + ie->rbhead = 0; + ie->rbtail = NBUFFS - 1; + + ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); + ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]); + + ptr = Align(ptr); + return ptr; +} + +/* + * Run the multicast setup command. + * Call at splimp(). + */ +static int mc_setup(int unit, caddr_t ptr, + volatile struct ie_sys_ctl_block *scb) { + struct ie_softc *ie = &ie_softc[unit]; + volatile struct ie_mcast_cmd *cmd = (void *)ptr; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + /* ignore cast-qual */ + bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs, + ie->mcast_count * sizeof *ie->mcast_addrs); + + cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */ + + scb->ie_command_list = MK_16(MEM, cmd); + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: multicast address setup command failed\n", unit); + return 0; + } + return 1; +} + +/* + * This routine takes the environment generated by check_ie_present() + * and adds to it all the other structures we need to operate the adapter. + * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, + * starting the receiver unit, and clearing interrupts. + * + * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. + */ +static void +ieinit(unit) + int unit; +{ + struct ie_softc *ie = &ie_softc[unit]; + volatile struct ie_sys_ctl_block *scb = ie->scb; + caddr_t ptr; + + ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */ + + /* + * Send the configure command first. + */ + { + volatile struct ie_config_cmd *cmd = (void *)ptr; + + ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10); + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + scb->ie_command_list = MK_16(MEM, cmd); + + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: configure command failed\n", unit); + return; + } + } + /* + * Now send the Individual Address Setup command. + */ + { + volatile struct ie_iasetup_cmd *cmd = (void *)ptr; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address, + sizeof cmd->ie_address); /* ignore cast-qual */ + + scb->ie_command_list = MK_16(MEM, cmd); + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: individual address setup command failed\n", unit); + return; + } + } + + /* + * Now run the time-domain reflectometer. + */ + run_tdr(unit, (void *)ptr); + + /* + * Acknowledge any interrupts we have generated thus far. + */ + ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); + + /* + * Set up the RFA. + */ + ptr = setup_rfa(ptr, ie); + + /* + * Finally, the transmit command and buffer are the last little bit of work. + */ + ie->xmit_cmds[0] = (void *)ptr; + ptr += sizeof *ie->xmit_cmds[0]; + ptr = Align(ptr); + ie->xmit_buffs[0] = (void *)ptr; + ptr += sizeof *ie->xmit_buffs[0]; + ptr = Align(ptr); + + /* Second transmit command */ + ie->xmit_cmds[1] = (void *)ptr; + ptr += sizeof *ie->xmit_cmds[1]; + ptr = Align(ptr); + ie->xmit_buffs[1] = (void *)ptr; + ptr += sizeof *ie->xmit_buffs[1]; + ptr = Align(ptr); + + /* Both transmit buffers */ + ie->xmit_cbuffs[0] = (void *)ptr; + ptr += IE_BUF_LEN; + ptr = Align(ptr); + ie->xmit_cbuffs[1] = (void *)ptr; + + bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */ + bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */ + bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */ + bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */ + + /* + * This must be coordinated with iestart() and ietint(). + */ + ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; + + ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */ + start_receiver(unit); + return; +} + +static void ie_stop(unit) + int unit; +{ + command_and_wait(unit, IE_RU_DISABLE, 0, 0); +} + +static int +ieioctl(ifp, command, data) + struct ifnet *ifp; + int command; + caddr_t data; +{ + struct ifaddr *ifa = (struct ifaddr *)data; + struct ie_softc *ie = &ie_softc[ifp->if_unit]; + int s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ieinit(ifp->if_unit); + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif /* INET */ + +#ifdef NS + /* This magic copied from if_is.c; I don't use XNS, so I have no + * way of telling if this actually works or not. + */ + case AF_NS: + { + struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if(ns_nullhost(*ina)) { + ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr); + } else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)ie->arpcom.ac_enaddr, + sizeof ie->arpcom.ac_enaddr); + } + + ieinit(ifp->if_unit); + } + break; +#endif /* NS */ + + default: + ieinit(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + /* + * Note that this device doesn't have an "all multicast" mode, so we + * must turn on promiscuous mode and do the filtering manually. + */ + if((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~IFF_RUNNING; + ie_stop(ifp->if_unit); + } else if((ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING) == 0) { + ie_softc[ifp->if_unit].promisc = + ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + ieinit(ifp->if_unit); + } else if(ie_softc[ifp->if_unit].promisc ^ + (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { + ie_softc[ifp->if_unit].promisc = + ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + ieinit(ifp->if_unit); + } + break; + +#ifdef MULTICAST + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Update multicast listeners + */ + error = ((command == SIOCADDMULTI) + ? ether_addmulti((struct ifreq *)data, &ie->arpcom) + : ether_delmulti((struct ifreq *)data, &ie->arpcom)); + + if(error == ENETRESET) { + /* reset multicast filtering */ + ie_mc_reset(ifp->if_unit); + error = 0; + } + break; +#endif /* MULTICAST */ + + default: + error = EINVAL; + } + + splx(s); + return error; +} + +#ifdef MULTICAST +static void ie_mc_reset(int unit) { + struct ie_softc *ie = &ie_softc[unit]; + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Step through the list of addresses. + */ + ie->mcast_count = 0; + ETHER_FIRST_MULTI(step, &ie->arpcom, enm); + while(enm) { + if(ie->mcast_count >= MAXMCAST + || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI; + ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0); + goto setflag; + } + + bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6); + ie->mcast_count++; + ETHER_NEXT_MULTI(step, enm); + } + +setflag: + ie->want_mcsetup = 1; +} + +#endif + +#ifdef DEBUG +void print_rbd(volatile struct ie_recv_buf_desc *rbd) { + printf("RBD at %08lx:\n" + "actual %04x, next %04x, buffer %08x\n" + "length %04x, mbz %04x\n", + (unsigned long)rbd, + rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer, + rbd->ie_rbd_length, rbd->mbz); +} +#endif /* DEBUG */ +#endif /* NIE > 0 */ + diff --git a/sys/dev/ie/if_iereg.h b/sys/dev/ie/if_iereg.h new file mode 100644 index 0000000..3588b84 --- /dev/null +++ b/sys/dev/ie/if_iereg.h @@ -0,0 +1,24 @@ +/* + * $Id$ + * definitions for AT&T StarLAN 10 etc... + */ + +#define IEATT_RESET 0 /* any write here resets the 586 */ +#define IEATT_ATTN 1 /* any write here sends a Chan attn */ +#define IEATT_REVISION 6 /* read here to figure out this board */ +#define IEATT_ATTRIB 7 /* more information about this board */ + +#define SL_BOARD(x) ((x) & 0x0f) +#define SL_REV(x) ((x) >> 4) + +#define SL1_BOARD 0 +#define SL10_BOARD 1 +#define EN100_BOARD 2 +#define SLFIBER_BOARD 3 + +#define SL_ATTR_WIDTH 0x04 /* bus width: clear -> 8-bit */ +#define SL_ATTR_SPEED 0x08 /* medium speed: clear -> 10 Mbps */ +#define SL_ATTR_CODING 0x10 /* encoding: clear -> Manchester */ +#define SL_ATTR_HBW 0x20 /* host bus width: clear -> 16-bit */ +#define SL_ATTR_TYPE 0x40 /* medium type: clear -> Ethernet */ +#define SL_ATTR_BOOTROM 0x80 /* set -> boot ROM present */ diff --git a/sys/dev/kbd/kbdtables.h b/sys/dev/kbd/kbdtables.h new file mode 100644 index 0000000..a923c45 --- /dev/null +++ b/sys/dev/kbd/kbdtables.h @@ -0,0 +1,859 @@ +/* + * Copyright (C) 1992, 1993, 1994 Søren Schmidt + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Søren Schmidt Email: sos@login.dkuug.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!login!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + * + * @(#)kbdtables.h 1.3 940123 + * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $ + */ + +#define SET8 0x80 /* eight bit for emacs SET8-key */ + +#ifdef DKKEYMAP +keymap_t key_map = { 0x69, /* DK iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00, +/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef UKKEYMAP +keymap_t key_map = { 0x69, /* uk iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef GRKEYMAP +keymap_t key_map = { 0x69, /* german iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01, +/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01, +/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00, +/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef SWKEYMAP +keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00, +/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01, +/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00, +/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef RUKEYMAP +keymap_t key_map = { 0xe9, /* keys number */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * ------------------------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '@', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '"', NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x00, +/* sc=29 */ '`', '~', NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00, +/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02, +/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* extended (ALTGR LOCK keys) */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00, +/* sc=02 */ '!', '1', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '"', '2', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00, +/* sc=04 */ '\'', '3', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ ';', '4', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ ':', '5', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ ',', '6', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00, +/* sc=08 */ '.', '7', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '*', '8', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '(', '9', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ ')', '0', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, F(16), NOP, NOP, SET8|0x09, F(16), NOP, NOP, 0x77, 0x00, +/* sc=10 */ 0xca, 0xea, 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01, +/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01, +/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01, +/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01, +/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01, +/* sc=15 */ 0xce, 0xee, 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01, +/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01, +/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01, +/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01, +/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01, +/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x01, +/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x01, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01, +/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01, +/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01, +/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01, +/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01, +/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01, +/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01, +/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01, +/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01, +/* sc=27 */ 0xd6, 0xf6, NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xdc, 0xfc, NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xa3, 0xb3, NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x01, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00, +/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01, +/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01, +/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01, +/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01, +/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01, +/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01, +/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01, +/* sc=33 */ 0xc2, 0xe2, NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x01, +/* sc=34 */ 0xc0, 0xe0, NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x01, +/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00, +/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02, +/* sc=4c */ F(48), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0xC2, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; + +#endif + +#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP) +keymap_t key_map = { 0x69, /* US iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00, +/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00, +/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), RBT, F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; + +#endif + +fkeytab_t fkey_tab[60] = { +/* 00-03 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3}, +/* 04-07 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3}, +/* 08-0B */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3}, +/* 0C-0F */ {"\033[W", 3}, {"\033[X", 3}, {"\033[Y", 3}, {"\033[Z", 3}, +/* 10-13 */ {"\033[a", 3}, {"\033[b", 3}, {"\033[c", 3}, {"\033[d", 3}, +/* 14-17 */ {"\033[e", 3}, {"\033[f", 3}, {"\033[g", 3}, {"\033[h", 3}, +/* 18-1B */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3}, +/* 1C-1F */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3}, +/* 20-23 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3}, +/* 24-27 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3}, +/* 28-2B */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3}, +/* 2C-2F */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3}, +/* 30-33 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1}, +/* 34-37 */ {"\033[D", 3}, {"\177" , 1}, {"\033[C", 3}, {"+" , 1}, +/* 38-3B */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3} +}; diff --git a/sys/dev/mcd/mcd.c b/sys/dev/mcd/mcd.c new file mode 100644 index 0000000..683b0e1 --- /dev/null +++ b/sys/dev/mcd/mcd.c @@ -0,0 +1,1335 @@ +/* + * Copyright 1993 by Holger Veit (data part) + * Copyright 1993 by Brian Moore (audio part) + * Changes Copyright 1993 by Gary Clark II + * + * Rewrote probe routine to work on newer Mitsumi drives. + * Additional changes (C) 1994 by Jordan K. Hubbard + * + * 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 was developed by Holger Veit and Brian Moore + * for use with "386BSD" and similar operating systems. + * "Similar operating systems" includes mainly non-profit oriented + * systems for research and education, including but not restricted to + * "NetBSD", "FreeBSD", "Mach" (by CMU). + * 4. Neither the name of the developer(s) nor the name "386BSD" + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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(S) 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. + * + * $Id: mcd.c,v 1.15 1994/04/20 07:06:41 davidg Exp $ + */ +static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; + +#include "mcd.h" +#if NMCD > 0 +#include "types.h" +#include "param.h" +#include "systm.h" +#include "conf.h" +#include "file.h" +#include "buf.h" +#include "stat.h" +#include "uio.h" +#include "ioctl.h" +#include "cdio.h" +#include "errno.h" +#include "dkbad.h" +#include "disklabel.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "mcdreg.h" + +/* user definable options */ +/*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */ +/*#define MCDMINI*/ /* define for a mini configuration for boot kernel */ + + +#ifdef MCDMINI +#define MCD_TRACE(fmt,a,b,c,d) +#ifdef MCD_TO_WARNING_ON +#undef MCD_TO_WARNING_ON +#endif +#else +#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}} +#endif + +#define mcd_part(dev) ((minor(dev)) & 7) +#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3) +#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6) +#define RAW_PART 0 + +/* flags */ +#define MCDOPEN 0x0001 /* device opened */ +#define MCDVALID 0x0002 /* parameters loaded */ +#define MCDINIT 0x0004 /* device is init'd */ +#define MCDWAIT 0x0008 /* waiting for something */ +#define MCDLABEL 0x0010 /* label is read */ +#define MCDPROBING 0x0020 /* probing */ +#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */ +#define MCDVOLINFO 0x0080 /* already read volinfo */ +#define MCDTOC 0x0100 /* already read toc */ +#define MCDMBXBSY 0x0200 /* local mbx is busy */ + +/* status */ +#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ +#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ +#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ +#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ + +/* These are apparently the different states a mitsumi can get up to */ +#define MCDCDABSENT 0x0030 +#define MCDCDPRESENT 0x0020 +#define MCDSCLOSED 0x0080 +#define MCDSOPEN 0x00a0 + +/* toc */ +#define MCD_MAXTOCS 104 /* from the Linux driver */ +#define MCD_LASTPLUS1 170 /* special toc entry */ + +struct mcd_mbx { + short unit; + short port; + short retry; + short nblk; + int sz; + u_long skip; + struct buf *bp; + int p_offset; + short count; +}; + +struct mcd_data { + short config; + short flags; + short status; + int blksize; + u_long disksize; + int iobase; + struct disklabel dlabel; + int partflags[MAXPARTITIONS]; + int openflags; + struct mcd_volinfo volinfo; +#ifndef MCDMINI + struct mcd_qchninfo toc[MCD_MAXTOCS]; + short audio_status; + struct mcd_read2 lastpb; +#endif + short debug; + struct buf head; /* head of buf queue */ + struct mcd_mbx mbx; +} mcd_data[NMCD]; + +/* reader state machine */ +#define MCD_S_BEGIN 0 +#define MCD_S_BEGIN1 1 +#define MCD_S_WAITSTAT 2 +#define MCD_S_WAITMODE 3 +#define MCD_S_WAITREAD 4 + +/* prototypes */ +int mcdopen(dev_t dev); +int mcdclose(dev_t dev); +void mcdstrategy(struct buf *bp); +int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags); +int mcdsize(dev_t dev); +static void mcd_done(struct mcd_mbx *mbx); +static void mcd_start(int unit); +static int mcd_getdisklabel(int unit); +static void mcd_configure(struct mcd_data *cd); +static int mcd_get(int unit, char *buf, int nmax); +static void mcd_setflags(int unit,struct mcd_data *cd); +static int mcd_getstat(int unit,int sflg); +static int mcd_send(int unit, int cmd,int nretrys); +static int bcd2bin(bcd_t b); +static bcd_t bin2bcd(int b); +static void hsg2msf(int hsg, bcd_t *msf); +static int msf2hsg(bcd_t *msf); +static int mcd_volinfo(int unit); +static int mcd_waitrdy(int port,int dly); +static void mcd_doread(int state, struct mcd_mbx *mbxin); +#ifndef MCDMINI +static int mcd_setmode(int unit, int mode); +static int mcd_getqchan(int unit, struct mcd_qchninfo *q); +static int mcd_subchan(int unit, struct ioc_read_subchannel *sc); +static int mcd_toc_header(int unit, struct ioc_toc_header *th); +static int mcd_read_toc(int unit); +static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te); +static int mcd_stop(int unit); +static int mcd_playtracks(int unit, struct ioc_play_track *pt); +static int mcd_play(int unit, struct mcd_read2 *pb); +static int mcd_pause(int unit); +static int mcd_resume(int unit); +#endif + +extern int hz; +extern int mcd_probe(struct isa_device *dev); +extern int mcd_attach(struct isa_device *dev); +struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; + +#define mcd_put(port,byte) outb(port,byte) + +#define MCD_RETRYS 5 +#define MCD_RDRETRYS 8 + +#define MCDBLK 2048 /* for cooked mode */ +#define MCDRBLK 2352 /* for raw mode */ + +/* several delays */ +#define RDELAY_WAITSTAT 300 +#define RDELAY_WAITMODE 300 +#define RDELAY_WAITREAD 800 + +#define DELAY_STATUS 10000l /* 10000 * 1us */ +#define DELAY_GETREPLY 200000l /* 200000 * 2us */ +#define DELAY_SEEKREAD 20000l /* 20000 * 1us */ +#define mcd_delay DELAY + +int mcd_attach(struct isa_device *dev) +{ + struct mcd_data *cd = mcd_data + dev->id_unit; + int i; + + cd->iobase = dev->id_iobase; + cd->flags |= MCDINIT; + cd->openflags = 0; + for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0; + +#ifdef NOTYET + /* wire controller for interrupts and dma */ + mcd_configure(cd); +#endif + + return 1; +} + +int mcdopen(dev_t dev) +{ + int unit,part,phys; + struct mcd_data *cd; + + unit = mcd_unit(dev); + if (unit >= NMCD) + return ENXIO; + + cd = mcd_data + unit; + part = mcd_part(dev); + phys = mcd_phys(dev); + + /* not initialized*/ + if (!(cd->flags & MCDINIT)) + return ENXIO; + + /* invalidated in the meantime? mark all open part's invalid */ + if (!(cd->flags & MCDVALID) && cd->openflags) + return ENXIO; + + if (mcd_getstat(unit,1) < 0) + return ENXIO; + + /* XXX get a default disklabel */ + mcd_getdisklabel(unit); + + if (mcdsize(dev) < 0) { + printf("mcd%d: failed to get disk size\n",unit); + return ENXIO; + } else + cd->flags |= MCDVALID; + +MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n", + part,cd->disksize,cd->blksize,0); + + if (part == RAW_PART || + (part < cd->dlabel.d_npartitions && + cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) { + cd->partflags[part] |= MCDOPEN; + cd->openflags |= (1<<part); + if (part == RAW_PART && phys != 0) + cd->partflags[part] |= MCDREADRAW; + return 0; + } + + return ENXIO; +} + +int mcdclose(dev_t dev) +{ + int unit,part,phys; + struct mcd_data *cd; + + unit = mcd_unit(dev); + if (unit >= NMCD) + return ENXIO; + + cd = mcd_data + unit; + part = mcd_part(dev); + phys = mcd_phys(dev); + + if (!(cd->flags & MCDINIT)) + return ENXIO; + + mcd_getstat(unit,1); /* get status */ + + /* close channel */ + cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW); + cd->openflags &= ~(1<<part); + MCD_TRACE("close: partition=%d\n",part,0,0,0); + + return 0; +} + +void +mcdstrategy(struct buf *bp) +{ + struct mcd_data *cd; + struct buf *qp; + int s; + + int unit = mcd_unit(bp->b_dev); + + cd = mcd_data + unit; + + /* test validity */ +/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n", + bp,unit,bp->b_blkno,bp->b_bcount);*/ + if (unit >= NMCD || bp->b_blkno < 0) { + printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n", + unit, bp->b_blkno, bp->b_bcount); + pg("mcd: mcdstratregy failure"); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + + /* if device invalidated (e.g. media change, door open), error */ + if (!(cd->flags & MCDVALID)) { +MCD_TRACE("strategy: drive not valid\n",0,0,0,0); + bp->b_error = EIO; + goto bad; + } + + /* read only */ + if (!(bp->b_flags & B_READ)) { + bp->b_error = EROFS; + goto bad; + } + + /* no data to read */ + if (bp->b_bcount == 0) + goto done; + + /* for non raw access, check partition limits */ + if (mcd_part(bp->b_dev) != RAW_PART) { + if (!(cd->flags & MCDLABEL)) { + bp->b_error = EIO; + goto bad; + } + /* adjust transfer if necessary */ + if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { + goto done; + } + } else { + bp->b_pblkno = bp->b_blkno; + bp->b_resid = 0; + } + + /* queue it */ + qp = &cd->head; + s = splbio(); + disksort(qp,bp); + splx(s); + + /* now check whether we can perform processing */ + mcd_start(unit); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + bp->b_resid = bp->b_bcount; + biodone(bp); + return; +} + +static void mcd_start(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct buf *bp, *qp = &cd->head; + struct partition *p; + int part; + register s = splbio(); + + if (cd->flags & MCDMBXBSY) + return; + + if ((bp = qp->b_actf) != 0) { + /* block found to process, dequeue */ + /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ + qp->b_actf = bp->b_actf; + splx(s); + } else { + /* nothing to do */ + splx(s); + return; + } + + /* changed media? */ + if (!(cd->flags & MCDVALID)) { + MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0); + return; + } + + p = cd->dlabel.d_partitions + mcd_part(bp->b_dev); + + cd->flags |= MCDMBXBSY; + cd->mbx.unit = unit; + cd->mbx.port = cd->iobase; + cd->mbx.retry = MCD_RETRYS; + cd->mbx.bp = bp; + cd->mbx.p_offset = p->p_offset; + + /* calling the read routine */ + mcd_doread(MCD_S_BEGIN,&(cd->mbx)); + /* triggers mcd_start, when successful finished */ + return; +} + +int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags) +{ + struct mcd_data *cd; + int unit,part; + + unit = mcd_unit(dev); + part = mcd_part(dev); + cd = mcd_data + unit; + +#ifdef MCDMINI + return ENOTTY; +#else + if (!(cd->flags & MCDVALID)) + return EIO; +MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0); + + switch (cmd) { + case DIOCSBAD: + return EINVAL; + case DIOCGDINFO: + case DIOCGPART: + case DIOCWDINFO: + case DIOCSDINFO: + case DIOCWLABEL: + return ENOTTY; + case CDIOCPLAYTRACKS: + return mcd_playtracks(unit, (struct ioc_play_track *) addr); + case CDIOCPLAYBLOCKS: + return mcd_play(unit, (struct mcd_read2 *) addr); + case CDIOCREADSUBCHANNEL: + return mcd_subchan(unit, (struct ioc_read_subchannel *) addr); + case CDIOREADTOCHEADER: + return mcd_toc_header(unit, (struct ioc_toc_header *) addr); + case CDIOREADTOCENTRYS: + return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr); + case CDIOCSETPATCH: + case CDIOCGETVOL: + case CDIOCSETVOL: + case CDIOCSETMONO: + case CDIOCSETSTERIO: + case CDIOCSETMUTE: + case CDIOCSETLEFT: + case CDIOCSETRIGHT: + return EINVAL; + case CDIOCRESUME: + return mcd_resume(unit); + case CDIOCPAUSE: + return mcd_pause(unit); + case CDIOCSTART: + return EINVAL; + case CDIOCSTOP: + return mcd_stop(unit); + case CDIOCEJECT: + return EINVAL; + case CDIOCSETDEBUG: + cd->debug = 1; + return 0; + case CDIOCCLRDEBUG: + cd->debug = 0; + return 0; + case CDIOCRESET: + return EINVAL; + default: + return ENOTTY; + } + /*NOTREACHED*/ +#endif /*!MCDMINI*/ +} + +/* this could have been taken from scsi/cd.c, but it is not clear + * whether the scsi cd driver is linked in + */ +static int mcd_getdisklabel(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (cd->flags & MCDLABEL) + return -1; + + bzero(&cd->dlabel,sizeof(struct disklabel)); + strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16); + strncpy(cd->dlabel.d_packname,"unknown ",16); + cd->dlabel.d_secsize = cd->blksize; + cd->dlabel.d_nsectors = 100; + cd->dlabel.d_ntracks = 1; + cd->dlabel.d_ncylinders = (cd->disksize/100)+1; + cd->dlabel.d_secpercyl = 100; + cd->dlabel.d_secperunit = cd->disksize; + cd->dlabel.d_rpm = 300; + cd->dlabel.d_interleave = 1; + cd->dlabel.d_flags = D_REMOVABLE; + cd->dlabel.d_npartitions= 1; + cd->dlabel.d_partitions[0].p_offset = 0; + cd->dlabel.d_partitions[0].p_size = cd->disksize; + cd->dlabel.d_partitions[0].p_fstype = 9; + cd->dlabel.d_magic = DISKMAGIC; + cd->dlabel.d_magic2 = DISKMAGIC; + cd->dlabel.d_checksum = dkcksum(&cd->dlabel); + + cd->flags |= MCDLABEL; + return 0; +} + +int mcdsize(dev_t dev) +{ + int size; + int unit = mcd_unit(dev); + struct mcd_data *cd = mcd_data + unit; + + if (mcd_volinfo(unit) >= 0) { + cd->blksize = MCDBLK; + size = msf2hsg(cd->volinfo.vol_msf); + cd->disksize = size * (MCDBLK/DEV_BSIZE); + return 0; + } + return -1; +} + +/*************************************************************** + * lower level of driver starts here + **************************************************************/ + +#ifdef NOTDEF +static char +irqs[] = { + 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, + 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 +}; + +static char +drqs[] = { + 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, +}; +#endif + +static void +mcd_configure(struct mcd_data *cd) +{ + outb(cd->iobase+mcd_config,cd->config); +} + +/* Wait for non-busy - return 0 on timeout */ +static int +twiddle_thumbs(int port, int unit, int count, char *whine) +{ + int i; + + for (i = 0; i < count; i++) { + if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) { + return 1; + } + } +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout %s\n", unit, whine); +#endif + return 0; +} + +/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */ + +int +mcd_probe(struct isa_device *dev) +{ + int port = dev->id_iobase; + int unit = dev->id_unit; + int i, j; + int status; + unsigned char stbytes[3]; + + mcd_data[unit].flags = MCDPROBING; + +#ifdef NOTDEF + /* get irq/drq configuration word */ + mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ +#else + mcd_data[unit].config = 0; +#endif + + /* send a reset */ + outb(port+MCD_FLAGS, M_RESET); + + /* + * delay awhile by getting any pending garbage (old data) and + * throwing it away. + */ + for (i = 1000000; i != 0; i--) { + inb(port+MCD_FLAGS); + } + + /* Get status */ + outb(port+MCD_DATA, MCD_CMDGETSTAT); + if (!twiddle_thumbs(port, unit, 1000000, "getting status")) { + return 0; /* Timeout */ + } + status = inb(port+MCD_DATA); + if (status != MCDCDABSENT && status != MCDCDPRESENT && + status != MCDSOPEN && status != MCDSCLOSED) + return 0; /* Not actually a Mitsumi drive here */ + /* Get version information */ + outb(port+MCD_DATA, MCD_CMDCONTINFO); + for (j = 0; j < 3; j++) { + if (!twiddle_thumbs(port, unit, 3000, "getting version info")) { + return 0; + } + stbytes[j] = (inb(port+MCD_DATA) & 0xFF); + } + printf("mcd%d: version information is %x %c %x\n", unit, + stbytes[0], stbytes[1], stbytes[2]); + if (stbytes[1] >= 4) { + outb(port+MCD_CTRL, M_PICKLE); + printf("mcd%d: Adjusted for newer drive model\n", unit); + } + return 4; +} + + +static int +mcd_waitrdy(int port,int dly) +{ + int i; + + /* wait until xfer port senses data ready */ + for (i=0; i<dly; i++) { + if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0) + return 0; + mcd_delay(1); + } + return -1; +} + +static int +mcd_getreply(int unit,int dly) +{ + int i; + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + + /* wait data to become ready */ + if (mcd_waitrdy(port,dly)<0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout getreply\n",unit); +#endif + return -1; + } + + /* get the data */ + return inb(port+mcd_status) & 0xFF; +} + +static int +mcd_getstat(int unit,int sflg) +{ + int i; + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + + /* get the status */ + if (sflg) + outb(port+mcd_command, MCD_CMDGETSTAT); + i = mcd_getreply(unit,DELAY_GETREPLY); + if (i<0) return -1; + + cd->status = i; + + mcd_setflags(unit,cd); + return cd->status; +} + +static void +mcd_setflags(int unit, struct mcd_data *cd) +{ + /* check flags */ + if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) { + MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0); + cd->flags &= ~MCDVALID; + } + +#ifndef MCDMINI + if (cd->status & MCDAUDIOBSY) + cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) + cd->audio_status = CD_AS_PLAY_COMPLETED; +#endif +} + +static int +mcd_get(int unit, char *buf, int nmax) +{ + int port = mcd_data[unit].iobase; + int i,k; + + for (i=0; i<nmax; i++) { + /* wait for data */ + if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout mcd_get\n",unit); +#endif + return -1; + } + buf[i] = k; + } + return i; +} + +static int +mcd_send(int unit, int cmd,int nretrys) +{ + int i,k; + int port = mcd_data[unit].iobase; + +/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/ + for (i=0; i<nretrys; i++) { + outb(port+mcd_command, cmd); + if ((k=mcd_getstat(unit,0)) != -1) { + break; + } + } + if (i == nretrys) { + printf("mcd%d: mcd_send retry cnt exceeded\n",unit); + return -1; + } +/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/ + return 0; +} + +static int +bcd2bin(bcd_t b) +{ + return (b >> 4) * 10 + (b & 15); +} + +static bcd_t +bin2bcd(int b) +{ + return ((b / 10) << 4) | (b % 10); +} + +static void +hsg2msf(int hsg, bcd_t *msf) +{ + hsg += 150; + M_msf(msf) = bin2bcd(hsg / 4500); + hsg %= 4500; + S_msf(msf) = bin2bcd(hsg / 75); + F_msf(msf) = bin2bcd(hsg % 75); +} + +static int +msf2hsg(bcd_t *msf) +{ + return (bcd2bin(M_msf(msf)) * 60 + + bcd2bin(S_msf(msf))) * 75 + + bcd2bin(F_msf(msf)) - 150; +} + +static int +mcd_volinfo(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + int i; + +/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ + + /* Get the status, in case the disc has been changed */ + if (mcd_getstat(unit, 1) < 0) return EIO; + + /* Just return if we already have it */ + if (cd->flags & MCDVOLINFO) return 0; + + /* send volume info command */ + if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) + return -1; + + /* get data */ + if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) { + printf("mcd%d: mcd_volinfo: error read data\n",unit); + return -1; + } + + if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) { + cd->flags |= MCDVOLINFO; /* volinfo is OK */ + return 0; + } + + return -1; +} + +void +mcdintr(unit) + int unit; +{ + int port = mcd_data[unit].iobase; + u_int i; + + MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0); + + /* just read out status and ignore the rest */ + if ((inb(port+mcd_xfer)&0xFF) != 0xFF) { + i = inb(port+mcd_status); + } +} + +/* state machine to process read requests + * initialize with MCD_S_BEGIN: calculate sizes, and read status + * MCD_S_WAITSTAT: wait for status reply, set mode + * MCD_S_WAITMODE: waits for status reply from set mode, set read command + * MCD_S_WAITREAD: wait for read ready, read data + */ +static struct mcd_mbx *mbxsave; + +static void +mcd_doread(int state, struct mcd_mbx *mbxin) +{ + struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin; + int unit = mbx->unit; + int port = mbx->port; + struct buf *bp = mbx->bp; + struct mcd_data *cd = mcd_data + unit; + + int rm,i,k; + struct mcd_read2 rbuf; + int blknum; + caddr_t addr; + +loop: + switch (state) { + case MCD_S_BEGIN: + mbx = mbxsave = mbxin; + + case MCD_S_BEGIN1: + /* get status */ + outb(port+mcd_command, MCD_CMDGETSTAT); + mbx->count = RDELAY_WAITSTAT; + timeout((timeout_func_t)mcd_doread, + (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ + return; + case MCD_S_WAITSTAT: + untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT); + if (mbx->count-- >= 0) { + if (inb(port+mcd_xfer) & MCD_ST_BUSY) { + timeout((timeout_func_t)mcd_doread, + (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ + return; + } + mcd_setflags(unit,cd); + MCD_TRACE("got WAITSTAT delay=%d\n", + RDELAY_WAITSTAT-mbx->count,0,0,0); + /* reject, if audio active */ + if (cd->status & MCDAUDIOBSY) { + printf("mcd%d: audio is active\n",unit); + goto readerr; + } + + /* to check for raw/cooked mode */ + if (cd->flags & MCDREADRAW) { + rm = MCD_MD_RAW; + mbx->sz = MCDRBLK; + } else { + rm = MCD_MD_COOKED; + mbx->sz = cd->blksize; + } + + mbx->count = RDELAY_WAITMODE; + + mcd_put(port+mcd_command, MCD_CMDSETMODE); + mcd_put(port+mcd_command, rm); + timeout((timeout_func_t)mcd_doread, + (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */ + return; + } else { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout getstatus\n",unit); +#endif + goto readerr; + } + + case MCD_S_WAITMODE: + untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE); + if (mbx->count-- < 0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout set mode\n",unit); +#endif + goto readerr; + } + if (inb(port+mcd_xfer) & MCD_ST_BUSY) { + timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100); + return; + } + mcd_setflags(unit,cd); + MCD_TRACE("got WAITMODE delay=%d\n", + RDELAY_WAITMODE-mbx->count,0,0,0); + /* for first block */ + mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; + mbx->skip = 0; + +nextblock: + blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + + mbx->p_offset + mbx->skip/mbx->sz; + + MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n", + blknum,bp,0,0); + + /* build parameter block */ + hsg2msf(blknum,rbuf.start_msf); + + /* send the read command */ + mcd_put(port+mcd_command,MCD_CMDREAD2); + mcd_put(port+mcd_command,rbuf.start_msf[0]); + mcd_put(port+mcd_command,rbuf.start_msf[1]); + mcd_put(port+mcd_command,rbuf.start_msf[2]); + mcd_put(port+mcd_command,0); + mcd_put(port+mcd_command,0); + mcd_put(port+mcd_command,1); + mbx->count = RDELAY_WAITREAD; + timeout((timeout_func_t)mcd_doread, + (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ + return; + case MCD_S_WAITREAD: + untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD); + if (mbx->count-- > 0) { + k = inb(port+mcd_xfer); + if ((k & 2)==0) { + MCD_TRACE("got data delay=%d\n", + RDELAY_WAITREAD-mbx->count,0,0,0); + /* data is ready */ + addr = bp->b_un.b_addr + mbx->skip; + outb(port+mcd_ctl2,0x04); /* XXX */ + for (i=0; i<mbx->sz; i++) + *addr++ = inb(port+mcd_rdata); + outb(port+mcd_ctl2,0x0c); /* XXX */ + + if (--mbx->nblk > 0) { + mbx->skip += mbx->sz; + goto nextblock; + } + + /* return buffer */ + bp->b_resid = 0; + biodone(bp); + + cd->flags &= ~MCDMBXBSY; + mcd_start(mbx->unit); + return; + } + if ((k & 4)==0) + mcd_getstat(unit,0); + timeout((timeout_func_t)mcd_doread, + (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ + return; + } else { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout read data\n",unit); +#endif + goto readerr; + } + } + +readerr: + if (mbx->retry-- > 0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: retrying\n",unit); +#endif + state = MCD_S_BEGIN1; + goto loop; + } + + /* invalidate the buffer */ + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + biodone(bp); + mcd_start(mbx->unit); + return; + +#ifdef NOTDEF + printf("mcd%d: unit timeout, resetting\n",mbx->unit); + outb(mbx->port+mcd_reset,MCD_CMDRESET); + DELAY(300000); + (void)mcd_getstat(mbx->unit,1); + (void)mcd_getstat(mbx->unit,1); + /*cd->status &= ~MCDDSKCHNG; */ + cd->debug = 1; /* preventive set debug mode */ + +#endif + +} + +#ifndef MCDMINI +static int +mcd_setmode(int unit, int mode) +{ + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + int retry; + + printf("mcd%d: setting mode to %d\n", unit, mode); + for(retry=0; retry<MCD_RETRYS; retry++) + { + outb(port+mcd_command, MCD_CMDSETMODE); + outb(port+mcd_command, mode); + if (mcd_getstat(unit, 0) != -1) return 0; + } + + return -1; +} + +static int +mcd_toc_header(int unit, struct ioc_toc_header *th) +{ + struct mcd_data *cd = mcd_data + unit; + + if (mcd_volinfo(unit) < 0) { + return ENXIO; + } + + th->len = msf2hsg(cd->volinfo.vol_msf); + th->starting_track = bcd2bin(cd->volinfo.trk_low); + th->ending_track = bcd2bin(cd->volinfo.trk_high); + + return 0; +} + +static int +mcd_read_toc(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct ioc_toc_header th; + struct mcd_qchninfo q; + int rc, trk, idx, retry; + + /* Only read TOC if needed */ + if (cd->flags & MCDTOC) { + return 0; + } + + printf("mcd%d: reading toc header\n", unit); + if (mcd_toc_header(unit, &th) != 0) { + return ENXIO; + } + + printf("mcd%d: stopping play\n", unit); + if ((rc=mcd_stop(unit)) != 0) { + return rc; + } + + /* try setting the mode twice */ + if (mcd_setmode(unit, MCD_MD_TOC) != 0) { + return EIO; + } + if (mcd_setmode(unit, MCD_MD_TOC) != 0) { + return EIO; + } + + printf("mcd%d: get_toc reading qchannel info\n",unit); + for(trk=th.starting_track; trk<=th.ending_track; trk++) + cd->toc[trk].idx_no = 0; + trk = th.ending_track - th.starting_track + 1; + for(retry=0; retry<300 && trk>0; retry++) + { + if (mcd_getqchan(unit, &q) < 0) break; + idx = bcd2bin(q.idx_no); + if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) { + if (cd->toc[idx].idx_no == 0) { + cd->toc[idx] = q; + trk--; + } + } + } + + if (mcd_setmode(unit, MCD_MD_COOKED) != 0) { + return EIO; + } + + if (trk != 0) { + return ENXIO; + } + + /* add a fake last+1 */ + idx = th.ending_track + 1; + cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr; + cd->toc[idx].trk_no = 0; + cd->toc[idx].idx_no = 0xAA; + cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0]; + cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1]; + cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2]; + + cd->flags |= MCDTOC; + + return 0; +} + +static int +mcd_toc_entry(int unit, struct ioc_read_toc_entry *te) +{ + struct mcd_data *cd = mcd_data + unit; + struct ret_toc { + struct ioc_toc_header th; + struct cd_toc_entry rt; + } ret_toc; + struct ioc_toc_header th; + int rc, i; + + /* Make sure we have a valid toc */ + if ((rc=mcd_read_toc(unit)) != 0) { + return rc; + } + + /* find the toc to copy*/ + i = te->starting_track; + if (i == MCD_LASTPLUS1) { + i = bcd2bin(cd->volinfo.trk_high) + 1; + } + + /* verify starting track */ + if (i < bcd2bin(cd->volinfo.trk_low) || + i > bcd2bin(cd->volinfo.trk_high)+1) { + return EINVAL; + } + + /* do we have room */ + if (te->data_len < sizeof(struct ioc_toc_header) + + sizeof(struct cd_toc_entry)) { + return EINVAL; + } + + /* Copy the toc header */ + if (mcd_toc_header(unit, &th) < 0) { + return EIO; + } + ret_toc.th = th; + + /* copy the toc data */ + ret_toc.rt.control = cd->toc[i].ctrl_adr; + ret_toc.rt.addr_type = te->address_format; + ret_toc.rt.track = i; + if (te->address_format == CD_MSF_FORMAT) { + ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0]; + ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1]; + ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2]; + } + + /* copy the data back */ + copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry) + + sizeof(struct ioc_toc_header)); + + return 0; +} + +static int +mcd_stop(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) { + return ENXIO; + } + cd->audio_status = CD_AS_PLAY_COMPLETED; + return 0; +} + +static int +mcd_getqchan(int unit, struct mcd_qchninfo *q) +{ + struct mcd_data *cd = mcd_data + unit; + + if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) { + return -1; + } + if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) { + return -1; + } + if (cd->debug) { + printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n", + unit, + q->ctrl_adr, q->trk_no, q->idx_no, + q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2], + q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]); + } + return 0; +} + +static int +mcd_subchan(int unit, struct ioc_read_subchannel *sc) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_qchninfo q; + struct cd_sub_channel_info data; + + printf("mcd%d: subchan af=%d, df=%d\n", unit, + sc->address_format, + sc->data_format); + if (sc->address_format != CD_MSF_FORMAT) { + return EIO; + } + if (sc->data_format != CD_CURRENT_POSITION) { + return EIO; + } + if (mcd_getqchan(unit, &q) < 0) { + return EIO; + } + + data.header.audio_status = cd->audio_status; + data.what.position.data_format = CD_MSF_FORMAT; + data.what.position.track_number = bcd2bin(q.trk_no); + + if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) { + return EFAULT; + } + return 0; +} + +static int +mcd_playtracks(int unit, struct ioc_play_track *pt) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_read2 pb; + int a = pt->start_track; + int z = pt->end_track; + int rc; + + if ((rc = mcd_read_toc(unit)) != 0) { + return rc; + } + printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit, + a, pt->start_index, z, pt->end_index); + + if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z || + z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) { + return EINVAL; + } + + pb.start_msf[0] = cd->toc[a].hd_pos_msf[0]; + pb.start_msf[1] = cd->toc[a].hd_pos_msf[1]; + pb.start_msf[2] = cd->toc[a].hd_pos_msf[2]; + pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0]; + pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1]; + pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2]; + + return mcd_play(unit, &pb); +} + +static int +mcd_play(int unit, struct mcd_read2 *pb) +{ + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + int retry, st; + + cd->lastpb = *pb; + for(retry=0; retry<MCD_RETRYS; retry++) { + outb(port+mcd_command, MCD_CMDREAD2); + outb(port+mcd_command, pb->start_msf[0]); + outb(port+mcd_command, pb->start_msf[1]); + outb(port+mcd_command, pb->start_msf[2]); + outb(port+mcd_command, pb->end_msf[0]); + outb(port+mcd_command, pb->end_msf[1]); + outb(port+mcd_command, pb->end_msf[2]); + if ((st=mcd_getstat(unit, 0)) != -1) { + break; + } + } + + if (cd->debug) { + printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st); + } + if (st == -1) { + return ENXIO; + } + cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + return 0; +} + +static int +mcd_pause(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_qchninfo q; + int rc; + + /* Verify current status */ + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) { + printf("mcd%d: pause attempted when not playing\n", unit); + return EINVAL; + } + + /* Get the current position */ + if (mcd_getqchan(unit, &q) < 0) { + return EIO; + } + + /* Copy it into lastpb */ + cd->lastpb.start_msf[0] = q.hd_pos_msf[0]; + cd->lastpb.start_msf[1] = q.hd_pos_msf[1]; + cd->lastpb.start_msf[2] = q.hd_pos_msf[2]; + + /* Stop playing */ + if ((rc=mcd_stop(unit)) != 0) { + return rc; + } + + /* Set the proper status and exit */ + cd->audio_status = CD_AS_PLAY_PAUSED; + return 0; +} + +static int +mcd_resume(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (cd->audio_status != CD_AS_PLAY_PAUSED) { + return EINVAL; + } + return mcd_play(unit, &cd->lastpb); +} +#endif /*!MCDMINI*/ + +#endif /* NMCD > 0 */ diff --git a/sys/dev/mcd/mcdreg.h b/sys/dev/mcd/mcdreg.h new file mode 100644 index 0000000..0ce5de7 --- /dev/null +++ b/sys/dev/mcd/mcdreg.h @@ -0,0 +1,159 @@ +/* + * Copyright 1993 by Holger Veit (data part) + * Copyright 1993 by Brian Moore (audio part) + * Changes Copyright 1993 by Gary Clark II + * 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 was developed by Holger Veit and Brian Moore + * for use with "386BSD" and similar operating systems. + * "Similar operating systems" includes mainly non-profit oriented + * systems for research and education, including but not restricted to + * "NetBSD", "FreeBSD", "Mach" (by CMU). + * 4. Neither the name of the developer(s) nor the name "386BSD" + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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(S) 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. + * + * This file contains definitions for some cdrom control commands + * and status codes. This info was "inherited" from the DOS MTMCDE.SYS + * driver, and is thus not complete (and may even be wrong). Some day + * the manufacturer or anyone else might provide better documentation, + * so this file (and the driver) will then have a better quality. + * + * $Id: mcdreg.h,v 1.2 1994/01/16 23:34:17 jkh Exp $ + */ + +#ifndef MCD_H +#define MCD_H + +#ifdef __GNUC__ +#if __GNUC__ >= 2 +#pragma pack(1) +#endif +#endif + +typedef unsigned char bcd_t; +#define M_msf(msf) msf[0] +#define S_msf(msf) msf[1] +#define F_msf(msf) msf[2] + +/* io lines used */ +#define MCD_IO_BASE 0x300 + +#define mcd_command 0 +#define mcd_status 0 +#define mcd_rdata 0 + +#define mcd_reset 1 +#define mcd_xfer 1 +#define mcd_ctl2 2 /* XXX Is this right? */ +#define mcd_config 3 + +#define MCD_MASK_DMA 0x07 /* bits 2-0 = DMA channel */ +#define MCD_MASK_IRQ 0x70 /* bits 6-4 = INT number */ + /* 001 = int 2,9 */ + /* 010 = int 3 */ + /* 011 = int 5 */ + /* 100 = int 10 */ + /* 101 = int 11 */ +/* flags */ +#define STATUS_AVAIL 0xB +#define DATA_AVAIL 0xF + +/* New Flags */ +#define M_STATUS_AVAIL 0xFB +#define M_DATA_AVAIL 0xFD + +/* New Commands */ +#define M_RESET 0x00 +#define M_PICKLE 0x04 + +/* ports */ +#define MCD_DATA 0 +#define MCD_FLAGS 1 +#define MCD_CTRL 2 +#define CHANNEL 3 /* XXX ??? */ + +/* Status bits */ +#define MCD_ST_DOOROPEN 0x80 +#define MCD_ST_DSKIN 0x40 +#define MCD_ST_DSKCHNG 0x20 +#define MCD_ST_BUSY 0x04 +#define MCD_ST_AUDIOBSY 0x02 + +/* commands known by the controller */ +#define MCD_CMDRESET 0x00 +#define MCD_CMDGETVOLINFO 0x10 /* gets mcd_volinfo */ +#define MCD_CMDGETQCHN 0x20 /* gets mcd_qchninfo */ +#define MCD_CMDGETSTAT 0x40 /* gets a byte of status */ +#define MCD_CMDSETMODE 0x50 /* set transmission mode, needs byte */ +#define MCD_MD_RAW 0x60 +#define MCD_MD_COOKED 0x01 +#define MCD_MD_TOC 0x05 +#define MCD_CMDSTOPAUDIO 0x70 +#define MCD_CMDGETVOLUME 0x8E /* gets mcd_volume */ +#define MCD_CMDSETVOLUME 0xAE /* sets mcd_volume */ +#define MCD_CMDREAD1 0xB0 /* read n sectors */ +#define MCD_CMDREAD2 0xC0 /* read from-to */ +#define MCD_CMDCONTINFO 0xDC /* Get controller info */ +#define MCD_CMDEJECTDISK 0xF6 +#define MCD_CMDCLOSETRAY 0xF8 +#define MCD_CMDLOCKDRV 0xFE /* needs byte */ +#define MCD_LK_UNLOCK 0x00 +#define MCD_LK_LOCK 0x01 +#define MCD_LK_TEST 0x02 + +struct mcd_volinfo { + bcd_t trk_low; + bcd_t trk_high; + bcd_t vol_msf[3]; + bcd_t trk1_msf[3]; +}; + +struct mcd_qchninfo { + u_char ctrl_adr; + u_char trk_no; + u_char idx_no; + bcd_t trk_size_msf[3]; + u_char :8; + bcd_t hd_pos_msf[3]; +}; + +struct mcd_volume { + u_char v0l; + u_char v0rs; + u_char v0r; + u_char v0ls; +}; + +struct mcd_read1 { + bcd_t start_msf[3]; + u_char nsec[3]; +}; + +struct mcd_read2 { + bcd_t start_msf[3]; + bcd_t end_msf[3]; +}; +#endif /* MCD_H */ diff --git a/sys/dev/mse/mse.c b/sys/dev/mse/mse.c new file mode 100644 index 0000000..466e936 --- /dev/null +++ b/sys/dev/mse/mse.c @@ -0,0 +1,499 @@ +/* + * Copyright 1992 by the University of Guelph + * + * Permission to use, copy and modify this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation. + * University of Guelph makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ +/* + * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and + * the X386 port, courtesy of + * Rick Macklem, rick@snowhite.cis.uoguelph.ca + * Caveats: The driver currently uses spltty(), but doesn't use any + * generic tty code. It could use splmse() (that only masks off the + * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. + * (This may be worth the effort, since the Logitech generates 30/60 + * interrupts/sec continuously while it is open.) + * NB: The ATI has NOT been tested yet! + */ + +/* + * Modification history: + * + * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) + * fixes to make it work with Microsoft InPort busmouse + * + * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) + * added patches for new "select" interface + * + * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) + * changed position of some spl()'s in mseread + * + * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) + * limit maximum negative x/y value to -127 to work around XFree problem + * that causes spurious button pushes. + */ + +#include "mse.h" +#if NMSE > 0 +#include "param.h" +#include "proc.h" +#include "user.h" +#include "buf.h" +#include "systm.h" +#include "kernel.h" +#include "ioctl.h" +#include "tty.h" +#include "uio.h" + +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" + +static int mseprobe(struct isa_device *); +static int mseattach(struct isa_device *); +void mseintr(int); + +struct isa_driver msedriver = { + mseprobe, mseattach, "mse" +}; + +/* + * Software control structure for mouse. The sc_enablemouse(), + * sc_disablemouse() and sc_getmouse() routines must be called spl'd(). + */ +#define PROTOBYTES 5 +struct mse_softc { + int sc_flags; + int sc_mousetype; + struct selinfo sc_selp; + u_int sc_port; + void (*sc_enablemouse)(); + void (*sc_disablemouse)(); + void (*sc_getmouse)(); + int sc_deltax; + int sc_deltay; + int sc_obuttons; + int sc_buttons; + int sc_bytesread; + u_char sc_bytes[PROTOBYTES]; +} mse_sc[NMSE]; + +/* Flags */ +#define MSESC_OPEN 0x1 +#define MSESC_WANT 0x2 + +/* and Mouse Types */ +#define MSE_LOGITECH 0x1 +#define MSE_ATIINPORT 0x2 + +#define MSE_PORTA 0 +#define MSE_PORTB 1 +#define MSE_PORTC 2 +#define MSE_PORTD 3 + +#define MSE_UNIT(dev) (minor(dev) >> 1) +#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) + +/* + * Logitech bus mouse definitions + */ +#define MSE_SETUP 0x91 /* What does this mean? */ +#define MSE_HOLD 0x80 +#define MSE_RXLOW 0x00 +#define MSE_RXHIGH 0x20 +#define MSE_RYLOW 0x40 +#define MSE_RYHIGH 0x60 +#define MSE_DISINTR 0x10 +#define MSE_INTREN 0x00 + +static int mse_probelogi(); +static void mse_enablelogi(), mse_disablelogi(), mse_getlogi(); + +/* + * ATI Inport mouse definitions + */ +#define MSE_INPORT_RESET 0x80 +#define MSE_INPORT_STATUS 0x00 +#define MSE_INPORT_DX 0x01 +#define MSE_INPORT_DY 0x02 +#define MSE_INPORT_MODE 0x07 +#define MSE_INPORT_HOLD 0x20 +#define MSE_INPORT_INTREN 0x09 + +static int mse_probeati(); +static void mse_enableati(), mse_disableati(), mse_getati(); + +#define MSEPRI (PZERO + 3) + +/* + * Table of mouse types. + * Keep the Logitech last, since I haven't figured out how to probe it + * properly yet. (Someday I'll have the documentation.) + */ +struct mse_types { + int m_type; /* Type of bus mouse */ + int (*m_probe)(); /* Probe routine to test for it */ + void (*m_enable)(); /* Start routine */ + void (*m_disable)(); /* Disable interrupts routine */ + void (*m_get)(); /* and get mouse status */ +} mse_types[] = { + { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati }, + { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi }, + { 0, }, +}; + +int +mseprobe(idp) + register struct isa_device *idp; +{ + register struct mse_softc *sc = &mse_sc[idp->id_unit]; + register int i; + + /* + * Check for each mouse type in the table. + */ + i = 0; + while (mse_types[i].m_type) { + if ((*mse_types[i].m_probe)(idp)) { + sc->sc_mousetype = mse_types[i].m_type; + sc->sc_enablemouse = mse_types[i].m_enable; + sc->sc_disablemouse = mse_types[i].m_disable; + sc->sc_getmouse = mse_types[i].m_get; + return (1); + } + i++; + } + return (0); +} + +int +mseattach(idp) + struct isa_device *idp; +{ + struct mse_softc *sc = &mse_sc[idp->id_unit]; + + sc->sc_port = idp->id_iobase; + return (1); +} + +/* + * Exclusive open the mouse, initialize it and enable interrupts. + */ +int +mseopen(dev, flag) + dev_t dev; + int flag; +{ + register struct mse_softc *sc; + int s; + + if (MSE_UNIT(dev) >= NMSE) + return (ENXIO); + sc = &mse_sc[MSE_UNIT(dev)]; + if (sc->sc_flags & MSESC_OPEN) + return (EBUSY); + sc->sc_flags |= MSESC_OPEN; + sc->sc_obuttons = sc->sc_buttons = 0x7; + sc->sc_deltax = sc->sc_deltay = 0; + sc->sc_bytesread = PROTOBYTES; + + /* + * Initialize mouse interface and enable interrupts. + */ + s = spltty(); + (*sc->sc_enablemouse)(sc->sc_port); + splx(s); + return (0); +} + +/* + * mseclose: just turn off mouse innterrupts. + */ +int +mseclose(dev, flag) + dev_t dev; + int flag; +{ + struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int s; + + s = spltty(); + (*sc->sc_disablemouse)(sc->sc_port); + sc->sc_flags &= ~MSESC_OPEN; + splx(s); + return(0); +} + +/* + * mseread: return mouse info using the MSC serial protocol, but without + * using bytes 4 and 5. + * (Yes this is cheesy, but it makes the X386 server happy, so...) + */ +int +mseread(dev, uio) + dev_t dev; + struct uio *uio; +{ + register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int xfer, s, error; + + /* + * If there are no protocol bytes to be read, set up a new protocol + * packet. + */ + s = spltty(); /* XXX Should be its own spl, but where is imlXX() */ + if (sc->sc_bytesread >= PROTOBYTES) { + while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && + (sc->sc_obuttons ^ sc->sc_buttons) == 0) { + if (MSE_NBLOCKIO(dev)) { + splx(s); + return (0); + } + sc->sc_flags |= MSESC_WANT; + if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH, + "mseread", 0)) { + splx(s); + return (error); + } + } + + /* + * Generate protocol bytes. + * For some reason X386 expects 5 bytes but never uses + * the fourth or fifth? + */ + sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8); + if (sc->sc_deltax > 127) + sc->sc_deltax = 127; + if (sc->sc_deltax < -127) + sc->sc_deltax = -127; + sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ + if (sc->sc_deltay > 127) + sc->sc_deltay = 127; + if (sc->sc_deltay < -127) + sc->sc_deltay = -127; + sc->sc_bytes[1] = sc->sc_deltax; + sc->sc_bytes[2] = sc->sc_deltay; + sc->sc_bytes[3] = sc->sc_bytes[4] = 0; + sc->sc_obuttons = sc->sc_buttons; + sc->sc_deltax = sc->sc_deltay = 0; + sc->sc_bytesread = 0; + } + splx(s); + xfer = min(uio->uio_resid, PROTOBYTES - sc->sc_bytesread); + if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio)) + return (error); + sc->sc_bytesread += xfer; + return(0); +} + +/* + * mseselect: check for mouse input to be processed. + */ +int +mseselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int s; + + s = spltty(); + if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 || + sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) { + splx(s); + return (1); + } + + /* + * Since this is an exclusive open device, any previous proc. + * pointer is trash now, so we can just assign it. + */ + selrecord(p, &sc->sc_selp); + splx(s); + return (0); +} + +/* + * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. + */ +void +mseintr(unit) + int unit; +{ + register struct mse_softc *sc = &mse_sc[unit]; + pid_t p; + +#ifdef DEBUG + static int mse_intrcnt = 0; + if((mse_intrcnt++ % 10000) == 0) + printf("mseintr\n"); +#endif /* DEBUG */ + if ((sc->sc_flags & MSESC_OPEN) == 0) + return; + + (*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons); + + /* + * If mouse state has changed, wake up anyone wanting to know. + */ + if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || + (sc->sc_obuttons ^ sc->sc_buttons) != 0) { + if (sc->sc_flags & MSESC_WANT) { + sc->sc_flags &= ~MSESC_WANT; + wakeup((caddr_t)sc); + } + selwakeup(&sc->sc_selp); + } +} + +/* + * Routines for the Logitech mouse. + */ +/* + * Test for a Logitech bus mouse and return 1 if it is. + * (until I know how to use the signature port properly, just disable + * interrupts and return 1) + */ +static int +mse_probelogi(idp) + register struct isa_device *idp; +{ + + outb(idp->id_iobase + MSE_PORTB, 0x55); + if (inb(idp->id_iobase + MSE_PORTB) == 0x55) { + outb(idp->id_iobase + MSE_PORTB, 0xaa); + if (inb(idp->id_iobase + MSE_PORTB) == 0xaa) + return (1); + } + return (0); +} + +/* + * Initialize Logitech mouse and enable interrupts. + */ +static void +mse_enablelogi(port) + register u_int port; +{ + int dx, dy, but; + + outb(port + MSE_PORTD, MSE_SETUP); + mse_getlogi(port, &dx, &dy, &but); +} + +/* + * Disable interrupts for Logitech mouse. + */ +static void +mse_disablelogi(port) + register u_int port; +{ + + outb(port + MSE_PORTC, MSE_DISINTR); +} + +/* + * Get the current dx, dy and button up/down state. + */ +static void +mse_getlogi(port, dx, dy, but) + register u_int port; + int *dx; + int *dy; + int *but; +{ + register char x, y; + + outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW); + x = inb(port + MSE_PORTA); + *but = (x >> 5) & 0x7; + x &= 0xf; + outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH); + x |= (inb(port + MSE_PORTA) << 4); + outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW); + y = (inb(port + MSE_PORTA) & 0xf); + outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH); + y |= (inb(port + MSE_PORTA) << 4); + *dx += x; + *dy += y; + outb(port + MSE_PORTC, MSE_INTREN); +} + +/* + * Routines for the ATI Inport bus mouse. + */ +/* + * Test for a ATI Inport bus mouse and return 1 if it is. + * (do not enable interrupts) + */ +static int +mse_probeati(idp) + register struct isa_device *idp; +{ + int i; + + for (i = 0; i < 2; i++) + if (inb(idp->id_iobase + MSE_PORTC) == 0xde) + return (1); + return (0); +} + +/* + * Initialize ATI Inport mouse and enable interrupts. + */ +static void +mse_enableati(port) + register u_int port; +{ + + outb(port + MSE_PORTA, MSE_INPORT_RESET); + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_INTREN); +} + +/* + * Disable interrupts for ATI Inport mouse. + */ +static void +mse_disableati(port) + register u_int port; +{ + + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, 0); +} + +/* + * Get current dx, dy and up/down button state. + */ +static void +mse_getati(port, dx, dy, but) + register u_int port; + int *dx; + int *dy; + int *but; +{ + register char byte; + + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_HOLD); + outb(port + MSE_PORTA, MSE_INPORT_STATUS); + *but = ~(inb(port + MSE_PORTB) & 0x7); + outb(port + MSE_PORTA, MSE_INPORT_DX); + byte = inb(port + MSE_PORTB); + *dx += byte; + outb(port + MSE_PORTA, MSE_INPORT_DY); + byte = inb(port + MSE_PORTB); + *dy += byte; + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_INTREN); +} +#endif /* NMSE */ diff --git a/sys/dev/ppbus/lptio.h b/sys/dev/ppbus/lptio.h new file mode 100644 index 0000000..87af5bc --- /dev/null +++ b/sys/dev/ppbus/lptio.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 1994 Geoffrey M. Rehmet + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za> + * + */ + +#ifndef _LPT_PRINTER_H_ +#define _LPT_PRINTER_H_ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */ + +#endif diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c new file mode 100644 index 0000000..dd4ee55 --- /dev/null +++ b/sys/dev/sio/sio.c @@ -0,0 +1,1920 @@ +/*- + * 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. + * + * from: @(#)com.c 7.5 (Berkeley) 5/16/91 + * $Id: sio.c,v 1.44 1994/04/03 12:25:57 ache Exp $ + */ + +#include "sio.h" +#if NSIO > 0 +#define DONT_MALLOC_TTYS +/* + * Serial driver, based on 386BSD-0.1 com driver. + * Mostly rewritten to use pseudo-DMA. + * Works for National Semiconductor NS8250-NS16550AF UARTs. + * COM driver, based on HP dca driver. + */ +#include "param.h" +#include "systm.h" +#include "ioctl.h" +#include "tty.h" +#include "proc.h" +#include "user.h" +#include "conf.h" +#include "file.h" +#include "uio.h" +#include "kernel.h" +#include "syslog.h" + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/sioreg.h" +#include "i386/isa/ic/ns16550.h" + +#define FAKE_DCD(unit) ((unit) == comconsole) +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RBSZ 1024 +#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE) +#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8) +#define RS_IBUFSIZE 256 +#define RS_OBUFSIZE 256 +#define TTY_BI TTY_FE /* XXX */ +#define TTY_OE TTY_PE /* XXX */ + +#ifdef COM_BIDIR +#define CALLOUT(x) (minor(x) & COM_CALLOUT_MASK) +#define COM_CALLOUT_MASK 0x80 +#define COM_MINOR_MAGIC_MASK 0x80 +#else /* COM_BIDIR */ +#define COM_MINOR_MAGIC_MASK 0 +#endif /* COM_BIDIR */ + +#define UNIT(x) (minor(x) & ~COM_MINOR_MAGIC_MASK) + +#ifdef COM_MULTIPORT +/* checks in flags for multiport and which is multiport "master chip" + * for a given card + */ +#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) +#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) +#define COM_NOMASTER(dev) ((dev)->id_flags & 0x04) +#endif /* COM_MULTIPORT */ + +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) + +#ifndef FIFO_TRIGGER +/* + * This driver is fast enough to work with any value and for high values + * to be only slightly more efficient. Low values may be better because + * they give lower latency. + * TODO: always use low values for low speeds. Mouse movements are jerky + * if more than one packet arrives at once. The low speeds used for + * serial mice help avoid this, but not if (large) fifos are enabled. + */ +#define FIFO_TRIGGER FIFO_TRIGGER_14 +#endif + +#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ + +#ifndef setsofttty +#define OLD_INTERRUPT_HANDLING /* XXX FreeBSD-1.1 and earlier */ +#define setsofttty() (ipending |= 1 << 4) /* XXX requires owning IRQ4 */ +extern u_int ipending; /* XXX */ +void softsio1 __P((void)); +#endif + +/* + * Input buffer watermarks. + * The external device is asked to stop sending when the buffer exactly reaches + * high water, or when the high level requests it. + * The high level is notified immediately (rather than at a later clock tick) + * when this watermark is reached. + * The buffer size is chosen so the watermark should almost never be reached. + * The low watermark is invisibly 0 since the buffer is always emptied all at + * once. + */ +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) + +/* + * com state bits. + * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher + * than the other bits so that they can be tested as a group without masking + * off the low bits. + * + * The following com and tty flags correspond closely: + * TS_BUSY = CS_BUSY (maintained by comstart() and comflush()) + * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + */ +#define CS_BUSY 0x80 /* output in progress */ +#define CS_TTGO 0x40 /* output not stopped by XOFF */ +#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ +#define CS_CHECKMSR 1 /* check of MSR scheduled */ +#define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ + +static char *error_desc[] = { +#define CE_OVERRUN 0 + "silo overflow", +#define CE_INTERRUPT_BUF_OVERFLOW 1 + "interrupt-level buffer overflow", +#define CE_TTY_BUF_OVERFLOW 2 + "tty-level buffer overflow", +}; + +#define CE_NTYPES 3 +#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) + +/* types. XXX - should be elsewhere */ +typedef u_int Port_t; /* hardware port */ +typedef u_char bool_t; /* boolean */ + +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + u_char cfcr_image; /* copy of value written to CFCR */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + u_char mcr_image; /* copy of value written to MCR */ +#ifdef COM_BIDIR + bool_t bidir; /* is this unit bidirectional? */ + bool_t active; /* is the port active _at all_? */ + bool_t active_in; /* is the incoming port in use? */ + bool_t active_out; /* is the outgoing port in use? */ +#endif /* COM_BIDIR */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */ + u_int tx_fifo_size; + + /* + * The high level of the driver never reads status registers directly + * because there would be too many side effects to handle conveniently. + * Instead, it reads copies of the registers stored here by the + * interrupt handler. + */ + u_char last_modem_status; /* last MSR read by intr handler */ + u_char prev_modem_status; /* last MSR handled by high level */ + + u_char *ibuf; /* start of input buffer */ + u_char *ibufend; /* end of input buffer */ + u_char *ihighwater; /* threshold in input buffer */ + u_char *iptr; /* next free spot in input buffer */ + + u_char *obufend; /* end of output buffer */ + int ocount; /* original count for current output */ + u_char *optr; /* next char to output */ + + Port_t data_port; /* i/o ports */ + Port_t int_id_port; + Port_t iobase; + Port_t modem_ctl_port; + Port_t line_status_port; + Port_t modem_status_port; + + struct tty *tp; /* cross reference */ + +#ifdef TIOCTIMESTAMP + bool_t do_timestamp; + struct timeval timestamp; +#endif + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_int error_counts[CE_NTYPES]; + + /* + * Ping-pong input buffers. The extra factor of 2 in the sizes is + * to allow for an error byte for each input byte. + */ +#define CE_INPUT_OFFSET RS_IBUFSIZE + u_char ibuf1[2 * RS_IBUFSIZE]; + u_char ibuf2[2 * RS_IBUFSIZE]; + u_char obuf[RS_OBUFSIZE]; +}; + +/* + * The public functions in the com module ought to be declared in a com-driver + * system header. + */ + +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int sioclose __P((dev_t dev, int fflag, int devtype, + struct proc *p)); +int sioread __P((dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); +int sioioctl __P((dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +int sioselect __P((dev_t dev, int rw, struct proc *p)); +#define siommap nommap +#define siostrategy nostrategy + +/* Console device entry points. */ +int siocngetc __P((dev_t dev)); +struct consdev; +void siocninit __P((struct consdev *cp)); +void siocnprobe __P((struct consdev *cp)); +void siocnputc __P((dev_t dev, int c)); + +static int sioattach __P((struct isa_device *dev)); +static void comflush __P((struct com_s *com)); +static void comhardclose __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); +static void commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void comstart __P((struct tty *tp)); +static void comwakeup __P((caddr_t chan, int ticks)); +static int tiocm_xxx2mcr __P((int tiocm_xxx)); + +/* table and macro for fast conversion from a unit number to its com struct */ +static struct com_s *p_com_addr[NSIO]; +#define com_addr(unit) (p_com_addr[unit]) + +static struct com_s com_structs[NSIO]; + +#ifdef TIOCTIMESTAMP +static struct timeval intr_timestamp; +#endif + +struct isa_driver siodriver = { + sioprobe, sioattach, "sio" +}; + +#ifdef COMCONSOLE +static int comconsole = COMCONSOLE; +#else +static int comconsole = -1; +#endif +static speed_t comdefaultrate = TTYDEF_SPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int commajor; +#define TB_OUT(tp) (&(tp)->t_outq) +#define TB_RAW(tp) (&(tp)->t_rawq) +struct tty sio_tty[NSIO]; +extern struct tty *constty; +extern int tk_nin; /* XXX */ +extern int tk_rawcc; /* XXX */ + +#ifdef KGDB +#include "machine/remote-sl.h" + +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif + +static struct speedtab comspeedtab[] = { + 0, 0, + 50, COMBRD(50), + 75, COMBRD(75), + 110, COMBRD(110), + 134, COMBRD(134), + 150, COMBRD(150), + 200, COMBRD(200), + 300, COMBRD(300), + 600, COMBRD(600), + 1200, COMBRD(1200), + 1800, COMBRD(1800), + 2400, COMBRD(2400), + 4800, COMBRD(4800), + 9600, COMBRD(9600), + 19200, COMBRD(19200), + 38400, COMBRD(38400), + 57600, COMBRD(57600), + 115200, COMBRD(115200), + -1, -1 +}; + +/* XXX - configure this list */ +static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; + +static int +sioprobe(dev) + struct isa_device *dev; +{ + static bool_t already_init; + Port_t *com_ptr; + Port_t iobase; + int result; + + if (!already_init) { + /* + * Turn off MCR_IENABLE for all likely serial ports. An unused + * port with its MCR_IENABLE gate open will inhibit interrupts + * from any used port that shares the interrupt vector. + */ + for (com_ptr = likely_com_ports; + com_ptr < &likely_com_ports[sizeof likely_com_ports + / sizeof likely_com_ports[0]]; + ++com_ptr) + outb(*com_ptr + com_mcr, 0); + already_init = TRUE; + } + iobase = dev->id_iobase; + result = IO_COMSIZE; + + /* + * We don't want to get actual interrupts, just masked ones. + * Interrupts from this line should already be masked in the ICU, + * but mask them in the processor as well in case there are some + * (misconfigured) shared interrupts. + */ + disable_intr(); + + /* + * Initialize the speed so that any junk in the THR or output fifo will + * be transmitted in a known time. (There may be lots of junk after a + * soft reboot, and output interrupts don't work right after a master + * reset, at least for 16550s. (The speed is undefined after MR, but + * MR empties the THR and the TSR so it's not clear why this matters)). + * Enable output interrupts (only) and check the following: + * o the CFCR, IER and MCR in UART hold the values written to them + * (the values happen to be all distinct - this is good for + * avoiding false positive tests from bus echoes). + * o an output interrupt is generated and its vector is correct. + * o the interrupt goes away when the IIR in the UART is read. + */ + outb(iobase + com_cfcr, CFCR_DLAB); + outb(iobase + com_dlbl, COMBRD(9600) & 0xff); + outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */ + outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */ + outb(iobase + com_ier, 0); /* ensure edge on next intr */ + outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */ + DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */ + if ( inb(iobase + com_cfcr) != CFCR_8BITS + || inb(iobase + com_ier) != IER_ETXRDY + || inb(iobase + com_mcr) != MCR_IENABLE +#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */ + || !isa_irq_pending(dev) +#endif + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY + || isa_irq_pending(dev) + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) + result = 0; + + /* + * Turn off all device interrupts and check that they go off properly. + * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to + * the ICU input. Closing the gate would give a floating ICU input + * (unless there is another device driving at) and spurious interrupts. + * (On the system that this was first tested on, the input floats high + * and gives a (masked) interrupt as soon as the gate is closed.) + */ + outb(iobase + com_ier, 0); + outb(iobase + com_mcr, MCR_IENABLE); /* dummy to avoid bus echo */ + if ( inb(iobase + com_ier) != 0 + || isa_irq_pending(dev) + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) + result = 0; + if (result == 0) + outb(iobase + com_mcr, 0); + + enable_intr(); + return (result); +} + +static int +sioattach(isdp) + struct isa_device *isdp; +{ + struct com_s *com; + static bool_t comwakeup_started = FALSE; + Port_t iobase; + int s; + int unit; + + iobase = isdp->id_iobase; + unit = isdp->id_unit; + s = spltty(); + + /* + * sioprobe() has initialized the device registers as follows: + * o cfcr = CFCR_8BITS. + * It is most important that CFCR_DLAB is off, so that the + * data port is not hidden when we enable interrupts. + * o ier = 0. + * Interrupts are only enabled when the line is open. + * o mcr = MCR_IENABLE. + * Keeping MCR_DTR and MCR_RTS off might stop the external + * device from sending before we are ready. + */ + + com = &com_structs[unit]; /* XXX malloc it */ + com->cfcr_image = CFCR_8BITS; + com->mcr_image = MCR_IENABLE; + com->dtr_wait = 3 * hz; + com->tx_fifo_size = 1; + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->iobase = iobase; + com->data_port = iobase + com_data; + com->int_id_port = iobase + com_iir; + com->modem_ctl_port = iobase + com_mcr; + com->line_status_port = iobase + com_lsr; + com->modem_status_port = iobase + com_msr; +#ifdef DONT_MALLOC_TTYS + com->tp = &sio_tty[unit]; +#endif + + /* attempt to determine UART type */ + printf("sio%d: type", unit); +#ifdef COM_MULTIPORT + if (!COM_ISMULTIPORT(isdp)) +#endif + { + u_char scr; + u_char scr1; + u_char scr2; + + scr = inb(iobase + com_scr); + outb(iobase + com_scr, 0xa5); + scr1 = inb(iobase + com_scr); + outb(iobase + com_scr, 0x5a); + scr2 = inb(iobase + com_scr); + outb(iobase + com_scr, scr); + if (scr1 != 0xa5 || scr2 != 0x5a) { + printf(" 8250"); + goto determined_type; + } + } + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); + DELAY(100); + switch (inb(com->int_id_port) & IIR_FIFO_MASK) { + case FIFO_TRIGGER_1: + printf(" 16450"); + break; + case FIFO_TRIGGER_4: + printf(" 16450?"); + break; + case FIFO_TRIGGER_8: + printf(" 16550?"); + break; + case FIFO_TRIGGER_14: + printf(" 16550A"); + if (COM_NOFIFO(isdp)) + printf(" fifo disabled"); + else { + com->hasfifo = TRUE; + com->tx_fifo_size = 16; + } + break; + } + outb(iobase + com_fifo, 0); +determined_type: ; + +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(isdp)) { + com->multiport = TRUE; + printf(" (multiport)"); + + /* Note: some cards have no master port (e.g., BocaBoards) */ + if (!COM_NOMASTER(isdp)) { + struct isa_device *masterdev; + + /* set the master's common-interrupt-enable reg., + * as appropriate. YYY See your manual + */ + /* enable only common interrupt for port */ + outb(com->modem_ctl_port, com->mcr_image = 0); + + masterdev = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp)); + outb(masterdev->id_iobase + com_scr, 0x80); + } + + } else + com->multiport = FALSE; +#endif /* COM_MULTIPORT */ + printf("\n"); + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (comconsole == unit) + kgdb_dev = -1; /* can't debug over console port */ + else { + int divisor; + + /* + * XXX now unfinished and broken. Need to do + * something more like a full open(). There's no + * suitable interrupt handler so don't enable device + * interrupts. Watch out for null tp's. + */ + outb(iobase + com_cfcr, CFCR_DLAB); + divisor = ttspeedtab(kgdb_rate, comspeedtab); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + outb(com->modem_status_port, + com->mcr_image |= MCR_DTR | MCR_RTS); + + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("sio%d: ", unit); + kgdb_connect(1); + } else + printf("sio%d: kgdb enabled\n", unit); + } + } +#endif + + com_addr(unit) = com; + splx(s); + if (!comwakeup_started) { + comwakeup((caddr_t) NULL, 0); + comwakeup_started = TRUE; + } + return (1); +} + +/* ARGSUSED */ +int +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ +#ifdef COM_BIDIR + bool_t callout; +#endif /* COM_BIDIR */ + struct com_s *com; + int error = 0; + bool_t got_status = FALSE; + Port_t iobase; + int s; + struct tty *tp; + int unit; + + unit = UNIT(dev); + if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) + return (ENXIO); +#ifdef COM_BIDIR + /* if it's a callout device, and bidir not possible on that dev, die */ + callout = CALLOUT(dev); + if (callout && !(com->bidir)) + return (ENXIO); +#endif /* COM_BIDIR */ + +#ifdef DONT_MALLOC_TTYS + tp = com->tp; +#else + sio_tty[unit] = ttymalloc(sio_tty[unit]); + tp = com->tp = sio_tty[unit]; +#endif + s = spltty(); + +#ifdef COM_BIDIR + +bidir_open_top: + got_status = FALSE; + /* if it's bidirectional, we've gotta deal with it... */ + if (com->bidir) { + if (callout) { + if (com->active_in) { + /* it's busy. die */ + splx(s); + return (EBUSY); + } else { + /* it's ours. lock it down, and set it up */ + com->active_out = TRUE; + } + } else { + if (com->active_out) { + /* it's busy, outgoing. wait, if possible */ + if (flag & O_NONBLOCK) { + /* can't wait; bail */ + splx(s); + return (EBUSY); + } else { + /* wait for it... */ + error = tsleep((caddr_t)&com->active_out, + TTIPRI|PCATCH, + "siooth", + 0); + /* if there was an error, take off. */ + if (error != 0) { + splx(s); + return (error); + } + /* else take it from the top */ + goto bidir_open_top; + } + } + disable_intr(); + com->prev_modem_status = + com->last_modem_status = inb(com->modem_status_port); + enable_intr(); + got_status = TRUE; + if (com->prev_modem_status & MSR_DCD + || FAKE_DCD(unit)) { + /* there's a carrier on the line; we win */ + com->active_in = TRUE; + } else { + /* there is no carrier on the line */ + if (flag & O_NONBLOCK) { + /* can't wait; let it open */ + com->active_in = TRUE; + } else { + /* put DTR & RTS up */ + /* XXX - bring up RTS earlier? */ + commctl(com, MCR_DTR | MCR_RTS, DMSET); + outb(com->iobase + com_ier, IER_EMSC); + + /* wait for it... */ + error = tsleep((caddr_t)&com->active_in, + TTIPRI|PCATCH, + "siodcd", + 0); + + /* if not active, turn intrs and DTR off */ + if (!com->active) { + outb(com->iobase + com_ier, 0); + commctl(com, MCR_DTR, DMBIC); + } + + /* if there was an error, take off. */ + if (error != 0) { + splx(s); + return (error); + } + /* else take it from the top */ + goto bidir_open_top; + } + } + } + } + + com->active = TRUE; +#endif /* COM_BIDIR */ + + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + if (!(tp->t_state & TS_ISOPEN)) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + if (tp->t_ispeed == 0) { + /* + * We don't use all the flags from <sys/ttydefaults.h> + * since those are only relevant for logins. It's + * important to have echo off initially so that the + * line doesn't start blathering before the echo flag + * can be turned off. + */ + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_cflag = CREAD | CS8; +#ifdef COM_BIDIR + if (com->bidir && !callout) + tp->t_cflag |= HUPCL; +#endif + tp->t_lflag = 0; + tp->t_ispeed = tp->t_ospeed = comdefaultrate; + if (unit == comconsole) { + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + } + } + + /* + * XXX the full state after a first open() needs to be + * programmable and separate for callin and callout. + */ +#ifdef COM_BIDIR + if (com->bidir) { + if (callout) + tp->t_cflag |= CLOCAL; + else + tp->t_cflag &= ~CLOCAL; + } +#endif + + commctl(com, MCR_DTR | MCR_RTS, DMSET); + error = comparam(tp, &tp->t_termios); + if (error != 0) + goto out; + ttsetwater(tp); + iobase = com->iobase; + if (com->hasfifo) { + /* (re)enable and drain FIFO */ + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER + | FIFO_RCV_RST | FIFO_XMT_RST); + DELAY(100); + } + disable_intr(); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + if (!got_status) + com->prev_modem_status = + com->last_modem_status = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); + if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit)) + tp->t_state |= TS_CARR_ON; + } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + splx(s); + return (EBUSY); + } + while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL) +#ifdef COM_BIDIR + /* We went through a lot of trouble to open it, + * but it's certain we have a carrier now, so + * don't spend any time on it now. + */ + && !(com->bidir) +#endif /* COM_BIDIR */ + && !(tp->t_state & TS_CARR_ON)) { + tp->t_state |= TS_WOPEN; + error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH, + ttopen, 0); + if (error != 0) + break; + } +out: + if (error == 0) + error = (*linesw[tp->t_line].l_open)(dev, tp); + splx(s); + +#ifdef COM_BIDIR + /* wakeup sleepers */ + wakeup((caddr_t) &com->active_in); +#endif /* COM_BIDIR */ + + /* + * XXX - the next step was once not done, so interrupts, DTR and RTS + * remained hot if the process was killed while it was sleeping + * waiting for carrier. Now there is the opposite problem. If several + * processes are sleeping waiting for carrier on the same line and one + * is killed, interrupts are turned off so the other processes will + * never see the carrier rise. + */ + if (error != 0 && !(tp->t_state & TS_ISOPEN)) + comhardclose(com); + tp->t_state &= ~TS_WOPEN; + + return (error); +} + +/*ARGSUSED*/ +int +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct com_s *com; + int s; + struct tty *tp; + + com = com_addr(UNIT(dev)); + tp = com->tp; + s = spltty(); + (*linesw[tp->t_line].l_close)(tp, flag); + siostop(tp, FREAD | FWRITE); + comhardclose(com); + ttyclose(tp); + splx(s); + return (0); +} + +static void +comhardclose(com) + struct com_s *com; +{ + Port_t iobase; + int s; + struct tty *tp; + int unit; + + unit = com - &com_structs[0]; + iobase = com->iobase; + s = spltty(); +#ifdef TIOCTIMESTAMP + com->do_timestamp = 0; +#endif + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#ifdef KGDB + /* do not disable interrupts or hang up if debugging */ + if (kgdb_dev != makedev(commajor, unit)) +#endif + { + outb(iobase + com_ier, 0); + tp = com->tp; + if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN +#ifdef COM_BIDIR + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. And we shouldn't look at DCD if + * CLOCAL is set (here or for the dialin device ...). + * When the termios state is reinitialized for initial + * opens, the correct CLOCAL bit will be + * ((the bit now) & (the initial bit)). + */ + || com->active_in + && !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit) +#endif + || !(tp->t_state & TS_ISOPEN)) { + commctl(com, MCR_RTS, DMSET); + if (com->dtr_wait != 0) + /* + * Uninterruptible sleep since we want to + * wait a fixed time. + * XXX - delay in open() (if necessary), + * not here (always). + */ + tsleep((caddr_t)&com->dtr_wait, TTIPRI, + "sioclose", com->dtr_wait); + } + } + +#ifdef COM_BIDIR + com->active = com->active_in = com->active_out = FALSE; + + /* wakeup sleepers who are waiting for out to finish */ + wakeup((caddr_t) &com->active_out); +#endif /* COM_BIDIR */ + + splx(s); +} + +int +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct tty *tp = com_addr(UNIT(dev))->tp; + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = UNIT(dev); + struct tty *tp = com_addr(unit)->tp; + + /* + * (XXX) We disallow virtual consoles if the physical console is + * a serial port. This is in case there is a display attached that + * is not the console. In that situation we don't need/want the X + * server taking over the console. + */ + if (constty && unit == comconsole) + constty = NULL; + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +#ifdef TIOCTIMESTAMP +/* Interrupt routine for timekeeping purposes */ +void +siointrts(unit) + int unit; +{ + microtime(&intr_timestamp); + siointr(unit); +} +#endif + +void +siointr(unit) + int unit; +{ +#ifndef COM_MULTIPORT + siointr1(com_addr(unit)); +#else /* COM_MULTIPORT */ + bool_t possibly_more_intrs; + struct com_s *com; + + /* + * Loop until there is no activity on any port. This is necessary + * to get an interrupt edge more than to avoid another interrupt. + * If the IRQ signal is just an OR of the IRQ signals from several + * devices, then the edge from one may be lost because another is + * on. + */ + do { + possibly_more_intrs = FALSE; + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL + && (inb(com->int_id_port) & IIR_IMASK) + != IIR_NOPEND) { + siointr1(com); + possibly_more_intrs = TRUE; + } + } + } while (possibly_more_intrs); +#endif /* COM_MULTIPORT */ +} + +static void +siointr1(com) + struct com_s *com; +{ + u_char line_status; + u_char modem_status; + u_char *ioptr; + u_char recv_data; + +#ifdef TIOCTIMESTAMP + if (com->do_timestamp) + /* XXX a little bloat here... */ + com->timestamp = intr_timestamp; +#endif + while (TRUE) { + line_status = inb(com->line_status_port); + + /* input event? (check first to help avoid overruns) */ + while (line_status & LSR_RCV_MASK) { + /* break/unnattached error bits or real input? */ + if (!(line_status & LSR_RXRDY)) + recv_data = 0; + else + recv_data = inb(com->data_port); + ++com->bytes_in; + /* XXX reduce SLIP input latency */ +#define FRAME_END 0xc0 + if (recv_data == FRAME_END) + setsofttty(); +#ifdef KGDB + /* trap into kgdb? (XXX - needs testing and optim) */ + if (recv_data == FRAME_END + && !(com->tp->t_state & TS_ISOPEN) + && kgdb_dev == makedev(commajor, unit)) { + kgdb_connect(0); + continue; + } +#endif /* KGDB */ + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + ++com_events; +#if 0 /* for testing input latency vs efficiency */ +if (com->iptr - com->ibuf == 8) + setsofttty(); +#endif + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = line_status; + com->iptr = ++ioptr; + if (ioptr == com->ihighwater + && com->state & CS_RTS_IFLOW) + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); + /* XXX - move this out of isr */ + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); + } + + /* + * "& 0x7F" is to avoid the gcc-1.40 generating a slow + * jump from the top of the loop to here + */ + line_status = inb(com->line_status_port) & 0x7F; + } + + /* modem status change? (always check before doing output) */ + modem_status = inb(com->modem_status_port); + if (modem_status != com->last_modem_status) { + /* + * Schedule high level to handle DCD changes. Note + * that we don't use the delta bits anywhere. Some + * UARTs mess them up, and it's easy to remember the + * previous bits and calculate the delta. + */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + setsofttty(); + } + + /* handle CTS change immediately for crisp flow ctl */ + if (com->state & CS_CTS_OFLOW) { + if (modem_status & MSR_CTS) + com->state |= CS_ODEVREADY; + else + com->state &= ~CS_ODEVREADY; + } + } + + /* output queued and everything ready? */ + if (line_status & LSR_TXRDY + && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) { + ioptr = com->optr; + if (com->tx_fifo_size > 1) { + u_int ocount; + + ocount = com->obufend - ioptr; + if (ocount > com->tx_fifo_size) + ocount = com->tx_fifo_size; + com->bytes_out += ocount; + do + outb(com->data_port, *ioptr++); + while (--ocount != 0); + } else { + outb(com->data_port, *ioptr++); + ++com->bytes_out; + } + com->optr = ioptr; + if (ioptr >= com->obufend) { + /* output just completed */ + com_events += LOTS_OF_EVENTS; + com->state ^= (CS_ODONE | CS_BUSY); + setsofttty(); /* handle at high level ASAP */ + } + } + + /* finished? */ +#ifndef COM_MULTIPORT + if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) +#endif /* COM_MULTIPORT */ + return; + } +} + +static int +tiocm_xxx2mcr(tiocm_xxx) + int tiocm_xxx; +{ + int mcr; + + mcr = 0; + if (tiocm_xxx & TIOCM_DTR) + mcr |= MCR_DTR; + if (tiocm_xxx & TIOCM_RTS) + mcr |= MCR_RTS; + return (mcr); +} + +int +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct com_s *com; + int error; + Port_t iobase; + int mcr; + int msr; + int s; + int tiocm_xxx; + struct tty *tp; + + com = com_addr(UNIT(dev)); + tp = com->tp; + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + error = ttioctl(tp, cmd, data, flag); + +#ifdef COM_BIDIR + /* XXX: plug security hole while sticky bits not yet implemented */ + if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0) + tp->t_cflag &= ~CLOCAL; +#endif + + if (error >= 0) + return (error); + + iobase = com->iobase; + s = spltty(); + switch (cmd) { + case TIOCSBRK: + outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); + break; + case TIOCCBRK: + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); + break; + case TIOCSDTR: + commctl(com, MCR_DTR, DMBIS); + break; + case TIOCCDTR: + commctl(com, MCR_DTR, DMBIC); + break; + case TIOCMSET: + commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET); + break; + case TIOCMBIS: + commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS); + break; + case TIOCMBIC: + commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC); + break; + case TIOCMGET: + tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ + mcr = com->mcr_image; + if (mcr & MCR_DTR) + tiocm_xxx |= TIOCM_DTR; + if (mcr & MCR_RTS) + tiocm_xxx |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + tiocm_xxx |= TIOCM_CTS; + if (msr & MSR_DCD) + tiocm_xxx |= TIOCM_CD; + if (msr & MSR_DSR) + tiocm_xxx |= TIOCM_DSR; + /* + * XXX - MSR_RI is naturally volatile, and we make MSR_TERI + * more volatile by reading the modem status a lot. Perhaps + * we should latch both bits until the status is read here. + */ + if (msr & (MSR_RI | MSR_TERI)) + tiocm_xxx |= TIOCM_RI; + *(int *)data = tiocm_xxx; + break; +#ifdef COM_BIDIR + case TIOCMSBIDIR: + /* must be root to set bidir. capability */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return(EPERM); + } + + /* if it's the console, can't do it (XXX why?) */ + if (UNIT(dev) == comconsole) { + splx(s); + return(ENOTTY); + } + +#if 0 + /* XXX - can't do the next, for obvious reasons... + * but there are problems to be looked at... + */ + /* if the port is active, don't do it */ + if (com->active) { + splx(s); + return(EBUSY); + } +#endif + + com->bidir = *(int *)data; + break; + case TIOCMGBIDIR: + *(int *)data = com->bidir; + break; +#endif /* COM_BIDIR */ +#if 0 + case TIOCMSDTRWAIT: + /* must be root since the wait applies to following logins */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return(EPERM); + } + + /* if it's the console, can't do it (XXX why?) */ + if (UNIT(dev) == comconsole) { + splx(s); + return(ENOTTY); + } + com->dtr_wait = *(int *)data; + break; + case TIOCMGDTRWAIT: + *(int *)data = com->dtr_wait; + break; +#endif +#ifdef TIOCTIMESTAMP + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; + break; +#endif + default: + splx(s); + return (ENOTTY); + } + splx(s); + return (0); +} + +/* cancel pending output */ +static void +comflush(com) + struct com_s *com; +{ + struct clist *rbp; + + disable_intr(); + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + enable_intr(); + while( getc( TB_OUT(com->tp)) != -1); + com->ocount = 0; + com->tp->t_state &= ~TS_BUSY; +} + +void +siopoll() +{ +#ifdef OLD_INTERRUPT_HANDLING + static bool_t awake = FALSE; + int s; +#endif + int unit; + + if (com_events == 0) + return; + +#ifdef OLD_INTERRUPT_HANDLING + disable_intr(); + if (awake) { + enable_intr(); + return; + } + awake = TRUE; + enable_intr(); + s = spltty(); +#endif + +repeat: + for (unit = 0; unit < NSIO; ++unit) { + u_char *buf; + struct com_s *com; + u_char *ibuf; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + tp = com->tp; +#ifdef DONT_MALLOC_TTYS + if (tp == NULL) + continue; +#endif + + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ + incc = 0; + } else { + buf = ibuf; + disable_intr(); + incc = com->iptr - buf; + com_events -= incc; + if (ibuf == com->ibuf1) + ibuf = com->ibuf2; + else + ibuf = com->ibuf1; + com->ibufend = ibuf + RS_IBUFSIZE; + com->ihighwater = ibuf + RS_IHIGHWATER; + com->iptr = ibuf; + + /* + * There is now room for another low-level buffer full + * of input, so enable RTS if it is now disabled and + * there is room in the high-level buffer. + */ + /* + * XXX this used not to look at CS_RTS_IFLOW. The + * change is to allow full control of MCR_RTS via + * ioctls after turning CS_RTS_IFLOW off. Check + * for races. We shouldn't allow the ioctls while + * CS_RTS_IFLOW is on. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) /* + && !(tp->t_state & TS_RTS_IFLOW) */) + outb(com->modem_ctl_port, + com->mcr_image |= MCR_RTS); + enable_intr(); + com->ibuf = ibuf; + } + + if (com->state & CS_CHECKMSR) { + u_char delta_modem_status; + + disable_intr(); + delta_modem_status = com->last_modem_status + ^ com->prev_modem_status; + com->prev_modem_status = com->last_modem_status; + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + enable_intr(); + if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) { + if (com->prev_modem_status & MSR_DCD) { + (*linesw[tp->t_line].l_modem)(tp, 1); +#ifdef COM_BIDIR + wakeup((caddr_t) &com->active_in); +#endif /* COM_BIDIR */ + } else + (*linesw[tp->t_line].l_modem)(tp, 0); + } + } + + /* XXX */ + if (TRUE) { + u_int delta; + int errnum; + u_long total; + + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + disable_intr(); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; + enable_intr(); + if (delta != 0) { + total = + com->error_counts[errnum] += delta; + log(LOG_WARNING, + "sio%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); + } + } + } + if (com->state & CS_ODONE) { + comflush(com); + /* XXX - why isn't the table used for t_line == 0? */ + if (tp->t_line != 0) + (*linesw[tp->t_line].l_start)(tp); + else + comstart(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) + continue; + if (com->state & CS_RTS_IFLOW + && TB_RAW(tp)->c_cc + incc >= RB_I_HIGH_WATER /* + && !(tp->t_state & TS_RTS_IFLOW) */ + /* + * XXX - need RTS flow control for all line disciplines. + * Only have it in standard one now. + */ + && linesw[tp->t_line].l_rint == ttyinput) { +/* tp->t_state |= TS_RTS_IFLOW; */ + ttstart(tp); + } +#if 0 + /* + * Avoid the grotesquely inefficient lineswitch routine + * (ttyinput) in "raw" mode. It usually takes about 450 + * instructions (that's without canonical processing or echo!). + * slinput is reasonably fast (usually 40 instructions plus + * call overhead). + */ + if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP + | IXOFF | IXON)) + && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG + | PENDIN)) + && !(tp->t_state & (TS_CNTTB | TS_LNCH)) + && linesw[tp->t_line].l_rint == ttyinput) { + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += incc - rb_write(TB_RAW(tp), (char *) buf, + incc); + ttwakeup(tp); + if (tp->t_state & TS_TTSTOP + && (tp->t_iflag & IXANY + || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { + tp->t_state &= ~TS_TTSTOP; + tp->t_lflag &= ~FLUSHO; + ttstart(tp); + } + } else { +#endif + do { + u_char line_status; + int recv_data; + + line_status = (u_char) buf[CE_INPUT_OFFSET]; + recv_data = (u_char) *buf++; + if (line_status + & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { + if (line_status & LSR_BI) + recv_data |= TTY_BI; + if (line_status & LSR_FE) + recv_data |= TTY_FE; + if (line_status & LSR_OE) + recv_data |= TTY_OE; + if (line_status & LSR_PE) + recv_data |= TTY_PE; + } + (*linesw[tp->t_line].l_rint)(recv_data, tp); + } while (--incc > 0); +#if 0 + } +#endif + if (com_events == 0) + break; + } + if (com_events >= LOTS_OF_EVENTS) + goto repeat; + +#ifdef OLD_INTERRUPT_HANDLING + splx(s); + awake = FALSE; +#endif +} + +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + u_int cfcr; + int cflag; + struct com_s *com; + int divisor; + int error; + Port_t iobase; + int s; + int unit; + + /* check requested parameters */ + divisor = ttspeedtab(t->c_ospeed, comspeedtab); + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + if (divisor < 0 || t->c_ispeed != t->c_ospeed) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + if (divisor == 0) + commctl(com, MCR_DTR, DMBIC); /* hang up line */ + else + commctl(com, MCR_DTR, DMBIS); + cflag = t->c_cflag; + switch (cflag & CSIZE) { + case CS5: + cfcr = CFCR_5BITS; + break; + case CS6: + cfcr = CFCR_6BITS; + break; + case CS7: + cfcr = CFCR_7BITS; + break; + default: + cfcr = CFCR_8BITS; + break; + } + if (cflag & PARENB) { + cfcr |= CFCR_PENAB; + if (!(cflag & PARODD)) + cfcr |= CFCR_PEVEN; + } + if (cflag & CSTOPB) + cfcr |= CFCR_STOPB; + + /* + * Some UARTs lock up if the divisor latch registers are selected + * while the UART is doing output (they refuse to transmit anything + * more until given a hard reset). Fix this by stopping filling + * the device buffers and waiting for them to drain. Reading the + * line status port outside of siointr1() might lose some receiver + * error bits, but that is acceptable here. + */ + disable_intr(); +retry: + com->state &= ~CS_TTGO; + enable_intr(); + while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) { + error = ttysleep(tp, (caddr_t)TB_RAW(tp), TTIPRI | PCATCH, + "sioparam", 1); + if (error != 0 && error != EAGAIN) { + if (!(tp->t_state & TS_TTSTOP)) { + disable_intr(); + com->state |= CS_TTGO; + enable_intr(); + } + splx(s); + return (error); + } + } + + disable_intr(); /* very important while com_data is hidden */ + + /* + * XXX - clearing CS_TTGO is not sufficient to stop further output, + * because siopoll() calls comstart() which usually sets it again + * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be + * sufficient, for similar reasons. + */ + if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) + goto retry; + + if (divisor != 0) { + outb(iobase + com_cfcr, cfcr | CFCR_DLAB); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + } + outb(iobase + com_cfcr, com->cfcr_image = cfcr); + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) + com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ + else + com->state &= ~CS_RTS_IFLOW; + + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 16+ msec latency, while CTS flow has 50- usec latency. + */ + com->state &= ~CS_CTS_OFLOW; + com->state |= CS_ODEVREADY; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->last_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } + + /* + * Recover from fiddling with CS_TTGO. We used to call siointr1() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + * + * XXX sioopen() is not careful waiting for carrier for the callout + * case. + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); + + enable_intr(); + splx(s); + return (0); +} + +static void +comstart(tp) + struct tty *tp; +{ + struct com_s *com; + int s; + int unit; + + unit = UNIT(tp->t_dev); + com = com_addr(unit); + s = spltty(); + disable_intr(); + if (tp->t_state & TS_TTSTOP) + com->state &= ~CS_TTGO; + else + com->state |= CS_TTGO; +#if 0 + if (tp->t_state & TS_RTS_IFLOW) { + if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) + outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); + } else { +#endif + /* + * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it + * appropriately in comparam() if RTS-flow is being changed. + * Check for races. + */ + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); +#if 0 + } +#endif + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (TB_OUT(tp)->c_cc <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)TB_OUT(tp)); + } + selwakeup(&tp->t_wsel); + } + if (com->ocount != 0) { + disable_intr(); + siointr1(com); + enable_intr(); + } else if (TB_OUT(tp)->c_cc != 0) { + tp->t_state |= TS_BUSY; + disable_intr(); + com->ocount = q_to_b(TB_OUT(tp), com->obuf, sizeof com->obuf); + com->optr = com->obuf; + com->obufend = com->obuf + com->ocount; + com->state |= CS_BUSY; + siointr1(com); /* fake interrupt to start output */ + enable_intr(); + } +out: + splx(s); +} + +void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + + com = com_addr(UNIT(tp->t_dev)); + if (rw & FWRITE) + comflush(com); + disable_intr(); + if (rw & FREAD) { + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } + if (tp->t_state & TS_TTSTOP) + com->state &= ~CS_TTGO; + else + com->state |= CS_TTGO; + enable_intr(); +} + +int +sioselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + return (ttselect(dev & ~COM_MINOR_MAGIC_MASK, rw, p)); +} + +static void +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + disable_intr(); + switch (how) { + case DMSET: + outb(com->modem_ctl_port, + com->mcr_image = bits | (com->mcr_image & MCR_IENABLE)); + break; + case DMBIS: + outb(com->modem_ctl_port, com->mcr_image |= bits); + break; + case DMBIC: + outb(com->modem_ctl_port, com->mcr_image &= ~bits); + break; + } + enable_intr(); +} + +static void +comwakeup(chan, ticks) + caddr_t chan; + int ticks; +{ + int unit; + + timeout((timeout_func_t)comwakeup, (caddr_t) NULL, hz / 100); + + if (com_events != 0) { +#ifndef OLD_INTERRUPT_HANDLING + int s = spltty(); +#endif + siopoll(); +#ifndef OLD_INTERRUPT_HANDLING + splx(s); +#endif + } + + /* recover from lost output interrupts */ + for (unit = 0; unit < NSIO; ++unit) { + struct com_s *com; + + com = com_addr(unit); + if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { + disable_intr(); + siointr1(com); + enable_intr(); + } + } +} + +#ifdef OLD_INTERRUPT_HANDLING +void +softsio1() +{ + siopoll(); +} +#endif + +/* + * Following are all routines needed for SIO to act as console + */ +#include "i386/i386/cons.h" + +struct siocnstate { + u_char dlbl; + u_char dlbh; + u_char ier; + u_char cfcr; + u_char mcr; +}; + +static Port_t siocniobase; + +static void +siocntxwait() +{ + int timo; + + /* + * Wait for any pending transmission to finish. Required to avoid + * the UART lockup bug when the speed is changed, and for normal + * transmits. + */ + timo = 100000; + while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY) && --timo != 0) + ; +} + +static void +siocnopen(sp) + struct siocnstate *sp; +{ + int divisor; + Port_t iobase; + + /* + * Save all the device control registers except the fifo register + * and set our default ones (cs8 -parenb speed=comdefaultrate). + * We can't save the fifo register since it is read-only. + */ + iobase = siocniobase; + sp->ier = inb(iobase + com_ier); + outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ + siocntxwait(); + sp->cfcr = inb(iobase + com_cfcr); + outb(iobase + com_cfcr, CFCR_DLAB); + sp->dlbl = inb(iobase + com_dlbl); + sp->dlbh = inb(iobase + com_dlbh); + divisor = ttspeedtab(comdefaultrate, comspeedtab); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + sp->mcr = inb(iobase + com_mcr); + outb(iobase + com_mcr, MCR_DTR | MCR_RTS); +} + +static void +siocnclose(sp) + struct siocnstate *sp; +{ + Port_t iobase; + + /* + * Restore the device control registers. + */ + siocntxwait(); + iobase = siocniobase; + outb(iobase + com_cfcr, CFCR_DLAB); + outb(iobase + com_dlbl, sp->dlbl); + outb(iobase + com_dlbh, sp->dlbh); + outb(iobase + com_cfcr, sp->cfcr); + /* + * XXX damp osicllations of MCR_DTR or MCR_RTS by not restoring them. + */ + outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); + outb(iobase + com_ier, sp->ier); +} + +void +siocnprobe(cp) + struct consdev *cp; +{ + int unit; + + /* locate the major number */ + /* XXX - should be elsewhere since KGDB uses it */ + for (commajor = 0; commajor < nchrdev; commajor++) + if (cdevsw[commajor].d_open == sioopen) + break; + + /* XXX: ick */ + unit = UNIT(CONUNIT); + siocniobase = CONADDR; + + /* make sure hardware exists? XXX */ + + /* initialize required fields */ + cp->cn_dev = makedev(commajor, unit); +#ifdef COMCONSOLE + cp->cn_pri = CN_REMOTE; /* Force a serial port console */ +#else + cp->cn_pri = CN_NORMAL; +#endif +} + +void +siocninit(cp) + struct consdev *cp; +{ + /* + * XXX can delete more comconsole stuff now that i/o routines are + * fairly reentrant. + */ + comconsole = UNIT(cp->cn_dev); +} + +int +siocngetc(dev) + dev_t dev; +{ + int c; + Port_t iobase; + int s; + struct siocnstate sp; + + iobase = siocniobase; + s = spltty(); + siocnopen(&sp); + while (!(inb(iobase + com_lsr) & LSR_RXRDY)) + ; + c = inb(iobase + com_data); + siocnclose(&sp); + splx(s); + return (c); +} + +void +siocnputc(dev, c) + dev_t dev; + int c; +{ + int s; + struct siocnstate sp; + + s = spltty(); + siocnopen(&sp); + siocntxwait(); + outb(siocniobase + com_data, c); + siocnclose(&sp); + splx(s); +} + +#endif /* NSIO > 0 */ diff --git a/sys/dev/sio/sioreg.h b/sys/dev/sio/sioreg.h new file mode 100644 index 0000000..4b0f1b6 --- /dev/null +++ b/sys/dev/sio/sioreg.h @@ -0,0 +1,114 @@ +/*- + * 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. + * + * from: @(#)comreg.h 7.2 (Berkeley) 5/9/91 + * $Id$ + */ + + +/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */ +#define COMBRD(x) (1843200 / (16*(x))) + +/* 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 + +/* character format control register */ +#define CFCR_DLAB 0x80 +#define CFCR_SBREAK 0x40 +#define CFCR_PZERO 0x30 +#define CFCR_PONE 0x20 +#define CFCR_PEVEN 0x10 +#define CFCR_PODD 0x00 +#define CFCR_PENAB 0x08 +#define CFCR_STOPB 0x04 +#define CFCR_8BITS 0x03 +#define CFCR_7BITS 0x02 +#define CFCR_6BITS 0x01 +#define CFCR_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 + +/* + * WARNING: Serial console is assumed to be at COM1 address + * and CONUNIT must be 0. + */ +#define CONADDR (0x3f8) +#define CONUNIT (0) diff --git a/sys/dev/speaker/speaker.h b/sys/dev/speaker/speaker.h new file mode 100644 index 0000000..af80a28 --- /dev/null +++ b/sys/dev/speaker/speaker.h @@ -0,0 +1,30 @@ +/* + * speaker.h -- interface definitions for speaker ioctl() + * + * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 + * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> + */ + +#ifndef _SPEAKER_H_ +#define _SPEAKER_H_ + +#include <sys/ioctl.h> + +#define SPKRTONE _IOW('S', 1, tone_t) /* emit tone */ +#define SPKRTUNE _IO('S', 2) /* emit tone sequence*/ + +typedef struct +{ + int frequency; /* in hertz */ + int duration; /* in 1/100ths of a second */ +} +tone_t; + +/* + * Strings written to the speaker device are interpreted as tunes and played; + * see the spkr(4) man page for details. + */ + +#endif /* _SPEAKER_H_ */ + +/* speaker.h ends here */ diff --git a/sys/dev/speaker/spkr.c b/sys/dev/speaker/spkr.c new file mode 100644 index 0000000..d273f31 --- /dev/null +++ b/sys/dev/speaker/spkr.c @@ -0,0 +1,541 @@ +/* + * spkr.c -- device driver for console speaker + * + * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 + * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> + * + * $Id: spkr.c,v 1.7 1994/01/25 23:04:27 ache Exp $ + */ + +#include "speaker.h" + +#if NSPEAKER > 0 + +#include "param.h" +#include "systm.h" +#include "kernel.h" +#include "errno.h" +#include "buf.h" +#include "uio.h" +#include "i386/isa/isa.h" +#include "i386/isa/timerreg.h" +#include "machine/speaker.h" + +/**************** MACHINE DEPENDENT PART STARTS HERE ************************* + * + * This section defines a function tone() which causes a tone of given + * frequency and duration from the 80x86's console speaker. + * Another function endtone() is defined to force sound off, and there is + * also a rest() entry point to do pauses. + * + * Audible sound is generated using the Programmable Interval Timer (PIT) and + * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The + * PPI controls whether sound is passed through at all; the PIT's channel 2 is + * used to generate clicks (a square wave) of whatever frequency is desired. + */ + +/* + * PIT and PPI port addresses and control values + * + * Most of the magic is hidden in the TIMER_PREP value, which selects PIT + * channel 2, frequency LSB first, square-wave mode and binary encoding. + * The encoding is as follows: + * + * +----------+----------+---------------+-----+ + * | 1 0 | 1 1 | 0 1 1 | 0 | + * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD | + * +----------+----------+---------------+-----+ + * Counter Write Mode 3 Binary + * Channel 2 LSB first, (Square Wave) Encoding + * MSB second + */ +#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ +#define PIT_MODE 0xB6 /* set timer mode for sound generation */ + +/* + * Magic numbers for timer control. + */ +#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */ + +#define SPKRPRI PSOCK +static char endtone, endrest; + +static void tone(thz, ticks) +/* emit tone of frequency thz for given number of ticks */ +unsigned int thz, ticks; +{ + unsigned int divisor = TIMER_CLK / thz; + int sps; + +#ifdef DEBUG + (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); +#endif /* DEBUG */ + + /* set timer to generate clicks at given frequency in Hertz */ + sps = spltty(); + + if (acquire_timer2(PIT_MODE)) { + /* enter list of waiting procs ??? */ + return; + } + outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */ + outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */ + splx(sps); + + /* turn the speaker on */ + outb(IO_PPI, inb(IO_PPI) | PPI_SPKR); + + /* + * Set timeout to endtone function, then give up the timeslice. + * This is so other processes can execute while the tone is being + * emitted. + */ + (void) tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); + outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR); + release_timer2(); +} + +static void rest(ticks) +/* rest for given number of ticks */ +int ticks; +{ + /* + * Set timeout to endrest function, then give up the timeslice. + * This is so other processes can execute while the rest is being + * waited out. + */ +#ifdef DEBUG + (void) printf("rest: %d\n", ticks); +#endif /* DEBUG */ + (void) tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks); +} + +/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** + * + * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; + * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- + * tracking facility are added. + * Requires tone(), rest(), and endtone(). String play is not interruptible + * except possibly at physical block boundaries. + */ + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + +#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) +#define isdigit(c) (((c) >= '0') && ((c) <= '9')) +#define dtoi(c) ((c) - '0') + +static int octave; /* currently selected octave */ +static int whole; /* whole-note time at current tempo, in ticks */ +static int value; /* whole divisor for note time, quarter note = 1 */ +static int fill; /* controls spacing of notes */ +static bool octtrack; /* octave-tracking on? */ +static bool octprefix; /* override current octave-tracking state? */ + +/* + * Magic number avoidance... + */ +#define SECS_PER_MIN 60 /* seconds per minute */ +#define WHOLE_NOTE 4 /* quarter notes per whole note */ +#define MIN_VALUE 64 /* the most we can divide a note by */ +#define DFLT_VALUE 4 /* default value (quarter-note) */ +#define FILLTIME 8 /* for articulation, break note in parts */ +#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ +#define NORMAL 7 /* 7/8ths of note interval is filled */ +#define LEGATO 8 /* all of note interval is filled */ +#define DFLT_OCTAVE 4 /* default octave */ +#define MIN_TEMPO 32 /* minimum tempo */ +#define DFLT_TEMPO 120 /* default tempo */ +#define MAX_TEMPO 255 /* max tempo */ +#define NUM_MULT 3 /* numerator of dot multiplier */ +#define DENOM_MULT 2 /* denominator of dot multiplier */ + +/* letter to half-tone: A B C D E F G */ +static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; + +/* + * This is the American Standard A440 Equal-Tempered scale with frequencies + * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... + * our octave 0 is standard octave 2. + */ +#define OCTAVE_NOTES 12 /* semitones per octave */ +static int pitchtab[] = +{ +/* C C# D D# E F F# G G# A A# B*/ +/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, +/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, +/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, +/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, +/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, +/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, +/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, +}; + +static void playinit() +{ + octave = DFLT_OCTAVE; + whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; + fill = NORMAL; + value = DFLT_VALUE; + octtrack = FALSE; + octprefix = TRUE; /* act as though there was an initial O(n) */ +} + +static void playtone(pitch, value, sustain) +/* play tone of proper duration for current rhythm signature */ +int pitch, value, sustain; +{ + register int sound, silence, snum = 1, sdenom = 1; + + /* this weirdness avoids floating-point arithmetic */ + for (; sustain; sustain--) + { + /* See the BUGS section in the man page for discussion */ + snum *= NUM_MULT; + sdenom *= DENOM_MULT; + } + + if (pitch == -1) + rest(whole * snum / (value * sdenom)); + else + { + sound = (whole * snum) / (value * sdenom) + - (whole * (FILLTIME - fill)) / (value * FILLTIME); + silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); + +#ifdef DEBUG + (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", + pitch, sound, silence); +#endif /* DEBUG */ + + tone(pitchtab[pitch], sound); + if (fill != LEGATO) + rest(silence); + } +} + +static int abs(n) +int n; +{ + if (n < 0) + return(-n); + else + return(n); +} + +static void playstring(cp, slen) +/* interpret and play an item from a notation string */ +char *cp; +size_t slen; +{ + int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; + +#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ + {v = v * 10 + (*++cp - '0'); slen--;} + for (; slen--; cp++) + { + int sustain, timeval, tempo; + register char c = toupper(*cp); + +#ifdef DEBUG + (void) printf("playstring: %c (%x)\n", c, c); +#endif /* DEBUG */ + + switch (c) + { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + + /* compute pitch */ + pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; + + /* this may be followed by an accidental sign */ + if (cp[1] == '#' || cp[1] == '+') + { + ++pitch; + ++cp; + slen--; + } + else if (cp[1] == '-') + { + --pitch; + ++cp; + slen--; + } + + /* + * If octave-tracking mode is on, and there has been no octave- + * setting prefix, find the version of the current letter note + * closest to the last regardless of octave. + */ + if (octtrack && !octprefix) + { + if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) + { + ++octave; + pitch += OCTAVE_NOTES; + } + + if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) + { + --octave; + pitch -= OCTAVE_NOTES; + } + } + octprefix = FALSE; + lastpitch = pitch; + + /* ...which may in turn be followed by an override time value */ + GETNUM(cp, timeval); + if (timeval <= 0 || timeval > MIN_VALUE) + timeval = value; + + /* ...and/or sustain dots */ + for (sustain = 0; cp[1] == '.'; cp++) + { + slen--; + sustain++; + } + + /* ...and/or a slur mark */ + oldfill = fill; + if (cp[1] == '_') + { + fill = LEGATO; + ++cp; + slen--; + } + + /* time to emit the actual tone */ + playtone(pitch, timeval, sustain); + + fill = oldfill; + break; + + case 'O': + if (cp[1] == 'N' || cp[1] == 'n') + { + octprefix = octtrack = FALSE; + ++cp; + slen--; + } + else if (cp[1] == 'L' || cp[1] == 'l') + { + octtrack = TRUE; + ++cp; + slen--; + } + else + { + GETNUM(cp, octave); + if (octave >= sizeof(pitchtab) / OCTAVE_NOTES) + octave = DFLT_OCTAVE; + octprefix = TRUE; + } + break; + + case '>': + if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1) + octave++; + octprefix = TRUE; + break; + + case '<': + if (octave > 0) + octave--; + octprefix = TRUE; + break; + + case 'N': + GETNUM(cp, pitch); + for (sustain = 0; cp[1] == '.'; cp++) + { + slen--; + sustain++; + } + oldfill = fill; + if (cp[1] == '_') + { + fill = LEGATO; + ++cp; + slen--; + } + playtone(pitch - 1, value, sustain); + fill = oldfill; + break; + + case 'L': + GETNUM(cp, value); + if (value <= 0 || value > MIN_VALUE) + value = DFLT_VALUE; + break; + + case 'P': + case '~': + /* this may be followed by an override time value */ + GETNUM(cp, timeval); + if (timeval <= 0 || timeval > MIN_VALUE) + timeval = value; + for (sustain = 0; cp[1] == '.'; cp++) + { + slen--; + sustain++; + } + playtone(-1, timeval, sustain); + break; + + case 'T': + GETNUM(cp, tempo); + if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) + tempo = DFLT_TEMPO; + whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; + break; + + case 'M': + if (cp[1] == 'N' || cp[1] == 'n') + { + fill = NORMAL; + ++cp; + slen--; + } + else if (cp[1] == 'L' || cp[1] == 'l') + { + fill = LEGATO; + ++cp; + slen--; + } + else if (cp[1] == 'S' || cp[1] == 's') + { + fill = STACCATO; + ++cp; + slen--; + } + break; + } + } +} + +/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** + * + * This section implements driver hooks to run playstring() and the tone(), + * endtone(), and rest() functions defined above. + */ + +static int spkr_active = FALSE; /* exclusion flag */ +static struct buf *spkr_inbuf; /* incoming buf */ + +int spkropen(dev) +dev_t dev; +{ +#ifdef DEBUG + (void) printf("spkropen: entering with dev = %x\n", dev); +#endif /* DEBUG */ + + if (minor(dev) != 0) + return(ENXIO); + else if (spkr_active) + return(EBUSY); + else + { +#ifdef DEBUG + (void) printf("spkropen: about to perform play initialization\n"); +#endif /* DEBUG */ + playinit(); + spkr_inbuf = geteblk(DEV_BSIZE); + spkr_active = TRUE; + return(0); + } +} + +int spkrwrite(dev, uio) +dev_t dev; +struct uio *uio; +{ +#ifdef DEBUG + printf("spkrwrite: entering with dev = %x, count = %d\n", + dev, uio->uio_resid); +#endif /* DEBUG */ + + if (minor(dev) != 0) + return(ENXIO); + else if (uio->uio_resid > DEV_BSIZE) /* prevent system crashes */ + return(E2BIG); + else + { + unsigned n; + char *cp; + int error; + + n = uio->uio_resid; + cp = spkr_inbuf->b_un.b_addr; + if (!(error = uiomove(cp, n, uio))) + playstring(cp, n); + return(error); + } +} + +int spkrclose(dev) +dev_t dev; +{ +#ifdef DEBUG + (void) printf("spkrclose: entering with dev = %x\n", dev); +#endif /* DEBUG */ + + if (minor(dev) != 0) + return(ENXIO); + else + { + wakeup((caddr_t)&endtone); + wakeup((caddr_t)&endrest); + brelse(spkr_inbuf); + spkr_active = FALSE; + return(0); + } +} + +int spkrioctl(dev, cmd, cmdarg) +dev_t dev; +int cmd; +caddr_t cmdarg; +{ +#ifdef DEBUG + (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n"); +#endif /* DEBUG */ + + if (minor(dev) != 0) + return(ENXIO); + else if (cmd == SPKRTONE) + { + tone_t *tp = (tone_t *)cmdarg; + + if (tp->frequency == 0) + rest(tp->duration); + else + tone(tp->frequency, tp->duration); + return 0; + } + else if (cmd == SPKRTUNE) + { + tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); + tone_t ttp; + int error; + + for (; ; tp++) { + error = copyin(tp, &ttp, sizeof(tone_t)); + if (error) + return(error); + if (ttp.duration == 0) + break; + if (ttp.frequency == 0) + rest(ttp.duration); + else + tone(ttp.frequency, ttp.duration); + } + return(0); + } + return(EINVAL); +} + +#endif /* NSPEAKER > 0 */ +/* spkr.c ends here */ diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c new file mode 100644 index 0000000..a2a931c --- /dev/null +++ b/sys/dev/syscons/syscons.c @@ -0,0 +1,2660 @@ +/*- + * Copyright (c) 1992-1994 Søren Schmidt + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from:@(#)syscons.c 1.3 940129 + * $Id: syscons.c,v 1.46 1994/05/25 08:59:56 rgrimes Exp $ + * + */ + +#if !defined(__FreeBSD__) +#define FAT_CURSOR +#endif + +#include "param.h" +#include <sys/systm.h> +#include "conf.h" +#include "ioctl.h" +#include "proc.h" +#include "user.h" +#include "tty.h" +#include "uio.h" +#include "callout.h" +#include "kernel.h" +#include "syslog.h" +#include "errno.h" +#include "malloc.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/timerreg.h" +#include "i386/i386/cons.h" +#include "machine/console.h" +#include "machine/psl.h" +#include "machine/frame.h" +#include "machine/pc/display.h" +#include "iso8859.font" +#include "kbdtables.h" +#include "sc.h" + +#if NSC > 0 + +#if !defined(NCONS) +#define NCONS 12 +#endif + +/* status flags */ +#define LOCK_KEY_MASK 0x0000F +#define LED_MASK 0x00007 +#define UNKNOWN_MODE 0x00010 +#define KBD_RAW_MODE 0x00020 +#define SWITCH_WAIT_REL 0x00040 +#define SWITCH_WAIT_ACQ 0x00080 + +/* video hardware memory addresses */ +#define VIDEOMEM 0x000A0000 + +/* misc defines */ +#define MAX_ESC_PAR 3 +#define TEXT80x25 1 +#define TEXT80x50 2 +#define COL 80 +#define ROW 25 +#define BELL_DURATION 5 +#define BELL_PITCH 800 +#define TIMER_FREQ 1193182 /* should be in isa.h */ +#define PCBURST 256 + +/* defines related to hardware addresses */ +#define MONO_BASE 0x3B4 /* crt controller base mono */ +#define COLOR_BASE 0x3D4 /* crt controller base color */ +#define ATC IO_VGA+0x00 /* attribute controller */ +#define TSIDX IO_VGA+0x04 /* timing sequencer idx */ +#define TSREG IO_VGA+0x05 /* timing sequencer data */ +#define PIXMASK IO_VGA+0x06 /* pixel write mask */ +#define PALRADR IO_VGA+0x07 /* palette read address */ +#define PALWADR IO_VGA+0x08 /* palette write address */ +#define PALDATA IO_VGA+0x09 /* palette data register */ +#define GDCIDX IO_VGA+0x0E /* graph data controller idx */ +#define GDCREG IO_VGA+0x0F /* graph data controller data */ + +/* special characters */ +#define cntlc 0x03 +#define cntld 0x04 +#define bs 0x08 +#define lf 0x0a +#define cr 0x0d +#define del 0x7f + +typedef struct term_stat { + int esc; /* processing escape sequence */ + int num_param; /* # of parameters to ESC */ + int last_param; /* last parameter # */ + int param[MAX_ESC_PAR]; /* contains ESC parameters */ + int cur_attr; /* current attributes */ + int std_attr; /* normal attributes */ + int rev_attr; /* reverse attributes */ +} term_stat; + +typedef struct scr_stat { + u_short *crt_base; /* address of screen memory */ + u_short *scr_buf; /* buffer when off screen */ + u_short *crtat; /* cursor address */ + int xpos; /* current X position */ + int ypos; /* current Y position */ + int xsize; /* X size */ + int ysize; /* Y size */ + term_stat term; /* terminal emulation stuff */ + char cursor_start; /* cursor start line # */ + char cursor_end; /* cursor end line # */ + u_char border; /* border color */ + u_short bell_duration; + u_short bell_pitch; + u_short status; /* status (bitfield) */ + u_short mode; /* mode */ + pid_t pid; /* pid of controlling proc */ + struct proc *proc; /* proc* of controlling proc */ + struct vt_mode smode; /* switch mode */ +} scr_stat; + +typedef struct default_attr { + int std_attr; /* normal attributes */ + int rev_attr; /* reverse attributes */ +} default_attr; + +static default_attr user_default = { + (FG_LIGHTGREY | BG_BLACK) << 8, + (FG_BLACK | BG_LIGHTGREY) << 8 +}; + +static default_attr kernel_default = { + (FG_WHITE | BG_BLACK) << 8, + (FG_BLACK | BG_LIGHTGREY) << 8 +}; + +#define CONSOLE_BUFFER_SIZE 1024 +int console_buffer_count; +char console_buffer[CONSOLE_BUFFER_SIZE]; + +static scr_stat console[NCONS]; +static scr_stat *cur_console = &console[0]; +static scr_stat *new_scp, *old_scp; +static term_stat kernel_console; +static default_attr *current_default; +static int switch_in_progress = 0; +static u_short *crtat = 0; +static u_int crtc_addr = MONO_BASE; +static char crtc_vga = 0; +static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; +static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; +static char palette[3*256]; +static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); +static int cur_cursor_pos = -1; +static char in_putc = 0; +static char polling = 0; +static int delayed_next_scr; +static char saved_console = -1; /* saved console number */ +static long scrn_blank_time = 0; /* screen saver timout value */ +static int scrn_blanked = 0; /* screen saver active flag */ +static int scrn_saver = 0; /* screen saver routine */ +static long scrn_time_stamp; +static u_char scr_map[256]; +extern int hz; +extern struct timeval time; + +/* function prototypes */ +int pcprobe(struct isa_device *dev); +int pcattach(struct isa_device *dev); +int pcopen(dev_t dev, int flag, int mode, struct proc *p); +int pcclose(dev_t dev, int flag, int mode, struct proc *p); +int pcread(dev_t dev, struct uio *uio, int flag); +int pcwrite(dev_t dev, struct uio *uio, int flag); +int pcparam(struct tty *tp, struct termios *t); +int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); +void pcxint(dev_t dev); +void pcstart(struct tty *tp); +void pccnprobe(struct consdev *cp); +void pccninit(struct consdev *cp); +void pccnputc(dev_t dev, char c); +int pccngetc(dev_t dev); +void scintr(int unit); +int pcmmap(dev_t dev, int offset, int nprot); +u_int sgetc(int noblock); +int getchar(void); +static void scinit(void); +static void scput(u_char c); +static u_int scgetc(int noblock); +static struct tty *get_tty_ptr(dev_t dev); +static scr_stat *get_scr_stat(dev_t dev); +static int get_scr_num(); +static void cursor_shape(int start, int end); +static void get_cursor_shape(int *start, int *end); +static void cursor_pos(int force); +static void clear_screen(scr_stat *scp); +static int switch_scr(u_int next_scr); +static void exchange_scr(void); +static void move_crsr(scr_stat *scp, int x, int y); +static void move_up(u_short *s, u_short *d, u_int len); +static void move_down(u_short *s, u_short *d, u_int len); +static void scan_esc(scr_stat *scp, u_char c); +static void ansi_put(scr_stat *scp, u_char c); +static u_char *get_fstr(u_int c, u_int *len); +static void update_leds(int which); +static void kbd_wait(void); +static void kbd_cmd(u_char command); +static void kbd_cmd2(u_char command, u_char arg); +static int kbd_reply(void); +static void set_mode(scr_stat *scp); +static void set_border(int color); +static void load_font(int segment, int size, char* font); +static void save_palette(void); +static void load_palette(void); +static void change_winsize(struct tty *tp, int x, int y); + + +/* available screen savers */ + +static void none_saver(int test); +static void blank_saver(int test); +static void fade_saver(int test); +static void star_saver(int test); +static void snake_saver(int test); + +static const struct { + char *name; + void (*routine)(); +} screen_savers[] = { + { "none", none_saver }, /* 0 */ + { "blank", blank_saver }, /* 1 */ + { "fade", fade_saver }, /* 2 */ + { "star", star_saver }, /* 3 */ + { "snake", snake_saver }, /* 4 */ +}; +#define SCRN_SAVER(arg) (*screen_savers[scrn_saver].routine)(arg) +#define NUM_SCRN_SAVERS (sizeof(screen_savers) / sizeof(screen_savers[0])) + +/* OS specific stuff */ + +#if defined(NetBSD) +#define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc()) +#define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc()) +#define frametype struct trapframe +#define eflags tf_eflags +extern u_short *Crtat; +struct tty *pc_tty[NCONS+1]; +int ttrstrt(); +#endif + +#if defined(__FreeBSD__) +#if 0 +#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x])) +#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS])) +struct tty *pccons[NCONS+1]; +#else +#define VIRTUAL_TTY(x) &pccons[x] +#define CONSOLE_TTY &pccons[NCONS] +struct tty pccons[NCONS+1]; +#endif +#define timeout_t timeout_func_t +#define frametype struct trapframe +#define eflags tf_eflags +#define MONO_BUF (KERNBASE+0xB0000) +#define CGA_BUF (KERNBASE+0xB8000) +#endif + +#if defined(__386BSD__) && !defined(__FreeBSD__) +#define VIRTUAL_TTY(x) &pccons[x] +#define CONSOLE_TTY &pccons[NCONS] +#define frametype struct syscframe +#define eflags sf_eflags +#define timeout_t caddr_t +#define MONO_BUF (0xFE0B0000) +#define CGA_BUF (0xFE0B8000) +struct tty pccons[NCONS+1]; +#endif + +#if defined(__386BSD__) || defined(__FreeBSD__) +u_short *Crtat = (u_short *)MONO_BUF; +void consinit(void) {scinit();} +#include "ddb.h" +#if NDDB > 0 +#define DDB 1 +#endif +#endif + +struct isa_driver scdriver = { + pcprobe, pcattach, "sc", +}; + + +int pcprobe(struct isa_device *dev) +{ + /* Enable interrupts and keyboard controller */ + kbd_wait(); + outb(KB_STAT, KB_WRITE); + kbd_cmd(0x4D); + + /* Start keyboard stuff RESET */ + for (;;) { + kbd_cmd(KB_RESET); + if (kbd_reply() == KB_ACK && /* command accepted */ + kbd_reply() == 0xaa) /* self test passed */ + break; + printf("Keyboard reset failed\n"); + } + return (IO_KBDSIZE); +} + + +int pcattach(struct isa_device *dev) +{ + scr_stat *scp; + int start = -1, end = -1, i; + + printf("sc%d: ", dev->id_unit); + if (crtc_vga) + if (crtc_addr == MONO_BASE) + printf("VGA mono"); + else + printf("VGA color"); + else + if (crtc_addr == MONO_BASE) + printf("MDA/hercules"); + else + printf("CGA/EGA"); + + if (NCONS > 1) + printf(" <%d virtual consoles>\n", NCONS); + else + printf("\n"); +#if defined(FAT_CURSOR) + start = 0; + end = 18; + if (crtc_vga) { +#else + if (crtc_vga) { + get_cursor_shape(&start, &end); +#endif + save_palette(); + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + } + current_default = &user_default; + for (i = 0; i < NCONS; i++) { + scp = &console[i]; + scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT); + scp->mode = TEXT80x25; + scp->term.esc = 0; + scp->term.std_attr = current_default->std_attr; + scp->term.rev_attr = current_default->rev_attr; + scp->term.cur_attr = scp->term.std_attr; + scp->border = BG_BLACK; + scp->cursor_start = start; + scp->cursor_end = end; + scp->xsize = COL; + scp->ysize = ROW; + scp->bell_pitch = BELL_PITCH; + scp->bell_duration = BELL_DURATION; + scp->status = 0; + scp->pid = 0; + scp->proc = NULL; + scp->smode.mode = VT_AUTO; + if (i > 0) { + scp->crt_base = scp->crtat = scp->scr_buf; + fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW); + } + } + /* get cursor going */ +#if defined(FAT_CURSOR) + cursor_shape(console[0].cursor_start, + console[0].cursor_end); +#endif + cursor_pos(1); + return 0; +} + + +static struct tty *get_tty_ptr(dev_t dev) +{ + int unit = minor(dev); + + if (unit > NCONS) + return(NULL); + if (unit == NCONS) + return(CONSOLE_TTY); + return(VIRTUAL_TTY(unit)); +} + + +static scr_stat *get_scr_stat(dev_t dev) +{ + int unit = minor(dev); + + if (unit > NCONS) + return(NULL); + if (unit == NCONS) + return(&console[0]); + return(&console[unit]); +} + + +static int get_scr_num() +{ + int i = 0; + + while ((i < NCONS) && (cur_console != &console[i])) i++; + return i < NCONS ? i : 0; +} + +int pcopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tty *tp = get_tty_ptr(dev); + + if (!tp) + return(ENXIO); + + tp->t_oproc = pcstart; + tp->t_param = pcparam; + tp->t_dev = dev; + if (!(tp->t_state & TS_ISOPEN)) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + pcparam(tp, &tp->t_termios); + ttsetwater(tp); + } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) + return(EBUSY); + tp->t_state |= TS_CARR_ON; + tp->t_cflag |= CLOCAL; + return((*linesw[tp->t_line].l_open)(dev, tp)); +} + + +int pcclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tty *tp = get_tty_ptr(dev); + struct scr_stat *scp; + + if (!tp) + return(ENXIO); + if (minor(dev) < NCONS) { + scp = get_scr_stat(tp->t_dev); + if (scp->status & SWITCH_WAIT_ACQ) + wakeup((caddr_t)&scp->smode); + scp->pid = 0; + scp->proc = NULL; + scp->smode.mode = VT_AUTO; + } + (*linesw[tp->t_line].l_close)(tp, flag); + ttyclose(tp); + return(0); +} + + +int pcread(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tp = get_tty_ptr(dev); + + if (!tp) + return(ENXIO); + return((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + + +int pcwrite(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tp = get_tty_ptr(dev); + + if (!tp) + return(ENXIO); + return((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + + +/* + * Got a console interrupt, keyboard action ! + * Catch the character, and see who it goes to. + */ +void scintr(int unit) +{ + static struct tty *cur_tty; + int c, len; + u_char *cp; + + /* make screensaver happy */ + scrn_time_stamp = time.tv_sec; + if (scrn_blanked) + SCRN_SAVER(0); + + c = scgetc(1); + + cur_tty = VIRTUAL_TTY(get_scr_num()); + if (!(cur_tty->t_state & TS_ISOPEN)) + cur_tty = CONSOLE_TTY; + + if (!(cur_tty->t_state & TS_ISOPEN) || polling) + return; + + switch (c & 0xff00) { + case 0x0000: /* normal key */ + (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); + break; + case NOKEY: /* nothing there */ + break; + case FKEY: /* function key, return string */ + if (cp = get_fstr((u_int)c, (u_int *)&len)) { + while (len-- > 0) + (*linesw[cur_tty->t_line].l_rint) + (*cp++ & 0xFF, cur_tty); + } + break; + case MKEY: /* meta is active, prepend ESC */ + (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); + (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); + break; + } +} + + +/* + * Set line parameters + */ +int pcparam(struct tty *tp, struct termios *t) +{ + int cflag = t->c_cflag; + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = cflag; + return 0; +} + + +int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +{ + int i, error; + struct tty *tp; + frametype *fp; + scr_stat *scp; + + tp = get_tty_ptr(dev); + if (!tp) + return ENXIO; + scp = get_scr_stat(tp->t_dev); + + switch (cmd) { /* process console hardware related ioctl's */ + + case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ + scrn_blank_time = *(int*)data; + return 0; + case CONS_SSAVER: /* set screen saver */ + { + register ssaver_t *sav = (ssaver_t *)data; + if (sav->num < 0 || sav->num >= NUM_SCRN_SAVERS) + return EIO; + SCRN_SAVER(0); + scrn_saver = sav->num; + scrn_blank_time = sav->time; + return 0; + } + case CONS_GSAVER: /* get screen saver info */ + { + register ssaver_t *sav = (ssaver_t *)data; + if (sav->num < 0) + sav->num = scrn_saver; + else if (sav->num >= NUM_SCRN_SAVERS) + return EIO; + sav->time = scrn_blank_time; + strcpy(sav->name, screen_savers[sav->num].name); + return 0; + } + case CONS_80x25TEXT: /* set 80x25 text mode */ + if (!crtc_vga) + return ENXIO; + scp->mode = TEXT80x25; + scp->ysize = 25; + free(scp->scr_buf, M_DEVBUF); + scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2, + M_DEVBUF, M_NOWAIT); + if (scp != cur_console) + scp->crt_base = scp->scr_buf; + set_mode(scp); + clear_screen(scp); + change_winsize(tp, scp->xsize, scp->ysize); + return 0; + + case CONS_80x50TEXT: /* set 80x50 text mode */ + if (!crtc_vga) + return ENXIO; + scp->mode = TEXT80x50; + scp->ysize = 50; + free(scp->scr_buf, M_DEVBUF); + scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2, + M_DEVBUF, M_NOWAIT); + if (scp != cur_console) + scp->crt_base = scp->scr_buf; + set_mode(scp); + clear_screen(scp); + change_winsize(tp, scp->xsize, scp->ysize); + return 0; + + case CONS_GETVERS: /* get version number */ + *(int*)data = 0x103; /* version 1.3 */ + return 0; + + case CONS_GETINFO: /* get current (virtual) console info */ + { + vid_info_t *ptr = (vid_info_t*)data; + if (ptr->size == sizeof(struct vid_info)) { + ptr->m_num = get_scr_num(); + ptr->mv_col = scp->xpos; + ptr->mv_row = scp->ypos; + ptr->mv_csz = scp->xsize; + ptr->mv_rsz = scp->ysize; + ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8; + ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12; + ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8; + ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12; + ptr->mv_grfc.fore = 0; /* not supported */ + ptr->mv_grfc.back = 0; /* not supported */ + ptr->mv_ovscan = scp->border; + ptr->mk_keylock = scp->status & LOCK_KEY_MASK; + return 0; + } + return EINVAL; + } + + case VT_SETMODE: /* set screen switcher mode */ + bcopy(data, &scp->smode, sizeof(struct vt_mode)); + if (scp->smode.mode == VT_PROCESS) { + scp->proc = p; + scp->pid = scp->proc->p_pid; + } + return 0; + + case VT_GETMODE: /* get screen switcher mode */ + bcopy(&scp->smode, data, sizeof(struct vt_mode)); + return 0; + + case VT_RELDISP: /* screen switcher ioctl */ + switch(*data) { + case VT_FALSE: /* user refuses to release screen, abort */ + if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { + old_scp->status &= ~SWITCH_WAIT_REL; + switch_in_progress = 0; + return 0; + } + return EINVAL; + + case VT_TRUE: /* user has released screen, go on */ + if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { + scp->status &= ~SWITCH_WAIT_REL; + exchange_scr(); + if (new_scp->smode.mode == VT_PROCESS) { + new_scp->status |= SWITCH_WAIT_ACQ; + psignal(new_scp->proc, + new_scp->smode.acqsig); + } + else + switch_in_progress = 0; + return 0; + } + return EINVAL; + + case VT_ACKACQ: /* acquire acknowledged, switch completed */ + if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { + scp->status &= ~SWITCH_WAIT_ACQ; + switch_in_progress = 0; + return 0; + } + return EINVAL; + + default: + return EINVAL; + } + /* NOT REACHED */ + + case VT_OPENQRY: /* return free virtual console */ + for (i = 0; i < NCONS; i++) { + tp = VIRTUAL_TTY(i); + if (!(tp->t_state & TS_ISOPEN)) { + *data = i + 1; + return 0; + } + } + return EINVAL; + + case VT_ACTIVATE: /* switch to screen *data */ + return switch_scr((*data) - 1); + + case VT_WAITACTIVE: /* wait for switch to occur */ + if (*data > NCONS) + return EINVAL; + if (minor(dev) == (*data) - 1) + return 0; + if (*data == 0) { + if (scp == cur_console) + return 0; + while ((error=tsleep((caddr_t)&scp->smode, + PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; + } + else + while ((error=tsleep( + (caddr_t)&console[*(data-1)].smode, + PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; + return error; + + case VT_GETACTIVE: + *data = get_scr_num()+1; + return 0; + + case KDENABIO: /* allow io operations */ + fp = (frametype *)p->p_md.md_regs; + fp->eflags |= PSL_IOPL; + return 0; + + case KDDISABIO: /* disallow io operations (default) */ + fp = (frametype *)p->p_md.md_regs; + fp->eflags &= ~PSL_IOPL; + return 0; + + case KDSETMODE: /* set current mode of this (virtual) console */ + switch (*data) { + case KD_TEXT: /* switch to TEXT (known) mode */ + /* restore fonts & palette ! */ + if (crtc_vga) { + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + load_palette(); + } + /* FALL THROUGH */ + + case KD_TEXT1: /* switch to TEXT (known) mode */ + /* no restore fonts & palette */ + scp->status &= ~UNKNOWN_MODE; + set_mode(scp); + clear_screen(scp); + return 0; + + case KD_GRAPHICS:/* switch to GRAPHICS (unknown) mode */ + scp->status |= UNKNOWN_MODE; + return 0; + default: + return EINVAL; + } + /* NOT REACHED */ + + case KDGETMODE: /* get current mode of this (virtual) console */ + *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; + return 0; + + case KDSBORDER: /* set border color of this (virtual) console */ + if (!crtc_vga) + return ENXIO; + scp->border = *data; + if (scp == cur_console) + set_border(scp->border); + return 0; + + case KDSKBSTATE: /* set keyboard state (locks) */ + if (*data >= 0 && *data <= LOCK_KEY_MASK) { + scp->status &= ~LOCK_KEY_MASK; + scp->status |= *data; + if (scp == cur_console) + update_leds(scp->status); + return 0; + } + return EINVAL; + + case KDGKBSTATE: /* get keyboard state (locks) */ + *data = scp->status & LOCK_KEY_MASK; + return 0; + + case KDSETRAD: /* set keyboard repeat & delay rates */ + if (*data & 0x80) + return EINVAL; + kbd_cmd2(KB_SETRAD, *data); + return 0; + + case KDSKBMODE: /* set keyboard mode */ + switch (*data) { + case K_RAW: /* switch to RAW scancode mode */ + scp->status |= KBD_RAW_MODE; + return 0; + + case K_XLATE: /* switch to XLT ascii mode */ + if (scp == cur_console && scp->status == KBD_RAW_MODE) + shfts = ctls = alts = agrs = metas = 0; + scp->status &= ~KBD_RAW_MODE; + return 0; + default: + return EINVAL; + } + /* NOT REACHED */ + + case KDGKBMODE: /* get keyboard mode */ + *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; + return 0; + + case KDMKTONE: /* sound the bell */ + if (scp == cur_console) + sysbeep(scp->bell_pitch, scp->bell_duration); + return 0; + + case KIOCSOUND: /* make tone (*data) hz */ + if (scp == cur_console) { + if (*(int*)data) { + int pitch = TIMER_FREQ/(*(int*)data); + /* set command for counter 2, 2 byte write */ + if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) { + return EBUSY; + } + /* set pitch */ + outb(TIMER_CNTR2, pitch); + outb(TIMER_CNTR2, (pitch>>8)); + /* enable counter 2 output to speaker */ + outb(IO_PPI, inb(IO_PPI) | 3); + } + else { + /* disable counter 2 output to speaker */ + outb(IO_PPI, inb(IO_PPI) & 0xFC); + release_timer2(); + } + } + return 0; + + case KDGKBTYPE: /* get keyboard type */ + *data = 0; /* type not known (yet) */ + return 0; + + case KDSETLED: /* set keyboard LED status */ + if (*data >= 0 && *data <= LED_MASK) { + scp->status &= ~LED_MASK; + scp->status |= *data; + if (scp == cur_console) + update_leds(scp->status); + return 0; + } + return EINVAL; + + case KDGETLED: /* get keyboard LED status */ + *data = scp->status & LED_MASK; + return 0; + + case GETFKEY: /* get functionkey string */ + if (*(u_short*)data < n_fkey_tab) { + fkeyarg_t *ptr = (fkeyarg_t*)data; + bcopy(&fkey_tab[ptr->keynum].str, + ptr->keydef, + fkey_tab[ptr->keynum].len); + ptr->flen = fkey_tab[ptr->keynum].len; + return 0; + } + else + return EINVAL; + + case SETFKEY: /* set functionkey string */ + if (*(u_short*)data < n_fkey_tab) { + fkeyarg_t *ptr = (fkeyarg_t*)data; + bcopy(ptr->keydef, + &fkey_tab[ptr->keynum].str, + min(ptr->flen, MAXFK)); + fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); + return 0; + } + else + return EINVAL; + + case GIO_SCRNMAP: /* get output translation table */ + bcopy(&scr_map, data, sizeof(scr_map)); + return 0; + + case PIO_SCRNMAP: /* set output translation table */ + bcopy(data, &scr_map, sizeof(scr_map)); + return 0; + + case GIO_KEYMAP: /* get keyboard translation table */ + bcopy(&key_map, data, sizeof(key_map)); + return 0; + + case PIO_KEYMAP: /* set keyboard translation table */ + bcopy(data, &key_map, sizeof(key_map)); + return 0; + + case PIO_FONT8x8: /* set 8x8 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x8, sizeof(font_8x8)); + load_font(1, 8, font_8x8); + return 0; + + case GIO_FONT8x8: /* get 8x8 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x8, data, sizeof(font_8x8)); + return 0; + + case PIO_FONT8x14: /* set 8x14 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x14, sizeof(font_8x14)); + load_font(2, 14, font_8x14); + return 0; + + case GIO_FONT8x14: /* get 8x14 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x14, data, sizeof(font_8x14)); + return 0; + + case PIO_FONT8x16: /* set 8x16 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x16, sizeof(font_8x16)); + load_font(0, 16, font_8x16); + return 0; + + case GIO_FONT8x16: /* get 8x16 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x16, data, sizeof(font_8x16)); + return 0; + + case CONSOLE_X_MODE_ON: /* just to be compatible */ + if (saved_console < 0) { + saved_console = get_scr_num(); + switch_scr(minor(dev)); + fp = (frametype *)p->p_md.md_regs; + fp->eflags |= PSL_IOPL; + scp->status |= UNKNOWN_MODE; + scp->status |= KBD_RAW_MODE; + return 0; + } + return EAGAIN; + + case CONSOLE_X_MODE_OFF:/* just to be compatible */ + fp = (frametype *)p->p_md.md_regs; + fp->eflags &= ~PSL_IOPL; + if (crtc_vga) { + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + load_palette(); + } + scp->status &= ~UNKNOWN_MODE; + set_mode(scp); + clear_screen(scp); + scp->status &= ~KBD_RAW_MODE; + switch_scr(saved_console); + saved_console = -1; + return 0; + + case CONSOLE_X_BELL: /* more compatibility */ + /* + * if set, data is a pointer to a length 2 array of + * integers. data[0] is the pitch in Hz and data[1] + * is the duration in msec. + */ + if (data) + sysbeep(TIMER_FREQ/((int*)data)[0], + ((int*)data)[1]*hz/3000); + else + sysbeep(scp->bell_pitch, scp->bell_duration); + return 0; + + default: + break; + } + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return(error); + error = ttioctl(tp, cmd, data, flag); + if (error >= 0) + return(error); + return(ENOTTY); +} + + +void pcxint(dev_t dev) +{ + struct tty *tp = get_tty_ptr(dev); + + if (!tp) + return; + tp->t_state &= ~TS_BUSY; + if (tp->t_line) + (*linesw[tp->t_line].l_start)(tp); + else + pcstart(tp); +} + + +void pcstart(struct tty *tp) +{ +#if defined(NetBSD) || defined(__FreeBSD__) + struct clist *rbp; + int i, s, len; + u_char buf[PCBURST]; + scr_stat *scp = get_scr_stat(tp->t_dev); + + if (scp->status & SLKED) + return; + s = spltty(); /* Isn't start always called at spltty? */ + if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { + tp->t_state |= TS_BUSY; + splx(s); + rbp = &tp->t_outq; + while (rbp->c_cc) { + len = q_to_b(rbp, buf, PCBURST); + for (i=0; i<len; i++) + if (buf[i]) ansi_put(scp, buf[i]); + } + s = spltty(); + tp->t_state &= ~TS_BUSY; +#if 0 + if (rbp->c_cc) { + tp->t_state |= TS_TIMEOUT; + timeout((timeout_t)ttrstrt, (caddr_t)tp, 1); + } +#endif + if (rbp->c_cc <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)rbp); + } + selwakeup(&tp->t_wsel); + } + } + splx(s); + +#else /* __386BSD__ */ + + int c, s, len, i; + scr_stat *scp = get_scr_stat(tp->t_dev); + u_char buf[PCBURST]; + + if (scp->status & SLKED) + return; + s = spltty(); + if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { + for (;;) { + if (RB_LEN(tp->t_out) <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)tp->t_out); + } + selwakeup(&tp->t_wsel); + } + if (RB_LEN(tp->t_out) == 0) + break; + if (scp->status & SLKED) + break; + len = 0; + while( len < PCBURST) { + buf[len++] = getc(tp->t_out); + if( RB_LEN(tp->t_out) == 0) + break; + } + tp->t_state |= TS_BUSY; + splx(s); + for(i=0;i<len;i++) + ansi_put(scp, buf[i]); + s = spltty(); + tp->t_state &= ~TS_BUSY; + } + tp->t_state |= TS_BUSY; + if( in_putc == 0) { + int i; + for(i=0;i<console_buffer_count;i++) { + scput(console_buffer[i]); + } + console_buffer_count = 0; + } + tp->t_state &= ~TS_BUSY; + } + splx(s); +#endif +} + + +void pccnprobe(struct consdev *cp) +{ + int maj; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if ((void*)cdevsw[maj].d_open == (void*)pcopen) + break; + + /* initialize required fields */ + cp->cn_dev = makedev(maj, NCONS); + cp->cn_pri = CN_INTERNAL; +#if defined(__386BSD__) && !defined(__FreeBSD__) + cp->cn_tp = CONSOLE_TTY; +#endif +} + + +void pccninit(struct consdev *cp) +{ + scinit(); +} + + +void pccnputc(dev_t dev, char c) +{ + if (c == '\n') + scput('\r'); + scput(c); + if (cur_console == &console[0]) { + int pos = cur_console->crtat - cur_console->crt_base; + if (pos != cur_cursor_pos) { + cur_cursor_pos = pos; + outb(crtc_addr,14); + outb(crtc_addr+1,pos >> 8); + outb(crtc_addr,15); + outb(crtc_addr+1,pos&0xff); + } + } +} + + +int pccngetc(dev_t dev) +{ + int s = spltty(); /* block scintr while we poll */ + int c = scgetc(0); + splx(s); + if (c == '\r') c = '\n'; + return(c); +} + +static void none_saver(int test) +{ +} + +static void fade_saver(int test) +{ + static int count = 0; + int i; + + if (test) { + scrn_blanked = 1; + if (count < 64) { + outb(PIXMASK, 0xFF); /* no pixelmask */ + outb(PALWADR, 0x00); + outb(PALDATA, 0); + outb(PALDATA, 0); + outb(PALDATA, 0); + for (i = 3; i < 768; i++) { + if (palette[i] - count > 15) + outb(PALDATA, palette[i]-count); + else + outb(PALDATA, 15); + } + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x20); /* enable palette */ + count++; + } + } + else { + count = scrn_blanked = 0; + load_palette(); + } +} + +static void blank_saver(int test) +{ + u_char val; + if (test) { + scrn_blanked = 1; + outb(TSIDX, 0x01); val = inb(TSREG); + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + } + else { + scrn_blanked = 0; + outb(TSIDX, 0x01); val = inb(TSREG); + outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); + } +} + +static u_long rand_next = 1; + +static int rand() +{ + return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF); +} + +/* + * Alternate saver that got its inspiration from a well known utility + * package for an unfamous OS. + */ + +#define NUM_STARS 50 + +static void star_saver(int test) +{ + scr_stat *scp = cur_console; + int cell, i; + char pattern[] = {"...........++++*** "}; + char colors[] = {FG_DARKGREY, FG_LIGHTGREY, + FG_WHITE, FG_LIGHTCYAN}; + static u_short stars[NUM_STARS][2]; + + if (test) { + if (!scrn_blanked) { + bcopy(Crtat, scp->scr_buf, + scp->xsize * scp->ysize * 2); + fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat, + scp->xsize * scp->ysize); + set_border(0); + i = scp->ysize * scp->xsize + 5; + outb(crtc_addr, 14); + outb(crtc_addr+1, i >> 8); + outb(crtc_addr, 15); + outb(crtc_addr+1, i & 0xff); + scrn_blanked = 1; + for(i=0; i<NUM_STARS; i++) { + stars[i][0] = + rand() % (scp->xsize*scp->ysize); + stars[i][1] = 0; + } + } + cell = rand() % NUM_STARS; + *((u_short*)(Crtat + stars[cell][0])) = + scr_map[pattern[stars[cell][1]]] | + colors[rand()%sizeof(colors)] << 8; + if ((stars[cell][1]+=(rand()%4)) >= sizeof(pattern)-1) { + stars[cell][0] = rand() % (scp->xsize*scp->ysize); + stars[cell][1] = 0; + } + } + else { + if (scrn_blanked) { + bcopy(scp->scr_buf, Crtat, scp->xsize*scp->ysize*2); + cur_cursor_pos = -1; + set_border(scp->border); + scrn_blanked = 0; + } + } +} + + +static void snake_saver(int test) +{ + const char saves[] = {"FreeBSD"}; + static u_char *savs[sizeof(saves)-1]; + static int dirx, diry; + int f; + scr_stat *scp = cur_console; + + if (test) { + if (!scrn_blanked) { + bcopy(Crtat, scp->scr_buf, + scp->xsize * scp->ysize * 2); + fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], + Crtat, scp->xsize * scp->ysize); + set_border(0); + dirx = (scp->xpos ? 1 : -1); + diry = (scp->ypos ? + scp->xsize : -scp->xsize); + for (f=0; f< sizeof(saves)-1; f++) + savs[f] = (u_char *)Crtat + 2 * + (scp->xpos+scp->ypos*scp->xsize); + *(savs[0]) = scr_map[*saves]; + f = scp->ysize * scp->xsize + 5; + outb(crtc_addr, 14); + outb(crtc_addr+1, f >> 8); + outb(crtc_addr, 15); + outb(crtc_addr+1, f & 0xff); + scrn_blanked = 1; + } + if (scrn_blanked++ < 4) + return; + scrn_blanked = 1; + *(savs[sizeof(saves)-2]) = scr_map[0x20]; + for (f=sizeof(saves)-2; f > 0; f--) + savs[f] = savs[f-1]; + f = (savs[0] - (u_char *)Crtat) / 2; + if ((f % scp->xsize) == 0 || + (f % scp->xsize) == scp->xsize - 1 || + (rand() % 50) == 0) + dirx = -dirx; + if ((f / scp->xsize) == 0 || + (f / scp->xsize) == scp->ysize - 1 || + (rand() % 20) == 0) + diry = -diry; + savs[0] += 2*dirx + 2*diry; + for (f=sizeof(saves)-2; f>=0; f--) + *(savs[f]) = scr_map[saves[f]]; + } + else { + if (scrn_blanked) { + bcopy(scp->scr_buf, Crtat, + scp->xsize * scp->ysize * 2); + cur_cursor_pos = -1; + set_border(scp->border); + scrn_blanked = 0; + } + } +} + +static void cursor_shape(int start, int end) +{ + outb(crtc_addr, 10); + outb(crtc_addr+1, start & 0xFF); + outb(crtc_addr, 11); + outb(crtc_addr+1, end & 0xFF); +} + + +#if !defined(FAT_CURSOR) +static void get_cursor_shape(int *start, int *end) +{ + outb(crtc_addr, 10); + *start = inb(crtc_addr+1) & 0x1F; + outb(crtc_addr, 11); + *end = inb(crtc_addr+1) & 0x1F; +} +#endif + + +static void cursor_pos(int force) +{ + int pos; + + if (cur_console->status & UNKNOWN_MODE) + return; + if (scrn_blank_time && (time.tv_sec > scrn_time_stamp+scrn_blank_time)) + SCRN_SAVER(1); + pos = cur_console->crtat - cur_console->crt_base; + if (force || (!scrn_blanked && pos != cur_cursor_pos)) { + cur_cursor_pos = pos; + outb(crtc_addr, 14); + outb(crtc_addr+1, pos>>8); + outb(crtc_addr, 15); + outb(crtc_addr+1, pos&0xff); + } + timeout((timeout_t)cursor_pos, 0, hz/20); +} + + +static void clear_screen(scr_stat *scp) +{ + move_crsr(scp, 0, 0); + fillw(scp->term.cur_attr | scr_map[0x20], scp->crt_base, + scp->xsize * scp->ysize); +} + + +static int switch_scr(u_int next_scr) +{ + if (in_putc) { /* delay switch if in putc */ + delayed_next_scr = next_scr+1; + return 0; + } + if (switch_in_progress && + (cur_console->proc != pfind(cur_console->pid))) + switch_in_progress = 0; + + if (next_scr >= NCONS || switch_in_progress) { + sysbeep(BELL_PITCH, BELL_DURATION); + return EINVAL; + } + + /* is the wanted virtual console open ? */ + if (next_scr) { + struct tty *tp = VIRTUAL_TTY(next_scr); + if (!(tp->t_state & TS_ISOPEN)) { + sysbeep(BELL_PITCH, BELL_DURATION); + return EINVAL; + } + } + + switch_in_progress = 1; + old_scp = cur_console; + new_scp = &console[next_scr]; + wakeup((caddr_t)&new_scp->smode); + if (new_scp == old_scp) { + switch_in_progress = 0; + return 0; + } + + /* has controlling process died? */ + if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) + old_scp->smode.mode = VT_AUTO; + if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) + new_scp->smode.mode = VT_AUTO; + + /* check the modes and switch approbiatly */ + if (old_scp->smode.mode == VT_PROCESS) { + old_scp->status |= SWITCH_WAIT_REL; + psignal(old_scp->proc, old_scp->smode.relsig); + } + else { + exchange_scr(); + if (new_scp->smode.mode == VT_PROCESS) { + new_scp->status |= SWITCH_WAIT_ACQ; + psignal(new_scp->proc, new_scp->smode.acqsig); + } + else + switch_in_progress = 0; + } + return 0; +} + + +static void exchange_scr(void) +{ + struct tty *tp; + + bcopy(Crtat, old_scp->scr_buf, old_scp->xsize * old_scp->ysize * 2); + old_scp->crt_base = old_scp->scr_buf; + move_crsr(old_scp, old_scp->xpos, old_scp->ypos); + cur_console = new_scp; + set_mode(new_scp); + new_scp->crt_base = Crtat; + move_crsr(new_scp, new_scp->xpos, new_scp->ypos); + bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2); + update_leds(new_scp->status); + if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + load_palette(); + } + if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) + shfts = ctls = alts = agrs = metas = 0; + delayed_next_scr = 0; +} + + +static void move_crsr(scr_stat *scp, int x, int y) +{ + if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize) + return; + scp->xpos = x; + scp->ypos = y; + scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos; +} + +static void move_up(u_short *s, u_short *d, u_int len) +{ + s += len; + d += len; + while (len-- > 0) + *--d = *--s; +} + +static void move_down(u_short *s, u_short *d, u_int len) +{ + while (len-- > 0) + *d++ = *s++; +} + +static void scan_esc(scr_stat *scp, u_char c) +{ + static u_char ansi_col[16] = + {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; + int i, n; + u_short *src, *dst, count; + + if (scp->term.esc == 1) { + switch (c) { + + case '[': /* Start ESC [ sequence */ + scp->term.esc = 2; + scp->term.last_param = -1; + for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) + scp->term.param[i] = 1; + scp->term.num_param = 0; + return; + + case 'M': /* Move cursor up 1 line, scroll if at top */ + if (scp->ypos > 0) + move_crsr(scp, scp->xpos, scp->ypos - 1); + else { + move_up(scp->crt_base, + scp->crt_base + scp->xsize, + (scp->ysize - 1) * scp->xsize); + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base, scp->xsize); + } + break; +#if notyet + case 'Q': + scp->term.esc = 4; + break; +#endif + case 'c': /* Clear screen & home */ + clear_screen(scp); + break; + } + } + else if (scp->term.esc == 2) { + if (c >= '0' && c <= '9') { + if (scp->term.num_param < MAX_ESC_PAR) { + if (scp->term.last_param != scp->term.num_param) { + scp->term.last_param = scp->term.num_param; + scp->term.param[scp->term.num_param] = 0; + } + else + scp->term.param[scp->term.num_param] *= 10; + scp->term.param[scp->term.num_param] += c - '0'; + return; + } + } + scp->term.num_param = scp->term.last_param + 1; + switch (c) { + + case ';': + if (scp->term.num_param < MAX_ESC_PAR) + return; + break; + + case '=': + scp->term.esc = 3; + scp->term.last_param = -1; + for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) + scp->term.param[i] = 1; + scp->term.num_param = 0; + return; + + case 'A': /* up n rows */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos, scp->ypos - n); + break; + + case 'B': /* down n rows */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos, scp->ypos + n); + break; + + case 'C': /* right n columns */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos + n, scp->ypos); + break; + + case 'D': /* left n columns */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos - n, scp->ypos); + break; + + case 'E': /* cursor to start of line n lines down */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, 0, scp->ypos + n); + break; + + case 'F': /* cursor to start of line n lines up */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, 0, scp->ypos - n); + break; + + case 'f': /* System V consoles .. */ + case 'H': /* Cursor move */ + if (scp->term.num_param == 0) + move_crsr(scp, 0, 0); + else if (scp->term.num_param == 2) + move_crsr(scp, scp->term.param[1] - 1, + scp->term.param[0] - 1); + break; + + case 'J': /* Clear all or part of display */ + if (scp->term.num_param == 0) + n = 0; + else + n = scp->term.param[0]; + switch (n) { + case 0: /* clear form cursor to end of display */ + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crtat, scp->crt_base + + scp->xsize * scp->ysize - + scp->crtat); + break; + case 1: /* clear from beginning of display to cursor */ + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base, + scp->crtat - scp->crt_base); + break; + case 2: /* clear entire display */ + clear_screen(scp); + break; + } + break; + + case 'K': /* Clear all or part of line */ + if (scp->term.num_param == 0) + n = 0; + else + n = scp->term.param[0]; + switch (n) { + case 0: /* clear form cursor to end of line */ + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crtat, scp->xsize - scp->xpos); + break; + case 1: /* clear from beginning of line to cursor */ + fillw(scp->term.cur_attr|scr_map[0x20], + scp->crtat - (scp->xsize - scp->xpos), + (scp->xsize - scp->xpos) + 1); + break; + case 2: /* clear entire line */ + fillw(scp->term.cur_attr|scr_map[0x20], + scp->crtat - (scp->xsize - scp->xpos), + scp->xsize); + break; + } + break; + + case 'L': /* Insert n lines */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->ysize - scp->ypos) + n = scp->ysize - scp->ypos; + src = scp->crt_base + scp->ypos * scp->xsize; + dst = src + n * scp->xsize; + count = scp->ysize - (scp->ypos + n); + move_up(src, dst, count * scp->xsize); + fillw(scp->term.cur_attr | scr_map[0x20], src, + n * scp->xsize); + break; + + case 'M': /* Delete n lines */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->ysize - scp->ypos) + n = scp->ysize - scp->ypos; + dst = scp->crt_base + scp->ypos * scp->xsize; + src = dst + n * scp->xsize; + count = scp->ysize - (scp->ypos + n); + move_down(src, dst, count * scp->xsize); + src = dst + count * scp->xsize; + fillw(scp->term.cur_attr | scr_map[0x20], src, + n * scp->xsize); + break; + + case 'P': /* Delete n chars */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->xsize - scp->xpos) + n = scp->xsize - scp->xpos; + dst = scp->crtat; + src = dst + n; + count = scp->xsize - (scp->xpos + n); + move_down(src, dst, count); + src = dst + count; + fillw(scp->term.cur_attr | scr_map[0x20], src, n); + break; + + case '@': /* Insert n chars */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->xsize - scp->xpos) + n = scp->xsize - scp->xpos; + src = scp->crtat; + dst = src + n; + count = scp->xsize - (scp->xpos + n); + move_up(src, dst, count); + fillw(scp->term.cur_attr | scr_map[0x20], src, n); + break; + + case 'S': /* scroll up n lines */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->ypos) + n = scp->ypos; + bcopy(scp->crt_base + (scp->xsize * n), + scp->crt_base, + scp->xsize * (scp->ysize - n) * + sizeof(u_short)); + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base + scp->xsize * + (scp->ysize - 1), + scp->xsize); + break; + + case 'T': /* scroll down n lines */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->ysize - scp->ypos) + n = scp->ysize - scp->ypos; + bcopy(scp->crt_base, + scp->crt_base + (scp->xsize * n), + scp->xsize * (scp->ysize - n) * + sizeof(u_short)); + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base, scp->xsize); + break; + + case 'X': /* delete n characters in line */ + n = scp->term.param[0]; if (n < 1) n = 1; + if (n > scp->xsize - scp->xpos) + n = scp->xsize - scp->xpos; + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base + scp->xpos + + ((scp->xsize*scp->ypos) * sizeof(u_short)), n); + break; + + case 'Z': /* move n tabs backwards */ + n = scp->term.param[0]; if (n < 1) n = 1; + if ((i = scp->xpos & 0xf8) == scp->xpos) + i -= 8*n; + else + i -= 8*(n-1); + if (i < 0) + i = 0; + move_crsr(scp, i, scp->ypos); + break; + + case '`': /* move cursor to column n */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, n, scp->ypos); + break; + + case 'a': /* move cursor n columns to the right */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos + n, scp->ypos); + break; + + case 'd': /* move cursor to row n */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos, n); + break; + + case 'e': /* move cursor n rows down */ + n = scp->term.param[0]; if (n < 1) n = 1; + move_crsr(scp, scp->xpos, scp->ypos + n); + break; + + case 'm': /* change attribute */ + if (scp->term.num_param == 0) + n = 0; + else + n = scp->term.param[0]; + switch (n) { + case 0: /* back to normal */ + scp->term.cur_attr = scp->term.std_attr; + break; + case 1: /* highlight (bold) */ + scp->term.cur_attr &= 0xFF00; + scp->term.cur_attr |= 0x0800; + break; + case 4: /* highlight (underline) */ + scp->term.cur_attr &= 0x0F00; + scp->term.cur_attr |= 0x0800; + break; + case 5: /* blink */ + scp->term.cur_attr &= 0xFF00; + scp->term.cur_attr |= 0x8000; + break; + case 7: /* reverse video */ + scp->term.cur_attr = scp->term.rev_attr; + break; + case 30: case 31: case 32: case 33: /* set fg color */ + case 34: case 35: case 36: case 37: + scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF) + | (ansi_col[(n - 30) & 7] << 8); + break; + case 40: case 41: case 42: case 43: /* set bg color */ + case 44: case 45: case 46: case 47: + scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF) + | (ansi_col[(n - 40) & 7] << 12); + break; + } + break; + + case 'x': + if (scp->term.num_param == 0) + n = 0; + else + n = scp->term.param[0]; + switch (n) { + case 0: /* reset attributes */ + scp->term.cur_attr = scp->term.std_attr = + current_default->std_attr; + scp->term.rev_attr = current_default->rev_attr; + break; + case 1: /* set ansi background */ + scp->term.cur_attr = scp->term.std_attr = + (scp->term.std_attr & 0x0F00) | + (ansi_col[(scp->term.param[1])&0x0F]<<12); + break; + case 2: /* set ansi foreground */ + scp->term.cur_attr = scp->term.std_attr = + (scp->term.std_attr & 0xF000) | + (ansi_col[(scp->term.param[1])&0x0F]<<8); + break; + case 3: /* set ansi attribute directly */ + scp->term.cur_attr = scp->term.std_attr = + (scp->term.param[1]&0xFF)<<8; + break; + case 5: /* set ansi reverse video background */ + scp->term.rev_attr = + (scp->term.rev_attr & 0x0F00) | + (ansi_col[(scp->term.param[1])&0x0F]<<12); + break; + case 6: /* set ansi reverse video foreground */ + scp->term.rev_attr = + (scp->term.rev_attr & 0xF000) | + (ansi_col[(scp->term.param[1])&0x0F]<<8); + break; + case 7: /* set ansi reverse video directly */ + scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8; + break; + } + break; + + case 'z': /* switch to (virtual) console n */ + if (scp->term.num_param == 1) + switch_scr(scp->term.param[0]); + break; + } + } + else if (scp->term.esc == 3) { + if (c >= '0' && c <= '9') { + if (scp->term.num_param < MAX_ESC_PAR) { + if (scp->term.last_param != scp->term.num_param) { + scp->term.last_param = scp->term.num_param; + scp->term.param[scp->term.num_param] = 0; + } + else + scp->term.param[scp->term.num_param] *= 10; + scp->term.param[scp->term.num_param] += c - '0'; + return; + } + } + scp->term.num_param = scp->term.last_param + 1; + switch (c) { + + case ';': + if (scp->term.num_param < MAX_ESC_PAR) + return; + break; + + case 'A': /* set display border color */ + if (scp->term.num_param == 1) + scp->border=scp->term.param[0] & 0xff; + if (scp == cur_console) + set_border(scp->border); + break; + + case 'B': /* set bell pitch and duration */ + if (scp->term.num_param == 2) { + scp->bell_pitch = scp->term.param[0]; + scp->bell_duration = scp->term.param[1]*10; + } + break; + + case 'C': /* set cursor shape (start & end line) */ + if (scp->term.num_param == 2) { + scp->cursor_start = scp->term.param[0] & 0x1F; + scp->cursor_end = scp->term.param[1] & 0x1F; + if (scp == cur_console) + cursor_shape(scp->cursor_start, + scp->cursor_end); + } + break; + + case 'F': /* set ansi foreground */ + if (scp->term.num_param == 1) + scp->term.cur_attr = scp->term.std_attr = + (scp->term.std_attr & 0xF000) + | ((scp->term.param[0] & 0x0F) << 8); + break; + + case 'G': /* set ansi background */ + if (scp->term.num_param == 1) + scp->term.cur_attr = scp->term.std_attr = + (scp->term.std_attr & 0x0F00) + | ((scp->term.param[0] & 0x0F) << 12); + break; + + case 'H': /* set ansi reverse video foreground */ + if (scp->term.num_param == 1) + scp->term.rev_attr = + (scp->term.rev_attr & 0xF000) + | ((scp->term.param[0] & 0x0F) << 8); + break; + + case 'I': /* set ansi reverse video background */ + if (scp->term.num_param == 1) + scp->term.rev_attr = + (scp->term.rev_attr & 0x0F00) + | ((scp->term.param[0] & 0x0F) << 12); + break; + } + } + scp->term.esc = 0; +} + + +static void ansi_put(scr_stat *scp, u_char c) +{ + if (scp->status & UNKNOWN_MODE) + return; + + /* make screensaver happy */ + if (scp == cur_console) { + scrn_time_stamp = time.tv_sec; + if (scrn_blanked) + SCRN_SAVER(0); + } + in_putc++; + if (scp->term.esc) + scan_esc(scp, c); + else switch(c) { + case 0x1B: /* start escape sequence */ + scp->term.esc = 1; + scp->term.num_param = 0; + break; + case 0x07: + if (scp == cur_console) + sysbeep(scp->bell_pitch, scp->bell_duration); + break; + case '\t': /* non-destructive tab */ + scp->crtat += (8 - scp->xpos % 8); + scp->xpos += (8 - scp->xpos % 8); + break; + case '\b': /* non-destructive backspace */ + if (scp->crtat > scp->crt_base) { + scp->crtat--; + if (scp->xpos > 0) + scp->xpos--; + else { + scp->xpos += scp->xsize - 1; + scp->ypos--; + } + } + break; + case '\r': /* return to pos 0 */ + move_crsr(scp, 0, scp->ypos); + break; + case '\n': /* newline, same pos */ + scp->crtat += scp->xsize; + scp->ypos++; + break; + case '\f': /* form feed, clears screen */ + clear_screen(scp); + break; + default: + /* Print only printables */ + *scp->crtat = (scp->term.cur_attr | scr_map[c]); + scp->crtat++; + if (++scp->xpos >= scp->xsize) { + scp->xpos = 0; + scp->ypos++; + } + break; + } + if (scp->crtat >= scp->crt_base + scp->ysize * scp->xsize) { + bcopy(scp->crt_base + scp->xsize, scp->crt_base, + scp->xsize * (scp->ysize - 1) * sizeof(u_short)); + fillw(scp->term.cur_attr | scr_map[0x20], + scp->crt_base + scp->xsize * (scp->ysize - 1), + scp->xsize); + scp->crtat -= scp->xsize; + scp->ypos--; + } + in_putc--; + if (delayed_next_scr) + switch_scr(delayed_next_scr - 1); +} + +static void scinit(void) +{ + u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; + unsigned cursorat; + int i; + + /* + * catch that once in a blue moon occurence when scinit is called + * TWICE, adding the CGA_BUF offset again -> poooff + */ + if (crtat != 0) + return; + /* + * Crtat initialized to point to MONO buffer, if not present change + * to CGA_BUF offset. ONLY ADD the difference since locore.s adds + * in the remapped offset at the "right" time + */ + was = *cp; + *cp = (u_short) 0xA55A; + if (*cp != 0xA55A) { + crtc_addr = MONO_BASE; + } else { + *cp = was; + crtc_addr = COLOR_BASE; + Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short); + } + + /* Extract cursor location */ + outb(crtc_addr,14); + cursorat = inb(crtc_addr+1)<<8 ; + outb(crtc_addr,15); + cursorat |= inb(crtc_addr+1); + crtat = Crtat + cursorat; + + /* is this a VGA or higher ? */ + outb(crtc_addr, 7); + if (inb(crtc_addr) == 7) + crtc_vga = 1; + + current_default = &user_default; + console[0].crtat = crtat; + console[0].crt_base = Crtat; + console[0].term.esc = 0; + console[0].term.std_attr = current_default->std_attr; + console[0].term.rev_attr = current_default->rev_attr; + console[0].term.cur_attr = current_default->std_attr; + console[0].xpos = cursorat % COL; + console[0].ypos = cursorat / COL; + console[0].border = BG_BLACK;; + console[0].xsize = COL; + console[0].ysize = ROW; + console[0].status = 0; + console[0].pid = 0; + console[0].proc = NULL; + console[0].smode.mode = VT_AUTO; + console[0].bell_pitch = BELL_PITCH; + console[0].bell_duration = BELL_DURATION; + kernel_console.esc = 0; + kernel_console.std_attr = kernel_default.std_attr; + kernel_console.rev_attr = kernel_default.rev_attr; + kernel_console.cur_attr = kernel_default.std_attr; + /* initialize mapscrn array to a one to one map */ + for (i=0; i<sizeof(scr_map); i++) + scr_map[i] = i; + clear_screen(&console[0]); +} + + +static void scput(u_char c) +{ + scr_stat *scp = &console[0]; + term_stat save; + + if (crtat == 0) + scinit(); + if( in_putc == 0) { + ++in_putc; + save = scp->term; + scp->term = kernel_console; + current_default = &kernel_default; + ansi_put(scp, c); + kernel_console = scp->term; + current_default = &user_default; + scp->term = save; + --in_putc; + } else { + if( console_buffer_count < CONSOLE_BUFFER_SIZE) + console_buffer[console_buffer_count++] = c; + } +} + + +static u_char *get_fstr(u_int c, u_int *len) +{ + u_int i; + + if (!(c & FKEY)) + return(NULL); + i = (c & 0xFF) - F_FN; + if (i > n_fkey_tab) + return(NULL); + *len = fkey_tab[i].len; + return(fkey_tab[i].str); +} + + +static void update_leds(int which) +{ + static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + /* replace CAPS led with ALTGR led for ALTGR keyboards */ + if (key_map.n_keys > ALTGR_OFFSET) { + if (which & ALKED) + which |= CLKED; + else + which &= ~CLKED; + } + kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]); +} + + +/* + * scgetc(noblock) : get a character from the keyboard. + * If noblock = 0 wait until a key is gotten. Otherwise return NOKEY. + */ +u_int scgetc(int noblock) +{ + u_char val, code, release; + u_int state, action; + struct key_t *key; + static u_char esc_flag = 0, compose = 0; + static u_int chr = 0; + +next_code: + kbd_wait(); + /* First see if there is something in the keyboard port */ + if (inb(KB_STAT) & KB_BUF_FULL) + val = inb(KB_DATA); + else if (noblock) + return(NOKEY); + else + goto next_code; + + if (cur_console->status & KBD_RAW_MODE) + return val; + + code = val & 0x7F; + release = val & 0x80; + + switch (esc_flag) { + case 0x00: /* normal scancode */ + switch(code) { + case 0x38: /* left alt (compose key) */ + if (release && compose) { + compose = 0; + if (chr > 255) { + sysbeep(BELL_PITCH, BELL_DURATION); + chr = 0; + } + } + else { + if (!compose) { + compose = 1; + chr = 0; + } + } + break; + case 0x60: + case 0x61: + esc_flag = code; + goto next_code; + } + break; + case 0x60: /* 0xE0 prefix */ + esc_flag = 0; + switch (code) { + case 0x1c: /* right enter key */ + code = 0x59; + break; + case 0x1d: /* right ctrl key */ + code = 0x5a; + break; + case 0x35: /* keypad divide key */ + code = 0x5b; + break; + case 0x37: /* print scrn key */ + code = 0x5c; + break; + case 0x38: /* right alt key (alt gr) */ + code = 0x5d; + break; + case 0x47: /* grey home key */ + code = 0x5e; + break; + case 0x48: /* grey up arrow key */ + code = 0x5f; + break; + case 0x49: /* grey page up key */ + code = 0x60; + break; + case 0x4b: /* grey left arrow key */ + code = 0x61; + break; + case 0x4d: /* grey right arrow key */ + code = 0x62; + break; + case 0x4f: /* grey end key */ + code = 0x63; + break; + case 0x50: /* grey down arrow key */ + code = 0x64; + break; + case 0x51: /* grey page down key */ + code = 0x65; + break; + case 0x52: /* grey insert key */ + code = 0x66; + break; + case 0x53: /* grey delete key */ + code = 0x67; + break; + default: /* ignore everything else */ + goto next_code; + } + break; + case 0x61: /* 0xE1 prefix */ + esc_flag = 0; + if (code == 0x1D) + esc_flag = 0x1D; + goto next_code; + /* NOT REACHED */ + case 0x1D: /* pause / break */ + esc_flag = 0; + if (code != 0x45) + goto next_code; + code = 0x68; + break; + } + + if (compose) { + switch (code) { + case 0x47: + case 0x48: /* keypad 7,8,9 */ + case 0x49: + if (!release) + chr = (code - 0x40) + chr*10; + goto next_code; + case 0x4b: + case 0x4c: /* keypad 4,5,6 */ + case 0x4d: + if (!release) + chr = (code - 0x47) + chr*10; + goto next_code; + case 0x4f: + case 0x50: /* keypad 1,2,3 */ + case 0x51: + if (!release) + chr = (code - 0x4e) + chr*10; + goto next_code; + case 0x52: /* keypad 0 */ + if (!release) + chr *= 10; + goto next_code; + case 0x38: /* left alt key */ + break; + default: + if (chr) { + compose = chr = 0; + sysbeep(BELL_PITCH, BELL_DURATION); + goto next_code; + } + break; + } + } + + state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); + if ((!agrs && (cur_console->status & ALKED)) + || (agrs && !(cur_console->status & ALKED))) + code += ALTGR_OFFSET; + key = &key_map.key[code]; + if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) + || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) + state ^= 1; + + /* Check for make/break */ + action = key->map[state]; + if (release) { /* key released */ + if (key->spcl & 0x80) { + switch (action) { + case LSH: + shfts &= ~1; + break; + case RSH: + shfts &= ~2; + break; + case LCTR: + ctls &= ~1; + break; + case RCTR: + ctls &= ~2; + break; + case LALT: + alts &= ~1; + break; + case RALT: + alts &= ~2; + break; + case NLK: + nlkcnt = 0; + break; + case CLK: + clkcnt = 0; + break; + case SLK: + slkcnt = 0; + break; + case ASH: + agrs = 0; + break; + case ALK: + alkcnt = 0; + break; + case META: + metas = 0; + break; + } + } + if (chr && !compose) { + action = chr; + chr = 0; + return(action); + } + } else { + /* key pressed */ + if (key->spcl & (0x80>>state)) { + switch (action) { + /* LOCKING KEYS */ + case NLK: + if (!nlkcnt) { + nlkcnt++; + if (cur_console->status & NLKED) + cur_console->status &= ~NLKED; + else + cur_console->status |= NLKED; + update_leds(cur_console->status); + } + break; + case CLK: + if (!clkcnt) { + clkcnt++; + if (cur_console->status & CLKED) + cur_console->status &= ~CLKED; + else + cur_console->status |= CLKED; + update_leds(cur_console->status); + } + break; + case SLK: + if (!slkcnt) { + slkcnt++; + if (cur_console->status & SLKED) { + cur_console->status &= ~SLKED; + pcstart(VIRTUAL_TTY(get_scr_num())); + } + else + cur_console->status |= SLKED; + update_leds(cur_console->status); + } + break; + case ALK: + if (!alkcnt) { + alkcnt++; + if (cur_console->status & ALKED) + cur_console->status &= ~ALKED; + else + cur_console->status |= ALKED; + update_leds(cur_console->status); + } + break; + + /* NON-LOCKING KEYS */ + case NOP: + break; + case RBT: +#if defined(__FreeBSD__) + shutdown_nice(); +#else + cpu_reset(); +#endif + break; + case DBG: +#if DDB > 0 /* try to switch to console 0 */ + if (cur_console->smode.mode == VT_AUTO && + console[0].smode.mode == VT_AUTO) + switch_scr(0); + Debugger("manual escape to debugger"); + return(NOKEY); +#else + printf("No debugger in kernel\n"); +#endif + break; + case LSH: + shfts |= 1; + break; + case RSH: + shfts |= 2; + break; + case LCTR: + ctls |= 1; + break; + case RCTR: + ctls |= 2; + break; + case LALT: + alts |= 1; + break; + case RALT: + alts |= 2; + break; + case ASH: + agrs = 1; + break; + case META: + metas = 1; + break; + case NEXT: + switch_scr((get_scr_num()+1)%NCONS); + break; + default: + if (action >= F_SCR && action <= L_SCR) { + switch_scr(action - F_SCR); + break; + } + if (action >= F_FN && action <= L_FN) + action |= FKEY; + return(action); + } + } + else { + if (metas) + action |= MKEY; + return(action); + } + } + goto next_code; +} + + +int getchar(void) +{ + u_char thechar; + int s; + + polling = 1; + s = splhigh(); + scput('>'); + thechar = (u_char) scgetc(0); + polling = 0; + splx(s); + switch (thechar) { + default: + if (thechar >= scr_map[0x20]) + scput(thechar); + return(thechar); + case cr: + case lf: + scput(cr); scput(lf); + return(lf); + case bs: + case del: + scput(bs); scput(scr_map[0x20]); scput(bs); + return(thechar); + case cntld: + scput('^'); scput('D'); scput('\r'); scput('\n'); + return(0); + } +} + + +u_int sgetc(int noblock) +{ + return (scgetc(noblock) & 0xff); +} + +int pcmmap(dev_t dev, int offset, int nprot) +{ + if (offset > 0x20000) + return EINVAL; + return i386_btop((VIDEOMEM + offset)); +} + + +static void kbd_wait(void) +{ + int i; + + for (i=0; i<1000; i++) { /* up to 10 msec */ + if ((inb(KB_STAT) & KB_READY) == 0) + break; + DELAY (10); + } +} + + +static void kbd_cmd(u_char command) +{ + kbd_wait(); + outb(KB_DATA, command); +} + + +static void kbd_cmd2(u_char command, u_char arg) +{ + int r, s = spltty(); + do { + kbd_cmd(command); + r = kbd_reply(); + if (r == KB_ACK) { + kbd_cmd(arg & 0x7f); + r = kbd_reply(); + } + } while (r != KB_ACK); + splx(s); +} + + +static int kbd_reply() +{ + int i; + + kbd_wait(); + for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */ + if (inb(KB_STAT) & KB_BUF_FULL) + return ((u_char) inb(KB_DATA)); + DELAY (10); + } + return(-1); +} + + +static void set_mode(scr_stat *scp) +{ + u_char byte; + int s; + + if (scp != cur_console) + return; + + /* (re)activate cursor */ + untimeout((timeout_t)cursor_pos, 0); + cursor_pos(1); + + /* change cursor type if set */ + if (scp->cursor_start != -1 && scp->cursor_end != -1) + cursor_shape(scp->cursor_start, scp->cursor_end); + + /* mode change only on VGA's */ + if (!crtc_vga) + return; + + /* setup video hardware for the given mode */ + s = splhigh(); + switch(scp->mode) { + case TEXT80x25: + outb(crtc_addr, 9); byte = inb(crtc_addr+1); + outb(crtc_addr, 9); outb(crtc_addr+1, byte | 0x0F); + outb(TSIDX, 0x03); outb(TSREG, 0x00); /* select font 0 */ + break; + case TEXT80x50: + outb(crtc_addr, 9); byte = inb(crtc_addr+1); + outb(crtc_addr, 9); outb(crtc_addr+1, (byte & 0xF0) | 0x07); + outb(TSIDX, 0x03); outb(TSREG, 0x05); /* select font 1 */ + break; + default: + break; + } + splx(s); + + /* set border color for this (virtual) console */ + set_border(scp->border); + return; +} + + +static void set_border(int color) +{ + inb(crtc_addr+6); /* reset flip-flop */ + outb(ATC, 0x11); outb(ATC, color); + inb(crtc_addr+6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable Palette */ +} + +static void load_font(int segment, int size, char* font) +{ + int ch, line, s; + u_char val; + + outb(TSIDX, 0x01); val = inb(TSREG); /* blank screen */ + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + + /* setup vga for loading fonts (graphics plane mode) */ + s = splhigh(); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x30); outb(ATC, 0x01); + outb(TSIDX, 0x02); outb(TSREG, 0x04); + outb(TSIDX, 0x04); outb(TSREG, 0x06); + outb(GDCIDX, 0x04); outb(GDCREG, 0x02); + outb(GDCIDX, 0x05); outb(GDCREG, 0x00); + outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */ + splx(s); + for (ch=0; ch < 256; ch++) + for (line=0; line < size; line++) + *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) = + font[(ch*size)+line]; + /* setup vga for text mode again */ + s = splhigh(); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x30); outb(ATC, 0x0C); + outb(TSIDX, 0x02); outb(TSREG, 0x03); + outb(TSIDX, 0x04); outb(TSREG, 0x02); + outb(GDCIDX, 0x04); outb(GDCREG, 0x00); + outb(GDCIDX, 0x05); outb(GDCREG, 0x10); + if (crtc_addr == MONO_BASE) { + outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ + } + else { + outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ + } + splx(s); + outb(TSIDX, 0x01); val = inb(TSREG); /* unblank screen */ + outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); +} + + +static void load_palette(void) +{ + int i; + + outb(PIXMASK, 0xFF); /* no pixelmask */ + outb(PALWADR, 0x00); + for (i=0x00; i<0x300; i++) + outb(PALDATA, palette[i]); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x20); /* enable palette */ +} + +static void save_palette(void) +{ + int i; + + outb(PALRADR, 0x00); + for (i=0x00; i<0x300; i++) + palette[i] = inb(PALDATA); + inb(crtc_addr+6); /* reset flip/flop */ +} + + +static void change_winsize(struct tty *tp, int x, int y) +{ + if (tp->t_winsize.ws_col != x || tp->t_winsize.ws_row != y) { + tp->t_winsize.ws_col = x; + tp->t_winsize.ws_row = y; + pgsignal(tp->t_pgrp, SIGWINCH, 1); + } +} + +#endif /* NSC */ |