📄 if_hpp.c
字号:
/* Written 1994 by Donald Becker. This driver is for the Hewlett Packard PC LAN (27***) plus ethercards. These cards are sold under several model numbers, usually 2724*. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 As is often the case, a great deal of credit is owed to Russ Nelson. The Crynwr packet driver was my primary source of HP-specific programming information.*//* * Ported to mach by Stephen Clawson, sclawson@cs.utah.edu * University of Utah CSL. * * Derived from the Linux driver by Donald Becker. * * Also uses code Shantanu Goel adapted from Donald Becker * for ns8930 support. * */#include <hpp.h>#if NHPP > 0#include <sys/types.h>#include "vm_param.h"#include <kern/time_out.h>#include <device/device_types.h>#include <device/errno.h>#include <device/io_req.h>#include <device/if_hdr.h>#include <device/if_ether.h>#include <device/net_status.h>#include <device/net_io.h>#include <chips/busses.h>#include <i386/ipl.h>#include <i386/pio.h>#include <i386at/gpl/if_nsreg.h>/* * XXX - This is some gross glue garbage. The io instructions really * should be integrated into pio.h... */#define IO_DELAY __asm__ __volatile__("outb %al,$0x80")#define outb_p(p, v) { outb(p, v); IO_DELAY; }#define inb_p(p) ({ unsigned char _v; _v = inb(p); IO_DELAY; _v; })static __inline voidinsw(u_short port, void *addr, int cnt){ __asm __volatile("cld\n\trepne\n\tinsw" : : "d" (port), "D" (addr), "c" (cnt) : "%edi", "%ecx");}static __inline voidoutsw(u_short port, void *addr, int cnt){ __asm __volatile("cld\n\trepne\n\toutsw" : : "d" (port), "S" (addr), "c" (cnt) : "%esi", "%ecx");}/* The HP EtherTwist chip implementation is a fairly routine DP8390 implementation. It allows both shared memory and programmed-I/O buffer access, using a custom interface for both. The programmed-I/O mode is entirely implemented in the HP EtherTwist chip, bypassing the problem ridden built-in 8390 facilities used on NE2000 designs. The shared memory mode is likewise special, with an offset register used to make packets appear at the shared memory base. Both modes use a base and bounds page register to hide the Rx ring buffer wrap -- a packet that spans the end of physical buffer memory appears continuous to the driver. (c.f. the 3c503 and Cabletron E2100) A special note: the internal buffer of the board is only 8 bits wide. This lays several nasty traps for the unaware: - the 8390 must be programmed for byte-wide operations - all I/O and memory operations must work on whole words (the access latches are serially preloaded and have no byte-swapping ability). This board is laid out in I/O space much like the earlier HP boards: the first 16 locations are for the board registers, and the second 16 are for the 8390. The board is easy to identify, with both a dedicated 16 bit ID register and a constant 0x530* value in the upper bits of the paging register.*/#define HP_ID 0x00 /* ID register, always 0x4850. */#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */ #define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option.*/#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page.*/#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page.*/#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page.*/#define HPP_NIC_OFFSET 0x10 /* Offset to the 8390 registers.*/#define HP_IO_EXTENT 32#define HP_START_PG 0x00 /* First page of TX buffer */#define HP_STOP_PG 0x80 /* Last page +1 of RX ring *//*#define HP_STOP_PG 0x1f/* The register set selected in HP_PAGING. */enum PageName { Perf_Page = 0, /* Normal operation. */ MAC_Page = 1, /* The ethernet address (+checksum). */ HW_Page = 2, /* EEPROM-loaded hw parameters. */ LAN_Page = 4, /* Transciever type, testing, etc. */ ID_Page = 6 }; /* The bit definitions for the HPP_OPTION register. */enum HP_Option { NICReset = 1, /* Active low, really UNreset. */ ChipReset = 2, EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20, MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };void hpp_reset_8390(struct nssoftc *ns);void hpp_mem_block_input(struct nssoftc *ns, int, char *, int);int hpp_mem_block_output(struct nssoftc *ns, int, char *, int);void hpp_io_block_input(struct nssoftc *ns, int, char *, int);int hpp_io_block_output(struct nssoftc *ns, int,char *, int);/* * Watchdog timer. */int hppwstart = 0;void hppwatch(void);/* * Autoconfig structures. */int hpp_std[] = { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0 };struct bus_device *hpp_info[NHPP];int hpp_probe();void hpp_attach();struct bus_driver hppdriver = { hpp_probe, 0, hpp_attach, 0, hpp_std, "hpp", hpp_info, 0, 0, 0};/* * ns8390 state. */struct nssoftc hppnssoftc[NHPP];/* * hpp state. */struct hppsoftc { unsigned long rmem_start; /* shmem "recv" start */ unsigned long rmem_end; /* shmem "recv" end */ unsigned long mem_start; /* shared mem start */ unsigned long mem_end; /* shared mem end */} hppsoftc[NHPP];/* * Probe a list of addresses for the card. * */int hpp_probe(port, dev) int port; struct bus_device *dev;{ int unit = dev->unit; char *str = "hp-plus ethernet board %d out of range.\n"; caddr_t base = (caddr_t) (dev ? dev->address : 0); int i; if ((unit < 0) || (unit >= NHPP)) { printf(str, unit); return(0); } /* Check a single specified location. */ if (base > (caddr_t) 0x1ff) return hpp_probe1(dev, base); else if (base != 0) /* Don't probe at all. */ return 0; for (i = 0; hpp_std[i]; i++) { int ioaddr = hpp_std[i]; if ( ioaddr > 0 && hpp_probe1(dev, ioaddr) ) { dev->address = ioaddr; hpp_std[i] = -1; /* Mark address used */ return(1); } } return 0;}/* * Do the interesting part of the probe at a single address. * */int hpp_probe1(dev, ioaddr) struct bus_device *dev; int ioaddr;{ int i; u_char checksum = 0; int mem_start; struct hppsoftc *hpp = &hppsoftc[dev->unit]; struct nssoftc *ns = &hppnssoftc[dev->unit]; struct ifnet *ifp = &ns->sc_if; /* Check for the HP+ signature, 50 48 0x 53. */ if (inw(ioaddr + HP_ID) != 0x4850 || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) return 0; printf("%s%d: HP PClan plus at %#3x,", dev->name, dev->unit, ioaddr); /* Retrieve and checksum the station address. */ outw(ioaddr + HP_PAGING, MAC_Page); printf("MAC_Page = %d, ioaddr = %x\n", MAC_Page, ioaddr); for(i = 0; i < ETHER_ADDR_LEN; i++) { u_char inval = inb(ioaddr + 8 + i); ns->sc_addr[i] = inval; checksum += inval; printf(" %2.2x", inval); } checksum += inb(ioaddr + 14); if (checksum != 0xff) { printf(" bad checksum %2.2x.\n", checksum); return 0; } else { /* Point at the Software Configuration Flags. */ outw(ioaddr + HP_PAGING, ID_Page); printf(" ID %4.4x", inw(ioaddr + 12)); } /* Read the IRQ line. */ outw(ioaddr + HP_PAGING, HW_Page); { int irq = inb(ioaddr + 13) & 0x0f; int option = inw(ioaddr + HPP_OPTION); dev->sysdep1 = irq; take_dev_irq(dev); if (option & MemEnable) { mem_start = inw(ioaddr + 9) << 8; printf(", IRQ %d, memory address %#x.\n", irq, mem_start); } else { mem_start = 0; printf(", IRQ %d, programmed-I/O mode.\n", irq); } } /* Set the wrap registers for string I/O reads. */ outw( ioaddr + 14, (HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8)); /* Set the base address to point to the NIC, not the "real" base! */ ns->sc_port = ioaddr + HPP_NIC_OFFSET; ns->sc_name = dev->name; ns->sc_unit = dev->unit; ns->sc_pingpong = 0; /* turn off pingpong mode */ ns->sc_word16 = 0; /* Agggghhhhh! Debug time: 2 days! */ ns->sc_txstrtpg = HP_START_PG; ns->sc_rxstrtpg = HP_START_PG + TX_2X_PAGES; ns->sc_stoppg = HP_STOP_PG; ns->sc_reset = hpp_reset_8390; ns->sc_input = hpp_io_block_input; ns->sc_output = hpp_io_block_output; /* Check if the memory_enable flag is set in the option register. */ if (mem_start) { ns->sc_input = hpp_mem_block_input; ns->sc_output = hpp_mem_block_output; hpp->mem_start = mem_start; hpp->rmem_start = hpp->mem_start + TX_2X_PAGES * 256; hpp->mem_end = hpp->rmem_end = hpp->mem_start + (HP_STOP_PG - HP_START_PG) * 256; } outw(ioaddr + HP_PAGING, Perf_Page); /* Leave the 8390 and HP chip reset. */ outw( ioaddr + HPP_OPTION, inw(ioaddr + HPP_OPTION) & ~EnableIRQ ); /* * Initialize interface header. */ ifp->if_unit = dev->unit; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST; ifp->if_header_size = sizeof(struct ether_header); ifp->if_header_format = HDR_ETHERNET; ifp->if_address_size = ETHER_ADDR_LEN; ifp->if_address = ns->sc_addr; if_init_queues(ifp); return (1);}/* * XXX * * this routine really should do the invasive part of the setup. */voidhpp_attach(dev) struct bus_device *dev;{ /* NULL */}inthppopen(dev, flag) dev_t dev; int flag;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -