VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 21
+SUBLEVEL = 22
all: Version zImage
bool 'Network device support?' CONFIG_NETDEVICES y
if [ "$CONFIG_NETDEVICES" = "n" ]; then
-comment 'Skipping ethercard configuration options...'
+comment 'Skipping network driver configuration options...'
else
-bool 'Dummy net driver support' CONFIG_DUMMY y
+bool 'Dummy net driver support' CONFIG_DUMMY n
bool 'SLIP (serial line) support' CONFIG_SLIP n
if [ "$CONFIG_SLIP" = "y" ]; then
bool ' CSLIP compressed headers' SL_COMPRESSED y
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
+bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
+bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
+if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
+ bool 'WD80*3 support' CONFIG_WD80x3 n
+ bool 'SMC Ultra support' CONFIG_ULTRA n
+fi
+bool '3COM cards' CONFIG_NET_VENDOR_3COM y
+if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
+ bool '3c501 support' CONFIG_EL1 n
+ bool '3c503 support' CONFIG_EL2 n
+ if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+ bool '3c505 support' CONFIG_ELPLUS n
+ bool '3c507 support' CONFIG_EL16 n
+ fi
+ bool '3c509/3c579 support' CONFIG_EL3 y
+fi
+bool 'Other ISA cards' CONFIG_NET_ISA n
+if [ "$CONFIG_NET_ISA" = "y" ]; then
+ bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE y
+ bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n
+ bool 'DEPCA support' CONFIG_DEPCA n
+ if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+ bool 'EtherExpress support' CONFIG_EEXPRESS n
+ fi
+ bool 'HP PCLAN support' CONFIG_HPLAN n
+ bool 'NE2000/NE1000 support' CONFIG_NE2000 n
+fi
bool 'PLIP (parallel port) support' CONFIG_PLIP n
-bool 'NE2000/NE1000 support' CONFIG_NE2000 n
-bool 'WD80*3 support' CONFIG_WD80x3 n
-bool 'SMC Ultra support' CONFIG_ULTRA n
-bool '3c501 support' CONFIG_EL1 n
-bool '3c503 support' CONFIG_EL2 n
-#bool '3c505 support' CONFIG_ELPLUS n
-#bool '3c507 support' CONFIG_EL16 n
-bool '3c509/3c579 support' CONFIG_EL3 y
-bool 'HP PCLAN support' CONFIG_HPLAN n
-bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
-bool 'AT1700 support' CONFIG_AT1700 n
-#bool 'Zenith Z-Note support' CONFIG_ZNET n
-#bool 'EtherExpress support' CONFIG_EEXPRESS n
-#bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
-bool 'DEPCA support' CONFIG_DEPCA n
+bool 'EISA and on board controllers' CONFIG_NET_EISA n
+ if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+ bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
+ bool 'AT1700 support' CONFIG_AT1700 n
+ fi
+ bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
#bool 'NI52EE support' CONFIG_NI52 n
#bool 'NI65EE support' CONFIG_NI65 n
-#bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
-#bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n
-bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
-bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
-bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
+bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n
+if [ "$CONFIG_NET_POCKET" = "y" ]; then
+ bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
+ bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
+ bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
+ bool 'Zenith Z-Note support' CONFIG_ZNET n
+fi
fi
fi
hd_info[drive].ctl = *(8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(14+BIOS);
+#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
if (hd_info[drive].cyl && NR_HD == drive)
NR_HD++;
+#endif
BIOS += 16;
}
/* mode flags */
unsigned long vc_charset : 1; /* Character set G0 / G1 */
unsigned long vc_s_charset : 1; /* Saved character set */
+ unsigned long vc_disp_ctrl : 1; /* Display chars < 32? */
+ unsigned long vc_toggle_meta : 1; /* Toggle high bit? */
unsigned long vc_decscnm : 1; /* Screen Mode */
unsigned long vc_decom : 1; /* Origin Mode */
unsigned long vc_decawm : 1; /* Autowrap Mode */
#define video_mem_start (vc_cons[currcons].vc_video_mem_start)
#define video_mem_end (vc_cons[currcons].vc_video_mem_end)
#define video_erase_char (vc_cons[currcons].vc_video_erase_char)
+#define disp_ctrl (vc_cons[currcons].vc_disp_ctrl)
+#define toggle_meta (vc_cons[currcons].vc_toggle_meta)
#define decscnm (vc_cons[currcons].vc_decscnm)
#define decom (vc_cons[currcons].vc_decom)
#define decawm (vc_cons[currcons].vc_decawm)
case 7:
reverse = 1;
break;
+ case 10: /* ANSI X3.64-1979 (SCO-ish?)
+ * Select primary font, don't display
+ * control chars if defined, don't set
+ * bit 8 on output.
+ */
+ translate = (charset == 0
+ ? G0_charset
+ : G1_charset);
+ disp_ctrl = 0;
+ toggle_meta = 0;
+ break;
+ case 11: /* ANSI X3.64-1979 (SCO-ish?)
+ * Select first alternate font, let's
+ * chars < 32 be displayed as ROM chars.
+ */
+ translate = NULL_TRANS;
+ disp_ctrl = 1;
+ toggle_meta = 0;
+ break;
+ case 12: /* ANSI X3.64-1979 (SCO-ish?)
+ * Select second alternate font, toggle
+ * high bit before displaying as ROM char.
+ */
+ translate = NULL_TRANS;
+ disp_ctrl = 1;
+ toggle_meta = 1;
+ break;
case 21:
case 22:
intensity = 1;
case 27:
reverse = 0;
break;
- case 39:
+ case 38: /* ANSI X3.64-1979 (SCO-ish?)
+ * Enables underscore, white foreground
+ * with white underscore (Linux - use
+ * default foreground).
+ */
+ color = (def_color & 0x0f) | background;
+ underline = 1;
+ break;
+ case 39: /* ANSI X3.64-1979 (SCO-ish?)
+ * Disable underline option.
+ * Reset colour to default? It did this
+ * before...
+ */
color = (def_color & 0x0f) | background;
+ underline = 0;
break;
case 49:
color = (def_color & 0xf0) | foreground;
charset = 0;
need_wrap = 0;
+ disp_ctrl = 0;
+ toggle_meta = 0;
+
decscnm = 0;
decom = 0;
decawm = 1;
while (!tty->stopped && count) {
c = from_user ? get_fs_byte(buf) : *buf;
buf++; n++; count--;
- if (vc_state == ESnormal && translate[c]) {
+ if (vc_state == ESnormal
+ && (c >= 32 || (disp_ctrl && (c&0x7f) != 27))
+ && (toggle_meta ? translate[c|0x80] : translate[c])) {
+ if (toggle_meta)
+ c |= 0x80;
if (need_wrap) {
cr(currcons);
lf(currcons);
--- /dev/null
+/*
+ * Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
+ * By Craig Southeren
+ *
+ * 3c505.c This module implements an interface to the 3Com
+ * Etherlink Plus (3c505) ethernet card. Linux device
+ * driver interface reverse engineered from the Linux 3C509
+ * device drivers. Vital 3C505 information gleaned from
+ * the Crynwr packet driver
+ *
+ * Version: @(#)3c505.c 0.1 23/09/93
+ *
+ * Authors: Linux 3c505 device driver by:
+ * Craig Southeren, <geoffw@extro.ucc.su.oz.au>
+ * Linux 3C509 driver by:
+ * Donald Becker, <becker@super.org>
+ * Crynwr packet driver by
+ * Krishnan Gopalan and Gregg Stefancik,
+ * Clemson Univesity Engineering Computer Operations.
+ * Portions of the code have been adapted from the 3c505
+ * driver for NCSA Telnet by Bruce Orchard and later
+ * modified by Warren Van Houten and krus@diku.dk.
+ * 3C505 technical information provided by
+ * Terry Murphy, of 3Com Network Adapter Division
+ * Special thanks to Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <asm/io.h>
+#ifndef port_read
+#include "iow.h"
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c505.h"
+
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
+#else
+static int elp_debug = 0;
+#endif
+
+/*
+ * 0 = no messages
+ * 1 = messages when high level commands performed
+ * 2 = messages when low level commands performed
+ * 3 = messages when interrupts received
+ */
+
+#define ELP_VERSION "0.1.0"
+
+extern struct device *irq2dev_map[16];
+
+/*****************************************************************
+ *
+ * useful macros
+ *
+ *****************************************************************/
+
+#define INB(port) inb((unsigned short)port)
+#define OUTB(val,port) outb((unsigned char)val,(unsigned short)port);
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/*****************************************************************
+ *
+ * PCB structure
+ *
+ *****************************************************************/
+
+typedef struct {
+ unsigned char command; /* PCB command code */
+ unsigned char length; /* PCB data length */
+ unsigned char data[MAX_PCB_DATA]; /* PCB data */
+} pcb_struct;
+
+
+/*****************************************************************
+ *
+ * structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+typedef struct {
+ int io_addr; /* base I/O address */
+ short got_configure; /* set to TRUE when configure response received */
+ pcb_struct tx_pcb; /* PCB for foreground sending */
+ pcb_struct rx_pcb; /* PCB for foreground receiving */
+ pcb_struct itx_pcb; /* PCB for background sending */
+ pcb_struct irx_pcb; /* PCB for background receiving */
+ struct enet_statistics stats;
+} elp_device;
+
+/*****************************************************************
+ *
+ * useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define GET_ASF() (get_status(adapter)&ASF_PCB_MASK)
+#define GET_STATUS() (get_status(adapter))
+
+static int get_status (elp_device * adapter)
+
+{
+ register int stat1;
+ do {
+ stat1 = INB(adapter->io_addr+PORT_STATUS);
+ } while (stat1 != INB(adapter->io_addr+PORT_STATUS));
+ return stat1;
+}
+
+#define SET_HSF(hsf) (set_hsf(adapter,hsf))
+
+static void set_hsf (elp_device * adapter, int hsf)
+
+{
+ cli();
+ OUTB((INB(adapter->io_addr+PORT_CONTROL)&(~HSF_PCB_MASK))|hsf, adapter->io_addr+PORT_CONTROL);
+ sti();
+}
+
+#define WAIT_HCRE(toval) (wait_hcre(adapter,toval))
+
+static int wait_hcre(elp_device * adapter, int toval)
+
+{
+ int timeout = jiffies + toval;
+ while(((INB(adapter->io_addr+PORT_STATUS)&STATUS_HCRE)==0) &&
+ (jiffies <= timeout))
+ ;
+ if (jiffies > timeout) {
+ printk("elp0: timeout waiting for HCRE\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ * Send a PCB to the adapter.
+ *
+ * output byte to command reg --<--+
+ * wait until HCRE is non zero |
+ * loop until all bytes sent -->--+
+ * set HSF1 and HSF2 to 1
+ * output pcb length
+ * wait until ASF give ACK or NAK
+ * set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+static int send_pcb(elp_device * adapter, pcb_struct * pcb)
+
+{
+ int i;
+ int cont;
+ int retry = 0;
+ int timeout;
+
+ while (1) {
+
+ cont = 1;
+
+ /*
+ * load each byte into the command register and
+ * wait for the HCRE bit to indicate the adapter
+ * had read the byte
+ */
+ SET_HSF(0);
+ OUTB(pcb->command, (adapter->io_addr)+PORT_COMMAND);
+ cont = WAIT_HCRE(5);
+ /* SET_HSF(0); */
+
+ if (cont) {
+ OUTB(pcb->length, (adapter->io_addr)+PORT_COMMAND);
+ cont = WAIT_HCRE(2);
+ }
+
+ for (i = 0; cont && (i < pcb->length); i++) {
+ OUTB(pcb->data[i], (adapter->io_addr)+PORT_COMMAND);
+ cont = WAIT_HCRE(2);
+ }
+
+ /* set the host status bits to indicate end of PCB */
+ /* send the total packet length as well */
+ /* wait for the adapter to indicate that it has read the PCB */
+ if (cont) {
+ SET_HSF(HSF_PCB_END);
+ OUTB(2+pcb->length, adapter->io_addr+PORT_COMMAND);
+ timeout = jiffies + 6;
+ while (jiffies < timeout) {
+ i = GET_ASF();
+ if ((i == ASF_PCB_ACK) ||
+ (i == ASF_PCB_NAK))
+ break;
+ }
+ if (i == ASF_PCB_ACK) {
+ SET_HSF(0);
+ return TRUE;
+ } else if (i = ASF_PCB_NAK) {
+ SET_HSF(0);
+ printk("elp0: PCB send was NAKed\n");
+ {
+ int to = jiffies + 5;
+ while (jiffies < to)
+ ;
+ }
+ }
+ }
+
+ if (elp_debug >= 6)
+ printk("elp0: NAK/timeout on send PCB\n");
+ if ((retry++ & 7) == 0)
+ printk("elp0: retry #%i on send PCB\n", retry);
+ }
+}
+
+/*****************************************************************
+ *
+ * receive_pcb
+ * Read a PCB to the adapter
+ *
+ * wait for ACRF to be non-zero ---<---+
+ * input a byte |
+ * if ASF1 and ASF2 were not both one |
+ * before byte was read, loop --->---+
+ * set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static int receive_pcb(elp_device * adapter, pcb_struct * pcb)
+
+{
+ int i;
+ int total_length;
+ int stat;
+
+ /* get the command code */
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ ;
+ SET_HSF(0);
+ pcb->command = INB(adapter->io_addr+PORT_COMMAND);
+ if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
+
+ /* read the data length */
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ ;
+ pcb->length = INB(adapter->io_addr+PORT_COMMAND);
+ if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
+
+ /* read the data */
+ i = 0;
+ do {
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ ;
+ pcb->data[i++] = INB(adapter->io_addr+PORT_COMMAND);
+ } while ((stat & ASF_PCB_MASK) != ASF_PCB_END);
+
+ /* woops, the last "data" byte was really the length! */
+ total_length = pcb->data[--i];
+
+ /* safety check total length vs data length */
+ if (total_length != (pcb->length + 2)) {
+ if (elp_debug >= 6)
+ printk("elp0: mangled PCB received\n");
+ SET_HSF(HSF_PCB_NAK);
+ return FALSE;
+ }
+ SET_HSF(HSF_PCB_ACK);
+ return TRUE;
+ }
+ }
+
+ SET_HSF(HSF_PCB_NAK);
+ return FALSE;
+}
+
+static void adapter_hard_reset(elp_device * adapter)
+
+{
+ int timeout;
+
+ /*
+ * take FLSH and ATTN high
+ */
+ OUTB(CONTROL_ATTN|CONTROL_FLSH, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now take them low
+ */
+ OUTB(0, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now hang around until the board gets it's act together
+ */
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF() != ASF_PCB_END)
+ break;
+}
+
+static void adapter_reset(elp_device * adapter)
+
+{
+ int timeout;
+
+ cli();
+ OUTB(CONTROL_ATTN|INB(adapter->io_addr+PORT_CONTROL), adapter->io_addr+PORT_CONTROL);
+ sti();
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ cli();
+ OUTB(INB(adapter->io_addr+PORT_CONTROL)&~(CONTROL_ATTN), adapter->io_addr+PORT_CONTROL);
+ sti();
+}
+
+/******************************************************
+ *
+ * queue a receive command on the adapter so we will get an
+ * interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static int start_receive(elp_device * adapter, pcb_struct * tx_pcb)
+
+{
+ if (elp_debug > 3)
+ printk("elp0: restarting receiver\n");
+ tx_pcb->command = CMD_RECEIVE_PACKET;
+ tx_pcb->length = 8;
+ tx_pcb->data[4] = 1600 & 0xff;
+ tx_pcb->data[5] = 1600 >> 8;
+ tx_pcb->data[6] = 0; /* set timeout to zero */
+ tx_pcb->data[7] = 0;
+ return send_pcb(adapter, tx_pcb);
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void receive_packet(struct device * dev,
+ elp_device * adapter,
+ int len)
+
+{
+ register int i;
+ unsigned short * ptr;
+ short d;
+
+ /*
+ * allocate a buffer to put the packet into.
+ */
+ struct sk_buff *skb;
+ skb = alloc_skb(len+3, GFP_ATOMIC);
+
+ /*
+ * make sure the data register is going the right way
+ */
+ OUTB(INB(adapter->io_addr+PORT_CONTROL)|CONTROL_DIR, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * if buffer could not be allocated, swallow it
+ */
+ if (skb == NULL) {
+ for (i = 0; i < (len/2); i++) {
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0)
+ ;
+ d = inw(adapter->io_addr+PORT_DATA);
+ }
+ adapter->stats.rx_dropped++;
+
+ } else {
+
+ /*
+ * now read the data from the adapter
+ */
+ ptr = (unsigned short *)(skb->data);
+ for (i = 0; i < (len/2); i++) {
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0) {
+ ;
+ }
+ *ptr = inw(adapter->io_addr+PORT_DATA);
+ ptr++;
+ }
+
+ /*
+ * the magic routine "dev_rint" passes the packet up the
+ * protocol chain. If it returns 0, we can assume the packet was
+ * swallowed up. If not, then we are responsible for freeing memory
+ */
+ if (dev_rint((unsigned char *)skb, len, IN_SKBUFF, dev) != 0) {
+ printk("%s: receive buffers full.\n", dev->name);
+ kfree_skb(skb, FREE_READ);
+ }
+ }
+
+ OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL);
+}
+
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static void elp_interrupt(int reg_ptr)
+
+{
+ int len;
+ int dlen;
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev;
+ elp_device * adapter;
+
+ if (irq < 0 || irq > 15) {
+ printk ("elp0: illegal IRQ number found in interrupt routine (%i)\n", irq);
+ return;
+ }
+
+ if (irq != 0xc) {
+ printk ("elp0: warning - interrupt routine has incorrect IRQ of %i\n", irq);
+ return;
+ }
+
+ dev = irq2dev_map[irq];
+ adapter = (elp_device *) dev->priv;
+
+ if (dev == NULL) {
+ printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ if (elp_debug >= 3)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ /*
+ * allow interrupts (we need timers!)
+ */
+ sti();
+
+ /*
+ * receive a PCB from the adapter
+ */
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0) {
+
+ if (receive_pcb(adapter, &adapter->irx_pcb)) {
+
+ switch (adapter->irx_pcb.command) {
+
+ /*
+ * 82586 configured correctly
+ */
+ case CMD_CONFIGURE_82586_RESPONSE:
+ adapter->got_configure = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - configure response received\n", dev->name);
+ break;
+
+ /*
+ * received a packet
+ */
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ len = adapter->irx_pcb.data[6] + (adapter->irx_pcb.data[7] << 8);
+ dlen = adapter->irx_pcb.data[4] + (adapter->irx_pcb.data[5] << 8);
+ if (adapter->irx_pcb.data[8] != 0) {
+ printk("%s: interrupt - packet not received correctly\n", dev->name);
+ } else {
+ if (elp_debug >= 3)
+ printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+ receive_packet(dev, adapter, dlen);
+ if (elp_debug >= 3)
+ printk("%s: packet received\n", dev->name);
+ adapter->stats.rx_packets++;
+ }
+ if (dev->start && !start_receive(adapter, &adapter->itx_pcb))
+ if (elp_debug >= 2)
+ printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: receive procedure complete\n", dev->name);
+
+ break;
+
+ /*
+ * sent a packet
+ */
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ if (elp_debug >= 3)
+ printk("%s: interrupt - packet sent\n", dev->name);
+ if (adapter->irx_pcb.data[4] != 0)
+ if (elp_debug >= 2)
+ printk("%s: interrupt - error sending packet %4.4x\n", dev->name,
+ adapter->irx_pcb.data[4] + (adapter->irx_pcb.data[5] << 8));
+ dev->tbusy = 0;
+ mark_bh(INET_BH);
+ adapter->stats.tx_packets++;
+ break;
+
+ /*
+ * some unknown PCB
+ */
+ default:
+ printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+ break;
+ }
+ } else
+ printk("%s: failed to read PCB on interrupt\n", dev->name);
+ }
+
+ /*
+ * indicate no longer in interrupt routine
+ */
+ dev->interrupt = 0;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int elp_open (struct device *dev)
+
+{
+ elp_device * adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 1)
+ printk("%s: request to open device\n", dev->name);
+
+ /*
+ * make sure we actually found the device
+ */
+ if (adapter == NULL) {
+ printk("%s: Opening a non-existent physical device\n", dev->name);
+ return -EAGAIN;
+ }
+
+ /*
+ * interrupt routine not entered
+ */
+ dev->interrupt = 0;
+
+ /*
+ * transmitter not busy
+ */
+ dev->tbusy = 0;
+
+ /*
+ * install our interrupt service routine
+ */
+ if (request_irq(dev->irq, &elp_interrupt))
+ return -EAGAIN;
+
+ /*
+ * make sure we can find the device header given the interrupt number
+ */
+ irq2dev_map[dev->irq] = dev;
+
+ /*
+ * enable interrupts on the board
+ */
+ OUTB(CONTROL_CMDE, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * device is now offically open!
+ */
+ dev->start = 1;
+
+ /*
+ * configure adapter to receive broadcast messages and wait for response
+ */
+ if (elp_debug >= 2)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.data[0] = 1;
+ adapter->tx_pcb.data[1] = 0;
+ adapter->tx_pcb.length = 2;
+ adapter->got_configure = 0;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else
+ while (adapter->got_configure == 0)
+ ;
+
+ /*
+ * queue a receive command to start things rolling
+ */
+ if (!start_receive(adapter, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+ if (elp_debug >= 2)
+ printk("%s: start receive command sent\n", dev->name);
+
+ return 0; /* Always succeed */
+}
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int elp_close (struct device *dev)
+
+{
+ elp_device * adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 1)
+ printk("%s: request to close device\n", dev->name);
+
+ /*
+ * disable interrupts on the board
+ */
+ OUTB(0x00, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * flag transmitter as busy (i.e. not available)
+ */
+ dev->tbusy = 1;
+
+ /*
+ * indicate device is closed
+ */
+ dev->start = 0;
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq);
+
+ /*
+ * and we no longer have to map irq to dev either
+ */
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static int send_packet (elp_device * adapter, unsigned char * ptr, int len)
+
+{
+ int i;
+
+ /*
+ * make sure the length is even and no shorter than 60 bytes
+ */
+ unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+
+ /*
+ * send the adapter a transmit packet command. Ignore segment and offset
+ * and make sure the length is even
+ */
+ adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+ adapter->tx_pcb.length = 6;
+ adapter->tx_pcb.data[4] = nlen & 0xff;
+ adapter->tx_pcb.data[5] = nlen >> 8;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ return FALSE;
+
+ /*
+ * make sure the data register is going the right way
+ */
+ cli();
+ OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL);
+ sti();
+
+ /*
+ * write data to the adapter
+ */
+ for (i = 0; i < (nlen/2);i++) {
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0)
+ ;
+ outw(*(short *)ptr, adapter->io_addr+PORT_DATA);
+ ptr +=2;
+ }
+
+ return TRUE;
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ * return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
+
+{
+ elp_device * adapter = (elp_device *) dev->priv;
+
+ /*
+ * not sure what this does, but the 3c609 driver does it, so...
+ */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * if we ended up with a munged length, don't send it
+ */
+ if (skb->len <= 0)
+ return 0;
+
+ if (elp_debug >= 1)
+ printk("%s: request to send packet of length %i\n", dev->name, skb->len);
+
+ /*
+ * if the transmitter is still busy, we have a transmit timeout...
+ */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 500)
+ return 1;
+ printk("%s: transmit timed out, resetting adapter\n", dev->name);
+ if ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0)
+ printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
+ adapter_reset(adapter);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ }
+
+ /*
+ * send the packet at (void *)(skb+1) for skb->len
+ */
+ if (!send_packet(adapter, (unsigned char *)(skb->data), skb->len)) {
+ printk("%s: send packet PCB failed\n", dev->name);
+ return 1;
+ }
+
+ if (elp_debug >= 2)
+ printk("%s: packet of length %i sent\n", dev->name, skb->len);
+
+
+ /*
+ * start the transmit timeout
+ */
+ dev->trans_start = jiffies;
+
+ /*
+ * the transmitter is now busy
+ */
+ dev->tbusy = 1;
+
+ /*
+ * if we have been asked to free the buffer, do so
+ */
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct enet_statistics * elp_get_stats(struct device *dev)
+
+{
+ if (elp_debug >= 1)
+ printk("%s: request for stats\n", dev->name);
+
+ elp_device * adapter = (elp_device *) dev->priv;
+ return &adapter->stats;
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Pus board
+ *
+ ******************************************************/
+
+static void elp_init(struct device *dev)
+
+{
+ int i;
+ elp_device * adapter;
+
+ /*
+ * NULL out buffer pointers
+ */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ /*
+ * set ptrs to various functions
+ */
+ dev->open = elp_open; /* local */
+ dev->stop = elp_close; /* local */
+ dev->get_stats = elp_get_stats; /* local */
+ dev->hard_start_xmit = elp_start_xmit; /* local */
+
+ dev->hard_header = eth_header; /* eth.c */
+ dev->add_arp = eth_add_arp; /* eth.c */
+ dev->rebuild_header = eth_rebuild_header; /* eth.c */
+ dev->type_trans = eth_type_trans; /* eth.c */
+
+ dev->queue_xmit = dev_queue_xmit; /* dev.c */
+
+ /*
+ * setup ptr to adapter specific information
+ */
+ adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+ adapter->io_addr = dev->base_addr;
+ memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
+
+
+ /*
+ * Ethernet information
+ */
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ for (i = 0; i < dev->addr_len; i++)
+ dev->broadcast[i] = 0xff;
+
+ /*
+ * New-style flags.
+ */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ /*
+ * memory information
+ */
+ dev->mem_start = dev->mem_end = dev->rmem_end = dev->mem_start = 0;
+}
+
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ * by attempting to get the ethernet address.
+ *
+ ******************************************************/
+
+int elp_probe(struct device *dev)
+
+{
+ elp_device adapter;
+ int i;
+
+ /*
+ * setup adapter structure
+ */
+ adapter.io_addr = dev->base_addr;
+
+ printk ("%s: probing for 3c505...", dev->name);
+
+ /*
+ * get the adapter's undivided attention (if it's there!)
+ */
+ adapter_hard_reset(&adapter);
+
+ /*
+ * use ethernet address command to probe for board in polled mode
+ */
+ adapter.tx_pcb.command = CMD_STATION_ADDRESS;
+ adapter.tx_pcb.length = 0;
+ if (!send_pcb (&adapter, &adapter.tx_pcb) ||
+ !receive_pcb(&adapter, &adapter.rx_pcb) ||
+ (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+ (adapter.rx_pcb.length != 6)) {
+ printk("not found\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = adapter.rx_pcb.data[i];
+
+ printk("found at port 0x%x, address = %s\n", dev->base_addr, eth_print(dev->dev_addr));
+
+ elp_init(dev);
+ return 0;
+}
+
--- /dev/null
+/*****************************************************************
+ *
+ * defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+/*
+ * I/O register offsets
+ */
+#define PORT_COMMAND 0x00 /* read/write */
+#define PORT_STATUS 0x02 /* read only */
+#define PORT_AUXDMA 0x02 /* write only */
+#define PORT_DATA 0x04 /* read/write */
+#define PORT_CONTROL 0x06 /* read/write */
+
+/*
+ * host control registers bits
+ */
+#define CONTROL_ATTN 0x80 /* attention */
+#define CONTROL_FLSH 0x40 /* flush data register */
+#define CONTROL_DMAE 0x20 /* DMA enable */
+#define CONTROL_DIR 0x10 /* direction */
+#define CONTROL_TCEN 0x08 /* terminal count interrupt enable */
+#define CONTROL_CMDE 0x04 /* command register interrupt enable */
+#define CONTROL_HSF2 0x02 /* host status flag 2 */
+#define CONTROL_HSF1 0x01 /* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define HSF_PCB_ACK (CONTROL_HSF1)
+#define HSF_PCB_NAK (CONTROL_HSF2)
+#define HSF_PCB_END (CONTROL_HSF2|CONTROL_HSF1)
+#define HSF_PCB_MASK (CONTROL_HSF2|CONTROL_HSF1)
+
+/*
+ * host status register bits
+ */
+#define STATUS_HRDY 0x80 /* data register ready */
+#define STATUS_HCRE 0x40 /* command register empty */
+#define STATUS_ACRF 0x20 /* adapter command register full */
+#define STATUS_DIR 0x10 /* direction */
+#define STATUS_DONE 0x08 /* DMA done */
+#define STATUS_ASF3 0x04 /* adapter status flag 3 */
+#define STATUS_ASF2 0x02 /* adapter status flag 2 */
+#define STATUS_ASF1 0x01 /* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define ASF_PCB_ACK (STATUS_ASF1)
+#define ASF_PCB_NAK (STATUS_ASF2)
+#define ASF_PCB_END (STATUS_ASF2|STATUS_ASF1)
+#define ASF_PCB_MASK (STATUS_ASF2|STATUS_ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define AUXDMA_BRST 0x01 /* DMA burst */
+
+/*
+ * maximum amount of data data allowed in a PCB
+ */
+#define MAX_PCB_DATA 62
+
+/*****************************************************************
+ *
+ * timeout value
+ * this is a rough value used for loops to stop them from
+ * locking up the whole machine in the case of failure or
+ * error conditions
+ *
+ *****************************************************************/
+
+#define TIMEOUT 10000
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+ /*
+ * host PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
+ CMD_CONFIGURE_82586 = 0x02,
+ CMD_STATION_ADDRESS = 0x03,
+ CMD_DMA_DOWNLOAD = 0x04,
+ CMD_DMA_UPLOAD = 0x05,
+ CMD_PIO_DOWNLOAD = 0x06,
+ CMD_PIO_UPLOAD = 0x07,
+ CMD_RECEIVE_PACKET = 0x08,
+ CMD_TRANSMIT_PACKET = 0x09,
+ CMD_NETWORK_STATISTICS = 0x0a,
+ CMD_LOAD_MULTICAST_LIST = 0x0b,
+ CMD_CLEAR_PROGRAM = 0x0c,
+ CMD_DOWNLOAD_PROGRAM = 0x0d,
+ CMD_EXECUTE_PROGRAM = 0x0e,
+ CMD_SELF_TEST = 0x0f,
+ CMD_SET_STATION_ADDRESS = 0x10,
+ CMD_ADAPTER_INFO = 0x11,
+
+ /*
+ * adapter PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
+ CMD_CONFIGURE_82586_RESPONSE = 0x32,
+ CMD_ADDRESS_RESPONSE = 0x33,
+ CMD_DOWNLOAD_DATA_REQUEST = 0x34,
+ CMD_UPLOAD_DATA_REQUEST = 0x35,
+ CMD_RECEIVE_PACKET_COMPLETE = 0x38,
+ CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
+ CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
+ CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
+ CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
+ CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
+ CMD_EXECUTE_RESPONSE = 0x3e,
+ CMD_SELF_TEST_RESPONSE = 0x3f,
+ CMD_SET_ADDRESS_RESPONSE = 0x40,
+ CMD_ADAPTER_INFO_RESPONSE = 0x41
+};
+
/* First check for a board on the EISA bus. */
if (EISA_bus) {
- for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ static int eisa_addr;
+ for (ioaddr=0x1000 ; ioaddr < 0x9000; ioaddr += 0x1000) {
+ eisa_addr = ioaddr;
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
--- /dev/null
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+ Written 1993, 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+ Adapter. The programming information is from the users manual, as related
+ by glee@ardnassak.math.clemson.edu.
+ */
+
+static char *version =
+ "ac3200.c:v0.03 2/6/94 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include "8390.h"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE 0x00
+#define AC_SA_PROM 0x16 /* The station address PROM. */
+#define AC_ADDR0 0x00 /* Prefix station address values. */
+#define AC_ADDR1 0x40 /* !!!!These are just guesses!!!! */
+#define AC_ADDR2 0x90
+#define AC_ID_PORT 0xC80
+#define AC_EISA_ID 0x0110d305
+#define AC_RESET_PORT 0xC84
+#define AC_RESET 0x00
+#define AC_ENABLE 0x01
+#define AC_CONFIG 0xC90 /* The configuration port. */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
+static char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval) addrmap[(configval) & 7]
+#define config2name(configval) port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG 0x00 /* First page of 8390 TX buffer */
+#define AC_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */
+
+int ac3200_probe(struct device *dev);
+static int ac_probe1(int ioaddr, struct device *dev);
+
+static int ac_open(struct device *dev);
+static void ac_reset_8390(struct device *dev);
+static int ac_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void ac_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static int ac_close_card(struct device *dev);
+\f
+
+/* Probe for the AC3200.
+
+ The AC3200 can be identified by either the EISA configuration registers,
+ or the unique value in the station address PROM.
+ */
+
+int ac3200_probe(struct device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return ac_probe1(ioaddr, dev);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* If you have a pre-pl15 machine you should delete this line. */
+ if ( ! EISA_bus)
+ return ENXIO;
+
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000)
+ if (ac_probe1(ioaddr, dev) == 0)
+ return 0;
+
+ return ENODEV;
+}
+
+static int ac_probe1(int ioaddr, struct device *dev)
+{
+ int i;
+
+#ifndef final_version
+ printk("AC3200 ethercard probe at %#3x:", ioaddr);
+
+ for(i = 0; i < 6; i++)
+ printk(" %02x", inb(ioaddr + AC_SA_PROM + i));
+#endif
+
+ /* !!!!The values of AC_ADDRn (see above) should be corrected when we
+ find out the correct station address prefix!!!! */
+ if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+ || inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+ || inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+#ifndef final_version
+ printk(" not found (invalid prefix).\n");
+#endif
+ return ENODEV;
+ }
+
+ /* The correct probe method is to check the EISA ID. */
+ for (i = 0; i < 4; i++)
+ if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+ printk("EISA ID mismatch, %8x vs %8x.\n",
+ inl(ioaddr + AC_EISA_ID), AC_EISA_ID);
+ return ENODEV;
+ }
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+#ifndef final_version
+ printk("\nAC3200 ethercard configuration register is %#02x,"
+ " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+ inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+ inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+ /* Assign and snarf the interrupt now. */
+ if (dev->irq == 0)
+ dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (irqaction (dev->irq, &ei_sigaction)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return 0;
+ }
+
+ dev->base_addr = ioaddr;
+
+#ifdef notyet
+ if (dev->mem_start) { /* Override the value from the board. */
+ for (i = 0; i < 7; i++)
+ if (addrmap[i] == dev->mem_start)
+ break;
+ if (i >= 7)
+ i = 0;
+ outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+ }
+#endif
+
+ dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+ dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start
+ + (AC_STOP_PG - AC_START_PG)*256;
+
+ ethdev_init(dev);
+
+ ei_status.name = "AC3200";
+ ei_status.tx_start_page = AC_START_PG;
+ ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+ ei_status.stop_page = AC_STOP_PG;
+ ei_status.word16 = 1;
+
+ printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory at %#x-%#x.\n",
+ dev->name, ioaddr, dev->irq, port_name[dev->if_port],
+ dev->mem_start, dev->mem_end-1);
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &ac_reset_8390;
+ ei_status.block_input = &ac_block_input;
+ ei_status.block_output = &ac_block_output;
+
+ dev->open = &ac_open;
+ dev->stop = &ac_close_card;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int ac_open(struct device *dev)
+{
+#ifdef notyet
+ /* Someday we may enable the IRQ and shared memory here. */
+ int ioaddr = dev->base_addr;
+
+ if (irqaction(dev->irq, &ei_sigaction))
+ return -EAGAIN;
+#endif
+
+ return ei_open(dev);
+}
+
+static void ac_reset_8390(struct device *dev)
+{
+ ushort ioaddr = dev->base_addr;
+
+ outb(AC_RESET, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("resetting AC3200, t=%d...", jiffies);
+
+ ei_status.txing = 0;
+ outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+
+ return;
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static int ac_block_input(struct device *dev, int count, char *buf,
+ int ring_offset)
+{
+ long xfer_start = dev->mem_start + ring_offset - (AC_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy(buf, (char*)xfer_start, semi_count);
+ count -= semi_count;
+ memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+ return dev->rmem_start + count;
+ }
+ memcpy(buf, (char*)xfer_start, count);
+
+ return ring_offset + count;
+}
+
+static void ac_block_output(struct device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = dev->mem_start + ((start_page - AC_START_PG)<<8);
+
+ memcpy((unsigned char *)shmem, buf, count);
+}
+
+static int ac_close_card(struct device *dev)
+{
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+ /* We should someday disable shared memory and interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ac3200.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
DE201 Turbo
DE202 Turbo (TP BNC)
DE210
+ DE422 (EISA)
The driver has been tested on DE100, DE200 and DE202 cards in a
- relatively busy network.
+ relatively busy network. The DE422 has been tested a little.
- This driver will not work for the DE203, DE204 and DE205 series of
- cards, since they have a new custom ASIC in place of the AMD LANCE chip.
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip.
The author may be reached as davies@wanton.lkg.dec.com or
- Digital Equipment Corporation, 146 Main Street, Maynard MA 01754.
+ Digital Equipment Corporation, 550 King Street, Littleton MA 01460.
=========================================================================
The driver was based on the 'lance.c' driver from Donald Becker which is
the filter bit positions correctly. Hash filtering is not yet
implemented in the current driver set.
- The original DEPCA card requires that the ethernet ROM address counter
+ The original DEPCA card requires that the ethernet ROM address counter
be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
- only done when a 0x08 is read as the first address octet (to minimise
- the chances of writing over some other hardware's I/O register). The
- size of the NICSR is tested by a word read: if both bytes are the same,
- the register is 8 bits wide. Also, there is a maximum of only 48kB
- network RAM for this card. My thanks to Torbjorn Lindh for help
- debugging all this (and holding my feet to the fire until I got it
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
right).
The DE200 series boards have on-board 64kB RAM for use as a shared
mode which has not been implemented in this driver (only the 32kB and
64kB modes are supported [16kB/48kB for the original DEPCA]).
- At the most only 2 DEPCA cards can be supported because there is only
- provision for two I/O base addresses on the cards (0x300 and 0x200). The
- base address is 'autoprobed' by looking for the self test PROM and
- detecting the card name. The shared memory base address is decoded by
- 'autoprobing' the Ethernet PROM address information. The second DEPCA is
- detected and information placed in the base_addr variable of the next
- device structure (which is created if necessary), thus enabling
- ethif_probe initialization for the device.
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
************************************************************************
- NOTE: If you are using two DEPCAs, it is important that you assign the
- base memory addresses correctly. The driver autoprobes I/O 0x300 then
- 0x200. The base memory address for the first device must be less than
- that of the second so that the auto probe will correctly assign the I/O
- and memory addresses on the same card. I can't think of a way to do
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
this unambiguously at the moment, since there is nothing on the cards to
tie I/O and memory information together.
Add jabber packet fix from murf@perftech.com
and becker@super.org
0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
- 0.35 8-mar-94 Added DE201 recognition.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
=========================================================================
*/
-static char *version = "depca.c:v0.35 3/8/94 davies@wanton.lkg.dec.com\n";
+static char *version = "depca.c:v0.36 5/16/94 davies@wanton.lkg.dec.com\n";
#include <stdarg.h>
#include <linux/config.h>
#include <asm/dma.h>
#include <linux/netdevice.h>
-#include "iow.h" /* left in for pl13/14 compatibility... */
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "depca.h"
int depca_debug = 1;
#endif
-#ifndef DEPCA_IRQ
-/*#define DEPCA_IRQ {5,9,10,11,15,0}*/
-#define DEPCA_IRQ 5
-#endif
-
#ifndef PROBE_LENGTH
#define PROBE_LENGTH 32
#endif
#endif
#ifndef DEPCA_SIGNATURE
-#define DEPCA_SIGNATURE {"DEPCA","DE100","DE200","DE201","DE202","DE210",""}
+#define DEPCA_SIGNATURE {"DEPCA","DE100",\
+ "DE200","DE201","DE202","DE210",\
+ "DE422",\
+ ""}
#define DEPCA_NAME_LENGTH 8
#endif
#define MAX_NUM_DEPCAS 2
#endif
+#ifndef DEPCA_EISA_IO_PORTS
+#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#endif
+
+#ifndef MAX_EISA_SLOTS
+#define MAX_EISA_SLOTS 8
+#endif
+
/*
** Set the number of Tx and Rx buffers.
*/
short misc; /* Errors and TDR info */
};
-struct depca_ring_info {
-};
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+ to LANCE memory address space */
/*
** The Lance initialization block, described in databook, in common memory.
struct depca_rx_head *rx_ring; /* Pointer to start of RX descriptor ring */
struct depca_tx_head *tx_ring; /* Pointer to start of TX descriptor ring */
struct depca_init init_block;/* Initialization block */
+ long bus_offset; /* (E)ISA bus address offset vs LANCE */
long dma_buffs; /* Start address of Rx and Tx buffers. */
int cur_rx, cur_tx; /* The next free ring entry */
int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
#ifdef HAVE_MULTICAST
static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table);
#endif
+static struct device *isa_probe(struct device *dev);
+static struct device *eisa_probe(struct device *dev);
+static struct device *alloc_device(struct device *dev, int ioaddr);
-static int depca_na;
static int num_depcas = 0, num_eth = 0;;
/*
outw(CSR0, DEPCA_ADDR);\
outw(STOP, DEPCA_DATA)
-#define GET_NICSR(a,b) \
- if (depca_na) { \
- (a) = inw((b)); \
- } else { \
- (a) = inb((b)); \
- }
-
-#define PUT_NICSR(a,b) \
- if (depca_na) { \
- outw((a), (b)); \
- } else { \
- outb((a), (b)); \
- }
-
\f
int depca_probe(struct device *dev)
{
- int *port, ports[] = DEPCA_IO_PORTS;
int base_addr = dev->base_addr;
- int status;
- struct device *eth0 = (struct device *) NULL;
+ int status = -ENODEV;
+ struct device *eth0;
if (base_addr > 0x1ff) { /* Check a single specified location. */
- status = -ENODEV;
if (DevicePresent(base_addr) == 0) { /* Is DEPCA really here? */
status = depca_probe1(dev, base_addr);
}
} else if (base_addr > 0) { /* Don't probe at all. */
- status = -ENXIO;
+ status = -ENXIO;
} else { /* First probe for the DEPCA test */
/* pattern in ROM */
-
- for (status = -ENODEV, port = &ports[0];
- *port && (num_depcas < MAX_NUM_DEPCAS); port++) {
- int ioaddr = *port;
-
-#ifdef HAVE_PORTRESERVE
- if (check_region(ioaddr, DEPCA_TOTAL_SIZE))
- continue;
-#endif
- if (DevicePresent(ioaddr) == 0) {
- if (num_depcas > 0) { /* only gets here in autoprobe */
-
- /*
- ** Check the device structures for an end of list or unused device
- */
- while (dev->next != (struct device *)NULL) {
- if (dev->next->base_addr == 0xffe0) break;
- dev = dev->next; /* walk through eth device list */
- num_eth++; /* increment eth device number */
- }
-
- /*
- ** If no more device structures, malloc one up. If memory could
- ** not be allocated, print an error message.
- **
- */
- if (dev->next == (struct device *)NULL) {
- dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
- GFP_KERNEL);
- } else {
- printk("eth%d: Device not initialised, insufficient memory\n",
- num_eth);
- }
-
- /*
- ** If the memory was allocated, point to the new memory area
- ** and initialize it (name, I/O address, next device (NULL) and
- ** initialisation probe routine).
- */
- if ((dev->next != (struct device *)NULL) &&
- (num_eth > 0) && (num_eth < 9999)) {
- dev = dev->next; /* point to the new device */
- dev->name = (char *)(dev + sizeof(struct device));
- sprintf(dev->name,"eth%d", num_eth); /* New device name */
- dev->base_addr = ioaddr; /* assign the io address */
- dev->next = (struct device *)NULL; /* mark the end of list */
- dev->init = &depca_probe;/* initialisation routine */
- }
- } else {
- eth0 = dev; /* remember the first device */
- status = depca_probe1(dev, ioaddr);
- }
- num_depcas++;
- num_eth++;
- }
- }
- if (eth0) dev = eth0; /* restore the first device */
+ eth0=isa_probe(dev);
+ eth0=eisa_probe(eth0);
+ if (dev->priv) status=0;
}
if (status) dev->base_addr = base_addr;
/*
- ** Stop the DEPCA. Enable the DBR ROM and the ethernet ROM address counter
- ** (for the really old DEPCAs). Disable interrupts and remote boot.
+ ** Stop the DEPCA. Enable the DBR ROM. Disable interrupts and remote boot.
*/
STOP_DEPCA;
- GET_NICSR(nicsr, DEPCA_NICSR);
- nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | AAC | IM);
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ nicsr = inb(DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
if (inw(DEPCA_DATA) == STOP) {
}
}
- if (*name != (char)NULL) { /* found a DEPCA device */
+ if (*name != (char)NULL) { /* found a DEPCA device */
mem_start = mem_base[i];
dev->base_addr = ioaddr;
- printk("%s: DEPCA at %#3x is a %s, ", dev->name, ioaddr, name);
+ if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */
+ printk("%s: %s at %#3x (EISA slot %d)",
+ dev->name, name, ioaddr, ((ioaddr>>12)&0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at %#3x", dev->name, name, ioaddr);
+ }
/* There is a 32 byte station address PROM at DEPCA_PROM address.
The first six bytes are the station address. They can be read
j = 0;
}
- printk("ethernet address ");
+ printk(", h/w address ");
for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet address */
printk("%2.2x:", dev->dev_addr[i] = inb(DEPCA_PROM + j));
}
}
/*
- ** Determine the base address for the DEPCA RAM from the NI-CSR
- ** and make up a DEPCA-specific-data structure.
+ ** Determine the base address for the DEPCA RAM from the NI-CSR
+ ** and make up a DEPCA-specific-data structure.
*/
if (nicsr & BUF) {
offset = 0x8000; /* 32kbyte RAM offset*/
nicsr &= ~BS; /* DEPCA RAM in top 32k */
- printk(",\n with %dkB RAM", netRAM-(offset >> 10));
+ printk(",\n has %dkB RAM", netRAM - 32);
+ } else if ((nicsr & _128KB) && (netRAM!=48)) {
+ offset = 0x0000;
+ printk(",\n has 128kB RAM");
} else {
offset = 0x0000; /* 64k/48k bytes RAM */
- printk(",\n with %dkB RAM", netRAM);
+ printk(",\n has %dkB RAM", netRAM);
}
- mem_start += offset;
- printk(" starting at 0x%.5lx", mem_start);
+ mem_start += offset; /* (E)ISA start address */
+ printk(" at 0x%.5lx", mem_start);
/*
- ** Enable the shadow RAM.
+ ** Enable the shadow RAM.
*/
- nicsr |= SHE;
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ if (strstr(name,"DEPCA")==(char *)NULL) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
/*
** Calculate the ring size based on the available RAM
lp->tx_ring = (struct depca_tx_head *)mem_start;
mem_start += (sizeof(struct depca_tx_head) * j);
- lp->dma_buffs = mem_start & 0x00ffffff;
+ lp->bus_offset = mem_start & 0x00ff0000;
+ mem_start &= LA_MASK; /* LANCE re-mapped start address */
+
+ lp->dma_buffs = mem_start;
mem_start += (PKT_BUF_SZ * j);
/* (mem_start now points to the start of the Tx buffers) */
- /* Initialise the data structures */
+ /* Initialise the data structures wrt CPU */
memset(lp->rx_ring, 0, sizeof(struct depca_rx_head)*j);
memset(lp->tx_ring, 0, sizeof(struct depca_tx_head)*j);
*/
LoadCSRs(dev);
- /*
- ** Store the NICSR width for this DEPCA
- */
- lp->depca_na = depca_na;
-
/*
** Enable DEPCA board interrupts for autoprobing
*/
nicsr = ((nicsr & ~IM)|IEN);
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ outb(nicsr, DEPCA_NICSR);
/* The DMA channel may be passed in on this parameter. */
dev->dma = 0;
dev->irq = autoirq_report(1);
if (dev->irq) {
- printk(" and probed IRQ%d.\n", dev->irq);
+ printk(" and uses IRQ%d.\n", dev->irq);
} else {
- printk(". Failed to detect IRQ line.\n");
+ printk(" and failed to detect IRQ line.\n");
status = -EAGAIN;
}
} else {
- printk(". Assigned IRQ%d.\n", dev->irq);
+ printk(" and assigned IRQ%d.\n", dev->irq);
}
} else {
status = -ENXIO;
dev->mem_start = 0;
/* Fill in the generic field of the device structure. */
- ether_setup(dev);
+ ether_setup(dev);
}
} else {
status = -ENXIO;
/*
** Stop the DEPCA & get the board status information.
*/
- depca_na=lp->depca_na;
STOP_DEPCA;
- GET_NICSR(nicsr, DEPCA_NICSR);
+ nicsr = inb(DEPCA_NICSR);
/*
** Re-initialize the DEPCA...
** Enable DEPCA board interrupts
*/
nicsr = ((nicsr & ~IM & ~LED)|SHE|IEN);
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ outb(nicsr, DEPCA_NICSR);
outw(CSR0,DEPCA_ADDR);
dev->tbusy = 0;
if (depca_debug > 1){
printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA));
- GET_NICSR(nicsr, DEPCA_NICSR);
- printk("nicsr: 0x%4.4x\n",nicsr);
+ printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
}
return 0; /* Always succeed */
for (i = 0; i < 4; i++) {
lp->init_block.filter[i] = 0x0000;
}
- lp->init_block.rx_ring = (unsigned long)lp->rx_ring | lp->rlen;
- lp->init_block.tx_ring = (unsigned long)lp->tx_ring | lp->rlen;
+ lp->init_block.rx_ring = ((unsigned long)lp->rx_ring & LA_MASK) | lp->rlen;
+ lp->init_block.tx_ring = ((unsigned long)lp->tx_ring & LA_MASK) | lp->rlen;
lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */
}
if (tickssofar < 10) {
status = -1;
} else {
- STOP_DEPCA;
- printk("%s: transmit timed out, status %4.4x, resetting.\n",
+ printk("%s: transmit timed out, status %04x, resetting.\n",
dev->name, inw(DEPCA_DATA));
+ STOP_DEPCA;
depca_init_ring(dev);
LoadCSRs(dev);
InitRestartDepca(dev);
char *p = (char *) skb->data;
entry &= lp->rmask; /* Ring around buffer number. */
- buf = (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
+ buf = (unsigned char *)((lp->tx_ring[entry].base+lp->bus_offset) &
+ 0x00ffffff);
/* Wait for a full ring to free up */
while (lp->tx_ring[entry].base < 0);
/* Get new buffer pointer */
entry = lp->cur_tx++;
entry &= lp->rmask; /* Ring around buffer number. */
- buf = (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
+ buf = (unsigned char *)((lp->tx_ring[entry].base+lp->bus_offset) &
+ 0x00ffffff);
/* Wait for a full ring to free up */
while (lp->tx_ring[entry].base < 0);
if (depca_debug > 4) {
unsigned char *pkt =
- (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
+ (unsigned char *)((lp->tx_ring[entry].base+lp->bus_offset) &
+ 0x00ffffff);
printk("%s: tx ring[%d], %#lx, sk_buf %#lx len %d.\n",
dev->name, entry, (unsigned long) &lp->tx_ring[entry],
if (dev == NULL) {
printk ("depca_interrupt(): irq %d for unknown device.\n", irq);
- return;
} else {
lp = (struct depca_private *)dev->priv;
ioaddr = dev->base_addr;
- depca_na = lp->depca_na;
- }
- if (dev->interrupt)
+ if (dev->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
- dev->interrupt = MASK_INTERRUPTS;
+ dev->interrupt = MASK_INTERRUPTS;
- /* mask the DEPCA board interrupts and turn on the LED */
- GET_NICSR(nicsr, DEPCA_NICSR);
- nicsr |= (IM|LED);
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ /* mask the DEPCA board interrupts and turn on the LED */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= (IM|LED);
+ outb(nicsr, DEPCA_NICSR);
- outw(CSR0, DEPCA_ADDR);
- csr0 = inw(DEPCA_DATA);
+ outw(CSR0, DEPCA_ADDR);
+ csr0 = inw(DEPCA_DATA);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(csr0 & ~(INEA|TDMD|STOP|STRT|INIT), DEPCA_DATA);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & ~(INEA|TDMD|STOP|STRT|INIT), DEPCA_DATA);
- if (depca_debug > 5)
+ if (depca_debug > 5)
printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, inw(DEPCA_DATA));
- if (csr0 & RINT) /* Rx interrupt (packet arrived) */
+ if (csr0 & RINT) /* Rx interrupt (packet arrived) */
depca_rx(dev);
- if (csr0 & TINT) /* Tx interrupt (packet sent) */
+ if (csr0 & TINT) /* Tx interrupt (packet sent) */
depca_tx(dev);
- /* Clear the interrupts we've handled. */
- outw(CSR0, DEPCA_ADDR);
- outw(BABL|CERR|MISS|MERR|RINT|TINT|IDON|INEA, DEPCA_DATA);
+ /* Clear the interrupts we've handled. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(BABL|CERR|MISS|MERR|RINT|TINT|IDON|INEA, DEPCA_DATA);
- if (depca_debug > 4) {
- printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
- dev->name, inw(DEPCA_ADDR),
- inw(DEPCA_DATA));
- }
+ if (depca_debug > 4) {
+ printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+ dev->name, inw(DEPCA_ADDR),
+ inw(DEPCA_DATA));
+ }
- /* Unmask the DEPCA board interrupts and turn off the LED */
- nicsr = (nicsr & ~IM & ~LED);
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ /* Unmask the DEPCA board interrupts and turn off the LED */
+ nicsr = (nicsr & ~IM & ~LED);
+ outb(nicsr, DEPCA_NICSR);
+ dev->interrupt = UNMASK_INTERRUPTS;
+ }
- dev->interrupt = UNMASK_INTERRUPTS;
return;
}
skb->len = pkt_len;
skb->dev = dev;
memcpy(skb->data,
- (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
+ (unsigned char *)((lp->rx_ring[entry].base+lp->bus_offset) &
+ 0x00ffffff),
pkt_len);
/*
** Notify the upper protocol layers that there is another
int ioaddr = dev->base_addr;
outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
- outw((unsigned short)(unsigned long)&lp->init_block, DEPCA_DATA);
+ outw((unsigned short)((unsigned long)(&lp->init_block) & LA_MASK),
+ DEPCA_DATA);
outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
- outw((unsigned short)((unsigned long)&lp->init_block >> 16), DEPCA_DATA);
+ outw((unsigned short)(((unsigned long)(&lp->init_block) & LA_MASK) >> 16),
+ DEPCA_DATA);
outw(CSR3, DEPCA_ADDR); /* ALE control */
outw(ACON, DEPCA_DATA);
outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
#endif /* HAVE_MULTICAST */
+/*
+** ISA bus I/O device probe
+*/
+static struct device *isa_probe(dev)
+struct device *dev;
+{
+ int *port, ports[] = DEPCA_IO_PORTS;
+ int status;
+
+ for (status = -ENODEV, port = &ports[0];
+ *port && (num_depcas < MAX_NUM_DEPCAS); port++) {
+ int ioaddr = *port;
+
+#ifdef HAVE_PORTRESERVE
+ if (check_region(ioaddr, DEPCA_TOTAL_SIZE))
+ continue;
+#endif
+
+ if (DevicePresent(ioaddr) == 0) {
+ if (num_depcas > 0) { /* only gets here in autoprobe */
+ dev = alloc_device(dev, ioaddr);
+ } else {
+ if ((status = depca_probe1(dev, ioaddr)) == 0) {
+ num_depcas++;
+ }
+ }
+ num_eth++;
+ }
+ }
+ return dev;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard.
+*/
+static struct device *eisa_probe(dev)
+struct device *dev;
+{
+ int i, ioaddr = DEPCA_EISA_IO_PORTS;
+ int status;
+
+ ioaddr+=0x1000; /* get the first slot address */
+ for (status = -ENODEV, i=1; i<MAX_EISA_SLOTS; i++, ioaddr+=0x1000) {
+
+#ifdef HAVE_PORTRESERVE
+ if (check_region(ioaddr, DEPCA_TOTAL_SIZE))
+ continue;
+#endif
+ if (DevicePresent(ioaddr) == 0) {
+ if (num_depcas > 0) { /* only gets here in autoprobe */
+ dev = alloc_device(dev, ioaddr);
+ } else {
+ if ((status = depca_probe1(dev, ioaddr)) == 0) {
+ num_depcas++;
+ }
+ }
+ num_eth++;
+ }
+ }
+ return dev;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(dev, ioaddr)
+struct device *dev;
+int ioaddr;
+{
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ while (dev->next != (struct device *)NULL) {
+ if (dev->next->base_addr == 0xffe0) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If no more device structures, malloc one up. If memory could
+ ** not be allocated, print an error message.
+ */
+ if (dev->next == (struct device *)NULL) {
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ if (dev->next == (struct device *)NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ }
+ }
+
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ if ((dev->next != (struct device *)NULL) &&
+ (num_eth > 0) && (num_eth < 9999)) {
+ dev = dev->next; /* point to the new device */
+ dev->name = (char *)(dev + sizeof(struct device));
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ dev->base_addr = ioaddr; /* assign the io address */
+ dev->next = (struct device *)NULL; /* mark the end of list */
+ dev->init = &depca_probe; /* initialisation routine */
+ num_depcas++;
+ }
+
+ return dev;
+}
+
/*
** Look for a particular board name in the on-board Remote Diagnostics
** and Boot (RDB) ROM. This will also give us a clue to the network RAM
** messing around with some other hardware, but it assumes that this DEPCA
** card initialized itself correctly. It also assumes that all past and
** future DEPCA/EtherWORKS cards will have ethernet addresses beginning with
-** a 0x08. The choice of byte or word addressing is made here based on whether
-** word read of the NICSR returns two identical lower and upper bytes: if so
-** the register is 8 bits wide.
+** a 0x08.
*/
static int DevicePresent(short ioaddr)
static short fp=1,sigLength=0;
static char devSig[] = PROBE_SEQUENCE;
char data;
- unsigned char LSB,MSB;
int i, j, nicsr, status = 0;
static char asc2hex(char value);
data = inb(DEPCA_PROM); /* clear counter */
data = inb(DEPCA_PROM); /* read data */
-/*
-** Determine whether a byte or word access should be made on the NICSR.
-** Since the I/O 'functions' are actually in-line code, the choice not to use
-** pointers to functions vs. just set a conditional, is made for us. This code
-** assumes that the NICSR has an asymmetric bit pattern already in it.
-*/
- nicsr = inw(DEPCA_NICSR);
- LSB = nicsr & 0xff;
- MSB = (((unsigned) nicsr) >> 8) & 0xff;
- if (MSB == LSB) {
- depca_na = 0; /* byte accesses */
- } else {
- depca_na = 1; /* word accesses */
- }
-
/*
** Enable counter
*/
if (data == 0x08) {
+ nicsr = inb(DEPCA_NICSR);
nicsr |= AAC;
- PUT_NICSR(nicsr, DEPCA_NICSR);
+ outb(nicsr, DEPCA_NICSR);
}
/*
/*
** Search the Ethernet address ROM for the signature. Since the ROM address
** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the length of the (signature - 1).
+** probe sequence length plus the (length_of_the_signature - 1).
** Stop the search IMMEDIATELY after the signature is found so that the
** PROM address counter is correctly positioned at the start of the
** ethernet address for later read out.
#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
+#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
+#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
/*
#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
+#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
#define IM 0x0004 /* Interrupt Mask (1->mask) */
#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
#define LED 0x0001 /* LED control */
#define TMD3_LCAR 0x0800 /* Loss of CARrier */
#define TMD3_RTRY 0x0400 /* ReTRY error */
+/*
+** EISA configuration Register (CNFG) bit definitions
+*/
+
+#define TIMEOUT 0x0100 /* 0:2.5 mins, 1: 30 secs */
+#define REMOTE 0x0080 /* Remote Boot Enable -> 1 */
+#define IRQ11 0x0040 /* Enable -> 1 */
+#define IRQ10 0x0020 /* Enable -> 1 */
+#define IRQ9 0x0010 /* Enable -> 1 */
+#define IRQ5 0x0008 /* Enable -> 1 */
+#define BUFF 0x0004 /* 0: 64kB or 128kB, 1: 32kB */
+#define PADR16 0x0002 /* RAM on 64kB boundary */
+#define PADR17 0x0001 /* RAM on 128kB boundary */
+
/*
** Miscellaneous
*/
#define MASK_INTERRUPTS 1
#define UNMASK_INTERRUPTS 0
+#define EISA_EN 0x0001 /* Enable EISA bus buffers */
+#define DEPCA_EISA_ID ioaddr+0x80 /* ID long word for EISA card */
+#define DEPCA_EISA_CTRL ioaddr+0x84 /* Control word for EISA card */
--- /dev/null
+/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
+/*
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a driver for the Cabletron E2100 series ethercards.
+
+ The Author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ The E2100 series ethercard is a fairly generic shared memory 8390
+ implementation. The only unusual aspect is the way the shared memory
+ registers are set: first you do an inb() in what is normally the
+ station address region, and the low four bits of next outb() is used
+ as the write value for that register. Either someone wasn't too used
+ to dem bit en bites, or they were trying to obfusicate the programming
+ interface.
+
+ There is an additional complication when setting the window on the packet
+ buffer. You must first do a read into the packet buffer region with the
+ low 8 address bits the address setting the page for the start of the packet
+ buffer window, and then do the above operation. See mem_on() for details.
+
+ One bug on the chip is that even a hard reset won't disable the memory
+ window, usually resulting in a hung machine if mem_off() isn't called.
+ If this happens, you must power down the machine for about 30 seconds.
+*/
+
+static char *version =
+ "e2100.c:v0.01 11/21/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#ifndef PRE_PL13
+#include <linux/ioport.h> /* Delete if your kernel doesn't have it. */
+#endif
+
+#include <linux/netdevice.h>
+#include "8390.h"
+
+/* Compatibility definitions for earlier kernel versions. */
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define snarf_region(ioaddr, size); do ; while (0)
+#endif
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c, in ioport.h for later versions. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+/* The map from IRQ number (as passed to the interrupt handler) to
+ 'struct device'. */
+extern struct device *irq2dev_map[16];
+#endif
+
+/* Offsets from the base_addr.
+ Read from the ASIC register, and the low 3(?) bits of the next outb() address
+ is used to set the cooresponding register. */
+#define E21_NIC_OFFSET 0 /* Offset to the 8390 NIC. */
+#define E21_ASIC 0x10
+#define E21_MEM_ENABLE 0x10
+#define E21_MEM_ON 0x05 /* Enable memory in 16 bit mode. */
+#define E21_MEM_ON_8 0x07 /* Enable memory in 8 bit mode. */
+#define E21_MEM_BASE 0x11
+#define E21_IRQ_LOW 0x12 /* The low three bits of the IRQ number. */
+#define E21_IRQ_HIGH 0x14 /* The high IRQ bit, and ... */
+#define E21_ALT_IFPORT 0x02 /* Set to use the other (BNC,AUI) port. */
+#define E21_BIG_MEM 0x04 /* Use a bigger (64K) buffer (we don't) */
+#define E21_SAPROM 0x10 /* Offset to station address data. */
+#define ETHERCARD_TOTAL_SIZE 0x20
+
+extern inline void mem_on(short port, volatile char *mem_base,
+ unsigned char start_page )
+{
+ /* This is a little weird: set the shared memory window by doing a
+ read. The low address bits specify the starting page. */
+ mem_base[start_page];
+ inb(port + E21_MEM_ENABLE);
+ outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
+}
+
+extern inline void mem_off(short port)
+{
+ inb(port + E21_MEM_ENABLE);
+ outb(0x00, port + E21_MEM_ENABLE);
+}
+
+/* In other drivers I put the TX pages first, but the E2100 window circuitry
+ is designed to have a 4K Tx region last. The windowing circuitry wraps the
+ window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
+ appear contiguously in the window. */
+#define E21_RX_START_PG 0x00 /* First page of RX buffer */
+#define E21_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define E21_BIG_RX_STOP_PG 0xF0 /* Last page +1 of RX ring */
+#define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */
+
+int e2100_probe(struct device *dev);
+int e21_probe1(struct device *dev, int ioaddr);
+
+static int e21_open(struct device *dev);
+static void e21_reset_8390(struct device *dev);
+static int e21_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void e21_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int e21_close(struct device *dev);
+
+\f
+/* Probe for the E2100 series ethercards. These cards have an 8390 at the
+ base address and the station address at both offset 0x10 and 0x18. I read
+ the station address from offset 0x18 to avoid the dataport of NE2000
+ ethercards, and look for Ctron's unique ID (first three octets of the
+ station address).
+ */
+
+int e2100_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x280, 0x380, 0x220, 0};
+ short base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return e21_probe1(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = &ports[0]; *port; port++) {
+ ushort ioaddr = *port;
+
+ if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+ continue;
+ if (inb(ioaddr + E21_SAPROM + 0) == 0x00
+ && inb(ioaddr + E21_SAPROM + 1) == 0x00
+ && inb(ioaddr + E21_SAPROM + 2) == 0x1d
+ && e21_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+int e21_probe1(struct device *dev, int ioaddr)
+{
+ int i, status;
+ unsigned char *station_addr = dev->dev_addr;
+
+ /* We've already checked the station address prefix, now verify by making
+ certain that there is a 8390 at the expected location. */
+ outb(E8390_NODMA + E8390_STOP, ioaddr);
+ SLOW_DOWN_IO;
+ status = inb(ioaddr);
+ if (status != 0x21 && status != 0x23)
+ return -ENODEV;
+
+#ifdef testing_only
+ printk("%s: E21xx at %#3x (PAXI backwards): ", dev->name, ioaddr);
+ for (i = 0; i < 16; i++)
+ printk(" %02X", inb(ioaddr + 0x1f - i));
+ printk("\n");
+#endif
+
+ /* Read the station address PROM. */
+ for (i = 0; i < 6; i++)
+ station_addr[i] = inb(ioaddr + E21_SAPROM + i);
+
+ /* Grab the region so we can find another board if needed . */
+ snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE);
+
+ printk("%s: E21xx at %#3x, ", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %02X", station_addr[i]);
+
+ if (dev->irq < 2) {
+ int irqlist[] = {15,11,10,12,5,9,3,4}, i;
+ for (i = 0; i < 8; i++)
+ if (request_irq (irqlist[i], NULL) != -EBUSY) {
+ dev->irq = irqlist[i];
+ break;
+ }
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. */
+ if (irqaction (dev->irq, &ei_sigaction)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return -EBUSY;
+ }
+
+ /* The 8390 is at the base address. */
+ dev->base_addr = ioaddr;
+
+ ethdev_init(dev);
+
+ ei_status.name = "E2100";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = E21_TX_START_PG;
+ ei_status.rx_start_page = E21_RX_START_PG;
+ ei_status.stop_page = E21_RX_STOP_PG;
+
+ /* Check the media port used. The port can be passed in on the
+ low mem_end bits. */
+ if (dev->mem_end & 15)
+ dev->if_port = dev->mem_end & 7;
+ else {
+ dev->if_port = 0;
+ inb_p(ioaddr + E21_IRQ_HIGH); /* Select if_port detect. */
+ for(i = 0; i < 6; i++)
+ if (station_addr[i] != inb(ioaddr + E21_SAPROM))
+ dev->if_port = 1;
+ }
+
+ /* Never map in the E21 shared memory unless you are actively using it.
+ Also, the shared memory has effective only one setting -- spread all
+ over the 128K region! */
+ if (dev->mem_start == 0)
+ dev->mem_start = 0xd0000;
+
+#ifdef notdef
+ /* These values are unused. The E2100 has a 2K window into the packet
+ buffer. The window can be set to start on any page boundary. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start + 2*1024;
+#endif
+
+ printk(" IRQ %d, %s interface, memory at %#x-%#x.\n", dev->irq,
+ dev->if_port ? "secondary" : "primary", dev->mem_start,
+ dev->mem_start + 2*1024 - 1);
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &e21_reset_8390;
+ ei_status.block_input = &e21_block_input;
+ ei_status.block_output = &e21_block_output;
+ dev->open = &e21_open;
+ dev->stop = &e21_close;
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static int
+e21_open(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ /* Set the interrupt line and memory base on the hardware. */
+ inb_p(ioaddr + E21_IRQ_LOW);
+ outb_p(0, ioaddr + E21_ASIC + (dev->irq & 7));
+ inb_p(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb_p(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
+ + (dev->if_port ? E21_ALT_IFPORT : 0));
+ inb_p(ioaddr + E21_MEM_BASE);
+ outb_p(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
+
+ return ei_open(dev);
+}
+
+static void
+e21_reset_8390(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outb(0x01, ioaddr);
+ if (ei_debug > 1) printk("resetting the E2180x3 t=%d...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Block input and output are easy on shared memory ethercards. The E21xx makes
+ block_input() especially easy by wrapping the top ring buffer to the bottom
+ automatically. */
+static int
+e21_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ short ioaddr = dev->base_addr;
+ char *shared_mem = (char *)dev->mem_start;
+ int start_page = (ring_offset>>8);
+
+ mem_on(ioaddr, shared_mem, start_page);
+
+ /* We'll always get a 4 byte header read first. */
+ if (count == 4)
+ ((int*)buf)[0] = ((int*)shared_mem)[0];
+ else
+ memcpy(buf, shared_mem + (ring_offset & 0xff), count);
+
+ /* Turn off memory access: we would need to reprogram the window anyway. */
+ mem_off(ioaddr);
+
+ return 0;
+}
+
+static void
+e21_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ short ioaddr = dev->base_addr;
+ volatile char *shared_mem = (char *)dev->mem_start;
+
+ /* Set the shared memory window start by doing a read, with the low address
+ bits specifing the starting page. */
+ *(shared_mem + start_page);
+ mem_on(ioaddr, shared_mem, start_page);
+
+ memcpy((char*)shared_mem, buf, count);
+ mem_off(ioaddr);
+}
+
+static int
+e21_close(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ NS8390_init(dev, 0);
+
+ mem_off(ioaddr);
+
+ return 0;
+}
+\f
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c e2100.c"
+ * version-control: t
+ * tab-width: 4
+ * kept-new-versions: 5
+ * End:
+ */
#endif
/* New-style flags. */
- dev->flags = IFF_LOOPBACK;
+ dev->flags = IFF_LOOPBACK|IFF_BROADCAST;
dev->family = AF_INET;
#ifdef CONFIG_INET
dev->pa_addr = in_aton("127.0.0.1");
--- /dev/null
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+static char *version = "znet.c:v0.04 5/10/94 becker@cesdis.gsfc.nasa.gov\n";
+
+/*
+ Written by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov.
+ This driver is based on the Linux skeleton driver. The copyright of the
+ skeleton driver is held by the United States Government, as represented
+ by DIRNSA, and it is released under the GPL.
+
+ Thanks to Mike Hollick for alpha testing and suggestions.
+
+ References:
+ The Crynwr packet driver.
+
+ "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ Intel Microcommunications Databook, Vol. 1, 1990.
+ As usual with Intel, the documentation is incomplete and inaccurate.
+ I had to read the Crynwr packet driver to figure out how to actually
+ use the i82593, and guess at what register bits matched the loosely
+ related i82586.
+
+ Theory of Operation
+
+ The i82593 used in the Zenith Z-Note series operates using two(!) slave
+ DMA channels, one interrupt, and one 8-bit I/O port.
+
+ While there several ways to configure '593 DMA system, I chose the one
+ that seemed commesurate with the highest system performance in the face
+ of moderate interrupt latency: Both DMA channels are configued as
+ recirculating ring buffers, with one channel (#0) dedicated to Rx and
+ the other channel (#1) to Tx and configuration. (Note that this is
+ different than the Crynwr driver, where the Tx DMA channel is initialized
+ before each operation. That approach simplifies operation and Tx error
+ recovery, but requires additional I/O in normal operation and precludes
+ transmit buffer chaining.)
+
+ Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
+ a reasonable ring size for Rx, while simplifying DMA buffer allocation --
+ DMA buffers must not cross a 128K boundary. (In truth the size selection
+ was influenced by my lack of '593 documentation. I thus was constrained
+ to use the Crynwr '593 initialization table, which sets the Rx ring size
+ to 8K.)
+
+ Despite my usual low opinion about Intel-designed parts, I must admit
+ that the bulk data handling of the i82593 is a good design for
+ an integrated system, like a laptop, where using two slave DMA channels
+ doesn't pose a problem. I still take issue with using only a single I/O
+ port. In the same controlled environment there are essentially no
+ limitations on I/O space, and using multiple locations would eliminate
+ the need for multiple operations when looking at status registers,
+ setting the Rx ring boundary, or switching to promiscuous mode.
+
+ I also question Zenith's selection of the '593: one of the advertised
+ advantages of earlier Intel parts was that if you figured out the magic
+ initialization incantation you could use the same part on many different
+ network types. Zenith's use of the "FriendlyNet" (sic) connector rather
+ than an on-board transceiver leads me to believe that they were planning
+ to take advantage of this. But, uhmmm, the '593 omits all but ethernet
+ functionality from the serial subsystem.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c, in ioport.h for later versions. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+/* The map from IRQ number (as passed to the interrupt handler) to
+ 'struct device'. */
+extern struct device *irq2dev_map[16];
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(addr, size) kfree_s(addr,size);
+#endif
+
+#ifndef ZNET_DEBUG
+#define ZNET_DEBUG 3
+#endif
+static unsigned int znet_debug = ZNET_DEBUG;
+
+/* The DMA modes we need aren't in <dma.h>. */
+#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
+#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+#define DMA_BUF_SIZE 8192
+#define RX_BUF_SIZE 8192
+#define TX_BUF_SIZE 8192
+
+/* Commands to the i82593 channel 0. */
+#define CMD0_CHNL_0 0x00
+#define CMD0_CHNL_1 0x10 /* Switch to channel 1. */
+#define CMD0_NOP (CMD0_CHNL_0)
+#define CMD0_PORT_1 CMD0_CHNL_1
+#define CMD1_PORT_0 1
+#define CMD0_IA_SETUP 1
+#define CMD0_CONFIGURE 2
+#define CMD0_MULTICAST_LIST 3
+#define CMD0_TRANSMIT 4
+#define CMD0_DUMP 6
+#define CMD0_DIAGNOSE 7
+#define CMD0_Rx_ENABLE 8
+#define CMD0_Rx_DISABLE 10
+#define CMD0_Rx_STOP 11
+#define CMD0_RETRANSMIT 12
+#define CMD0_ABORT 13
+#define CMD0_RESET 14
+
+#define CMD0_ACK 0x80
+
+#define CMD0_STAT0 (0 << 5)
+#define CMD0_STAT1 (1 << 5)
+#define CMD0_STAT2 (2 << 5)
+#define CMD0_STAT3 (3 << 5)
+
+#define net_local znet_private
+struct znet_private {
+ int rx_dma, tx_dma;
+ struct enet_statistics stats;
+ /* The starting, current, and end pointers for the packet buffers. */
+ ushort *rx_start, *rx_cur, *rx_end;
+ ushort *tx_start, *tx_cur, *tx_end;
+ ushort tx_buf_len; /* Tx buffer lenght, in words. */
+};
+
+/* Only one can be built-in;-> */
+static struct znet_private zn;
+static ushort dma_buffer1[DMA_BUF_SIZE/2];
+static ushort dma_buffer2[DMA_BUF_SIZE/2];
+static ushort dma_buffer3[DMA_BUF_SIZE/2 + 8];
+
+/* The configuration block. What an undocumented nightmare. The first
+ set of values are those suggested (without explaination) for ethernet
+ in the Intel 82586 databook. The rest appear to be completely undocumented,
+ except for cryptic notes in the Crynwr packet driver. This driver uses
+ the Crynwr values verbatim. */
+
+static unsigned char i593_init[] = {
+ 0xAA, /* 0: 16-byte input & 80-byte output FIFO. */
+ /* threshhold, 96-byte FIFO, 82593 mode. */
+ 0x88, /* 1: Continuous w/interrupts, 128-clock DMA.*/
+ 0x2E, /* 2: 8-byte preamble, NO address insertion, */
+ /* 6-byte Ethernet address, loopback off.*/
+ 0x00, /* 3: Default priorities & backoff methods. */
+ 0x60, /* 4: 96-bit interframe spacing. */
+ 0x00, /* 5: 512-bit slot time (low-order). */
+ 0xF2, /* 6: Slot time (high-order), 15 COLL retries. */
+ 0x00, /* 7: Promisc-off, broadcast-on, default CRC. */
+ 0x00, /* 8: Default carrier-sense, collision-detect. */
+ 0x40, /* 9: 64-byte minimum frame length. */
+ 0x5F, /* A: Type/length checks OFF, no CRC input,
+ "jabber" termination, etc. */
+ 0x00, /* B: Full-duplex disabled. */
+ 0x3F, /* C: Default multicast addresses & backoff. */
+ 0x07, /* D: Default IFS retriggering. */
+ 0x31, /* E: Internal retransmit, drop "runt" packets,
+ synchr. DRQ deassertion, 6 status bytes. */
+ 0x22, /* F: Receive ring-buffer size (8K),
+ receive-stop register enable. */
+};
+
+struct netidblk {
+ char magic[8]; /* The magic number (string) "NETIDBLK" */
+ unsigned char netid[8]; /* The physical station address */
+ char nettype, globalopt;
+ char vendor[8]; /* The machine vendor and product name. */
+ char product[8];
+ char irq1, irq2; /* Interrupts, only one is currently used. */
+ char dma1, dma2;
+ short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
+ short iobase1, iosize1;
+ short iobase2, iosize2; /* Second iobase unused. */
+ char driver_options; /* Misc. bits */
+ char pad;
+};
+
+int znet_probe(struct device *dev);
+static int znet_open(struct device *dev);
+static int znet_send_packet(struct sk_buff *skb, struct device *dev);
+static void znet_interrupt(int reg_ptr);
+static void znet_rx(struct device *dev);
+static int znet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static void hardware_init(struct device *dev);
+static int do_command(short ioaddr, int command, int length, ushort *buffer);
+static int wait_for_done(short ioaddr);
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
+
+#ifdef notdef
+static struct sigaction znet_sigaction = { &znet_interrupt, 0, 0, NULL, };
+#endif
+
+\f
+/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
+ BIOS area. We just scan for the signature, and pull the vital parameters
+ out of the structure. */
+
+int znet_probe(struct device *dev)
+{
+ int i;
+ struct netidblk *netinfo;
+ char *p;
+
+ /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
+ for(p = (char *)0xf0000; p < (char *)0x100000; p++)
+ if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ break;
+
+ if (p >= (char *)0x100000) {
+ if (znet_debug > 1)
+ printk("No Z-Note ethernet adaptor found.\n");
+ return ENODEV;
+ }
+ netinfo = (struct netidblk *)p;
+ dev->base_addr = netinfo->iobase1;
+ dev->irq = netinfo->irq1;
+
+ printk("%s: ZNET at %#3x,", dev->name, dev->base_addr);
+
+ /* The station address is in the "netidblk" at 0x0f0000. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = netinfo->netid[i]);
+
+ printk(", using IRQ %d DMA %d and %d.\n", dev->irq, netinfo->dma1,
+ netinfo->dma2);
+
+ if (znet_debug > 1) {
+ printk("%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
+ dev->name, netinfo->vendor,
+ netinfo->irq1, netinfo->irq2,
+ netinfo->dma1, netinfo->dma2);
+ printk("%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
+ dev->name, netinfo->iobase1, netinfo->iosize1,
+ netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
+ }
+
+ if (znet_debug > 0)
+ printk(version);
+
+ dev->priv = (void *) &zn;
+ zn.rx_dma = netinfo->dma1;
+ zn.tx_dma = netinfo->dma2;
+
+ /* These should never fail. You can't add devices to a sealed box! */
+ if (request_irq(dev->irq, &znet_interrupt)
+ || request_dma(zn.rx_dma)
+ || request_dma(zn.tx_dma)) {
+ printk("Not opened -- resource busy?!?\n");
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Allocate buffer memory. We can cross a 128K boundary, so we
+ must be careful about the allocation. It's easiest to waste 8K. */
+ if (dma_page_eq(dma_buffer1, &dma_buffer1[RX_BUF_SIZE/2-1]))
+ zn.rx_start = dma_buffer1;
+ else
+ zn.rx_start = dma_buffer2;
+
+ if (dma_page_eq(dma_buffer3, &dma_buffer3[RX_BUF_SIZE/2-1]))
+ zn.tx_start = dma_buffer3;
+ else
+ zn.tx_start = dma_buffer2;
+ zn.rx_end = zn.rx_start + RX_BUF_SIZE/2;
+ zn.tx_buf_len = TX_BUF_SIZE/2;
+ zn.tx_end = zn.tx_start + zn.tx_buf_len;
+
+ /* The ZNET-specific entries in the device structure. */
+ dev->open = &znet_open;
+ dev->hard_start_xmit = &znet_send_packet;
+ dev->stop = &znet_close;
+ dev->get_stats = net_get_stats;
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+
+ /* Fill in the generic field of the device structure. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ dev->hard_header = eth_header;
+ dev->add_arp = eth_add_arp;
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->broadcast[i]=0xff;
+ }
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return 0;
+}
+
+\f
+static int znet_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 2)
+ printk("%s: znet_open() called.\n", dev->name);
+
+ /* Turn on the 82501 SIA, using zenith-specific magic. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) | 0x84, 0xe7); /* Turn on LAN power (bit 2). */
+ /* According to the Crynwr driver we should wait 50 msec. for the
+ LAN clock to stabilize. My experiments indicates that the '593 can
+ be initialized immediately. The delay is probably needed for the
+ DC-to-DC converter to come up to full voltage, and for the oscillator
+ to be spot-on at 20Mhz before transmitting.
+ Until this proves to be a problem we rely on the higher layers for the
+ delay and save allocating a timer entry. */
+
+ /* This follows the packet driver's lead, and checks for success. */
+ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ printk("%s: Problem turning on the transceiver power.\n", dev->name);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ hardware_init(dev);
+ dev->start = 1;
+
+ return 0;
+}
+
+static int znet_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 4)
+ printk("%s: ZNet_send_packet(%d).\n", dev->name, dev->tbusy);
+
+ /* Transmitter timeout, could be a serious problems. */
+ if (dev->tbusy) {
+ ushort event, tx_status, rx_offset, state;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ outb(CMD0_STAT0, ioaddr); event = inb(ioaddr);
+ outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr); rx_offset = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr); state = inb(ioaddr);
+ printk("%s: transmit timed out, status %02x %04x %04x %02x,"
+ " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+ if (tx_status == 0x0400)
+ printk("%s: Tx carrier error, check transceiver cable.\n",
+ dev->name);
+ outb(CMD0_RESET, ioaddr);
+ hardware_init(dev);
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Fill in the ethernet header. */
+ if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
+ skb->dev = dev;
+ arp_queue (skb);
+ return 0;
+ }
+
+ /* Check that the part hasn't reset itself, probably from suspend. */
+ outb(CMD0_STAT0, ioaddr);
+ if (inw(ioaddr) == 0x0010
+ && inw(ioaddr) == 0x0000
+ && inw(ioaddr) == 0x0010)
+ hardware_init(dev);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = (void *)(skb+1);
+ ushort *tx_link = zn.tx_cur - 1;
+ ushort rnd_len = (length + 1)>>1;
+
+ {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ addr <<= 1;
+ if (((int)zn.tx_cur & 0x1ffff) != addr)
+ printk("Address mismatch at Tx: %#x vs %#x.\n",
+ (int)zn.tx_cur & 0xffff, addr);
+ zn.tx_cur = (ushort *)(((int)zn.tx_cur & 0xfe0000) | addr);
+ }
+
+ if (zn.tx_cur >= zn.tx_end)
+ zn.tx_cur = zn.tx_start;
+ *zn.tx_cur++ = length;
+ if (zn.tx_cur + rnd_len + 1 > zn.tx_end) {
+ int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */
+ memcpy(zn.tx_cur, buf, semi_cnt);
+ rnd_len -= semi_cnt>>1;
+ memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt);
+ zn.tx_cur = zn.tx_start + rnd_len;
+ } else {
+ memcpy(zn.tx_cur, buf, skb->len);
+ zn.tx_cur += rnd_len;
+ }
+ *zn.tx_cur++ = 0;
+ cli(); {
+ *tx_link = CMD0_TRANSMIT + CMD0_CHNL_1;
+ /* Is this always safe to do? */
+ outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr);
+ } sti();
+
+ dev->trans_start = jiffies;
+ if (znet_debug > 4)
+ printk("%s: Transmitter queued, length %d.\n", dev->name, length);
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/* The ZNET interrupt handler. */
+static void znet_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int ioaddr;
+ int boguscnt = 20;
+
+ if (dev == NULL) {
+ printk ("znet_interrupt(): IRQ %d for unknown device.\n", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ ioaddr = dev->base_addr;
+
+ outb(CMD0_STAT0, ioaddr);
+ do {
+ ushort status = inb(ioaddr);
+ if (znet_debug > 5) {
+ ushort result, rx_ptr, running;
+ outb(CMD0_STAT1, ioaddr);
+ result = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr);
+ rx_ptr = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr);
+ running = inb(ioaddr);
+ printk("%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
+ dev->name, status, result, rx_ptr, running, boguscnt);
+ }
+ if ((status & 0x80) == 0)
+ break;
+
+ if ((status & 0x0F) == 4) { /* Transmit done. */
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tx_status;
+ outb(CMD0_STAT1, ioaddr);
+ tx_status = inw(ioaddr);
+ /* It's undocumented, but tx_status seems to match the i82586. */
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ } else {
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ /* ...and the catch-all. */
+ if (tx_status | 0x0760 != 0x0760)
+ lp->stats.tx_errors++;
+ }
+ dev->tbusy = 0;
+ mark_bh(INET_BH); /* Inform upper layers. */
+ }
+
+ if ((status & 0x40)
+ || (status & 0x0f) == 11) {
+ znet_rx(dev);
+ }
+ /* Clear the interrupts we've handled. */
+ outb(CMD0_ACK,ioaddr);
+ } while (boguscnt--);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static void znet_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 1;
+ short next_frame_end_offset = 0; /* Offset of next frame start. */
+ short *cur_frame_end;
+ short cur_frame_end_offset;
+
+ outb(CMD0_STAT2, ioaddr);
+ cur_frame_end_offset = inw(ioaddr);
+
+ if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) {
+ printk("%s: Interrupted, but nothing to receive, offset %03x.\n",
+ dev->name, cur_frame_end_offset);
+ return;
+ }
+
+ /* Use same method as the Crynwr driver: construct a forward list in
+ the same area of the backwards links we now have. This allows us to
+ pass packets to the upper layers in the order they were received --
+ important for fast-path sequential operations. */
+ while (zn.rx_start + cur_frame_end_offset != zn.rx_cur
+ && ++boguscount < 5) {
+ unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ int count, status;
+
+ if (cur_frame_end_offset < 4) {
+ /* Oh no, we have a special case: the frame trailer wraps around
+ the end of the ring buffer. We've saved space at the end of
+ the ring buffer for just this problem. */
+ memcpy(zn.rx_end, zn.rx_start, 8);
+ cur_frame_end_offset += (RX_BUF_SIZE/2);
+ }
+ cur_frame_end = zn.rx_start + cur_frame_end_offset - 4;
+
+ lo_status = *cur_frame_end++;
+ hi_status = *cur_frame_end++;
+ status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ lo_cnt = *cur_frame_end++;
+ hi_cnt = *cur_frame_end++;
+ count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+
+ if (znet_debug > 5)
+ printk("Constructing trailer at location %03x, %04x %04x %04x %04x"
+ " count %#x status %04x.\n",
+ cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
+ count, status);
+ cur_frame_end[-4] = status;
+ cur_frame_end[-3] = next_frame_end_offset;
+ cur_frame_end[-2] = count;
+ next_frame_end_offset = cur_frame_end_offset;
+ cur_frame_end_offset -= ((count + 1)>>1) + 3;
+ if (cur_frame_end_offset < 0)
+ cur_frame_end_offset += RX_BUF_SIZE/2;
+ };
+
+ /* Now step forward through the list. */
+ do {
+ ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ int status = this_rfp_ptr[-4];
+ int pkt_len = this_rfp_ptr[-2];
+
+ if (znet_debug > 5)
+ printk("Looking at trailer ending at %04x status %04x length %03x"
+ " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
+ this_rfp_ptr[-3]<<1);
+ /* Once again we must assume that the i82586 docs apply. */
+ if ( ! (status & 0x2000)) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x0800) lp->stats.rx_crc_errors++;
+ if (status & 0x0400) lp->stats.rx_frame_errors++;
+ if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */
+ if (status & 0x0100) lp->stats.rx_fifo_errors++;
+ if (status & 0x0080) lp->stats.rx_length_errors++;
+ } else if (pkt_len > 1536) {
+ lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ int sksize = sizeof(struct sk_buff) + pkt_len;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ if (znet_debug)
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) {
+ int semi_cnt = (zn.rx_end - zn.rx_cur)<<1;
+ memcpy((unsigned char *) (skb + 1), zn.rx_cur, semi_cnt);
+ memcpy((unsigned char *) (skb + 1) + semi_cnt, zn.rx_start,
+ pkt_len - semi_cnt);
+ } else {
+ memcpy((unsigned char *) (skb + 1), zn.rx_cur, pkt_len);
+ if (znet_debug > 6) {
+ unsigned int *packet = (unsigned int *) (skb + 1);
+ printk("Packet data is %08x %08x %08x %08x.\n", packet[0],
+ packet[1], packet[2], packet[3]);
+ }
+ }
+
+#ifdef HAVE_NETIF_RX
+ netif_rx(skb);
+#else
+ skb->lock = 0;
+ if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
+ kfree_s(skb, sksize);
+ lp->stats.rx_dropped++;
+ break;
+ }
+#endif
+ lp->stats.rx_packets++;
+ }
+ zn.rx_cur = this_rfp_ptr;
+ if (zn.rx_cur >= zn.rx_end)
+ zn.rx_cur -= RX_BUF_SIZE/2;
+ update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1);
+ next_frame_end_offset = this_rfp_ptr[-3];
+ if (next_frame_end_offset == 0) /* Read all the frames? */
+ break; /* Done for now */
+ this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ } while (--boguscount);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to znet_open(). */
+static int znet_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(CMD0_RESET, ioaddr); /* CMD0_RESET */
+
+ disable_dma(zn.rx_dma);
+ disable_dma(zn.tx_dma);
+
+ free_irq(dev->irq);
+
+ if (znet_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ /* Turn off transceiver power. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) & ~0x84, 0xe7); /* Turn on LAN power (bit 2). */
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+#ifdef HAVE_MULTICAST
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ As a side effect this routine must also initialize the device parameters.
+ This is taken advantage of in open().
+
+ N.B. that we change i593_init[] in place. This (properly) makes the
+ mode change persistent, but must be changed if this code is moved to
+ a multiple adaptor environment.
+ */
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+
+ if (num_addrs < 0) {
+ /* Enable promiscuous mode */
+ i593_init[7] &= ~3; i593_init[7] |= 1;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else if (num_addrs > 0) {
+ /* Enable accept-all-multicast mode */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else { /* Enable normal mode. */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 0;
+ }
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+#ifdef not_tested
+ if (num_addrs > 0) {
+ int addrs_len = 6*num_addrs;
+ *zn.tx_cur++ = addrs_len;
+ memcpy(zn.tx_cur, addrs, addrs_len);
+ outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr);
+ zn.tx_cur += addrs_len>>1;
+ }
+#endif
+}
+#endif
+
+void show_dma(void)
+{
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Addr: %04x cnt:%3x...", addr<<1,
+ get_dma_residue(zn.tx_dma));
+}
+
+/* Initialize the hardware. We have to do this when the board is open()ed
+ or when we come out of suspend mode. */
+static void hardware_init(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ zn.rx_cur = zn.rx_start;
+ zn.tx_cur = zn.tx_start;
+
+ /* Reset the chip, and start it up. */
+ outb(CMD0_RESET, ioaddr);
+
+ cli(); { /* Protect against a DMA flip-flop */
+ disable_dma(zn.rx_dma); /* reset by an interrupting task. */
+ clear_dma_ff(zn.rx_dma);
+ set_dma_mode(zn.rx_dma, DMA_RX_MODE);
+ set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start);
+ set_dma_count(zn.rx_dma, RX_BUF_SIZE);
+ enable_dma(zn.rx_dma);
+ /* Now set up the Tx channel. */
+ disable_dma(zn.tx_dma);
+ clear_dma_ff(zn.tx_dma);
+ set_dma_mode(zn.tx_dma, DMA_TX_MODE);
+ set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start);
+ set_dma_count(zn.tx_dma, zn.tx_buf_len<<1);
+ enable_dma(zn.tx_dma);
+ } sti();
+
+ if (znet_debug > 1)
+ printk("%s: Initializing the i82593, tx buf %p... ", dev->name,
+ zn.tx_start);
+ /* Do an empty configure command, just like the Crynwr driver. This
+ resets to chip to its default values. */
+ *zn.tx_cur++ = 0;
+ *zn.tx_cur++ = 0;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+
+ update_stop_hit(ioaddr, 8192);
+ if (znet_debug > 1) printk("enabling Rx.\n");
+ outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr);
+ dev->tbusy = 0;
+}
+
+#ifdef notdef
+foo()
+{
+ /*do_command(ioaddr, CMD0_CONFIGURE+CMD0_CHNL_1, sizeof(i593_init) + 2,
+ zn.tx_buffer);*/
+ /*do_command(ioaddr, CMD0_CONFIGURE+CMD0_CHNL_1, 32, zn.tx_buffer);*/
+ /*outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);*/
+
+ if (znet_debug > 1) printk("Set Address... ");
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ {
+ unsigned stop_time = jiffies + 3;
+ while (jiffies < stop_time);
+ }
+ if (znet_debug > 2) {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Terminal addr is %04x, cnt. %03x...", addr<<1,
+ get_dma_residue(zn.tx_dma));
+ }
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ {
+ unsigned stop_time = jiffies + 2;
+ while (jiffies < stop_time);
+ }
+ if (znet_debug > 2) {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Terminal addr is %04x, cnt. %03x...", addr<<1,
+ get_dma_residue(zn.tx_dma));
+ }
+ wait_for_done(ioaddr);
+
+ if (znet_debug > 1) printk("Set Mode... ");
+ set_multicast_list(dev, 0, 0);
+ {
+ unsigned stop_time = jiffies + 3;
+ while (jiffies < stop_time);
+ }
+ if (znet_debug > 2) {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Terminal addr is %04x, cnt. %03x...", addr<<1,
+ get_dma_residue(zn.tx_dma));
+ }
+ if (znet_debug > 2) {
+ int i;
+ outb(CMD0_DUMP+CMD0_CHNL_0, ioaddr);
+ printk("Dumping state:");
+ for (i = 0; i < 16; i++)
+ printk(" %04x", *zn.rx_cur++);
+ printk("\n :");
+ for (;i < 32; i++)
+ printk(" %04x", *zn.rx_cur++);
+ printk("\n");
+ wait_for_done(ioaddr);
+ }
+}
+
+static int do_command(short ioaddr, int command, int length, ushort *buffer)
+{
+ /* This isn't needed, but is here for safety. */
+ outb(CMD0_NOP+CMD0_STAT3,ioaddr);
+ if (inb(ioaddr) & 3)
+ printk("znet: do_command() while the i82593 is busy.\n");
+
+ cli();
+ disable_dma(zn.tx_dma);
+ clear_dma_ff(zn.tx_dma);
+ set_dma_mode(zn.tx_dma,DMA_MODE_WRITE);
+ set_dma_addr(zn.tx_dma,(unsigned int) zn.tx_start);
+ set_dma_count(zn.tx_dma,length);
+ sti();
+ enable_dma(zn.tx_dma);
+ outb(command, ioaddr);
+ return 0;
+}
+
+/* wait_for_done - this is a blatent rip-off of the wait_for_done routine
+ ** from the Crynwr packet driver. It does not work correctly - doesn't
+ ** acknowledge the interrupts it gets or something. It does determine
+ ** when the command is done, or if there are none executing, though...
+ ** -Mike
+ */
+static int wait_for_done(short ioaddr)
+{
+ unsigned int stat;
+ unsigned stop_time = jiffies + 10;
+ int ticks = 0;
+
+ /* check to see if we are busy */
+ outb(CMD0_NOP+CMD0_STAT3,ioaddr);
+ stat = inb(ioaddr);
+
+ /* check if busy */
+ if ((stat&3)==0) {
+ if (znet_debug > 5)
+ printk("wait_for_done(): Not busy, status %02x.\n", stat);
+ return 0;
+ }
+
+ while (jiffies < stop_time) {
+ /* now check */
+ outb(CMD0_NOP+CMD0_STAT3,ioaddr);
+ stat = inb(ioaddr);
+ if ((stat&3)==0) {
+ if (znet_debug > 5)
+ printk("Command completed after %d ticks status %02x.\n",
+ ticks, stat);
+ outb((CMD0_NOP|CMD0_ACK),ioaddr);
+ return 0;
+ }
+ ticks++;
+ }
+ outb(CMD0_ABORT, ioaddr);
+ if (znet_debug)
+ printk("wait_for_done: command not ACK'd, status %02x after abort %02x.\n",
+ stat, inb(ioaddr));
+
+ /* should re-initialize here... */
+ return 1;
+}
+#endif /* notdef */
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+ outb(CMD0_PORT_1, ioaddr);
+ if (znet_debug > 5)
+ printk("Updating stop hit with value %02x.\n",
+ (rx_stop_offset >> 6) | 0x80);
+ outb((rx_stop_offset >> 6) | 0x80, ioaddr);
+ outb(CMD1_PORT_0, ioaddr);
+}
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
{"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","MXT-1240S","I1.2"}, /* Locks up when LUN > 0 polled */
{"MAXTOR","XT-4170S","B5A"}, /* Locks-up sometimes when LUN>0 polled. */
+ {"MAXTOR","XT-8760S","B6B"}, /* Locks-up when LUN > 0 is polled */
{"MAXTOR","XT-8760S","B7B"}, /* guess what? */
{"NEC","CD-ROM DRIVE:841","1.0"}, /* Locks-up when LUN>0 polled. */
{"RODIME","RO3000S","2.33"}, /* Locks up if polled for lun != 0 */
#define ARPHRD_CSLIP6 259
#define ARPHRD_RSRVD 260 /* Notional KISS type */
#define ARPHRD_ADAPT 264
+#define ARPHRD_PPP 512
/* ARP protocol opcodes. */
#define ARPOP_REQUEST 1 /* ARP request */
/* This structure gets passed by the SIOCADDRTOLD and SIOCDELRTOLD calls. */
+
struct old_rtentry {
unsigned long rt_genmask;
struct sockaddr rt_dst;
struct ifnet *rt_ifp;
short rt_metric; /* +1 for binary compatibility! */
char *rt_dev; /* forcing the device at add */
- unsigned long rt_mtu; /* per route MTU/Window */
+ unsigned long rt_mss; /* per route MTU/Window */
+ unsigned long rt_window; /* Window clamping */
};
#define RTF_REINSTATE 0x0008 /* re-instate route after tmout */
#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
-#define RTF_MTU 0x0040 /* specific MSS for this route */
+#define RTF_MSS 0x0040 /* specific MSS for this route */
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+
+/*
+ * REMOVE THESE BY 1.2.0 !!!!!!!!!!!!!!!!!
+ */
+
+#define RTF_MTU RTF_MSS
+#define rt_mtu rt_mss
+
#endif /* _LINUX_ROUTE_H */
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
+#include <linux/segment.h>
+#include <asm/segment.h>
/*
* Define this if things work differently on a i386 and a i486:
{
struct vm_area_struct * vma;
+ /* If the current user space is mapped to kernel space (for the
+ * case where we use a fake user buffer with get_fs/set_fs()) we
+ * don't expect to find the address in the user vm map.
+ */
+ if (get_fs() == get_ds())
+ return 0;
+
for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
goto bad_area;
-3c509.o de600.o 3c501.o plip.o
+3c509.o de600.o de620.o 3c501.o plip.o
int err;
sock->conn = NULL;
- if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+ if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
{
sock->state = SS_CONNECTED;
/* Connection completing after a connect/EINPROGRESS/select/connect */
*/
#ifdef not_a_good_idea
ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
- ip, 0, icmph->un.gateway, dev,0);
+ ip, 0, icmph->un.gateway, dev,0, 0);
break;
#endif
case ICMP_REDIR_HOST:
break;
printk("redirect from %08lx\n", source);
ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
- ip, 0, icmph->un.gateway, dev,0);
+ ip, 0, icmph->un.gateway, dev,0, 0);
break;
case ICMP_REDIR_NETTOS:
case ICMP_REDIR_HOSTTOS:
#ifdef CONFIG_IP_FORWARD
ip_forward(skb, dev, is_frag);
#else
- printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
- iph->saddr,iph->daddr);
+/* printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
+ iph->saddr,iph->daddr);*/
ip_statistics.IpInAddrErrors++;
#endif
/*
* identification, support for local net 0 and
* multiple datalinks <Greg Page>
* Revision 0.26: Device drop kills IPX routes via it. (needed for modules)
+ * Revision 0.27: Autobind <Mark Evans>
*
*
*
static unsigned short first_free_socketnum(void)
{
static unsigned short socketNum = 0x4000;
- unsigned short startNum, foundNum = 0;
- startNum = socketNum;
- do {
- if (ipx_find_socket(htons(socketNum)) == NULL) {
- foundNum = socketNum;
- }
- socketNum++;
+ while (ipx_find_socket(htons(socketNum)) != NULL)
if (socketNum > 0x7ffc) socketNum = 0x4000;
- } while (!foundNum && (socketNum != startNum));
- return htons(foundNum);
+ return htons(socketNum++);
}
static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
return(-EINVAL);
addr=(struct sockaddr_ipx *)uaddr;
- if(sk->ipx_source_addr.net==0) /* Must bind first - no autobinding in this */
- return -EINVAL;
-
+ if(sk->ipx_source_addr.net==0)
+ /* put the autobinding in */
+ {
+ struct sockaddr_ipx uaddr;
+ int ret;
+
+ uaddr.sipx_port = 0;
+ uaddr.sipx_network = 0L;
+ ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+ if (ret != 0) return (ret);
+ }
sk->ipx_dest_addr.net=addr->sipx_network;
sk->ipx_dest_addr.sock=addr->sipx_port;
if(usipx)
{
+ if(sk->ipx_source_addr.net==0)
+ /* put the autobinding in */
+ {
+ struct sockaddr_ipx uaddr;
+ int ret;
+
+ uaddr.sipx_port = 0;
+ uaddr.sipx_network = 0L;
+ ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx));
+ if (ret != 0) return (ret);
+ }
+
if(addr_len <sizeof(*usipx))
return(-EINVAL);
if(usipx->sipx_family != AF_IPX)
* Alan Cox : Added BSD route gw semantics
* Alan Cox : Super /proc >4K
* Alan Cox : MTU in route table
+ * Alan Cox : MSS actually. Also added the window
+ * clamper.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
*/
void ip_rt_add(short flags, unsigned long dst, unsigned long mask,
- unsigned long gw, struct device *dev, unsigned short mtu)
+ unsigned long gw, struct device *dev, unsigned short mtu, unsigned long window)
{
struct rtable *r, *rt;
struct rtable **rp;
rt->rt_dev = dev;
rt->rt_gateway = gw;
rt->rt_mask = mask;
- rt->rt_mtu = dev->mtu;
+ rt->rt_mss = dev->mtu - HEADER_SIZE;
+ rt->rt_window = 0; /* Default is no clamping */
/* Are the MSS/Window valid ? */
- if(rt->rt_flags & RTF_MTU)
- rt->rt_mtu = mtu;
+ if(rt->rt_flags & RTF_MSS)
+ rt->rt_mss = mtu;
+
+ if(rt->rt_flags & RTF_WINDOW)
+ rt->rt_window = window;
/*
* What we have to do is loop though this until we have
* Add the route
*/
- ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mtu);
+ ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window);
return 0;
}
int size;
len += sprintf(buffer,
- "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\n");
+ "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\n");
pos=len;
/*
for (r = rt_base; r != NULL; r = r->rt_next)
{
- size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\n",
+ size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\n",
r->rt_dev->name, r->rt_dst, r->rt_gateway,
r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
- r->rt_mask, (int)r->rt_mtu);
+ r->rt_mask, (int)r->rt_mss, r->rt_window);
len+=size;
pos+=size;
if(pos<offset)
unsigned char rt_metric;
short rt_refcnt;
unsigned long rt_use;
- unsigned short rt_mss, rt_mtu;
+ unsigned short rt_mss;
+ unsigned long rt_window;
struct device *rt_dev;
};
extern void ip_rt_flush(struct device *dev);
extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
- unsigned long gw, struct device *dev, unsigned short);
+ unsigned long gw, struct device *dev, unsigned short mss, unsigned long window);
extern struct rtable *ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr);
extern struct rtable *ip_rt_local(unsigned long daddr, struct options *opt, unsigned long *src_addr);
extern int rt_get_info(char * buffer, char **start, off_t offset, int length);
volatile unsigned short mss; /* current eff. mss - can change */
volatile unsigned short user_mss; /* mss requested by user in ioctl */
volatile unsigned short max_window;
+ unsigned long window_clamp;
unsigned short num;
volatile unsigned short cong_window;
volatile unsigned short cong_count;
* Alan Cox : Move to kernel side addressing changes.
* Alan Cox : Beginning work on TCP fastpathing (not yet usable)
* Arnt Gulbrandsen: Turbocharged tcp_check() routine.
+ * Alan Cox : TCP fast path debugging
+ * Alan Cox : Window clamping
+ * Michael Riepe : Bug in tcp_check()
*
*
* To Fix:
#include <asm/segment.h>
#include <linux/mm.h>
-#define TCP_FASTPATH
+#undef TCP_FASTPATH
#define SEQ_TICK 3
unsigned long seq_offset;
in order to get the data we are waiting for into the memory limit.
Secondly we bin common duplicate forms at receive time
- TODO: add sk->window_clamp to limit windows over the DE600 and AX.25
-
Better heuristics welcome
*/
{
int new_window = sk->prot->rspace(sk);
+ if(sk->window_clamp)
+ new_window=min(sk->window_clamp,new_window);
/*
* two things are going on here. First, we don't ever offer a
* window less than min(sk->mss, MAX_WINDOW/2). This is the
adcl $0, %%ebx
movl %%edx, %%ecx
2: andl $28, %%ecx
- cmpl $4, %%ecx
- jb 4f
+ je 4f
shrl $2, %%ecx
clc
3: lodsl
je 5f
lodsw
addl %%eax, %%ebx
+ adcl $0, %%ebx
movw $0, %%ax
5: test $1, %%edx
je 6f
lodsb
addl %%eax, %%ebx
+ adcl $0, %%ebx
6: movl %%ebx, %%eax
shrl $16, %%eax
addw %%ax, %%bx
*/
rt=ip_rt_route(saddr, NULL,NULL);
+
+ if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
+ sk->window_clamp=rt->rt_window;
+ else
+ sk->window_clamp=0;
+
if (sk->user_mss)
newsk->mtu = sk->user_mss;
- else if(rt!=NULL && (rt->rt_flags&RTF_MTU))
- newsk->mtu = rt->rt_mtu - HEADER_SIZE;
+ else if(rt!=NULL && (rt->rt_flags&RTF_MSS))
+ newsk->mtu = rt->rt_mss - HEADER_SIZE;
else
{
#ifdef CONFIG_INET_SNARL /* Sub Nets ARe Local */
sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
if (tmp < 0)
{
+ sk->write_seq++; /* Very important 8) */
kfree_skb(buff,FREE_WRITE);
/*
if (sk->retransmits && sk->timeout == TIME_KEEPOPEN)
sk->retransmits = 0;
+#if 0
/*
* Not quite clear why the +1 and -1 here, and why not +1 in next line
*/
if (after(ack, sk->sent_seq+1) || before(ack, sk->rcv_ack_seq-1))
+#else
+ if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq))
+#endif
{
+ if(sk->debug)
+ printk("Ack ignored %lu %lu\n",ack,sk->sent_seq);
if (after(ack, sk->sent_seq) ||
(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT))
{
if (sk->state == TCP_FIN_WAIT1)
{
+
if (!sk->dead)
sk->state_change(sk);
if (sk->rcv_ack_seq == sk->write_seq)
if (sk->state == TCP_CLOSING)
{
+
if (!sk->dead)
sk->state_change(sk);
if (sk->rcv_ack_seq == sk->write_seq)
sk->state_change(sk);
return(0);
}
+#if 0
/* Discard the frame here - we've already proved its a duplicate */
kfree_skb(skb, FREE_READ);
return(0);
+#endif
}
/*
* Now we have to walk the chain, and figure out where this one
t1->urg_ptr = 0;
t1->doff = 6;
/* use 512 or whatever user asked for */
+
+ if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
+ sk->window_clamp=rt->rt_window;
+ else
+ sk->window_clamp=0;
if (sk->user_mss)
sk->mtu = sk->user_mss;
- else if(rt!=NULL && rt->rt_flags&RTF_MTU)
- sk->mtu = rt->rt_mtu;
+ else if(rt!=NULL && (rt->rt_flags&RTF_MTU))
+ sk->mtu = rt->rt_mss;
else
{
#ifdef CONFIG_INET_SNARL
*/
/* Im trusting gcc to optimise this sensibly... might need judicious application of a software mallet */
- if(!(sk->shutdown & RCV_SHUTDOWN) && sk->state==TCP_ESTABLISHED && !th->urg && !th->syn && !th->fin && !th->rst && !th->urg)
+ if(!(sk->shutdown & RCV_SHUTDOWN) && sk->state==TCP_ESTABLISHED && !th->urg && !th->syn && !th->fin && !th->rst)
{
/* Packets in order. Fits window */
if(th->seq == sk->acked_seq+1 && sk->window && tcp_clean_end(sk))
sk->window-=skb->len; /* We know its effect on the window */
else
sk->window=0;
- sk->acked_seq = th->ack_seq; /* Easy */
+ sk->acked_seq = th->seq+skb->len; /* Easy */
skb->acked=1; /* Guaranteed true */
if(!sk->delay_acks || sk->ack_backlog >= sk->max_ack_backlog ||
sk->bytes_rcv > sk->max_unacked)
return(0);
}
- if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) {
- kfree_skb(skb, FREE_READ);
- release_sock(sk);
- return(0);
- }
if (tcp_data(skb, sk, saddr, len)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
+
+ if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
release_sock(sk);
return(0);