| /* depca.c: A DIGITAL DEPCA ethernet driver for linux. |
| |
| Written 1994 by David C. Davies. |
| |
| Copyright 1994 David C. Davies and United States Government as |
| represented by the Director, National Security Agency. This software |
| may be used and distributed according to the terms of the GNU Public |
| License, incorporated herein by reference. |
| |
| This driver is written for the Digital Equipment Corporation series |
| of DEPCA ethernet cards: |
| |
| DE100 DEPCA |
| DE200 DEPCA Turbo |
| DE202 DEPCA Turbo (TP BNC) |
| DE210 DEPCA |
| |
| The driver has been tested on DE100 and DE20x cards in a relatively busy |
| network. |
| |
| The author may be reached as davies@wanton.enet.dec.com or |
| Digital Equipment Corporation, 146 Main Street, Maynard MA 01754. |
| |
| ========================================================================= |
| The driver was based on the 'lance.c' driver from Donald Becker which is |
| included with the standard driver distribution for linux. Modifications |
| were made to most routines and the hardware recognition routines were |
| written from scratch. Primary references used were: |
| |
| 1) Lance.c code in /linux/drivers/net/ |
| 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", |
| AMD, 1992 [(800) 222-9323]. |
| 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", |
| AMD, Pub. #17881, May 1993. |
| 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", |
| AMD, Pub. #16907, May 1992 |
| 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", |
| Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 |
| 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", |
| Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 |
| 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR |
| Digital Equipment Corporation, 1989 |
| 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", |
| Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 |
| |
| Peter Bauer's depca.c (V0.5) was referred to when debugging this driver. |
| The hash filter code was derived from Reference 3 and has been tested |
| only to the extent that the Table A-1, page A-7, was confirmed to fill |
| the filter bit positions correctly. Hash filtering is not yet |
| implemented in the current driver set. |
| |
| The DE200 series boards have on-board 64kB RAM for use as a shared |
| memory network buffer. Only the DE100 cards make use of a 2kB buffer |
| mode which has not been implemented in this driver (only the 32kB and |
| 64kB modes are supported). |
| |
| At the most only 2 DEPCA cards can be supported because there is only |
| provision for two I/O base addresses on the cards (0x300 and 0x200). The |
| base address is 'autoprobed' by looking for the self test PROM and |
| detecting the card name. The shared memory base address is decoded by |
| 'autoprobing' the Ethernet PROM address information. The second DEPCA is |
| detected and information placed in the base_addr variable of the next |
| device structure (which is created if necessary), thus enabling |
| ethif_probe initialization for the device. |
| |
| ************************************************************************ |
| |
| NOTE: If you are using two DEPCAs, it is important that you assign the |
| base memory addresses correctly. The driver autoprobes I/O 0x300 then |
| 0x200. The base memory address for the first device must be less than |
| that of the second so that the auto probe will correctly assign the I/O |
| and memory addresses on the same card. I can't think of a way to do |
| this unambiguously at the moment, since there is nothing on the cards to |
| tie I/O and memory information together. |
| |
| I am unable to test 2 DEPCAs together for now, so this code is |
| unchecked. All reports, good or bad, are welcome. |
| |
| ************************************************************************ |
| |
| The board IRQ setting must be at an unused IRQ which is auto-probed |
| using Donald Becker's autoprobe routines. DE100 board IRQs are |
| {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is |
| really IRQ9 in machines with 16 IRQ lines. |
| |
| No 16MB memory limitation should exist with this driver as DMA is not |
| used and the common memory area is in low memory on the network card (my |
| current system has 20MB and I've not had problems yet). |
| |
| The DE203, DE204 and DE205 cards may also work with this driver (I |
| haven't tested them so I don't know). If you have one of these cards, |
| place the name in the DEPCA_SIGNATURE string around line 160, recompile |
| the kernel and reboot. Check if the card is recognised and works - mail |
| me if so, so that I can add it into the list of supported cards! |
| |
| TO DO: |
| ------ |
| |
| 1. Implement the 2k buffer mode - does anyone need it?? |
| |
| Revision History |
| ---------------- |
| |
| Version Date Description |
| |
| 0.1 25-jan-94 Initial writing |
| 0.2 27-jan-94 Added LANCE TX buffer chaining |
| 0.3 1-feb-94 Added multiple DEPCA support |
| 0.31 4-feb-94 Added DE202 recognition |
| 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support. |
| |
| ========================================================================= |
| */ |
| |
| static char *version = "depca.c:v0.32 2/19/94 davies@wanton.enet.dec.com\n"; |
| |
| #include <stdarg.h> |
| #include <linux/config.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/string.h> |
| #include <linux/ptrace.h> |
| #include <linux/errno.h> |
| #include <linux/ioport.h> |
| #include <linux/malloc.h> |
| #include <linux/interrupt.h> |
| #include <asm/bitops.h> |
| #include <asm/io.h> |
| #include <asm/dma.h> |
| |
| #include "dev.h" |
| #include "iow.h" |
| #include "eth.h" |
| #include "skbuff.h" |
| #include "arp.h" |
| #include "depca.h" |
| |
| #ifdef DEPCA_DEBUG |
| int depca_debug = DEPCA_DEBUG; |
| #else |
| int depca_debug = 1; |
| #endif |
| |
| #ifndef DEPCA_IRQ |
| /*#define DEPCA_IRQ {5,9,10,11,15,0}*/ |
| #define DEPCA_IRQ 5 |
| #endif |
| |
| #ifndef PROBE_LENGTH |
| #define PROBE_LENGTH 32 |
| #endif |
| |
| #ifndef PROBE_SEQUENCE |
| #define PROBE_SEQUENCE "FF0055AAFF0055AA" |
| #endif |
| |
| #ifndef DEPCA_SIGNATURE |
| #define DEPCA_SIGNATURE {"DEPCA","DE100","DE200","DE202","DE210",""} |
| #define DEPCA_NAME_LENGTH 8 |
| #endif |
| |
| #ifndef DEPCA_RAM_BASE_ADDRESSES |
| #define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000} |
| #endif |
| static short mem_chkd = 0; /* holds which base addrs have been */ |
| /* checked, for multi-DEPCA case */ |
| |
| #ifndef DEPCA_IO_PORTS |
| #define DEPCA_IO_PORTS {0x300, 0x200, 0} |
| #endif |
| |
| #ifndef DEPCA_TOTAL_SIZE |
| #define DEPCA_TOTAL_SIZE 0x10 |
| #endif |
| |
| #ifndef MAX_NUM_DEPCAS |
| #define MAX_NUM_DEPCAS 2 |
| #endif |
| |
| /* |
| ** Set the number of Tx and Rx buffers. |
| */ |
| #ifndef DEPCA_BUFFER_LOG_SZ |
| #define RING_SIZE 16 /* 16 buffers */ |
| #else |
| #define RING_SIZE (1 << (DEPCA_BUFFERS_LOG_SZ)) |
| #endif /* DEPCA_BUFFER_LOG_SZ */ |
| |
| #define PKT_BUF_SZ 1544 /* Buffer size for each Tx/Rx buffer */ |
| #define PKT_SZ 1514 /* Maximum ethernet packet length */ |
| #define DAT_SZ 1500 /* Maximum ethernet data length */ |
| #define PKT_HDR_LEN 14 /* Addresses and data length info */ |
| |
| #ifdef HAVE_MULTICAST |
| #ifndef CRC_POLYNOMIAL |
| #define CRC_POLYNOMIAL 0x04c11db7 /* Ethernet CRC polynomial */ |
| #endif /* CRC_POLYNOMIAL */ |
| #endif /* HAVE_MULTICAST */ |
| |
| #ifndef HAVE_ALLOC_SKB |
| #define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority) |
| #define kfree_skbmem(buff, size) kfree_s(buff,size) |
| #endif /* HAVE_ALLOC_SKB */ |
| |
| /* |
| ** The DEPCA Rx and Tx ring descriptors. |
| */ |
| struct depca_rx_head { |
| long base; |
| short buf_length; /* This length is negative 2's complement! */ |
| short msg_length; /* This length is "normal". */ |
| }; |
| |
| struct depca_tx_head { |
| long base; |
| short length; /* This length is negative 2's complement! */ |
| short misc; /* Errors and TDR info */ |
| }; |
| |
| struct depca_ring_info { |
| }; |
| |
| /* |
| ** The Lance initialization block, described in databook, in common memory. |
| */ |
| struct depca_init { |
| unsigned short mode; /* Mode register */ |
| unsigned char phys_addr[ETH_ALEN]; /* Physical ethernet address */ |
| unsigned short filter[4]; /* Multicast filter. */ |
| unsigned long rx_ring; /* Rx ring base pointer & ring length */ |
| unsigned long tx_ring; /* Tx ring base pointer & ring length */ |
| }; |
| |
| struct depca_private { |
| char devname[8]; /* Not used */ |
| struct depca_rx_head *rx_ring; /* Pointer to start of RX descriptor ring */ |
| struct depca_tx_head *tx_ring; /* Pointer to start of TX descriptor ring */ |
| struct depca_init init_block;/* Initialization block */ |
| long dma_buffs; /* Start address of Rx and Tx buffers. */ |
| int cur_rx, cur_tx; /* The next free ring entry */ |
| int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ |
| int dma; |
| struct enet_statistics stats; |
| char old_depca; |
| short ringSize; /* ring size based on available memory */ |
| short rmask; /* modulus mask based on ring size */ |
| long rlen; /* log2(ringSize) for the descriptors */ |
| }; |
| |
| /* |
| ** Public Functions |
| */ |
| static int depca_open(struct device *dev); |
| static int depca_start_xmit(struct sk_buff *skb, struct device *dev); |
| static void depca_interrupt(int reg_ptr); |
| static int depca_close(struct device *dev); |
| static struct enet_statistics *depca_get_stats(struct device *dev); |
| #ifdef HAVE_MULTICAST |
| static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); |
| #endif |
| |
| /* |
| ** Private functions |
| */ |
| static int depca_probe1(struct device *dev, short ioaddr); |
| static void depca_init_ring(struct device *dev); |
| static int depca_rx(struct device *dev); |
| static int depca_tx(struct device *dev); |
| |
| static void LoadCSRs(struct device *dev); |
| static int InitRestartDepca(struct device *dev); |
| static char *DepcaSignature(unsigned long mem_addr); |
| static int DevicePresent(short ioaddr); |
| #ifdef HAVE_MULTICAST |
| static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table); |
| #endif |
| |
| static int num_depcas = 0, num_eth = 0;; |
| |
| /* |
| ** Miscellaneous defines... |
| */ |
| #define STOP_DEPCA \ |
| outw(CSR0, DEPCA_ADDR);\ |
| outw(STOP, DEPCA_DATA) |
| |
| |
| |
| int depca_probe(struct device *dev) |
| { |
| int *port, ports[] = DEPCA_IO_PORTS; |
| int base_addr = dev->base_addr; |
| int status; |
| struct device *eth0 = (struct device *) NULL; |
| |
| if (base_addr > 0x1ff) { /* Check a single specified location. */ |
| status = depca_probe1(dev, base_addr); |
| } else if (base_addr > 0) { /* Don't probe at all. */ |
| status = -ENXIO; |
| } else { /* First probe for the DEPCA test */ |
| /* pattern in ROM */ |
| |
| for (status = -ENODEV, port = &ports[0]; |
| *port && (num_depcas < MAX_NUM_DEPCAS); port++) { |
| int ioaddr = *port; |
| |
| #ifdef HAVE_PORTRESERVE |
| if (check_region(ioaddr, DEPCA_TOTAL_SIZE)) |
| continue; |
| #endif |
| if (DevicePresent(DEPCA_PROM) == 0) { |
| if (num_depcas > 0) { /* only gets here in autoprobe */ |
| |
| /* |
| ** Check the device structures for an end of list or unused device |
| */ |
| while (dev->next != (struct device *)NULL) { |
| if (dev->next->base_addr == 0xffe0) break; |
| dev = dev->next; /* walk through eth device list */ |
| num_eth++; /* increment eth device number */ |
| } |
| |
| /* |
| ** If no more device structures, malloc one up. If memory could |
| ** not be allocated, print an error message. |
| ** |
| */ |
| if (dev->next == (struct device *)NULL) { |
| dev->next = (struct device *)kmalloc(sizeof(struct device) + 8, |
| GFP_KERNEL); |
| } else { |
| printk("eth%d: Device not initialised, insufficient memory\n", |
| num_eth); |
| } |
| |
| /* |
| ** If the memory was allocated, point to the new memory area |
| ** and initialize it (name, I/O address, next device (NULL) and |
| ** initialisation probe routine). |
| */ |
| if ((dev->next != (struct device *)NULL) && |
| (num_eth > 0) && (num_eth < 9999)) { |
| dev = dev->next; /* point to the new device */ |
| dev->name = (char *)(dev + sizeof(struct device)); |
| sprintf(dev->name,"eth%d", num_eth); /* New device name */ |
| dev->base_addr = ioaddr; /* assign the io address */ |
| dev->next = (struct device *)NULL; /* mark the end of list */ |
| dev->init = &depca_probe;/* initialisation routine */ |
| } |
| } else { |
| eth0 = dev; /* remember the first device */ |
| status = depca_probe1(dev, ioaddr); |
| } |
| num_depcas++; |
| num_eth++; |
| } |
| } |
| if (eth0) dev = eth0; /* restore the first device */ |
| } |
| |
| if (status) dev->base_addr = base_addr; |
| |
| return status; /* ENODEV would be more accurate. */ |
| } |
| |
| static int |
| depca_probe1(struct device *dev, short ioaddr) |
| { |
| struct depca_private *lp; |
| int i,j, status=0; |
| unsigned long mem_start, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; |
| char *name=(char *)NULL; |
| int nicsr, offset; |
| |
| |
| /* |
| ** Stop the DEPCA. Enable the DBR ROM. Disable interrupts and remote boot |
| */ |
| STOP_DEPCA; |
| |
| nicsr = inw(DEPCA_NICSR); |
| nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); |
| outw(nicsr, DEPCA_NICSR); |
| |
| if (inw(DEPCA_DATA) == STOP) { |
| |
| /* Now find out what kind of DEPCA we have. The DE100 uses a different |
| ** addressing scheme for some registers compared to the DE2xx series. |
| ** Note that a base address location is marked as checked if no DEPCA is |
| ** there or one is found (when the search is immediately terminated). This |
| ** shortens the search time a little for multiple DEPCAs. |
| */ |
| |
| for (j = 0, i = 0; mem_base[i] && (j == 0);) { |
| if (((mem_chkd >> i) & 0x01) == 0) { /* has the memory been checked? */ |
| name = DepcaSignature(mem_base[i]);/* check for a DEPCA here */ |
| mem_chkd |= (0x01 << i); /* mark location checked */ |
| if (*name != (char)NULL) { /* one found? */ |
| j = 1; /* set exit flag */ |
| } else { |
| i++; /* increment search index */ |
| } |
| } |
| } |
| |
| if (*name != (char)NULL) { /* found a DEPCA device */ |
| mem_start = mem_base[i]; |
| dev->base_addr = ioaddr; |
| |
| printk("%s: DEPCA at %#3x is a %s, ", dev->name, ioaddr, name); |
| |
| /* There is a 32 byte station address PROM at DEPCA_PROM address. |
| The first six bytes are the station address. They can be read |
| directly since the signature search set up the ROM address |
| counter correctly just before this function. |
| |
| For the DE100 we have to be careful about which port is used to |
| read the ROM info. |
| */ |
| |
| if (strstr(name,"DE100")!=(char *)NULL) { |
| j = 1; |
| } else { |
| j = 0; |
| } |
| |
| printk("ethernet address "); |
| for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet address */ |
| printk("%2.2x:", dev->dev_addr[i] = inb(DEPCA_PROM + j)); |
| } |
| printk("%2.2x", dev->dev_addr[i] = inb(DEPCA_PROM + j)); |
| |
| for (;i<32;i++) { /* leave ROM counter in known state */ |
| j=inb(DEPCA_PROM); |
| } |
| |
| #ifdef HAVE_PORTRESERVE |
| snarf_region(ioaddr, DEPCA_TOTAL_SIZE); |
| #endif |
| |
| /* |
| ** Determine the base address for the DEPCA RAM from the NI-CSR |
| ** and make up a DEPCA-specific-data structure. |
| */ |
| |
| if (nicsr & BUF) { |
| offset = 0x8000; /* 32kbyte RAM */ |
| nicsr &= ~BS; /* DEPCA RAM in top 32k */ |
| printk(",\n with 32kB RAM"); |
| } else { |
| offset = 0x0000; /* 64kbyte RAM */ |
| printk(",\n with 64kB RAM"); |
| } |
| |
| mem_start += offset; |
| printk(" starting at 0x%.5lx", mem_start); |
| |
| /* |
| ** Enable the shadow RAM. |
| */ |
| nicsr |= SHE; |
| outw(nicsr, DEPCA_NICSR); |
| |
| /* |
| ** Calculate the ring size based on the available RAM |
| ** found above. Allocate an equal number of buffers, each |
| ** of size PKT_BUF_SZ (1544 bytes) to the Tx and Rx. Make sure |
| ** that this ring size is <= RING_SIZE. The ring size must be |
| ** a power of 2. |
| */ |
| |
| j = ((0x10000 - offset) / PKT_BUF_SZ) >> 1; |
| for (i=0;j>1;i++) { |
| j >>= 1; |
| } |
| |
| /* Hold the ring size information here before the depca |
| ** private structure is allocated. Need this for the memory |
| ** space calculations. |
| */ |
| j = 1 << i; |
| |
| /* |
| ** Set up memory information in the device structure. |
| ** Align the descriptor rings on an 8 byte (quadword) boundary. |
| ** |
| ** depca_private area |
| ** rx ring descriptors |
| ** tx ring descriptors |
| ** rx buffers |
| ** tx buffers |
| ** |
| */ |
| |
| /* private area & initialise */ |
| dev->priv = (void *)((mem_start + 0x07) & ~0x07); |
| lp = (struct depca_private *)dev->priv; |
| memset(dev->priv, 0, sizeof(struct depca_private)); |
| |
| /* Tx & Rx descriptors (aligned to a quadword boundary) */ |
| mem_start = ((((unsigned long)dev->priv + |
| sizeof(struct depca_private)) + |
| (unsigned long)0x07) & (unsigned long)~0x07); |
| lp->rx_ring = (struct depca_rx_head *)mem_start; |
| |
| mem_start += (sizeof(struct depca_rx_head) * j); |
| lp->tx_ring = (struct depca_tx_head *)mem_start; |
| |
| mem_start += (sizeof(struct depca_tx_head) * j); |
| lp->dma_buffs = mem_start & 0x00ffffff; |
| |
| mem_start += (PKT_BUF_SZ * j); |
| /* (mem_start now points to the start of the Tx buffers) */ |
| |
| /* Initialise the data structures */ |
| memset(lp->rx_ring, 0, sizeof(struct depca_rx_head)*j); |
| memset(lp->tx_ring, 0, sizeof(struct depca_tx_head)*j); |
| |
| /* This should never happen. */ |
| if ((int)(lp->rx_ring) & 0x07) { |
| printk("\n **ERROR** DEPCA Rx and Tx descriptor rings not on a quadword boundary.\n"); |
| return -ENXIO; |
| } |
| |
| /* |
| ** Finish initialising the ring information. |
| */ |
| lp->ringSize = j; |
| if (lp->ringSize > RING_SIZE) lp->ringSize = RING_SIZE; |
| lp->rmask = lp->ringSize - 1; |
| |
| /* |
| ** calculate the real RLEN size for the descriptors. It is |
| ** log2(ringSize). |
| */ |
| for (i=0, j = lp->ringSize; j>1; i++) { |
| j >>= 1; |
| } |
| lp->rlen = (unsigned long)(i << 29); |
| |
| /* |
| ** load the initialisation block |
| */ |
| depca_init_ring(dev); |
| |
| /* |
| ** Initialise the control and status registers |
| */ |
| LoadCSRs(dev); |
| |
| /* |
| ** Enable DEPCA board interrupts for autoprobing |
| */ |
| nicsr = ((nicsr & ~IM)|IEN); |
| outw(nicsr, DEPCA_NICSR); |
| |
| /* The DMA channel may be passed in on this parameter. */ |
| dev->dma = 0; |
| |
| /* To auto-IRQ we enable the initialization-done and DMA err, |
| interrupts. For now we will always get a DMA error. */ |
| if (dev->irq < 2) { |
| autoirq_setup(0); |
| |
| /* Trigger an initialization just for the interrupt. */ |
| outw(INEA | INIT, DEPCA_DATA); |
| |
| dev->irq = autoirq_report(1); |
| if (dev->irq) { |
| printk(" and probed IRQ%d.\n", dev->irq); |
| } else { |
| printk(". Failed to detect IRQ line.\n"); |
| status = -EAGAIN; |
| } |
| } else { |
| printk(". Assigned IRQ%d.\n", dev->irq); |
| } |
| } else { |
| status = -ENXIO; |
| } |
| if (!status) { |
| if (depca_debug > 0) { |
| printk(version); |
| } |
| |
| /* The DEPCA-specific entries in the device structure. */ |
| dev->open = &depca_open; |
| dev->hard_start_xmit = &depca_start_xmit; |
| dev->stop = &depca_close; |
| dev->get_stats = &depca_get_stats; |
| #ifdef HAVE_MULTICAST |
| dev->set_multicast_list = &set_multicast_list; |
| #endif |
| |
| dev->mem_start = 0; |
| |
| /* Fill in the generic field of the device structure. */ |
| for (i = 0; i < DEV_NUMBUFFS; i++) { |
| dev->buffs[i] = NULL; |
| } |
| |
| dev->hard_header = eth_header; |
| dev->add_arp = eth_add_arp; |
| dev->queue_xmit = dev_queue_xmit; |
| dev->rebuild_header = eth_rebuild_header; |
| dev->type_trans = eth_type_trans; |
| |
| dev->type = ARPHRD_ETHER; |
| dev->hard_header_len = ETH_HLEN; |
| dev->mtu = 1500; /* eth_mtu */ |
| dev->addr_len = ETH_ALEN; |
| |
| for (i = 0; i < dev->addr_len; i++) { |
| dev->broadcast[i]=0xff; |
| } |
| |
| /* New-style flags. */ |
| dev->flags = IFF_BROADCAST; |
| dev->family = AF_INET; |
| dev->pa_addr = 0; |
| dev->pa_brdaddr = 0; |
| dev->pa_mask = 0; |
| dev->pa_alen = sizeof(unsigned long); |
| } |
| } else { |
| status = -ENXIO; |
| } |
| |
| return status; |
| } |
| |
| |
| static int |
| depca_open(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int i,nicsr,ioaddr = dev->base_addr; |
| |
| if (request_irq(dev->irq, &depca_interrupt)) { |
| printk("depca_open(): Requested IRQ%d is busy\n",dev->irq); |
| return -EAGAIN; |
| } |
| |
| irq2dev_map[dev->irq] = dev; |
| |
| /* |
| ** Stop the DEPCA & get the board status information. |
| */ |
| STOP_DEPCA; |
| nicsr = inw(DEPCA_NICSR); |
| |
| /* |
| ** Re-initialize the DEPCA... |
| */ |
| depca_init_ring(dev); /* initialize the descriptor rings */ |
| LoadCSRs(dev); |
| |
| if (depca_debug > 1){ |
| printk("%s: depca open with irq %d\n",dev->name,dev->irq); |
| printk("Descriptor head addresses:\n"); |
| printk("\t0x%8.8lx 0x%8.8lx\n",(long)lp->rx_ring,(long)lp->tx_ring); |
| printk("Descriptor addresses:\n"); |
| for (i=0;i<lp->ringSize;i++){ |
| printk("\t0x%8.8lx 0x%8.8lx\n",(long)&lp->rx_ring[i].base, |
| (long)&lp->tx_ring[i].base); |
| } |
| printk("Buffer addresses:\n"); |
| for (i=0;i<lp->ringSize;i++){ |
| printk("\t0x%8.8lx 0x%8.8lx\n",(long)lp->rx_ring[i].base, |
| (long)lp->tx_ring[i].base); |
| } |
| printk("Initialisation block at 0x%8.8lx\n",(long)&lp->init_block); |
| printk("\tmode: 0x%4.4x\n",lp->init_block.mode); |
| printk("\tphysical address: "); |
| for (i=0;i<6;i++){ |
| printk("%2.2x:",(short)lp->init_block.phys_addr[i]); |
| } |
| printk("\n\tlogical address filter: 0x"); |
| for (i=0;i<4;i++){ |
| printk("%2.2x",(short)lp->init_block.filter[i]); |
| } |
| printk("\n\trx_ring at: 0x%8.8lx\n",(long)lp->init_block.rx_ring); |
| printk("\ttx_ring at: 0x%8.8lx\n",(long)lp->init_block.tx_ring); |
| printk("dma_buffs: 0x%8.8lx\n",(long)lp->dma_buffs); |
| printk("Ring size: %d\nMask: 0x%2.2x\nLog2(ringSize): 0x%8.8lx\n", |
| (short)lp->ringSize, |
| (char)lp->rmask, |
| (long)lp->rlen); |
| outw(CSR2,DEPCA_ADDR); |
| printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA)); |
| outw(CSR1,DEPCA_ADDR); |
| printk("%4.4x\n",inw(DEPCA_DATA)); |
| outw(CSR3,DEPCA_ADDR); |
| printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA)); |
| } |
| |
| /* |
| ** Enable DEPCA board interrupts |
| */ |
| nicsr = ((nicsr & ~IM & ~LED)|SHE|IEN); |
| outw(nicsr, DEPCA_NICSR); |
| outw(CSR0,DEPCA_ADDR); |
| |
| dev->tbusy = 0; |
| dev->interrupt = 0; |
| dev->start = 1; |
| |
| InitRestartDepca(dev); /* ignore the return status */ |
| |
| if (depca_debug > 1){ |
| printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA)); |
| printk("nicsr: 0x%4.4x\n",inw(DEPCA_NICSR)); |
| } |
| |
| return 0; /* Always succeed */ |
| } |
| |
| /* Initialize the lance Rx and Tx descriptor rings. */ |
| static void |
| depca_init_ring(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| unsigned long i; |
| |
| lp->init_block.mode = DTX | DRX; /* Disable Rx and Tx. */ |
| lp->cur_rx = lp->cur_tx = 0; |
| lp->dirty_rx = lp->dirty_tx = 0; |
| |
| /* Initialize the base addresses and length of each buffer in the ring */ |
| for (i = 0; i < lp->ringSize; i++) { |
| lp->rx_ring[i].base = (lp->dma_buffs + i*PKT_BUF_SZ) | R_OWN; |
| lp->rx_ring[i].buf_length = -PKT_BUF_SZ; |
| lp->tx_ring[i].base = (lp->dma_buffs + (i+lp->ringSize) * PKT_BUF_SZ) & |
| (unsigned long)(0x00ffffff); |
| } |
| |
| /* Set up the initialization block */ |
| for (i = 0; i < ETH_ALEN; i++) { |
| lp->init_block.phys_addr[i] = dev->dev_addr[i]; |
| } |
| for (i = 0; i < 4; i++) { |
| lp->init_block.filter[i] = 0x0000; |
| } |
| lp->init_block.rx_ring = (unsigned long)lp->rx_ring | lp->rlen; |
| lp->init_block.tx_ring = (unsigned long)lp->tx_ring | lp->rlen; |
| |
| lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ |
| } |
| |
| /* |
| ** Writes a socket buffer to TX descriptor ring and starts transmission |
| */ |
| static int |
| depca_start_xmit(struct sk_buff *skb, struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int ioaddr = dev->base_addr; |
| int status = 0; |
| |
| /* Transmitter timeout, serious problems. */ |
| if (dev->tbusy) { |
| int tickssofar = jiffies - dev->trans_start; |
| if (tickssofar < 5) { |
| status = -1; |
| } else { |
| STOP_DEPCA; |
| printk("%s: transmit timed out, status %4.4x, resetting.\n", |
| dev->name, inw(DEPCA_DATA)); |
| |
| depca_init_ring(dev); |
| LoadCSRs(dev); |
| InitRestartDepca(dev); |
| dev->tbusy=0; |
| dev->trans_start = jiffies; |
| } |
| return status; |
| } |
| |
| if (skb == NULL) { |
| dev_tint(dev); |
| return 0; |
| } |
| |
| /* Fill in the ethernet header. */ |
| if (!skb->arp && dev->rebuild_header(skb+1, dev)) { |
| skb->dev = dev; |
| arp_queue (skb); |
| return 0; |
| } |
| skb->arp=1; |
| |
| if (skb->len <= 0) { |
| return 0; |
| } |
| |
| if (depca_debug > 3) { |
| outw(CSR0, DEPCA_ADDR); |
| printk("%s: depca_start_xmit() called, csr0 %4.4x.\n", dev->name, |
| inw(DEPCA_DATA)); |
| } |
| |
| /* Block a timer-based transmit from overlapping. This could better be |
| done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ |
| if (set_bit(0, (void*)&dev->tbusy) != 0) |
| printk("%s: Transmitter access conflict.\n", dev->name); |
| |
| /* |
| ** The TX buffer, skb, has to be copied into the local network RAM |
| ** for the LANCE to access it. The skb may be at > 16MB for large |
| ** (memory) systems. |
| */ |
| { /* Fill in a Tx ring entry */ |
| unsigned char *buf; |
| int entry = lp->cur_tx++; |
| int len; |
| long skbL = skb->len; |
| char *p = (char *)(skb + 1); |
| |
| entry &= lp->rmask; /* Ring around buffer number. */ |
| buf = (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff); |
| |
| /* Wait for a full ring to free up */ |
| while (lp->tx_ring[entry].base < 0); |
| |
| /* |
| ** Caution: the write order is important here... don't set up the |
| ** ownership rights until all the other information is in place. |
| */ |
| len = ((skbL > PKT_SZ) ? PKT_SZ : skbL); /* skb too long */ |
| if (len < ETH_ZLEN) len = ETH_ZLEN; /* len too short */ |
| skbL -= len; |
| lp->tx_ring[entry].length = -len; |
| |
| /* Clears various error flags */ |
| lp->tx_ring[entry].misc = 0x0000; |
| |
| /* copy the data from the socket buffer to the net memory */ |
| memcpy((unsigned char *)(buf), (unsigned char *)(skb + 1), len); |
| |
| /* Hand over buffer ownership to the LANCE */ |
| if (skbL <= 0) lp->tx_ring[entry].base |= (T_ENP); |
| lp->tx_ring[entry].base |= (T_OWN|T_STP); |
| |
| /* Trigger an immediate send demand. */ |
| outw(CSR0, DEPCA_ADDR); |
| outw(INEA | TDMD, DEPCA_DATA); |
| |
| dev->trans_start = jiffies; |
| |
| for (p += len; skbL > 0; p += len) { |
| |
| /* Get new buffer pointer */ |
| entry = lp->cur_tx++; |
| entry &= lp->rmask; /* Ring around buffer number. */ |
| buf = (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff); |
| |
| /* Wait for a full ring to free up */ |
| while (lp->tx_ring[entry].base < 0); |
| dev->tbusy=0; |
| |
| /* Copy ethernet header to the new buffer */ |
| memcpy((unsigned char *)buf, (unsigned char *)(skb + 1), PKT_HDR_LEN); |
| |
| /* Determine length of data buffer */ |
| len = ((skbL > DAT_SZ) ? DAT_SZ : skbL); /* skbL too long */ |
| if (len < ETH_ZLEN) len = ETH_ZLEN; /* len too short */ |
| skbL -= len; |
| lp->tx_ring[entry].length = -len; |
| |
| /* Clears various error flags */ |
| lp->tx_ring[entry].misc = 0x0000; |
| |
| /* copy the data from the socket buffer to the net memory */ |
| memcpy((unsigned char *)(buf + PKT_HDR_LEN), (unsigned char *)p, len); |
| |
| /* Hand over buffer ownership to the LANCE */ |
| if (skbL <= 0) lp->tx_ring[entry].base |= T_ENP; |
| lp->tx_ring[entry].base |= T_OWN; |
| } |
| |
| if (depca_debug > 4) { |
| unsigned char *pkt = |
| (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff); |
| |
| printk("%s: tx ring[%d], %#lx, sk_buf %#lx len %d.\n", |
| dev->name, entry, (unsigned long) &lp->tx_ring[entry], |
| lp->tx_ring[entry].base, -lp->tx_ring[entry].length); |
| printk("%s: Tx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n", |
| dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6], |
| pkt[7], pkt[8], pkt[11], pkt[12], pkt[13], |
| pkt[14], pkt[15]); |
| } |
| |
| /* Check if the TX ring is full or not - 'tbusy' cleared if not full. */ |
| if (lp->tx_ring[(entry+1) & lp->rmask].base >= 0) { |
| dev->tbusy=0; |
| } |
| |
| if (skb->free) { |
| kfree_skb (skb, FREE_WRITE); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| ** The DEPCA interrupt handler. |
| */ |
| static void |
| depca_interrupt(int reg_ptr) |
| { |
| int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); |
| struct device *dev = (struct device *)(irq2dev_map[irq]); |
| struct depca_private *lp; |
| int csr0, ioaddr, nicsr; |
| |
| if (dev == NULL) { |
| printk ("depca_interrupt(): irq %d for unknown device.\n", irq); |
| return; |
| } else { |
| lp = (struct depca_private *)dev->priv; |
| ioaddr = dev->base_addr; |
| } |
| |
| if (dev->interrupt) |
| printk("%s: Re-entering the interrupt handler.\n", dev->name); |
| |
| dev->interrupt = MASK_INTERRUPTS; |
| |
| /* mask the DEPCA board interrupts and turn on the LED */ |
| nicsr = inw(DEPCA_NICSR); |
| nicsr |= (IM|LED); |
| outw(nicsr, DEPCA_NICSR); |
| |
| outw(CSR0, DEPCA_ADDR); |
| csr0 = inw(DEPCA_DATA); |
| |
| /* Acknowledge all of the current interrupt sources ASAP. */ |
| outw(csr0 & ~(INEA|TDMD|STOP|STRT|INIT), DEPCA_DATA); |
| |
| if (depca_debug > 5) |
| printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", |
| dev->name, csr0, inw(DEPCA_DATA)); |
| |
| if (csr0 & RINT) /* Rx interrupt (packet arrived) */ |
| depca_rx(dev); |
| |
| if (csr0 & TINT) /* Tx interrupt (packet sent) */ |
| depca_tx(dev); |
| |
| /* Clear the interrupts we've handled. */ |
| outw(CSR0, DEPCA_ADDR); |
| outw(BABL|CERR|MISS|MERR|RINT|TINT|IDON|INEA, DEPCA_DATA); |
| |
| if (depca_debug > 4) { |
| printk("%s: exiting interrupt, csr%d=%#4.4x.\n", |
| dev->name, inw(DEPCA_ADDR), |
| inw(DEPCA_DATA)); |
| } |
| |
| /* Unmask the DEPCA board interrupts and turn off the LED */ |
| nicsr = (nicsr & ~IM & ~LED); |
| outw(nicsr, DEPCA_NICSR); |
| |
| dev->interrupt = UNMASK_INTERRUPTS; |
| return; |
| } |
| |
| static int |
| depca_rx(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int entry = lp->cur_rx & lp->rmask; |
| |
| /* If we own the next entry, it's a new packet. Send it up. */ |
| for (; lp->rx_ring[entry].base >= 0; entry = (++lp->cur_rx) & lp->rmask) { |
| int status = lp->rx_ring[entry].base >> 16 ; |
| |
| if (status & R_ERR) { /* There was an error. */ |
| lp->stats.rx_errors++; /* Update the error stats. */ |
| if (status & R_FRAM) lp->stats.rx_frame_errors++; |
| if (status & R_OFLO) lp->stats.rx_over_errors++; |
| if (status & R_CRC) lp->stats.rx_crc_errors++; |
| if (status & R_BUFF) lp->stats.rx_fifo_errors++; |
| } else { /* Malloc up new buffer, compatible with net-2e. */ |
| short pkt_len = lp->rx_ring[entry].msg_length; |
| int sksize = sizeof(struct sk_buff) + pkt_len; |
| struct sk_buff *skb; |
| |
| skb = alloc_skb(sksize, GFP_ATOMIC); |
| if (skb == NULL) { |
| printk("%s: Memory squeeze, deferring packet.\n", dev->name); |
| lp->stats.rx_dropped++; /* Really, deferred. */ |
| break; |
| } |
| skb->mem_len = sksize; |
| skb->mem_addr = skb; |
| skb->len = pkt_len; |
| skb->dev = dev; |
| memcpy((unsigned char *)(skb + 1), |
| (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff), |
| pkt_len); |
| /* |
| ** Notify the upper protocol layers that there is another |
| ** packet to handle |
| */ |
| #ifdef HAVE_NETIF_RX |
| netif_rx(skb); |
| #else |
| skb->lock = 0; |
| if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) { |
| kfree_skbmem(skb, sksize); |
| lp->stats.rx_dropped++; |
| break; |
| } |
| #endif |
| lp->stats.rx_packets++; |
| } |
| |
| /* turn over ownership of the current entry back to the LANCE */ |
| lp->rx_ring[entry].base |= R_OWN; |
| } |
| |
| /* |
| ** We should check that at least two ring entries are free. If not, |
| ** we should free one and mark stats->rx_dropped++. |
| */ |
| |
| return 0; |
| } |
| |
| /* |
| ** Buffer sent - check for buffer errors. |
| */ |
| static int |
| depca_tx(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int dirty_tx = lp->dirty_tx & lp->rmask; |
| |
| if (depca_debug > 5) |
| printk("%s: Cleaning tx ring, dirty %d clean %d.\n", |
| dev->name, dirty_tx, (lp->cur_tx & lp->rmask)); |
| |
| /* |
| ** While the dirty entry is not the current one AND |
| ** the LANCE doesn't own it... |
| */ |
| for (; dirty_tx!=(lp->cur_tx & lp->rmask) && lp->tx_ring[dirty_tx].base>0; |
| dirty_tx = ++lp->dirty_tx & lp->rmask) { |
| unsigned long *tmdp = (unsigned long *)(&lp->tx_ring[dirty_tx]); |
| int status = lp->tx_ring[dirty_tx].base >> 16; |
| |
| if (status < 0) { /* Packet not yet sent! */ |
| printk("interrupt for packet not yet sent!\n"); |
| break; |
| } |
| if (status & T_ERR) { /* There was an major error, log it. */ |
| int err_status = lp->tx_ring[dirty_tx].misc; |
| |
| lp->stats.tx_errors++; |
| if (err_status & TMD3_RTRY) lp->stats.tx_aborted_errors++; |
| if (err_status & TMD3_LCAR) lp->stats.tx_carrier_errors++; |
| if (err_status & TMD3_LCOL) lp->stats.tx_window_errors++; |
| if (err_status & TMD3_UFLO) lp->stats.tx_fifo_errors++; |
| /* We should re-init() after the FIFO error. */ |
| } else if (status & (T_MORE | T_ONE)) { |
| lp->stats.collisions++; |
| } else { |
| lp->stats.tx_packets++; |
| } |
| |
| if (depca_debug > 5) |
| printk("%s: Tx done entry %d, %4.4lx %4.4lx %4.4lx %4.4lx.\n", |
| dev->name, dirty_tx, |
| tmdp[0], tmdp[1], tmdp[2], tmdp[3]); |
| } |
| /*mark_bh(INET_BH);*/ |
| return 0; |
| } |
| |
| static int |
| depca_close(struct device *dev) |
| { |
| int ioaddr = dev->base_addr; |
| |
| dev->start = 0; |
| dev->tbusy = 1; |
| |
| outw(CSR0, DEPCA_ADDR); |
| |
| if (depca_debug > 1) { |
| printk("%s: Shutting down ethercard, status was %2.2x.\n", |
| dev->name, inw(DEPCA_DATA)); |
| } |
| |
| /* |
| ** We stop the DEPCA here -- it occasionally polls |
| ** memory if we don't. |
| */ |
| outw(STOP, DEPCA_DATA); |
| |
| free_irq(dev->irq); |
| |
| irq2dev_map[dev->irq] = 0; |
| |
| return 0; |
| } |
| |
| static void LoadCSRs(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int ioaddr = dev->base_addr; |
| |
| outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ |
| outw((unsigned short)(unsigned long)&lp->init_block, DEPCA_DATA); |
| outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ |
| outw((unsigned short)((unsigned long)&lp->init_block >> 16), DEPCA_DATA); |
| outw(CSR3, DEPCA_ADDR); /* ALE control */ |
| outw(ACON, DEPCA_DATA); |
| outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ |
| } |
| |
| static int InitRestartDepca(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| int ioaddr = dev->base_addr; |
| int i, status=0; |
| |
| outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ |
| outw(INIT, DEPCA_DATA); /* initialize DEPCA */ |
| |
| /* wait for lance to complete initialisation */ |
| for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++); |
| |
| if (i!=100) { |
| /* clear IDON by writing a "1", enable interrupts and start lance */ |
| outw(IDON | INEA | STRT, DEPCA_DATA); |
| if (depca_debug > 2) { |
| printk("%s: DEPCA open after %d ticks, init block %#lx csr0 %4.4x.\n", |
| dev->name, i, (long) &lp->init_block, inw(DEPCA_DATA)); |
| } |
| } else { |
| status = -1; |
| printk("%s: DEPCA unopened after %d ticks, init block %#lx csr0 %4.4x.\n", |
| dev->name, i, (long) &lp->init_block, inw(DEPCA_DATA)); |
| } |
| |
| return status; |
| } |
| |
| static struct enet_statistics * |
| depca_get_stats(struct device *dev) |
| { |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| |
| /* Null body since there is no framing error counter */ |
| |
| return &lp->stats; |
| } |
| |
| #ifdef HAVE_MULTICAST |
| /* |
| ** Set or clear the multicast filter for this adaptor. |
| ** num_addrs == -1 Promiscuous mode, receive all packets |
| ** num_addrs == 0 Normal mode, clear multicast list |
| ** num_addrs > 0 Multicast mode, receive normal and MC packets, and do |
| ** best-effort filtering. |
| */ |
| static void |
| set_multicast_list(struct device *dev, int num_addrs, void *addrs) |
| { |
| short ioaddr = dev->base_addr; |
| struct depca_private *lp = (struct depca_private *)dev->priv; |
| |
| /* We take the simple way out and always enable promiscuous mode. */ |
| STOP_DEPCA; /* Temporarily stop the depca. */ |
| |
| lp->init_block.mode = PROM; /* Set promiscuous mode */ |
| if (num_addrs >= 0) { |
| short multicast_table[4]; |
| int i; |
| |
| SetMulticastFilter(num_addrs, (char *)addrs, (char *)multicast_table); |
| |
| /* We don't use the multicast table, but rely on upper-layer filtering. */ |
| memset(multicast_table, (num_addrs==0) ? 0 : -1, sizeof(multicast_table)); |
| |
| for (i = 0; i < 4; i++) { |
| lp->init_block.filter[i] = multicast_table[i]; |
| } |
| lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */ |
| } else { |
| lp->init_block.mode |= PROM; /* Set promiscuous mode */ |
| } |
| |
| outw(CSR0, DEPCA_ADDR); |
| outw(IDON|INEA|STRT, DEPCA_DATA); /* Resume normal operation. */ |
| } |
| |
| /* |
| ** Calculate the hash code and update the logical address filter |
| ** from a list of ethernet multicast addresses. |
| ** Derived from a 'C' program in the AMD data book: |
| ** "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", |
| ** Pub #17781, Rev. A, May 1993 |
| */ |
| static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table) |
| { |
| char j, ctrl, bit, octet, hashcode; |
| short int i; |
| long int CRC, poly = (long int) CRC_POLYNOMIAL; |
| |
| for (i=0;i<num_addrs;i++) { /* for each address in the list */ |
| if (((char) *(addrs+ETH_ALEN*i) & 0x01) == 1) {/* is multicast address? */ |
| CRC = (long int) 0xffffffff; /* init CRC for each address */ |
| for (octet=0;octet<ETH_ALEN;octet++) { /* for each address octet */ |
| for(j=0;j<8;j++) { /* process each address bit */ |
| bit = (((char)* (addrs+ETH_ALEN*i+octet)) >> j) & 0x01; |
| ctrl = ((CRC < 0) ? 1 : 0); /* shift the control bit */ |
| CRC <<= 1; /* shift the CRC */ |
| if (bit ^ ctrl) { /* (bit) XOR (control bit) */ |
| CRC ^= poly; /* (CRC) XOR (polynomial) */ |
| } |
| } |
| } |
| hashcode = (CRC & 0x00000001); /* hashcode is 6 LSb of CRC ... */ |
| for (j=0;j<5;j++) { /* ... in reverse order. */ |
| hashcode <<= 1; |
| CRC >>= 1; |
| hashcode |= (CRC & 0x00000001); |
| } |
| octet = hashcode >> 3; /* bit[3-5] -> octet in filter */ |
| /* bit[0-2] -> bit in octet */ |
| multicast_table[octet] |= (1 << (hashcode & 0x07)); |
| } |
| } |
| return; |
| } |
| |
| #endif /* HAVE_MULTICAST */ |
| |
| /* |
| ** Look for a particular board name in the on-board Remote Diagnostics |
| ** and Boot (RDB) ROM. This will also give us a clue to the network RAM |
| ** base address. |
| */ |
| static char *DepcaSignature(unsigned long mem_addr) |
| { |
| unsigned long i,j,k; |
| static char signatures[][DEPCA_NAME_LENGTH] = DEPCA_SIGNATURE; |
| static char thisName[DEPCA_NAME_LENGTH]; |
| char tmpstr[17]; |
| |
| for (i=0;i<16;i++) { /* copy the first 16 bytes of ROM to */ |
| tmpstr[i] = *(unsigned char *)(mem_addr+0xc000+i); /* a temporary string */ |
| } |
| tmpstr[i]=(char)NULL; |
| |
| strcpy(thisName,""); |
| for (i=0;*signatures[i]!=(char)NULL && *thisName==(char)NULL;i++) { |
| for (j=0,k=0;j<16 && k<strlen(signatures[i]);j++) { |
| if (signatures[i][k] == tmpstr[j]) { /* track signature */ |
| k++; |
| } else { /* lost signature; begin search again */ |
| k=0; |
| } |
| } |
| if (k == strlen(signatures[i])) { |
| strcpy(thisName,signatures[i]); |
| } |
| } |
| |
| return thisName; /* return the device name string */ |
| } |
| |
| /* |
| ** Look for a special sequence in the Ethernet station address PROM that |
| ** is common across all DEPCA products. |
| */ |
| |
| static int DevicePresent(short ioaddr) |
| { |
| static short fp=1,sigLength=0; |
| static char devSig[] = PROBE_SEQUENCE; |
| char data; |
| int i, j, status = 0; |
| static char asc2hex(char value); |
| |
| /* |
| ** Convert the ascii signature to a hex equivalent & pack in place |
| */ |
| if (fp) { /* only do this once!... */ |
| for (i=0,j=0;devSig[i]!=(char)NULL && !status;i+=2,j++) { |
| if ((devSig[i]=asc2hex(devSig[i]))>=0) { |
| devSig[i]<<=4; |
| if((devSig[i+1]=asc2hex(devSig[i+1]))>=0){ |
| devSig[j]=devSig[i]+devSig[i+1]; |
| } else { |
| status= -1; |
| } |
| } else { |
| status= -1; |
| } |
| } |
| sigLength=j; |
| fp = 0; |
| } |
| |
| /* |
| ** Search the Ethernet address ROM for the signature. Since the ROM address |
| ** counter can start at an arbitrary point, the search must include the entire |
| ** probe sequence length plus the length of the (signature - 1). |
| ** Stop the search IMMEDIATELY after the signature is found so that the |
| ** PROM address counter is correctly positioned at the start of the |
| ** ethernet address for later read out. |
| */ |
| if (!status) { |
| for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) { |
| data = inb(ioaddr); |
| if (devSig[j] == data) { /* track signature */ |
| j++; |
| } else { /* lost signature; begin search again */ |
| j=0; |
| } |
| } |
| |
| if (j!=sigLength) { |
| status = -ENODEV; /* search failed */ |
| } |
| } |
| |
| return status; |
| } |
| |
| static char asc2hex(char value) |
| { |
| value -= 0x30; /* normalise to 0..9 range */ |
| if (value >= 0) { |
| if (value > 9) { /* but may not be 10..15 */ |
| value &= 0x1f; /* make A..F & a..f be the same */ |
| value -= 0x07; /* normalise to 10..15 range */ |
| if ((value < 0x0a) || (value > 0x0f)) { /* if outside range then... */ |
| value = -1; /* ...signal error */ |
| } |
| } |
| } else { /* outside 0..9 range... */ |
| value = -1; /* ...signal error */ |
| } |
| return value; /* return hex char or error */ |
| } |
| |
| |
| /* |
| * Local variables: |
| * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c depca.c" |
| * End: |
| */ |
| |
| |
| |