From 9cf11cac1f4c79b44e1596dd77dd3f14d83e6bd1 Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Mon, 3 Jul 2006 01:02:17 +0000 Subject: [PATCH] Be, Inc sample code for NE2K-compatible network cards... (Yes, this means QEMU network hw now has an Haiku driver!) PS: Will cleanup code, looks like real Be Sample Code currently :) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18008 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- build/jam/HaikuImage | 2 +- src/add-ons/kernel/drivers/network/Jamfile | 3 +- .../kernel/drivers/network/etherpci/Jamfile | 31 + .../drivers/network/etherpci/etherpci.c | 1783 +++++++++++++++++ .../network/etherpci/etherpci_private.h | 146 ++ 5 files changed, 1963 insertions(+), 2 deletions(-) create mode 100644 src/add-ons/kernel/drivers/network/etherpci/Jamfile create mode 100644 src/add-ons/kernel/drivers/network/etherpci/etherpci.c create mode 100644 src/add-ons/kernel/drivers/network/etherpci/etherpci_private.h diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 0b8a15e7b2..38d44ac700 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -85,7 +85,7 @@ BEOS_ADD_ONS_DRIVERS_AUDIO = auich auvia emuxki ; BEOS_ADD_ONS_DRIVERS_GRAPHICS = $(X86_ONLY)radeon.driver $(X86_ONLY)nv.driver $(X86_ONLY)nm.driver $(X86_ONLY)mga.driver $(X86_ONLY)intel_extreme vesa ; -BEOS_ADD_ONS_DRIVERS_NET = ipro1000 rtl8139 rtl8169 sis900 +BEOS_ADD_ONS_DRIVERS_NET = etherpci ipro1000 rtl8139 rtl8169 sis900 via-rhine wb840 net_stack_driver $(GPL_ONLY)bcm440x $(GPL_ONLY)bcm570x ; diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index be7e23edc4..e2b8658381 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network wb840 ; SubInclude HAIKU_TOP src add-ons kernel drivers network via-rhine ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipro1000 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network etherpci ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm440x ; -SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm570x ; \ No newline at end of file +SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm570x ; diff --git a/src/add-ons/kernel/drivers/network/etherpci/Jamfile b/src/add-ons/kernel/drivers/network/etherpci/Jamfile new file mode 100644 index 0000000000..28b4e91402 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/etherpci/Jamfile @@ -0,0 +1,31 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network etherpci ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +# set some additional flags +SubDirCcFlags -Wall ; + +# For ether_driver.h +UsePrivateHeaders net ; + +KernelAddon etherpci : kernel drivers bin : + etherpci.c + ; + +Package haiku-networkingkit-cvs : + etherpci : + boot home config add-ons kernel drivers bin ; +PackageDriverSymLink haiku-networkingkit-cvs : net etherpci ; + + +# Installation + +HaikuInstall install-networking : /boot/home/config/add-ons/kernel/drivers/bin : + etherpci +; + +HaikuInstallRelSymLink install-networking : /boot/home/config/add-ons/kernel/drivers/dev/net : + etherpci : + installed-symlink +; + diff --git a/src/add-ons/kernel/drivers/network/etherpci/etherpci.c b/src/add-ons/kernel/drivers/network/etherpci/etherpci.c new file mode 100644 index 0000000000..2317315f28 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/etherpci/etherpci.c @@ -0,0 +1,1783 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * etherpci.c + * Copyright (c) 1998 Be, Inc. All Rights Reserved + * + * Ethernet driver: handles PCI NE2000 cards + * + * Modification History (most recent first): + * + * 18 May 98 malyn new today + */ +#include +#include +#include +#include "etherpci_private.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define kDevName "etherpci" +#define kDevDir "net/" kDevName "/" +#define DEVNAME_LENGTH 64 +#define MAX_CARDS 4 /* maximum number of driver instances */ + + +/* debug flags */ +#define ERR 0x0001 +#define INFO 0x0002 +#define RX 0x0004 /* dump received frames */ +#define TX 0x0008 /* dump transmitted frames */ +#define INTERRUPT 0x0010 /* interrupt calls */ +#define FUNCTION 0x0020 /* function calls */ +#define PCI_IO 0x0040 /* pci reads and writes */ +#define SEQ 0x0080 /* trasnmit & receive TCP/IP sequence sequence numbers */ +#define WARN 0x0100 /* Warnings - off on final release */ + +/* diagnostic debug flags - compile in here or set while running with debugger "AcmeRoadRunner" command */ +#define DEFAULT_DEBUG_FLAGS ( ERR | INFO | WARN | FUNCTION ) + +//void ETHER_DEBUG(int32 debug_mask, int32 enabled, char * format, ...); + +static void ETHER_DEBUG(int32 debug_mask, int32 enabled, char * format, ...) +{ + if (debug_mask & enabled) { + va_list args; + char s[4096]; + va_start(args, format); + vsprintf( s, format, args ); + va_end(args); + dprintf("%s",s); + } +} + + +static pci_module_info *gPCIModInfo; +static char *gDevNameList[MAX_CARDS+1]; +static pci_info *gDevList[MAX_CARDS+1]; +static int32 gOpenMask = 0; + + +const char** publish_devices(void ) { + dprintf(kDevName ": publish_devices()\n" ); + return (const char **)gDevNameList; +} + + +/* Driver Entry Points */ +__declspec(dllexport) status_t init_hardware(void ); +__declspec(dllexport) status_t init_driver(void ); +__declspec(dllexport) void uninit_driver(void ); +__declspec(dllexport) const char** publish_devices(void ); +__declspec(dllexport) device_hooks *find_device(const char *name ); + + + +/* + * Define STAY_ON_PAGE_0 if you want the driver to stay on page 0 all the + * time. This is faster than if it has to switch to page 1 to get the + * current register to see if there are any more packets. Reason: the + * interrupt handler does not have to do any locking if it knows it is + * always on page 0. I'm not sure why the speed difference is so dramatic, + * so perhaps there is actually a bug in page-changing version. + * + * STAY_ON_PAGE_0 uses a rather dubious technique to avoid changing the + * register page and it may not be 100% reliable. The technique used is to + * make sure all ring headers are zeroed out before packets are received + * into them. Then, if you detect a non-zero ring header, you can be pretty + * sure that it is another packet. + * + * We never read the "might-be-a-packet" immediately. Instead, we just + * release a semaphore so that the next read will occur later on enough + * so that the ring header information should be completely filled in. + * + */ +#define STAY_ON_PAGE_0 0 + + +/* + * We only care about these interrupts in our driver + */ +#define INTS_WE_CARE_ABOUT (ISR_RECEIVE | ISR_RECEIVE_ERROR |\ + ISR_TRANSMIT | ISR_TRANSMIT_ERROR | ISR_COUNTER) + + +typedef struct etherpci_private { + int32 devID; /* device identifier: 0-n */ + pci_info *pciInfo; + uint16 irq; /* IRQ line */ + uint32 reg_base; /* hardware register base address */ + area_id ioarea; /* Area used for MMIO of hostRegs */ + int boundary; /* boundary register value (mirrored) */ + ether_address_t myaddr; /* my ethernet address */ + unsigned nmulti; /* number of multicast addresses */ + ether_address_t multi[MAX_MULTI]; /* multicast addresses */ + sem_id iolock; /* ethercard io, except interrupt handler */ + int nonblocking; /* non-blocking mode */ +#if !STAY_ON_PAGE_0 + spinlock intrlock; /* ethercard io, including interrupt handler */ +#endif /* !STAY_ON_PAGE_0 */ + volatile int interrupted; /* interrupted system call */ + sem_id inrw; /* in read or write function */ + sem_id ilock; /* waiting for input */ + sem_id olock; /* waiting to output */ + + /* + * Various statistics + */ + volatile int ints; /* total number of interrupts */ + volatile int rints; /* read interrupts */ + volatile int wints; /* write interrupts */ + volatile int reads; /* reads */ + volatile int writes; /* writes */ + volatile int resets; /* resets */ + volatile int rerrs; /* read errors */ + volatile int werrs; /* write errors */ + volatile int interrs;/* unknown interrupts */ + volatile int frame_errs; /* frame alignment errors */ + volatile int crc_errs; /* crc errors */ + volatile int frames_lost;/* frames lost due to buffer problems */ + + /* + * Most recent values of the error statistics to detect any changes in them + */ + int rerrs_last; + int werrs_last; + int interrs_last; + int frame_errs_last; + int crc_errs_last; + int frames_lost_last; + + + /* stats from the hardware */ + int chip_rx_frame_errors; + int chip_rx_crc_errors; + int chip_rx_missed_errors; + /* + * These values are set once and never looked at again. They are + * almost as good as constants, but they differ for the various + * cards, so we can't set them now. + */ + int ETHER_BUF_START; + int ETHER_BUF_SIZE; + int EC_VMEM_PAGE; + int EC_VMEM_NPAGES; + int EC_RXBUF_START_PAGE; + int EC_RXBUF_END_PAGE; + int EC_RINGSTART; + int EC_RINGSIZE; + + uint32 debug; + +} etherpci_private_t; + + + +#if __INTEL__ +#define ether_outb(device, offset, value) (*gPCIModInfo->write_io_8)((device->reg_base + (offset)), (value)) +#define ether_outw(device, offset, value) (*gPCIModInfo->write_io_16)((device->reg_base + (offset)), (value)) +#define ether_inb(device, offset) ((*gPCIModInfo->read_io_8)(device->reg_base + (offset))) +#define ether_inw(device, offset) ((*gPCIModInfo->read_io_16)(device->reg_base + (offset))) + +#else /* PPC */ + +#define ether_inb(device, offset) (*((volatile uint8*)(device->reg_base + (offset)))); __eieio() +#define ether_inw(device, offset) (*((volatile uint16*)(device->reg_base + (offset)))); __eieio() +#define ether_outb(device, offset, value) (*((volatile uint8 *)(device->reg_base + (offset))) = (value)); __eieio() +#define ether_outw(device, offset, value) (*((volatile uint16*)(device->reg_base + (offset))) = (value)); __eieio() + +#endif + +#if 0 +#if __INTEL__ + +uint8 ether_inb(etherpci_private_t *device, uint32 offset) { + uint8 result; + result = ((*gPCIModInfo->read_io_8)(device->reg_base + (offset))); + ETHER_DEBUG(PCI_IO, device->debug, " inb(%x) %x \n", offset, result); + return result; +}; +uint16 ether_inw(etherpci_private_t *device, uint32 offset) { + uint16 result; + result = ((*gPCIModInfo->read_io_16)(device->reg_base + (offset))); + ETHER_DEBUG(PCI_IO, device->debug, " inw(%x) %x \n", offset, result); + return result; +}; +void ether_outb(etherpci_private_t *device, uint32 offset, uint8 value) { + (*gPCIModInfo->write_io_8)((device->reg_base + (offset)), (value)); + ETHER_DEBUG(PCI_IO, device->debug, " outb(%x) %x \n", offset, value); +}; +void ether_outw(etherpci_private_t *device, uint32 offset, uint16 value) { + (*gPCIModInfo->write_io_16)((device->reg_base + (offset)), (value)); + ETHER_DEBUG(PCI_IO, device->debug, " outb(%x) %x \n", offset, value); +}; + + +#else /* PPC */ + +uint8 ether_inb(etherpci_private_t *device, uint32 offset) { + uint8 result; + result = (*((volatile uint8*) (device->reg_base + (offset)))); __eieio(); + ETHER_DEBUG(PCI_IO, device->debug, " inb(%x) %x \n", offset, result); + return result; +}; + +uint16 ether_inw(etherpci_private_t *device, uint32 offset) { + uint16 result; + result = (*((volatile uint16*) (device->reg_base + (offset)))); __eieio(); + ETHER_DEBUG(PCI_IO, device->debug, " inw(%x) %x \n", offset, result); + return result; +}; + +void ether_outb(etherpci_private_t *device, uint32 offset, uint8 value) { + (*((volatile uint8 *)(device->reg_base + (offset))) = (value)); __eieio(); + ETHER_DEBUG(PCI_IO, device->debug, " outb(%x) %x \n", offset, value); +}; +void ether_outw(etherpci_private_t *device, uint32 offset, uint16 value) { + (*((volatile uint16 *)(device->reg_base + (offset))) = (value)); __eieio(); + ETHER_DEBUG(PCI_IO, device->debug, " outb(%x) %x \n", offset, value); +}; + +#endif +#endif + + + + + +/* for serial debug command*/ +#define DEBUGGER_COMMAND true +#if DEBUGGER_COMMAND +etherpci_private_t * gdev; +static int etherpci(int argc, char **argv); /* serial debug command */ +#endif + +/* + * io_lock gets you exclusive access to the card, except that + * the interrupt handler can still run. + * There is probably no need to io_lock() a 3com card, so look into + * removing it for that case. + */ +#define io_lock(data) acquire_sem(data->iolock) +#define io_unlock(data) release_sem_etc(data->iolock, 1, B_DO_NOT_RESCHEDULE) + +/* + * output_wait wakes up when the card is ready to transmit another packet + */ +#define output_wait(data, t) acquire_sem_etc(data->olock, 1, B_TIMEOUT, t) +#define output_unwait(data, c) release_sem_etc(data->olock, c, B_DO_NOT_RESCHEDULE) + +/* + * input_wait wakes up when the card has at least one packet on it + */ +#define input_wait(data) acquire_sem_etc(data->ilock ,1, B_CAN_INTERRUPT, 0) +#define input_unwait(data, c) release_sem_etc(data->ilock, c, B_DO_NOT_RESCHEDULE) + + +/* prototypes */ +static status_t open_hook(const char *name, uint32 flags, void **cookie); +static status_t close_hook(void *); +static status_t free_hook(void *); +static status_t control_hook(void * cookie,uint32 msg,void *buf,size_t len); +static status_t read_hook(void *data, off_t pos, void *buf, size_t *len); +static status_t write_hook(void *data, off_t pos, const void *buf, size_t *len); +static int32 etherpci_interrupt(void *data); /* interrupt handler */ +static int32 get_pci_list(pci_info *info[], int32 maxEntries); /* Get pci_info for each device */ +static status_t free_pci_list(pci_info *info[]); /* Free storage used by pci_info list */ +static void dump_packet(const char * msg, unsigned char * buf, uint16 size); /* diagnostic packet trace */ +static status_t enable_addressing(etherpci_private_t *data); /* enable pci io address space for device */ +static int domulti(etherpci_private_t *data,char *addr); + +static device_hooks gDeviceHooks = { + open_hook, /* -> open entry point */ + close_hook, /* -> close entry point */ + free_hook, /* -> free entry point */ + control_hook, /* -> control entry point */ + read_hook, /* -> read entry point */ + write_hook, /* -> write entry point */ + NULL, /* -> select entry point */ + NULL, /* -> deselect entry point */ + NULL, /* -> readv */ + NULL /* -> writev */ +}; + +/* Driver Entry Points */ +status_t init_hardware(void ) +{ + return B_NO_ERROR; +} + + +status_t init_driver() +{ + status_t status; + int32 entries; + char devName[64]; + int32 i; + + dprintf(kDevName ": init_driver "); + + if((status = get_module( B_PCI_MODULE_NAME, (module_info **)&gPCIModInfo )) != B_OK) { + dprintf(kDevName " Get module failed! %s\n", strerror(status )); + return status; + } + + /* Find Lan cards*/ + if ((entries = get_pci_list(gDevList, MAX_CARDS )) == 0) { + dprintf("init_driver: " kDevName " not found\n"); + free_pci_list(gDevList); + put_module(B_PCI_MODULE_NAME ); + return B_ERROR; + } + + /* Create device name list*/ + for (i=0; iget_nth_pci_info(i, item)) != B_OK) + break; + if ( ((item->vendor_id == 0x10ec) && (item->device_id == 0x8029)) // RealTek 8029 + || ((item->vendor_id == 0x1106) && (item->device_id == 0x0926)) // VIA + || ((item->vendor_id == 0x4a14) && (item->device_id == 0x5000)) // NetVin 5000 + || ((item->vendor_id == 0x1050) && (item->device_id == 0x0940)) // ProLAN + || ((item->vendor_id == 0x11f6) && (item->device_id == 0x1401)) // Compex + || ((item->vendor_id == 0x8e2e) && (item->device_id == 0x3000)) // KTI + ) { /* check if the device really has an IRQ */ + if ((item->u.h0.interrupt_line == 0) || (item->u.h0.interrupt_line == 0xFF)) { + dprintf(kDevName " found with invalid IRQ - check IRQ assignement"); + continue; + } + dprintf(kDevName " found at IRQ %x ", item->u.h0.interrupt_line); + info[entries++] = item; + item = (pci_info *)malloc(sizeof(pci_info)); + } + } + info[entries] = NULL; + free(item); + return entries; +} + +static status_t free_pci_list(pci_info *info[]) +{ + int32 i; + pci_info *item; + + for (i=0; (item=info[i]); i++) + free(item); + return B_OK; +} + +/* + * How many waiting for io? + */ +static long +io_count(etherpci_private_t *data) +{ + long count; + + get_sem_count(data->iolock, &count); + return (count); +} + +/* + * How many waiting for output? + */ +static long +output_count(etherpci_private_t *data) +{ + long count; + + get_sem_count(data->olock, &count); + return (count); +} + +/* + * How many waiting for input? + */ +static long +input_count(etherpci_private_t *data) +{ + long count; + + get_sem_count(data->ilock, &count); + return (count); +} + +#if STAY_ON_PAGE_0 + +#define INTR_LOCK(data, expression) (expression) + +#else /* STAY_ON_PAGE_0 */ + +/* + * Spinlock for negotiating access to card with interrupt handler + */ +#define intr_lock(data) acquire_spinlock(&data->intrlock) +#define intr_unlock(data) release_spinlock(&data->intrlock) + +/* + * The interrupt handler must lock all calls to the card + * This macro is useful for that purpose. + */ +#define INTR_LOCK(data, expression) (intr_lock(data), (expression), intr_unlock(data)) + +#endif /* STAY_ON_PAGE_0 */ + + + + +/* + * Calculate various constants + * These must be done at runtime, since 3com and ne2000 cards have different + * values. + */ +static void +calc_constants(etherpci_private_t *data) +{ + data->EC_VMEM_PAGE = (data->ETHER_BUF_START >> EC_PAGE_SHIFT); + data->EC_VMEM_NPAGES = (data->ETHER_BUF_SIZE >> EC_PAGE_SHIFT); + + data->EC_RXBUF_START_PAGE = (data->EC_VMEM_PAGE + 6); + data->EC_RXBUF_END_PAGE = (data->EC_VMEM_PAGE + data->EC_VMEM_NPAGES); + + data->EC_RINGSTART = (data->EC_RXBUF_START_PAGE << EC_PAGE_SHIFT); + data->EC_RINGSIZE = ((data->EC_VMEM_NPAGES - 6) << EC_PAGE_SHIFT); +} + +/* + * Print an ethernet address + */ +static void +print_address( + ether_address_t *addr + ) +{ + int i; + char buf[3 * 6 + 1]; + + for (i = 0; i < 5; i++) { + sprintf(&buf[3*i], "%02x:", addr->ebyte[i]); + } + sprintf(&buf[3*5], "%02x", addr->ebyte[5]); + dprintf("%s\n", buf); +} + + + +/* + * Get the isr register + */ +static unsigned char +getisr(etherpci_private_t *data) +{ + unsigned char isr; + + isr = ether_inb(data, EN0_ISR); + return (isr); +} + +/* + * Set the isr register + */ +static void +setisr(etherpci_private_t *data, unsigned char isr) +{ + ether_outb(data, EN0_ISR, isr); +} + +/* + * Wait for the DMA to complete + */ +static int +wait_for_dma_complete( + etherpci_private_t *data, + unsigned short addr, + unsigned short size + ) +{ + unsigned short hi, low; + unsigned short where; + int bogus; + +#define MAXBOGUS 20 + + /* + * This is the expected way to wait for DMA completion, which + * is in fact incorrect. I think ISR_DMADONE gets set too early. + */ + bogus = 0; + while (!(getisr(data) & ISR_DMADONE) && ++bogus < MAXBOGUS) { + /* keep waiting */ + } + if (bogus >= MAXBOGUS) { + dprintf("Bogus alert: waiting for ISR\n"); + } + + /* + * This is the workaround + */ + bogus = 0; + do { + hi = ether_inb(data, EN0_RADDRHI); + low = ether_inb(data, EN0_RADDRLO); + where = (hi << 8) | low; + } while (where < addr + size && ++bogus < MAXBOGUS); + if (bogus >= MAXBOGUS * 2) { + /* + * On some cards, the counters will never clear. + * So only print this message when debugging. + */ + dprintf("Bogus alert: waiting for counters to zero\n"); + return -1; + } + + setisr(data, ISR_DMADONE); + ether_outb(data, EN_CCMD, ENC_NODMA); + return 0; +} + +/* + * Get data from the ne2000 card + */ +static void +etherpci_min( + etherpci_private_t *data, + unsigned char *dst, + unsigned src, + unsigned len + ) +{ + int i; + + + if (len & 1) { + len++; + } + ether_outb(data, EN0_RCNTLO, len & 0xff); + ether_outb(data, EN0_RCNTHI, len >> 8); + ether_outb(data, EN0_RADDRLO, src & 0xff); + ether_outb(data, EN0_RADDRHI, src >> 8); + ether_outb(data, EN_CCMD, ENC_DMAREAD); + for (i = 0; i < len; i += 2) { + unsigned short word; + + word = ether_inw(data, NE_DATA); +#if __INTEL__ + dst[i + 1] = word >> 8; + dst[i + 0] = word & 0xff; +#else + dst[i] = word >> 8; + dst[i + 1] = word & 0xff; +#endif + } + wait_for_dma_complete(data, src, len); +} + +/* + * Put data on the ne2000 card + */ +static void +etherpci_mout( + etherpci_private_t *data, + unsigned dst, + const unsigned char *src, + unsigned len + ) +{ + int i; + int tries = 1; + + + // This loop is for a bug that showed up with the old ISA 3com cards + // that were in the original BeBox. Sometimes the dma status would just + // stop on some part of the buffer, never finishing. + // If we notice this error, we redo the dma transfer. +again: +#define MAXTRIES 2 + if (tries > MAXTRIES) { + dprintf("ether_mout : tried %d times, stopping\n", tries); + return; + } + + if (len & 1) { + len++; + } + ether_outb(data, EN0_RCNTHI, len >> 8); + ether_outb(data, EN0_RADDRLO, dst & 0xff); + ether_outb(data, EN0_RADDRHI, dst >> 8); + + /* + * The 8390 hardware has documented bugs in it related to DMA write. + * So, we follow the documentation on how to work around them. + */ + + /* + * Step 1: You must put a non-zero value in this register. We use 2. + */ + if ((len & 0xff) == 0) { + ether_outb(data, EN0_RCNTLO, 2); + } else { + ether_outb(data, EN0_RCNTLO, len & 0xff); + } + +#if you_want_to_follow_step2_even_though_it_hangs + ether_outb(data, EN_CCMD, ENC_DMAREAD); /* Step 2 */ +#endif + + ether_outb(data, EN_CCMD, ENC_DMAWRITE); + + for (i = 0; i < len; i += 2) { + unsigned short word; + +#if __INTEL__ + word = (src[i + 1] << 8) | src[i + 0]; +#else + word = (src[i] << 8) | src[i + 1]; +#endif + ether_outw(data, NE_DATA, word); + } + if ((len & 0xff) == 0) { + /* + * Write out the two extra bytes + */ + ether_outw(data, NE_DATA, 0); + len += 2; + } + if (wait_for_dma_complete(data, dst, len) != 0) { + tries++; + goto again; + } + //if (tries != 1) { dprintf("wait_for_dma worked after %d tries\n", tries); } +} + + +#if STAY_ON_PAGE_0 +/* + * Zero out the headers in the ring buffer + */ +static void +ringzero( + etherpci_private_t *data, + unsigned boundary, + unsigned next_boundary + ) +{ + ring_header ring; + int i; + int pages; + unsigned offset; + + ring.count = 0; + ring.next_packet = 0; + ring.status = 0; + + if (data->boundary < next_boundary) { + pages = next_boundary - data->boundary; + } else { + pages = (data->EC_RINGSIZE >> EC_PAGE_SHIFT) - (data->boundary - next_boundary); + } + + for (i = 0; i < pages; i++) { + offset = data->boundary << EC_PAGE_SHIFT; + etherpci_mout(data, offset, (unsigned char *)&ring, sizeof(ring)); + data->boundary++; + if (data->boundary >= data->EC_RXBUF_END_PAGE) { + data->boundary = data->EC_RXBUF_START_PAGE; + } + } +} +#endif /* STAY_ON_PAGE_0 */ + + +/* + * Determine if we have an ne2000 PCI card + */ +static int probe(etherpci_private_t *data) +{ + int i; + int reg; + unsigned char test[EC_PAGE_SIZE]; + short waddr[ETHER_ADDR_LEN]; + uint8 reg_val; + + data->ETHER_BUF_START = ETHER_BUF_START_NE2000; + data->ETHER_BUF_SIZE = ETHER_BUF_SIZE_NE2000; + calc_constants(data); + + reg = ether_inb(data, NE_RESET); + snooze(2000); + ether_outb(data, NE_RESET, reg); + snooze(2000); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_STOP | ENC_PAGE0); + i = 10000; + while ( i-- > 0) { + reg_val = ether_inb(data, EN0_ISR); + if (reg_val & ISR_RESET) break; + }; + if (i < 0) { + dprintf("reset failed -- ignoring\n"); + } + ether_outb(data, EN0_ISR, 0xff); + ether_outb(data, EN0_DCFG, DCFG_BM16); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_STOP | ENC_PAGE0); + snooze(2000); + reg = ether_inb(data, EN_CCMD); + if (reg != (ENC_NODMA|ENC_STOP|ENC_PAGE0)) { + dprintf("command register failed: %02x != %02x\n", reg, ENC_NODMA|ENC_STOP); + return (0); + } + ether_outb(data, EN0_TXCR, 0); + ether_outb(data, EN0_RXCR, ENRXCR_MON); + ether_outb(data, EN0_STARTPG, data->EC_RXBUF_START_PAGE); + ether_outb(data, EN0_STOPPG, data->EC_RXBUF_END_PAGE); + ether_outb(data, EN0_BOUNDARY, data->EC_RXBUF_END_PAGE); + ether_outb(data, EN0_IMR, 0); + ether_outb(data, EN0_ISR, 0); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_PAGE1 | ENC_STOP); + ether_outb(data, EN1_CURPAG, data->EC_RXBUF_START_PAGE); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_PAGE0 | ENC_STOP); + + /* stop chip */ + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_PAGE0); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_STOP); + + memset(&waddr[0], 0, sizeof(waddr)); + etherpci_min(data, (unsigned char *)&waddr[0], 0, sizeof(waddr)); + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + data->myaddr.ebyte[i] = ((unsigned char *)&waddr[0])[2*i]; + } + + /* test memory */ + for (i = 0; i < sizeof(test); i++) { + test[i] = i; + } + etherpci_mout(data, data->ETHER_BUF_START, (unsigned char *)&test[0], sizeof(test)); + memset(&test, 0, sizeof(test)); + etherpci_min(data, (unsigned char *)&test[0], data->ETHER_BUF_START, sizeof(test)); + for (i = 0; i < sizeof(test); i++) { + if (test[i] != i) { + dprintf("memory test failed: %02x %02x\n", i, test[i]); + return (0); + } + } + + dprintf("ne2000 pci ethernet card found - "); + print_address(&data->myaddr); + return (1); +} + + +/* + * Initialize the ethernet card + */ +static void +init( + etherpci_private_t *data + ) +{ + int i; + + +#if STAY_ON_PAGE_0 + /* + * Set all the ring headers to zero + */ + ringzero(data, data->EC_RXBUF_START_PAGE, data->EC_RXBUF_END_PAGE); +#endif /* STAY_ON_PAGE_0 */ + + + /* initialize data configuration register */ + ether_outb(data, EN0_DCFG, DCFG_BM16); + + /* clear remote byte count registers */ + ether_outb(data, EN0_RCNTLO, 0x0); + ether_outb(data, EN0_RCNTHI, 0x0); + + /* initialize receive configuration register */ + ether_outb(data, EN0_RXCR, ENRXCR_BCST); + + /* get into loopback mode */ + ether_outb(data, EN0_TXCR, TXCR_LOOPBACK); + + /* set boundary, page start and page stop */ + ether_outb(data, EN0_BOUNDARY, data->EC_RXBUF_END_PAGE); + data->boundary = data->EC_RXBUF_START_PAGE; + ether_outb(data, EN0_STARTPG, data->EC_RXBUF_START_PAGE); + ether_outb(data, EN0_STOPPG, data->EC_RXBUF_END_PAGE); + + /* set transmit page start register */ + ether_outb(data, EN0_TPSR, data->EC_VMEM_PAGE); + + /* clear interrupt status register */ + ether_outb(data, EN0_ISR, 0xff); + + /* initialize interrupt mask register */ + ether_outb(data, EN0_IMR, INTS_WE_CARE_ABOUT); + + /* set page 1 */ + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_PAGE1); + + /* set physical address */ + for (i = 0; i < 6; i++) { + ether_outb(data, EN1_PHYS + i, data->myaddr.ebyte[i]); + } + + /* set multicast address */ + for (i = 0; i < 8; i++) { + ether_outb(data, EN1_MULT+i, 0xff); + } + data->nmulti = 0; + + /* set current pointer */ + ether_outb(data, EN1_CURPAG, data->EC_RXBUF_START_PAGE); + + + /* start chip */ + ether_outb(data, EN_CCMD, ENC_START | ENC_PAGE0 | ENC_NODMA); + + /* initialize transmit configuration register */ + ether_outb(data, EN0_TXCR, 0x00); +} + + + +/* + * Copy data from the card's ring buffer + */ +static void +ringcopy( + etherpci_private_t *data, + unsigned char *ether_buf, + int offset, + int len + ) +{ + int roffset; + int rem; + + roffset = offset - data->EC_RINGSTART; + rem = data->EC_RINGSIZE - roffset; + if (len > rem) { + etherpci_min(data, ðer_buf[0], offset, rem); + etherpci_min(data, ðer_buf[rem], data->EC_RINGSTART, len - rem); + } else { + etherpci_min(data, ðer_buf[0], offset, len); + } +} + + +/* + * Set the boundary register, both on the card and internally + * NOTE: you cannot make the boundary = current register on the card, + * so we actually set it one behind. + */ +static void +setboundary(etherpci_private_t *data, unsigned char nextboundary) +{ + if (nextboundary != data->EC_RXBUF_START_PAGE) { + ether_outb(data, EN0_BOUNDARY, nextboundary - 1); + } else { + /* since it's a ring buffer */ + ether_outb(data, EN0_BOUNDARY, data->EC_RXBUF_END_PAGE - 1); + } + data->boundary = nextboundary; +} + +/* + * Start resetting the chip, because of ring overflow + */ +static int +reset(etherpci_private_t *data) +{ + unsigned char cmd; + int resend = false; + + cmd = ether_inb(data, EN_CCMD); + ether_outb(data, EN_CCMD, ENC_STOP | ENC_NODMA); + snooze(10 * 1000); + ether_outb(data, EN0_RCNTLO, 0x0); + ether_outb(data, EN0_RCNTHI, 0x0); + if (cmd & ENC_TRANS) { + if(!(getisr(data) & (ISR_TRANSMIT | ISR_TRANSMIT_ERROR))) + resend = true; // xmit command issued but ISR shows its not completed + } + /* get into loopback mode */ + ether_outb(data, EN0_TXCR, TXCR_LOOPBACK); + ether_outb(data, EN_CCMD, ENC_START | ENC_PAGE0 | ENC_NODMA); + return (resend); +} + +/* + * finish the reset + */ +static void +finish_reset( + etherpci_private_t *data, + int resend + ) +{ + setisr(data, ISR_OVERWRITE); + ether_outb(data, EN0_TXCR, 0x00); + + if (resend) { +// dprintf("Xmit CMD resent\n"); + ether_outb(data, EN_CCMD, ENC_START | ENC_PAGE0 | ENC_NODMA | ENC_TRANS); + } +} + + +/* + * Handle ethernet interrupts + */ +static int32 +etherpci_interrupt(void *_data) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + unsigned char isr; + int wakeup_reader = 0; + int wakeup_writer = 0; + int32 handled = B_UNHANDLED_INTERRUPT; + data->ints++; + + ETHER_DEBUG(INTERRUPT, data->debug, "ENTR isr=%x & %x?\n",getisr(data), INTS_WE_CARE_ABOUT); + for (INTR_LOCK(data, isr = getisr(data)); + isr & INTS_WE_CARE_ABOUT; + INTR_LOCK(data, isr = getisr(data))) { + + + if (isr & ISR_RECEIVE) { + data->rints++; + wakeup_reader++; + INTR_LOCK(data, setisr(data, ISR_RECEIVE)); + handled = B_HANDLED_INTERRUPT; + continue; + } + if (isr & ISR_TRANSMIT_ERROR) { + data->werrs++; + INTR_LOCK(data, setisr(data, ISR_TRANSMIT_ERROR)); + wakeup_writer++; + handled = B_HANDLED_INTERRUPT; + continue; + } + if (isr & ISR_TRANSMIT) { + data->wints++; + INTR_LOCK(data, setisr(data, ISR_TRANSMIT)); + wakeup_writer++; + handled = B_HANDLED_INTERRUPT; + continue; + } + if (isr & ISR_RECEIVE_ERROR) { + uint32 err_count; + err_count = ether_inb(data, EN0_CNTR0); data->frame_errs += err_count; + err_count = ether_inb(data, EN0_CNTR1); data->crc_errs += err_count; + err_count = ether_inb(data, EN0_CNTR2); data->frames_lost += err_count; + + isr &= ~ISR_RECEIVE_ERROR; + INTR_LOCK(data, setisr(data, ISR_RECEIVE_ERROR)); + handled = B_HANDLED_INTERRUPT; + } + if (isr & ISR_DMADONE) { + isr &= ~ISR_DMADONE; /* handled elsewhere */ + handled = B_HANDLED_INTERRUPT; + } + if (isr & ISR_OVERWRITE) { + isr &= ~ISR_OVERWRITE; /* handled elsewhere */ + handled = B_HANDLED_INTERRUPT; + } + + if (isr & ISR_COUNTER) { + isr &= ~ISR_COUNTER; /* handled here */ +// dprintf("Clearing Stats Cntr\n"); + INTR_LOCK(data, setisr(data, ISR_COUNTER)); + handled = B_HANDLED_INTERRUPT; + } + + if (isr) { + /* + * If any other interrupts, just clear them (hmmm....) + * ??? This doesn't seem right - HB + */ +// ddprintf("ISR=%x rdr=%x wtr=%x io=%x inrw=%x nonblk=%x\n", +// isr,input_count(data), output_count(data),io_count(data), +// data->inrw,data->nonblocking); + INTR_LOCK(data, setisr(data, isr)); + data->interrs++; + } + } + if (wakeup_reader) { + input_unwait(data, 1); + } + if (wakeup_writer) { + output_unwait(data, 1); + } + + return handled; +} + +/* + * Check to see if there are any new errors + */ +static void +check_errors(etherpci_private_t *data) +{ +#define DOIT(stat, message) \ + if (stat > stat##_last) { \ + stat##_last = stat; \ + dprintf(message, stat##_last); \ + } + + DOIT(data->rerrs, "Receive errors now %d\n"); + DOIT(data->werrs, "Transmit errors now %d\n"); + DOIT(data->interrs, "Interrupt errors now %d\n"); + DOIT(data->frames_lost, "Frames lost now %d\n"); +#undef DOIT +#if 0 + /* + * these are normal errors because collisions are normal + * so don't make a big deal about them. + */ + DOIT(data->frame_errs, "Frame alignment errors now %d\n"); + DOIT(data->crc_errs, "CRC errors now %d\n"); +#endif +} + +/* + * Find out if there are any more packets on the card + */ +static int +more_packets( + etherpci_private_t *data, + int didreset + ) +{ +#if STAY_ON_PAGE_0 + + unsigned offset; + ring_header ring; + + offset = data->boundary << EC_PAGE_SHIFT; + etherpci_min(data, (unsigned char *)&ring, offset, sizeof(ring)); + return (ring.status && ring.next_packet && ring.count); + +#else /* STAY_ON_PAGE_0 */ + + cpu_status ps; + unsigned char cur; + + /* + * First, get the current registe + */ + ps = disable_interrupts(); + intr_lock(data); + ether_outb(data, EN_CCMD, ENC_PAGE1); + cur = ether_inb(data, EN1_CURPAG); + ether_outb(data, EN_CCMD, ENC_PAGE0); + intr_unlock(data); + restore_interrupts(ps); + + /* + * Then return the result + * Must use didreset since cur == boundary in + * an overflow situation. + */ + return (didreset || cur != data->boundary); + +#endif /* STAY_ON_PAGE_0 */ +} + +/* + * Copy a packet from the ethernet card + */ +static int +copy_packet( + etherpci_private_t *data, + unsigned char *ether_buf, + int buflen + ) +{ + ring_header ring; + unsigned offset; + int len; + int rlen; + int ether_len = 0; + int didreset = 0; + int resend = 0; + + io_lock(data); + check_errors(data); + + + /* + * Check for overwrite error first + */ + if (getisr(data) & ISR_OVERWRITE) { +// dprintf("starting ether reset!\n"); + data->resets++; + resend = reset(data); + didreset++; + } + + if (more_packets(data, didreset)) do { + /* + * Read packet ring header + */ + offset = data->boundary << EC_PAGE_SHIFT; + etherpci_min(data, (unsigned char *)&ring, offset, sizeof(ring)); + + len = swapshort(ring.count); + + if (!(ring.status & RSR_INTACT)) { + dprintf("packet not intact! (%02x,%u,%02x) (%d)\n", + ring.status, + ring.next_packet, + ring.count, + data->boundary); + /* discard bad packet */ + ether_len = 0; setboundary(data, ring.next_packet); + break; + } + if (ring.next_packet < data->EC_RXBUF_START_PAGE || + ring.next_packet >= data->EC_RXBUF_END_PAGE) { + dprintf("etherpci_read: bad next packet! (%02x,%u,%02x) (%d)\n", + ring.status, + ring.next_packet, + ring.count, + data->boundary); + data->rerrs++; + /* discard bad packet */ + ether_len = 0; setboundary(data, ring.next_packet); + break; + } + + len = swapshort(ring.count); + rlen = len - 4; + if (rlen < ETHER_MIN_SIZE || rlen > ETHER_MAX_SIZE) { + dprintf("etherpci_read: bad length! (%02x,%u,%02x) (%d)\n", + ring.status, + ring.next_packet, + ring.count, + data->boundary); + data->rerrs++; + /* discard bad packet */ + ether_len = 0; setboundary(data, ring.next_packet); + break; + } + + + if (rlen > buflen) { + rlen = buflen; + } + ringcopy(data, ether_buf, offset + 4, rlen); + +#if STAY_ON_PAGE_0 + ringzero(data, data->boundary, ring.next_packet); +#endif /* STAY_ON_PAGE_0 */ + + ether_len = rlen; + setboundary(data, ring.next_packet); + data->reads++; + + + } while (0); + + if (didreset) { + dprintf("finishing reset!\n"); + finish_reset(data, resend); + } + + if (input_count(data) <= 0 && more_packets(data, didreset)) { + /* + * Looks like there is another packet + * So, make sure they get woken up + */ + input_unwait(data, 1); + } + + io_unlock(data); + return (ether_len); +} + +static int +my_packet( + etherpci_private_t *data, + char *addr + ) +{ + int i; + const char broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (memcmp(addr, &data->myaddr, sizeof(data->myaddr)) == 0) { + return (1); + } + if (memcmp(addr, broadcast, sizeof(broadcast)) == 0) { + return (1); + } + for (i = 0; i < data->nmulti; i++) { + if (memcmp(addr, &data->multi[i], sizeof(data->multi[i])) == 0) { + return (1); + } + } + return (0); +} + +/* + * Implements the read() system call to the ethernet driver + */ +static status_t +read_hook( void *_data,off_t pos,void *buf, size_t *len) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + ulong buflen; + int packet_len; + + buflen = *len; + atomic_add(&data->inrw, 1); + if (data->interrupted) { + atomic_add(&data->inrw, -1); + return (B_INTERRUPTED); + } + do { + if (!data->nonblocking) { + input_wait(data); + } + if (data->interrupted) { + atomic_add(&data->inrw, -1); + return (B_INTERRUPTED); + } + packet_len = copy_packet(data, (unsigned char *)buf, buflen); + if ((packet_len) && (data->debug & RX)) { + dump_packet("RX:" ,buf, packet_len); + } + } while (!data->nonblocking && packet_len == 0 && !my_packet(data, buf)); + atomic_add(&data->inrw, -1); + *len = packet_len; + return 0; +} + + +/* + * Check the status of the last packet transmitted + */ +static void +check_transmit_status(etherpci_private_t *data) +{ + unsigned char status; + + status = ether_inb(data, EN0_TPSR); + if (status & (TSR_ABORTED | TSR_UNDERRUN)) { + dprintf("transmit error: %02x\n", status); + } +#if 0 + if (data->wints + data->werrs != data->writes) { + dprintf("Write interrupts %d, errors %d, transmits %d\n", + data->wints, data->werrs, data->writes); + } +#endif +} + + +/* + * implements the write() system call to the ethernet + */ +static status_t +write_hook(void *_data,off_t pos,const void *buf,size_t *len) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + ulong buflen; + int status; + + + buflen = *len; + atomic_add(&data->inrw, 1); + if (data->interrupted) { + atomic_add(&data->inrw, -1); + return (B_INTERRUPTED); + } + /* + * Wait for somebody else (if any) to finish transmitting + */ + status = output_wait(data, ETHER_TRANSMIT_TIMEOUT); + if (status < B_NO_ERROR || data->interrupted) { + atomic_add(&data->inrw, -1); + return (status); + } + + io_lock(data); + check_errors(data); + + if (data->writes > 0) { + check_transmit_status(data); + } + etherpci_mout(data, data->ETHER_BUF_START, (const unsigned char *)buf, buflen); + if (buflen < ETHER_MIN_SIZE) { + /* + * Round up to ETHER_MIN_SIZE + */ + buflen = ETHER_MIN_SIZE; + } + ether_outb(data, EN0_TCNTLO, (char)(buflen & 0xff)); + ether_outb(data, EN0_TCNTHI, (char)(buflen >> 8)); + ether_outb(data, EN_CCMD, ENC_NODMA | ENC_TRANS); + data->writes++; + io_unlock(data); + atomic_add(&data->inrw, -1); + *len = buflen; + + if (data->debug & TX) { + dump_packet("TX:",(unsigned char *) buf, buflen); + } + return 0; +} + +static long +inrw(etherpci_private_t *data) +{ + return (data->inrw); +} + + +/* + * Allocate and initialize semaphores and spinlocks. + */ +static status_t allocate_resources(etherpci_private_t *data) { + + // create locks + data->iolock = create_sem(1, "ethercard io"); + set_sem_owner(data->iolock, B_SYSTEM_TEAM); + data->olock = create_sem(1, "ethercard output"); + set_sem_owner(data->olock, B_SYSTEM_TEAM); + data->ilock = create_sem(0, "ethercard input"); + set_sem_owner(data->ilock, B_SYSTEM_TEAM); + data->interrupted = 0; + data->inrw = 0; + data->nonblocking = 0; + + return (B_OK); +} + +static void free_resources(etherpci_private_t *data) { + delete_sem(data->iolock); + delete_sem(data->olock); + delete_sem(data->ilock); +} + +device_hooks *find_device(const char *name) +{ + int32 i; + char *item; + + /* Find device name */ + for (i=0; (item=gDevNameList[i]); i++) + { + if (strcmp(name, item) == 0) + { + return &gDeviceHooks; + } + } + return NULL; /*Device not found */ +} + + +static status_t +open_hook(const char *name, uint32 flags, void **cookie) +{ + int32 devID; + int32 mask; + status_t status; + char *devName; + etherpci_private_t *data; + + /* Find device name*/ + for (devID=0; (devName=gDevNameList[devID]); devID++) { + if (strcmp(name, devName) == 0) + break; + } + if (!devName) + return EINVAL; + + /* Check if the device is busy and set in-use flag if not */ + mask = 1 << devID; + if (atomic_or(&gOpenMask, mask) &mask) { + return B_BUSY; + } + + /* Allocate storage for the cookie*/ + if (!(*cookie = data = (etherpci_private_t *)malloc(sizeof(etherpci_private_t)))) { + status = B_NO_MEMORY; + goto err0; + } + memset(data, 0, sizeof(etherpci_private_t)); + + /* Setup the cookie */ + data->pciInfo = gDevList[devID]; + data->devID = devID; + + data->debug = DEFAULT_DEBUG_FLAGS; + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": open %s dev=%x\n", name, data); + +#if DEBUGGER_COMMAND + gdev = data; + add_debugger_command (kDevName, etherpci, "Ethernet driver Info"); +#endif + + /* enable access to the cards address space */ + if ((status = enable_addressing(data)) != B_OK) + goto err1; + + if (!probe(data)) { + dprintf(kDevName ": probe failed\n"); + goto err1; + } + + if (allocate_resources(data) != B_OK) { + goto err2; + } + + /* Setup interrupts */ + install_io_interrupt_handler( data->pciInfo->u.h0.interrupt_line, etherpci_interrupt, *cookie, 0 ); + + dprintf("Interrupts installed at %x\n", data->pciInfo->u.h0.interrupt_line); + /* Init Device */ + init(data); + + return B_NO_ERROR; + + + err2: +#if !__INTEL__ + delete_area(data->ioarea); +#endif + err1: +#if DEBUGGER_COMMAND + remove_debugger_command (kDevName, etherpci); +#endif + free(data); + + err0: + atomic_and(&gOpenMask, ~mask); + dprintf(kDevName ": open failed!\n"); + return B_ERROR; +} + + +static status_t +close_hook(void *_data) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": close dev=%x\n", data); + + + /* + * Force pending reads and writes to terminate + */ + io_lock(data); + data->interrupted = 1; + input_unwait(data, 1); + output_unwait(data, 1); + io_unlock(data); + while (inrw(data)) { + snooze(1000000); + dprintf("ether: still waiting for read/write to finish\n"); + } + + /* + * Stop the chip + */ + ether_outb(data, EN_CCMD, ENC_STOP); + snooze(2000); + + /* + * And clean up + */ + remove_io_interrupt_handler(data->pciInfo->u.h0.interrupt_line, etherpci_interrupt, data); + delete_sem(data->iolock); + delete_sem(data->ilock); + delete_sem(data->olock); + + + /* + * Reset all the statistics + */ + data->ints = 0; + data->rints = 0; + data->rerrs = 0; + data->wints = 0; + data->werrs = 0; + data->reads = 0; + data->writes = 0; + data->interrs = 0; + data->resets = 0; + data->frame_errs = 0; + data->crc_errs = 0; + data->frames_lost = 0; + + data->rerrs_last = 0; + data->werrs_last = 0; + data->interrs_last = 0; + data->frame_errs_last = 0; + data->crc_errs_last = 0; + data->frames_lost_last = 0; + + data->chip_rx_frame_errors = 0; + data->chip_rx_crc_errors = 0; + data->chip_rx_missed_errors = 0; + +#if DEBUGGER_COMMAND + remove_debugger_command (kDevName, etherpci); +#endif + + + return (B_NO_ERROR); +} + +static status_t +free_hook(void *_data) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": free %s dev=%x\n", data); + +#if !__INTEL__ + delete_area(data->ioarea); +#endif + free(data); + return 0; +} + +static status_t enable_addressing(etherpci_private_t *data) +{ + unsigned char cmd; + +#if __INTEL__ + data->reg_base = data->pciInfo->u.h0.base_registers[0]; +#else + uint32 base, size, offset; + base = data->pciInfo->u.h0.base_registers[0]; + size = data->pciInfo->u.h0.base_register_sizes[0]; + + /* Round down to nearest page boundary */ + base = base & ~(B_PAGE_SIZE-1); + + /* Adjust the size */ + offset = data->pciInfo->u.h0.base_registers[0] - base; + size += offset; + size = (size +(B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); + + dprintf(kDevName ": PCI base=%x size=%x offset=%x\n", base, size, offset); + + if ((data->ioarea = map_physical_memory(kDevName "_regs", (void *)base, size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, (void **)&data->reg_base)) < 0) + return B_ERROR; + + data->reg_base = data->reg_base + offset; + +#endif + dprintf(kDevName ": reg_base=%lx\n", data->reg_base); + + /* enable pci address access */ + cmd = (gPCIModInfo->read_pci_config)(data->pciInfo->bus, data->pciInfo->device, data->pciInfo->function, PCI_command, 2); + (gPCIModInfo->write_pci_config)(data->pciInfo->bus, data->pciInfo->device, data->pciInfo->function, PCI_command, 2, cmd | PCI_command_io); + + return B_OK; +} + + + + +/* + * Standard driver control function + */ +static status_t +control_hook(void *_data,uint32 msg,void *buf,size_t len) +{ + etherpci_private_t *data = (etherpci_private_t *) _data; + + switch (msg) { + case ETHER_INIT: + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": control: ETHER_INIT \n"); + return B_NO_ERROR; + + case ETHER_GETADDR: + if (data == NULL) { + return (B_ERROR); + } + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": control: GET_ADDR \n"); + memcpy(buf, &data->myaddr, sizeof(data->myaddr)); + return (B_NO_ERROR); + + case ETHER_NONBLOCK: + if (data == NULL) { + return (B_ERROR); + } + memcpy(&data->nonblocking, buf, sizeof(data->nonblocking)); + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": control: NON_BLOCK %x\n", data->nonblocking); + return (B_NO_ERROR); + + case ETHER_ADDMULTI: + ETHER_DEBUG(FUNCTION, data->debug, kDevName ": control: DO_MULTI %x\n"); + return (domulti(data, (char *)buf)); + } + return B_ERROR; +} + + + +static int +domulti(etherpci_private_t *data,char *addr) +{ + int i; + int nmulti = data->nmulti; + + if (nmulti == MAX_MULTI) { + return (B_ERROR); + } + for (i = 0; i < nmulti; i++) { + if (memcmp(&data->multi[i], addr, sizeof(data->multi[i])) == 0) { + break; + } + } + if (i == nmulti) { + /* + * Only copy if it isn't there already + */ + memcpy(&data->multi[i], addr, sizeof(data->multi[i])); + data->nmulti++; + } + if (data->nmulti == 1) { + dprintf("Enabling multicast\n"); + ether_outb(data, EN0_RXCR, ENRXCR_BCST | ENRXCR_MCST); + } + return (B_NO_ERROR); +} + +/* Serial Debugger command + Connect a terminal emulator to the serial port at 19.2 8-1-None + Press the keys ( alt-sysreq on Intel) or (Clover-leaf Power on Mac ) to enter the debugger + At the kdebug> prompt enter "etherpci arg...", + for example "etherpci R" to enable a received packet trace. +*/ +#if DEBUGGER_COMMAND +static int +etherpci(int argc, char **argv) { + uint16 i,j; + const char * usage = "usage: etherpci { Function_calls | PCI_IO | Stats | Rx_trace | Tx_trace }\n"; + + + if (argc < 2) { + kprintf("%s",usage); return 0; + } + + for (i= argc, j= 1; i > 1; i--, j++) { + switch (*argv[j]) { + case 'F': + case 'f': + gdev->debug ^= FUNCTION; + if (gdev->debug & FUNCTION) + kprintf("Function() call trace Enabled\n"); + else + kprintf("Function() call trace Disabled\n"); + break; + case 'N': + case 'n': + gdev->debug ^= SEQ; + if (gdev->debug & SEQ) + kprintf("Sequence numbers packet trace Enabled\n"); + else + kprintf("Sequence numbers packet trace Disabled\n"); + break; + case 'R': + case 'r': + gdev->debug ^= RX; + if (gdev->debug & RX) + kprintf("Receive packet trace Enabled\n"); + else + kprintf("Receive packet trace Disabled\n"); + break; + case 'T': + case 't': + gdev->debug ^= TX; + if (gdev->debug & TX) + kprintf("Transmit packet trace Enabled\n"); + else + kprintf("Transmit packet trace Disabled\n"); + break; + case 'S': + case 's': + kprintf(kDevName " statistics\n"); + kprintf("rx_ints %d, tx_ints %d\n", gdev->rints, gdev->wints); + kprintf("resets %d \n", gdev->resets); + kprintf("crc_errs %d, frame_errs %d, frames_lost %d\n", gdev->crc_errs, gdev->frame_errs, gdev->frames_lost); + break; + case 'P': + case 'p': + gdev->debug ^= PCI_IO; + if (gdev->debug & PCI_IO) + kprintf("PCI IO trace Enabled\n"); + else + kprintf("PCI IO trace Disabled\n"); + break; + default: + kprintf("%s",usage); + return 0; + } + } + + return 0; +} +#endif /* DEBUGGER_COMMAND */ + +void dump_packet( const char * msg, unsigned char * buf, uint16 size) { + + uint16 j; + + dprintf("%s dumping %p size %u \n", msg, buf, size); + for (j=0; j +#endif + +/* + * Wait this long before giving up on an ethernet transmit + */ +#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)1000000) /* one second */ + +#define NIRQS 128 //x hmm... + +#define MAX_MULTI 14 + +#define ETHER_BUF_START_NE2000 0x4000 +#define ETHER_BUF_SIZE_NE2000 0x4000 + +#define ETHER_BUF_SIZE_MAX ETHER_BUF_SIZE_NE2000 + +#define ETHER_MTU 1500 +#define ETHER_MIN_SIZE 60 +#define ETHER_MAX_SIZE 1514 +#define ETHER_IRQ2 0x10 +#define ETHER_IRQ3 0x20 +#define ETHER_IRQ4 0x40 +#define ETHER_IRQ5 0x80 + + +#define ECNTRL_RESET 0x01 +#define ECNTRL_ONBOARD 0x02 +#define ECNTRL_SAPROM 0x04 + +#define EGACFR_NORM 0x49 +#define EGACFR_IRQOFF 0xc9 + +#define EC_PAGE_SIZE 256 +#define EC_PAGE_SHIFT 8 + +#define EN_CCMD 0x0 + +#define EN0_STARTPG 0x01 +#define EN0_STOPPG 0x02 +#define EN0_BOUNDARY 0x03 +#define EN0_TPSR 0x04 +#define EN0_TCNTLO 0x05 +#define EN0_TCNTHI 0x06 +#define EN0_ISR 0x07 +#define EN0_RADDRLO 0x08 +#define EN0_RADDRHI 0x09 +#define EN0_RCNTLO 0x0a +#define EN0_RCNTHI 0x0b + +#define NE_DATA 0x10 +#define NE_RESET 0x1f + +#define ISR_RECEIVE 0x01 +#define ISR_TRANSMIT 0x02 +#define ISR_RECEIVE_ERROR 0x04 +#define ISR_TRANSMIT_ERROR 0x08 +#define ISR_OVERWRITE 0x10 +#define ISR_COUNTER 0x20 +#define ISR_DMADONE 0x40 +#define ISR_RESET 0x80 + +#define EN0_RXCR 0xc +#define EN0_TXCR 0xd +#define EN0_DCFG 0xe +#define EN0_IMR 0xf + +#define EN0_CNTR0 0x0d +#define EN0_CNTR1 0x0e +#define EN0_CNTR2 0x0f + +#define DCFG_BM8 0x48 +#define DCFG_BM16 0x49 + +#define EN1_PHYS 0x1 +#define EN1_CURPAG 0x7 +#define EN1_MULT 0x8 + +#define ENRXCR_MON 0x20 +#define ENRXCR_MCST 0x08 +#define ENRXCR_BCST 0x04 + +#define TXCR_LOOPBACK 0x02 + +#define ENC_PAGE0 0x00 +#define ENC_STOP 0x01 +#define ENC_START 0x02 +#define ENC_TRANS 0x04 +#define ENC_DMAREAD 0x08 +#define ENC_DMAWRITE 0x10 +#define ENC_NODMA 0x20 +#define ENC_PAGE1 0x40 + + +#define RSR_INTACT 0x01 + +#define TSR_ABORTED 0x08 +#define TSR_UNDERRUN 0x20 +#define TSR_HEARTBEAT 0x40 + +#define ETHER_ADDR_LEN 6 + +/* + * Maximum iterations to poll before assuming error + */ +#define MAXWAIT 10000 + + +/* + * Swap the bytes in a short, but not on a little-endian machine + */ +static const union { long l; char b[4]; } ENDIAN_TEST = { 1 }; +#define LITTLE_ENDIAN ENDIAN_TEST.b[0] +#define SWAPSHORT(x) (((x & 0xff) << 8) | ((x >> 8) & 0xff)) +#define swapshort(x) (LITTLE_ENDIAN ? (x) : SWAPSHORT(x)) + + +/* + * NS8390 ring header structure + */ +typedef struct ring_header { + unsigned char status; + unsigned char next_packet; + unsigned short count; +} ring_header; + +#endif