--- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/Documentation/networking/ne3200.txt Fri Oct 31 01:39:47 2003 @@ -0,0 +1,60 @@ +Novell NE3200 Ethernet driver for Linux. + +Module options. +--------------- +irq=n1,n2,n3,... +Specifies which interrupt line is used by the board. 0 means autodetect, +which is the default. If you sset irq to something else than 0, you must +also set irq_type to something else than 0, otherwise autodetection is used. +See below about the configure_board option also. + +irq_type=n1,n2,n3,... +Specifies how the interrupt is triggered. 0 means autodetect, which is the +default. 1 means level triggered interrupt, which can be shared with other +devices. 2 means edge triggered interrupt. If you set irq_type to something +else than 0, you must also set irq to something else than 0, otherwise +autodetection is used. See below about the configure_board option also. + +media=n1,n2,n3,... +Specifies the media to use. 0 means unknown, which is the default. 1 means +10base-2 (BNC), 2 means 10base-T and 3 means 10base-5. This setting can not +be autodetected, but normally it doesn't matter; however, see below about +the configure_board option. + +configure_board=b1,b2,b3,... +Specifies whether to configure the board with irq, irq_type and media or +not. 0 means don't configure the board, which is the default. 1 means +configure the board. To configure the board, irq, irq_type and media must +be known, that is, either autodetected or specified using module parameters. +Additionally, the only valid values for irq are 5, 9, 10, 11 and 15. + +fw_timeout=n +reset_delay=n +See `modinfo ne3200`. Even I have never used these two options. + +debug=n +Specifies which types of debug messages to print. To get a list, use +`grep -e 'NETIF_MSG_.*= ' include/linux/netdevice.h`. Add the desired values +to get the number to use with this option. Note that NETIF_MSG_HW will give +lots of rarely useful output. + +extra_eisa_id=n +If you have a board which you think is NE3200 compatible and the driver +does not detect it because the EISA ID is not known to the driver, you can +use this option to specify the EISA ID of your board, which should appear in +the debug output with NETIF_MSG_PROBE enabled (see the debug option). + +Notes on interrupt line autodetection (irq and irq_type). +--------------------------------------------------------- +Autodetection of irq and irq_type is not possible if the interrupt line is +already in use (due to request_irq()) and may even hang the system. To +check if the interrupt line is already in use, see /proc/interrupts. In that +case, use the irq and irq_type options. + +Having multiple NE3200 boards on the same interrupt line should not prevent +autodetection because the driver does not call request_irq() until the board +is actually used when a level triggered interrupt is used. + +Other notes. +------------ +NE3200 boards must be installed into a busmaster capable slot. --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/82586.c Thu Oct 30 22:27:20 2003 @@ -0,0 +1,1743 @@ +/* 82586.c: A generic Intel 82586 driver module for linux. + * + * Written 2000, 2002-2003 by Rask Ingemann Lambertsen. + * + * Copyright 2000, 2002-2003 Rask Ingemann Lambertsen. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * The author may be reached as , or by + * by snail mail addressed to: + * + * Rask Ingemann Lambertsen + * Vesterleds Alle 17B + * DK-2605 Brøndby + * Denmark + * + * This ia a generic driver module for Ethernet boards with an Intel + * 82586 Ethernet chip, similiar to 8390.c for NS8390 based boards. + * + */ + +static const char *version = + "82586.c:v0.07 2003-10-30 Rask Ingemann Lambertsen \n"; + +/* + * Sources: + * + * Donald Becker's skeleton.c (now isa-skeleton.c) driver outline. + * + * The Intel 82596CA manual (Intel order number 290218-006). + * + * The other i82586 drivers for Linux: + * 3c507 by Donald Becker. + * 3c323 by Chris Beauregard. + * eexpress by John Sullivan. + * ni52 by Michael Hipp. + * wavelan by Bruce Janson. + * + * skb fragment handling based on net/core/skbuff.c/skb_copy_bits(). + * + */ + +/* + +Theory of operation. + +I. Board compatibility. + +This driver module is intended to support boards with an i82586 Ethernet +coprocessor. It should support any "simple" board with just an i82586 and +memory as well as to some extent be of use for more complex boards with an +onboard processor and custom firmware. +Note that currently, only 16-bit boards are supported because it is assumed +that atomic 16-bit reads and writes are available. + +II. Board specific settings. + +This driver module does not deal with board specific settings. It is +completely up to board specific code to determine how much memory should be +set aside for e.g. receive memory and transmit memory. + +III. Driver operation. + +IIIa. Receive buffers. + +The driver module allocates as many receive frame descritors, receive buffer +descriptors and 190-byte receive buffers as possible in the receive memory +area. Small receive buffers are used to reduce the amount of wasted buffer +space when receiving small frames. The receive buffers form a contiguous +memory area, so frames spanning multiple buffers are not fragmented except +when wrapping at the end of the receive memory area. + +When kernel memory runs out, incoming frames are kept on the board for later +processing if the 82586 hasn't run out of buffers. + +IIIb. Transmit buffers. + +The driver module dynamically allocates space for transmit commands, transmit +buffer descriptors and transmit buffers in the transmit memory area. Other +commands (multicast, configuration etc.) are also placed here when needed. +Just as in the other 82586 drivers, each command is followed by a busy-wait NOP +to avoid the time consuming restarts of the 82586 Command Unit. + +IIIc. Syncronisation. + +The transmit buffers are accessed from multiple sources and contexts +(user context for configuration, interrupt context when (transmit) commands +finish and unknown context when transmitting packets) so access is single +threaded using b->tx_lock. + +The receive buffers are only accessed by the interrupt handler, which is +single threaded by the hardware and other software. + +IIId. Device features. + +The 82586 supports automatic padding of short frames and hardware checksumming +at the MAC level, and these features are enabled, although padding of short fra- +mes doesn't work for Ethernet frames. Additionally, the driver module supports +scatter-gather of transmitted packets (NETIF_F_SG, but currently not +NETIF_F_FRAGLIST). +The driver module hacks an all-multicast mode, which the 82586 doesn't support +directly, using the 82586's multicast filter. +Jumbo frames (including VLAN frames) are supported to the extent that board +memory permits. The maximum MTU is 16365 bytes. + +TODO: + Split TX packets into two when wrapping around at the end of txmem. + Support socket buffer fragment lists. + Allow the board specific code to configure more 82586 settings. + Timer for SCB statistics and deferred frames. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "82586.h" + +/* Unfortunately, the definition of the SCP in i82586.h is broken because the + * first word is missing. So here comes a workaround: + */ +#define scp_t broken_scp_t +#if LINUX_VERSION_CODE < KERNEL_VERSION (2,5,3) +#include "i82586.h" +#else +#include "wireless/i82586.h" +#endif +#undef scp_t + +typedef struct scp_t +{ + unsigned short zero; + unsigned short sysbus; + unsigned short junk[2]; + unsigned short iscp_basel; + unsigned short iscp_baseh; +} scp_t; + +/* Receive Frame Descriptor. Like fd_t in i82586.h but without the unused + * fd_dest, fd_source and fd_length fields. + */ +typedef struct rfd_t +{ + unsigned short rfd_status; + unsigned short rfd_command; + unsigned short rfd_link_offset; + unsigned short rfd_rbd_offset; +} rfd_t; + +#define tbd_length tbd_status +#define TBD_LENGTH_EOF TBD_STATUS_EOF +#define TBD_LENGTH(x) ((x) & TBD_STATUS_ACNT) + +/* Missing from i82586.h for some odd reason. */ +#define AC_SFLD_S11 (1 << 11) + +/* Calculate the offset of the field f in the structure t. */ +#define offset(t,f) ( ((char *) &( ((t *) 0) ->f) ) - (char *) 0) + +#define rfdadr(r,f) ((r) + offset (rfd_t, f)) +#define rbdadr(r,f) ((r) + offset (rbd_t, f)) +#define scbbadr(s,f) ((s) + offset (scb_block_t, f)) +#define scbadr(s,f) ((s) + offset (scb_t, f)) +#define sacadr(s,f) ((s) + offset (struct sa_cmd_t, f)) +#define cfgadr(s,f) ((s) + offset (struct init_cfg_t, f)) +#define cmdadr(s,f) ((s) + offset (ach_t, f)) +#define mcsadr(s,f) ((s) + offset (ac_mcs_t, f)) + +/* The number of bytes in each RX buffer. This value is chosen so that an + * 8 kB board can contain the SCB block, and either two full size TX packets + * and three full size Rx packets or one full size Tx packet and four full + * size Rx packets. It must be less than 256 bytes (see find_last_rbd()). + */ +#define RXBUF_SIZE 190 + +/* Used to lock access to init_scp, init_iscp and init_scb. */ +static spinlock_t init_lock = SPIN_LOCK_UNLOCKED; + +/* System Configuration Pointer (SCP). */ +static struct scp_t init_scp = { + 0, /* Pad word. */ + cpu_to_le16 (SCP_SY_16BBUS), /* Set bus size to 16 bits. */ + { 0, 0 }, /* Pad words. */ + 0x0000, 0x0000, /* ISCP phys addr, set in i82586_init(). */ +}; + +/* Intermediate System Configuration Pointer (ISCP). */ +static struct iscp_t init_iscp = { + cpu_to_le16 (1),/* Status word that's cleared when init is done. */ + 0x0000,0,0 /* SCB offset, (skip, skip) */ +}; + +/* This structure forms the setup program for the 82586. The NOP is + * used to busy loop the Command Unit when there are no frames to transmit. + */ +typedef struct scb_block_t scb_block_t; +static struct scb_block_t { + scb_t scb; /* 16 bytes. */ + ac_nop_t nop; /* 6 bytes. */ +} init_scb = { +{ + /* System Control Block (SCB). */ + 0, /* SCB status. */ + 0, /* SCB command. */ + 0xdead, /* Command list pointer. */ + 0xbeef, /* Rx block list. */ + 0,0,0,0 /* Error count: CRC, align, buffer, overrun. */ +},{ + /* NOP, looping back to itself. Point to first Tx buffer to Tx. */ + { 0, cpu_to_le16 (acmd_nop), 0xf00d } +} +}; + +static void i82586_reap_commands (struct i82586_board *b); + +/* Locking of the TX/cmd ring. The standard spinlocks can't be used because + * they would need to disable interrupts, including the timer interrupt which + * the ne3200 code needs. If a TX interrupt occurs while the lock is held, it + * is up to the lock owner to clean up after the TX path. Note that + * i82586_tx_lock_outside_interrupt() might livelock if calls are not serialised + * using e.g. b->dev->xmit_lock. What is needed here is a function such as + * atomic_test_and_inc_if_negative(). + */ +static __inline__ void i82586_tx_lock_outside_interrupt (struct i82586_board *b) +{ + while (! atomic_inc_and_test (& b->tx_lock)) + atomic_dec (& b->tx_lock); +} + +static __inline__ void i82586_tx_unlock_outside_interrupt (struct i82586_board *b) +{ + while (! atomic_add_negative (-1, & b->tx_lock)) { + i82586_reap_commands (b); + } +} + +static __inline__ int i82586_tx_trylock_inside_interrupt (struct i82586_board *b) +{ + return (atomic_inc_and_test (& b->tx_lock)); +} + +static __inline__ void i82586_tx_unlock_inside_interrupt (struct i82586_board *b) +{ + atomic_dec (& b->tx_lock); +} + +static __inline__ void print_tx (struct i82586_board *b) +{ + printk (KERN_DEBUG "%s: TX: start 0x%04x tail 0x%04x reap 0x%04x " + "link 0x%04x head 0x%04x end 0x%04x.\n", b->dev->name, + b->txmem_start, b->tx_tail, b->tx_reap, b->tx_cmd_link, + b->tx_head, b->txmem_start + b->txmem_size); +} + +/* The Tx-block list is written as needed. We just set up the values. The + * tx_lock must be held. */ +static __inline__ void i82586_init_txmem (struct i82586_board *b) +{ + b->tx_cmd_link = scbbadr (b->scb_addr, nop.nop_h.ac_link); + b->tx_head = b->tx_tail = b->txmem_start; + b->tx_reap = (uint) -1; + if (netif_msg_tx_done (b)) + print_tx (b); +} + +/* Initialise the 82586. This triggers an interrupt from the 82586. + * It is permissible to use this function for IRQ autodetection if b->reset() + * is called afterwards. + * FIXME The initial value of scb.command was bogus since the 82586 clears + * it on startup. Likewise, the boguscount loop looks suspicious (dubious + * expectations of startup behaviour due to scb.command misunderstanding). + */ +EXPORT_SYMBOL (i82586_init); +int i82586_init (struct i82586_board *b) +{ + struct net_device *dev = b->dev; + board_handle h = b->handle; + unsigned int boguscnt; + + /* Reset the 82586 before memory initialization. */ + b->reset (dev); + b->configured = 0; + + spin_lock (&init_lock); + + /* Write the System Configuration Pointer. */ + init_scp.iscp_basel = cpu_to_le16 (b->iscp_addr & 0xffff); + init_scp.iscp_baseh = cpu_to_le16 (b->iscp_addr >> 16); + b->copy_toboard (h, &init_scp, b->scp_addr, sizeof (scp_t)); + + /* Write the Intermediate System Configuration Pointer. */ + init_iscp.iscp_offset = cpu_to_le16 (b->scb_addr & 0xffff); + init_iscp.iscp_baseh = cpu_to_le16 (b->scb_addr >> 16); + b->copy_toboard (h, &init_iscp, b->iscp_addr, sizeof (iscp_t)); + + /* Relocate the SCB and command structures. */ + init_scb.nop.nop_h.ac_link = cpu_to_le16 (scbbadr (b->scb_addr, nop)); + + /* Upload it all to the board. */ + b->copy_toboard (h, &init_scb, b->scb_addr, sizeof (scb_block_t)); + spin_unlock (&init_lock); + + /* This was time consuming to track down: you need to give two channel + attention signals to reliably start up the i82586. */ + b->ca (dev); + + boguscnt = 50; + while (b->reada (h, scbbadr (b->scb_addr, scb.scb_status)) == 0) + if (--boguscnt == 0) { + printk (KERN_WARNING "%s: i82586 initialization timed out " + "with status 0x%04hx, cmd 0x%04hx.\n", dev->name, + b->reada (h, scbbadr (b->scb_addr, scb.scb_status)), + b->reada (h, scbbadr (b->scb_addr, scb.scb_command))); + + /* Issue channel-attn -- the 82586 won't start. */ + b->ca (dev); + break; + } + return (0); +} + +/* Find the previous RFD in the RFD list. */ +static __inline__ unsigned short rfd_pred (struct i82586_board *b, uint rfd) +{ + if (rfd == b->rx_first_rfd) + return (b->rx_last_rfd); + else + return (rfd - sizeof (rfd_t)); +} + +/* Initialise the Receive Frame Area (RFA). The Receive Unit is assumed to be + * idle or suspended when this function is called. + * This is the layout of the RFA (with rxmem_start to the left): + * + * first last first last first last + * RBD ... RBD RFD ... RFD buffer ... buffer + * head tail head tail head tail + * + */ +static void i82586_init_rxmem (struct i82586_board *b) +{ + board_handle h = b->handle; + uint rfd, rbd, buf; + + /* First, we write the Receive Buffer Descriptors. */ + for (rbd = b->rx_first_rbd, buf = b->rx_first_buffer; + rbd <= b->rx_last_rbd; rbd += sizeof (rbd_t), buf += RXBUF_SIZE) + { + rbd_t temp = { + rbd_status: 0, + rbd_next_rbd_offset: cpu_to_le16 (rbd + sizeof (rbd_t)), + rbd_bufl: cpu_to_le16 (buf & 0xffff), + rbd_bufh: cpu_to_le16 (buf >> 16), + rbd_el_size: cpu_to_le16 (RXBUF_SIZE) + }; + b->copy_toboard (h, &temp, rbd, sizeof (temp)); + } + + /* Second, we write the Receive Frame Descriptors. */ + for (rfd = b->rx_first_rfd; rfd <= b->rx_last_rfd; rfd += sizeof (rfd_t)) { + rfd_t temp = { + rfd_status: 0, + rfd_command: 0, + rfd_link_offset: cpu_to_le16 (rfd + sizeof (rfd_t)), + rfd_rbd_offset: I82586NULL + }; + b->copy_toboard (h, &temp, rfd, sizeof (temp)); + } + + /* Set the head RBD. */ + b->rx_bhead = b->rx_first_rbd; + + /* Fix up the last RBD to complete the ring. */ + rbd -= sizeof (rbd_t); + b->writew (h, b->rx_first_rbd, rbdadr (rbd, rbd_next_rbd_offset)); + + /* Fix up the tail RBD to end the list. */ + b->rx_btail = rbd; + b->writew (h, RBD_EL | RXBUF_SIZE, rbdadr (rbd, rbd_el_size)); + + /* Fix up the last RFD to complete the ring. */ + rfd -= sizeof (rfd_t); + b->writew (h, b->rx_first_rfd, rfdadr (rfd, rfd_link_offset)); + + /* Fix up the head RFD to point to the head RBD. */ + b->rx_fhead = b->rx_first_rfd; + b->writew (h, b->rx_bhead, rfdadr (b->rx_fhead, rfd_rbd_offset)); + + /* Fix up the tail RFD to end the list. */ + b->rx_ftail = b->rx_last_rfd; + b->writew (h, FD_COMMAND_EL, rfdadr (b->rx_ftail, rfd_command)); +} + +/* Try to find "size" bytes of available space on the TX list. Returns + * -1 on failure. Must be called with b->tx_lock held. + * TODO: A transmit command can be split into two pieces to fit. + */ +static uint i82586_cmdspace (struct i82586_board *b, uint size) +{ + uint cmd = (uint) -1; + uint txmem_end = b->txmem_start + b->txmem_size; + + if (unlikely (b->tx_tail == (uint) -1)) + cmd = (uint) -1; + + /* List hasn't wrapped around. */ + else if (b->tx_tail <= b->tx_head) { + /* txmem_start tx_tail tx_head txmem_end */ + /* < .... FREE .... >< .. USED .. >< ... FREE ...> */ + /* Try between tx_head and txmem_end. */ + if (txmem_end - b->tx_head >= size) + cmd = b->tx_head; + /* Try between txmem_start and tx_tail. */ + else if (b->tx_tail - b->txmem_start >= size) + cmd = b->txmem_start; + } + /* List has wrapped around. */ + else /* tx_head < tx_tail */ { + /* txmem_start tx_head tx_tail txmem_end */ + /* < .... USED .... >< .. FREE .. >< ... USED ... > */ + /* Try between tx_head and tx_tail. */ + if (b->tx_tail - b->tx_head >= size) + cmd = b->tx_head; + } + return (cmd); +} + +/* This is the structure used to send packets. */ +typedef struct { + ac_tx_t tx; + ac_nop_t nop; + tbd_t tbd; +} txblock_t; +#define txbadr(s,m) ((s) + offset (txblock_t, m)) +#define MAX_TX_CMD_SIZE(b) (sizeof (txblock_t) + VLAN_ETH_HLEN + (b)->mtu) + +/* Link a command into the TX list. This causes the command to be executed + * when the Command Unit gets around to it. b->tx_lock must be held. */ +static __inline__ +void i82586_add_command (struct i82586_board *b, uint cmd, uint cmdsize, + uint new_cmd_link) +{ + b->tx_head = cmd + cmdsize; + /* The new command might have filled up the list. */ + if (unlikely (b->tx_head == b->tx_tail)) + b->tx_tail = (uint) -1; + if (b->tx_reap == (uint) -1) + b->tx_reap = cmd; + b->writea (b->handle, cmd, b->tx_cmd_link); + b->tx_cmd_link = new_cmd_link; + if (netif_msg_tx_queued (b)) + print_tx (b); + + /* Linux NET brain damage: Stop the upper layers from transmitting + * even small packets if a full size packet can't be sent. */ + if ((uint) -1 == i82586_cmdspace (b, MAX_TX_CMD_SIZE (b))) + netif_stop_queue (b->dev); +} + +/* Copy a packet from an skb to board memory. */ +__inline__ +static void i82586_skb_toboard (struct i82586_board *b, struct sk_buff *skb, + u8 *trailer, uint trailersize, uint txbuf_ptr) +{ + board_handle h = b->handle; + skb_frag_t *frag; + u8 *frag_data; + uint fragnr; + + if (skb_headlen (skb) > 0) { + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Buffer fragment -1 of %5hu " + "bytes at 0x%p to %#04x.\n", b->dev->name, + skb_headlen (skb), skb->data, txbuf_ptr); + b->copy_toboard (h, skb->data, txbuf_ptr, skb_headlen (skb)); + txbuf_ptr += skb_headlen (skb); + } + for (fragnr = 0; + fragnr < skb_shinfo (skb)->nr_frags; + fragnr ++, txbuf_ptr += frag->size) + { + frag = &skb_shinfo (skb)->frags[fragnr]; + frag_data = kmap_skb_frag (frag) + frag->page_offset; + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Buffer fragment %2u of %5hu " + "bytes at 0x%p to %#04x.\n", b->dev->name, + fragnr, frag->size, frag_data, txbuf_ptr); + b->copy_toboard (h, frag_data, txbuf_ptr, frag->size); + kunmap_skb_frag (frag_data - frag->page_offset); + } + b->copy_toboard (h, trailer, txbuf_ptr, trailersize); +} + +/* Transmit a packet. This is never called in an interrupt context. + * If something goes wrong, 1 is returned and the skb not touched + * because the network queueing layer still owns it then. */ +/* The packet data is placed before the txblock_t structure so that it can be + * provide up to 22 bytes of padding for short packets, which is often enough. + * For really short packets with less than 24 (60 - 14 - 22) bytes of payload, + * more padding is added in front of the txblock, but this will be rare since + * an IPv4 header alone is 20 bytes, and usually followed by a UDP header of + * 8 bytes or a TCP header of 20 bytes. + * The whole padding business is complicated by several restrictions: + * 1) All addresses given to the 82586 must be word aligned. This includes + * commands, buffer descriptors and even buffers (i.e. packet data). + * 2) Security sensitive data must not be leaked (EtherLeak bug). + * 3) Keep it fast and preferably simple. + */ +EXPORT_SYMBOL (i82586_send_packet); +int i82586_send_packet (struct i82586_board *b, struct sk_buff *skb) +{ + board_handle h = b->handle; + txblock_t *txblock; + u8 *txtrailer; + unsigned int frame_len, pad_len; + uint txcmd, txcmd_size, txbuf_ptr; + + /* Pad short frames. We should try to spot ETH_P_802_3 frames and + * leave the padding of them to the 82586 (if possible). + */ + if (skb->len < ETH_ZLEN) { + uint total_pad_len = ETH_ZLEN - skb->len; + frame_len = ETH_ZLEN; + if (unlikely (total_pad_len > sizeof (txblock_t))) + pad_len = total_pad_len - sizeof (txblock_t); + else + pad_len = skb->len & 1; /* TX command alignment. */ + } else { + frame_len = skb->len; + pad_len = skb->len & 1; /* TX command alignment. */ + } + txtrailer = & b->tx_trailer.pad[ETH_ZLEN - sizeof (txblock_t) - pad_len]; + txblock = (txblock_t *) (& b->tx_trailer.cmd); + txblock->tbd.tbd_length = cpu_to_le16 (frame_len + TBD_LENGTH_EOF); + + txcmd_size = sizeof (txblock_t) + pad_len + skb->len; + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Transmit %5u byte packet (%5u byte " + "cmd, %5u byte frame, %2u byte padding).\n", + b->dev->name, skb->len, txcmd_size, frame_len, pad_len); + + i82586_tx_lock_outside_interrupt (b); + txbuf_ptr = i82586_cmdspace (b, txcmd_size); + + /* This should not happen, but just in case it does: */ + if (unlikely (txbuf_ptr == (uint) -1)) { + netif_stop_queue (b->dev); + printk (KERN_WARNING "%s: i82586_send_packet() out of space " + "(%u byte packet, %u byte command)!\n", b->dev->name, + skb->len, txcmd_size); + print_tx (b); + i82586_tx_unlock_outside_interrupt (b); + return (1); + } + + txcmd = txbuf_ptr + pad_len + skb->len; + txblock->tx.tx_h.ac_link = cpu_to_le16 (txbadr (txcmd, nop)); + txblock->nop.nop_h.ac_link = cpu_to_le16 (txbadr (txcmd, nop)); + txblock->tx.tx_tbd_offset = cpu_to_le16 (txbadr (txcmd, tbd)); + txblock->tbd.tbd_bufl = cpu_to_le16 (txbuf_ptr); + +#if 0 + printk (KERN_DEBUG "%s: skb 0x%p len %4u data_len %4u.\n", + b->dev->name, skb, skb->len, skb->data_len); +#endif + if (b->skb_toboard) + b->skb_toboard (h, skb, txtrailer, + sizeof (txblock_t) + pad_len, txbuf_ptr); + else + i82586_skb_toboard (b, skb, txtrailer, + sizeof (txblock_t) + pad_len, txbuf_ptr); + + i82586_add_command (b, txcmd, sizeof (txblock_t), + txbadr (txcmd, nop.nop_h.ac_link)); + b->dev->trans_start = jiffies; + + i82586_tx_unlock_outside_interrupt (b); + b->stats.tx_bytes += skb->len; /* FIXME What about the checksum? */ + b->stats.tx_packets ++; + dev_kfree_skb (skb); + return (0); +} + +/* Set the station address to dev->addr. */ +EXPORT_SYMBOL (i82586_set_station_address); +int i82586_set_station_address (struct i82586_board *b) +{ + uint sa; + struct sa_cmd_t { + ac_ias_t set_sa; + ac_nop_t nop; + } sa_cmd = { + { + { 0, cpu_to_le16 (acmd_ia_setup | AC_CFLD_I) }, + { }, + }, { + { 0, cpu_to_le16 (acmd_nop) } + } + }; + + memcpy (sa_cmd.set_sa.ias_addr, b->dev->dev_addr, ADDR_LEN); + + i82586_tx_lock_outside_interrupt (b); + sa = i82586_cmdspace (b, sizeof (sa_cmd)); + if (sa == (uint) -1) { + i82586_tx_unlock_outside_interrupt (b); + return (-ENOMEM); + } + + /* Prepare, relocate and upload station address command. */ + sa_cmd.set_sa.ias_h.ac_link = cpu_to_le16 (sacadr (sa, nop)); + sa_cmd.nop.nop_h.ac_link = cpu_to_le16 (sacadr (sa, nop)); + b->copy_toboard (b->handle, &sa_cmd, sa, sizeof (sa_cmd)); + + /* Link the station address command into the TX list. */ + i82586_add_command (b, sa, sizeof (sa_cmd), + sacadr (sa, nop.nop_h.ac_link)); + + i82586_tx_unlock_outside_interrupt (b); + return (0); +} + +/* Upload multicast addresses to the 82586. + * If IFF_ALLMULTI is not set in b->dev->flags, then b->dev->mc_count + * addresses are loaded from b->dev->mc_list. + * If IFF_ALLMULTI is set in b->dev->flags, then we overload the 64-bit hash + * address filter of the 82586 with 64 addresses from 1:0:0:0:0:0 to + * 1:0:0:0:0:3f, causing all addresses to match. The 82586 is fortunately + * smart enough to filter out unicast addresses, letting just multicast + * addresses through. */ +EXPORT_SYMBOL (i82586_set_multicast_list); +int i82586_set_multicast_list (struct i82586_board *b) +{ + board_handle h = b->handle; + uint cmd_size, cmd_ptr, nop_ptr, mcdest; + uint mcbytes = ((b->dev->flags & IFF_ALLMULTI) ? 64 : b->dev->mc_count) + * ADDR_LEN; + + ac_mcs_t smc_cmd = { + mcs_h: { ac_status: 0, + ac_command: cpu_to_le16 (acmd_mc_setup | AC_CFLD_I) }, + mcs_cnt: cpu_to_le16 (mcbytes) + }; + ac_nop_t nop_cmd = { + nop_h: { ac_status: 0, + ac_command: cpu_to_le16 (acmd_nop) } + }; + + cmd_size = sizeof (ac_mcs_t) + mcbytes + sizeof (ac_nop_t); + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Multicast cmd with %d addresses (%u " + "bytes).\n", b->dev->name, mcbytes / ADDR_LEN, cmd_size); + i82586_tx_lock_outside_interrupt (b); + cmd_ptr = i82586_cmdspace (b, cmd_size); + if (cmd_ptr == (uint) -1) { + i82586_tx_unlock_outside_interrupt (b); + return (-ENOMEM); + } + + /* Relocate the command and upload. */ + mcdest = cmd_ptr + sizeof (smc_cmd); + nop_ptr = mcdest + mcbytes; + smc_cmd.mcs_h.ac_link = cpu_to_le16 (nop_ptr); + nop_cmd.nop_h.ac_link = cpu_to_le16 (nop_ptr); + b->copy_toboard (h, &smc_cmd, cmd_ptr, sizeof (smc_cmd)); + if (b->dev->flags & IFF_ALLMULTI) { + uint i; + u8 addr[ADDR_LEN] = { 1, 0, 0, 0, 0 }; + + for (i = 0; i < 64; i ++, mcdest += ADDR_LEN) { + addr[ADDR_LEN - 1] = i; + b->copy_toboard (h, addr, mcdest, ADDR_LEN); + } + } else { + struct dev_mc_list *mcentry; + uint i; + for (i = 0, mcentry = b->dev->mc_list; + i < b->dev->mc_count && mcentry; + i ++, mcentry = mcentry->next, mcdest += ADDR_LEN) + { + u8 *a = mcentry->dmi_addr; + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Adding multicast address" + " %02x:%02x:%02x:%02x:%02x:%02x.\n", + b->dev->name, + a[0], a[1], a[2], a[3], a[4], a[5]); + b->copy_toboard (h, mcentry->dmi_addr, mcdest, ADDR_LEN); + } + } + b->copy_toboard (h, &nop_cmd, nop_ptr, sizeof (nop_cmd)); + + i82586_add_command (b, cmd_ptr, cmd_size, nop_ptr + + offset (ac_nop_t, nop_h.ac_link)); + + i82586_tx_unlock_outside_interrupt (b); + return (0); +} + +/* Access to this structure is protected by i82586_board->tx_lock. */ +static struct init_cfg_t { + ac_cfg_t c_cfg; + ac_nop_t c_nop; +} init_cfg ={ +{ + /* Configure command, for which 11 config bytes are defined. If you + * specify fewer config bytes, sensible defaults are assumed for the + * rest. Too bad that the promiscuous bit and the padding bit is all + * the way down in byte 8, with the only other non-default being + * AC_CFG_ALOC in byte 3. + */ + { + 0, /* Status. */ + cpu_to_le16 (acmd_configure | AC_CFLD_I),/* Interrupt after cfg. */ + 0xabad /* Offset of next command. */ + }, + /* Config byte 0. */ + AC_CFG_BYTE_CNT (9), /* 9 bytes of config data. */ + + /* Config byte 1. */ + AC_CFG_FIFOLIM (8), /* FIFO threshold at half full. */ + + /* Config byte 2. */ + AC_CFG_SAV_BF (0) | /* Don't save bad frames. */ + AC_CFG_SRDY (1), /* Use external sync. */ + + /* Config byte 3. */ + AC_CFG_ELPBCK (0) | /* External loopback disabled. */ + AC_CFG_ILPBCK (0) | /* Internal loopback disabled. */ + AC_CFG_PRELEN (AC_CFG_PLEN_8) | /* 8 byte preamble. */ + AC_CFG_ALOC (1) | /* Addr/length field is part of the frame. */ + AC_CFG_ADDRLEN (ADDR_LEN), /* Standard Ethernet address length. */ + + /* Config byte 4. */ + AC_CFG_LINPRIO (0) | /* Standard 802.3 algorithm. */ + AC_CFG_ACR (0) | /* Standard 802.3 algorithm. */ + AC_CFG_BOFMET (0), /* Standard 802.3 algorithm. */ + + /* Config byte 5. */ + 96 /* bit times */, /* Interframe spacing. */ + + /* Config byte 6. */ + 512 & 0xff, /* Slot time (in bit times). */ + + /* Config byte 7. */ + AC_CFG_SLTTMHI (512 >> 8) | /* Slot time (in bit times). */ + AC_CFG_RETRYNUM (15), /* Retries on collision (default). */ + + /* Config byte 8. */ + AC_CFG_PRM (0) | /* Promiscuous mode disabled. */ + AC_CFG_BCDIS (0) | /* Broadcast reception enabled. */ + AC_CFG_MANCH (0) | /* Manchester coding disabled. */ + AC_CFG_TNCRS (0) | /* Don't transmit if no carrier. */ + AC_CFG_NCRC (0) | /* Append CRC to xmitted frames. */ + AC_CFG_CRC16 (0) | /* Use 32-bit CRC. */ + AC_CFG_BTSTF (0) | /* Disable bitstuffing. */ + AC_CFG_FLGPAD (1) /* Enable padding of short frames. */ +},{ + { + 0, /* Status. */ + cpu_to_le16 (acmd_nop), /* Command (NOP). */ + 0xfade /* Offset of next command. */ + } +}}; + +/* Configure the 82586 according to b->dev->flags etc. */ +EXPORT_SYMBOL (i82586_configure); +int i82586_configure (struct i82586_board *b) +{ + uint cfg; + + if (netif_msg_tx_queued (b)) + printk (KERN_DEBUG "%s: Configure cmd (%u " + "bytes).\n", b->dev->name, sizeof (init_cfg)); + + /* Find enough space for a configure command. */ + i82586_tx_lock_outside_interrupt (b); + cfg = i82586_cmdspace (b, sizeof (init_cfg)); + if (cfg == (uint) -1) { + i82586_tx_unlock_outside_interrupt (b); + return (-ENOMEM); + } + + /* Prepare, relocate and upload configure command. */ + if (b->dev->flags & IFF_PROMISC) + init_cfg.c_cfg.cfg_byte14 |= AC_CFG_PRM (1); + else + init_cfg.c_cfg.cfg_byte14 &= ~AC_CFG_PRM (1); + init_cfg.c_cfg.cfg_h.ac_link = cpu_to_le16 (cfgadr (cfg, c_nop)); + init_cfg.c_nop.nop_h.ac_link = cpu_to_le16 (cfgadr (cfg, c_nop)); + b->copy_toboard (b->handle, &init_cfg, cfg, sizeof (init_cfg)); + + /* Link the configure command into the TX list. */ + i82586_add_command (b, cfg, sizeof (init_cfg), + cfgadr (cfg, c_nop.nop_h.ac_link)); + + i82586_tx_unlock_outside_interrupt (b); + return (0); +} + +/* Configure the i82586 receive mode according to dev->flags, dev->mc_count + * and dev->mc_list. */ +EXPORT_SYMBOL (i82586_set_rx_mode); +void i82586_set_rx_mode (struct i82586_board *b) +{ + struct net_device *dev = b->dev; + + if (dev->flags & IFF_PROMISC) { + /* Enable promiscuous mode. Presumably there's no need to + * reset the multicast list in this case. */ + printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", + b->dev->name); + i82586_configure (b); + } else if (dev->flags & IFF_ALLMULTI) { + /* The 82586 doesn't have a mode in which all multicasts are + * received, but i82586_set_multicast_list() contains a hack + * which implements IFF_ALLMULTI. */ + i82586_set_multicast_list (b); + i82586_configure (b); + } else if (dev->mc_count) { + /* Walk the address list, and load the filter, regardless of + * how many addresses are on the list. We load the address + * list before possibly disabling promiscuous mode to avoid + * rejecting packets we should be receiving. + * FIXME: Check for lack of space for address list. */ + i82586_set_multicast_list (b); + i82586_configure (b); + } else { + /* Reconfigure before resetting the multicast list, because + * we want to possibly disable promiscuous mode as soon as + * possible to preserve receive buffer space. */ + i82586_configure (b); + i82586_set_multicast_list (b); + } +} + +int i82586_allocate_memory (struct i82586_board *b, uint mtu, uint check_only); + +/* This function is called by the board's dev->open() function. */ +EXPORT_SYMBOL (i82586_open); +void i82586_open (struct i82586_board *b) +{ + /* Because the RX and TX buffer format differ from the default, the + * receive unit must not be started and packets must not be transmitted + * before the configure command has run. The interrupt handler will not + * start the receive unit until it sees a completed configure command. + * The command unit wll be started shortly after i82586_init() returns. + * So the order of initialisation is important. + */ + i82586_allocate_memory (b, b->dev->mtu, 0); + i82586_init_txmem (b); + i82586_init (b); + i82586_init_rxmem (b); + i82586_configure (b); + i82586_set_station_address (b); + i82586_set_multicast_list (b); + netif_start_queue (b->dev); +} + +/* This function is called by the board's dev->close() function. */ +EXPORT_SYMBOL (i82586_close); +void i82586_close (struct i82586_board *b) +{ + netif_stop_queue (b->dev); + b->reset (b->dev); + b->configured = 0; + i82586_update_stats (b); +} + +/* Process a finished transmit command. This includes updates of the stats. + * TODO: Look into the possibility of rerunning (some) of the aborted transmit + * commands instead of relying on a higher layer to time out and retransmit. + * The 8390 module does this, for example. + */ +static void i82586_reap_tx (struct i82586_board *b, uint cmd_ptr, uint status) +{ + /* Skip over uninterresting frames. Note: Inverted heartbeat flag. */ + status &= (AC_SFLD_MAXCOL | AC_SFLD_S5 | AC_SFLD_S6 | AC_SFLD_S8 | + AC_SFLD_S9 | AC_SFLD_S10 | AC_SFLD_S11 | AC_SFLD_A); + status ^= AC_SFLD_S6; + if (status == 0) + return; + + if (netif_msg_tx_err (b) && status & ~AC_SFLD_MAXCOL) + printk (KERN_DEBUG "%s: TX error:", b->dev->name); + + /* 1-15 collisions, but successful transmission. */ + if ((status & AC_SFLD_MAXCOL) > 0) + b->stats.collisions += status & AC_SFLD_MAXCOL; + + /* 16 collisions and transmission failed. */ + else if (status & AC_SFLD_S5) { + b->stats.collisions += 16; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" 16 collisions"); + } + /* Missing heartbeat errors. Transmission successful (I think). */ + if (status & AC_SFLD_S6) { + b->stats.tx_heartbeat_errors ++; + if (netif_msg_tx_err (b)) + printk (" heartbeat"); + } + /* Transmission deferred because of link traffic, but successful. */ + if (status & AC_SFLD_S7) { + /* There's no stats field for deferrals. */ + } + /* FIFO underrun due to slow DMA. Transmission unsuccessful. */ + if (status & AC_SFLD_S8) { + b->stats.tx_fifo_errors ++; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" FIFO underrun"); + } + /* CTS lost during transmission. Transmission unsuccessful. */ + if (status & AC_SFLD_S9) { + b->stats.tx_carrier_errors ++; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" lost CTS"); + } + /* Carrier lost during transmission. Transmission unsuccessful. */ + if (status & AC_SFLD_S10) { + b->stats.tx_carrier_errors ++; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" lost carrier"); + } + /* Collision after the first 512 bits (64 bytes) were transmitted. */ + if (status & AC_SFLD_S11) { + b->stats.tx_window_errors ++; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" late collision"); + } + /* Command Unit abort command sent during transmission. */ + if (status & AC_SFLD_A) { + b->stats.tx_aborted_errors ++; + b->stats.tx_errors ++; + if (netif_msg_tx_err (b)) + printk (" aborted by CU"); + } + if (netif_msg_tx_err (b) && status & ~AC_SFLD_MAXCOL) + printk (".\n"); +} + +/* Remove finished commands from the TX list. Must be called with tx_lock held. + * The most tricky part here is to find out where the next command is, because + * the TX ring might have wrapped, so we find the next command by parsing the + * TX list to find the NOP that follows each command. The NOP contains a pointer + * to the next command. + */ +static void i82586_reap_commands (struct i82586_board *b) +{ + uint command, cmd_ptr, size, status; + uint nop = (uint) -1; + board_handle h = b->handle; + unsigned int max_interrupt_work = 30; + + if (unlikely (b->tx_reap == (uint) -1)) + return; + + for (cmd_ptr = b->tx_reap; + cmd_ptr != nop && max_interrupt_work --; + b->tx_tail = cmd_ptr + size, + b->tx_reap = cmd_ptr = b->readw (h, cmdadr (nop, ac_link))) + { + status = b->readw (h, cmdadr (cmd_ptr, ac_status)); + + if ((status & AC_SFLD_C) == 0) { + if (netif_msg_tx_done (b)) + printk (KERN_DEBUG "%s: Command at 0x%04x not " + "completed (0x%04hx).\n", b->dev->name, + cmd_ptr, status); + break; + } + command = b->readw (h, cmdadr (cmd_ptr, ac_command)); + + if (netif_msg_tx_done (b)) + printk (KERN_DEBUG "%s: Reaping command 0x%04hx at " + "0x%04hx.\n", b->dev->name, command, cmd_ptr); + + switch (command & AC_CFLD_CMD) + { + case acmd_ia_setup: + nop = cmd_ptr + sizeof (ac_ias_t); + size = sizeof (ac_ias_t) + sizeof (ac_nop_t); + break; + + /* We always use full size configure commands, even + * though we only use 9 configuration bytes. */ + case acmd_configure: + nop = cfgadr (cmd_ptr, c_nop); + size = sizeof (init_cfg); + b->configured = 1; + break; + + case acmd_mc_setup: + size = sizeof (ac_mcs_t) + + b->readw (h, mcsadr (cmd_ptr, mcs_cnt)); + nop = cmd_ptr + size; + size += sizeof (ac_nop_t); + break; + + case acmd_transmit: + i82586_reap_tx (b, cmd_ptr, status); + nop = txbadr (cmd_ptr, nop); + size = sizeof (txblock_t); + break; + + case acmd_tdr: + nop = cmd_ptr + sizeof (ac_tdr_t); + size = sizeof (ac_tdr_t) + sizeof (ac_nop_t); + break; + + case acmd_dump: + nop = cmd_ptr + sizeof (ac_dmp_t); + size = sizeof (ac_dmp_t) + sizeof (ac_nop_t) + DUMPBYTES; + break; + + case acmd_diagnose: + nop = cmd_ptr + sizeof (ac_dgn_t); + size = sizeof (ac_dgn_t) + sizeof (ac_nop_t); + break; + + default: + /* The TX list is corrupt. Clear it and stop the CU. */ + printk (KERN_WARNING "%s: Unknown command 0x%04hx found" + " in 82586 command list at 0x%04hx.\n", + b->dev->name, command, cmd_ptr); + nop = (uint) -1; + size = 0; + print_tx (b); + i82586_init_txmem (b); + b->writew (h, SCB_CMD_CUC_ABT, + scbbadr (b->scb_addr, scb.scb_command)); + b->ca (b->dev); + break; + } + if (unlikely (nop == (uint) -1)) + break; + } + if (max_interrupt_work > 30) { + printk (KERN_WARNING "%s: Infinite loop in TX list.\n", b->dev->name); + print_tx (b); + } + /* This is where the CU is busy-waiting, i.e. the last + * command on the TX list, so pass the CU on to the NOP + * in the SCB block and clear the TX list (defragmentation). + * FIXME There is probably a race condition here. Since the NOP + * of the last command is now in the free part of the list, the + * 82586 might still be executing it when we overwrite it with + * new commands. + */ + if (cmd_ptr == nop && nop != (uint) -1) { + b->writew (h, scbbadr (b->scb_addr, nop), + scbbadr (b->scb_addr, nop.nop_h.ac_link)); + i82586_init_txmem (b); + b->writea (h, scbbadr (b->scb_addr, nop), + cmdadr (nop, ac_link)); + } + /* It may now be possible to send more packets. */ + if ((uint) -1 != i82586_cmdspace (b, MAX_TX_CMD_SIZE (b))) + netif_wake_queue (b->dev); +} + +/* Find the next RFD in the RFD list. */ +static __inline__ uint rfd_succ (struct i82586_board *b, uint rfd) +{ + if (rfd == b->rx_last_rfd) + return (b->rx_first_rfd); + else + return (rfd + sizeof (rfd_t)); +} + +/* Find the next RBD in the RBD list. */ +static __inline__ uint rbd_succ (struct i82586_board *b, uint rbd) +{ + if (rbd == b->rx_last_rbd) + return (b->rx_first_rbd); + else + return (rbd + sizeof (rbd_t)); +} + +/* Find the previous RBD in the RBD list. */ +static __inline__ uint rbd_pred (struct i82586_board *b, uint rbd) +{ + if (rbd == b->rx_first_rbd) + return (b->rx_last_rbd); + else + return (rbd - sizeof (rbd_t)); +} + +/* Find the last RBD of a packet. Return it and its length << 24. + * Bugs: The buffer must have less than 256 bytes in it. */ +__inline__ +static u32 find_last_rbd (struct i82586_board *b, uint first_rbd, + uint next_rfd_first_rbd) +{ + uint rbd, next_rbd, length; + + /* Try the easy way first. The 82586 tells us which RBD is the first + * one for the next frame. The previous RBD ends this frame. */ + if (next_rfd_first_rbd != I82586NULL) { + rbd = rbd_pred (b, next_rfd_first_rbd); + length = b->readw (b->handle, rbdadr (rbd, rbd_status)); + if (likely (length & RBD_STATUS_EOF)) + return (((length & RBD_STATUS_ACNT) << 24) + rbd); + + printk (KERN_WARNING "%s: Last RBD of frame starting at RBD " + "%#04hx mispredicted as %#04hx.\n", b->dev->name, + first_rbd, rbd); + /* Fall through to the hard way. */ + } + /* One of the few bad things about the 82586 is that the frame length + * is not stored anywhere, making it needlessly tricky to find the + * last RBD and/or the frame length. Here we have to walk the list. */ + next_rbd = first_rbd; + do { + rbd = next_rbd; + length = b->readw (b->handle, rbdadr (rbd, rbd_status)); + next_rbd = rbd_succ (b, rbd); + } while ( (!(length & RBD_STATUS_EOF)) & (rbd != b->rx_btail)); + + if (unlikely (!(length & RBD_STATUS_EOF))) { + printk (KERN_WARNING "%s: EOF flag not set in RBD %#04x.\n", + b->dev->name, rbd); + rbd |= 1; + } + return (((length & RBD_STATUS_ACNT) << 24) + rbd); +} + +/* Try to feed a single packet into Linux NET. Returns non-zero if a packet + * was removed from the list and 0 if the frame was deferred. There are three + * actions that this function may take, based on RU status and memory shortage: + * 1) Remove a packet from the list and feed it to Linux NET. + * 2) Remove a packet from the list and discard it (out of memory and RBDs). + * 3) Defer the packet for later (out of memory, but not out of RBDs). + * This function is responsible for relinking the RBD list and maintaining the + * receiver statistics that the 82586 doesn't maintain. + * There are several error cases that must be patched up due to 82586 bugs. + */ +static __inline__ +int i82586_receive_one (struct i82586_board *b, uint ru_status, uint rfd_status, + uint first_rbd, uint next_rfd_first_rbd) +{ + board_handle h = b->handle; + struct sk_buff *skb = NULL; + uint last_rbd, last_length; + uint buf1, size1, buf2, size2; + unsigned char bogus_frame = 0; + + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: RBD 0x%04x stat 0x%04hx next " + "0x%04hx buf 0x%hx%04hx size 0x%04hx\n", + b->dev->name, first_rbd, + b->readw (h, rbdadr (first_rbd, rbd_status)), + b->readw (h, rbdadr (first_rbd, rbd_next_rbd_offset)), + b->readw (h, rbdadr (first_rbd, rbd_bufh)), + b->readw (h, rbdadr (first_rbd, rbd_bufl)), + b->readw (h, rbdadr (first_rbd, rbd_el_size))); + { + u32 temp; + temp = find_last_rbd (b, first_rbd, next_rfd_first_rbd); + bogus_frame = temp & 1; + last_rbd = (temp >> 0) & 0xfffffe; + last_length = (temp >> 24) & 0xff; + } + if (bogus_frame) { + b->stats.rx_missed_errors ++; + b->stats.rx_errors ++; + } + /* Presumably it is faster to compute the buffer addresses and lengths + * than it is to read them from the RBDs on the board. */ + buf1 = (first_rbd - b->rx_first_rbd) / sizeof (rbd_t) + * RXBUF_SIZE + b->rx_first_buffer; + if (likely (first_rbd <= last_rbd)) { + size1 = (last_rbd - first_rbd) / sizeof (rbd_t) + * RXBUF_SIZE + last_length; + size2 = 0; + buf2 = (uint) -1; + } else { + size1 = (b->rx_last_rbd - first_rbd) / sizeof (rbd_t) + * RXBUF_SIZE + RXBUF_SIZE; + size2 = (last_rbd - b->rx_first_rbd) / sizeof (rbd_t) + * RXBUF_SIZE + last_length; + buf2 = b->rx_first_buffer; + } + + /* The 82596CA documentation says that you can only get bad frames + * if you ask for bad frames, but my 82586 happily saves truncated + * frames even when not told to save bad frames. + */ + if (! bogus_frame && rfd_status & FD_STATUS_OK && + size1 + size2 >= ETH_ZLEN) { + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: Received %u (%u + %u) byte " + "packet.\n", b->dev->name, size1 + size2, size1, size2); + } else { + bogus_frame = 1; + /* Short frame or missing EOF flag. The 82586 counts the + * other errors for us. */ + if (rfd_status & (FD_STATUS_S7 | FD_STATUS_S6)) { + b->stats.rx_length_errors ++; + b->stats.rx_errors ++; + } + } + if (bogus_frame) { + if (netif_msg_rx_err (b)) + printk (KERN_DEBUG "%s: Discarded %u (%u + %u) byte " + "packet with errors (status %#x).\n", + b->dev->name, size1 + size2, size1, size2, + rfd_status); + } + /* Pass the packet to Linux NET if possible. */ + else if (b->skb_fromboard) + skb = b->skb_fromboard (h, buf1, size1, buf2, size2); + else { + skb = dev_alloc_skb (2 + size1 + size2); + if (likely (skb != NULL)) { + /* Longword align IP header. */ + skb_reserve (skb, 2); + b->copy_fromboard (h, buf1, skb_put (skb, size1), size1); + if (size2) + b->copy_fromboard (h, buf2, skb_put (skb, size2), size2); + } + } + if (likely (skb != NULL)) { + skb->dev = b->dev; + skb->protocol = eth_type_trans (skb, b->dev); + netif_rx (skb); /* FIXME Check return value. */ + b->dev->last_rx = jiffies; + b->stats.rx_bytes += (size1 + size2); + b->stats.rx_packets ++; + /* TODO: Count b->stats.multicast packets. */ + + /* Or drop the packet if there is no room at all for it. */ + } else if (ru_status & SCB_ST_RUS_NRES) { + printk (KERN_WARNING "%s: Memory squeeze, dropping packet.\n", + b->dev->name); + b->stats.rx_dropped ++; + + /* Or keep the packet on the board for later processing. */ + } else { + printk (KERN_WARNING "%s: Memory squeeze, deferring packet.\n", + b->dev->name); + return (0); + } + + /* If the tail RBD remains the tail RBD, then we're done. The usual + * procedure would clear the end-of-list flag of the (old) tail RBD. + */ + if (b->rx_btail == last_rbd) + return (1); + + /* Update new tail, which is this frame's last RBD. */ + b->writew (h, (RXBUF_SIZE & RBD_SIZE) | RBD_EL, + rbdadr (last_rbd, rbd_el_size)); + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: New tail RBD 0x%04x next 0x%04x size " + "0x%04x\n", b->dev->name, last_rbd, + b->readw (h, rbdadr (last_rbd, rbd_next_rbd_offset)), + (RXBUF_SIZE & RBD_SIZE) | RBD_EL); + + /* Update old tail. */ + b->writea (h, RXBUF_SIZE & RBD_SIZE, rbdadr (b->rx_btail, rbd_el_size)); + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: Old tail RBD 0x%04x next 0x%04x size " + "0x%04x\n", b->dev->name, b->rx_btail, + rbd_succ (b, b->rx_btail), RXBUF_SIZE & RBD_SIZE); + + b->rx_btail = last_rbd; + + /* The next packet's first RBD becomes the new head RBD. */ + b->rx_bhead = rbd_succ (b, b->rx_btail); + + return (1); +} + +/* Try to find packets to feed into Linux NET. This function is responsible + * for relinking the RFD list, but not the RBD list. + * TODO: Add max_interrupt_work limit. + */ +static void i82586_packets_received (struct i82586_board *b, uint ru_status) +{ + board_handle h = b->handle; + unsigned int max_interrupt_work = 30; + uint rfd, rfd_status, next_rfd, first_rbd, next_rfd_first_rbd; + + for (rfd = b->rx_fhead, first_rbd = I82586NULL; max_interrupt_work --; + rfd = next_rfd, first_rbd = next_rfd_first_rbd) + { + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: RFD list: first 0x%04x" + " head 0x%04x tail 0x%04x last 0x%04x\n", + b->dev->name, b->rx_first_rfd, + b->rx_fhead, b->rx_ftail, b->rx_last_rfd); + + rfd_status = b->reada (h, rfdadr (rfd, rfd_status)); + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: RFD 0x%04x status 0x%04x cmd " + "0x%04x next 0x%04x RBD 0x%04x\n", + b->dev->name, rfd, rfd_status, + b->readw (h, rfdadr (rfd, rfd_command)), + b->readw (h, rfdadr (rfd, rfd_link_offset)), + b->readw (h, rfdadr (rfd, rfd_rbd_offset))); + + if (! (rfd_status & FD_STATUS_C)) + break; /* No more completed frames. */ + + if (first_rbd == I82586NULL) + first_rbd = b->readw (h, rfdadr (rfd, rfd_rbd_offset)); + next_rfd = rfd_succ (b, rfd); + next_rfd_first_rbd = b->reada (h, rfdadr (next_rfd, rfd_rbd_offset)); + + if (first_rbd == I82586NULL) { + printk (KERN_WARNING "%s: Caught 82586 prefetch-past-EOL" + " bug in RFD %#04x with RBD %#04x.\n", + b->dev->name, rfd, first_rbd); + b->stats.rx_missed_errors ++; + b->stats.rx_errors ++; + } else { + if (! (i82586_receive_one (b, ru_status, rfd_status, + first_rbd, next_rfd_first_rbd))) + break; /* Defer frame. */ + } + /* Reset the now empty RFD. I don't think it _should_ be + * necessary to clear the RBD pointer, but my 82586 thinks + * differently, and the docs say nothing on the matter. :-( + * Clearing it prevents the 82586 from using the pointer before + * it has initialised it to point to a free RBD. + */ + b->writew (h, I82586NULL, rfdadr (rfd, rfd_rbd_offset)); + + /* The next RFD becomes the new head RFD. */ + b->rx_fhead = next_rfd; + + /* This RFD (the old head RFD) becomes the new tail RFD. */ + b->writew (h, FD_COMMAND_EL, rfdadr (rfd, rfd_command)); + + /* The old tail RFD is no longer at the end of the RFD list. */ + b->writea (h, 0, rfdadr (b->rx_ftail, rfd_command)); + b->rx_ftail = rfd; + if (netif_msg_rx_status (b)) + printk (KERN_DEBUG "%s: Finished RFD 0x%04x.\n", + b->dev->name, rfd); + } + if (max_interrupt_work > 30) + printk (KERN_WARNING "%s: Infinite loop in RFD list at 0x%04x.\n", + b->dev->name, rfd); +} + +/* Process an unusual i82586 interrupt. This means anything else than RX and + * TX interrupts. In particular, this function handles: + * 1) Starting the Command Unit initially. + * 2) Starting the Receive Unit initially when it is safe to do so. + * 3) Recovery from unintended CU and RU stops. + */ +static +uint i82586_process_unusual_interrupt (struct i82586_board *b, uint status) +{ + board_handle h = b->handle; + uint command = 0; + + if (status & SCB_ST_CNA) { + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: 82586 CU is no longer " + "active.\n", b->dev->name); + } + /* If the CU isn't running, remove any finished commands from the TX + * list and try to (re)start the CU where it left off. The TX list + * might be empty, so fix up the idle NOP link pointer too. + */ + if ((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV + && i82586_tx_trylock_inside_interrupt (b)) { + uint cmd_ptr; + + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: Starting inactive 82586 CU.\n", + b->dev->name); + i82586_reap_commands (b); + cmd_ptr = (b->tx_reap == (uint) -1) ? + scbbadr (b->scb_addr, nop) : b->tx_reap; + b->writew (h, cmd_ptr, + scbbadr (b->scb_addr, nop.nop_h.ac_link)); + b->writew (h, cmd_ptr, + scbbadr (b->scb_addr, scb.scb_cbl_offset)); + i82586_tx_unlock_inside_interrupt (b); + command |= SCB_CMD_CUC_GO; + } + if (status & SCB_ST_RNR) { + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: 82586 RU is no longer " + "ready.\n", b->dev->name); + } + /* If the RU runs out of RBDs or RFDs, just process whatever packets + * were received to free up RX memory, then fix up the RX list and + * restart the RU. Handle a suspended RU the same way. + */ + if (status & (SCB_ST_RUS_NRES | SCB_ST_RUS_SUSP)) { + if (netif_msg_intr (b) && (status & SCB_ST_RUS_NRES)) + printk (KERN_DEBUG "%s: 82586 RU ran out of " + "resources.\n", b->dev->name); + if (netif_msg_intr (b) && (status & SCB_ST_RUS_SUSP)) + printk (KERN_DEBUG "%s: 82586 RU has been " + "suspended!\n", b->dev->name); + + /* Clean up RX mem. Fix broken RBD pointer in head RFD. */ + i82586_packets_received (b, status); + b->writew (h, b->rx_bhead, + b->rx_fhead + offset (rfd_t, rfd_rbd_offset)); + b->writew (h, b->rx_fhead, + scbbadr (b->scb_addr, scb.scb_rfa_offset)); + command |= SCB_CMD_RUC_GO; + } + /* If the RU is idle, we can only start it if the i82586 has been + * configured already. + */ + if ((status & SCB_ST_RUS) == SCB_ST_RUS_IDLE && b->configured) { + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: Starting idle 82586 " + "RU.\n", b->dev->name); + b->writew (h, b->rx_fhead, + scbbadr (b->scb_addr, scb.scb_rfa_offset)); + command |= SCB_CMD_RUC_GO; + } + return (command); +} + +/* Process an 82586 interrupt. There are basicly three actions: + * 1) Handle various errors. + * 2) Feed received packets into the upper network layer. + * 3) Clean up after transmitted packets and record statistics. + */ +EXPORT_SYMBOL (i82586_process_interrupt); +irqreturn_t i82586_process_interrupt (struct i82586_board *b) +{ + uint status, command; + board_handle h = b->handle; + + status = b->reada (h, scbbadr (b->scb_addr, scb.scb_status)); + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: 82586 interrupt status 0x%04x.\n", + b->dev->name, status); + command = status & SCB_ST_INT; /* Ack all interrupts. */ + + if (status & SCB_ST_FR) { + i82586_packets_received (b, status); + } + if (status & SCB_ST_CX) { + /* If we get the TX lock, then process the finished commands, + * otherwise just leave tx_lock > 0 so the TX path will process + * the finished commands. */ + if (i82586_tx_trylock_inside_interrupt (b)) { + i82586_reap_commands (b); + i82586_tx_unlock_inside_interrupt (b); + } + } + if (unlikely ((status & (SCB_ST_CNA | SCB_ST_RNR | SCB_ST_CUS | SCB_ST_RUS)) + != (SCB_ST_CUS_ACTV | SCB_ST_RUS_RDY))) + command |= i82586_process_unusual_interrupt (b, status); + + if (netif_msg_intr (b)) + printk (KERN_DEBUG "%s: 82586 interrupt command 0x%04x.\n", + b->dev->name, command); + if (command) { + b->writea (h, command, scbbadr (b->scb_addr, scb.scb_command)); + b->ca (b->dev); + } + if (likely (status & SCB_ST_INT)) + return IRQ_RETVAL (IRQ_HANDLED); + else + return IRQ_RETVAL (IRQ_NONE); +} + +/* Try to handle a transmitter timeout. + * For now, just assume that a TX interrupt was lost and process the TX list. + */ +EXPORT_SYMBOL (i82586_tx_timeout); +void i82586_tx_timeout (struct i82586_board *b) +{ + + i82586_tx_lock_outside_interrupt (b); + i82586_reap_commands (b); + if (netif_queue_stopped (b->dev)) { + printk (KERN_ERR "%s: Transmission timed out.\n", b->dev->name); + print_tx (b); + } else + printk (KERN_WARNING "%s: Interrupt lost on irq %d.\n", + b->dev->name, b->dev->irq); + i82586_tx_unlock_outside_interrupt (b); +} + +/* Initialise the net_device structure of the board. The board specific code + * may afterwards adjust these values if they aren't appropriate. */ +EXPORT_SYMBOL (i82586_setup_dev); +void i82586_setup_dev (struct i82586_board *b) +{ + txblock_t *txblock = (txblock_t *) (& b->tx_trailer.cmd); + atomic_t unlocked = ATOMIC_INIT (-1); + static uint version_printed = 0; + + if (! version_printed) { + printk (KERN_DEBUG "%s", version); + version_printed = 1; + } + + b->tx_lock = unlocked; + + txblock->tx.tx_h.ac_status = 0; + txblock->tx.tx_h.ac_command = cpu_to_le16 (acmd_transmit | AC_CFLD_I); + txblock->nop.nop_h.ac_status = 0; + txblock->nop.nop_h.ac_command = cpu_to_le16 (acmd_nop); + txblock->tbd.tbd_next_bd_offset = I82586NULL; + txblock->tbd.tbd_bufh = 0; + + b->dev->features |= NETIF_F_SG | NETIF_F_HIGHDMA; + b->dev->watchdog_timeo = 2*HZ; + + /* Board memory autoconfiguration. */ + if (b->scb_addr == (uint) -1) + b->scb_addr = b->mem_start; + if (b->iscp_addr == (uint) -1) + b->iscp_addr = b->scb_addr + sizeof (scb_block_t); +} + +/* Update statistics. This function should be called frequently enough that + * none of the 16-bit counters overflow and stop between each call. An atomic + * read_and_clear() function is required to ensure that the stats are correct, + * but the correctness of the stats is not important for the operation of the + * driver. Note that if the counters would wrap around to zero rather than + * stop counting, a simple reada() would have been enough for correctness. + * Why should Linux NET and Intel agree on the naming of these fields? + * Linux NET Intel + * rx_crc_errors scb_crcerrs (CRCERRS) + * rx_frame_errors scb_alnerrs (ALNERRS) + * rx_over_errors scb_rscerrs (RSCERRS) + * rx_fifo_errors scb_ovrnerrs (OVRNERRS) + */ +EXPORT_SYMBOL (i82586_update_stats); +struct net_device_stats *i82586_update_stats (struct i82586_board *b) +{ + uint scb = scbbadr (b->scb_addr, scb); + board_handle h = b->handle; + unsigned long delta; + + /* Aligned packets with CRC error. */ + delta = b->reada (h, scbadr (scb, scb_crcerrs)); + b->writea (h, 0, scbadr (scb, scb_crcerrs)); + b->stats.rx_crc_errors += delta; + b->stats.rx_errors += delta; + + /* Misaligned packets (with CRC error). */ + delta = b->reada (h, scbadr (scb, scb_alnerrs)); + b->writea (h, 0, scbadr (scb, scb_alnerrs)); + b->stats.rx_frame_errors += delta; + b->stats.rx_errors += delta; + + /* The board ran out of buffers for a packet. */ + delta = b->reada (h, scbadr (scb, scb_rscerrs)); + b->writea (h, 0, scbadr (scb, scb_rscerrs)); + b->stats.rx_over_errors += delta; + b->stats.rx_errors += delta; + + /* FIFO overruns due to slow memory access. */ + delta = b->reada (h, scbadr (scb, scb_ovrnerrs)); + b->writea (h, 0, scbadr (scb, scb_ovrnerrs)); + b->stats.rx_fifo_errors += delta; + b->stats.rx_errors += delta; + + return (& b->stats); +} + +/* Helper functions/macros for i82586_allocate_memory(). */ +/* The amount of memory taken by RX descriptors. */ +static __inline__ +uint rxdmem (uint rbds, uint rfds) +{ + return (rbds * sizeof (rbd_t) + rfds * sizeof (rfd_t)); +} + +/* The amount of memory taken by RX descriptors and buffers. */ +static __inline__ +uint rxmem (uint rbds, uint rfds) +{ + return (rxdmem (rbds, rfds) + rbds * RXBUF_SIZE); +} + +/* The amount of memory taken by TX packets. */ +static __inline__ +uint txmem (uint txes, uint mtu) +{ + return ((mtu + sizeof (txblock_t)) * txes); +} + +#define MEMOK(n) (unused >= (n)) +#define SEGOK(n) (memstart + txmem (txes, mtu) + rxdmem (rbds, rfds) + (n) <= segend) +#define TXOK(n) (MEMOK (txmem ((n), mtu)) && SEGOK (txmem ((n), mtu))) +#define RFDOK(n) (MEMOK (rxmem (0, (n))) && SEGOK (rxdmem (0, (n)))) +#define RDPOK(n) (MEMOK (rxmem ((n), (n))) && SEGOK (rxdmem ((n), (n)))) +#define PKTOK(n) (MEMOK (rxmem ((n) * pktbufs, (n))) && SEGOK (rxdmem ((n) * pktbufs, (n)))) + +/* Try to set up packet buffers in a reasonable way with the amount of + * memory available. Return 0 on success, "-errno" otherwise. + * If the board has lots of memory and the MTU is huge, some memory may be + * left unused. But even 512 kB of memory with a 9500 byte MTU works fine. + * Other parts of the driver depend on memstart to be less than 64 kB and it + * should be kept small. This goes for b->scb_addr too. + */ +int i82586_allocate_memory (struct i82586_board *b, uint mtu, uint check_only) +{ + uint memstart, memsize, memend, segend, unused, pktbufs; + uint rfds, rbds, txes, txbytes; + + /* Make room for the SCB. */ + if (b->scb_addr + sizeof (scb_block_t) >= b->mem_start) + memstart = b->scb_addr + sizeof (scb_block_t); + else + memstart = b->mem_start; + + memend = b->mem_start + b->mem_size; + memsize = memend - memstart; + segend = (memend > 65536UL) ? 65536UL : memend; + if (memstart >= segend) + return (-EFAULT); + + /* Account for header. Make the mtu even for packet alignment. */ + mtu += VLAN_ETH_HLEN; + mtu += mtu & 1; + pktbufs = (mtu + RXBUF_SIZE - 1) / RXBUF_SIZE; + + for (rbds = 0, rfds = 0, txes = 0; /* forever */; ) { + unused = memsize - (rxmem (rbds, rfds) + txmem (txes, mtu)); + /* Want at least one full size TX packet. */ + if (txes == 0 && TXOK (1)) + txes = 1; + + /* Want at least one full size RX packet. */ + else if (rfds == 0 && PKTOK (1)) + rfds = 1, rbds = 1 * pktbufs; + + /* Try to get two full size RX packets. */ + else if (rfds == 1 && PKTOK (1)) + rfds = 2, rbds = 2 * pktbufs; + + /* Try to get two full size TX packets. */ + else if (txes == 1 && TXOK (1)) + txes = 2; + + /* Try to get (2 + RX packets / 12) TX packets. + * Cut off at 4 packets unless we've used less than 4 kB. */ + else if ((txes < 4 || txmem (txes, mtu) < 4096) && + (txes < 2 + (rbds / pktbufs) / 12) && TXOK (1)) + txes ++; + + /* Try to get enough RFDs for an average RX size of mtu / 4. */ + else if (rfds < rbds && rfds * pktbufs / 4 < rbds && RFDOK (1)) + rfds ++; + + /* Try to get more full size RX packets. */ + else if (PKTOK (1)) + rfds ++, rbds += pktbufs; + + /* Allocate the remaining memory to RFD/RBD pairs and RFDs. */ + else if (RDPOK (1)) + rfds ++, rbds ++; + else if (rfds < rbds && RFDOK (1)) + rfds ++; + + /* We used up the memory. */ + else + break; + }; + if (rxmem (rbds, rfds) + txmem (txes, mtu) > memsize) + return (-EFAULT); + + if (txes < 1 || rfds < 1 || rbds < pktbufs) + return (-EINVAL); + + /* Find the size of the TX list. */ + if (segend - rxdmem (rbds, rfds) + rxmem (rbds, rfds) > memend) + /* txbytes restricted by total memory. */ + txbytes = memsize - rxmem (rbds, rfds); + else + /* txbytes restricted by 64 kB segment. */ + txbytes = segend - memstart - rxdmem (rbds, rfds); + + if (check_only) + return (0); + + b->txmem_start = memstart; + b->txmem_size = memsize - rxmem (rbds, rfds); + b->rx_first_rbd = b->txmem_start + b->txmem_size; + b->rx_last_rbd = b->rx_first_rbd + (rbds - 1) * sizeof (rbd_t); + b->rx_first_rfd = b->rx_last_rbd + sizeof (rbd_t); + b->rx_last_rfd = b->rx_first_rfd + (rfds - 1) * sizeof (rfd_t); + b->rx_first_buffer = b->rx_last_rfd + sizeof (rfd_t); + b->buffer_mtu = b->mtu = mtu - VLAN_ETH_HLEN; + + if (! netif_msg_drv (b)) + return (0); + + printk (KERN_INFO "%s: RX: %u.%u kB (%u-%u packets). TX: %u.%u kB " + "(%u+ packets).\n", b->dev->name, rbds * RXBUF_SIZE / 1024, + rbds * RXBUF_SIZE % 1024 * 10 / 1024, rbds / pktbufs, rfds, + b->txmem_size / 1024, b->txmem_size % 1024 * 10 / 1024, txes); + return (0); +} + +EXPORT_SYMBOL (i82586_change_mtu); +int i82586_change_mtu (struct i82586_board *b, int mtu) +{ + int retval; + + /* Check against hardware limits. */ + if (mtu < 0 || mtu + VLAN_ETH_HLEN > TBD_STATUS_ACNT) + return (-EINVAL); + + /* We don't support increasing the MTU of a running interface. */ + if (mtu > b->buffer_mtu && netif_running (b->dev)) + return (-EBUSY); + + /* Check that the board has enough memory. */ + if ((retval = i82586_allocate_memory (b, mtu, 1))) + return (retval); + + b->dev->mtu = mtu; + i82586_tx_lock_outside_interrupt (b); + b->mtu = mtu + (mtu & 1); + i82586_tx_unlock_outside_interrupt (b); + return (0); +} + +MODULE_AUTHOR ("Rask Ingemann Lambertsen "); +MODULE_DESCRIPTION ("Intel i82586 driver module for use by Ethernet board drivers."); +MODULE_LICENSE("GPL"); --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/82586.h Wed Oct 29 15:52:56 2003 @@ -0,0 +1,210 @@ +/* 82586.h: Definitions for the generic Intel 82586 driver module. + * + * Written 2000, 2002-2003 by Rask Ingemann Lambertsen. + * + * Copyright 2000, 2002-2003 Rask Ingemann Lambertsen. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The author may be reached as . + * See 82586.c for further information. + */ + +/* Deal with compatibility with Linux 2.4, 2.5 and 2.6. */ +#include + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(x) +#endif + +#ifndef EXPORT_NO_SYMBOLS +#define EXPORT_NO_SYMBOLS +#endif + +#ifndef IRQ_HANDLED +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#define IRQ_RETURN(x) { x ; return; } +#else +#define IRQ_RETURN(x) return (x); +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(x) kfree(x) +#endif + +typedef union { + void *p; + unsigned long int u; + signed long int s; +} board_handle; + +/* This structure defines the interface to the board. */ +struct i82586_board +{ + /* The first many fields are filled in by the board driver before the + * 82586 module is attached to the board. These fields may only be + * modified again when the 82586 modules has been detached from the + * board. + */ + + /* Back pointer to the network device structure. */ + struct net_device *dev; + + /* Message level for this interface. */ + u32 msg_enable; + + /* User defined handle passed to the memory access functions. */ + board_handle handle; + + /* These functions are used to access the board memory. Word reads + * should convert from little endian to CPU byte ordering. Word writes + * should convert from CPU to little endian byte ordering. + */ + unsigned short (*readw) (board_handle handle, unsigned int addr); + void (*writew) (board_handle handle, unsigned short data, unsigned int addr); + + /* Word read/write of board memory which must be indivisible as + * seen by the 82586. Byte ordering as with readw()/writew(). */ + unsigned short (*reada) (board_handle handle, unsigned int addr); + void (*writea) (board_handle handle, unsigned short data, unsigned int addr); + + /* To avoid confusion, these two functions are not called memcpy_xxx() + * since they don't have memcpy() style reverse argument order. */ + void (*copy_fromboard) (board_handle handle, unsigned int source, void *dest, unsigned short nbytes); + void (*copy_toboard) (board_handle handle, void *source, unsigned int dest, unsigned short nbytes); + + /* Copy from a socket buffer to the board, appending the trailer. + * All memory, including the trailer, can be DMA'd from. */ + void (*skb_toboard) (board_handle handle, struct sk_buff *skb, + u8 *trailer, uint trailersize, uint dest); + + /* Copy from the board to a socket buffer. */ + struct sk_buff *(*skb_fromboard) (board_handle handle, uint frag1, + uint size1, uint frag2, uint size2); + + /* This function is used to reset the 82586. It should pull the reset + * pin on the 82586 but not begin to set up the chip. */ + void (*reset) (struct net_device *dev); + + /* This function is used to send the Channel Attention signal to the + * 82586. */ + void (*ca) (struct net_device *dev); + + /* This is where RX and TX buffers will be placed. */ + uint mem_start; + uint mem_size; + + /* Defines the location of the 12-byte SCP. This will be something + * like 0x(FF)FFF4 on most boards, but might be configurable. It is + * ok to overlap it with receive and/or transmit buffer memory. + */ + unsigned int scp_addr; + + /* Defines the 8-byte memory area where you want to have the ISCP. + * It too may be overlapped by receive and/or transmit buffer memory. + * Set it to -1 for autoconfiguration. + */ + uint iscp_addr; + + /* Defines the memory area where you want to have the 16-byte SCB and + * the associated structures (40 bytes in total). + * Set it to -1 for autoconfiguration. + */ + uint scb_addr; + + /* The rest of the fields are used only by the 82586 module. */ + int last_restart; + + /* The first and last RFDs in the contiguous block that forms the list + * of RFDs. */ + uint rx_first_rfd; + uint rx_last_rfd; + + /* Points to the first RFD which might contain a packet. */ + uint rx_fhead; + + /* Points to the last RFD which might contain a packet, i.e. the one + * flagged as being "end of list". */ + uint rx_ftail; + + /* The first and last RBDs in the contiguous block that forms the list + * of RBDs. This does not include the errata RBD. */ + uint rx_first_rbd; + uint rx_last_rbd; + + /* Points to the first RBD which might be used. */ + uint rx_bhead; + + /* Points to the last RBD which might be used, i.e. the one flagged as + * being "end of list". */ + uint rx_btail; + + /* The first of the receive buffers. They form a contiguous block. */ + uint rx_first_buffer; + + /* Defines which memory area may be used for transmit buffers. */ + uint txmem_start; + uint txmem_size; + + /* Points to the first available (free) TX buffer location. */ + uint tx_head; + + /* Points to the first unavailable (used) TX buffer location. + * It is -1 if the list is full. */ + uint tx_tail; + + /* Points to the ac_link field of the NOP command where the Command + * Unit is currently busy-waiting for the next command. */ + uint tx_cmd_link; + + /* First TX buffer to process in TX interrupt. + * It is -1 if the list is empty. */ + uint tx_reap; + + /* Protects tx_head, tx_tail, tx_cmd_link, tx_reap and the TX list. + * The lock can take these values: + * -1: Unlocked. + * 0: Locked by TX path or by TX interrupt. + * >0: TX attempt while in TX interrupt, so the TX path will busy wait + * for the TX interrupt to finish, or TX interrupt while in TX path, + * so the TX interrupt will finish immediately and leave it to the + * TX path to clean up after TX commands. + */ + atomic_t tx_lock; + + /* This structure provides padding for short frames for transmission + as well as space for the transmit command etc. itself. */ + struct { + u8 pad[ETH_ZLEN - 22]; + u8 cmd[22]; + } tx_trailer; + + /* dev->mtu rounded up to an even number of bytes. */ + uint mtu; + + /* The MTU that buffers have been allocated for (buffer_mtu >= mtu). */ + uint buffer_mtu; + + /* Statistics. */ + struct net_device_stats stats; + + uint configured : 1; +}; + +int i82586_init (struct i82586_board *b); +int i82586_send_packet (struct i82586_board *b, struct sk_buff *skb); +int i82586_set_station_address (struct i82586_board *b); +int i82586_set_multicast_list (struct i82586_board *b); +int i82586_configure (struct i82586_board *b); +void i82586_set_rx_mode (struct i82586_board *b); +void i82586_open (struct i82586_board *b); +void i82586_close (struct i82586_board *b); +irqreturn_t i82586_process_interrupt (struct i82586_board *b); +void i82586_tx_timeout (struct i82586_board *b); +void i82586_setup_dev (struct i82586_board *b); +struct net_device_stats *i82586_update_stats (struct i82586_board *b); +int i82586_change_mtu (struct i82586_board *b, int mtu); --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/Makefile Wed Nov 5 17:54:02 2003 @@ -0,0 +1,50 @@ +# +# Makefile for Intel i82586 based Ethernet boards. +# + +AS_I186=as +LD_I186=ld +OBJDUMP_I186=objdump + +EXTRA_CFLAGS=-I $(TOPDIR)/drivers/net + +ifeq ($(CONFIG_NE3200_BUILD_FIRMWARE),y) + +ne3200fw.o : ne3200fw.s + $(AS_I186) $< -o $@ + +# We need --omagic to turn off section alignment, as no other documented way +# works (GNU ld version 2.11.93.0.2 20020207). +ne3200fw.bin : ne3200fw.o + $(LD_I186) -Ttext 0 $< --omagic --oformat binary -o $@ + +ne3200fw.v : ne3200fw.bin + echo -n "#define firmware_size " >$@ && \ + wc -c <$< >>$@ && \ + echo 'static const char firmware_data[] = "\' >>$@ && \ + sed -n -e l $< | sed -e 's/\$$$$/\\n\\/' -e 's/"/\\"/g' >>$@ && \ + echo '";' >>$@ + +ne3200fw.h : ne3200fw.o + $(OBJDUMP_I186) --syms $< | \ + awk -- '$$5 ~ /^(BMIC_MBOX|CMD|INT)_/ { print "#define", $$5, "0x" $$1; }' >$@ + +else # CONFIG_NE3200_BUILD_FIRMWARE=n + +ne3200fw.h : ne3200fw.h_shipped + cp $< $@ + +ne3200fw.v : ne3200fw.v_shipped + cp $< $@ + +endif + +ne3200.o : ne3200fw.v ne3200fw.h + +obj-$(CONFIG_NE3200) += ne3200.o 82586.o + +export-objs += 82586.o + +clean-files := ne3200fw.o ne3200fw.bin ne3200fw.h ne3200fw.v + +include $(TOPDIR)/Rules.make --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/ne3200.c Thu Oct 30 22:16:58 2003 @@ -0,0 +1,1710 @@ +/* ne3200.c: A Novell NE3200 network driver for linux. + * + * Written 2000, 2002-2003 by Rask Ingemann Lambertsen. + * + * Copyright 2000, 2002-2003 Rask Ingemann Lambertsen. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * The author may be reached as , or by + * by snail mail addressed to: + * + * Rask Ingemann Lambertsen + * Vesterleds Alle 17B + * DK-2605 Brøndby + * Denmark + * + * This is a Linux driver for Novell NE3200 EISA Ethernet cards and + * clones thereof. See below for a list of boards. + * + * The board consists of: + * - an Intel 82586 Ethernet chip. + * - an Intel 82355 EISA bus master interface chip (BMIC). + * - an Intel 80186 CPU/microcontroller (20 MHz). + * - a bank of 16 kB static RAM (35 ns). + * - a bank of ?? kB station address PROM. + * - a bank of 32 kB static RAM (35 ns). + * - a bank of 32 kb ROM with the initial firmware. + * This was the good news. + * + * The bad news is that it is impossible to directly access the 82586 + * and the RAM. All you can do is to communicate with the 80186 and + * the firmware it runs by means of the 82355's semaphore and mailbox + * registers. Fortunately, the initial ROM firmware allows you to + * upload your own firmware (up to 16 kB) which is how this driver + * tries to repair the interface. + * + * The following boards are expected to work with this driver: + * o Novell NE3200 and NE3200T + * o Eagle Technology NE3200 + * o Microdyne EXOS 235T + * o Intel EtherExpress 32 and EtherExpress 32N + * (note: The "EtherExpress Flash32" is completely different) + * o Anthem NE3200 + * o Compaq NE3200 + * o are there more clones out there? + * + */ + +static const char *version = + "ne3200.c:v0.07 2003-10-30 Rask Ingemann Lambertsen \n"; + +/* + * Sources: + * + * Donald Becker's skeleton.c (now isa-skeleton.c) driver outline. + * + * Paul Gortmaker's ne3210.c for EISA detection hints. + * + * Achim Leubner's scsi/gdth.h for i82355 (BMIC) register info. + * + * Example "cne3200" code published by Novell on their website at + * + * for the firmware download sequence. + * + * "Linux Device Drivers" by Alessandro Rubini. ISBN 1-56592-292-1. + * + * Spring 1999 special course "Driver-programs for Linux" at DTU + * (now runs each spring as course 49422). + * + * Random i82355 info found at Intel's website: + * + * + * + * + * + * Lots of experimentation and endless hex dumps to find out things the + * hard way. + * + * Deficiencies: + * + * No section in Documeentation/networking/net-modules.txt. + * Does not check EISA_bus. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Hardware definitions. */ +#include "ne3200.h" + +/* This is the firmware which will be used with this driver. */ +#include "ne3200fw.v" + +/* Firmware definitions. */ +#include "ne3200fw.h" + +/* 82856 module definitions. */ +#include "82586.h" + +MODULE_AUTHOR ("Rask Ingemann Lambertsen "); +MODULE_DESCRIPTION ("Novell NE3200 Ethernet (and clones) driver"); +MODULE_LICENSE ("GPL"); + +/* How long to wait for a response from the firmware after reset/upload + * before the driver gives up. The timeout is 1/fw_timeout s. The + * ROM firmware on my board needs 0.190 s. The default fw_timeout of 3 + * gives the firmware 0.333 s to respond. */ +#define FW_TIMEOUT 3 + +/* How long to keep the reset bit on when resetting the board. + * The timeout is 1/reset_delay s. */ +#define RESET_DELAY 100 + +MODULE_PARM_DESC (irq, "0 = autodetect (default), others = override (5/9/10/11/15 can be configured)."); +MODULE_PARM_DESC (irq_type, "0 = autodetect (default), 1 = level triggered, 2 = edge triggered."); +MODULE_PARM_DESC (media, "0 = unknown (default), 1 = 10base-2 (BNC), 2 = 10base-T (RJ45), 3 = 10base-5 (AUI)."); +MODULE_PARM_DESC (configure_board, "0 = don't configure board (default), 1 = configure board with irq, irq_type and media."); +MODULE_PARM_DESC (fw_timeout, "(1/fw_timeout) s is the maximum time to wait for a firmware response after reset. Default = 1/" __MODULE_STRING (FW_TIMEOUT) " s."); +MODULE_PARM_DESC (reset_delay, "(1/reset_delay) s is the length of time to hold the reset line during reset. Default = 1/" __MODULE_STRING (RESET_DELAY) " s."); +MODULE_PARM_DESC (debug, "NETIF_MSG_* style flags to control debug messages."); +MODULE_PARM_DESC (extra_eisa_id, "Extra NE3200 EISA board ID to recognise."); + +EXPORT_NO_SYMBOLS; + +/* Maximum number of board which can have options passed. */ +#define MAX_OPTION_BOARDS 8 + +/* Which debug messages to print by default. */ +#define NET_DEBUG (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_RX_ERR \ + | NETIF_MSG_TX_ERR) + +static int irq [MAX_OPTION_BOARDS] = { 0 }; +static uint irq_type[MAX_OPTION_BOARDS] = { 0 }; +static uint media [MAX_OPTION_BOARDS] = { 0 }; +static uint configure_board [MAX_OPTION_BOARDS] = { 0 }; +static uint fw_timeout = FW_TIMEOUT; +static uint reset_delay = RESET_DELAY; +static uint debug = NET_DEBUG; +static ulong extra_eisa_id = 0; + +MODULE_PARM(irq, "1-" __MODULE_STRING (MAX_OPTION_BOARDS) "i"); +MODULE_PARM(irq_type, "1-" __MODULE_STRING (MAX_OPTION_BOARDS) "i"); +MODULE_PARM(media, "1-" __MODULE_STRING (MAX_OPTION_BOARDS) "i"); +MODULE_PARM(configure_board, "1-" __MODULE_STRING (MAX_OPTION_BOARDS) "i"); +MODULE_PARM(fw_timeout, "1i"); +MODULE_PARM(reset_delay, "1i"); +MODULE_PARM(debug, "1i"); +MODULE_PARM(extra_eisa_id, "1l"); + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ +static const char *cardname = "NE3200"; + +/* Information that needs to be kept for each board. */ +struct net_local { + struct i82586_board board; + + /* This lock protects the BMIC registers, in particular the mailboxes. + * The most obvious problem would be an RX interrupt while uploading a + * TX packet to the board. */ + spinlock_t fw_lock; + + /* The type of interrupt: 0 = edge triggered, 1 = level triggered. */ + uint level_triggered : 1, + + /* 1 if level_triggered is valid, 0 otherwise. */ + irq_type_valid : 1; + + /* Next board controlled by the module. */ + struct net_device *next_dev; +}; + +/* First board controlled by the module. */ +static struct net_device *first_dev; + +/* The number of I/O ports used by the ethercard. Note that this covers only + * the BMIC registers, not the reset and config registers. */ +#define NETCARD_IO_EXTENT 0x30 + +/* A zero-terminated list of I/O addresses to be probed. */ +/* Since this is an EISA board, probe EISA slot 1-15. */ +static unsigned int netcard_portlist[] __initdata = + { 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000, + 0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0x0}; + +/* Board probe/setup/shutdown functions. */ +static int ne3200_probe1 (uint ioaddr, uint index); +static void interrupt_from_ne3200 (uint ioaddr); + +/* Interface with Linux NET. */ +static int net_open(struct net_device *dev); +static int net_send_packet(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int net_close(struct net_device *dev); +static struct net_device_stats *net_get_stats(struct net_device *dev); +static void ne3200_set_rx_mode(struct net_device *dev); +static void net_tx_timeout(struct net_device *dev); + +/* Interface functions for the 82586 module. */ +static unsigned short lreadw_ne3200 (board_handle handle, unsigned int addr); +static unsigned short lreada_ne3200 (board_handle handle, unsigned int addr); +static void lwritew_ne3200 (board_handle handle, unsigned short data, unsigned int addr); +static void lwritea_ne3200 (board_handle handle, unsigned short data, unsigned int addr); +static void copyfrom_ne3200 (board_handle handle, unsigned int src, void *dest, unsigned short n); +static void copyto_ne3200 (board_handle handle, void *src, unsigned int dest, unsigned short n); +static void skb_to_ne3200 (board_handle handle, struct sk_buff *skb, u8 *trailer, uint size, uint dest); +static struct sk_buff *skb_from_ne3200 (board_handle handle, uint frag1, uint size1, uint frag2, uint size2); +static void reset586_ne3200 (struct net_device *dev); +static void ca586_ne3200 (struct net_device *dev); + +/* Board specific functions. */ +static int ne3200_reset (unsigned int ioaddr); +static unsigned short readw_ne3200 (unsigned int ioaddr, unsigned short addr); +static unsigned short reada_ne3200 (unsigned int ioaddr, unsigned short addr); +static void writew_ne3200 (unsigned int ioaddr, unsigned short addr, unsigned short int data); +static void writea_ne3200 (unsigned int ioaddr, unsigned short addr, unsigned short int data); + +/* Debugging aid. */ +static void print_firmware_crashdump (uint ioaddr, u8 status); +static void print_mailboxes (unsigned int ioaddr); +static void print_registers (unsigned int ioaddr); +static void firmware_timeout_debug (uint ioaddr, char *cmd); + +/* Functions used for exploring an unknown board. */ +static void print_iospace (unsigned int ioaddr); +static void print_station_address (unsigned int ioaddr); +static void print_prom (unsigned int ioaddr); +static void ram_speed_test (unsigned int ioaddr); +static void probe_80186_pcb (unsigned int ioaddr); +static void print_interrupt_info (unsigned int ioaddr); +static void print_interrupt_stats (unsigned int ioaddr); +static unsigned short inw_ne3200 (unsigned int ioaddr, unsigned short addr); + +/* + * Probe each of the supported I/O addresses for a supported board. + */ +static int __init ne3200_probe (void) +{ + uint i, boards_found; + + first_dev = NULL; + for (boards_found = 0, i = 0; netcard_portlist[i]; i++) { + int ioaddr = netcard_portlist[i]; + if (ne3200_probe1 (ioaddr, boards_found) == 0) + boards_found ++; + } + if (boards_found > 0) + return 0; + else + return -ENODEV; +} +module_init (ne3200_probe); + +static void release_regions (uint ioaddr) +{ + release_region (ioaddr + NE3200_BMIC_OFFSET, NETCARD_IO_EXTENT); + release_region (ioaddr + NE3200_BOARD_CONFIG, 4); + release_region (ioaddr + NE3200_BOARD_RESET, 4); +} + +static void __exit ne3200_cleanup_module (void) +{ + struct net_device *dev, *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + for (dev = first_dev; dev; dev = next_dev) { + struct net_local *np = (void *) dev->priv; + + unregister_netdev (dev); + + if (debug & NETIF_MSG_PROBE) { + printk (KERN_DEBUG "%s: Unloading module for board at " + "%#lx.\n", dev->name, dev->base_addr); + print_interrupt_stats (dev->base_addr); + } + + /* + * If we don't do this, we can't re-insmod it later. + * Release irq/dma here, when you have jumpered versions and + * allocate them in net_probe1(). + */ + if (! np->level_triggered) + free_irq (dev->irq, dev); + release_regions (dev->base_addr); + + next_dev = ((struct net_local *) dev->priv)->next_dev; + free_netdev (dev); + } +} +module_exit (ne3200_cleanup_module); + +/* Prettyprint crash dump from the firmware. */ +static void print_firmware_crashdump (uint ioaddr, u8 status) +{ + uint mb = ioaddr + BMIC_MAILBOXES; + printk (KERN_ERR "0x%x: Firmware crashed (0x%x): AX=%04x BX=%04x DX=%04x" + " BP=%04x SP=%04x DS=%04x CS=%04x IP=%04x\n", ioaddr, status, + inw (mb + 0), inw (mb + 2), inw (mb + 4), inw (mb + 6), + inw (mb + 8), inw (mb + 10), inw (mb + 12), inw (mb + 14)); +} + +/* Dump the contents of the BMIC mailboxes. */ +static void print_mailboxes (unsigned int ioaddr) +{ + unsigned int i = ioaddr + BMIC_MAILBOXES; + printk (KERN_DEBUG "ne3200: Mailboxes = " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x.\n", inb (i+0), + inb (i+1), inb (i+2), inb (i+3), inb (i+4), inb (i+5), + inb (i+6), inb (i+7), inb (i+8), inb (i+9), inb (i+10), + inb (i+11), inb (i+12), inb (i+13), inb (i+14), inb (i+15)); +} + +static void firmware_timeout_debug (uint ioaddr, char *cmd) +{ + u8 int_status = inb (ioaddr + BMIC_SYS_INT_STATUS); + + if (int_status & INT_FWFAULT) { + printk (KERN_ERR "0x%x: Firmware crashed on command %s.\n", + ioaddr, cmd); + print_firmware_crashdump (ioaddr, int_status); + } else { + printk (KERN_ERR "0x%x: Firmware timed out on command %s " + "(status 0x%2x interrupt 0x%x)\n", ioaddr, cmd, + inb (ioaddr + NE3200_BMIC_OFFSET + BMIC_MBOX_CMD), + int_status); + print_mailboxes (ioaddr); + } +} + +/* Disable all interrupts (except fw crash reports) from this ne3200 board. + * If you disable interrupts using e.g. spin_lock_irqsave(), then a firmware + * crash will go undetected and hang the whole machine (at least on UP systems). + */ +static unsigned char __inline__ ne3200_irqsave (unsigned int ioaddr) +{ + unsigned char intena; + + intena = inb (ioaddr + BMIC_SYS_INT_MASK); + outb (INT_FWFAULT, ioaddr + BMIC_SYS_INT_MASK); + return (intena); +} + +/* Restore interrupts on this ne3200 board. */ +static void __inline__ ne3200_irqrestore (unsigned int ioaddr, unsigned char intena) +{ + outb (intena, ioaddr + BMIC_SYS_INT_MASK); +} + +/* Read a word from the NE3200 RAM. Needs the firmware to be running. + * Reads 32 kB in 340 ms or about 94 kB/s. */ +static unsigned short __inline__ readw_ne3200 (unsigned int ioaddr, unsigned short addr) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outw (cpu_to_be16(addr),ioaddr + BMIC_MBOX_BADDR); + outb (CMD_READW_MEM, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_READW_MEM"); + return (le16_to_cpu(inw (ioaddr + BMIC_MBOX_DATA))); +} + +static unsigned short lreadw_ne3200 (board_handle handle, unsigned int addr) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned short data; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + data = readw_ne3200 (dev->base_addr, addr); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); + return (data); +} + +/* Write a word to the NE3200 RAM. Needs the firmware to be running. + * Writes 32 kB in 340 ms or about 94 kB/s. */ +static void __inline__ writew_ne3200 (unsigned int ioaddr, unsigned short addr, unsigned short data) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outw (cpu_to_be16(addr),ioaddr + BMIC_MBOX_BADDR); + outw (cpu_to_le16(data),ioaddr + BMIC_MBOX_DATA); + outb (CMD_WRITEW_MEM, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_WRITEW_MEM"); +} + +static void lwritew_ne3200 (board_handle handle, unsigned short data, unsigned int addr) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + writew_ne3200 (dev->base_addr, addr, data); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* Read a word from the NE3200 RAM. Needs the firmware to be running. + * This read will not be split into two 8-bit reads. + * Reads 32 kB in 350 ms or about 91 kB/s. */ +static unsigned short __inline__ reada_ne3200 (unsigned int ioaddr, unsigned short addr) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outw (cpu_to_be16(addr),ioaddr + BMIC_MBOX_BADDR); + outb (CMD_READA_MEM, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_READA_MEM"); + return (le16_to_cpu(inw (ioaddr + BMIC_MBOX_DATA))); +} + +static unsigned short lreada_ne3200 (board_handle handle, unsigned int addr) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned short data; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + data = reada_ne3200 (dev->base_addr, addr); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); + return (data); +} + +/* Write a word to the NE3200 RAM. Needs the firmware to be running. + * This write will not be split into two 8-bit writes. + * Writes 32 kB in 350 ms or about 91 kB/s. */ +static void __inline__ writea_ne3200 (unsigned int ioaddr, unsigned short addr, unsigned short data) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outw (cpu_to_be16(addr),ioaddr + BMIC_MBOX_BADDR); + outw (cpu_to_be16(data),ioaddr + BMIC_MBOX_DATA); + outb (CMD_WRITEA_MEM, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_WRITEA_MEM"); +} + +static void lwritea_ne3200 (board_handle handle, unsigned short data, unsigned int addr) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + writea_ne3200 (dev->base_addr, addr, data); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* Copy from the NE3200 RAM to memory. Needs the firmware to be running. + * Reads 32 kB in 70 ms or about 457 kB/s. + * Data is transferred through the 16 mailbox registers in one or more blocks. + * The first block is limited to 12 bytes. Other blocks can be 16 bytes. + * Between blocks, the firmware raises INT_MEMCPY when data is available. + */ +static void copyfrom_ne3200 (board_handle handle, unsigned int src, void *dest, + unsigned short n) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned long timeout; + unsigned int ioaddr = dev->base_addr; + unsigned int bm = ioaddr + NE3200_BMIC_OFFSET; + unsigned short block, bi; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + outw (cpu_to_be16 (src), bm + BMIC_MBOX_BADDR); + outw (cpu_to_be16 (n) , bm + BMIC_MBOX_COUNT); + outb (CMD_COPYFROMBOARD, bm + BMIC_MBOX_CMD); + for (block = 12, bi = 4; n > block; n -= block, block = 16, bi = 0) { + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (!(inb (ioaddr + BMIC_SYS_INT_STATUS) & INT_MEMCPY) + && jiffies <= timeout) + ; + for (; bi < 16; bi += 4) + *((long int *) dest)++ = inl (ioaddr + BMIC_MAILBOXES + bi); + outb (INT_MEMCPY, ioaddr + BMIC_SYS_INT_STATUS); + } + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (bm + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + for (; n >= 4; bi += 4, n -= 4) + *((long int *) dest)++ = inl (ioaddr + BMIC_MAILBOXES + bi); + for (; n > 0; bi ++, n --) + *((char *) dest)++ = inb (ioaddr + BMIC_MAILBOXES + bi); + if (inb (bm + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr, "CMD_COPYFROMBOARD"); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* Copy from memory to the NE3200 RAM. Needs the firmware to be running. + * Writes 32 kB in 60 ms or about 533 kB/s. + * Data is transferred through the 16 mailbox registers in one or more blocks. + * The first block is limited to 12 bytes. Other blocks can be 16 bytes. + * Between blocks, the firmware raises INT_MEMCPY when data has been read. + */ +static void copyto_ne3200 (board_handle handle, void *src, unsigned int dest, + unsigned short n) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + unsigned long timeout; + unsigned int ioaddr = dev->base_addr; + unsigned int bm = ioaddr + NE3200_BMIC_OFFSET; + unsigned short block, bi; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + outw (cpu_to_be16 (dest), bm + BMIC_MBOX_BADDR); + outw (cpu_to_be16 (n) , bm + BMIC_MBOX_COUNT); + + for (block = 12, bi = 4; n > block; n-= block, block = 16, bi = 0) { + for (; bi < 16; bi += 4) + outl (*((long int *) src)++, ioaddr + BMIC_MAILBOXES + bi); + if (block == 12) + outb (CMD_COPYTOBOARD, bm + BMIC_MBOX_CMD); + else + outb (INT_MEMCPY, ioaddr + BMIC_SYS_INT_STATUS); + + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (!(inb (ioaddr + BMIC_SYS_INT_STATUS) & INT_MEMCPY) + && jiffies <= timeout) + ; + } + for (; n >= 4; bi += 4, n-= 4) + outl (*((long int *) src)++, ioaddr + BMIC_MAILBOXES + bi); + for (; n > 0; bi ++, n--) + outb (*((char *) src)++, ioaddr + BMIC_MAILBOXES + bi); + + if (block == 12) + outb (CMD_COPYTOBOARD, bm + BMIC_MBOX_CMD); + else + outb (INT_MEMCPY, ioaddr + BMIC_SYS_INT_STATUS); + + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (bm + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + if (inb (bm + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr, "CMD_COPYTOBOARD"); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* Copy from DMA'able memory to NE3200 RAM. + * Writes 32 kB in xx ms or about xxx kB/s. + * Data is transferred using DMA in 1 or 2 fragments. + */ +static __inline__ +void dma_to_ne3200 (board_handle handle, u16 dest1, dma_addr_t src1, u16 count1, + u8 oddbyte1, dma_addr_t src2, u16 count2, u8 oddbyte2) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + ulong timeout; + uint ioaddr = dev->base_addr; + uint bm = ioaddr + NE3200_BMIC_OFFSET; + u16 dest2 = dest1 + count1; + u8 flags; + + /* Adjust byte count and source if destination is odd. */ + count1 -= dest1 & 1; + src1 += dest1 & 1; + count2 -= dest2 & 1; + src2 += dest2 & 1; + + flags = ne3200_irqsave (ioaddr); + spin_lock (& nl->fw_lock); + outw (cpu_to_be16 (dest1), bm + BMIC_MBOX_BADDR); + outw (cpu_to_le16 (count1), bm + BMIC_MBOX_COUNT); + outl (cpu_to_le32 (src1), bm + BMIC_MBOX_HADDR1); + outw (cpu_to_le16 (count2), bm + BMIC_MBOX_COUNT3); + outl (cpu_to_le32 (src2), bm + BMIC_MBOX_HADDR2); + outb (oddbyte2, bm + BMIC_MBOX_ODDBYTE2); + outb (oddbyte1, bm + BMIC_MBOX_ODDBYTE1); + outb (CMD_FRAGTOBOARD, bm + BMIC_MBOX_CMD); + + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (bm + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + if (inb (bm + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr, "CMD_FRAGTOBOARD"); + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (ioaddr, flags); +} + +/* Copy from socket buffer to NE3200 RAM. Needs the firmware to be running. + * Because the DMA channel appends a junk byte to all transfers with an + * unaligned ending address, all DMA transfers must be ordered. That's why it + * is so complicated. + */ +static void skb_to_ne3200 (board_handle handle, struct sk_buff *skb, + u8 *trailer, uint trailersize, uint dest) +{ + struct net_device *dev = (struct net_device *) handle.p; + struct net_local *nl = (struct net_local *) dev->priv; + dma_addr_t src1, src2; + uint dmalen1, dmalen2; + uint frag_len; + u16 dest2; + u8 oddbyte1, oddbyte2; + + frag_len = skb_headlen (skb); + dest2 = dest + frag_len; + if (netif_msg_tx_queued (& nl->board)) + printk (KERN_DEBUG "%s: Buffer fragment -1 of %5hu " + "bytes at 0x%p to %#04hx.\n", dev->name, + frag_len, skb->data, dest); + if (likely (frag_len > 12)) { + src1 = pci_map_single (NULL, skb->data, frag_len, PCI_DMA_TODEVICE); + dmalen1 = frag_len; + oddbyte1 = skb->data[0]; + } else { + copyto_ne3200 (handle, skb->data, dest, frag_len); + dest += frag_len; + src1 = 0; + dmalen1 = 0; + oddbyte1 = 0; + } + if (likely (trailersize > 12)) { + src2 = pci_map_single (NULL, trailer, trailersize, PCI_DMA_TODEVICE); + dmalen2 = trailersize; + oddbyte2 = trailer[0]; + } else { + src2 = 0; + dmalen2 = 0; + oddbyte2 = 0; + } + if (unlikely (frag_len <= 12)) { + src1 = src2; + dmalen1 = dmalen2; + oddbyte1 = oddbyte2; + dmalen2 = 0; + } + if (dmalen1) { + dma_to_ne3200 (handle, dest, src1, dmalen1, oddbyte1, + src2, dmalen2, oddbyte2); + pci_unmap_single (NULL, src1, dmalen1, PCI_DMA_TODEVICE); + } + if (dmalen2) + pci_unmap_single (NULL, src2, dmalen2, PCI_DMA_TODEVICE); + if (unlikely (trailersize <= 12)) + copyto_ne3200 (handle, trailer, dest2, trailersize); + +#if 0 /* temporarily unimplemented */ + skb_frag_t *frag; + u8 *frag_data; + uint fragnr; + for (fragnr = 0; + fragnr < skb_shinfo (skb)->nr_frags; + fragnr ++, dest += frag->size) + { + frag = &skb_shinfo (skb)->frags[fragnr]; + + frag_data = kmap_skb_frag (frag) + frag->page_offset; + if (netif_msg_tx_queued (& nl->board)) + printk (KERN_DEBUG "%s: Buffer fragment %2u of %5hu " + "bytes at 0x%p to %#04hx.\n", dev->name, + fragnr, frag->size, frag_data, dest); + if (frag->size > 12) { + src = pci_map_page (NULL, frag->page, frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + dma_to_ne3200 (handle, src, dest, frag->size, frag_data[0]); + pci_unmap_page (NULL, src, frag->size, PCI_DMA_TODEVICE); + } else + copyto_ne3200 (handle, frag_data, dest, frag->size); + kunmap_skb_frag (frag_data - frag->page_offset); + } +#endif +} + +/* Copy from NE3200 RAM to a socket buffer. Needs the firmware to be running. */ +static struct sk_buff *skb_from_ne3200 (board_handle h, uint frag1, uint size1, + uint frag2, uint size2) +{ + struct net_device *dev = (struct net_device *) h.p; + struct net_local *nl = (struct net_local *) dev->priv; + struct sk_buff *skb; + unsigned long int timeout; + dma_addr_t dest; + uint bm = dev->base_addr + NE3200_BMIC_OFFSET; + uint pktlen; + u8 flags; + + /* Save the packet length for later. */ + pktlen = size1 + size2; + + /* Sanitise the fragment lengths. */ + if (unlikely (size1 < 6)) { + if (unlikely (size1 == 0)) { + size1 = size2; + frag1 = frag2; + size2 = 0; + } else { + size1 = 6; + size2 = 0; /* XXX Dubious, but necessary. */ + } + } + if (unlikely (size2 > 0 && size2 < 6)) + size2 = 6; + + skb = dev_alloc_skb (2 + size1 + size2); + if (skb == NULL) + return (skb); + + /* Longword align IP header. */ + skb_reserve (skb, 2); + dest = pci_map_single (NULL, skb->tail, size1 + size2, PCI_DMA_FROMDEVICE); + skb_put (skb, pktlen); + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& nl->fw_lock); + outw (cpu_to_le16 (frag1 / 2), bm + BMIC_MBOX_BADDR); + outw (cpu_to_le16 (size1), bm + BMIC_MBOX_COUNT); + outb (CMD_FRAGFROMBOARD_DIR, bm + BMIC_MBOX_DIR); + outl (cpu_to_le32 (dest), bm + BMIC_MBOX_HADDR); + outw (cpu_to_be16 (size2), bm + BMIC_MBOX_COUNT2); + outw (cpu_to_be16 (frag2 / 2), bm + BMIC_MBOX_BADDR2); + outb (CMD_FRAGFROMBOARD, bm + BMIC_MBOX_CMD); + + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (bm + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + if (inb (bm + BMIC_MBOX_CMD)) + firmware_timeout_debug (dev->base_addr, "CMD_FRAGFROMBOARD"); + + spin_unlock (& nl->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); + pci_unmap_single (NULL, dest, size1 + size2, PCI_DMA_FROMDEVICE); + return (skb); +} + +/* Tells the firmware to reset the 82586. */ +static void reset586_ne3200 (struct net_device *dev) +{ + struct net_local *np = (struct net_local *) dev->priv; + unsigned long timeout; + unsigned int ioaddr = dev->base_addr + NE3200_BMIC_OFFSET; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& np->fw_lock); + outb (CMD_RESET_82586, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_RESET_82586"); + spin_unlock (& np->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* Tells the firmware to send Channel Attention to the 82586. */ +static void ca586_ne3200 (struct net_device *dev) +{ + struct net_local *np = (struct net_local *) dev->priv; + unsigned long timeout; + unsigned int ioaddr = dev->base_addr + NE3200_BMIC_OFFSET; + unsigned char flags; + + flags = ne3200_irqsave (dev->base_addr); + spin_lock (& np->fw_lock); + outb (CMD_CA_82586, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_CA_82586"); + spin_unlock (& np->fw_lock); + ne3200_irqrestore (dev->base_addr, flags); +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +net_open(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int ioaddr = dev->base_addr; + + /* + * This is used if the interrupt line can turned off (shared). + * See 3c503.c for an example of selecting the IRQ at config-time. + */ + if (lp->level_triggered) { + if (request_irq (dev->irq, net_interrupt, + lp->level_triggered * SA_SHIRQ, + dev->name, dev)) { + return -EAGAIN; + } + } + /* Now, enable interrupts. */ + outb (INT_82586, ioaddr + BMIC_SYS_INT_STATUS); /* Clear. */ + outb (INT_82586, ioaddr + BMIC_SYS_INT_MASK); /* Unmask. */ + outb (1, ioaddr + BMIC_SYS_INT_MASK_CTRL); /* Enable. */ + + i82586_open (& lp->board); + return 0; +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int ioaddr = dev->base_addr; + + i82586_close (& lp->board); + + /* If not IRQ or DMA jumpered, free up the line. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK); /* Mask all interrupts. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK_CTRL); /* Release the physical interrupt line. */ + if (lp->level_triggered) + free_irq (dev->irq, dev); + return 0; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *net_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + /* Update the statistics from the device registers. */ + return (i82586_update_stats (& lp->board)); +} + +/* This function is called when TX queue has been stopped for too long. */ +static void net_tx_timeout(struct net_device *dev) +{ + struct net_local *np = (struct net_local *)dev->priv; + + i82586_tx_timeout (& np->board); +} + +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ +static int net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *np = (struct net_local *)dev->priv; + + return (i82586_send_packet (& np->board, skb)); +} + +/* + * Set the receive mode, including the multicast filter, of this ne3200 board. + * Currently, the board's firmware is not used for additional filtering, so + * the default 82586 code is used. + */ +static void ne3200_set_rx_mode (struct net_device *dev) +{ + struct i82586_board *board; + + board = &((struct net_local *) dev->priv)->board; + i82586_set_rx_mode (board); +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static irqreturn_t net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct net_local *np; + int ioaddr; + u16 mask_status; + u8 status, mask; + + if (! dev_id) { + printk (KERN_CRIT "ne3200: Interrupt %d with NULL dev_id!\n", + irq); + return IRQ_RETVAL (IRQ_NONE); + } + + ioaddr = dev->base_addr; + np = (struct net_local *)dev->priv; + + /* See which interrupts are enabled and which ones are active. */ + mask_status = be16_to_cpu (inw (ioaddr + BMIC_SYS_INT_MASK)); + mask = (mask_status >> 8) & 0xff; + status = (mask_status >> 0) & 0xff; + + if (netif_msg_intr (& np->board)) + printk (KERN_DEBUG "%s: NE3200 interrupt: status = 0x%02x, " + "mask = 0x%02x.\n", dev->name, status, mask); + + /* If no enabled interrupts are active, do nothing and exit. + * This can happen for two reasons: + * 1) The interrupt was for another device (shared interrupt). + * 2) The interrupt was delivered late, i.e. after board interrupts were + * disabled. This may happen with SMP interrupt delivery. In this + * case, the interrupt will be handled when board interrupts are + * reenabled. + */ + if ((status & mask) == 0) + return IRQ_RETVAL (IRQ_NONE); + + /* Catch firmware crashes. */ + if (unlikely ((status & mask) & INT_FWFAULT)) { + print_firmware_crashdump (dev->base_addr, status); + outb (INT_FWFAULT, ioaddr + BMIC_SYS_INT_STATUS); + return IRQ_RETVAL (IRQ_HANDLED); + } + + /* Clear active interrupts. */ + outb (status & mask, ioaddr + BMIC_SYS_INT_STATUS); + + /* Handle 82586 related interrupts. */ + if ((status & mask) & INT_82586) + i82586_process_interrupt (& np->board); + return IRQ_RETVAL (IRQ_HANDLED); +} + +static int ne3200_change_mtu (struct net_device *dev, int mtu) +{ + struct net_local *np = dev->priv; + + return (i82586_change_mtu (& np->board, mtu)); +} + +/* Reset the board and upload firmware. Returns nonzero in case of failure. */ +static int ne3200_reset (unsigned int ioaddr) +{ + void *fwcopy; + dma_addr_t fw_dma; + unsigned int status; + unsigned long timeout, start, stop; + unsigned int m; + + /* Pull the reset line. Clearing BMIC_MAILBOXES + 0 here makes + * it possible to check for a working ROM firmware. */ + outl (1UL, ioaddr + NE3200_BOARD_RESET); + outb (0U, ioaddr + BMIC_MAILBOXES + 0); + set_current_state (TASK_UNINTERRUPTIBLE); + schedule_timeout ((HZ + reset_delay - 1) / reset_delay); + outl (0UL, ioaddr + NE3200_BOARD_RESET); + + /* Wait for the ROM firmware to respond. There is no need to poll more + * often than 100 times per second because it takes so long (about 200 + * ms) to get a response from the board. + */ + if (debug & NETIF_MSG_HW) + printk (KERN_DEBUG "ne3200: Waiting for response from ROM firmware..."); + timeout = jiffies + (HZ + fw_timeout - 1) / fw_timeout; + start = jiffies; + do { + set_current_state (TASK_UNINTERRUPTIBLE); + schedule_timeout (HZ < 100 ? 1 : HZ / 100); + status = inb (ioaddr + BMIC_MAILBOXES + 0); + } while (status == 0 && jiffies <= timeout); + stop = jiffies; + if (debug & NETIF_MSG_HW) + printk (" %lu ms.\n", (1000 * (stop - start)) / HZ); + + if ((status & RFWF_OK_WORKING) == 0) { + if (status == 0) + printk (KERN_ERR "ne3200: ROM firmware failed (0x%x).\n", status); + else if (status & RFWF_RAM_FAILURE) + printk (KERN_ERR "ne3200: Onboard RAM failure reported by ROM firmware (0x%x)\n", + status); + else + printk (KERN_ERR "ne3200: ROM firmware checksum error (0x%x).\n", + status); + + return (-ENODEV); /* FIXME correct error code. */ + } + + /* pci_map_single() doesn't work on the static copy. */ + fwcopy = kmalloc (firmware_size, GFP_KERNEL); + if (fwcopy == 0) { + printk (KERN_ERR "ne3200: No memory for firmware upload.\n"); + return (-ENOMEM); + } + memcpy (fwcopy, firmware_data, firmware_size); + fw_dma = pci_map_single (NULL, fwcopy, firmware_size, PCI_DMA_TODEVICE); + + if (debug & NETIF_MSG_HW) + printk (KERN_DEBUG "ne3200: Uploading firmware (virt = 0x%p, bu" + "s = 0x%p, phys = 0x%08lx, size = 0x%04x)...\n", fwcopy, + (void *) fw_dma, virt_to_phys (fwcopy), firmware_size); + + /* This is the magic sequence that starts a firmware upload. */ + m = ioaddr + BMIC_MAILBOXES; + outb (firmware_size & 0xff, m + 1); + outb (firmware_size >> 8, m + 2); + outl (cpu_to_le32 (fw_dma), m + 4); + outl (0UL, m + 8); + outb (0x00U, m + 0); + + /* Wait for the uploaded firmware to show signs of life. */ + if (debug & NETIF_MSG_HW) { + print_mailboxes (ioaddr); + printk (KERN_DEBUG "ne3200: Waiting for response from RAM firmware..."); + } + timeout = jiffies + HZ / fw_timeout; + start = jiffies; + do + ; + while ((inb (m + 1) != 9 || inb (m + 2) != 8 || inb (m + 3) != 7 || + inb (m + 4) != 6 || inb (m + 5) != 5 || inb (m + 6) != 4 || + inb (m + 7) != 3 || inb (m + 8) != 2 || inb (m + 9) != 1 || + inb (m + 0) != 10) && jiffies <= timeout); + stop = jiffies; + if (debug & NETIF_MSG_HW) + printk (" %lu ms.\n", (1000 * (stop - start)) / HZ); + + pci_unmap_single (NULL, fw_dma, firmware_size, PCI_DMA_TODEVICE); + kfree (fwcopy); + + if (debug & NETIF_MSG_HW) + print_mailboxes (ioaddr); + + if (stop >= timeout) { + status = inb (ioaddr + BMIC_SYS_INT_STATUS); + outb (0xff, ioaddr + BMIC_SYS_INT_STATUS); + printk (KERN_ERR "ne3200: Firmware upload failed: 0x%x.\n", status); + return (-ENODEV); /* FIXME error code */ + } + + /* The firmware is waiting for us to trigger an 80186 interrupt. */ + outb (0xff, ioaddr + BMIC_LOCAL_INT_STATUS); + + /* Wait until the firmware can accept commands. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_LOCAL_INT_STATUS) && jiffies <= timeout) + ; + if (jiffies > timeout) + printk (KERN_ERR "ne3200: Firmware irq detection timed out.\n"); + + if (debug & NETIF_MSG_HW) + print_interrupt_info (ioaddr); + return (0); +} + +/* Allocate the I/O port ranges used by the board. */ +static int __init request_regions (int ioaddr) +{ + if (! request_region (ioaddr + NE3200_BOARD_RESET, 4, cardname)) + return (0); + else if (! request_region (ioaddr + NE3200_BOARD_CONFIG, 4, cardname)) { + release_region (ioaddr + NE3200_BOARD_RESET, 4); + return (0); + } else if (! request_region (ioaddr + NE3200_BMIC_OFFSET, + NETCARD_IO_EXTENT, cardname)) { + release_region (ioaddr + NE3200_BOARD_CONFIG, 4); + release_region (ioaddr + NE3200_BOARD_RESET, 4); + return (0); + } else + return (1); +} + +/* Tell the firmware to trigger an interrupt from the board. */ +static void interrupt_from_ne3200 (unsigned int ioaddr) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outb (CMD_TRIGGER_IRQ, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + if (inb (ioaddr + BMIC_MBOX_CMD)) + firmware_timeout_debug (ioaddr - NE3200_BMIC_OFFSET, "CMD_TRIGGER_IRQ"); +} + +/* Dump the contents of the BMIC registers. */ +static void print_registers (unsigned int ioaddr) +{ + unsigned int i = ioaddr + BMIC_EISA_ID; + printk (KERN_DEBUG "ne3200: Registers = " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x.\n", inb (i+0), + inb (i+1), inb (i+2), inb (i+3), inb (i+4), inb (i+5), + inb (i+6), inb (i+7), inb (i+8), inb (i+9), inb (i+10), + inb (i+11), inb (i+12), inb (i+13), inb (i+14), inb (i+15)); +} + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probe avoids doing writes, and + * verifies that the correct device exists and functions. + */ +static int __init ne3200_probe1 (uint ioaddr, uint index) +{ + struct net_device *dev; + int i; + unsigned long eisa_id; + struct net_local *np; + struct i82586_board *b; + uint irq_probed = 0, do_board_config = 0; + + /* Grab the regions so that no one else tries to probe our ioports. */ + if (! request_regions (ioaddr)) + return (-ENODEV); + + eisa_id = be32_to_cpu (inl (ioaddr + BMIC_EISA_ID)); + + /* Debug: Print the EISA ID of probed slots. */ + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: Probe at 0x%04x (EISA slot %2u), EISA ID 0x%08lx\n", + ioaddr, ioaddr / 0x1000U, eisa_id); + + /* Check for an NE3200 or compatible board. */ + if ((eisa_id & ~255) == MK_EISA_ID (NE3200_VENDOR, NE3200_PROD, 0) || + (extra_eisa_id != 0 && eisa_id == extra_eisa_id)) + { + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: Found NE3200 board at 0x%04x.\n", ioaddr); + } + else { + release_regions (ioaddr); + return (-ENODEV); /* Board not found. */ + } + + if (debug & NETIF_MSG_HW) + print_iospace (ioaddr); + + /* Reset board and upload firmware. */ + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: Resetting board...\n"); + i = ne3200_reset (ioaddr); + if (i != 0) { + release_regions (ioaddr); + return (i); + } + + /* + * It looks like we have found a board, so allocate a device + * structure for it. + */ + dev = alloc_etherdev (sizeof (struct net_local)); + if (dev == NULL) { + release_regions (ioaddr); + return -ENOMEM; + } + + if (debug && index == 0) + printk(KERN_DEBUG "%s", version); + + /* Fill in the 'dev' fields. */ + np = (struct net_local *) dev->priv; + SET_MODULE_OWNER (dev); + dev->base_addr = ioaddr; + if (index < MAX_OPTION_BOARDS) { + dev->irq = irq [index]; + dev->if_port = media [index]; + do_board_config = configure_board [index]; + switch (irq_type[index]) { + case 1: /* Level triggered. */ + np->irq_type_valid = 1; + np->level_triggered = 1; + break; + + case 2: /* Edge triggered. */ + np->irq_type_valid = 1; + np->level_triggered = 0; + break; + + default: + np->irq_type_valid = 0; + break; + } + } else { + dev->irq = 0; + dev->if_port = 0; + do_board_config = 0; + np->irq_type_valid = 0; + } + + /* The firmware copies the Ethernet station address from the SA + * PROM to the BMIC mailbox registers, so retrieve it now before + * it is overwritten by communication with the firmware. + */ + for (i = 0; i < ETH_ALEN; i ++) + dev->dev_addr[i] = inb (ioaddr + NE3200_BMIC_OFFSET + + BMIC_MBOX_STATION_ADDR + i); + + if (debug & NETIF_MSG_HW) { + print_station_address (ioaddr); + print_prom (ioaddr); + ram_speed_test (ioaddr); + } + /* Find the board irq, usually by probing for it. */ + if (dev->irq == -1) + ; /* Do nothing: a user-level program will set it. */ + else if (dev->irq <= 0 || np->irq_type_valid == 0) { /* "Auto-IRQ" */ + unsigned long setup1, mask1, setup2, mask2; + + /* Disable board interrupt for robustness. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK_CTRL); /* Master disable. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK); /* Disable interrupt. */ + outb (0xFF, ioaddr + BMIC_SYS_INT_STATUS); /* Clear interrupt. */ + if (debug & NETIF_MSG_PROBE) + print_registers (ioaddr); + + setup1 = probe_irq_on (); + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: IRQ probe 1 setup: %#07lo.\n", setup1); + + /* Enable board interrupt here. */ + outb (0xFF, ioaddr + BMIC_SYS_INT_STATUS); /* Clear interrupt. */ + outb (0xFF, ioaddr + BMIC_SYS_INT_MASK); /* Enable interrupt. */ + outb (0x01, ioaddr + BMIC_SYS_INT_MASK_CTRL); /* Master enable. */ + if (debug & NETIF_MSG_PROBE) + print_registers (ioaddr); + + /* Trigger an interrupt using the 80186 on the board. */ + interrupt_from_ne3200 (ioaddr); + if (debug & NETIF_MSG_PROBE) + print_registers (ioaddr); + + mask1 = probe_irq_mask (setup1); + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: IRQ probe 1 returned %#07lo.\n", mask1); + + /* See if the interrupt line stays active. */ + setup2 = probe_irq_on (); + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: IRQ probe 2 setup: %#07lo.\n", setup2); + mask2 = probe_irq_mask (setup2); + if (debug & NETIF_MSG_PROBE) + printk (KERN_DEBUG "ne3200: IRQ probe 2 returned %#07lo.\n", mask2); + + /* Disable board interrupt again. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK_CTRL); /* Master disable. */ + outb (0x00, ioaddr + BMIC_SYS_INT_MASK); /* Disable interrupt. */ + outb (0xFF, ioaddr + BMIC_SYS_INT_STATUS); /* Clear interrupt. */ + + if (debug & NETIF_MSG_PROBE) + print_registers (ioaddr); + + /* Find the (hopefully only) detected irq. */ + { + unsigned short i; + for (i = 0; i < 16; i ++) { + if (mask1 & (1 << i)) + dev->irq = i; + } + } + /* Determine level vs. edge triggered interrupt. */ + if ((setup1 & mask1) != (setup2 & mask1)) + np->level_triggered = 1; + np->irq_type_valid = 1; + irq_probed = 1; + + } else if (dev->irq == 2) + /* + * Fixup for users that don't know that IRQ 2 is really + * IRQ9, or don't know which one to set. + */ + dev->irq = 9; + + if (do_board_config && dev->irq > 0 && np->irq_type_valid && + dev->if_port >= 1 && dev->if_port <= 3) { + unsigned char irqflags, mediaflags; + + if (dev->if_port == IF_PORT_AUI) + mediaflags = NBCF_MEDIA_AUI; + else + mediaflags = NBCF_MEDIA_BNC; + + switch (dev->irq) { + case 5: irqflags = NBCF_IRQ_LINE_5; break; + case 9: irqflags = NBCF_IRQ_LINE_9; break; + case 10: irqflags = NBCF_IRQ_LINE_10; break; + case 11: irqflags = NBCF_IRQ_LINE_11; break; + case 15: irqflags = NBCF_IRQ_LINE_15; break; + default: irqflags = 0; break; + } + if (irqflags) { + if (np->level_triggered) + irqflags |= NBCF_IRQ_TYPE_LEVEL; + else + irqflags |= NBCF_IRQ_TYPE_EDGE; + outb (mediaflags | irqflags, ioaddr + NE3200_BOARD_CONFIG); + } + } + /* If the interrupt is edge triggered, we can't share it with another + * device, so allocate it now to mark it as used. Don't do so if it is + * level triggered (and thus possibly shared) because it is dangerous + * to run the irq probe when a handler has been installed for the + * interrupt for another device. It could deadlock because the + * interrupt from *this* board needs to be acknowledged and the + * installed interrupt handlers for *other* devices won't be able to + * do so. The probe routine knows how to do so but will be preempted by + * the interrupt handlers before it gets a chance to do so. Hopefully + * multiple NE3200 boards sharing an interrupt won't cause problems. + * FIXME Check to see if the interrupt line can be tristated even when + * configured for edge triggering. Some other boards allow it. + */ + if (! np->level_triggered) + { + int irqval = request_irq (dev->irq, net_interrupt, + np->level_triggered * SA_SHIRQ, + cardname, dev); + if (irqval) { + printk (KERN_ERR "%s/%u: unable to get IRQ %d (irqval=%d).\n", + cardname, index, dev->irq, irqval); + release_regions (ioaddr); + free_netdev (dev); + return -EAGAIN; + } + } + + if (debug & NETIF_MSG_HW) + probe_80186_pcb (ioaddr); + + /* Initialize the device structure. */ + spin_lock_init (& np->fw_lock); + b = &np->board; + + b->dev = dev; + b->msg_enable = debug; + b->handle.p = dev; + b->readw = lreadw_ne3200; + b->writew = lwritew_ne3200; + b->reada = lreada_ne3200; + b->writea = lwritea_ne3200; + b->copy_fromboard = copyfrom_ne3200; + b->copy_toboard = copyto_ne3200; + b->skb_toboard = skb_to_ne3200; + b->skb_fromboard = skb_from_ne3200; + b->reset = reset586_ne3200; + b->ca = ca586_ne3200; + + /* Looks like the 82586 can't use the first 16 kB RAM block. :-( */ + /* There's a DMA prefetch bug for the last 2 bytes. :-( */ + b->mem_start = NE3200_RAM2_START, + b->mem_size = NE3200_RAM2_SIZE - 2; + b->scp_addr = 0xfff4; + b->iscp_addr = (uint) -1; + b->scb_addr = (uint) -1; + /* The rest is set up by the i82586 code. */ + + dev->open = net_open; + dev->stop = net_close; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + dev->set_multicast_list = ne3200_set_rx_mode; + dev->tx_timeout = net_tx_timeout; + dev->change_mtu = ne3200_change_mtu; + i82586_setup_dev (& np->board); + /* The board doesn't support 64-bit addressing. SG is unimplemented. */ + dev->features &= ~(NETIF_F_HIGHDMA | NETIF_F_SG); + + /* Now the firmware needs to detect the 82586 irq. */ + if (netif_msg_probe (b)) + printk (KERN_DEBUG "ne3200: Firing up 82586 to detect irq.\n"); + + i82586_init (& np->board); + { + unsigned long timeout; + unsigned int bm = ioaddr + NE3200_BMIC_OFFSET; + outb (CMD_DETECT_586IRQ, bm + BMIC_MBOX_CMD); + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (bm + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + if (inb (bm + BMIC_MBOX_CMD)) { + firmware_timeout_debug (ioaddr, "CMD_DETECT_586IRQ"); + if (! np->level_triggered) + free_irq (dev->irq, dev); + release_regions (ioaddr); + free_netdev (dev); + return (-ENODEV); + } + } + reset586_ne3200 (dev); + + if (netif_msg_probe (b)) { + print_interrupt_info (ioaddr); + print_interrupt_stats (ioaddr); + } + np->next_dev = first_dev; + first_dev = dev; + register_netdev (dev); + printk (KERN_INFO "%s: %s rev %lu at %#4x, %2.2x %2.2x %2.2x %2.2x " + "%2.2x %2.2x, %s triggered irq %d %s.\n", dev->name, cardname, + eisa_id & 255, ioaddr, dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], + dev->dev_addr[5], np->level_triggered ? "level" : "edge", + dev->irq, irq_probed ? "autodetected" : "set by user"); + return 0; +} + +/* Print information about interrupts detected by the firmware. */ +static void print_interrupt_info (unsigned int ioaddr) +{ + unsigned int ds = firmware_size; + printk (KERN_DEBUG "ne3200: inserv = 0x%x, bogus = 0x%x, " + "bogus2 = 0x%x, 82355 irq = %u, 82586 irq = %u.\n", + readw_ne3200 (ioaddr, ds+0), readw_ne3200 (ioaddr, ds+2), + readw_ne3200 (ioaddr, ds+4), + readw_ne3200 (ioaddr, ds+6) & 0xff, + readw_ne3200 (ioaddr, ds+6) >> 8); +} + +/* Print interrupt statistics collected by the firmware. */ +static void print_interrupt_stats (unsigned int ioaddr) +{ + printk (KERN_DEBUG "ne3200: Handled %u commands in %u interrupts.\n", + readw_ne3200 (ioaddr, firmware_size + 8) + + (readw_ne3200 (ioaddr, firmware_size + 10) << 16), + readw_ne3200 (ioaddr, firmware_size + 12)); +} + +/* See how fast we can read and write the onboard RAM. */ +static void ram_speed_test (unsigned int ioaddr) +{ + unsigned int i, block; + unsigned long start, stop, timeout; + unsigned short int mb = ioaddr + NE3200_BMIC_OFFSET; +#if 0 + start = jiffies; + for (i = NE3200_RAM2_START; i < NE3200_RAM2_START + NE3200_RAM2_SIZE; i += 2) + readw_ne3200 (ioaddr, i); + stop = jiffies; + printk (KERN_DEBUG "ne3200: Reading 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); + start = jiffies; + for (i = NE3200_RAM2_START; i < NE3200_RAM2_START + NE3200_RAM2_SIZE; i += 2) + reada_ne3200 (ioaddr, i); + stop = jiffies; + printk (KERN_DEBUG "ne3200: Reading 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); +#endif + start = jiffies; + i = NE3200_RAM2_SIZE; + outw (cpu_to_be16(NE3200_RAM2_START), mb + BMIC_MBOX_BADDR); + outw (cpu_to_be16(i), mb + BMIC_MBOX_COUNT); + outb (CMD_COPYFROMBOARD, mb + BMIC_MBOX_CMD); + for (block = 12; i > block; i -= block, block = 16) { + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (!(inb (ioaddr + BMIC_SYS_INT_STATUS) & INT_MEMCPY) + && jiffies <= timeout) + ; + outb (INT_MEMCPY, ioaddr + BMIC_SYS_INT_STATUS); + } + stop = jiffies; + printk (KERN_DEBUG "ne3200: Reading 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); + if (stop > timeout) + printk (KERN_DEBUG "ne3200: timeout on the last %u bytes.\n", i); +#if 0 + start = jiffies; + for (i = NE3200_RAM2_START; i < NE3200_RAM2_START + NE3200_RAM2_SIZE; i += 2) + writew_ne3200 (ioaddr, i, 0); + stop = jiffies; + printk (KERN_DEBUG "ne3200: Writing 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); + start = jiffies; + for (i = NE3200_RAM2_START; i < NE3200_RAM2_START + NE3200_RAM2_SIZE; i += 2) + writea_ne3200 (ioaddr, i, 0); + stop = jiffies; + printk (KERN_DEBUG "ne3200: Writing 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); +#endif + start = jiffies; + i = NE3200_RAM2_SIZE; + outw (cpu_to_be16(NE3200_RAM2_START), mb + BMIC_MBOX_BADDR); + outw (cpu_to_be16(i), mb + BMIC_MBOX_COUNT); + outb (CMD_COPYTOBOARD, mb + BMIC_MBOX_CMD); + for (block = 12; i > block; i -= block, block = 16) { + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (!(inb (ioaddr + BMIC_SYS_INT_STATUS) & INT_MEMCPY) + && jiffies <= timeout) + ; + outb (INT_MEMCPY, ioaddr + BMIC_SYS_INT_STATUS); + } + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (mb + BMIC_MBOX_CMD) && jiffies <= timeout) + ; + stop = jiffies; + printk (KERN_DEBUG "ne3200: Writing 32 kB of onboard RAM took %lu ms.\n", (1000 * (stop - start)) / HZ); + if (stop > timeout) + printk (KERN_DEBUG "ne3200: timeout on the last %u bytes.\n", i); +} + +/* Dump the I/O space of the board. */ +static void print_iospace (unsigned int ioaddr) +{ + unsigned int i; + + printk (KERN_DEBUG "ne3200: Dumping the I/O space.\n"); + for (i = 0; i < 0x1000; i ++) { + /* Avoid adresses reserved for ISA cards! */ + if ((i & 0x0300) != 0x0000) + continue; + if (i % 32 == 0) + printk (KERN_DEBUG "ne3200: 0x%04x:", ioaddr + i); + /* Avoid reading write-only registers. */ + if ((i & ~3) == NE3200_BOARD_RESET || + (i & ~3) == NE3200_BOARD_CONFIG) + printk (" ??"); + else + printk (" %2x", inb (ioaddr + i)); + if (i % 32 == 31) + printk ("\n"); + } +} + +/* Print he station address from mailboxes and directly from the PROM for + * comparison purposes. They should match. */ +static void print_station_address (unsigned int ioaddr) +{ + unsigned int i; + unsigned long start, stop; + + printk (KERN_DEBUG "ne3200: Ethernet station address (MBOX) ="); + for (i = NE3200_BMIC_OFFSET + BMIC_MBOX_STATION_ADDR; + i < NE3200_BMIC_OFFSET + BMIC_MBOX_STATION_ADDR + ETH_ALEN; i ++) + printk (" %02x", inb (ioaddr + i)); + printk (".\n"); + + start = jiffies; + printk (KERN_DEBUG "ne3200: Ethernet station address (PROM) ="); + for (i = NE3200_PROM_START; i < NE3200_PROM_START + ETH_ALEN * 2; i += 2) + printk (" %02x", readw_ne3200 (ioaddr, i) & 0xff); + stop = jiffies; + printk (" (%lu ms).\n", (1000 * (stop - start)) / HZ); +} + +/* Debug: Dump the board's PROM with the station address. */ +static void print_prom (unsigned int ioaddr) +{ + unsigned int i; + + printk (KERN_DEBUG "ne3200: Dumping the first 64 B of the onboard PROM.\n"); + for (i = 0x4000; i < 0x4040; i += 2) { + unsigned int tmp; + if (i % 32 == 0) + printk (KERN_DEBUG "ne3200: 0x%04x:", i); + tmp = readw_ne3200 (ioaddr, i); + printk (" %2x %2x", tmp >> 8, tmp & 0xff); + if (i % 32 == 30) + printk ("\n"); + } +} + +/* Try to find the 80186 Peripheral Control Block and print information about + * the way the chip select lines are used. Note that it tries to probe the 1 + * MB address space on the 80186, not just 64 kB, but readw_ne3200 only + * supports reading the first 64 kB. So a PCB above 64 kB won't be found. :-( + * The check for a valid PCB could be much better than the one used here. + */ +static void probe_80186_pcb (unsigned int ioaddr) +{ + unsigned long lmsize; + unsigned long mmstart, mmsize, mmend; + unsigned long umstart, umsize; + unsigned long pcs_base; + unsigned long base, pcb_base; + unsigned short (*wread) (unsigned int ioaddr, unsigned short addr); + unsigned short tmp; + unsigned char lmws, mmws, umws; + struct { + unsigned int cs65 : 1, + csmem : 1, + nobrcs30 : 1, + nobrcs64 : 1, + wscs30 : 2, + wscs64 : 2; + } pcsconf; + + printk (KERN_DEBUG "ne3200: Probing for Peripheral Control Block...\n"); + for (base = 0; base < 0x110000; base += 0x100) { + if (base < 0x100000) { + pcb_base = base; + tmp = readw_ne3200 (ioaddr, pcb_base + 0xfe); + if ((tmp & 0x1fff) != (0x1000 | (pcb_base >> 8))) + continue; + wread = &readw_ne3200; + } else { + pcb_base = base - 0x100000; + tmp = inw_ne3200 (ioaddr, pcb_base + 0xfe); + if ((tmp & 0x1fff) != (0x0000 | (pcb_base >> 8))) + continue; + wread = &inw_ne3200; + } + + printk (KERN_DEBUG "ne3200: Possible PCB found at 0x%05lx in %s space.\n", + pcb_base, base < 0x100000 ? "memory" : "I/O"); + + /* Detect upper memory. */ + tmp = (*wread) (ioaddr, pcb_base + 0xa0); + printk (KERN_DEBUG "ne3200: UMCS = 0x%04hx.\n", tmp); + umsize = 1024 * (256 - ((tmp >> 6) & 0xff)); + umstart = 1024*1024 - umsize; + umws = tmp & 7; + + /* Detect lower memory. */ + tmp = (*wread) (ioaddr, pcb_base + 0xa2); + printk (KERN_DEBUG "ne3200: LMCS = 0x%04hx.\n", tmp); + lmsize = 1024 * (((tmp >> 6) & 0xff) + 1); + lmws = tmp & 7; + + /* Detect middle memory. */ + tmp = (*wread) (ioaddr, pcb_base + 0xa6); + printk (KERN_DEBUG "ne3200: MMCS = 0x%04hx.\n", tmp); + mmstart = (tmp >> 9) << 13; + mmws = tmp & 7; + tmp = (*wread) (ioaddr, pcb_base + 0xa8); + printk (KERN_DEBUG "ne3200: MPCS = 0x%04hx.\n", tmp); + mmsize = ((tmp >> 8) & 0x7f) * 8192; + mmend = mmstart + mmsize; + + /* Detect peripherals. */ + pcsconf.cs65 = (tmp >> 7) & 1; + pcsconf.csmem = (tmp >> 6) & 1; + pcsconf.nobrcs64 = (tmp >> 2) & 1; + pcsconf.wscs64 = tmp & 3; + tmp = (*wread) (ioaddr, pcb_base + 0xa4); + printk (KERN_DEBUG "ne3200: PACS = 0x%04hx.\n", tmp); + pcs_base = (tmp >> 6) << 10; + pcsconf.nobrcs30 = (tmp >> 2) & 1; + pcsconf.wscs30 = tmp & 3; + + /* Prettyprint the results. */ + printk (KERN_DEBUG "ne3200: L mem %4lu - %4lu (%3lu kB).\n", + 0UL, lmsize / 1024, lmsize / 1024); + printk (KERN_DEBUG "ne3200: M mem %4lu - %4lu (%3lu kB).\n", + mmstart / 1024, mmend / 1024, mmsize / 1024); + printk (KERN_DEBUG "ne3200: U mem %4lu - %4lu (%3lu kB).\n", + umstart / 1024, 1024UL, umsize / 1024); + printk (KERN_DEBUG "ne3200: PCS at 0x%05lx in %s space.\n", + pcs_base, pcsconf.csmem ? "memory" : "I/O"); + printk (KERN_DEBUG "ne3200: PCS6-5 pins are configured as %s.\n", + pcsconf.cs65 ? "PCS6/PCS5" : "A2/A1"); + printk (KERN_DEBUG "ne3200: PCS6-4 use %u wait states %s bus ready.\n", + pcsconf.wscs64, pcsconf.nobrcs64 ? "w/o" : "and"); + printk (KERN_DEBUG "ne3200: PCS3-0 use %u wait states %s bus ready.\n", + pcsconf.wscs30, pcsconf.nobrcs30 ? "w/o" : "and"); + } +} + +/* Read a word from an 80186 I/O port. Needs the firmware to be running. */ +static unsigned short inw_ne3200 (unsigned int ioaddr, unsigned short addr) +{ + unsigned long int timeout; + + ioaddr += NE3200_BMIC_OFFSET; + outw (cpu_to_be16(addr),ioaddr + BMIC_MBOX_BADDR); + outb (CMD_READW_IO, ioaddr + BMIC_MBOX_CMD); + /* Wait for completion. Timeout >= 0.5 ms. */ + timeout = jiffies + (HZ > 1000 ? HZ / 1000 : 1); + while (inb (ioaddr + BMIC_MBOX_CMD) != 0 && jiffies <= timeout) + ; + return (le16_to_cpu(inw (ioaddr + BMIC_MBOX_DATA))); +} --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/ne3200.h Sat Oct 18 22:48:46 2003 @@ -0,0 +1,77 @@ +/* ne3200.h: Definitions for the NE3200 network driver for Linux. + * + * Written 2000, 2002-2003 by Rask Ingemann Lambertsen. + * + * Copyright 2000, 2002-2003 Rask Ingemann Lambertsen. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The author may be reached as . + * See ne3200.c for further information. + * + * Most of this file consists of definitions of the NE3200 hardware, + * however a small part is definitions of the firmware used by this + * driver. + * + */ + +/* WO means write only, RO means read only, RW means read/write. */ + +/* This is the map of the NE3200's directly accessible I/O ports. */ + +#define NE3200_BOARD_RESET 0x0000 /* Write 1UL to reset board (WO). */ +#define NE3200_BOARD_CONFIG 0x0800 /* IRQ and media conf. (WO) */ +#define NE3200_BMIC_OFFSET 0x0c80 /* Start of BMIC registers. */ +#define BMIC_EISA_ID 0x0c80 /* EISA four byte card ID. */ +#define BMIC_RESERVED_0 0x0c84 /* 0xc84 - 0xc87 reserved. */ +#define BMIC_GLOBAL_CFG_REG 0x0c88 /* I have no idea. :-( */ +#define BMIC_SYS_INT_MASK_CTRL 0x0c89 /* Bit 0 enables, bit 1 indicates. */ +#define BMIC_SEMAPHORES 0x0c8a /* Two addresses. */ +#define BMIC_LOCAL_INT_MASK 0x0c8c /* Local = NE3200 CPU. */ +#define BMIC_LOCAL_INT_STATUS 0x0c8d +#define BMIC_SYS_INT_MASK 0x0c8e /* Set bits enables. */ +#define BMIC_SYS_INT_STATUS 0x0c8f +#define BMIC_MAILBOXES 0x0c90 /* 16 addresses. GPRs. */ +#define BMIC_RESERVED_1 0x0ca0 /* 0x0ca0 - 0x0caf reserved. */ + +/* Make an EISA ID from vendor abbreviation, product number and revision. */ +#define MK_EISA_ID(v,p,r) ((((v) & 65535) << 16) | (((p) & 255) << 8) | ((r) & 255)) +#define EISA_VENDOR(v,e,n) ( ((((v) - 'A' + 1UL) & 63) << 10) | \ + ((((e) - 'A' + 1UL) & 31) << 5) | \ + ((((n) - 'A' + 1UL) & 31) << 0) ) + +/* Definitions to use with MK_EISA_ID for BMIC_EISA_ID. */ +#define NE3200_VENDOR EISA_VENDOR('N','V','L') /* NE3200 vendor ID. */ +#define NE3200_PROD 7 /* NE3200 product number. */ + +/* Definitions for BMIC_SYS_INT_MASK_CTRL. */ +#define BSIMCF_INT_DISABLED 0x00 +#define BSIMCF_INT_ENABLED 0x01 /* RW. */ +#define BSIMCF_INT_PENDING 0x02 /* RO. */ + +/* Definitions for NE3200_BOARD_CONFIG (WO!). Someone should be shot for + * making this register write only. */ +#define NBCF_IRQ_LINE_5 0x01 /* Use interrupt 5. */ +#define NBCF_IRQ_LINE_9 0x02 /* Use interrupt 9. */ +#define NBCF_IRQ_LINE_10 0x03 /* Use interrupt 10. */ +#define NBCF_IRQ_LINE_11 0x04 /* Use interrupt 11. */ +#define NBCF_IRQ_LINE_15 0x05 /* Use interrupt 15. */ +#define NBCF_MEDIA_AUI 0x00 /* Use AUI connector. */ +#define NBCF_MEDIA_BNC 0x08 /* Use BNC/UTP connector. */ +#define NBCF_IRQ_TYPE_LEVEL 0x00 /* Level triggered interrupt. */ +#define NBCF_IRQ_TYPE_EDGE 0x20 /* Edge triggered interrupt. */ + + +/* Map of the NE3200's indirectly accessible RAM. */ + +#define NE3200_RAM1_START 0x0000 +#define NE3200_RAM1_SIZE (16 * 1024) +#define NE3200_PROM_START 0x4000 +#define NE3200_PROM_SIZE (16 * 1024) +#define NE3200_RAM2_START 0x8000 +#define NE3200_RAM2_SIZE (32 * 1024) + +/* ROM firmware responses in BMIC_MAILBOXES + 0 after reset. */ +#define RFWF_RAM_FAILURE 0x40 +#define RFWF_OK_WORKING 0x80 --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/ne3200fw.h_shipped Fri Oct 31 11:53:34 2003 @@ -0,0 +1,35 @@ +#define BMIC_MBOX_CMD 0x0000000d +#define BMIC_MBOX_BADDR 0x00000010 +#define BMIC_MBOX_DATA 0x00000012 +#define BMIC_MBOX_COUNT 0x00000012 +#define BMIC_MBOX_DATA2 0x00000014 +#define BMIC_MBOX_DIR 0x00000014 +#define BMIC_MBOX_HADDR1 0x00000014 +#define BMIC_MBOX_HADDR 0x00000015 +#define BMIC_MBOX_COUNT3 0x00000018 +#define BMIC_MBOX_COUNT2 0x00000019 +#define BMIC_MBOX_HADDR2 0x0000001a +#define BMIC_MBOX_BADDR2 0x0000001b +#define BMIC_MBOX_ODDBYTE2 0x0000001e +#define BMIC_MBOX_ODDBYTE1 0x0000001f +#define BMIC_MBOX_STATION_ADDR 0x0000001a +#define INT_82586 0x00000008 +#define INT_MEMCPY 0x00000020 +#define INT_FWFAULT 0x00000040 +#define INT_ILLEGAL 0x00000041 +#define INT_NMI 0x00000042 +#define CMD_IDLE 0x00000000 +#define CMD_WRITEW_MEM 0x00000004 +#define CMD_READA_MEM 0x0000000d +#define CMD_READW_MEM 0x0000000d +#define CMD_WRITEA_MEM 0x00000018 +#define CMD_CA_82586 0x00000022 +#define CMD_RESET_82586 0x00000025 +#define CMD_TRIGGER_IRQ 0x0000002d +#define CMD_READW_IO 0x00000034 +#define CMD_DETECT_586IRQ 0x00000040 +#define CMD_COPYTOBOARD 0x00000052 +#define CMD_COPYFROMBOARD 0x00000072 +#define CMD_FRAGTOBOARD 0x00000093 +#define CMD_FRAGFROMBOARD 0x000000e3 +#define CMD_FRAGFROMBOARD_DIR 0x000000c0 --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/ne3200fw.s Thu Oct 30 21:26:11 2003 @@ -0,0 +1,1095 @@ +/* Simple NE3200 firmware for the Linux NE3200 driver. + +; Written 2000, 2002-2003 by Rask Ingemann Lambertsen. + +; Copyright 2000, 2002-2003 Rask Ingemann Lambertsen. + +; This software may be used and distributed according to the terms +; of the GNU General Public License, incorporated herein by reference. + +; The author may be reached as . +; See ne3200.c for further information. + +; ne3200fw.s:v0.07 2003-10-30 Rask Ingemann Lambertsen + +Sources: + + Intel's 80186/80188 manual. + + Example "cne3200" code published by Novell on their website at + + for the memory map, I/O map and BMIC register map as seen from the + onboard 80186 CPU. + + Random i82355 info found at Intel's website: + + + + + +; The memory map of the NE3200 (as seen from the 80186): + +; 0x0000 - 0x3fff 16 kB of RAM. +; 0x4000 - 0x7fff 16 kB of ROM. +; 0x8000 - 0xffff 32 kB of RAM + +; The 32 kB RAM block at 0x8000 is overlaid with ROM when the board is +; reset. The 16 kB ROM at 0x4000 contains the Ethernet station address +; in byte 0, 2, 4, 6, 8, 10 and 12 (and several other places). + +; The intented RAM usage is as follows: +; 0 kB - 2 kB Firmware (code, data, stack). +; 2 kB - 16 kB TX (& RX?) buffers. +; 32 kB - 64 kB RX buffers. +*/ + +# Memory address of the Peripheral Control Block. +.equ PCB, 0x4200 + +# Offsets into the PCB with Intel's register names. +EOI = 0x22 +POLL = 0x24 +POLLSTS = 0x26 +IMASK = 0x28 +PRIMSK = 0x2A +INSERV = 0x2C +REQST = 0x2E +INSTS = 0x30 +TCUCON = 0x32 +DMA0CON = 0x34 +DMA1CON = 0x36 +I0CON = 0x38 +I1CON = 0x3A +I2CON = 0x3C +I3CON = 0x3E + +PACS = 0xA4 + +RELREG = 0xFE + +# 80186 ports +.equ i82586_RESET, 0x0100 +.equ i82586_CA, 0x0180 +.equ BMIC_DATA, 0x0200 +.equ BMIC_INDEX, 0x0202 +.equ BMIC_STATUS, 0x0204 +.equ INIT_PCB, 0xff00 + +# BMIC_INDEX definitions +.equ BMICF_AUTOINC, 0x80 # Make BMIC_INDEX autoincrement + +# BMIC register numbers for BMIC_INDEX +BMIC_EISA_ID = 0x00 /* EISA four byte card ID. */ +BMIC_RESERVED_0 = 0x04 /* 0x04 - 0x07 reserved. */ +BMIC_GLOBAL_CFG_REG = 0x08 /* I have no idea. :-( */ +BMIC_SYS_INT_MASK_CTRL = 0x09 /* Bit 0 enables, bit 1 indicates. */ +BMIC_SEMAPHORES = 0x0a /* Two addresses. */ +BMIC_LOCAL_INT_MASK = 0x0c /* Local = NE3200 CPU. */ +BMIC_LOCAL_INT_STATUS = 0x0d +BMIC_SYS_INT_MASK = 0x0e /* Set bits enable. */ +BMIC_SYS_INT_STATUS = 0x0f +BMIC_MAILBOXES = 0x10 /* General purpose registers. */ + +# BMIC registers used for communication (mostly mailboxes) +BMIC_MBOX_CMD = 0x0d # 1 byte command. +BMIC_MBOX_BADDR = 0x10 # 2 byte board address (low byte first). +BMIC_MBOX_DATA = 0x12 # 2 byte data. +BMIC_MBOX_COUNT = 0x12 # 2 byte byte count (for transfers). +BMIC_MBOX_DATA2 = 0x14 # 0-12 data bytes (for PIO transfers). +BMIC_MBOX_DIR = 0x14 # DMA direction (for DMA transfers). +BMIC_MBOX_HADDR1 = 0x14 # 4 byte host address (for DMA transfers). +BMIC_MBOX_HADDR = 0x15 # 4 byte host address (for DMA transfers). +BMIC_MBOX_COUNT3 = 0x18 # 2 byte byte count (for transfers). +BMIC_MBOX_COUNT2 = 0x19 # 2 byte byte count (for DMA transfers). +BMIC_MBOX_HADDR2 = 0x1a # 4 byte host address (for DMA transfers). +BMIC_MBOX_BADDR2 = 0x1b # 2 byte board address (for DMA transfers). +BMIC_MBOX_ODDBYTE2 = 0x1e # Optional "odd" byte (for DMA transfers). +BMIC_MBOX_ODDBYTE1 = 0x1f # Optional "odd" byte (for DMA transfers). + +BMIC_MBOX_STATION_ADDR = 0x1a # Holds the Ethernet station address initially. + +# Definitions for BMIC_SYS_INT_*. +INT_82586 = 1 << 3 +INT_MEMCPY = 1 << 5 +INT_FWFAULT = 1 << 6 +INT_ILLEGAL = (INT_FWFAULT) + 1 +INT_NMI = (INT_FWFAULT) + 2 + +# The contents of BMIC_MBOX_CMD when the i186 has completed the the previous +# command and a new one can be writen to BMIC_MBOX_CMD. +.equ CMD_IDLE, 0 + +# BMIC registers for DMA channels. CH0 from the board, CH1 to the board. +.equ CH0_COUNT, 0x40 +.equ CH0_HOST_ADDR, 0x43 +.equ CH0_CONFIG, 0x48 +.equ CH0_STROBE, 0x49 +.equ CH0_STATUS, 0x4A +.equ CH0_LOCAL_ADDR, 0x4B + +.equ CH1_COUNT, 0x60 +.equ CH1_HOST_ADDR, 0x63 +.equ CH1_CONFIG, 0x68 +.equ CH1_STROBE, 0x69 +.equ CH1_STATUS, 0x6A +.equ CH1_LOCAL_ADDR, 0x6B + +# Bit values for CHx_COUNT+2 +.equ AUTO_START, 0x80 +.equ ADAPTER_TO_EISA, 0x40 + +# Bit values for CHx_STATUS +.equ FIFO_STALLED, 0x10 +.equ TRANSFER_ENABLED, 0x08 +.equ TRANSFER_IN_PROGRESS, 0x04 +.equ TRANSFER_TERMINATED, 0x02 +.equ TRANSFER_COMPLETE, 0x01 + +# BMIC registers for read/write/exchange in host memory. +RWE_DATA = 0x30 +RWE_ADDR = 0x34 +RWE_CTRL = 0x38 + +# Values for the RWE_CTRL register. +RWE_EXCHL = 0b01111111 +RWE_WRITEL = 0b00111111 +RWE_READL = 0b01011111 + +# Flag definitions for BMIC_STATUS. +BSF_CH0_BASE_BUSY = 0x01 # Set when a new transfer can not be programmed. +BSF_CH1_BASE_BUSY = 0x02 +BSF_PEEK_POKE_PENDING = 0x04 +BSF_LOCAL_INT_ACTIVE = 0x08 +BSF_LOCAL_INT_ENABLED = 0x10 +BSF_CH0_INT_PENDING = 0x20 +BSF_CH1_INT_PENDING = 0x40 +BSF_LOCAL_INT_PENDING = 0x80 + +# Two nop bytes are defined as symbols to be able to use them as fill value +# for .balign, .org and the like. Safenop is the x86 nop instruction (3 +# cycles) which is safe to use anywhere. Fastnop is the x86 SS override +# prefix (2 cycles) which should be safe in most cases. +safenop = 0b10010000 +fastnop = 0b00110110 + +.macro vector o, s=0 +.short \o - 35*16, \s + 35 +.endm /* vector */ + +.text +.att_syntax noprefix +.arch i186,nojumps +.code16 + +.org 0x0000 + +.global _start +_start: + ljmp $0,$main + +# Initialise interrupt vectors. ljmp overwrites the first two. +.org 2*4, 0b00101110 + vector handleNMI # 2: NMI + vector handleIgnore # 3: Breakpoint (int 3) + vector handleIgnore # 4: Overflow (into) + vector handleIgnore # 5: Array bounds (bound) + vector handleIllegal # 6: Illegal instruction + vector handleIllegal # 7: Floating point emulation + vector handleIgnore # 8: Timer 0 + vector handleIgnore # 9: Undefined + vector handleIgnore # 10: DMA 0 + vector handleIgnore # 11: DMA 1 + vector handleIgnore # 12: External int 0 + vector handleIgnore # 13: External int 1 + vector handleIgnore # 14: External int 2 + vector handleIgnore # 15: External int 3 + vector handleIllegal # 16: Floating point exception + vector handleIgnore # 17: Undefined + vector handleIgnore # 18: Timer 1 + vector handleIgnore # 19: Timer 2 + +# Skip interrupt vectors (just to be sure). +.org 20*4, 0b00101110 + +# The first thing to do is to get the 80186 into a known state. + +main: + cli # Turn off interrupts + + mov cs,bx # Clear segment registers to create a + mov bx,es # 64 kB linear address space (cs was + mov bx,ss # cleared by ljmp at 0x0000). + mov bx,ds + mov $2048,sp # Stack grows downward from 2 kB. + cld # Index registers autoincrement. + +# Set up the first two interrupt vectors. + mov $handleIllegal,ax + mov ax,(bx) # Divide error. + mov cs,2(bx) + mov ax,4(bx) # Single step. + mov cs,6(bx) + +# Relocate Peripheral Control Block to memory. + mov $RELREG+INIT_PCB,dx + mov $1 << 12 | PCB >> 8,ax + out al,(dx) # This is a word-wide write. + +# Board initialisation. + mov ss,ax + out al,$0x80 # Disable 32 kB ROM overlay. + +# Set up the 80186 interrupt controller. + mov $INSERV+PCB,si + mov $inserv,di + movsw (si),(di) # INSERV: Are we in interrupt? + movsw (si),(di) # REQST: Detect bogus interrupts. + movsw (si),(di) # INTSTS: Ditto. + mov si,di + mov $1 << 3 | 0,al + stos ax,(di) # TCUCON: Irq priority 0, disabled. + inc ax + stos ax,(di) # DMA0CON: Irq priority 1, disabled. + inc ax + stos ax,(di) # DMA1CON: Irq priority 2, disabled. + mov $3 << 3 | 3,al + stos ax,(di) # I0CON: Irq priority 3, disabled, level. + inc ax + stos ax,(di) # I1CON: Irq priority 4, disabled, level. + inc ax + stos ax,(di) # I2CON: Irq priority 5, disabled, level. + inc ax + stos ax,(di) # I3CON: Irq priority 6, disabled, level. + movw $7,PRIMSK-TCUCON(si) # Unmask from priority 7 and down. + +# These global constants are available from here on: +# ss = es = 0 +# ds = BMIC_INDEX +# bl = low byte of BMIC_DATA +# bh = BMIC_MBOX_BADDR | BMICF_AUTOINC +# bp = PCB + 128 + mov $(BMIC_MBOX_BADDR | BMICF_AUTOINC) << 8 | (BMIC_DATA & 0xff),bx + mov $PCB+128,bp + +# BMIC initialisation + mov $BMIC_INDEX,dx + mov dx,ds # Save $BMIC_INDEX for later. + mov $BMIC_LOCAL_INT_MASK | BMICF_AUTOINC,al + out al,(dx) + mov bl,dl # BMIC_DATA + mov $0xff,al # Enable interrupts from driver. + out al,(dx) + # BMIC_LOCAL_INT_STATUS + out al,(dx) # Clear all interrupt request bits. + + mov $BMIC_STATUS,dx + mov $BSF_LOCAL_INT_ENABLED,al # Enable BMIC interrupts. + out al,(dx) + + mov ds,dx # Point BMIC to DMA channel 0 config. + mov $CH0_CONFIG,al + out al,(dx) + mov bl,dl + mov $0x98,al # Magic for BMIC DMA config. + out al,(dx) + + mov ds,dx # Point BMIC to DMA channel 1 config. + mov $CH1_CONFIG,al + out al,(dx) + mov bl,dl + mov $0x98,al # Magic for BMIC DMA config. + out al,(dx) + +# Put an easily recognisable pattern into the first 10 mailboxes. + mov ds,dx # BMIC_INDEX + mov $BMIC_MAILBOXES | BMICF_AUTOINC,al + out al,dx + mov bl,dl + mov $10,al +lzloop: + out al,dx + dec ax # Shorter than dec al (and ah = 0), + jnz lzloop + +# Copy the Ethernet station address into the last six mailbox registers. + mov $0x4000,si + mov $6,cx + rep # The high byte will contain junk, but the + outsw es:(si),(dx) # BMIC only uses the low byte, so this works. + +# Wait for the driver to trigger an interrupt for us to detect. + mov ds,dx # BMIC_INDEX + mov $BMIC_MBOX_CMD,al # BMIC_LOCAL_INT_STATUS + out al,(dx) + mov bl,dl # BMIC_DATA +irqloop: + in (dx),al + cmp $0,al + je irqloop + +# Probe for the interrupt number, install the handler and enable it. + call probeirq + mov al,es:int355 # Store interrupt number. + mov $handleBMIC,ax + stos ax,es:(di) # Store handler address. + mov cs,es:(di) + and si,IMASK-128(bp) # Enable the interrupt. + +# Clear the firmware statistics. + mov ss,ax + mov $fwstats,di + stos ax,(di) # numcmdsL + stos ax,(di) # numcmdsH + stos ax,(di) # numints + +# Finally, clear the cmd/status register to indicate that the firmware +# is ready to accept a command. + mov $0xff,al + out al,(dx) # Clear all bits (= CMD_IDLE). + jmp intloopstart + +intloop: + movw $0x8000,EOI-128(bp) + incw es:numints +intloopstart: + mov int355-PCB-128(bp),cx # ch = int586, cl = int355 +.balign 2, safenop +1: mov POLL-128(bp),ax + shl ah # Request bit set? + jnc 1b + and $0b00011111,al + cmp cl,al # Interrupt from BMIC? + je handleBMIC + cmp ch,al # Interrupt from 82586? + jne handleIgnore + +# Handle interrupts from the 82586. We simply pass them to the host. +handle586: + mov ds,dx # BMIC_INDEX + mov $BMIC_SYS_INT_STATUS,al + out al,(dx) + mov bl,dl # BMIC_DATA + mov $INT_82586,al + out al,(dx) + jmp intloop + +# We disable any unknown interrupts. This assumes that only one interrupt +# is in service at a time. +.balign 2, fastnop +handleIgnore: + mov INSERV-128(bp),ax + or ax,IMASK-128(bp) + jmp intloop + +.balign 2, fastnop +handleBMIC: + mov ds,dx # BMIC_INDEX + mov $BMIC_MBOX_CMD,al + out al,(dx) + mov bl,dl # BMIC_DATA + mov ss,ax + in (dx),al + shl ax + add $servicertns,ax + jmp *ax + +# We're ready to accept a new command, so clear the command register. +.balign 2, fastnop +lnewcmd: + addw $1,es:numcmdsL + adcw $0,es:numcmdsH + mov ds,dx # BMIC_INDEX + mov $BMIC_MBOX_CMD,al + out al,dx + mov bl,dl # BMIC_DATA + mov $0x00ff,ax # Clear all irq bits (= CMD_IDLE). + out al,dx + +# Poll the cmd/status register, waiting for another command to be sent. +# Stop polling if nothing happens for a while. + mov $11,cx # 3 is enough on my 486DX2-66 + +.balign 2, fastnop +lcpoll: + in dx,al + cmp $CMD_IDLE,al + loope lcpoll + +# Jump to the service routine for the command. + shl ax + add $servicertns,ax + jmp *ax + +# The service routines begin here, with command numbers >= 1. +# First comes the code to handle the case where command = 0. +# The BMIC might interrupt for other reasons than driver commands. +.balign 2, fastnop +servicertns: + mov $BMIC_STATUS,dx + mov $BSF_LOCAL_INT_ENABLED,al + out al,(dx) # Silence interrupts. + jmp intloop + +# Write a word to memory. +# Inputs: +# BMIC_MBOX_BADDR: The address to write to (big endian). +# BMIC_MBOX_DATA: The data word to write (little endian). +# Outputs: +# None. +.balign 2, fastnop +CMD_WRITEW_MEM = (.-servicertns) / 2 +# Fetch the address from the mailbox. + mov ds,dx # BMIC_INDEX + mov bh,al # (BMIC_MBOX_BADDR)+ + out al,(dx) + mov bl,dl # BMIC_DATA + in (dx),al + mov al,ah + in (dx),al + mov ax,di +# Address is now in di. Copy the data from the mailbox to memory. + insb (dx),(di) + insb (dx),(di) +# Return. + jmp lnewcmd + + +# Read a word from memory atomically. +# Inputs +# BMIC_MBOX_BADDR: The address to read from (big endian). +# Outputs +# BMIC_MBOX_DATA: The data word read from memory (little endian). +.balign 2, fastnop +CMD_READA_MEM = (.-servicertns) / 2 +CMD_READW_MEM = (.-servicertns) / 2 +# Fetch the address from the mailbox. + mov ds,dx # BMIC_INDEX + mov bh,al # (BMIC_MBOX_BADDR)+ + out al,(dx) + mov bl,dl # BMIC_DATA + in (dx),al + mov al,ah + in (dx),al + mov ax,si +# Address is now in si, fetch the data word. + mov es:(si),ax # Faster than lods es:(si),ax +# Put the data into the mailbox. + out al,(dx) + mov ah,al + out al,(dx) +# Return. + jmp lnewcmd + + +# Write a word to memory atomically. +# Inputs: +# BMIC_MBOX_BADDR: The address to write to (big endian). +# BMIC_MBOX_DATA: The data word to write (big endian). +# Outputs: +# None. +.balign 2, fastnop +CMD_WRITEA_MEM = (.-servicertns) / 2 +# Fetch the address from the mailbox. + mov ds,dx # BMIC_INDEX + mov bh,al # (BMIC_MBOX_BADDR)+ + out al,(dx) + mov bl,dl # BMIC_DATA + in (dx),al + mov al,ah + in (dx),al + mov ax,di +# Address is now in di. Get the data word from the mailbox. + in (dx),al + mov al,ah + in (dx),al +# Write the word to memory. + stos ax,(di) # Faster than mov ax,nn(bp,di). +# Return. + jmp lnewcmd + + +# Send the Channel Attention signal to the 82586. +# Inputs: +# None. +# Outputs: +# None. +.balign 2, fastnop +CMD_CA_82586 = (.-servicertns) / 2 + mov $i82586_CA,dx + in (dx),al + jmp lnewcmd + + +# Reset the 82586. The 82596CA manual specifies that RESET should be active +# for at least five clock cycles. I'm assuming that the 82586 also needs +# RESET to be active for several clock cycles. +# Inputs: +# None. +# Outputs: +# None. +.balign 2, fastnop +CMD_RESET_82586 = (.-servicertns) / 2 + mov $i82586_RESET,dx +.if 0 /* as emits undefined opcodes: 0x83 0x4e 0x24 0x03 :-( */ + orw $3,PACS-128(bp) # Insert 3 wait states. +.else + .byte 0x81, 0x4e, 0x24, 0x03, 0x00 +.endif + in (dx),al +.if 0 /* More undefined opcodes: 0x83 0x66 0x24 0xfc :-( */ + andw $~3,PACS-128(bp) # Revert to 0 wait states. +.else + .byte 0x81, 0x66, 0x24, 0xfc, 0xff +.endif + jmp lnewcmd + + +# Trigger an interrupt on the EISA bus for irq detection. +# Inputs: +# None. +# Outputs: +# None. +.balign 2, fastnop +CMD_TRIGGER_IRQ = (.-servicertns) / 2 + mov ds,dx + mov $BMIC_SYS_INT_STATUS,al + out al,(dx) + mov bl,dl + mov $0xff,al + out al,(dx) + jmp lnewcmd + + +# Read a word from an I/O port. +# Inputs: +# BMIC_MBOX_BADDR: The port number (big endian). +# Outputs: +# BMIC_MOBX_DATA: The data word (little endian). +.balign 2, fastnop +CMD_READW_IO = (.-servicertns) / 2 +# Fetch the address from the mailbox. + mov ds,dx # BMIC_INDEX + mov bh,al # (BMIC_MBOX_BADDR)+ + out al,(dx) + mov bl,dl # BMIC_DATA + in (dx),al + mov al,ah + in (dx),al +# Address is now in ax. Fetch the data word. + xchg ax,dx # Save dx in cx, move I/O address to dx. + mov ax,cx # (xchg ax,dx is shorter than mov ax,dx.) + in (dx),ax +# Put the data into the mailbox. + mov cx,dx # Restore dx. + out al,(dx) + mov ah,al + out al,(dx) +# Return. + jmp lnewcmd + + +# Detect the IRQ of the 82586 Ethernet controller, install the handler, set +# up the interrupt controller and enable the interrupt. Because the interrupt +# handler doesn't clear the 82586 interrupt, we'd see endless triggering if +# the interrupt pin was configured for level triggered operation. So we +# configure it for edge triggering instead. +# Inputs: +# None. +# Outputs: +# None. +.balign 2, fastnop +CMD_DETECT_586IRQ = (.-servicertns) / 2 + mov $BMIC_STATUS,dx + mov ss,ax + out al,(dx) # Disable interrupts from the BMIC. + + call probeirq # Probe for the 82586 irq. + mov al,es:int586 # Store interrupt number. + mov $handle586,ax + stos ax,es:(di) # Store handler address. + mov es,es:(di) + xchg cx,si +.if 0 + andw $~(1<<4),(bp,si) # Configure for edge triggering. +.else + .byte 0x81, 0x22, 0xef, 0xff +.endif + and cx,IMASK-128(bp) # Enable the interrupt. + + mov $BSF_LOCAL_INT_ENABLED,al + out al,(dx) # Reenable interrupts from the BMIC. + jmp lnewcmd + + +# Memory copy to board. +# Inputs: +# BMIC_MBOX_BADDR: The memory address (big endian). +# BMIC_MBOX_COUNT: The number of bytes (big endian). +# BMIC_MBOX_DATA2: The first 12 (or less) data bytes. +# Outputs: +# None. +.balign 2, fastnop +CMD_COPYTOBOARD = (.-servicertns) / 2 + mov ds,dx + mov bh,al + out al,(dx) # Read arguments. + mov bl,dl + in (dx),al + mov al,ah + in (dx),al + mov ax,di # Address to di. + in (dx),al + mov al,ah + in (dx),al + mov ax,si # Byte count to si. + mov $12,cx + sub cx,si + jbe lastfewT # Last block if byte count <= 12. + +.balign 2, fastnop +next16T: + rep + insb (dx),(di) # Copy full block of 16 (12) bytes. + + mov ds,dx + mov $BMIC_SYS_INT_STATUS,al + out al,(dx) + mov bl,dl + mov $INT_MEMCPY,al + out al,(dx) # Signal completion of this block. + mov $16,cl # Try 16 bytes for next block. + +0: in (dx),al + and $INT_MEMCPY,al + jnz 0b # Wait for more data. + + mov ds,dx + mov bh,al + out al,(dx) # Autoincrement from first mailbox. + mov bl,dl + + sub cx,si # Update byte count. + ja next16T # Repeat if more than one block left. + +lastfewT: + add si,cx # Find remaining number of bytes. + rep + insb (dx),(di) # Copy last block. + jmp lnewcmd + + +# Memory copy from board. +# Inputs: +# BMIC_MBOX_BADDR: The memory address (big endian). +# BMIC_MBOX_COUNT: The number of bytes (big endian). +# Outputs: +# BMIC_MBOX_DATA2: The first 12 (or less) data bytes. +.balign 2, fastnop +CMD_COPYFROMBOARD = (.-servicertns) / 2 + mov ds,dx + mov bh,al + out al,(dx) # Read arguments. + mov bl,dl + in (dx),al + mov al,ah + in (dx),al + mov ax,si # Address to si. + in (dx),al + mov al,ah + in (dx),al + mov ax,di # Byte count to di. + mov $12,cx + sub cx,di + jbe lastfewF # Last block if byte count <= 12. + +/* Aligning to a word boundary here probably doesn't help because the next + * instruction is three bytes and therefore takes 4 extra cycles to fetch. */ +next16F: + rep + outsb es:(si),(dx) # Copy full block of 16 (12) bytes. + + mov ds,dx + mov $BMIC_SYS_INT_STATUS,al + out al,(dx) + mov bl,dl + mov $INT_MEMCPY,al + out al,(dx) # Signal completion of this block. + mov $16,cl # Try 16 bytes for next block. + +0: in (dx),al + and $INT_MEMCPY,al + jnz 0b # Wait for more data. + + mov ds,dx + mov bh,al + out al,(dx) # Autoincrement from first mailbox. + mov bl,dl + + sub cx,di # Update byte count. + ja next16F # Repeat if more than one block left. + +lastfewF: + add di,cx # Find remaining number of bytes. + + rep + outsb es:(si),(dx) # Copy last block. + jmp lnewcmd + + +# Copy 1 or 2 fragments to board using DMA. +# Inputs: +# BMIC_MBOX_BADDR: First fragment board memory address (big endian). +# BMIC_MBOX_COUNT: First fragment length in bytes (little endian). +# BMIC_MBOX_HADDR1: First fragment host memory address (little endian). +# BMIC_MBOX_COUNT3: Second fragment length in bytes (little endian). +# BMIC_MBOX_HADDR2: Second fragment host memory address (little endian). +# BMIC_MBOX_ODDBYTE1: The first byte of fragment if BMIC_MBOX_BADDR2 is odd. +# BMIC_MBOX_ODDBYTE2: The first byte of fragment if BMIC_MBOX_BADDR is odd. +# Outputs: +# None. +# The byte counts and host addresses do not include the "odd" byte. An +# unaligned ending board memory address causes an extra junk byte to be +# transferred. +.balign 2, fastnop +CMD_FRAGTOBOARD = (.-servicertns) / 2 +fragtoboard: + push bx + mov ds,dx # Point BMIC to arguments. + mov bh,al + out al,(dx) + mov bl,dl + in (dx),al # Read first fragment local address into si. + mov al,ah + in (dx),al + mov ax,si + in (dx),al # Read first fragment length into bx. + mov al,bl + in (dx),al + mov al,bh + mov $dmatoboard,di + mov $12,cx # Read the rest. + rep + insb (dx),(di) + + # Program first transfer of first fragment. We have: + # si: Local address. + # bx: Length. + # dmatoboard: Host address. + # dmatoboard+11: First byte if di is odd. + + mov ds,dx # Point BMIC to DMA local address. + mov $CH1_LOCAL_ADDR | BMICF_AUTOINC,al + out al,(dx) + mov $0,dl + mov si,ax # Get local address + inc ax # Convert to TBI format. + shr ax # (preserve C flag until odd byte transfer.) + out al,(dx) + mov ah,al + out al,(dx) + + mov ds,dx # Point BMIC to byte count and host address. + mov $CH1_COUNT | BMICF_AUTOINC,al + out al,(dx) + mov $0,dl + mov bl,al # Write first fragment length. + out al,(dx) + mov bh,al + out al,(dx) + mov $AUTO_START,al + out al,(dx) + mov si,di # Save the board address for later. + mov $dmatoboard,si + mov $4,cl # Write host address to BMIC. + rep + outsb es:(si),(dx) + inb (dx),al # Skip unknown register. + inb (dx),al # Skip config register + out al,(dx) # Strobe to start transfer. + + # The C flag is clear if we have an odd byte to transfer. + jc 1f + mov es:dmatoboard+11,al + stos al,(di) + + # Program transfer of second fragment. We have: + # di: Board address of first fragment (even). + # bx: Length of first fragment. + # si: Points to dmatoboard+4. + # dmatoboard+4: Length of second fragment. + # dmatoboard+6: Host address of second fragment. + # dmatoboard+10: Odd byte if di+bx is odd. + +.balign 2, fastnop +1: add es:(si),cx # Get second fragment length. + jz 3f # Skip if zero. + + mov ds,dx # Point BMIC to DMA local address. + mov $CH1_LOCAL_ADDR | BMICF_AUTOINC,al + out al,(dx) + mov $0,dl + lea 1(bx,di),ax # Calculate board address in TBI format. + shr ax # (preserve C flag until odd byte transfer.) + out al,(dx) + mov ah,al + out al,(dx) + + mov ds,dx # Point BMIC to byte count and host address. + mov $CH1_COUNT | BMICF_AUTOINC,al + out al,(dx) + mov $0,dl + mov cl,al # Write second fragment length. + out al,(dx) + mov ch,al + out al,(dx) + mov $AUTO_START,al + out al,(dx) + add $2,si # Write second fragment host address. + mov $4,cx + rep + outsb es:(si),(dx) + inb (dx),al # Skip unknown register. + inb (dx),al # Skip config register + out al,(dx) # Strobe to start transfer. + + # Transfer the odd byte if there is one. If there is one, then the + # DMA transfer will write a junk byte to the address where the odd + # byte is supposed to go, so wait for the first transfer to finish. + + jc 3f # Skip if there is no odd byte. + add bx,di + mov $BMIC_STATUS,dx +2: in (dx),al + and $BSF_CH0_BASE_BUSY,al + jnz 2b + movsb es:(si),(di) + +3: pop bx + mov ds,dx # Wait for transfer to finish. + mov $CH1_STATUS,al + out al,(dx) + mov bl,dl +4: in (dx),al + and $TRANSFER_IN_PROGRESS | TRANSFER_ENABLED,al + jne 4b + jmp lnewcmd + + +# Copy 1 or 2 packet fragments from board using DMA. +# Inputs: +# BMIC_MBOX_BADDR: First fragment board memory address / 2 (little endian). +# BMIC_MBOX_COUNT: Length in bytes of first fragment (little endian). +# BMIC_MBOX_DIR: CMD_FRAGFROMBOARD_DIR +# BMIC_MBOX_HADDR: The host memory address (little endian). +# BMIC_MBOX_COUNT2: Length in bytes of second fragment (big endian). +# BMIC_MBOX_BADDR2: Second fragment board memory address / 2 (big endian, even). +.balign 2, fastnop +CMD_FRAGFROMBOARD = (.-servicertns) / 2 +CMD_FRAGFROMBOARD_DIR = AUTO_START | ADAPTER_TO_EISA +fragfromboard: + mov ds,dx # Point BMIC to arguments. + mov bh,al + out al,(dx) + mov bl,dl + mov $dmafromboard,di + mov $9,cx # Read the lot for first fragment. + rep + insb (dx),(di) + inb (dx),al # Read second fragment length into ax. + mov al,ah + inb (dx),al + + cmp ax,cx # Second fragment length != 0? + jz 1f + mov ax,di # di = second fragment length. + inb (dx),al # Read second fragment address into ah:bh. + mov al,ah + inb (dx),al + mov al,bh + +# Take care not to trash ah:bh or the Z flag until "jnz" below! +1: mov ds,dx # Point BMIC to CH0_LOCAL_ADDR. + mov $CH0_LOCAL_ADDR | BMICF_AUTOINC,al + out al,(dx) + mov bl,dl + mov $dmafromboard,si + outsb es:(si),(dx) # Write local address. + outsb es:(si),(dx) + mov ds,dx # Point BMIC to byte count and host address. + mov $CH0_COUNT | BMICF_AUTOINC,al + out al,(dx) + mov bl,dl + mov $7,cl # Write byte count and host address. + rep + outsb es:(si),(dx) + inb (dx),al # Skip unknown register. + inb (dx),al # Skip config register + out al,(dx) # Strobe to start transfer. + + jnz 4f # Load second fragment if there is one. + +2: mov ds,dx # Wait for transfer to finish. + mov $CH0_STATUS,al + out al,(dx) + mov bl,dl +3: in (dx),al + and $TRANSFER_IN_PROGRESS | TRANSFER_ENABLED,al + jne 3b + jmp lnewcmd + +# Program transfer of second fragment. We have: +# ah:bh = Board address of second fragment. +# di = Length of second fragment. +# si = Points to dmafromboard+9. +# dmafromboard+2 = Length of first fragment. +# dmafromboard+5 = Host destination address. + +.balign 2, fastnop +4: mov ds,dx # Point BMIC to CH0_LOCAL_ADDR. + mov $CH0_LOCAL_ADDR | BMICF_AUTOINC,al + out al,(dx) + mov bl,dl + mov bh,al + out al,(dx) # Second board memory address. + mov ah,al + out al,(dx) + mov ds,dx # Point BMIC to byte count and host address. + mov $CH0_COUNT | BMICF_AUTOINC,al + out al,(dx) + mov bl,dl + mov di,ax + out al,(dx) # Second fragment length. + mov ah,al + out al,(dx) + mov $CMD_FRAGFROMBOARD_DIR,al + out al,(dx) + mov es:dmafromboard+5,ax + add es:-7(si),ax + out al,(dx) # Host memory address, low part. + mov ah,al + out al,(dx) + mov es:dmafromboard+7,ax + adc cx,ax + out al,(dx) # Host memory address, high part. + mov ah,al + out al,(dx) + inb (dx),al # Skip unknown register. + inb (dx),al # Skip config register + out al,(dx) # Strobe to start transfer. + mov $BMIC_MBOX_BADDR | BMICF_AUTOINC,bh + jmp 2b + + +# The function used to probe for an external irq. +# On entry, the interrupt to be probed for should have its request line +# active. Only one external interrupt should be active, but any number of +# internal interrupts may be active. +# Inputs: +# bp: Points to the PCB + 128. +# Outputs: +# ax: The interrupt number [12...15] or 9 on failure. +# cx: The offset of the interrupt control register relative to bp. +# di: The interrupt vector address corresponding to ax. +# si: The interrupt mask necessary to enable the interrupt. + +.balign 2, fastnop +probeirq: + mov REQST-128(bp),cx + shr $4,cx # Extract int3-0 request bits. + + imul $16,cx,ax # Get interrupt mask. + xor $0b11111101,al # Zeros in mask enable interrupts. + xchg ax,si + + imul $3,cx,cx # Table lookup to find 15-irq. + mov $(0<<24)|(1<<12)|(2<<6)|(3<<3)|(6<<0),ax + shr cl,ax + +.if 0 /* as emits 0x83 0xe0 0x07 - there's no such instruction. :-( */ + and $0b00000111,ax # Mask out the junk. +.else + .byte 0b00100101, 0b00000111, 0 # and $0b00000111,ax +.endif + xor $0b00001111,al # irq = 15 - ax + + imul $4,ax,di # Calculate vector address. + + imul $2,ax,cx # Calculate control register offset from bp. + sub $12*2-I0CON+128,cx + ret + + +# Interrupt handlers start here. + +# We're in deep sh*t if we get to handleIllegal or handleNMI. +# We dump selected registers into the mailboxes for debugging. +# We notify the host accordingly, then go into an endless loop. +# No assumptions can be made about the contents of any register. + +.balign 2, fastnop +handleIllegal: + mov $INT_ILLEGAL << 8 | BMIC_SYS_INT_STATUS,cx + jmp deathnotify + +.balign 2, fastnop +handleNMI: + mov $INT_NMI << 8 | BMIC_SYS_INT_STATUS,cx +deathnotify: + cli + # Save 8 registers at address 8192. + mov ds,di + xor si,si + mov si,ds + mov $8192,si + mov ax, (si) # ax + mov bx, 2(si) # bx + mov dx, 4(si) # dx + mov bp, 6(si) # bp + mov sp, 8(si) # sp + mov di,10(si) # ds + mov sp,bp + mov 2(bp),es + mov es,12(si) # cs + mov (bp),es + mov es,14(si) # ip + + # Dump the registers into the mailboxes. + mov $BMIC_INDEX,dx + mov $BMIC_MAILBOXES | BMICF_AUTOINC,al + out al,(dx) + mov $BMIC_DATA,dx + xchg cx,ax + mov $16,cx + rep + outsb (si),(dx) + + # Signal the failure. + mov $BMIC_INDEX,dx + out al,(dx) + mov $BMIC_DATA,dx + mov ah,al + out al,(dx) +deathloop: + jmp deathloop + +.bss + +# Data space used to store information about the interrupt controller. +.balign 2 +inserv: .space 2 +bogus: .space 2 +bogus2: .space 2 +int355: .space 1 +int586: .space 1 + +fwstats: +numcmdsL: .short 0 +numcmdsH: .short 0 +numints: .short 0 +fwstatsend: + +.balign 2 +dmatoboard: .space 12 + .space 1 +# Keep this odd-aligned for speed. +dmafromboard: .space 9 --- /dev/null Thu Jan 1 01:00:00 1970 +++ linux-2.4.22/drivers/net/i82586/ne3200fw.v_shipped Fri Oct 31 14:27:26 2003 @@ -0,0 +1,60 @@ +#define firmware_size 1072 +static const char firmware_data[] = "\ +\352P\000\000\000...\270\001#\000\000\377#\000\000\377#\000\000\377#\ +\000\262\001#\000\262\001#\000\000\377#\000\000\377#\000\000\377#\000\ +\000\377#\000\000\377#\000\000\377#\000\000\377#\000\000\377#\000\262\ +\001#\000\000\377#\000\000\377#\000\000\377#\000\372\214\313\216\303\ +\216\323\216\333\274\000\b\374\270\342\003\211\a\214O\002\211G\004\ +\214O\006\272\376\377\270B\020\356\214\320\346\200\276,B\2770\004\245\ +\245\245\211\367\260\b\253@\253@\253\260\033\253@\253@\253@\253\307D\ +\370\a\000\273\000\220\275\200B\272\002\002\216\332\260\214\356\210\ +\332\260\377\356\356\272\004\002\260\020\356\214\332\260H\356\210\332\ +\260\230\356\214\332\260h\356\210\332\260\230\356\214\332\260\220\356\ +\210\332\260\n\ +\356Hu\374\276\000@\271\006\000\363&o\214\332\260\r\356\210\332\354<\ +\000t\373\350\325\002&\2426\004\2708\001\253&\214\r!v\250\214\320\277\ +8\004\253\253\253\260\377\356\353\n\ +\307F\242\000\200&\377\006<\004\213\216\266\301\213F\244\320\344s\371\ +$\0378\310t\0318\350u\r\214\332\260\017\356\210\332\260\b\356\353\325\ +6\213F\254\tF\250\353\314\214\332\260\r\356\210\332\214\320\354\321\ +\340\005p\001\377\3406&\203\0068\004\001&\203\026:\004\000\214\332\ +\260\r\356\210\332\270\377\000\356\271\v\000\354<\000\341\373\321\340\ +\005p\001\377\340\272\004\002\260\020\356\353\214\214\332\210\370\356\ +\210\332\354\210\304\354\211\307ll\353\3016\214\332\210\370\356\210\ +\332\354\210\304\354\211\306&\213\004\356\210\340\356\353\252\214\332\ +\210\370\356\210\332\354\210\304\354\211\307\354\210\304\354\253\353\ +\226\272\200\001\354\353\220\272\000\001\201N$\003\000\354\201f$\374\ +\377\353\200\214\332\260\017\356\210\332\260\377\356\351s\3776\214\ +\332\210\370\356\210\332\354\210\304\354\222\211\301\355\211\312\356\ +\210\340\356\351Z\377\272\004\002\214\320\356\350\305\001&\2427\004\ +\270#\001\253&\214\005\207\316\201\"\357\377!N\250\260\020\356\3517\ +\3776\214\332\210\370\356\210\332\354\210\304\354\211\307\354\210\304\ +\354\211\306\271\f\000)\316v\036\363l\214\332\260\017\356\210\332\260\ + \356\261\020\354$ u\373\214\332\210\370\356\210\332)\316w\342\001\ +\361\363l\351\367\3766\214\332\210\370\356\210\332\354\210\304\354\ +\211\306\354\210\304\354\211\307\271\f\000)\317v\037\363&n\214\332\ +\260\017\356\210\332\260 \356\261\020\354$ u\373\214\332\210\370\356\ +\210\332)\317w\341\001\371\363&n\351\265\3766S\214\332\210\370\356\ +\210\332\354\210\304\354\211\306\354\210\303\354\210\307\277>\004\271\ +\f\000\363l\214\332\260\353\356\262\000\211\360@\321\350\356\210\340\ +\356\214\332\260\340\356\262\000\210\330\356\210\370\356\260\200\356\ +\211\367\276>\004\261\004\363&n\354\354\356r\005&\240I\004\252&\003\f\ +t:\214\332\260\353\356\262\000\215A\001\321\350\356\210\340\356\214\ +\332\260\340\356\262\000\210\310\356\210\350\356\260\200\356\203\306\ +\002\271\004\000\363&n\354\354\356r\f\001\337\272\004\002\354$\001u\ +\373&\244[\214\332\260j\356\210\332\354$\fu\373\351\025\3766\214\332\ +\210\370\356\210\332\277K\004\271\t\000\363l\354\210\304\3549\301t\b\ +\211\307\354\210\304\354\210\307\214\332\260\313\356\210\332\276K\004\ +&n&n\214\332\260\300\356\210\332\261\a\363&n\354\354\356u\020\214\332\ +\260J\356\210\332\354$\fu\373\351\307\3756\214\332\260\313\356\210\ +\332\210\370\356\210\340\356\214\332\260\300\356\210\332\211\370\356\ +\210\340\356\260\300\356&\241P\004&\003D\371\356\210\340\356&\241R\ +\004\021\310\356\210\340\356\354\354\356\267\220\353\266\213N\256\301\ +\351\004k\301\0204\375\226k\311\003\270\236\020\323\350%\a\0004\017k\ +\370\004k\310\002\203\351`\3036\271\017A\353\0046\271\017B\372\214\ +\3371\366\216\336\276\000 \211\004\211\\\002\211T\004\211l\006\211d\b\ +\211|\n\ +\211\345\216F\002\214D\f\216F\000\214D\016\272\002\002\260\220\356\ +\272\000\002\221\271\020\000\363n\272\002\002\356\272\000\002\210\340\ +\356\353\376\220\n\ +"; --- linux-2.4.22/drivers/net/Config.in-orig Wed Nov 5 18:16:55 2003 +++ linux-2.4.22/drivers/net/Config.in Wed Nov 5 19:15:00 2003 @@ -195,6 +195,8 @@ dep_tristate ' Myson MTD-8xx PCI Ethernet support' CONFIG_FEALNX $CONFIG_PCI dep_tristate ' National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI dep_tristate ' PCI NE2000 and clones support (see help)' CONFIG_NE2K_PCI $CONFIG_PCI + dep_tristate ' Novell/Eagle/Microdyne/Intel/Compaq NE3200 EISA support (EXPERIMENTAL)' CONFIG_NE3200 $CONFIG_EISA $CONFIG_EXPERIMENTAL + dep_mbool ' Build NE3200 firmware' CONFIG_NE3200_BUILD_FIRMWARE $CONFIG_NE3200 dep_tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL dep_tristate ' Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL dep_tristate ' RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_8139CP $CONFIG_PCI $CONFIG_EXPERIMENTAL --- linux-2.4.22/drivers/net/Makefile-orig Sun Nov 2 23:00:51 2003 +++ linux-2.4.22/drivers/net/Makefile Wed Nov 5 21:41:00 2003 @@ -35,6 +35,10 @@ obj-y += bonding/bonding.o endif +ifeq ($(CONFIG_NE3200),y) + obj-y += i82586/ne3200.o i82586/82586.o +endif + ifeq ($(CONFIG_ISDN_PPP),y) obj-$(CONFIG_ISDN) += slhc.o endif @@ -54,6 +58,7 @@ subdir-$(CONFIG_E100) += e100 subdir-$(CONFIG_E1000) += e1000 subdir-$(CONFIG_BONDING) += bonding +subdir-$(CONFIG_NE3200) += i82586 # # link order important here --- linux-2.4.22/Documentation/Configure.help-orig Wed Nov 5 18:23:46 2003 +++ linux-2.4.22/Documentation/Configure.help Wed Nov 5 19:12:49 2003 @@ -12568,12 +12568,37 @@ module, say M here and read as well as . +Novell/Eagle/Microdyne/Intel/Compaq NE3200 EISA support +CONFIG_NE3200 + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called ne3200.o. If you want to compile it as a + module, say M here and read as well + as . + +Build NE3200 firmware +CONFIG_NE3200_BUILD_FIRMWARE + This option should only be enabled if you are modifying the firmware + source to the ne3200 driver and wish to have the generated firmware + include files updated during a normal kernel build. You will need + versions of as, ld and objdump which are capable of building Intel + i80186 code. This is normally the case on x86 systems. You can modify + the top of to use other programs + than the default as, ld and objdump when necessary. + + If in doubt, say N. + Novell/Eagle/Microdyne NE3210 EISA support CONFIG_NE3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - . Note that this driver - will NOT WORK for NE3200 cards as they are completely different. + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want).