]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.22 1.1.22
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:32 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:32 +0000 (15:09 -0500)
26 files changed:
Makefile
config.in
drivers/block/hd.c
drivers/char/console.c
drivers/net/3c505.c [new file with mode: 0644]
drivers/net/3c505.h [new file with mode: 0644]
drivers/net/3c509.c
drivers/net/ac32000.c [new file with mode: 0644]
drivers/net/depca.c
drivers/net/depca.h
drivers/net/e2100.c [new file with mode: 0644]
drivers/net/loopback.c
drivers/net/znote.c [new file with mode: 0644]
drivers/scsi/scsi.c
include/linux/if_arp.h
include/linux/route.h
mm/memory.c
modules/NET_MODULES
net/inet/af_inet.c
net/inet/icmp.c
net/inet/ip.c
net/inet/ipx.c
net/inet/route.c
net/inet/route.h
net/inet/sock.h
net/inet/tcp.c

index d884c14786b06e3f84969ab18e8ae738de147820..1e9fd91825f9865e1f4c678ce6b5aa54f9295a5d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 21
+SUBLEVEL = 22
 
 all:   Version zImage
 
index d0ea821496eaf71421a2182a562a8ec6459958f8..b90ab874a796a9b3a88a866949ee25bc04ed4b50 100644 (file)
--- a/config.in
+++ b/config.in
@@ -70,10 +70,10 @@ comment 'Network device support'
 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
@@ -81,29 +81,49 @@ if [ "$CONFIG_SLIP" = "y" ]; then
 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
 
index 7dabb9e9b9eaffa239c0dc8e2335892960dddaf7..abe91d0cd1d21dd293e13143d2d8d456bb8361f5 100644 (file)
@@ -655,8 +655,10 @@ static void hd_geninit(void)
                        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;
                }
 
index 532e5416990122a3d20cb85646f3421f15623e48..4c43aee9943049a4f6a085953e5d88583ee25b4a 100644 (file)
@@ -133,6 +133,8 @@ static struct {
        /* 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 */
@@ -185,6 +187,8 @@ static int console_blanked = 0;
 #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)
@@ -682,6 +686,33 @@ static void csi_m(int currcons)
                        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;
@@ -695,8 +726,21 @@ static void csi_m(int currcons)
                        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;
@@ -957,6 +1001,9 @@ static void reset_terminal(int currcons, int do_clear)
        charset         = 0;
        need_wrap       = 0;
 
+       disp_ctrl       = 0;
+       toggle_meta     = 0;
+
        decscnm         = 0;
        decom           = 0;
        decawm          = 1;
@@ -1026,7 +1073,11 @@ static int con_write(struct tty_struct * tty, int from_user,
        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);
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
new file mode 100644 (file)
index 0000000..02eb824
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+ * 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;
+}
+
diff --git a/drivers/net/3c505.h b/drivers/net/3c505.h
new file mode 100644 (file)
index 0000000..727f03b
--- /dev/null
@@ -0,0 +1,124 @@
+/*****************************************************************
+ *
+ *  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
+};
+
index 1385fddf55760cc0009ec0bf473600ea519b3bb6..f4d03f08cb0738ee4c9710f9c62a48e3d103d256 100644 (file)
@@ -94,7 +94,9 @@ int el3_probe(struct device *dev)
 
        /* 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;
diff --git a/drivers/net/ac32000.c b/drivers/net/ac32000.c
new file mode 100644 (file)
index 0000000..1e22eb2
--- /dev/null
@@ -0,0 +1,275 @@
+/* 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:
+ */
index 24351b727742a59683596fe843ae5eff8868cf47..986f82a9e87212a22cabd2bc6e1c160cf7b1495c 100644 (file)
        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>
@@ -148,7 +157,6 @@ static char *version = "depca.c:v0.35 3/8/94 davies@wanton.lkg.dec.com\n";
 #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"
@@ -159,11 +167,6 @@ int depca_debug = DEPCA_DEBUG;
 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
@@ -173,7 +176,10 @@ int depca_debug = 1;
 #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
 
@@ -195,6 +201,14 @@ static short mem_chkd = 0;               /* holds which base addrs have been */
 #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. 
 */
@@ -230,8 +244,8 @@ struct depca_tx_head {
     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.
@@ -249,6 +263,7 @@ struct depca_private {
     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. */
@@ -287,8 +302,10 @@ static int DevicePresent(short ioaddr);
 #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;;
 
 /*
@@ -298,96 +315,26 @@ 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;
@@ -406,14 +353,13 @@ depca_probe1(struct device *dev, short ioaddr)
 
 
     /*
-    ** 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) {
 
@@ -436,11 +382,16 @@ depca_probe1(struct device *dev, short ioaddr)
        }
       }
 
-      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
@@ -457,7 +408,7 @@ depca_probe1(struct device *dev, short ioaddr)
          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));
        }
@@ -481,27 +432,32 @@ depca_probe1(struct device *dev, short ioaddr)
        }
 
        /* 
-        ** 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
@@ -551,12 +507,15 @@ depca_probe1(struct device *dev, short ioaddr)
        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);
 
@@ -592,16 +551,11 @@ depca_probe1(struct device *dev, short ioaddr)
        */
        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;
@@ -616,13 +570,13 @@ depca_probe1(struct device *dev, short ioaddr)
          
          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;
@@ -644,7 +598,7 @@ depca_probe1(struct device *dev, short ioaddr)
        dev->mem_start = 0;
        
        /* Fill in the generic field of the device structure. */
-       ether_setup(dev);
+       ether_setup(dev);
       }
     } else {
       status = -ENXIO;
@@ -670,9 +624,8 @@ depca_open(struct device *dev)
     /*
     ** 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... 
@@ -723,7 +676,7 @@ depca_open(struct device *dev)
     ** 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;                         
@@ -734,8 +687,7 @@ depca_open(struct device *dev)
 
     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 */
@@ -767,8 +719,8 @@ depca_init_ring(struct device *dev)
     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 */ 
 }
@@ -789,10 +741,10 @@ depca_start_xmit(struct sk_buff *skb, struct device *dev)
       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);
@@ -835,7 +787,8 @@ depca_start_xmit(struct sk_buff *skb, struct device *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);
@@ -870,7 +823,8 @@ depca_start_xmit(struct sk_buff *skb, struct device *dev)
        /* 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);
@@ -898,7 +852,8 @@ depca_start_xmit(struct sk_buff *skb, struct device *dev)
 
       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],
@@ -933,54 +888,52 @@ depca_interrupt(int reg_ptr)
 
     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;
 }
 
@@ -1036,7 +989,8 @@ depca_rx(struct device *dev)
            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 
@@ -1146,9 +1100,11 @@ static void LoadCSRs(struct device *dev)
   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 */
@@ -1273,6 +1229,118 @@ static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table
 
 #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
@@ -1315,9 +1383,7 @@ static char *DepcaSignature(unsigned long mem_addr)
 ** 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)
@@ -1325,7 +1391,6 @@ 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);
 
@@ -1336,27 +1401,13 @@ static int DevicePresent(short ioaddr)
   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);
   }
   
 /* 
@@ -1382,7 +1433,7 @@ static int DevicePresent(short ioaddr)
 /* 
 ** 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.
index 531211836dc22c4c58a208b01b6c927d1de1e6ff..a3a30a8aa9cf72385c20448208e94cd20df6f54d 100644 (file)
@@ -14,7 +14,9 @@
 #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.) */
 
 /*
@@ -35,6 +37,7 @@
 #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 */
diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c
new file mode 100644 (file)
index 0000000..b8927d3
--- /dev/null
@@ -0,0 +1,341 @@
+/* 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:
+ */
index f95a7cede16f189ca9b636a22689e4ed9d96bd70..20908d6058964be352eb0ea86200424eb6057ab1 100644 (file)
@@ -128,7 +128,7 @@ loopback_init(struct device *dev)
 #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");
diff --git a/drivers/net/znote.c b/drivers/net/znote.c
new file mode 100644 (file)
index 0000000..9586693
--- /dev/null
@@ -0,0 +1,947 @@
+/* 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:
+ */
index 724155152881b2996be955af3bf934d70e957d38..93b01764314de8fe14f8d967bc19d3ee6e40e8c6 100644 (file)
@@ -142,6 +142,7 @@ static struct blist blacklist[] =
    {"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 */
index ffe9d3e864588f2314f70091522512aa96645414..c62fdcebae5e27f551c365b9acbe65316fad720a 100644 (file)
@@ -37,6 +37,7 @@
 #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                  */
index 2fc76e89ab9716ab79d6851cf2f6f7dcf1e1d005..454917a0cfccd3770ee306bf49896a7b3ffa1405 100644 (file)
@@ -22,6 +22,7 @@
 
 
 /* This structure gets passed by the SIOCADDRTOLD and SIOCDELRTOLD calls. */
+
 struct old_rtentry {
        unsigned long   rt_genmask;
        struct sockaddr rt_dst;
@@ -44,7 +45,8 @@ struct rtentry {
        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 */
 };
 
 
@@ -54,5 +56,14 @@ struct rtentry {
 #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 */
index 711917f61909c1643a0a4493d11f9e2ba6c98d14..ab511b9610f0e480369af5805ea3b6dbffe9a748 100644 (file)
@@ -45,6 +45,8 @@
 #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:
@@ -678,6 +680,13 @@ int verify_area(int type, const void * addr, unsigned long size)
 {
        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;
index d8e7ca0ea1bd3ca35fc0e5e49ab1ba3af9485b70..d7da2013641470b42401f475af8e04c6eb417f4b 100644 (file)
@@ -1 +1 @@
-3c509.o de600.o 3c501.o plip.o
+3c509.o de600.o de620.o 3c501.o plip.o
index e52587de515fed68d64275c52749a37c0b3b0c18..f6062c5dabb0e7401b9a405d190c9fd47cb64080 100644 (file)
@@ -839,7 +839,7 @@ static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
        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 */
index db42b6154d448f907486dbaa1f2c96b6f5c52c4f..46caae53256a7c4734dfb642340cef36b3acfcfd 100644 (file)
@@ -308,7 +308,7 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb,
                         */
 #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:
@@ -324,7 +324,7 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb,
                                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:
index 61f0acf495239fc50cf628adbbe7ec55cdd72d29..7c6222b3a49b3301113d2abf09ae58527d801da8 100644 (file)
@@ -1523,8 +1523,8 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 #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                 
                /*
index 1f450f878bfd0d15d6dfb5271fdd2399fa17932e..9de5d3eec8f73ec9b94bca887a902e8b6b49b413 100644 (file)
@@ -26,6 +26,7 @@
  *                     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>
  *
  *                     
  *
@@ -640,18 +641,11 @@ static int ipx_release(struct socket *sock, struct socket *peer)
 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)
@@ -736,9 +730,17 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
                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;
@@ -970,6 +972,18 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
                
        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)
index 617295d5076d192337ea057db12fd1c8c0f4934e..4ea8dfb09c741e42102bf34232bb20e2c1d0dd11 100644 (file)
@@ -21,6 +21,8 @@
  *             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
@@ -195,7 +197,7 @@ static inline struct device * get_gw_dev(unsigned long gw)
  */
  
 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;
@@ -267,12 +269,16 @@ void ip_rt_add(short flags, unsigned long dst, unsigned long mask,
        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
@@ -449,7 +455,7 @@ static int rt_new(struct rtentry *r)
         *      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;
 }
 
@@ -481,7 +487,7 @@ int rt_get_info(char *buffer, char **start, off_t offset, int length)
        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;
   
        /*
@@ -490,10 +496,10 @@ int rt_get_info(char *buffer, char **start, off_t offset, int length)
         
        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)
index 9c1745250cd2f63fe1b3c4cd4a071ea2b67998b5..a693ffb41499848a719c774122875e44910dd659 100644 (file)
@@ -36,14 +36,15 @@ struct rtable
        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);
index 4db4278f15f68722feaffd4c158947cda0053ee7..331e2cfa0ad3eee59cf742eb30f08b16bdb88fdc 100644 (file)
@@ -109,6 +109,7 @@ struct sock {
   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;
index 79886b8889dc5e42038a4a5a910eb7898c1788c5..eb7c7bf0cbade7b15a5dcf0dc7b3f6faedf65fed 100644 (file)
@@ -76,6 +76,9 @@
  *             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;
@@ -183,8 +186,6 @@ static __inline__ int min(unsigned int a, unsigned int b)
    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
 */
    
@@ -192,6 +193,8 @@ int tcp_select_window(struct sock *sk)
 {
        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
@@ -565,8 +568,7 @@ unsigned short tcp_check(struct tcphdr *th, int len,
            adcl $0, %%ebx
            movl %%edx, %%ecx
 2:         andl $28, %%ecx
-           cmpl $4, %%ecx
-           jb 4f
+           je 4f
            shrl $2, %%ecx
            clc
 3:         lodsl
@@ -578,11 +580,13 @@ unsigned short tcp_check(struct tcphdr *th, int len,
            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
@@ -2024,10 +2028,16 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         */
 
        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 */
@@ -2270,6 +2280,7 @@ static void tcp_close(struct sock *sk, int timeout)
                                         sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
                        if (tmp < 0) 
                        {
+                               sk->write_seq++;        /* Very important 8) */
                                kfree_skb(buff,FREE_WRITE);
 
                                /*
@@ -2481,12 +2492,18 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int
        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)) 
                {
@@ -2841,6 +2858,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int
 
        if (sk->state == TCP_FIN_WAIT1) 
        {
+
                if (!sk->dead) 
                        sk->state_change(sk);
                if (sk->rcv_ack_seq == sk->write_seq) 
@@ -2866,6 +2884,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int
 
        if (sk->state == TCP_CLOSING) 
        {
+
                if (!sk->dead) 
                        sk->state_change(sk);
                if (sk->rcv_ack_seq == sk->write_seq) 
@@ -2974,10 +2993,12 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk,
                                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
@@ -3523,11 +3544,16 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
        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
@@ -3779,7 +3805,7 @@ tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
  */
  
        /* 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))
@@ -3804,7 +3830,7 @@ tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                                        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)
@@ -3939,17 +3965,18 @@ tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                                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);