]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.25 1.1.25
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:33 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:33 +0000 (15:09 -0500)
63 files changed:
Makefile
config.in
drivers/block/cdu31a.c
drivers/block/floppy.c
drivers/block/mcd.c
drivers/block/ramdisk.c
drivers/net/3c505.c
drivers/net/3c505.h
drivers/net/3c505dta.h [new file with mode: 0644]
drivers/net/Makefile
drivers/net/README.3c505 [new file with mode: 0644]
drivers/net/Space.c
drivers/net/ac3200.c [new file with mode: 0644]
drivers/net/hp-plus.c [new file with mode: 0644]
drivers/net/sk_g16.c [new file with mode: 0644]
drivers/net/sk_g16.h [new file with mode: 0644]
drivers/net/slhc.c
drivers/net/slip.c
drivers/net/znet.c [new file with mode: 0644]
drivers/scsi/ChangeLog
drivers/scsi/README.st
drivers/scsi/aha152x.c
drivers/scsi/aha152x.h
drivers/scsi/aha1542.c
drivers/scsi/aha1542.h
drivers/scsi/aha1740.c
drivers/scsi/aha1740.h
drivers/scsi/buslogic.c
drivers/scsi/buslogic.h
drivers/scsi/fdomain.c
drivers/scsi/fdomain.h
drivers/scsi/hosts.h
drivers/scsi/pas16.c
drivers/scsi/pas16.h
drivers/scsi/scsi.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_debug.h
drivers/scsi/sd.c
drivers/scsi/sd.h
drivers/scsi/sd_ioctl.c
drivers/scsi/seagate.c
drivers/scsi/seagate.h
drivers/scsi/sr.c
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/t128.c
drivers/scsi/t128.h
drivers/scsi/ultrastor.c
drivers/scsi/ultrastor.h
drivers/scsi/wd7000.c
drivers/scsi/wd7000.h
fs/buffer.c
fs/devices.c
fs/isofs/inode.c
fs/locks.c
fs/nfs/sock.c
fs/open.c
include/linux/fs.h
include/linux/mcd.h
include/linux/mtio.h
kernel/ksyms.c
net/inet/af_inet.c
net/inet/icmp.c

index 0b06d89ce11bc4afa4fea7abd9912cfa2a092dc1..508e0a6832fb85725f1f554c2d9b228a16344eac 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 24
+SUBLEVEL = 25
 
 all:   Version zImage
 
@@ -38,6 +38,12 @@ endif
 
 ROOT_DEV = CURRENT
 
+#
+# INSTALL_PATH specifies where to place the updated kernel and system map
+# images.  Uncomment if you want to place them anywhere other than root.
+
+#INSTALL_PATH=/boot
+
 #
 # If you want to preset the SVGA mode, uncomment the next line and
 # set SVGA_MODE to whatever number you want.
@@ -188,10 +194,10 @@ zdisk: zImage
        dd bs=8192 if=zImage of=/dev/fd0
 
 zlilo: $(CONFIGURE) zImage
-       if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi
-       if [ -f /zSystem.map ]; then mv /zSystem.map /zSystem.old; fi
-       cat zImage > /vmlinuz
-       cp zSystem.map /
+       if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+       if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi
+       cat zImage > $(INSTALL_PATH)/vmlinuz
+       cp zSystem.map $(INSTALL_PATH)/
        if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
 
 tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs
index b90ab874a796a9b3a88a866949ee25bc04ed4b50..a9a463fc449393234b7a14b343e2815d637d4288 100644 (file)
--- a/config.in
+++ b/config.in
@@ -80,6 +80,8 @@ if [ "$CONFIG_SLIP" = "y" ]; then
 #  bool ' SLIP debugging on' SL_DUMP y
 fi
 bool 'PPP (point-to-point) support' CONFIG_PPP n
+bool 'PLIP (parallel port) support' CONFIG_PLIP n
+bool 'SK_G16 support' CONFIG_SK_G16 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
@@ -104,15 +106,14 @@ if [ "$CONFIG_NET_ISA" = "y" ]; then
        bool 'DEPCA support' CONFIG_DEPCA n
        if [ "$CONFIG_NET_ALPHA" = "y" ]; then
                bool 'EtherExpress support' CONFIG_EEXPRESS n
+               bool 'AT1700 support' CONFIG_AT1700 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 '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
index 9e73e7899abf6ac4f3a9026328f5cce54bcb1839..eab81b0786a47488621c0ada24c5713c38105f00 100644 (file)
  *
  */
 
+/*
+ *
+ * Setting up the Sony CDU31A/CDU33A drive interface card.  If
+ * You have another card, you are on your own.
+ * 
+ *      +----------+-----------------+----------------------+
+ *      |  JP1     |  34 Pin Conn    |                     |
+ *      |  JP2     +-----------------+                     |
+ *      |  JP3                                             |
+ *      |  JP4                                             |
+ *      |                                                  +--+
+ *      |                                                  |  +-+
+ *      |                                                  |  | |  External
+ *      |                                                  |  | |  Connector
+ *      |                                                  |  | |
+ *      |                                                  |  +-+
+ *      |                                                  +--+
+ *      |                                                  |
+ *      |                                         +--------+
+ *      |                                         |
+ *      +------------------------------------------+
+ * 
+ *    JP1 sets the Base Address, using the following settings:
+ * 
+ *     Address         Pin 1           Pin 2
+ *     -------         -----           -----
+ *     0x320           Short           Short
+ *     0x330           Short           Open
+ *     0x340           Open            Short
+ *     0x360           Open            Open
+ * 
+ *    JP2 and JP3 configure the DMA channel; they must be set the same.
+ * 
+ *     DMA             Pin 1           Pin 2           Pin 3
+ *     ---             -----           -----           -----
+ *     1               On              Off             On
+ *     2               Off             On              Off
+ *     3               Off             Off             On
+ * 
+ *    JP4 Configures the IRQ:
+ * 
+ *     IRQ     Pin 1           Pin 2           Pin 3           Pin 4
+ *     ---     -----           -----           -----           -----
+ *     3       Off             Off             On              Off
+ *     4       Off             Off*            Off             On
+ *     5       On              Off             Off             Off
+ *     6       Off             On              Off             Off
+ * 
+ *             * The documentation states to set this for interrupt
+ *               4, but I think that is a mistake.
+ */
+
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -110,6 +162,8 @@ static struct
    { 0x360,    -1,     0 },    /* Secondary standard Sony Interface */
    { 0x320,    -1,     0 },    /* Secondary standard Sony Interface */
    { 0x330,    -1,     0 },    /* Secondary standard Sony Interface */
+   { 0x634,    -1,     0 },    /* Sound FX SC400 */
+   { 0x654,    -1,     0 },    /* Sound FX SC400 */
    { 0 }
 };
 
@@ -201,6 +255,8 @@ static struct wait_queue *cdu31a_irq_wait = NULL;
 static int curr_control_reg = 0; /* Current value of the control register */
 
 
+#if 1 /* This will go away as soon as the isofs code is fixed
+         to use the fops struct. */
 /*
  * This routine returns 1 if the disk has been changed since the last
  * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
@@ -226,6 +282,30 @@ check_cdu31a_media_change(int full_dev, int flag)
 
    return retval;
 }
+#endif
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
+ */
+static int
+scd_disk_change(dev_t full_dev)
+{
+   int retval, target;
+
+
+   target = MINOR(full_dev);
+
+   if (target > 0) {
+      printk("Sony CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   retval = sony_disc_changed;
+   sony_disc_changed = 0;
+
+   return retval;
+}
 
 static inline void
 enable_interrupts(void)
@@ -1823,7 +1903,10 @@ static struct file_operations scd_fops = {
    NULL,                   /* mmap */
    scd_open,               /* open */
    scd_release,            /* release */
-   NULL                    /* fsync */
+   NULL,                   /* fsync */
+   NULL,                  /* fasync */
+   scd_disk_change,       /* media_change */
+   NULL                           /* revalidate */
 };
 
 
index 5fd673fb163df690610e209974edab7f398daa88..dda4bf1e74a723d176e1cf346bd42772f831e676 100644 (file)
@@ -74,6 +74,8 @@
 #define FLOPPY_DMA 2
 #define FDC_FIFO_UNTESTED           /* -bb */
 
+#define FDC_FIFO_BUG
+
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
@@ -287,8 +289,8 @@ static void redo_fd_request(void);
 static void floppy_ready(void);
 static void recalibrate_floppy(void);
 
-static int floppy_grab_irq_and_dma(void);
-static void floppy_release_irq_and_dma(void);
+int floppy_grab_irq_and_dma(void);
+void floppy_release_irq_and_dma(void);
 
 /*
  * These are global variables, as that's the easiest way to give
@@ -506,6 +508,32 @@ static void output_byte(char byte)
        printk("Unable to send byte to FDC\n");
 }
 
+#ifdef FDC_FIFO_BUG
+
+static void output_byte_force(char byte)
+{
+       int counter;
+       unsigned char status;
+
+       if (reset)
+               return;
+       for (counter = 0 ; counter < 10000 ; counter++) {
+               status = inb_p(FD_STATUS);
+               if ((status & (STATUS_READY | STATUS_DIR)) == STATUS_READY) {
+                       outb(byte,FD_DATA);
+                       return;
+               }
+               if ((status & (STATUS_READY | STATUS_BUSY)) == (STATUS_READY | STATUS_BUSY)) {
+                       outb(byte,FD_DATA);
+                       return;
+               }
+       }
+       current_track = NO_TRACK;
+       reset = 1;
+       printk("Unable to send byte to FDC\n");
+}
+#endif         /* FDC_FIFO_BUG */
+
 static int result(void)
 {
        int i = 0, counter, status;
@@ -565,15 +593,27 @@ static inline void perpendicular_mode(unsigned char rate)
                if (rate & 0x40) {
                        unsigned char r = rate & 0x03;
                        if (r == 0)
+#ifndef FDC_FIFO_BUG
                                output_byte(2); /* perpendicular, 500 kbps */
+#else
+                               output_byte_force(2);   /* perpendicular, 500 kbps */
+#endif
                        else if (r == 3)
+#ifndef FDC_FIFO_BUG
                                output_byte(3); /* perpendicular, 1Mbps */
+#else
+                               output_byte_force(3);   /* perpendicular, 1Mbps */
+#endif
                        else {
                                printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n");
                                reset = 1;
                        }
                } else
+#ifndef FDC_FIFO_BUG
                        output_byte(0);         /* conventional mode */
+#else
+                       output_byte_force(0);           /* conventional mode */
+#endif
        } else {
                if (rate & 0x40) {
                        printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n");
@@ -594,9 +634,17 @@ static void configure_fdc_mode(void)
        if (need_configure && (fdc_version == FDC_TYPE_82077)) {
                /* Enhanced version with FIFO & vertical recording. */
                output_byte(FD_CONFIGURE);
+#ifndef FDC_FIFO_BUG
                output_byte(0);
+#else
+               output_byte_force(0);
+#endif
                output_byte(0x1A);      /* FIFO on, polling off, 10 byte threshold */
+#ifndef FDC_FIFO_BUG
                output_byte(0);         /* precompensation from track 0 upwards */
+#else
+               output_byte_force(0);           /* precompensation from track 0 upwards */
+#endif
                need_configure = 0;
                printk(DEVICE_NAME ": FIFO enabled\n");
        }
@@ -1403,7 +1451,7 @@ void floppy_init(void)
 
 static int usage_count = 0;
 
-static int floppy_grab_irq_and_dma(void)
+int floppy_grab_irq_and_dma(void)
 {
        if (usage_count++)
                return 0;
@@ -1420,7 +1468,7 @@ static int floppy_grab_irq_and_dma(void)
        return 0;
 }
 
-static void floppy_release_irq_and_dma(void)
+void floppy_release_irq_and_dma(void)
 {
        if (--usage_count)
                return;
index c89aff7c2c525e38bbf408b5f8ec305327e6e85c..6b62bbd347e73b4e5f4ee169e7b94aafd06c88fc 100644 (file)
                   (Jon Tombs <jon@robots.ox.ac.uk>)
         0.3.3 Added more #defines and mcd_setup()
                   (Jon Tombs <jon@gtex02.us.es>)
+
+       October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
+       Braunschweig, Germany: Total rework to speed up data read operation.
+       Also enabled definition of irq and address from bootstrap, using the
+       environment. linux/init/main.c must be patched to export the env.
+       November 93 added code for FX001 S,D (single & double speed).
+       February 94 added code for broken M 5/6 series of 16-bit single speed.
 */
 
 
@@ -41,6 +48,8 @@
 #include <linux/kernel.h>
 #include <linux/cdrom.h>
 #include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
 
 /* #define REALLY_SLOW_IO  */
 #include <asm/system.h>
@@ -57,8 +66,53 @@ static int mcd_sizes[] = { 0 };
 
 static int mcdPresent = 0;
 
-static char mcd_buf[2048];     /* buffer for block size conversion */
-static int   mcd_bn   = -1;
+#if 0
+#define TEST1 /* <int-..> */
+#define TEST2 /* do_mcd_req */
+#define TEST3 */ /* MCD_S_state */
+#define TEST4 /* QUICK_LOOP-counter */
+#define TEST5 */ /* port(1) state */
+#endif
+
+#if 1
+#define QUICK_LOOP_DELAY udelay(45)  /* use udelay */
+#define QUICK_LOOP_COUNT 20
+#else
+#define QUICK_LOOP_DELAY
+#define QUICK_LOOP_COUNT 140 /* better wait constant time */
+#endif
+/* #define DOUBLE_QUICK_ONLY */
+
+#define CURRENT_VALID \
+  (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
+   && CURRENT -> sector != -1)
+#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
+#define MCD_BUF_SIZ 16
+static volatile int mcd_transfer_is_active;
+static char mcd_buf[2048*MCD_BUF_SIZ]; /* buffer for block size conversion */
+static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
+static volatile int mcd_buf_in, mcd_buf_out = -1;
+static volatile int mcd_error;
+static int mcd_open_count;
+enum mcd_state_e {
+  MCD_S_IDLE,   /* 0 */
+  MCD_S_START,  /* 1 */
+  MCD_S_MODE, /* 2 */
+  MCD_S_READ,   /* 3 */
+  MCD_S_DATA,   /* 4 */
+  MCD_S_STOP,   /* 5 */
+  MCD_S_STOPPING /* 6 */
+};
+static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
+static int mcd_mode = -1;
+static int MCMD_DATA_READ= MCMD_PLAY_READ;
+#define READ_TIMEOUT 3000
+#define WORK_AROUND_MITSUMI_BUG_92
+#define WORK_AROUND_MITSUMI_BUG_93
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+int mitsumi_bug_93_wait = 0;
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
 static short mcd_port = MCD_BASE_ADDR;
 static int   mcd_irq  = MCD_INTR_NR;
 
@@ -75,10 +129,8 @@ static char tocUpToDate;
 static char mcdVersion;
 
 static void mcd_transfer(void);
-static void mcd_start(void);
-static void mcd_status(void);
-static void mcd_read_cmd(void);
-static void mcd_data(void);
+static void mcd_poll(void);
+static void mcd_invalidate_buffers(void);
 static void do_mcd_request(void);
 static void hsg2msf(long hsg, struct msf *msf);
 static void bin2bcd(unsigned char *p);
@@ -99,6 +151,10 @@ void mcd_setup(char *str, int *ints)
       mcd_port = ints[1];
    if (ints[0] > 1)      
       mcd_irq  = ints[2];
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+   if (ints[0] > 2)
+      mitsumi_bug_93_wait = ints[3];
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
 }
 
  
@@ -507,16 +563,34 @@ printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);
 static void
 mcd_transfer(void)
 {
-       long offs;
-
-       while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4)
-       {
-               offs = (CURRENT -> sector & 3) * 512;
-               memcpy(CURRENT -> buffer, mcd_buf + offs, 512);
-               CURRENT -> nr_sectors--;
-               CURRENT -> sector++;
-               CURRENT -> buffer += 512;
+  if (CURRENT_VALID) {
+    while (CURRENT -> nr_sectors) {
+      int bn = CURRENT -> sector / 4;
+      int i;
+      for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
+       ;
+      if (i < MCD_BUF_SIZ) {
+       int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+       int nr_sectors = 4 - (CURRENT -> sector & 3);
+       if (mcd_buf_out != i) {
+         mcd_buf_out = i;
+         if (mcd_buf_bn[i] != bn) {
+           mcd_buf_out = -1;
+           continue;
+         }
        }
+       if (nr_sectors > CURRENT -> nr_sectors)
+         nr_sectors = CURRENT -> nr_sectors;
+       memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
+       CURRENT -> nr_sectors -= nr_sectors;
+       CURRENT -> sector += nr_sectors;
+       CURRENT -> buffer += nr_sectors * 512;
+      } else {
+       mcd_buf_out = -1;
+       break;
+      }
+    }
+  }
 }
 
 
@@ -531,220 +605,419 @@ mcd_interrupt(int unused)
        int st;
 
        st = inb(MCDPORT(1)) & 0xFF;
-       if (st != 0xFF)
+#ifdef TEST1
+               printk("<int1-%02X>", st);
+#endif
+       if (!(st & MFL_STATUS))
        {
                st = inb(MCDPORT(0)) & 0xFF;
-#if 0
-               printk("<int-%02X>", st);
+#ifdef TEST1
+               printk("<int0-%02X>", st);
 #endif
+               if ((st & 0xFF) != 0xFF)
+                 mcd_error = st ? st & 0xFF : -1;
        }
 }
 
 
-/*
- * I/O request routine called from Linux kernel.
- */
-
 static void
 do_mcd_request(void)
 {
-       unsigned int block,dev;
-       unsigned int nsect;
+#ifdef TEST2
+  printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+  mcd_transfer_is_active = 1;
+  while (CURRENT_VALID) {
+    if (CURRENT->bh) {
+      if (!CURRENT->bh->b_lock)
+       panic(DEVICE_NAME ": block not locked");
+    }
+    mcd_transfer();
+    if (CURRENT -> nr_sectors == 0) {
+      end_request(1);
+    } else {
+      mcd_buf_out = -1;                /* Want to read a block not in buffer */
+      if (mcd_state == MCD_S_IDLE) {
+       if (!tocUpToDate) {
+         if (updateToc() < 0) {
+           while (CURRENT_VALID)
+             end_request(0);
+           break;
+         }
+       }
+       mcd_state = MCD_S_START;
+       McdTries = 5;
+       SET_TIMER(mcd_poll, 1);
+      }
+      break;
+    }
+  }
+  mcd_transfer_is_active = 0;
+#ifdef TEST2
+  printk(" do_mcd_request ends\n");
+#endif
+}
 
-repeat:
-       if (!(CURRENT) || CURRENT->dev < 0) return;
-       INIT_REQUEST;
-       dev = MINOR(CURRENT->dev);
-       block = CURRENT->sector;
-       nsect = CURRENT->nr_sectors;
 
-       if (CURRENT == NULL || CURRENT -> sector == -1)
-               return;
 
-       if (CURRENT -> cmd != READ)
-       {
-               printk("mcd: bad cmd %d\n", CURRENT -> cmd);
-               end_request(0);
-               goto repeat;
+static void
+mcd_poll(void)
+{
+  int st;
+
+
+  if (mcd_error) {
+    if (mcd_error & 0xA5) {
+      printk("mcd: I/O error 0x%02x", mcd_error);
+      if (mcd_error & 0x80)
+       printk(" (Door open)");
+      if (mcd_error & 0x20)
+       printk(" (Disk changed)");
+      if (mcd_error & 0x04)
+       printk(" (Read error)");
+      printk("\n");
+      mcd_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+      if (McdTries == 5)
+       printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+      if (!McdTries--) {
+       printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+       if (mcd_transfer_is_active) {
+         McdTries = 0;
+         goto ret;
        }
+       if (CURRENT_VALID)
+         end_request(0);
+       McdTries = 5;
+      }
+    }
+    mcd_error = 0;
+    mcd_state = MCD_S_STOP;
+  }
 
-       mcd_transfer();
 
-       /* if we satisfied the request from the buffer, we're done. */
 
-       if (CURRENT -> nr_sectors == 0)
-       {
-               end_request(1);
-               goto repeat;
-       }
+ immediatly:
+  switch (mcd_state) {
 
-       McdTries = MCD_RETRY_ATTEMPTS;
-       mcd_start();
-}
 
 
-/*
- * Start the I/O for the cdrom. Handle retry count.
- */
+  case MCD_S_IDLE:
+#ifdef TEST3
+    printk("MCD_S_IDLE\n");
+#endif
+    return;
 
-static void
-mcd_start()
-{
-       if (McdTries == 0)
-       {
-               printk("mcd: read failed after %d tries\n", MCD_RETRY_ATTEMPTS);
-               end_request(0);
-               SET_TIMER(do_mcd_request, 1);   /* wait a bit, try again */
-               return;
-       }
 
-       McdTries--;
-       outb(0x40, MCDPORT(0));         /* get status */
-       McdTimeout = MCD_STATUS_DELAY;
-       SET_TIMER(mcd_status, 1);
-}
 
+  case MCD_S_START:
+#ifdef TEST3
+    printk("MCD_S_START\n");
+#endif
 
-/*
- * Called from the timer to check the results of the get-status cmd.
- * On success, send the set-mode command.
- */
+    outb(MCMD_GET_STATUS, MCDPORT(0));
+    mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
+    McdTimeout = 3000;
+    break;
 
-static void
-mcd_status()
-{
-       int st;
 
-       McdTimeout--;
-       st = mcdStatus();
-       if (st == -1)
-       {
-               if (McdTimeout == 0)
-               {
-                       printk("mcd: status timed out\n");
-                       SET_TIMER(mcd_start, 1);        /* wait a bit, try again */
-                       return;
-               }
 
-               SET_TIMER(mcd_status, 1);
-               return;
-       }
+  case MCD_S_MODE:
+#ifdef TEST3
+    printk("MCD_S_MODE\n");
+#endif
 
-       if (st & MST_DSK_CHG)
-       {
-               mcdDiskChanged = 1;
-       }
-       
-       if ((st & MST_READY) == 0)
-       {
-               printk("mcd: disk removed\n");
-               mcdDiskChanged = 1;             
-               end_request(0);
-               do_mcd_request();
-               return;
+    if ((st = mcdStatus()) != -1) {
+
+      if (st & MST_DSK_CHG) {
+       mcdDiskChanged = 1;
+       tocUpToDate = 0;
+       mcd_invalidate_buffers();
+      }
+
+    set_mode_immediatly:
+
+      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+       mcdDiskChanged = 1;
+       tocUpToDate = 0;
+       if (mcd_transfer_is_active) {
+         mcd_state = MCD_S_START;
+         goto immediatly;
        }
+       printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+       mcd_state = MCD_S_IDLE;
+       while (CURRENT_VALID)
+         end_request(0);
+       return;
+      }
 
-       outb(0x50, MCDPORT(0)); /* set mode */
-       outb(0x01, MCDPORT(0)); /* mode = cooked data */
-       McdTimeout = 100;
-       SET_TIMER(mcd_read_cmd, 1);
-}
+      outb(MCMD_SET_MODE, MCDPORT(0));
+      outb(1, MCDPORT(0));
+      mcd_mode = 1;
+      mcd_state = MCD_S_READ;
+      McdTimeout = 3000;
 
+    }
+    break;
 
-/*
- * Check the result of the set-mode command.  On success, send the
- * read-data command.
- */
 
-static void
-mcd_read_cmd()
-{
-       int st;
-       long block;
-       struct mcd_Play_msf mcdcmd;
 
-       McdTimeout--;
-       st = mcdStatus();
+  case MCD_S_READ:
+#ifdef TEST3
+    printk("MCD_S_READ\n");
+#endif
 
-       if (st & MST_DSK_CHG)
-       {
-               mcdDiskChanged = 1;
+    if ((st = mcdStatus()) != -1) {
+
+      if (st & MST_DSK_CHG) {
+       mcdDiskChanged = 1;
+       tocUpToDate = 0;
+       mcd_invalidate_buffers();
+      }
+
+    read_immediatly:
+
+      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+       mcdDiskChanged = 1;
+       tocUpToDate = 0;
+       if (mcd_transfer_is_active) {
+         mcd_state = MCD_S_START;
+         goto immediatly;
        }
-       
-       if (st == -1)
-       {
-               if (McdTimeout == 0)
-               {
-                       printk("mcd: set mode timed out\n");
-                       SET_TIMER(mcd_start, 1);        /* wait a bit, try again */
-                       return;
-               }
+       printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+       mcd_state = MCD_S_IDLE;
+       while (CURRENT_VALID)
+         end_request(0);
+       return;
+      }
+
+      if (CURRENT_VALID) {
+       struct mcd_Play_msf msf;
+       mcd_next_bn = CURRENT -> sector / 4;
+       hsg2msf(mcd_next_bn, &msf.start);
+       msf.end.min = ~0;
+       msf.end.sec = ~0;
+       msf.end.frame = ~0;
+       sendMcdCmd(MCMD_DATA_READ, &msf);
+       mcd_state = MCD_S_DATA;
+       McdTimeout = READ_TIMEOUT;
+      } else {
+       mcd_state = MCD_S_STOP;
+       goto immediatly;
+      }
+
+    }
+    break;
+
+
+  case MCD_S_DATA:
+#ifdef TEST3
+    printk("MCD_S_DATA\n");
+#endif
 
-               SET_TIMER(mcd_read_cmd, 1);
-               return;
+    st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
+  data_immediatly:
+#ifdef TEST5
+    printk("Status %02x\n",st);
+#endif
+    switch (st) {
+
+    case MFL_DATA:
+#ifdef WARN_IF_READ_FAILURE
+      if (McdTries == 5)
+       printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+      if (!McdTries--) {
+       printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+       if (mcd_transfer_is_active) {
+         McdTries = 0;
+         break;
+       }
+       if (CURRENT_VALID)
+         end_request(0);
+       McdTries = 5;
+      }
+      mcd_state = MCD_S_START;
+      McdTimeout = READ_TIMEOUT;
+      goto immediatly;
+
+    case MFL_STATUSorDATA:
+      break;
+
+    default:
+      McdTries = 5;
+      if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
+       mcd_state = MCD_S_STOP;
+       goto immediatly;
+      }
+      mcd_buf_bn[mcd_buf_in] = -1;
+      READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
+      mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
+      if (mcd_buf_out == -1)
+       mcd_buf_out = mcd_buf_in;
+      mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
+      if (!mcd_transfer_is_active) {
+       while (CURRENT_VALID) {
+         mcd_transfer();
+         if (CURRENT -> nr_sectors == 0)
+           end_request(1);
+         else
+           break;
        }
+      }
+
+      if (CURRENT_VALID
+         && (CURRENT -> sector / 4 < mcd_next_bn || 
+             CURRENT -> sector / 4 > mcd_next_bn + 16)) {
+       mcd_state = MCD_S_STOP;
+       goto immediatly;
+      }
+      McdTimeout = READ_TIMEOUT;
+#ifdef DOUBLE_QUICK_ONLY
+      if (MCMD_DATA_READ != MCMD_PLAY_READ)
+#endif
+      {
+       int count= QUICK_LOOP_COUNT;
+       while (count--) {
+          QUICK_LOOP_DELAY;
+         if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
+#   ifdef TEST4
+/*         printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
+           printk(" %d ",QUICK_LOOP_COUNT-count);
+#   endif
+           goto data_immediatly;
+         }
+       }
+#   ifdef TEST4
+/*      printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
+       printk("ended ");
+#   endif
+      }
+      break;
+    }
+    break;
 
-       mcd_bn = -1;                    /* purge our buffer */
-       block = CURRENT -> sector / 4;
-       hsg2msf(block, &mcdcmd.start);  /* cvt to msf format */
 
-       mcdcmd.end.min = 0;
-       mcdcmd.end.sec = 0;
-       mcdcmd.end.frame = 1;
 
-       sendMcdCmd(MCMD_PLAY_READ, &mcdcmd);    /* read command */
-       McdTimeout = 200;
-       SET_TIMER(mcd_data, 1);
-}
+  case MCD_S_STOP:
+#ifdef TEST3
+    printk("MCD_S_STOP\n");
+#endif
 
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+    if (!mitsumi_bug_93_wait)
+      goto do_not_work_around_mitsumi_bug_93_1;
+
+    McdTimeout = mitsumi_bug_93_wait;
+    mcd_state = 9+3+1;
+    break;
+
+  case 9+3+1:
+    if (McdTimeout)
+      break;
+
+  do_not_work_around_mitsumi_bug_93_1:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+    outb(MCMD_STOP, MCDPORT(0));
+
+#ifdef WORK_AROUND_MITSUMI_BUG_92
+    if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+      int i = 4096;
+      do {
+       inb(MCDPORT(0));
+      } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+      outb(MCMD_STOP, MCDPORT(0));
+      if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+       i = 4096;
+       do {
+         inb(MCDPORT(0));
+       } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+       outb(MCMD_STOP, MCDPORT(0));
+      }
+    }
+#endif /* WORK_AROUND_MITSUMI_BUG_92 */
+
+    mcd_state = MCD_S_STOPPING;
+    McdTimeout = 1000;
+    break;
+
+  case MCD_S_STOPPING:
+#ifdef TEST3
+    printk("MCD_S_STOPPING\n");
+#endif
 
-/*
- * Check the completion of the read-data command.  On success, read
- * the 2048 bytes of data from the disk into our buffer.
- */
+    if ((st = mcdStatus()) == -1 && McdTimeout)
+      break;
 
-static void
-mcd_data()
-{
-       int i;
+    if ((st != -1) && (st & MST_DSK_CHG)) {
+      mcdDiskChanged = 1;
+      tocUpToDate = 0;
+      mcd_invalidate_buffers();
+    }
 
-       McdTimeout--;
-       cli();
-       i =inb(MCDPORT(1)) & (MFL_STATUS | MFL_DATA);
-       if (i == MFL_DATA)
-       {
-               printk("mcd: read failed\n");
-#ifdef MCD_DEBUG
-               printk("got 0xB %02X\n", inb(MCDPORT(0)) & 0xFF);
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+    if (!mitsumi_bug_93_wait)
+      goto do_not_work_around_mitsumi_bug_93_2;
+
+    McdTimeout = mitsumi_bug_93_wait;
+    mcd_state = 9+3+2;
+    break;
+
+  case 9+3+2:
+    if (McdTimeout)
+      break;
+
+    st = -1;
+
+  do_not_work_around_mitsumi_bug_93_2:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+#ifdef TEST3
+    printk("CURRENT_VALID %d mcd_mode %d\n",
+          CURRENT_VALID, mcd_mode);
 #endif
-               SET_TIMER(mcd_start, 1);
-               sti();
-               return;
-       }
-       
-       if (i == (MFL_STATUS | MFL_DATA))
-       {
-               if (McdTimeout == 0)
-               {
-                       printk("mcd: data timeout, retrying\n");
-                       SET_TIMER(mcd_start, 1);
-               }
-               
-               else
-                       SET_TIMER(mcd_data, 1);
-               
-               sti();
-               return;
-       }
 
-       CLEAR_TIMER;
-       READ_DATA(MCDPORT(0), &mcd_buf[0], 2048);
-       sti();
+    if (CURRENT_VALID) {
+      if (st != -1) {
+       if (mcd_mode == 1)
+         goto read_immediatly;
+       else
+         goto set_mode_immediatly;
+      } else {
+       mcd_state = MCD_S_START;
+       McdTimeout = 1;
+      }
+    } else {
+      mcd_state = MCD_S_IDLE;
+      return;
+    }
+    break;
+
+  default:
+    printk("mcd: invalid state %d\n", mcd_state);
+    return;
+  }
+
+ ret:
+  if (!McdTimeout--) {
+    printk("mcd: timeout in state %d\n", mcd_state);
+    mcd_state = MCD_S_STOP;
+  }
+
+  SET_TIMER(mcd_poll, 1);
+}
+
 
-       mcd_bn = CURRENT -> sector / 4;
-       mcd_transfer();
-       end_request(1);
-       SET_TIMER(do_mcd_request, 1);
+
+static void
+mcd_invalidate_buffers(void)
+{
+  int i;
+  for (i = 0; i < MCD_BUF_SIZ; ++i)
+    mcd_buf_bn[i] = -1;
+  mcd_buf_out = -1;
 }
 
 
@@ -760,6 +1033,10 @@ mcd_open(struct inode *ip, struct file *fp)
        if (mcdPresent == 0)
                return -ENXIO;                  /* no hardware */
 
+       if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
+
+       mcd_invalidate_buffers();
+
        st = statusCmd();                       /* check drive status */
        if (st == -1)
                return -EIO;                    /* drive doesn't respond */
@@ -773,6 +1050,9 @@ mcd_open(struct inode *ip, struct file *fp)
        if (updateToc() < 0)
                return -EIO;
 
+       }
+       ++mcd_open_count;
+
        return 0;
 }
 
@@ -784,9 +1064,11 @@ mcd_open(struct inode *ip, struct file *fp)
 static void
 mcd_release(struct inode * inode, struct file * file)
 {
-       mcd_bn = -1;
+  if (!--mcd_open_count) {
+       mcd_invalidate_buffers();
        sync_dev(inode->i_rdev);
        invalidate_buffers(inode -> i_rdev);
+  }
 }
 
 
@@ -799,7 +1081,8 @@ static struct file_operations mcd_fops = {
        mcd_ioctl,              /* ioctl */
        NULL,                   /* mmap */
        mcd_open,               /* open */
-       mcd_release             /* release */
+       mcd_release,            /* release */
+       NULL                    /* fsync */
 };
 
 
@@ -825,15 +1108,22 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
        int count;
        unsigned char result[3];
 
+       if (mcd_port <= 0 || mcd_irq <= 0) {
+         printk("skip mcd_init\n");
+         return mem_start;
+       }
+
+       printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
+
        if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
        {
-               printk("mcd: Unable to get major %d for Mitsumi CD-ROM\n",
+               printk("Unable to get major %d for Mitsumi CD-ROM\n",
                       MAJOR_NR);
                return mem_start;
        }
 
         if (check_region(mcd_port, 4)) {
-         printk("mcd: Init failed, I/O port (%X) already in use\n",
+         printk("Init failed, I/O port (%X) already in use\n",
                 mcd_port);
          return mem_start;
        }
@@ -844,16 +1134,16 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
        /* check for card */
 
        outb(0, MCDPORT(1));                    /* send reset */
-       for (count = 0; count < 1000000; count++)
+       for (count = 0; count < 2000000; count++)
                (void) inb(MCDPORT(1));         /* delay a bit */
 
        outb(0x40, MCDPORT(0));                 /* send get-stat cmd */
-       for (count = 0; count < 1000000; count++)
+       for (count = 0; count < 2000000; count++)
                if (!(inb(MCDPORT(1)) & MFL_STATUS))
                        break;
 
-       if (count >= 1000000) {
-               printk("mcd: Init failed. No mcd device at 0x%x irq %d\n",
+       if (count >= 2000000) {
+               printk("Init failed. No mcd device at 0x%x irq %d\n",
                     mcd_port, mcd_irq);
                return mem_start;
        }
@@ -862,7 +1152,7 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
        outb(MCMD_GET_VERSION,MCDPORT(0));
        for(count=0;count<3;count++)
                if(getValue(result+count)) {
-                       printk("mcd: mitsumi get version failed at 0x%d\n",
+                       printk("mitsumi get version failed at 0x%d\n",
                               mcd_port);
                        return mem_start;
                }       
@@ -870,9 +1160,10 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
        if (result[0] == result[1] && result[1] == result[2])
                return mem_start;
 
-       printk("mcd: Mitsumi version : %02X %c %x\n",
+       printk("Mitsumi status, type and version : %02X %c %x\n",
               result[0],result[1],result[2]);
 
+       if (result[1] == 'D') MCMD_DATA_READ= 0xC1;
 
        mcdVersion=result[2];
 
@@ -883,13 +1174,24 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
 
        if (irqaction(mcd_irq,  &mcd_sigaction))
        {
-               printk("mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
+               printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
                return mem_start;
        }
        snarf_region(mcd_port, 4);
+
+       outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+       outb(0x02,MCDPORT(0));
+       outb(0x00,MCDPORT(0));
+       getValue(result);
+
+       outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+       outb(0x10,MCDPORT(0));
+       outb(0x04,MCDPORT(0));
+       getValue(result);
+
+       mcd_invalidate_buffers();
        mcdPresent = 1;
-       printk("mcd: Mitsumi CD-ROM Drive present at addr %x, irq %d\n",
-              mcd_port, mcd_irq);
+       printk("\n");
        return mem_start;
 }
 
@@ -1189,6 +1491,7 @@ GetToc()
        {
                outb(MCMD_SET_MODE, MCDPORT(0));
                outb(0x05, MCDPORT(0));                 /* mode: toc */
+               mcd_mode = 0x05;
                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
                        break;
        }
@@ -1219,6 +1522,7 @@ GetToc()
        {
                 outb(MCMD_SET_MODE, MCDPORT(0));
                 outb(0x01, MCDPORT(0));
+               mcd_mode = 1;
                 if (getMcdStatus(MCD_STATUS_DELAY) != -1)
                         break;
        }
index 0a13e7a586c0feec7bbd11a36b34fc1733612c6a..90d4f64723673666355be2e1608d89c8bf330156 100644 (file)
@@ -94,12 +94,7 @@ long rd_init(long mem_start, int length)
        return(length);
 }
 
-/*
- * If the root device is the RAM disk, try to load it.
- * In order to do this, the root device is originally set to the
- * floppy, and we later change it to be RAM disk.
- */
-void rd_load(void)
+static void do_load(void)
 {
        struct buffer_head *bh;
        struct minix_super_block s;
@@ -108,14 +103,6 @@ void rd_load(void)
        int             nblocks;
        char            *cp;
 
-       /* If no RAM disk specified, give up early. */
-       if (!rd_length) return;
-       printk("RAMDISK: %d bytes, starting at 0x%x\n",
-                                       rd_length, (int) rd_start);
-
-       /* If we are doing a diskette boot, we might have to pre-load it. */
-       if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
-
        /*
         * Check for a super block on the diskette.
         * The old-style boot/root diskettes had their RAM image
@@ -176,3 +163,32 @@ void rd_load(void)
                return;
        }
 }
+
+int floppy_grab_irq_and_dma(void);
+void floppy_release_irq_and_dma(void);
+
+/*
+ * If the root device is the RAM disk, try to load it.
+ * In order to do this, the root device is originally set to the
+ * floppy, and we later change it to be RAM disk.
+ */
+void rd_load(void)
+{
+       /* If no RAM disk specified, give up early. */
+       if (!rd_length)
+               return;
+       printk("RAMDISK: %d bytes, starting at 0x%x\n",
+                       rd_length, (int) rd_start);
+
+       /* If we are doing a diskette boot, we might have to pre-load it. */
+       if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
+               return;
+
+/* ugly, ugly */
+       if (floppy_grab_irq_and_dma()) {
+               printk("Unable to gram floppy IRQ/DMA for loading ramdisk image\n");
+               return;
+       }
+       do_load();
+       floppy_release_irq_and_dma();
+}
index 02eb824d32fefb6f58cd8a9bc4b11b1eb6cc958b..34022ff585ca068559da8a022dd9a49adc4d41a7 100644 (file)
@@ -2,16 +2,20 @@
  * Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
  *     By Craig Southeren
  *
- * 3c505.c     This module implements an interface to the 3Com
+ * elplus.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
+ * Version:    @(#)elplus.c    0.5     11-Jun-94
  *
  * Authors:    Linux 3c505 device driver by:
  *                     Craig Southeren, <geoffw@extro.ucc.su.oz.au>
+ *              Final debugging by:
+ *                     Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ *             Auto irq, auto detect, cleanup and v1.1.4+ kernel mods by:
+ *                     Juha Laiho, <jlaiho@ichaos.nullnet.fi>
  *              Linux 3C509 driver by:
  *                             Donald Becker, <becker@super.org>
  *             Crynwr packet driver by
  *                        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>
  *                     
  */
 
+
+/*********************************************************
+ *
+ *  set ELP_KERNEL_TYPE to the following values depending upon
+ *  the kernel type:
+ *       0   = 0.99pl14 or earlier
+ *       1   = 0.99pl15 through 1.1.3
+ *       2   = 1.1.4 through 1.1.11
+ *       3   = 1.1.12 through 1.1.19
+ *       4   = 1.1.20
+ *
+ *********************************************************/
+
+#define        ELP_KERNEL_TYPE 4
+
+/*********************************************************
+ *
+ *  set ELP_NEED_HARD_RESET to 1, if having problems with
+ *  "warm resets" from DOS. Bootup will then take some more
+ *  time, as the adapter will perform self-test upon hard
+ *  reset. This misbehaviour is reported to happen at least
+ *  after use of Windows real-mode NDIS drivers.
+ *
+ *********************************************************/
+
+#define ELP_NEED_HARD_RESET 0
+
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
 #include <asm/io.h>
-#ifndef port_read
-#include "iow.h"
-#endif
 
+#if (ELP_KERNEL_TYPE < 2)
+#include "dev.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+#else
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#endif
+
+#ifndef port_read
+#include "iow.h"
+#endif
 
 #include "3c505.h"
 
-#ifdef ELP_DEBUG
-static int elp_debug = ELP_DEBUG;
+#define ELPLUS_DEBUG 0
+
+/*********************************************************
+ *
+ *  define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static char * filename = __FILE__;
+
+static char * null_msg = "*** NULL at %s(%d) ***\n";
+#define CHECK_NULL(p) if (!p) printk(null_msg, filename, __LINE__)
+
+static char * timeout_msg = "*** timeout at %s(%d) ***\n";
+#define TIMEOUT_MSG()     printk(timeout_msg, filename,__LINE__)
+
+static char * invalid_pcb_msg = "*** invalid pcb length %d at %s(%d) ***\n";
+
+static char * search_msg = "%s: Looking for 3c505 adapter at address 0x%x...";
+
+static char * stilllooking_msg = "still looking...";
+
+static char * found_msg = "found.\n";
+
+static char * notfound_msg = "not found (reason = %d)\n";
+
+static char * couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ *  various other debug stuff
+ *
+ *********************************************************/
+
+#ifdef ELPLUS_DEBUG
+static int elp_debug = ELPLUS_DEBUG;
 #else
 static int elp_debug = 0;
 #endif
 
+#if (ELP_KERNEL_TYPE < 2)
+extern void    skb_check(struct sk_buff *skb,int, char *);
+#ifndef IS_SKB
+#define IS_SKB(skb)    skb_check((skb),__LINE__,filename)
+#endif
+#else
+#ifndef IS_SKB
+#define IS_SKB(skb)    skb_check((skb),0,__LINE__,filename)
+#endif
+#endif
+
+
 /*
  *  0 = no messages
  *  1 = messages when high level commands performed
@@ -58,9 +145,7 @@ static int elp_debug = 0;
  *  3 = messages when interrupts received
  */
 
-#define        ELP_VERSION     "0.1.0"
-
-extern struct device *irq2dev_map[16];
+#define        ELPLUS_VERSION  "0.4.0"
 
 /*****************************************************************
  *
@@ -68,8 +153,27 @@ extern struct device *irq2dev_map[16];
  *
  *****************************************************************/
 
-#define        INB(port)       inb((unsigned short)port) 
-#define        OUTB(val,port)  outb((unsigned char)val,(unsigned short)port);
+/*
+ *  kernels before pl15 used an unobvious method for accessing
+ *  the skb data area
+ */
+#if (ELP_KERNEL_TYPE < 1)
+#define        SKB_DATA        (skb+1)
+#else
+#define        SKB_DATA        (skb->data) 
+#endif
+
+/*
+ *  not all kernels before 1.1.4 had an alloc_skb function (apparently!!)
+ */
+#if (ELP_KERNEL_TYPE < 2)
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#endif
+#endif
+
+#define        INB(port)       inb((unsigned short)(port)) 
+#define        OUTB(val,port)  outb((unsigned char)(val),(unsigned short)(port));
 
 #ifndef        TRUE
 #define        TRUE    1
@@ -80,18 +184,21 @@ extern struct device *irq2dev_map[16];
 #endif
 
 
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+const int addr_list[]={0x300,0x280,0x310,0}; 
+
 /*****************************************************************
  *
  * 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;
-
+#include "3c505dta.h"
 
 /*****************************************************************
  *
@@ -101,7 +208,8 @@ typedef struct {
 
 typedef struct {
   int        io_addr;       /* base I/O address */
-  short      got_configure;  /* set to TRUE when configure response received */
+  char *     name;           /* used for debug output */
+  short      got[NUM_TRANSMIT_CMDS];  /* flags for command completion */
   pcb_struct tx_pcb;         /* PCB for foreground sending */
   pcb_struct rx_pcb;         /* PCB for foreground receiving */
   pcb_struct itx_pcb;        /* PCB for background sending */
@@ -127,10 +235,13 @@ typedef struct {
 static int get_status (elp_device * adapter)
 
 {
+  int timeout = jiffies + TIMEOUT;  
   register int stat1;
   do {
     stat1 = INB(adapter->io_addr+PORT_STATUS);
-  } while (stat1 != INB(adapter->io_addr+PORT_STATUS));
+  } while (stat1 != INB(adapter->io_addr+PORT_STATUS) && jiffies < timeout);
+  if (jiffies >= timeout)
+    TIMEOUT_MSG();
   return stat1;
 }
 
@@ -153,8 +264,8 @@ static int wait_hcre(elp_device * adapter, int toval)
   while(((INB(adapter->io_addr+PORT_STATUS)&STATUS_HCRE)==0) &&
          (jiffies <= timeout))
     ;
-  if (jiffies > timeout) {
-    printk("elp0: timeout waiting for HCRE\n");
+  if (jiffies >= timeout) {
+    TIMEOUT_MSG();
     return FALSE;
   }
   return TRUE;
@@ -183,6 +294,12 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
   int retry = 0;
   int timeout;
 
+  CHECK_NULL(pcb);
+  CHECK_NULL(adapter);
+
+  if (pcb->length > MAX_PCB_DATA)
+    printk(invalid_pcb_msg, pcb->length, filename, __LINE__);
+
   while (1) {
 
     cont = 1;
@@ -195,7 +312,6 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
     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);
@@ -203,7 +319,7 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
     }
 
     for (i = 0; cont && (i < pcb->length); i++) {
-      OUTB(pcb->data[i], (adapter->io_addr)+PORT_COMMAND);
+      OUTB(pcb->data.raw[i], (adapter->io_addr)+PORT_COMMAND);
       cont = WAIT_HCRE(2);
     }
 
@@ -220,12 +336,16 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
             (i == ASF_PCB_NAK))
           break;
       }
+
+      if (jiffies >= timeout)
+        TIMEOUT_MSG();
+
       if (i == ASF_PCB_ACK) {
         SET_HSF(0); 
         return TRUE;
-      } else if (i = ASF_PCB_NAK) {
+      } else if (i == ASF_PCB_NAK) {
         SET_HSF(0);
-        printk("elp0: PCB send was NAKed\n");
+        printk("%s: PCB send was NAKed\n", adapter->name);
         {
           int to = jiffies + 5;
           while (jiffies < to)
@@ -234,11 +354,13 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
       }
     }
 
-    if (elp_debug >= 6)
-      printk("elp0: NAK/timeout on send PCB\n");
+    if (elp_debug >= 2)
+      printk("%s: NAK/timeout on send PCB\n", adapter->name);
     if ((retry++ & 7) == 0) 
-      printk("elp0: retry #%i on send PCB\n", retry);
+      printk("%s: retry #%i on send PCB\n", adapter->name, retry);
   }
+
+  return FALSE;
 }
 
 /*****************************************************************
@@ -260,38 +382,60 @@ static int receive_pcb(elp_device * adapter, pcb_struct * pcb)
   int i;
   int total_length;
   int stat;
+  int timeout;
+
+  CHECK_NULL(pcb);
+  CHECK_NULL(adapter);
 
   /* get the command code */
-  while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+  timeout = jiffies + TIMEOUT;
+  while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
     ;
+  if (jiffies >= timeout)
+    TIMEOUT_MSG();
+
   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)
+    timeout = jiffies + TIMEOUT;
+    while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
       ;
+    if (jiffies >= timeout)
+      TIMEOUT_MSG();
     pcb->length = INB(adapter->io_addr+PORT_COMMAND);
+
+    if (pcb->length > MAX_PCB_DATA)
+      printk(invalid_pcb_msg, pcb->length, filename,__LINE__);
+
     if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
 
       /* read the data */
       i = 0;
+      timeout = jiffies + TIMEOUT;
       do {
-        while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+        while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
           ;
-        pcb->data[i++] = INB(adapter->io_addr+PORT_COMMAND);
-      } while ((stat & ASF_PCB_MASK) != ASF_PCB_END);
-
+        pcb->data.raw[i++] = INB(adapter->io_addr+PORT_COMMAND);
+       if (i > MAX_PCB_DATA)
+         printk(invalid_pcb_msg, i, filename, __LINE__);
+      } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && jiffies < timeout);
+
+      if (jiffies >= timeout)
+        TIMEOUT_MSG();
+      
       /* woops, the last "data" byte was really the length! */
-      total_length = pcb->data[--i];
+      total_length = pcb->data.raw[--i];
 
       /* safety check total length vs data length */
       if (total_length != (pcb->length + 2)) {
-        if (elp_debug >= 6)
-          printk("elp0: mangled PCB received\n");
+        if (elp_debug >= 2)
+          printk("%s: mangled PCB received\n", adapter->name);
         SET_HSF(HSF_PCB_NAK);
         return FALSE;
       }
+
       SET_HSF(HSF_PCB_ACK);
       return TRUE;
     }
@@ -301,11 +445,15 @@ static int receive_pcb(elp_device * adapter, pcb_struct * pcb)
   return FALSE;
 }
 
+#if ELP_NEED_HARD_RESET
+
 static void adapter_hard_reset(elp_device * adapter)
 
 {
   int timeout;
 
+  CHECK_NULL(adapter);
+
   /*
    * take FLSH and ATTN high
    */
@@ -336,11 +484,14 @@ static void adapter_hard_reset(elp_device * adapter)
       break;
 }
 
-static void adapter_reset(elp_device * adapter)
+#endif /* ELP_NEED_HARD_RESET */
 
+static void adapter_reset(elp_device * adapter)
 {
   int timeout;
 
+  CHECK_NULL(adapter);
+
   cli();
   OUTB(CONTROL_ATTN|INB(adapter->io_addr+PORT_CONTROL), adapter->io_addr+PORT_CONTROL);
   sti();
@@ -350,10 +501,11 @@ static void adapter_reset(elp_device * adapter)
    */
   for (timeout = jiffies + 20; jiffies <= timeout; )
     ;
-  
+
   cli();
   OUTB(INB(adapter->io_addr+PORT_CONTROL)&~(CONTROL_ATTN), adapter->io_addr+PORT_CONTROL);
   sti();
+
 }
 
 /******************************************************
@@ -366,14 +518,16 @@ static void adapter_reset(elp_device * adapter)
 static int start_receive(elp_device * adapter, pcb_struct * tx_pcb)
 
 {
-  if (elp_debug > 3)
-    printk("elp0: restarting receiver\n");
+  CHECK_NULL(adapter);
+  CHECK_NULL(tx_pcb);
+
+  if (elp_debug >= 3)
+    printk("%s: restarting receiver\n", adapter->name);
   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;
+  tx_pcb->length = sizeof(struct Rcv_pkt);
+  tx_pcb->data.rcv_pkt.buf_seg = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+  tx_pcb->data.rcv_pkt.buf_len = 1600;
+  tx_pcb->data.rcv_pkt.timeout = 0;    /* set timeout to zero */
   return send_pcb(adapter, tx_pcb); 
 }
 
@@ -394,12 +548,32 @@ static void receive_packet(struct device * dev,
   register int i;
   unsigned short * ptr;
   short d;
+  int timeout;
+  int rlen;
+  struct sk_buff *skb;
 
   /*
    * allocate a buffer to put the packet into.
+   * (for kernels prior to 1.1.4 only)
    */
-  struct sk_buff *skb;
-  skb = alloc_skb(len+3, GFP_ATOMIC);
+#if (ELP_KERNEL_TYPE < 2)
+  int sksize = sizeof(struct sk_buff) + len + 4;
+#endif
+
+  CHECK_NULL(dev);
+  CHECK_NULL(adapter);
+
+  if (len <= 0 || ((len & ~1) != len))
+    if (elp_debug >= 3)
+      printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+
+  rlen = (len+1) & ~1;
+
+#if (ELP_KERNEL_TYPE < 2)
+  skb = alloc_skb(sksize, GFP_ATOMIC);
+#else
+  skb = alloc_skb(rlen, GFP_ATOMIC);
+#endif
 
   /*
    * make sure the data register is going the right way
@@ -410,23 +584,52 @@ static void receive_packet(struct device * dev,
    * 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)
+    for (i = 0; i < (rlen/2); i++) {
+      timeout = jiffies + TIMEOUT;
+      while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 && 
+            jiffies < timeout)
         ;
+      if (jiffies >= timeout)
+        TIMEOUT_MSG();
+
       d = inw(adapter->io_addr+PORT_DATA);
     }
     adapter->stats.rx_dropped++;
 
   } else {
+    skb->lock     = 0;
+    skb->len = rlen;
+    skb->dev = dev;
+
+/*
+ * for kernels before 1.1.4, the driver allocated the buffer
+ */
+#if (ELP_KERNEL_TYPE < 2)
+    skb->mem_len = sksize;
+    skb->mem_addr = skb;
+#endif
 
     /*
      * 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 = (unsigned short *)SKB_DATA;
+    for (i = 0; i < (rlen/2); i++) { 
+      timeout = jiffies + TIMEOUT;
+      while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 && 
+            jiffies < timeout) 
+       ;
+      if (jiffies >= timeout)
+       {
+         printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+                        filename,__LINE__, i, rlen/2); 
+#if (ELP_KERNEL_TYPE < 2)
+         kfree_s(skb, sksize);
+#else
+         kfree_s(skb, rlen);
+#endif
+         return;
+       }
+
       *ptr = inw(adapter->io_addr+PORT_DATA); 
       ptr++; 
     }
@@ -436,10 +639,21 @@ static void receive_packet(struct device * dev,
      * 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) {
+
+    IS_SKB(skb);
+
+/*
+ * for kernels before 1.1.4, the driver allocated the buffer, so it had
+ * to free it
+ */
+#if (ELP_KERNEL_TYPE < 2)
+    if (dev_rint((unsigned char *)skb, rlen, IN_SKBUFF, dev) != 0) {
       printk("%s: receive buffers full.\n", dev->name);
-      kfree_skb(skb, FREE_READ);
+      kfree_s(skb, sksize);
     }
+#else
+    netif_rx(skb);
+#endif
   }
 
   OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL); 
@@ -460,27 +674,34 @@ static void elp_interrupt(int reg_ptr)
   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
   struct device *dev;
   elp_device * adapter;
+  int timeout;
 
   if (irq < 0 || irq > 15) {
-    printk ("elp0: illegal IRQ number found in interrupt routine (%i)\n", irq);
+    printk ("elp_interrupt(): 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);
+/* FIXME: How do I do this kind of check without a fixed IRQ? */
+#if 0
+  if (irq != ELP_IRQ) {
+    printk ("elp_interrupt(): - interrupt routine has incorrect IRQ of %i\n", irq);
     return;
   }
+#endif
 
   dev = irq2dev_map[irq];
-  adapter = (elp_device *) dev->priv;
 
   if (dev == NULL) {
     printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
     return;
   }
 
+  adapter = (elp_device *) dev->priv;
+
+  CHECK_NULL(adapter);
+
   if (dev->interrupt)
-    if (elp_debug >= 3)
+    if (elp_debug >= 2)
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
   dev->interrupt = 1;
 
@@ -492,7 +713,9 @@ static void elp_interrupt(int reg_ptr)
   /*
    * receive a PCB from the adapter
    */
-  while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0) {
+  timeout = jiffies + TIMEOUT;
+  while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0 &&
+        jiffies < timeout) {
     
     if (receive_pcb(adapter, &adapter->irx_pcb)) {
 
@@ -502,18 +725,66 @@ static void elp_interrupt(int reg_ptr)
          * 82586 configured correctly
          */
         case CMD_CONFIGURE_82586_RESPONSE:
-          adapter->got_configure = 1;
+          adapter->got[CMD_CONFIGURE_82586] = 1;
           if (elp_debug >= 3)
             printk("%s: interrupt - configure response received\n", dev->name);
           break;
 
+       /*
+        * Adapter memory configuration
+        */
+       case CMD_CONFIGURE_ADAPTER_RESPONSE:
+         adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+         if (elp_debug >= 3)
+           printk("%s: Adapter memory configuration %s.",dev->name,
+                  adapter->irx_pcb.data.failed?"failed":"succeeded");
+         break;
+       
+       /*
+        * Multicast list loading
+        */
+       case CMD_LOAD_MULTICAST_RESPONSE:
+         adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+          if (elp_debug >= 3)
+           printk("%s: Multicast address list loading %s.",dev->name,
+                  adapter->irx_pcb.data.failed?"failed":"succeeded");
+         break;
+
+       /*
+        * Station address setting
+        */
+       case CMD_SET_ADDRESS_RESPONSE:
+         adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+          if (elp_debug >= 3)
+            printk("%s: Ethernet address setting %s.",dev->name,
+                   adapter->irx_pcb.data.failed?"failed":"succeeded");
+          break;
+
+
+        /*
+         * received board statistics
+         */
+        case CMD_NETWORK_STATISTICS_RESPONSE:
+          adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+          adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+          adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+          adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+          adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+          adapter->got[CMD_NETWORK_STATISTICS] = 1;
+          if (elp_debug >= 3)
+            printk("%s: interrupt - statistics 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) {
+          /* if the device isn't open, don't pass packets up the stack */
+          if (dev->start == 0)
+            break;
+          len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+          dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+          if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
             printk("%s: interrupt - packet not received correctly\n", dev->name);
           } else {
             if (elp_debug >= 3)
@@ -521,7 +792,6 @@ static void elp_interrupt(int reg_ptr)
             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)
@@ -537,13 +807,18 @@ static void elp_interrupt(int reg_ptr)
         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 (dev->start == 0)
+            break;
+          if (adapter->irx_pcb.data.xmit_resp.c_stat != 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));
+              printk("%s: interrupt - error sending packet %4.4x\n",
+                dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
           dev->tbusy = 0;
+#if (ELP_KERNEL_TYPE < 3)
           mark_bh(INET_BH);
-          adapter->stats.tx_packets++;
+#else
+          mark_bh(NET_BH);
+#endif
           break;
 
         /*
@@ -556,6 +831,8 @@ static void elp_interrupt(int reg_ptr)
     } else 
       printk("%s: failed to read PCB on interrupt\n", dev->name);
   }
+  if (jiffies >= timeout)
+    TIMEOUT_MSG();
 
   /*
    * indicate no longer in interrupt routine
@@ -575,7 +852,9 @@ static int elp_open (struct device *dev)
 {
   elp_device * adapter = (elp_device *) dev->priv;
 
-  if (elp_debug >= 1)
+  CHECK_NULL(dev);
+
+  if (elp_debug >= 3)  
     printk("%s: request to open device\n", dev->name);
 
   /*
@@ -586,6 +865,16 @@ static int elp_open (struct device *dev)
     return -EAGAIN;
   }
 
+  /*
+   * disable interrupts on the board
+   */
+  OUTB(0x00, adapter->io_addr+PORT_CONTROL);
+
+  /*
+   * clear any pending interrupts
+   */
+  INB(adapter->io_addr+PORT_COMMAND);
+
   /*
    * interrupt routine not entered
    */
@@ -617,75 +906,61 @@ static int elp_open (struct device *dev)
    */
   dev->start = 1;
 
+  /*
+   * configure adapter memory: we need 10 multicast addresses, default==0
+   */
+  if (elp_debug >= 3)
+    printk("%s: sending 3c505 memory configuration command\n", dev->name);
+  adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+  adapter->tx_pcb.data.memconf.cmd_q = 10;
+  adapter->tx_pcb.data.memconf.rcv_q = 20;
+  adapter->tx_pcb.data.memconf.mcast = 10;
+  adapter->tx_pcb.data.memconf.frame = 20;
+  adapter->tx_pcb.data.memconf.rcv_b = 20;
+  adapter->tx_pcb.data.memconf.progs = 0;
+  adapter->tx_pcb.length = sizeof(struct Memconf);
+  adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+  if (!send_pcb(adapter, &adapter->tx_pcb))
+    printk("%s: couldn't send memory configuration command\n", dev->name);
+  else {
+    int timeout = jiffies + TIMEOUT;
+    while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+      ;
+    if (jiffies >= timeout)
+      TIMEOUT_MSG();
+  }
+
+
   /*
    * configure adapter to receive broadcast messages and wait for response
    */
-  if (elp_debug >= 2)
+  if (elp_debug >= 3)
     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.data.configure = NO_LOOPBACK | RECV_BROAD;
   adapter->tx_pcb.length  = 2;
-  adapter->got_configure  = 0;
+  adapter->got[CMD_CONFIGURE_82586] = 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)
+  else {
+    int timeout = jiffies + TIMEOUT;
+    while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
       ;
+    if (jiffies >= timeout)
+      TIMEOUT_MSG();
+  }
 
   /*
-   * queue a receive command to start things rolling
+   * queue receive commands to provide buffering
    */
   if (!start_receive(adapter, &adapter->tx_pcb))
     printk("%s: start receive command failed \n", dev->name);
-  if (elp_debug >= 2)
+  if (elp_debug >= 3)
     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;
-}
-
 
 /******************************************************
  *
@@ -703,14 +978,20 @@ static int send_packet (elp_device * adapter, unsigned char * ptr, int len)
    */
   unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
 
+  CHECK_NULL(adapter);
+  CHECK_NULL(ptr);
+
+  if (nlen < len)
+    printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
   /*
    * 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;
+  adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+  adapter->tx_pcb.data.xmit_pkt.buf_ofs = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+  adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
   if (!send_pcb(adapter, &adapter->tx_pcb)) 
     return FALSE;
 
@@ -725,8 +1006,15 @@ static int send_packet (elp_device * adapter, unsigned char * ptr, int len)
    * write data to the adapter
    */
   for (i = 0; i < (nlen/2);i++) {
-    while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0)
+    int timeout = jiffies + TIMEOUT;
+    while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 && jiffies < timeout)
       ;
+    if (jiffies >= timeout) {
+      printk("*** timeout at %s(%d) writing word %d of %d ***\n",
+            filename,__LINE__, i, nlen/2);
+      return FALSE;
+    }
+
     outw(*(short *)ptr, adapter->io_addr+PORT_DATA);
     ptr +=2;
   }
@@ -746,29 +1034,45 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
 {
   elp_device * adapter = (elp_device *) dev->priv;
 
+  CHECK_NULL(dev);
+
   /*
-   * not sure what this does, but the 3c609 driver does it, so...
+   * not sure what this does, but the 3c509 driver does it, so...
    */
   if (skb == NULL) {
     dev_tint(dev);
     return 0;
   }
 
+  /*
+   * Fill in the ethernet header 
+   * (for kernels prior to 1.1.4 only)
+   */
+#if (ELP_KERNEL_TYPE < 2)
+  IS_SKB(skb);
+  if (!skb->arp && dev->rebuild_header(SKB_DATA, dev)) {
+    skb->dev = dev;
+    IS_SKB(skb);
+    arp_queue (skb);
+    return 0;
+  }
+#endif
+
   /*
    * 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 (elp_debug >= 3)
+    printk("%s: request to send packet of length %d\n", dev->name, (int)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)
+    if (tickssofar < 200) /* was 500, AJT */
       return 1;
     printk("%s: transmit timed out, resetting adapter\n", dev->name);
     if ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0) 
@@ -779,15 +1083,15 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
   }
 
   /*
-   * send the packet at (void *)(skb+1) for skb->len
+   * send the packet at skb->data for skb->len
    */
-  if (!send_packet(adapter, (unsigned char *)(skb->data), 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);
+  if (elp_debug >= 3)
+    printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
 
 
   /*
@@ -803,7 +1107,15 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
   /*
    * if we have been asked to free the buffer, do so
    */
+#if (ELP_KERNEL_TYPE < 4)
+  if (skb->free)
+    {
+      IS_SKB(skb);
+      kfree_skb(skb, FREE_WRITE);
+    }
+#else
   dev_kfree_skb(skb, FREE_WRITE);
+#endif
 
   return 0;
 }
@@ -817,30 +1129,174 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
 static struct enet_statistics * elp_get_stats(struct device *dev)
 
 {
-  if (elp_debug >= 1)
+  elp_device *adapter = (elp_device *) dev->priv;
+
+  if (elp_debug >= 3)
     printk("%s: request for stats\n", dev->name);
 
-  elp_device * adapter = (elp_device *) dev->priv;
+  /* If the device is closed, just return the latest stats we have,
+     - we cannot ask from the adapter without interrupts */
+  if (!dev->start)
+    return &adapter->stats;
+
+  /* send a get statistics command to the board */
+  adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+  adapter->tx_pcb.length  = 0;
+  adapter->got[CMD_NETWORK_STATISTICS] = 0;
+  if (!send_pcb(adapter, &adapter->tx_pcb))
+    printk("%s: couldn't send get statistics command\n", dev->name);
+  else
+    {
+      int timeout = jiffies + TIMEOUT;
+      while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+       ;
+      if (jiffies >= timeout) {
+        TIMEOUT_MSG();
+        return &adapter->stats;
+      }
+    }
+
+  /* statistics are now up to date */
   return &adapter->stats;
 }
 
 /******************************************************
  *
- * initialise Etherlink Pus board
+ * close the board
  *
  ******************************************************/
 
-static void elp_init(struct device *dev)
+static int elp_close (struct device *dev)
 
 {
+  elp_device * adapter = (elp_device *) dev->priv;
+
+  CHECK_NULL(dev);
+  CHECK_NULL(adapter);
+
+  if (elp_debug >= 3)
+    printk("%s: request to close device\n", dev->name);
+
+  /* Someone may request the device statistic information even when
+   * the interface is closed. The following will update the statistics
+   * structure in the driver, so we'll be able to give current statistics.
+   */
+  (void) elp_get_stats(dev);
+
+  /*
+   * 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;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void elp_set_mc_list(struct device *dev, int num_addrs, void *addrs)
+{
+  elp_device *adapter = (elp_device *) dev->priv;
   int i;
+
+  if (elp_debug >= 3)
+    printk("%s: request to set multicast list\n", dev->name);
+
+  if (num_addrs != -1) {
+    /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+    /* if num_addrs==0 the list will be cleared */
+    adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+    adapter->tx_pcb.length  = 6*num_addrs;
+    for (i=0;i<num_addrs;i++)
+      memcpy(adapter->tx_pcb.data.multicast[i], addrs+6*i,6);
+    adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+    if (!send_pcb(adapter, &adapter->tx_pcb))
+      printk("%s: couldn't send set_multicast command\n", dev->name);
+    else {
+      int timeout = jiffies + TIMEOUT;
+      while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+        ;
+      if (jiffies >= timeout) {
+        TIMEOUT_MSG();
+      }
+    }
+    if (num_addrs)
+      adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+    else /* num_addrs == 0 */
+      adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+  } else /* num_addrs == -1 */
+    adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_ALL;
+  /*
+   * configure adapter to receive messages (as specified above)
+   * and wait for response
+   */
+  if (elp_debug >= 3)
+    printk("%s: sending 82586 configure command\n", dev->name);
+  adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+  adapter->tx_pcb.length  = 2;
+  adapter->got[CMD_CONFIGURE_82586]  = 0;
+  if (!send_pcb(adapter, &adapter->tx_pcb))
+    printk("%s: couldn't send 82586 configure command\n", dev->name);
+  else {
+    int timeout = jiffies + TIMEOUT;
+    while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+      ;
+    if (jiffies >= timeout)
+      TIMEOUT_MSG();
+  }
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static void elp_init(struct device *dev)
+
+{
   elp_device * adapter;
 
+  CHECK_NULL(dev);
+
   /*
    * NULL out buffer pointers
+   * (kernels prior to 1.1.4 only)
    */
-  for (i = 0; i < DEV_NUMBUFFS; i++)
-    dev->buffs[i] = NULL;
+#if (ELP_KERNEL_TYPE < 2)
+  {
+    int i;
+    for (i = 0; i < DEV_NUMBUFFS; i++)
+      dev->buffs[i] = NULL;
+  }
+#endif
 
   /*
    * set ptrs to various functions
@@ -849,31 +1305,43 @@ static void elp_init(struct device *dev)
   dev->stop             = elp_close;           /* local */
   dev->get_stats       = elp_get_stats;        /* local */
   dev->hard_start_xmit  = elp_start_xmit;       /* local */
+  dev->set_multicast_list = elp_set_mc_list;   /* local */
 
+#if (ELP_KERNEL_TYPE < 2)
   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 */
+#else
+  /* Setup the generic properties */
+  ether_setup(dev);
+#endif
 
   /*
    * setup ptr to adapter specific information
    */
   adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+  CHECK_NULL(adapter);
   adapter->io_addr = dev->base_addr;
+  adapter->name    = dev->name;
   memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
 
 
   /*
    * Ethernet information
+   * (for kernels prior to 1.1.4 only)
    */
+#if (ELP_KERNEL_TYPE < 2)
   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;
+  {
+    int i;
+    for (i = 0; i < dev->addr_len; i++) 
+      dev->broadcast[i] = 0xff;
+  }
 
   /*
    * New-style flags. 
@@ -884,41 +1352,139 @@ static void elp_init(struct device *dev)
   dev->pa_brdaddr      = 0;
   dev->pa_mask         = 0;
   dev->pa_alen         = sizeof(unsigned long);
+#endif
 
   /*
    * memory information
    */
   dev->mem_start = dev->mem_end = dev->rmem_end = dev->mem_start = 0;
+
+#if ELP_NEED_HARD_RESET
+  adapter_hard_reset(adapter);
+#else
+  adapter_reset(adapter);
+#endif
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int elp_sense(int addr)
+{
+  int timeout;
+  byte orig_HCR=INB(addr+PORT_CONTROL),
+       orig_HSR=INB(addr+PORT_STATUS);
+  
+  if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+       ( (orig_HCR & CONTROL_DIR) != (orig_HSR & STATUS_DIR) ) )
+    return 1;                /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+
+  /* Wait for a while; the adapter may still be booting up */
+  if (elp_debug > 0)
+    printk(stilllooking_msg);
+  for (timeout = jiffies + (100 * 15); jiffies <= timeout; ) 
+    if ((INB(addr+PORT_STATUS) & ASF_PCB_MASK) != ASF_PCB_END)
+      break;
+
+  if (orig_HCR & CONTROL_DIR) {
+    /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+    OUTB(orig_HCR & ~CONTROL_DIR,addr+PORT_CONTROL);
+    timeout = jiffies+30;
+    while (jiffies < timeout)
+      ;
+    if (INB(addr+PORT_STATUS) & STATUS_DIR) {
+      OUTB(orig_HCR,addr+PORT_CONTROL);
+      return 2;
+    }
+  } else {
+    /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+    OUTB(orig_HCR | CONTROL_DIR,addr+PORT_CONTROL);
+    timeout = jiffies+300;
+    while (jiffies < timeout)
+      ;
+    if (!(INB(addr+PORT_STATUS) & STATUS_DIR)) {
+      OUTB(orig_HCR,addr+PORT_CONTROL);
+      return 3;
+    }
+  }
+  return 0;    /* It certainly looks like a 3c505. */
 }
 
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int elp_autodetect(struct device * dev)
+{
+  int idx=0, addr;
+
+  /* if base address set, then only check that address
+     otherwise, run through the table */
+  if ( (addr=dev->base_addr) ) { /* dev->base_addr == 0 ==> plain autodetect */
+    if (elp_debug > 0)
+      printk(search_msg, dev->name, addr);
+    if (elp_sense(addr) == 0)
+    {
+      if (elp_debug > 0)
+        printk(found_msg);
+      return addr;
+    } else if (elp_debug > 0)
+      printk(notfound_msg);
+  } else while ( (addr=addr_list[idx++]) ) {
+    if (elp_debug > 0)
+      printk(search_msg, dev->name, addr);
+    if (elp_sense(addr) == 0) {
+      if (elp_debug > 0)
+        printk(found_msg);
+      return addr;
+    } else if (elp_debug > 0)
+      printk(notfound_msg);
+  }
+
+  /* could not find an adapter */
+  if (elp_debug == 0)
+    printk(couldnot_msg, dev->name);
+  return 0; /* Because of this, the layer above will return -ENODEV */
+}
 
 /******************************************************
  *
  * probe for an Etherlink Plus board at the specified address
- * by attempting to get the ethernet address. 
  *
  ******************************************************/
 
-int elp_probe(struct device *dev)
+int elplus_probe(struct device *dev)
 
 {
   elp_device adapter;
   int            i;
 
+  CHECK_NULL(dev);
+
   /*
    *  setup adapter structure
    */
-  adapter.io_addr = dev->base_addr;
 
-  printk ("%s: probing for 3c505...", dev->name);
+  adapter.io_addr = dev->base_addr = elp_autodetect(dev);
+  if ( !adapter.io_addr )
+    return -ENODEV;
 
   /*
-   * get the adapter's undivided attention (if it's there!)
+   *  As we enter here from bootup, the adapter should have IRQs enabled,
+   *  but we can as well enable them anyway.
    */
-  adapter_hard_reset(&adapter); 
+  OUTB(INB(dev->base_addr+PORT_CONTROL) | CONTROL_CMDE,
+       dev->base_addr+PORT_CONTROL);
+  autoirq_setup(0);
 
   /*
    * use ethernet address command to probe for board in polled mode
+   * (this also makes us the IRQ that we need for automatic detection)
    */
   adapter.tx_pcb.command = CMD_STATION_ADDRESS;
   adapter.tx_pcb.length  = 0;
@@ -926,16 +1492,59 @@ int elp_probe(struct device *dev)
       !receive_pcb(&adapter, &adapter.rx_pcb) ||
       (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
       (adapter.rx_pcb.length != 6)) {
-    printk("not found\n");
+    printk("%s: not responding to first PCB\n", dev->name);
     return -ENODEV;
   }
-
+  if (dev->irq) { /* Is there a preset IRQ? */
+     if (dev->irq != autoirq_report(0)) {
+        printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+       return -ENODEV;
+     }
+     /* if dev->irq == autoirq_report(0), all is well */
+  } else /* No preset IRQ; just use what we can detect */
+     dev->irq=autoirq_report(0);
+  switch (dev->irq) { /* Legal, sane? */
+    case 0: printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+            printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+           return -ENODEV;
+    case 1:
+    case 6:
+    case 8:
+    case 13: 
+            printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+                  dev->name,
+                  dev->irq);
+           return -ENODEV;
+  }
+  /*
+   *  Now we have the IRQ number so we can disable the interrupts from
+   *  the board until the board is opened.
+   */
+  OUTB(INB(dev->base_addr+PORT_CONTROL) & ~CONTROL_CMDE,
+       dev->base_addr+PORT_CONTROL);
+  
+  /*
+   * copy ethernet address into structure
+   */
   for (i = 0; i < 6; i++) 
-    dev->dev_addr[i] = adapter.rx_pcb.data[i];
+    dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
 
-  printk("found at port 0x%x, address = %s\n", dev->base_addr, eth_print(dev->dev_addr));
+  /*
+   * print remainder of startup message
+   */
+#if (ELP_KERNEL_TYPE < 2)
+  printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %s\n",
+         dev->name, dev->base_addr, dev->irq, eth_print(dev->dev_addr));
+#else
+  printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+         dev->name, dev->base_addr, dev->irq,
+         dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+         dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+#endif
   
+  /*
+   * initialise the device
+   */
   elp_init(dev);
   return 0;
 }
-
index 727f03b88a95167803b15e16f199db0ec4a1c584..ecb5f902aa20f52900f9a2af433ef1a78f89ded9 100644 (file)
@@ -72,7 +72,7 @@
  *
  *****************************************************************/
 
-#define        TIMEOUT 10000
+#define        TIMEOUT 300
 
 /*****************************************************************
  *
@@ -101,6 +101,7 @@ enum {
   CMD_SELF_TEST                        = 0x0f,
   CMD_SET_STATION_ADDRESS      = 0x10,
   CMD_ADAPTER_INFO             = 0x11,
+  NUM_TRANSMIT_CMDS,
 
   /*
    * adapter PCB commands
diff --git a/drivers/net/3c505dta.h b/drivers/net/3c505dta.h
new file mode 100644 (file)
index 0000000..5c7004f
--- /dev/null
@@ -0,0 +1,119 @@
+/* This header file defines some data structures used by the 3c505 driver */
+
+/* Data units */
+typedef unsigned char         byte;
+typedef unsigned short int    word;
+typedef unsigned long int     dword;
+
+
+/* Data structures */
+struct Memconf {
+       word    cmd_q,
+               rcv_q,
+               mcast,
+               frame,
+               rcv_b,
+               progs;
+};
+
+struct Rcv_pkt {
+       word    buf_ofs,
+               buf_seg,
+               buf_len,
+               timeout;
+};
+
+struct Xmit_pkt {
+       word    buf_ofs,
+               buf_seg,
+               pkt_len;
+};
+
+struct Rcv_resp {
+       word    buf_ofs,
+               buf_seg,
+               buf_len,
+               pkt_len,
+               timeout,
+               status;
+       dword   timetag;
+};
+
+struct Xmit_resp {
+       word    buf_ofs,
+               buf_seg,
+               c_stat,
+               status;
+};
+
+
+struct Netstat {
+       dword   tot_recv,
+               tot_xmit;
+       word    err_CRC,
+               err_align,
+               err_res,
+               err_ovrrun;
+};
+
+
+struct Selftest {
+       word    error;
+       union {
+               word ROM_cksum;
+               struct {
+                       word ofs, seg;
+               } RAM;
+               word i82586;
+       } failure;
+};
+
+struct Info {
+       byte    minor_vers,
+               major_vers;
+       word    ROM_cksum,
+               RAM_sz,
+               free_ofs,
+               free_seg;
+};
+
+struct Memdump {
+       word size,
+            off,
+            seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the ethernet
+data, which has different packaging.)
+*/
+typedef struct {
+       byte    command;
+       byte    length;
+       union   {
+               struct Memconf          memconf;
+               word                    configure;
+               struct Rcv_pkt          rcv_pkt;
+               struct Xmit_pkt         xmit_pkt;
+               byte                    multicast[10][6];
+               byte                    eth_addr[6];
+               byte                    failed;
+               struct Rcv_resp         rcv_resp;
+               struct Xmit_resp        xmit_resp;
+               struct Netstat          netstat;
+               struct Selftest         selftest;
+               struct Info             info;
+               struct Memdump          memdump;
+               byte                    raw[62];
+       } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION   0x00
+#define RECV_BROAD     0x01
+#define RECV_MULTI     0x02
+#define RECV_ALL       0x04
+#define NO_LOOPBACK    0x00
+#define INT_LOOPBACK   0x08
+#define EXT_LOOPBACK   0x10
index 8fb9d9f05b6e08fd9ab609f92f77873b922d8a2d..938e659d2a19f84613b03f569b291b5e3bba9e6c 100644 (file)
@@ -20,6 +20,10 @@ Space.o: Space.c ../../include/linux/autoconf.h
 
 net_init.o: ../../include/linux/autoconf.h
 
+ifdef CONFIG_SK_G16
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(sk_g16.o)
+endif
+
 ifdef CONFIG_WD80x3
 NETDRV_OBJS := $(NETDRV_OBJS) net.a(wd.o)
 CONFIG_8390 = CONFIG_8390
@@ -48,6 +52,11 @@ hp.o:        hp.c CONFIG
        $(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
 endif
 
+ifdef CONFIG_HPLAN_PLUS
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(hp-plus.o)
+CONFIG_8390 = CONFIG_8390
+endif
+
 ifdef CONFIG_ULTRA
 NETDRV_OBJS := $(NETDRV_OBJS) net.a(smc-ultra.o)
 CONFIG_8390 = CONFIG_8390
diff --git a/drivers/net/README.3c505 b/drivers/net/README.3c505
new file mode 100644 (file)
index 0000000..f97bb32
--- /dev/null
@@ -0,0 +1,35 @@
+The address and IRQ used by the 3c505 driver can be configured at boot
+time by typing 'ether=eth0,15,0x300' (replace IRQ and base address with
+ones that tell how your adapter is jumpered).
+
+If no base address is given at the boot time, the driver will look for
+a 3c505 adapter at addresses 0x300, 0x280 and 0x310 in this order,
+possibly messing up any other hardware residing in these addresses.
+If a base address is given, it will be verified.
+
+There's two #defines one may need to change in the 3c505 driver:
+ELP_KERNEL_TYPE
+ this exists just to adapt the driver with pretty wide range of kernels.
+ See 3c505.c for exact information.
+
+ELP_NEED_HARD_RESET
+ some DOS drivers seem to get the adapter to some irrecoverable state
+ if the machine is "warm booted" from DOS to Linux. If you experience
+ problems when warm booting, but "cold boot" works, #defining this
+ to 1 may help.
+
+
+Known problems:
+ when 'ifconfig up' is run for the first time after bootup, the driver
+ complains:
+elp_interrupt(): irq 15 for unknown device.
+                     ^^
+ There should be the IRQ the ELPlus adapter is using. IF the IRQ doesn't
+ match, something is seriously wrong.
+
+Authors:
+ The driver is mainly written by Craig Southeren, email c/o
+ <geoffw@extro.ucc.su.OZ.AU>.
+ Parts of the driver (adapting the driver to 1.1.4+ kernels,
+ IRQ/address detection, minor changes) and this (lousy) 'readme'
+ by Juha Laiho <jlaiho@ichaos.nullnet.fi>.
index ff5d3d2cac9b457aa942b0a7924c7023c9ceef25..0fb46432876b97fe8482dbce062be4c05183c255 100644 (file)
@@ -42,6 +42,7 @@ extern int wd_probe(struct device *dev);
 extern int el2_probe(struct device *dev);
 extern int ne_probe(struct device *dev);
 extern int hp_probe(struct device *dev);
+extern int hp_plus_probe(struct device *dev);
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int el3_probe(struct device *);
@@ -53,6 +54,7 @@ extern int el16_probe(struct device *);
 extern int elplus_probe(struct device *);
 extern int ac3200_probe(struct device *);
 extern int e2100_probe(struct device *);
+extern int SK_init(struct device *dev);
 
 /* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
 extern int atp_init(struct device *);
@@ -83,6 +85,9 @@ ethif_probe(struct device *dev)
 #if defined(CONFIG_HPLAN) || defined(HPLAN)
        && hp_probe(dev)
 #endif
+#if defined(CONFIG_HPLAN_PLUS)
+       && hp_plus_probe(dev)
+#endif
 #ifdef CONFIG_AT1500
        && at1500_probe(dev)
 #endif
@@ -121,6 +126,9 @@ ethif_probe(struct device *dev)
 #endif
 #ifdef CONFIG_DE620            /* D-Link DE-620 adapter */
        && de620_probe(dev)
+#endif
+#if defined(CONFIG_SK_G16)
+       && SK_init(dev)
 #endif
        && 1 ) {
        return 1;       /* -ENODEV or -EAGAIN would be more accurate. */
diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c
new file mode 100644 (file)
index 0000000..5b2a24d
--- /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 Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+       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:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\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 %#lx-%#lx.\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=%ld...", 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:
+ */
diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
new file mode 100644 (file)
index 0000000..d60b6b1
--- /dev/null
@@ -0,0 +1,366 @@
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+       Written 1994 by Donald Becker.
+
+       This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+       These cards are sold under several model numbers, usually 2724*.
+
+       This software may be used and distributed according to the terms
+       of the GNU Public License, incorporated herein by reference.
+
+       The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+
+       Center of Excellence in Space Data and Information Sciences
+               Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+       As is often the case, a great deal of credit is owed to Russ Nelson.
+       The Crynwr packet driver was my primary source of HP-specific
+       programming information.
+*/
+
+static char *version =
+"hp-plus.c:v0.04 6/16/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/config.h>
+#include <linux/string.h>              /* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#ifdef start_bh_atomic                 /* This checks for kernels >= 1.1.0. */
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#else
+#include "dev.h"
+#endif
+
+#include "8390.h"
+
+/*
+   The HP EtherTwist chip implementation is a fairly routine DP8390
+   implementation.  It allows both shared memory and programmed-I/O buffer
+   access, using a custom interface for both.  The programed-I/O mode is
+   entirely implemented in the HP EtherTwist chip, bypassing the problem
+   ridden built-in 8390 facilities used on NE2000 designs.  The shared
+   memory mode is likewise special, with an offset register used to make
+   packets appear at the shared memory base.  Both modes use a base and bounds
+   page register to hide the Rx ring buffer wrap -- a packet that spans the
+   end of physical buffer memory appears continuous to the driver. (c.f. the
+   3c503 and Cabletron E2100)
+
+   A special note: the internal buffer of the board is only 8 bits wide.
+   This lays several nasty traps for the unaware:
+   - the 8390 must be programmed for byte-wide operations
+   - all I/O and memory operations must work on whole words (the access
+     latches are serially preloaded and have no byte-swapping ability).
+
+   This board is laid out in I/O space much like the earlier HP boards:
+   the first 16 locations are for the board registers, and the second 16 are
+   for the 8390.  The board is easy to identify, with both a dedicated 16 bit
+   ID register and a constant 0x530* value in the upper bits of the paging
+   register.
+*/
+
+#define HPP_PROBE_LIST {0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0}
+
+#define HP_IO_EXTENT   32
+
+#define HP_ID                  0x00    /* ID register, always 0x4850. */
+#define HP_PAGING              0x02    /* Registers visible @ 8-f, see PageName. */ 
+#define HPP_OPTION             0x04    /* Bitmapped options, see HP_Option.    */
+#define HPP_OUT_ADDR   0x08    /* I/O output location in Perf_Page.    */
+#define HPP_IN_ADDR            0x0A    /* I/O input location in Perf_Page.             */
+#define HP_DATAPORT            0x0c    /* I/O data transfer in Perf_Page.              */
+#define NIC_OFFSET             0x10    /* Offset to the 8390 registers.                */
+
+#define HP_START_PG            0x00    /* First page of TX buffer */
+#define HP_STOP_PG             0x80    /* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+       Perf_Page = 0,                          /* Normal operation. */
+       MAC_Page = 1,                           /* The ethernet address (+checksum). */
+       HW_Page = 2,                            /* EEPROM-loaded hardware parameters. */
+       LAN_Page = 4,                           /* Transceiver selection, testing, etc. */
+       ID_Page = 6 }; 
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+       NICReset = 1, ChipReset = 2,    /* Active low, really UNreset. */
+       EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+       MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+int hpplus_probe(struct device *dev);
+int hpp_probe1(struct device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct device *dev);
+static int hpp_open(struct device *dev);
+static int hpp_close(struct device *dev);
+static int hpp_mem_block_input(struct device *dev, int count,
+                                                 char *buf, int ring_offset);
+static void hpp_mem_block_output(struct device *dev, int count,
+                                                       const unsigned char *buf, const start_page);
+static int hpp_io_block_input(struct device *dev, int count,
+                                                 char *buf, int ring_offset);
+static void hpp_io_block_output(struct device *dev, int count,
+                                                       const unsigned char *buf, const start_page);
+
+\f
+/*     Probe a list of addresses for an HP LAN+ adaptor.
+       This routine is almost boilerplate. */
+
+int hp_plus_probe(struct device *dev)
+{
+       int *port, ports[] = HPP_PROBE_LIST;
+       short ioaddr = dev->base_addr;
+
+       if (ioaddr > 0x1ff)                     /* Check a single specified location. */
+               return hpp_probe1(dev, ioaddr);
+       else if (ioaddr > 0)                            /* Don't probe at all. */
+               return ENXIO;
+
+       for (port = &ports[0]; *port; port++) {
+               if (check_region(*port, HP_IO_EXTENT))
+                       continue;
+               if (hpp_probe1(dev, *port) == 0) {
+                       return 0;
+               }
+       }
+       return ENODEV;
+}
+
+/* Do the interesting part of the probe at a single address. */
+int hpp_probe1(struct device *dev, int ioaddr)
+{
+       int i;
+       unsigned char checksum = 0;
+       char *name = "HP-PC-LAN+";
+       unsigned char *station_addr = dev->dev_addr;
+       int mem_start;
+
+       /* Check for the HP+ signature, 50 48 0x 53. */
+       if (inw(ioaddr + HP_ID) != 0x4850
+               || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
+               return ENODEV;
+
+       /* OK, we think that we have it.  Get and checksum the physical address. */
+       printk("%s: %s at %#3x,", dev->name, name, ioaddr);
+
+       /* Retrieve and checksum the station address. */
+       outw(MAC_Page, ioaddr + HP_PAGING);
+
+       for(i = 0; i < ETHER_ADDR_LEN; i++) {
+               unsigned char inval = inb(ioaddr + 8 + i);
+               station_addr[i] = inval;
+               checksum += inval;
+               printk(" %2.2x", inval);
+       }
+       checksum += inb(ioaddr + 14);
+
+       if (checksum != 0xff) {
+               printk(" bad checksum %2.2x.\n", checksum);
+               return ENODEV;
+       } else {
+               /* Point at the Software Configuration Flags. */
+               outw(ID_Page, ioaddr + HP_PAGING);
+               printk(" ID %4.4x", inw(ioaddr + 12));
+       }
+
+       /* Grab the region so we can find another board if something fails. */
+       snarf_region(ioaddr, HP_IO_EXTENT);
+
+       /* Read the IRQ line. */
+       outw(HW_Page, ioaddr + HP_PAGING);
+       {
+               int irq = inb(ioaddr + 13) & 0x0f;
+               int option = inw(ioaddr + HPP_OPTION);
+
+               dev->irq = irq;
+               if (option & MemEnable) {
+                       mem_start = inw(ioaddr + 9) << 8;
+                       printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+               } else {
+                       mem_start = 0;
+                       printk(", IRQ %d, programmed-I/O mode.\n", irq);
+               }
+       }
+
+       printk( "%s%s", KERN_INFO, version);
+
+       /* Set the wrap registers for string I/O reads.   */
+       outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+       /* Set the base address to point to the NIC, not the "real" base! */
+       dev->base_addr = ioaddr + NIC_OFFSET;
+
+       ethdev_init(dev);
+
+    dev->open = &hpp_open;
+    dev->stop = &hpp_close;
+
+       ei_status.name = name;
+       ei_status.word16 = 0;           /* Agggghhhhh! Debug time: 2 days! */
+       ei_status.tx_start_page = HP_START_PG;
+       ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES;
+       ei_status.stop_page = HP_STOP_PG;
+
+       ei_status.reset_8390 = &hpp_reset_8390;
+       ei_status.block_input = &hpp_io_block_input;
+       ei_status.block_output = &hpp_io_block_output;
+
+       /* Check if the memory_enable flag is set in the option register. */
+       if (mem_start) {
+               ei_status.block_input = &hpp_mem_block_input;
+               ei_status.block_output = &hpp_mem_block_output;
+               dev->mem_start = mem_start;
+               dev->rmem_start = dev->mem_start + TX_2X_PAGES*256;
+               dev->mem_end = dev->rmem_end
+                       = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+       }
+
+       outw(Perf_Page, ioaddr + HP_PAGING);
+       NS8390_init(dev, 0);
+       /* Leave the 8390 and HP chip reset. */
+       outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+       return 0;
+}
+
+static int
+hpp_open(struct device *dev)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       int option_reg;
+
+       if (request_irq(dev->irq, &ei_interrupt)) {
+           return -EAGAIN;
+       }
+
+       /* Reset the 8390 and HP chip. */
+       option_reg = inw(ioaddr + HPP_OPTION);
+       outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+       SLOW_DOWN_IO; SLOW_DOWN_IO;
+       /* Unreset the board and enable interrupts. */
+       outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+       /* Set the wrap registers for programmed-I/O operation.   */
+       outw(HW_Page, ioaddr + HP_PAGING);
+       outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+       /* Select the operational page. */
+       outw(Perf_Page, ioaddr + HP_PAGING);
+
+    return ei_open(dev);
+}
+
+static int
+hpp_close(struct device *dev)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       int option_reg = inw(ioaddr + HPP_OPTION);
+
+    free_irq(dev->irq);
+    irq2dev_map[dev->irq] = NULL;
+    NS8390_init(dev, 0);
+       outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+                ioaddr + HPP_OPTION);
+
+    return 0;
+}
+
+static void
+hpp_reset_8390(struct device *dev)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       int option_reg = inw(ioaddr + HPP_OPTION);
+
+       if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+       outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+       /* Pause a few cycles for the hardware reset to take place. */
+       SLOW_DOWN_IO;
+       SLOW_DOWN_IO;
+       ei_status.txing = 0;
+       outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+       SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+
+       if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+               printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+       if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+       return;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.
+   Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static int
+hpp_io_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+
+       outw(ring_offset, ioaddr + HPP_IN_ADDR);
+       insw(ioaddr + HP_DATAPORT, buf, count>>1);
+       if (count & 0x01)
+        buf[count-1] = inw(ioaddr + HP_DATAPORT);
+       return ring_offset + count;
+}
+
+static int
+hpp_mem_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       int option_reg = inw(ioaddr + HPP_OPTION);
+
+       outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+       outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+       /* Caution: this relies on 8390.c rounding up allocations! */
+       memcpy(buf, (char*)dev->mem_start,  (count + 3) & ~3);
+       outw(option_reg, ioaddr + HPP_OPTION);
+
+       return ring_offset + count;
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+   It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct device *dev, int count,
+                                       const unsigned char *buf, const start_page)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+       outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+       return;
+}
+
+static void
+hpp_mem_block_output(struct device *dev, int count,
+                               const unsigned char *buf, const start_page)
+{
+       int ioaddr = dev->base_addr - NIC_OFFSET;
+       int option_reg = inw(ioaddr + HPP_OPTION);
+
+       outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+       outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+       memcpy((char *)dev->mem_start, buf, (count + 3) & ~3);
+       outw(option_reg, ioaddr + HPP_OPTION);
+
+       return;
+}
+
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c
new file mode 100644 (file)
index 0000000..e63c486
--- /dev/null
@@ -0,0 +1,2119 @@
+/*-
+ * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module         : sk_g16.c
+ *
+ * Version        : $Revision: 1.1 $
+ *
+ * Author         : Patrick J.D. Weichmann
+ *
+ * Date Created   : 94/05/26
+ * Last Updated   : $Date: 1994/06/30 16:25:15 $
+ *
+ * Description    : Schneider & Koch G16 Ethernet Device Driver for
+ *                  Linux Kernel >= 1.1.22
+ * Update History :
+ *
+-*/
+
+static char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $";
+
+/*
+ * The Schneider & Koch (SK) G16 Network device driver is based
+ * on the 'ni6510' driver from Michael Hipp which can be found at
+ * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz
+ * 
+ * Sources: 1) ni6510.c by M. Hipp
+ *          2) depca.c  by D.C. Davies
+ *          3) skeleton.c by D. Becker
+ *          4) Am7990 Local Area Network Controller for Ethernet (LANCE),
+ *             AMD, Pub. #05698, June 1989
+ *
+ * Many Thanks for helping me to get things working to: 
+ *                 
+ *                 A. Cox (A.Cox@swansea.ac.uk)
+ *                 M. Hipp (mhipp@student.uni-tuebingen.de)
+ *                 R. Bolz (Schneider & Koch, Germany)
+ *
+ * See README.sk_g16 for details about limitations and bugs for the
+ * current version.
+ *
+ * To Do: 
+ *        - Support of SK_G8 and other SK Network Cards.
+ *        - Autoset memory mapped RAM. Check for free memory and then
+ *          configure RAM correctly. 
+ *        - SK_close should really set card in to initial state.
+ *        - Test if IRQ 3 is not switched off. Use autoirq() functionality.
+ *          (as in /drivers/net/skeleton.c)
+ *        - Implement Multicast addressing. At minimum something like
+ *          in depca.c. 
+ *        - Redo the statistics part.
+ *        - Try to find out if the board is in 8 Bit or 16 Bit slot.
+ *          If in 8 Bit mode don't use IRQ 11.
+ *        - (Try to make it slightly faster.) 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/string.h> 
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h> 
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sk_g16.h"
+
+/* 
+ * Schneider & Koch Card Definitions 
+ * =================================
+ */  
+
+#define SK_NAME   "SK_G16"
+
+/*
+ * SK_G16 Configuration
+ * --------------------
+ */ 
+
+/* 
+ * Abbreviations
+ * -------------
+ *  
+ * RAM - used for the 16KB shared memory 
+ * Boot_ROM, ROM - are used for referencing the BootEPROM
+ *
+ * SK_BOOT_ROM and SK_ADDR are symbolic constants used to configure
+ * the behaviour of the driver and the SK_G16.
+ *
+ * ! See sk_g16.install on how to install and configure the driver !   
+ *
+ * SK_BOOT_ROM defines if the Boot_ROM should be switched off or not.
+ *
+ * SK_ADDR defines the address where the RAM will be mapped into the real
+ *         host memory.
+ *         valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps.
+ */  
+#define SK_BOOT_ROM     1              /* 1=BootROM on 0=off */
+
+#define SK_ADDR         0xcc000
+
+/* 
+ * In POS3 are bits A14-A19 of the address bus. These bits can be set
+ * to choose the RAM address. Thats why we only can choose the RAM address
+ * in 16KB steps.
+ */
+
+#define POS_ADDR       (rom_addr>>14)  /* Do not change this line */
+
+/* 
+ * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations
+ * ----------------------------------------------
+ */
+
+/* 
+ * As nearly every card has also SK_G16 a specified I/O Port region and
+ * only a few possible IRQ's.
+ * In the Installation Guide from Schneider & Koch is listed a possible
+ * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt
+ * controllers. So we use in SK_IRQS IRQ9.
+ */
+
+/* Don't touch any of the following #defines. */
+
+#define SK_IO_PORTS     { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 }
+
+#define SK_IRQS         { 3, 5, 9, 11, 0 }
+
+#define SK_BOOT_ROM_LOCATIONS { 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0xd8000, 0xdc000, 0 }
+
+#define SK_BOOT_ROM_ID  { 0x55, 0xaa, 0x10, 0x50, 0x06, 0x33 }
+
+/* 
+ * SK_G16 POS REGISTERS 
+ * --------------------
+ */
+
+/*
+ * SK_G16 has a Programmable Option Select (POS) Register.
+ * The POS is composed of 8 separate registers (POS0-7) which 
+ * are I/O mapped on an address set by the W1 switch.                    
+ *
+ */
+
+#define SK_POS_SIZE 8           /* 8 I/O Ports are used by SK_G16 */
+
+#define SK_POS0     ioaddr      /* Card-ID Low (R) */
+#define SK_POS1     ioaddr+1    /* Card-ID High (R) */
+#define SK_POS2     ioaddr+2    /* Card-Enable, Boot-ROM Disable (RW) */
+#define SK_POS3     ioaddr+3    /* Base address of RAM */
+#define SK_POS4     ioaddr+4    /* IRQ */
+
+/* POS5 - POS7 are unused */
+
+/* 
+ * SK_G16 MAC PREFIX 
+ * -----------------
+ */
+
+/* 
+ * Scheider & Koch manufactorer code (00:00:a5).
+ * This must be checked, that we are sure it is a SK card.
+ */
+
+#define SK_MAC0         0x00
+#define SK_MAC1         0x00
+#define SK_MAC2         0x5a
+
+/* 
+ * SK_G16 ID 
+ * ---------
+ */ 
+
+/* 
+ * If POS0,POS1 contain the following ID, then we know
+ * at which I/O Port Address we are. 
+ */
+
+#define SK_IDLOW  0xfd 
+#define SK_IDHIGH 0x6a
+
+
+/* 
+ * LANCE POS Bit definitions 
+ * -------------------------
+ */
+
+#define SK_ROM_RAM_ON  (POS2_CARD)
+#define SK_ROM_RAM_OFF (POS2_EPROM)
+#define SK_ROM_ON      (inb(SK_POS2) & POS2_CARD)
+#define SK_ROM_OFF     (inb(SK_POS2) | POS2_EPROM)
+#define SK_RAM_ON      (inb(SK_POS2) | POS2_CARD)
+#define SK_RAM_OFF     (inb(SK_POS2) & POS2_EPROM) 
+
+#define POS2_CARD  0x0001              /* 1 = SK_G16 on      0 = off */
+#define POS2_EPROM 0x0002              /* 1 = Boot EPROM off 0 = on */ 
+
+/* 
+ * SK_G16 Memory mapped Registers
+ * ------------------------------
+ *
+ */ 
+
+#define SK_IOREG        (board->ioreg) /* LANCE data registers.     */ 
+#define SK_PORT         (board->port)  /* Control, Status register  */
+#define SK_IOCOM        (board->iocom) /* I/O Command               */
+
+/* 
+ * SK_G16 Status/Control Register bits
+ * -----------------------------------
+ *
+ * (C) Controlreg (S) Statusreg 
+ */
+
+/* 
+ * Register transfer: 0 = no transfer
+ *                    1 = transfering data between LANCE and I/O reg 
+ */
+#define SK_IORUN        0x20   
+
+/* 
+ * LANCE interrupt: 0 = LANCE interrupt occured        
+ *                  1 = no LANCE interrupt occured
+ */
+#define SK_IRQ          0x10   
+                       
+#define SK_RESET        0x08   /* Reset SK_CARD: 0 = RESET 1 = normal */
+#define SK_RW           0x02   /* 0 = write to 1 = read from */
+#define SK_ADR          0x01   /* 0 = REG DataPort 1 = RAP Reg addr port */
+
+  
+#define SK_RREG         SK_RW  /* Transferdirection to read from lance */
+#define SK_WREG         0      /* Transferdirection to write to lance */
+#define SK_RAP          SK_ADR /* Destination Register RAP */
+#define SK_RDATA        0      /* Destination Register REG DataPort */
+
+/* 
+ * SK_G16 I/O Command 
+ * ------------------
+ */
+
+/* 
+ * Any bitcombination sets the internal I/O bit (transfer will start) 
+ * when written to I/O Command
+ */
+
+#define SK_DOIO         0x80   /* Do Transfer */ 
+/* 
+ * LANCE RAP (Register Adress Port). 
+ * ---------------------------------
+ */
+
+/*   
+ * The LANCE internal registers are selected through the RAP. 
+ * The Registers are:
+ *
+ * CSR0 - Status and Control flags 
+ * CSR1 - Low order bits of initialize block (bits 15:00)
+ * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved)
+ * CSR3 - Allows redifinition of the Bus Master Interface.
+ *        This register must be set to 0x0002, which means BSWAP = 0,
+ *        ACON = 1, BCON = 0;
+ *
+ */
+#define CSR0            0x00   
+#define CSR1            0x01  
+#define CSR2            0x02 
+#define CSR3            0x03
+
+/* 
+ * General Definitions 
+ * ===================
+ */
+
+/* 
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * We have 16KB RAM which can be accessed by the LANCE. In the 
+ * memory are not only the buffers but also the ring descriptors and
+ * the initialize block. 
+ * Don't change anything unless you really know what you do.
+ */
+
+#define LC_LOG_TX_BUFFERS 1               /* (2 == 2^^1) 2 Transmit buffers */
+#define LC_LOG_RX_BUFFERS 3               /* (8 == 2^^3) 8 Receive buffers */
+
+/* Descriptor ring sizes */
+
+#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */
+#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */
+
+/* Define Mask for setting RMD, TMD length in the LANCE init_block */
+
+#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29)
+#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29)
+
+/*
+ * Data Buffer size is set to maximum packet length.
+ */
+
+#define PKT_BUF_SZ              1518 
+
+/* 
+ * The number of low I/O ports used by the ethercard. 
+ */
+
+#define ETHERCARD_TOTAL_SIZE    SK_POS_SIZE
+
+/* 
+ * Portreserve is there to mark the Card I/O Port region as used. 
+ * Check_region is to check if the region at ioaddr with the size "size" 
+ * is free or not.
+ * Snarf_region allocates the I/O Port region.
+ */
+
+#ifndef HAVE_PORTRESERVE
+
+#define check_region(ioaddr, size)              0
+#define snarf_region(ioaddr, size);             do ; while (0)
+
+#endif
+
+/* 
+ * SK_DEBUG
+ *
+ * Here you can choose what level of debugging wanted.
+ *
+ * If SK_DEBUG and SK_DEBUG2 are undefined, then only the
+ *  necessary messages will be printed.
+ *
+ * If SK_DEBUG is defined, there will be many debugging prints
+ *  which can help to find some mistakes in configuration or even
+ *  in the driver code.
+ *
+ * If SK_DEBUG2 is defined, many many messages will be printed 
+ *  which normally you don't need. I used this to check the interrupt
+ *  routine. 
+ *
+ * (If you define only SK_DEBUG2 then only the messages for 
+ *  checking interrupts will be printed!)
+ *
+ * Normal way of live is: 
+ *
+ * For the whole thing get going let both symbolic constants
+ * undefined. If you face any problems and you know whats going
+ * on (you know something about the card and you can interpret some
+ * hex LANCE register output) then define SK_DEBUG
+ * 
+ */
+
+#undef  SK_DEBUG       /* debugging */
+#undef  SK_DEBUG2      /* debugging with more verbose report */
+
+#ifdef SK_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+
+#ifdef SK_DEBUG2
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x) /**/
+#endif
+
+/* 
+ * SK_G16 RAM
+ *
+ * The components are memory mapped and can be set in a region from
+ * 0x00000 through 0xfc000 in 16KB steps. 
+ *
+ * The Network components are: dual ported RAM, Prom, I/O Reg, Status-,
+ * Controlregister and I/O Command.
+ *
+ * dual ported RAM: This is the only memory region which the LANCE chip
+ *      has access to. From the Lance it is addressed from 0x0000 to
+ *      0x3fbf. The host accesses it normaly.
+ *
+ * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a
+ *       8-Bit PROM, this means only the 16 even addresses are used of the
+ *       32 Byte Address region. Access to a odd address results in invalid
+ *       data.
+ * 
+ * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write,
+ *       Hi-Byte Write, Low-Byte Read, Hi-Byte Read.
+ *       Transfer from or to the LANCE is always in 16Bit so Low and High
+ *       registers are always relevant.
+ *
+ *       The Data from the Readregister is not the data in the Writeregister!!
+ *       
+ * Port: Status- and Controlregister. 
+ *       Two different registers which share the same address, Status is 
+ *       read-only, Control is write-only.
+ *    
+ * I/O Command: 
+ *       Any bitcombination written in here starts the transmission between
+ *       Host and LANCE.
+ */
+
+typedef struct
+{
+       unsigned char  ram[0x3fc0];   /* 16KB dual ported ram */
+       unsigned char  rom[0x0020];   /* 32Byte PROM containing 6Byte MAC */
+       unsigned char  res1[0x0010];  /* reserved */
+       unsigned volatile short ioreg;/* LANCE I/O Register */
+       unsigned volatile char  port; /* Statusregister and Controlregister */
+       unsigned char  iocom;         /* I/O Command Register */
+} SK_RAM;
+
+/* struct  */
+
+/* 
+ * This is the structure for the dual ported ram. We
+ * have exactly 16 320 Bytes. In here there must be:
+ *
+ *     - Initialize Block   (starting at a word boundary)
+ *     - Receive and Transmit Descriptor Rings (quadword boundary)
+ *     - Data Buffers (arbitrary boundary)
+ *
+ * This is because LANCE has on SK_G16 only access to the dual ported
+ * RAM and nowhere else.
+ */
+
+struct SK_ram
+{
+    struct init_block ib;
+    struct tmd tmde[TMDNUM];
+    struct rmd rmde[RMDNUM];
+    char tmdbuf[TMDNUM][PKT_BUF_SZ];
+    char rmdbuf[RMDNUM][PKT_BUF_SZ];
+};
+
+/* 
+ * Structure where all necessary information is for ring buffer 
+ * management and statistics.
+ */
+
+struct priv
+{
+    struct SK_ram *ram;  /* dual ported ram structure */
+    struct rmd *rmdhead; /* start of receive ring descriptors */
+    struct tmd *tmdhead; /* start of transmit ring descriptors */
+    int        rmdnum;   /* actual used ring descriptor */
+    int        tmdnum;   /* actual transmit descriptor for transmitting data */
+    int        tmdlast;  /* last sent descriptor used for error handling, etc */
+    void       *rmdbufs[RMDNUM]; /* pointer to the receive buffers */
+    void       *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */
+    struct enet_statistics stats; /* Device driver statistics */
+};
+
+/* global variable declaration */
+
+/* IRQ map used to reserve a IRQ (see SK_open()) */
+
+extern void *irq2dev_map[16]; 
+
+/* static variables */
+
+static SK_RAM *board;  /* pointer to our memory mapped board components */
+
+/* Macros */
+
+\f
+/* Function Prototypes */
+
+/*
+ * Device Driver functions
+ * -----------------------
+ * See for short explanation of each function its definitions header.
+ */
+
+int          SK_init(struct device *dev);
+static int   SK_probe(struct device *dev, short ioaddr);
+
+static int   SK_open(struct device *dev);
+static int   SK_send_packet(struct sk_buff *skb, struct device *dev);
+static void  SK_interrupt(int reg_ptr);
+static void  SK_rxintr(struct device *dev);
+static void  SK_txintr(struct device *dev);
+static int   SK_close(struct device *dev);
+
+static struct enet_statistics *SK_get_stats(struct device *dev);
+
+unsigned int SK_rom_addr(void);
+
+#ifdef HAVE_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
+
+/*
+ * LANCE Functions
+ * ---------------
+ */
+
+static int SK_lance_init(struct device *dev, unsigned short mode);
+void   SK_reset_board(void);
+void   SK_set_RAP(int reg_number);
+int    SK_read_reg(int reg_number);
+int    SK_rread_reg(void);
+void   SK_write_reg(int reg_number, int value);
+
+/* 
+ * Debuging functions
+ * ------------------
+ */
+
+void SK_print_pos(struct device *dev, char *text);
+void SK_print_dev(struct device *dev, char *text);
+void SK_print_ram(struct device *dev);
+
+\f
+/*-
+ * Function       : SK_init
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : Check for a SK_G16 network adaptor and initialize it.
+ *                  This function gets called by dev_init which initializes
+ *                  all Network devices.
+ *
+ * Parameters     : I : struct device *dev - structure preconfigured 
+ *                                           from Space.c
+ * Return Value   : 0 = Driver Found and initialized 
+ * Errors         : ENODEV - no device found
+ *                  ENXIO  - not probed
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+/* 
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, alloate space for the device and return success
+ *                         (detachable devices only).
+ */
+
+int SK_init(struct device *dev)
+{
+       int ioaddr         = 0;            /* I/O port address used for POS regs */
+       int *port, ports[] = SK_IO_PORTS;  /* SK_G16 supported ports */
+
+       /* get preconfigured base_addr from dev which is done in Space.c */
+       int base_addr = dev->base_addr; 
+
+        PRINTK(("%s: %s", SK_NAME, rcsid));
+        rcsid = NULL;                 /* We do not want to use this further */
+
+       if (base_addr > 0x0ff)        /* Check a single specified address */
+       {
+           /* Check if on specified address is a SK_G16 */
+
+           if ( (inb(SK_POS0) == SK_IDLOW) ||
+                (inb(SK_POS1) == SK_IDHIGH) )  
+           {
+               return SK_probe(dev, base_addr);  
+           }
+
+           return ENODEV;            /* Sorry, but on specified address NO SK_G16 */
+       }
+       else if (base_addr > 0)       /* Don't probe at all */
+       {
+               return ENXIO;
+       }
+
+       /* Autoprobe base_addr */
+
+       for (port = &ports[0]; *port; port++) 
+       {
+           ioaddr = *port;           /* we need ioaddr for accessing POS regs */
+
+           /* Check if I/O Port region is used by another board */
+
+           if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+           {
+               continue;             /* Try next Port address */
+           }
+
+           /* Check if at ioaddr is a SK_G16 */
+
+           if ( !(inb(SK_POS0) == SK_IDLOW) ||
+                !(inb(SK_POS1) == SK_IDHIGH) )
+           {
+               continue;             /* Try next Port address */
+           }
+
+           dev->base_addr = ioaddr;  /* Set I/O Port Address */
+
+           if (SK_probe(dev, ioaddr) == 0)  
+           {
+               return 0; /* Card found and initialized */
+           }
+       }
+
+       dev->base_addr = base_addr;   /* Write back original base_addr */
+
+       return ENODEV;                /* Failed to find or init driver */
+
+} /* End of SK_init */
+
+\f
+/*-
+ * Function       : SK_probe
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : This function is called by SK_init and 
+ *                  does the main part of initialization.
+ *                  
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ *                  I : short ioaddr       - I/O Port address where POS is.
+ * Return Value   : 0 = Initilization done             
+ * Errors         : ENODEV - No SK_G16 found
+ *                  -1     - Configuration problem
+ * Globals        : irq2dev_map - Which device uses which IRQ
+ *                : board       - pointer to SK_RAM
+ * Update History :
+ *     YY/MM/DD  uid  Description
+ *     94/06/30  pwe  SK_ADDR now checked and at the correct place
+-*/
+
+int SK_probe(struct device *dev, short ioaddr)
+{
+    int i,j;                /* Counters */
+    int sk_addr_flag = 0;   /* SK ADDR correct? 1 - no, 0 - yes */
+    unsigned int rom_addr;  /* used to store RAM address used for POS_ADDR */
+
+    struct priv *p;         /* SK_G16 private structure */
+
+    if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000)
+    {
+      
+       sk_addr_flag = 1;
+
+       /* 
+        * Now here we could use a routine which searches for a free
+        * place in the ram and set SK_ADDR if found. TODO. 
+        */
+    }
+
+    if (SK_BOOT_ROM)            /* Shall we keep Boot_ROM on ? */
+    {
+        PRINTK(("## %s: SK_BOOT_ROM is set.\n", SK_NAME));
+
+        rom_addr = SK_rom_addr();
+
+       if (rom_addr == 0)      /* No Boot_ROM found */
+       {
+            if (sk_addr_flag)   /* No or Invalid SK_ADDR is defined */ 
+            {
+                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+                       dev->name, SK_ADDR);
+                return -1;
+            }
+
+           rom_addr = SK_ADDR; /* assign predefined address */
+
+           PRINTK(("## %s: NO Bootrom found \n", SK_NAME));
+
+           outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+           outb(POS_ADDR, SK_POS3);       /* Set RAM address */
+           outb(SK_RAM_ON, SK_POS2);      /* enable RAM */
+       }
+       else if (rom_addr == SK_ADDR) 
+       {
+            printk("%s: RAM + ROM are set to the same address %#08x\n"
+                   "   Check configuration. Now switching off Boot_ROM\n",
+                   SK_NAME, rom_addr);
+
+           outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off*/
+           outb(POS_ADDR, SK_POS3);       /* Set RAM address */
+           outb(SK_RAM_ON, SK_POS2);      /* enable RAM */
+       }
+       else
+       {
+            PRINTK(("## %s: Found ROM at %#08x\n", SK_NAME, rom_addr));
+           PRINTK(("## %s: Keeping Boot_ROM on\n", SK_NAME));
+
+            if (sk_addr_flag)       /* No or Invalid SK_ADDR is defined */ 
+            {
+                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+                       dev->name, SK_ADDR);
+                return -1;
+            }
+
+           rom_addr = SK_ADDR;
+
+           outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */ 
+           outb(POS_ADDR, SK_POS3);       /* Set RAM address */
+           outb(SK_ROM_RAM_ON, SK_POS2);  /* RAM on, BOOT_ROM on */
+       }
+    }
+    else /* Don't keep Boot_ROM */
+    {
+        PRINTK(("## %s: SK_BOOT_ROM is not set.\n", SK_NAME));
+
+        if (sk_addr_flag)           /* No or Invalid SK_ADDR is defined */ 
+        {
+            printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+                   dev->name, SK_ADDR);
+            return -1;
+        }
+
+       rom_addr = SK_rom_addr();          /* Try to find a Boot_ROM */
+
+       /* IF we find a Boot_ROM disable it */
+
+       outb(SK_ROM_RAM_OFF, SK_POS2);     /* Boot_ROM + RAM off */  
+
+        /* We found a Boot_ROM and its gone. Set RAM address on
+         * Boot_ROM address. 
+         */ 
+
+       if (rom_addr) 
+       {
+            printk("%s: We found Boot_ROM at %#08x. Now setting RAM on"
+                   "that address\n", SK_NAME, rom_addr);
+
+           outb(POS_ADDR, SK_POS3);       /* Set RAM on Boot_ROM address */
+       }
+       else /* We did not find a Boot_ROM, use predefined SK_ADDR for ram */
+       {
+            if (sk_addr_flag)       /* No or Invalid SK_ADDR is defined */ 
+            {
+                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+                       dev->name, SK_ADDR);
+                return -1;
+            }
+
+           rom_addr = SK_ADDR;
+
+           outb(POS_ADDR, SK_POS3);       /* Set RAM address */ 
+       }
+       outb(SK_RAM_ON, SK_POS2);          /* enable RAM */
+    }
+
+#ifdef SK_DEBUG
+    SK_print_pos(dev, "POS registers after ROM, RAM config");
+#endif
+
+    board = (SK_RAM *) rom_addr; 
+
+    /* Read in station address */
+    for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
+    {
+       dev->dev_addr[i] = board->rom[j];          
+    }
+
+    /* Check for manufactorer code */
+    if (!(dev->dev_addr[0] == SK_MAC0 &&
+         dev->dev_addr[1] == SK_MAC1 &&
+         dev->dev_addr[2] == SK_MAC2) )
+    {
+        PRINTK(("## %s: We did not find SK_G16 at RAM location.\n",
+                SK_NAME)); 
+       return ENODEV;                     /* NO SK_G16 found */
+    }
+
+    printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n",
+           dev->name,
+           "Schneider & Koch Netcard",
+           (unsigned int) dev->base_addr,
+           dev->dev_addr[0],
+           dev->dev_addr[1],
+           dev->dev_addr[2],
+           dev->dev_addr[3],
+           dev->dev_addr[4],
+           dev->dev_addr[5]);
+
+    /* Grap the I/O Port region */
+    snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE);
+
+    /* Initialize device structure */
+
+    /* Allocate memory for private structure */
+    p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
+    memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */
+
+    /* Assign our Device Driver functions */
+
+    dev->open                   = &SK_open;
+    dev->stop                   = &SK_close;
+    dev->hard_start_xmit        = &SK_send_packet;
+    dev->get_stats              = &SK_get_stats;
+
+#ifdef HAVE_MULTICAST
+    dev->set_multicast_list     = &set_multicast_list;
+#endif
+
+
+    /* Set the generic fields of the device structure */
+
+    ether_setup(dev);
+
+    /* Initialize private structure */
+
+    p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */
+    p->tmdhead = &(p->ram)->tmde[0];     /* Set TMD head */
+    p->rmdhead = &(p->ram)->rmde[0];     /* Set RMD head */
+
+    /* Initialize buffer pointers */
+
+    for (i = 0; i < TMDNUM; i++)
+    {
+       p->tmdbufs[i] = &(p->ram)->tmdbuf[i];
+    }
+
+    for (i = 0; i < RMDNUM; i++)
+    {
+       p->rmdbufs[i] = &(p->ram)->rmdbuf[i]; 
+    }
+
+#ifdef SK_DEBUG
+    SK_print_pos(dev, "End of SK_probe");
+    SK_print_ram(dev);
+#endif 
+
+    return 0;                            /* Initialization done */
+
+} /* End of SK_probe() */
+
+\f
+/*- 
+ * Function       : SK_open
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : This function is called sometimes after booting 
+ *                  when ifconfig programm is run.
+ *
+ *                  This function requests an IRQ, sets the correct
+ *                  IRQ in the card. Then calls SK_lance_init() to 
+ *                  init and start the LANCE chip. Then if everthing is 
+ *                  ok returns with 0 (OK), which means SK_G16 is now
+ *                  opened and operational.
+ *
+ *                  (Called by dev_open() /net/inet/dev.c)
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ * Return Value   : 0 - Device opened
+ * Errors         : -EAGAIN - Open failed
+ * Globals        : irq2dev_map - which device uses which irq
+ * Side Effects   : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static int SK_open(struct device *dev)
+{
+    int i = 0;
+    int irqval = 0;
+    int ioaddr = dev->base_addr;
+
+    int irqtab[] = SK_IRQS; 
+
+    struct priv *p = (struct priv *)dev->priv;
+
+    PRINTK(("## %s: At beginning of SK_open(). CSR0: %#06x\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+    if (dev->irq == 0) /* Autoirq */
+    {
+       i = 0;
+
+       /* 
+         * Check if one IRQ out of SK_IRQS is free and install 
+        * interrupt handler.
+        * Most done by request_irq(). 
+        * irqval: 0       - interrupt handler installed for IRQ irqtab[i]
+        *         -EBUSY  - interrupt busy 
+         *         -EINVAL - irq > 15 or handler = NULL
+        */
+
+       do
+       {
+         irqval = request_irq(irqtab[i], &SK_interrupt);
+         i++;
+       } while (irqval && irqtab[i]);
+
+       if (irqval) /* We tried every possible IRQ but no success */
+       {
+           printk("%s: unable to get an IRQ\n", dev->name);
+           return -EAGAIN;
+       }
+
+       dev->irq = irqtab[--i]; 
+       
+       outb(i<<2, SK_POS4);           /* Set Card on probed IRQ */
+
+    }
+    else if (dev->irq == 2) /* IRQ2 is always IRQ9 */
+    {
+       if (request_irq(9, &SK_interrupt))
+       {
+           printk("%s: unable to get IRQ 9\n", dev->name);
+           return -EAGAIN;
+       } 
+       dev->irq = 9;
+       
+        /* 
+         * Now we set card on IRQ2.
+         * This can be confusing, but remember that IRQ2 on the network
+         * card is in reality IRQ9
+         */
+       outb(0x08, SK_POS4);           /* set card to IRQ2 */
+
+    }
+    else  /* Check IRQ as defined in Space.c */
+    {
+       int i = 0;
+
+       /* check if IRQ free and valid. Then install Interrupt handler */
+
+       if (request_irq(dev->irq, &SK_interrupt))
+       {
+           printk("%s: unable to get selected IRQ\n", dev->name);
+           return -EAGAIN;
+       }
+
+       switch(dev->irq)
+       {
+           case 3: i = 0;
+                   break;
+           case 5: i = 1;
+                   break;
+           case 2: i = 2;
+                   break;
+           case 11:i = 3;
+                   break;
+           default: 
+               printk("%s: Preselected IRQ %d is invalid for %s boards",
+                      dev->name,
+                      dev->irq,
+                       SK_NAME);
+               return -EAGAIN;
+       }      
+  
+       outb(i<<2, SK_POS4);           /* Set IRQ on card */
+    }
+
+    irq2dev_map[dev->irq] = dev;       /* Set IRQ as used by us */
+    
+    printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n",
+           dev->name, (unsigned int)dev->base_addr, 
+           (int) dev->irq, (unsigned int) p->ram);
+
+    if (!(i = SK_lance_init(dev, 0)))  /* LANCE init OK? */
+    {
+
+
+       dev->tbusy = 0;
+       dev->interrupt = 0;
+       dev->start = 1;
+
+#ifdef SK_DEBUG
+
+        /* 
+         * This debug block tries to stop LANCE,
+         * reinit LANCE with transmitter and receiver disabled,
+         * then stop again and reinit with NORMAL_MODE
+         */
+
+        printk("## %s: After lance init. CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0));
+        SK_write_reg(CSR0, CSR0_STOP);
+        printk("## %s: LANCE stopped. CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0));
+        SK_lance_init(dev, MODE_DTX | MODE_DRX);
+        printk("## %s: Reinit with DTX + DRX off. CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0));
+        SK_write_reg(CSR0, CSR0_STOP);
+        printk("## %s: LANCE stopped. CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0));
+        SK_lance_init(dev, MODE_NORMAL);
+        printk("## %s: LANCE back to normal mode. CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0));
+        SK_print_pos(dev, "POS regs before returning OK");
+
+#endif /* SK_DEBUG */
+       
+       return 0;              /* SK_open() is successful */
+    }
+    else /* LANCE init failed */
+    {
+
+       PRINTK(("## %s: LANCE init failed: CSR0: %#06x\n", 
+               SK_NAME, SK_read_reg(CSR0)));
+
+       dev->start = 0;        /* Device not ready */
+       return -EAGAIN;
+    }
+
+} /* End of SK_open() */
+
+\f
+/*-
+ * Function       : SK_lance_init
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : Reset LANCE chip, fill RMD, TMD structures with
+ *                  start values and Start LANCE.
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ *                  I : int mode - put LANCE into "mode" see data-sheet for
+ *                                 more info.
+ * Return Value   : 0  - Init done
+ * Errors         : -1 - Init failed
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static int SK_lance_init(struct device *dev, unsigned short mode)
+{
+    int i;
+    struct priv *p = (struct priv *) dev->priv; 
+    struct tmd  *tmdp;
+    struct rmd  *rmdp;
+
+    PRINTK(("## %s: At beginning of LANCE init. CSR0: %#06x\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+    /* Reset LANCE */
+    SK_reset_board();
+
+    /* Initialize TMD's with start values */
+    p->tmdnum = 0;                   /* First descriptor for transmitting */ 
+    p->tmdlast = 0;                  /* First descriptor for reading stats */
+
+    for (i = 0; i < TMDNUM; i++)     /* Init all TMD's */
+    {
+       tmdp = p->tmdhead + i; 
+   
+       tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */
+       
+       /* Mark TMD as start and end of packet */
+       tmdp->u.s.status = TX_STP | TX_ENP;
+    }
+
+
+    /* Initialize RMD's with start values */
+
+    p->rmdnum = 0;                   /* First RMD which will be used */
+    for (i = 0; i < RMDNUM; i++)     /* Init all RMD's */
+    {
+       rmdp = p->rmdhead + i;
+
+       
+       rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */
+       
+       /* 
+         * LANCE must be owner at beginning so that he can fill in 
+        * receiving packets, set status and release RMD 
+        */
+
+       rmdp->u.s.status = RX_OWN;  
+
+       rmdp->blen = -PKT_BUF_SZ;    /* Buffer Size in a two's comliment */
+
+       rmdp->mlen = 0;              /* init message length */       
+       
+    }
+
+    /* Fill LANCE Initialize Block */
+
+    (p->ram)->ib.mode = mode;        /* Set operation mode */
+
+    for (i = 0; i < ETH_ALEN; i++)   /* Set physical address */
+    {
+       (p->ram)->ib.paddr[i] = dev->dev_addr[i]; 
+    }
+
+    for (i = 0; i < 8; i++)          /* Set multicast, logical adress */
+    {
+       (p->ram)->ib.laddr[i] = 0;   /* We do not use logical addressing */
+    } 
+
+    /* Set ring descriptor pointers and set number of descriptors */
+
+    (p->ram)->ib.rdrp = (int)  p->rmdhead | RMDNUMMASK;
+    (p->ram)->ib.tdrp = (int)  p->tmdhead | TMDNUMMASK;
+
+    /* Prepare LANCE Controll and Status Registers */
+
+    cli();
+
+    SK_write_reg(CSR3, CSR3_ACON);   /* Ale Control !!!THIS MUST BE SET!!!! */
+    /* 
+     * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to
+     * PC Memory locations.
+     *
+     * In structure SK_ram is defined that the first thing in ram
+     * is the initalization block. So his address is for LANCE always
+     * 0x0000
+     *
+     * CSR1 contains low order bits 15:0 of initialization block address
+     * CSR2 is built of: 
+     *    7:0  High order bits 23:16 of initialization block address
+     *   15:8  reserved, must be 0
+     */
+    
+    /* Set initialization block address (must be on word boundary) */
+    SK_write_reg(CSR1, 0);          /* Set low order bits 15:0 */
+    SK_write_reg(CSR2, 0);          /* Set high order bits 23:16 */ 
+    
+
+    PRINTK(("## %s: After setting CSR1-3. CSR0: %#06x\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+    /* Initialize LANCE */
+
+    /* 
+     * INIT = Initialize, when set, cuases the LANCE to begin the
+     * initialization procedure and access the Init Block.
+     */
+
+    SK_write_reg(CSR0, CSR0_INIT); 
+
+    sti();
+
+    /* Wait until LANCE finished initialization */
+    
+    SK_set_RAP(CSR0);              /* Register Address Pointer to CSR0 */
+
+    for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++) 
+       ; /* Wait until init done or go ahead if problems (i>=100) */
+
+    if (i >= 100) /* Something is wrong ! */
+    {
+       printk("%s: can't init am7990, status: %04x "
+              "init_block: %#08x\n", 
+               dev->name, (int) SK_read_reg(CSR0), 
+               (unsigned int) &(p->ram)->ib);
+
+#ifdef SK_DEBUG
+       SK_print_pos(dev, "LANCE INIT failed");
+       SK_print_dev(dev,"Device Structure:");
+#endif
+
+       return -1;                 /* LANCE init failed */
+    }
+
+    PRINTK(("## %s: init done after %d ticks\n", SK_NAME, i));
+
+    /* Clear Initialize done, enable Interrupts, start LANCE */
+
+    SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT);
+
+    PRINTK(("## %s: LANCE started. CSR0: %#06x\n", SK_NAME, 
+            SK_read_reg(CSR0)));
+
+    return 0;                      /* LANCE is up and running */
+
+} /* End of SK_lance_init() */
+
+
+\f
+/*-
+ * Function       : SK_send_packet
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/27
+ *
+ * Description    : Writes an socket buffer into a transmit descriptor
+ *                  and starts transmission.
+ *
+ * Parameters     : I : struct sk_buff *skb - packet to transfer
+ *                  I : struct device *dev  - SK_G16 device structure
+ * Return Value   : 0 - OK
+ *                  1 - Could not transmit (dev_queue_xmit will queue it)
+ *                      and try to sent it later
+ * Globals        : None
+ * Side Effects   : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static int SK_send_packet(struct sk_buff *skb, struct device *dev)
+{
+    struct priv *p = (struct priv *) dev->priv;
+    struct tmd *tmdp;
+
+    if (dev->tbusy)
+    {
+       /* if Transmitter more than 150ms busy -> time_out */
+
+       int tickssofar = jiffies - dev->trans_start;
+       if (tickssofar < 15)
+       {
+           return 1;                    /* We have to try transmit later */
+       }
+
+       printk("%s: xmitter timed out, try to restart!\n", dev->name);
+
+       SK_lance_init(dev, MODE_NORMAL); /* Reinit LANCE */
+
+       dev->tbusy = 0;                  /* Clear Transmitter flag */
+
+       dev->trans_start = jiffies;      /* Mark Start of transmission */
+
+    }
+
+    /* 
+     * If some upper Layer thinks we missed a transmit done interrupt
+     * we are passed NULL.
+     * (dev_queue_xmit net/inet/dev.c 
+     */
+
+    if (skb == NULL)
+    {
+        /* 
+         * Dequeue packets from transmit queue and send them.
+         */
+       dev_tint(dev); 
+                    
+       return 0;
+    }
+    
+    PRINTK2(("## %s: SK_send_packet() called, CSR0 %#04x.\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+
+    /* 
+     * Block a timer-based transmit from overlapping. 
+     * This means check if we are already in. 
+     */
+
+    if (set_bit(0, (void *) &dev->tbusy) != 0) /* dev->tbusy already set ? */ 
+    {
+       printk("%s: Transmitter access conflict.\n", dev->name);
+    }
+    else
+    {
+       /* Evaluate Packet length */
+       short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 
+       
+       tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */
+
+       /* Fill in Transmit Message Descriptor */
+
+       /* Copy data into dual ported ram */
+
+       memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data,
+              skb->len);
+
+       tmdp->blen = -len;            /* set length to transmit */
+
+       /* 
+        * Packet start and end is always set because we use the maximum
+        * packet length as buffer length.
+        * Relinquish ownership to LANCE
+        */
+
+       tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP;
+       
+       /* Start Demand Transmission */
+       SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA);
+
+       dev->trans_start = jiffies;   /* Mark start of transmission */
+
+       /* Set pointer to next transmit buffer */
+       p->tmdnum++; 
+       p->tmdnum &= TMDNUM-1; 
+
+       /* Do we own the next transmit buffer ? */
+       if (! ((p->tmdhead + p->tmdnum)->u.s.status & TX_OWN) )
+       {
+          /* 
+           * We own next buffer and are ready to transmit, so
+           * clear busy flag
+           */
+          dev->tbusy = 0;
+       }
+    }
+    dev_kfree_skb(skb, FREE_WRITE);
+    return 0;  
+} /* End of SK_send_packet */
+
+\f
+/*-
+ * Function       : SK_interrupt
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/27
+ *
+ * Description    : SK_G16 interrupt handler which checks for LANCE
+ *                  Errors, handles transmit and receive interrupts
+ *
+ * Parameters     : I : int reg_ptr -
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : None
+ * Side Effects   : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static void SK_interrupt(int reg_ptr)
+{
+    int irq = - (((struct pt_regs *)reg_ptr)->orig_eax+2);
+    int csr0;
+    struct device *dev = (struct device *) irq2dev_map[irq];
+    struct priv *p = (struct priv *) dev->priv;
+
+
+    PRINTK2(("## %s: SK_interrupt(). status: %#06x\n", 
+            SK_NAME, SK_read_reg(CSR0)));
+
+    if (dev == NULL)
+    {
+       printk("SK_interrupt(): IRQ %d for unknown device.\n", irq);
+    }
+    
+
+    if (dev->interrupt)
+    {
+       printk("%s: Re-entering the interrupt handler.\n", dev->name);
+    }
+
+    csr0 = SK_read_reg(CSR0);      /* store register for checking */
+
+    dev->interrupt = 1;            /* We are handling an interrupt */
+
+    /* 
+     * Acknowledge all of the current interrupt sources, disable      
+     * Interrupts (INEA = 0) 
+     */
+
+    SK_write_reg(CSR0, csr0 & CSR0_CLRALL); 
+
+    if (csr0 & CSR0_ERR) /* LANCE Error */
+    {
+       printk("%s: error: %04x\n", dev->name, csr0);
+      
+        if (csr0 & CSR0_MISS)      /* No place to store packet ? */
+        { 
+            p->stats.rx_dropped++;
+        }
+    }
+
+    if (csr0 & CSR0_RINT)          /* Receive Interrupt (packet arrived) */ 
+    {
+       SK_rxintr(dev); 
+    }
+
+    if (csr0 & CSR0_TINT)          /* Transmit interrupt (packet sent) */
+    {
+       SK_txintr(dev);
+    }
+
+    SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+
+    dev->interrupt = 0;            /* We are out */
+} /* End of SK_interrupt() */ 
+
+\f
+/*-
+ * Function       : SK_txintr
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/27
+ *
+ * Description    : After sending a packet we check status, update
+ *                  statistics and relinquish ownership of transmit 
+ *                  descriptor ring.
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static void SK_txintr(struct device *dev)
+{
+    int tmdstat;
+    struct tmd *tmdp;
+    struct priv *p = (struct priv *) dev->priv;
+
+
+    PRINTK2(("## %s: SK_txintr() status: %#06x\n", 
+            SK_NAME, SK_read_reg(CSR0)));
+
+    tmdp = p->tmdhead + p->tmdlast;     /* Which buffer we sent at last ? */
+
+    /* Set next buffer */
+    p->tmdlast++;
+    p->tmdlast &= TMDNUM-1;
+
+    tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */
+
+    /* 
+     * We check status of transmitted packet.
+     * see LANCE data-sheet for error explanation
+     */
+    if (tmdstat & TX_ERR) /* Error occured */
+    {
+       printk("%s: TX error: %04x %04x\n", dev->name, (int) tmdstat,
+               (int) tmdp->status2);
+
+       if (tmdp->status2 & TX_TDR)    /* TDR problems? */
+       {
+           printk("%s: tdr-problems \n", dev->name);
+       }
+
+       if (tmdp->status2 & TX_RTRY)   /* Failed in 16 attempts to transmit ? */
+            p->stats.tx_aborted_errors++;   
+        if (tmdp->status2 & TX_LCOL)   /* Late collision ? */
+            p->stats.tx_window_errors++; 
+       if (tmdp->status2 & TX_LCAR)   /* Loss of Carrier ? */  
+            p->stats.tx_carrier_errors++;
+        if (tmdp->status2 & TX_UFLO)   /* Underflow error ? */
+        {
+            p->stats.tx_fifo_errors++;
+
+            /* 
+             * If UFLO error occurs it will turn tranmitter of.
+             * So we must reinit LANCE
+             */
+
+            SK_lance_init(dev, MODE_NORMAL);
+        }
+       
+       p->stats.tx_errors++;
+
+       tmdp->status2 = 0;             /* Clear error flags */
+    }
+    else if (tmdstat & TX_MORE)        /* Collisions occured ? */
+    {
+        /* 
+         * Here I have a problem.
+         * I only know that there must be one or up to 15 collisions.
+         * Thats why TX_MORE is set, because after 16 attempts TX_RTRY
+         * will be set which means couldn't send packet aborted transfer.
+         *
+         * First I did not have this in but then I thought at minimum
+         * we see that something was not ok.
+         * If anyone knows something better than this to handle this
+         * please report it. (see Email addresses in the README file)
+         */ 
+
+        p->stats.collisions++; 
+    }
+    else   /* Packet sent without any problems */
+    {
+        p->stats.tx_packets++; 
+    }
+
+    /* 
+     * We mark transmitter not busy anymore, because now we have a free
+     * transmit descriptor which can be filled by SK_send_packet and
+     * afterwards sent by the LANCE
+     */
+
+    dev->tbusy = 0; 
+
+    /* 
+     * mark_bh(NET_BH);
+     * This will cause net_bh() to run after this interrupt handler.
+     *
+     * The function which do handle slow IRQ parts is do_bottom_half()
+     * which runs at normal kernel priority, that means all interrupt are
+     * enabled. (see kernel/irq.c)
+     *  
+     * net_bh does something like this:
+     *  - check if already in net_bh
+     *  - try to transmit something from the send queue
+     *  - if something is in the receive queue send it up to higher 
+     *    levels if it is a known protocol
+     *  - try to transmit something from the send queue
+     */
+
+    mark_bh(NET_BH); 
+
+} /* End of SK_txintr() */
+
+\f
+/*-
+ * Function       : SK_rxintr
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/27
+ *
+ * Description    : Buffer sent, check for errors, relinquish ownership
+ *                  of the receive message descriptor. 
+ *
+ * Parameters     : I : SK_G16 device structure
+ * Return Value   : None
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static void SK_rxintr(struct device *dev)
+{
+
+    struct rmd *rmdp;
+    int rmdstat;
+    struct priv *p = (struct priv *) dev->priv;
+
+    PRINTK2(("## %s: SK_rxintr(). CSR0: %#06x\n", 
+            SK_NAME, SK_read_reg(CSR0)));
+
+    rmdp = p->rmdhead + p->rmdnum;
+
+    /* As long as we own the next entry, check status and send
+     * it up to higher layer 
+     */
+
+    while (!( (rmdstat = rmdp->u.s.status) & RX_OWN))
+    {
+       /* 
+         * Start and end of packet must be set, because we use 
+        * the ethernet maximum packet length (1518) as buffer size.
+        * 
+        * Because our buffers are at maximum OFLO and BUFF errors are
+        * not to be concerned (see Data sheet)
+        */
+
+       if (rmdstat & (RX_STP | RX_ENP) != (RX_STP | RX_ENP))
+       {
+           /* Start of a frame > 1518 Bytes ? */
+
+           if (rmdstat & RX_STP) 
+           {
+               p->stats.rx_errors++;        /* bad packet received */
+               p->stats.rx_length_errors++; /* packet to long */
+
+               printk("%s: packet too long\n", dev->name);
+           }
+           
+           /* 
+             * All other packets will be ignored until a new frame with
+            * start (RX_STP) set follows.
+            * 
+            * What we do is just give descriptor free for new incoming
+            * packets. 
+            */
+
+           rmdp->u.s.status = RX_OWN;      /* Relinquish ownership to LANCE */ 
+
+       }
+       else if (rmdstat & RX_ERR)          /* Receive Error ? */
+       {
+           printk("%s: RX error: %04x\n", dev->name, (int) rmdstat);
+           
+           p->stats.rx_errors++;
+
+           if (rmdstat & RX_FRAM) p->stats.rx_frame_errors++;
+           if (rmdstat & RX_CRC)  p->stats.rx_crc_errors++;
+
+           rmdp->u.s.status = RX_OWN;      /* Relinquish ownership to LANCE */
+
+       }
+       else /* We have a packet which can be queued for the upper layers */
+       {
+
+           int len = (rmdp->mlen & 0x0fff);  /* extract message length from receive buffer */
+           struct sk_buff *skb;
+
+           skb = alloc_skb(len, GFP_ATOMIC); /* allocate socket buffer */ 
+
+           if (skb == NULL)                /* Could not get mem ? */
+           {
+    
+               /* 
+                 * Couldn't allocate sk_buffer so we give descriptor back
+                * to Lance, update statistics and go ahead.
+                */
+
+               rmdp->u.s.status = RX_OWN;  /* Reliquish ownershipt to LANCE */
+               printk("%s: Couldn't allocate sk_buff, deferring packet.\n",
+                      dev->name);
+               p->stats.rx_dropped++;
+
+               break;                      /* Jump out */
+           }
+           
+           /* Prepare sk_buff to queue for upper layers */
+
+            skb->len = len;
+           skb->dev = dev;
+           
+           /* 
+             * Copy data out of our receive descriptor into sk_buff.
+            *
+            * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and 
+            * ignore status fields) 
+            */
+
+           memcpy(skb->data, (unsigned char *) (rmdp->u.buffer & 0x00ffffff),
+                  len);
+
+
+           /* 
+             * Notify the upper protocol layers that there is another packet
+            * to handle
+            *
+            * netif_rx() always succeeds. see /net/inet/dev.c for more.
+            */
+
+           netif_rx(skb);                 /* queue packet and mark it for processing */
+          
+           /* 
+             * Packet is queued and marked for processing so we
+            * free our descriptor and update statistics 
+            */
+
+           rmdp->u.s.status = RX_OWN;
+           p->stats.rx_packets++;
+
+
+           p->rmdnum++;
+           p->rmdnum %= RMDNUM;
+
+           rmdp = p->rmdhead + p->rmdnum;
+       }
+    }
+} /* End of SK_rxintr() */
+
+\f
+/*-
+ * Function       : SK_close
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : close gets called from dev_close() and should
+ *                  deinstall the card (free_irq, mem etc).
+ *
+ * Parameters     : I : struct device *dev - our device structure
+ * Return Value   : 0 - closed device driver
+ * Errors         : None
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+/* I have tried to set BOOT_ROM on and RAM off but then, after a 'ifconfig
+ * down' the system stops. So I don't shut set card to init state.
+ */
+
+static int SK_close(struct device *dev)
+{
+
+    PRINTK(("## %s: SK_close(). CSR0: %#06x\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+    dev->tbusy = 1;                /* Transmitter busy */
+    dev->start = 0;                /* Card down */
+
+    printk("%s: Shuting %s down CSR0 %#06x\n", dev->name, SK_NAME, 
+           (int) SK_read_reg(CSR0));
+
+    SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
+
+    free_irq(dev->irq);            /* Free IRQ */
+    irq2dev_map[dev->irq] = 0;     /* Mark IRQ as unused */
+
+    return 0; /* always succed */
+    
+} /* End of SK_close() */
+
+\f
+/*-
+ * Function       : SK_get_stats
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : Return current status structure to upper layers.
+ *                  It is called by sprintf_stats (dev.c).
+ *
+ * Parameters     : I : struct device *dev   - our device structure
+ * Return Value   : struct enet_statistics * - our current statistics
+ * Errors         : None
+ * Side Effects   : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+static struct enet_statistics *SK_get_stats(struct device *dev)
+{
+
+    struct priv *p = (struct priv *) dev->priv;
+
+    PRINTK(("## %s: SK_get_stats(). CSR0: %#06x\n", 
+           SK_NAME, SK_read_reg(CSR0)));
+
+    return &p->stats;             /* Return Device status */
+
+} /* End of SK_get_stats() */
+
+#ifdef HAVE_MULTICAST
+\f
+/*-
+ * Function       : set_multicast_list
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/26
+ *
+ * Description    : This function gets called when a programm performs
+ *                  a SIOCSIFFLAGS call. Ifconfig does this if you call
+ *                  'ifconfig [-]allmultie' which enables or disables the
+ *                  Promiscous mode.
+ *                  Promiscous mode is when the Network card accepts all
+ *                  packets, not only the packets which match our MAC 
+ *                  Address. It is useful for writing a network monitor,
+ *                  but it is also a security problem. You have to remeber
+ *                  that all information on the net is not encrypted.
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device Structure
+ *                  I : int num_addrs      - explanation further down
+ *                  I : void *addrs        - 
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+
+/* Set or clear the multicast filter for SK_G16.
+ *
+ * num_addrs == -1      Promiscous mode, receive all packets
+ * num_addrs == 0       Normal mode, clear multicast list
+ * num_addrs > 0        Multicast mode, receive normal and MC packets
+ */
+
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+
+    if (num_addrs == -1)
+    {
+       /* Reinitialize LANCE with MODE_PROM set */
+       SK_lance_init(dev, MODE_PROM);
+    }
+    else if (num_addrs == 0)
+    {
+       /* Reinitialize LANCE without MODE_PROM */
+       SK_lance_init(dev, MODE_NORMAL);
+    }
+    else
+    {
+       /* Multicast with logical address filter on */
+       
+       /* Not implemented yet. */
+    }
+} /* End of set_multicast_list() */
+
+#endif     
+
+
+\f
+/*-
+ * Function       : SK_rom_addr
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/06/01
+ *
+ * Description    : Try to find a Boot_ROM at all possible locations
+ *
+ * Parameters     : None
+ * Return Value   : Address where Boot_ROM is
+ * Errors         : 0 - Did not find Boot_ROM
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+unsigned int SK_rom_addr(void)
+{
+    int i,j;
+    int rom_found = 0;
+    unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS;
+    unsigned char rom_id[] = SK_BOOT_ROM_ID;
+    unsigned char *test_byte;
+
+    /* Autodetect Boot_ROM */
+    PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME));
+
+    for (i = 0; (rom_location[i] != 0) && (rom_found == 0); i++)
+    {
+       
+       PRINTK(("##   Trying ROM location %#08x", rom_location[i]));
+       
+       rom_found = 1; 
+       for (j = 0; j < 6; j++)
+       {
+           test_byte = (unsigned char *) (rom_location[i]+j);
+           PRINTK((" %02x ", *test_byte));
+
+           if(!(*test_byte == rom_id[j]))
+           {
+               rom_found = 0;
+           } 
+       }
+       PRINTK(("\n"));
+    }
+
+    if (rom_found == 1)
+    {
+       PRINTK(("## %s: Boot_ROM found at %#08x\n", 
+               SK_NAME, rom_location[(i-1)]));
+
+       return (rom_location[--i]);
+    }
+    else
+    {
+       PRINTK(("%s: No Boot_ROM found\n", SK_NAME));
+       return 0;
+    }
+} /* End of SK_rom_addr() */
+
+
+\f
+/* LANCE access functions 
+ *
+ * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set !
+ */
+
+
+/*-
+ * Function       : SK_reset_board
+ *
+ * Author         : Patrick J.D. Weichmann
+ *
+ * Date Created   : 94/05/25
+ *
+ * Description    : This function resets SK_G16 and all components, but
+ *                  POS registers are not changed
+ *
+ * Parameters     : None
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : SK_RAM *board - SK_RAM structure pointer
+ *
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_reset_board(void)
+{
+    int i;
+
+    SK_PORT = 0x00;           /* Reset aktiv */
+    for (i = 0; i < 10 ; i++) /* Delay min 5ms */
+       ;
+    SK_PORT = SK_RESET;       /* Set back to normal operation */
+
+} /* End of SK_reset_board() */
+
+\f
+/*-
+ * Function       : SK_set_RAP
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/25
+ *
+ * Description    : Set LANCE Register Address Port to register
+ *                  for later data trasfer.
+ *
+ * Parameters     : I : reg_number - which CSR to read/write from/to
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_set_RAP(int reg_number)
+{
+    SK_IOREG = reg_number;
+    SK_PORT  = SK_RESET | SK_RAP | SK_WREG;
+    SK_IOCOM = SK_DOIO;
+
+    while (SK_PORT & SK_IORUN) 
+       ;
+} /* End of SK_set_RAP() */
+
+\f
+/*-
+ * Function       : SK_read_reg
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/25
+ *
+ * Description    : Set RAP and read data from a LANCE CSR register
+ *
+ * Parameters     : I : reg_number - which CSR to read from
+ * Return Value   : Register contents
+ * Errors         : None
+ * Globals        : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+int SK_read_reg(int reg_number)
+{
+    SK_set_RAP(reg_number);
+
+    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG;
+    SK_IOCOM = SK_DOIO;
+
+    while (SK_PORT & SK_IORUN)
+       ;
+    return (SK_IOREG);
+
+} /* End of SK_read_reg() */
+
+\f
+/*-
+ * Function       : SK_rread_reg
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/28
+ *
+ * Description    : Read data from preseted register.
+ *                  This function requires that you know which
+ *                  Register is actually set. Be aware that CSR1-3
+ *                  can only be accessed when in CSR0 STOP is set.
+ *
+ * Return Value   : Register contents
+ * Errors         : None
+ * Globals        : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+int SK_rread_reg(void)
+{
+    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG;
+
+    SK_IOCOM = SK_DOIO;
+
+    while (SK_PORT & SK_IORUN)
+       ;
+    return (SK_IOREG);
+
+} /* End of SK_rread_reg() */
+
+\f
+/*-
+ * Function       : SK_write_reg
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/25
+ *
+ * Description    : This function sets the RAP then fills in the
+ *                  LANCE I/O Reg and starts Transfer to LANCE.
+ *                  It waits until transfer has ended which is max. 7 ms
+ *                  and then it returns.
+ *
+ * Parameters     : I : reg_number - which CSR to write to
+ *                  I : value      - what value to fill into register 
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_write_reg(int reg_number, int value)
+{
+    SK_set_RAP(reg_number);
+
+    SK_IOREG = value;
+    SK_PORT  = SK_RESET | SK_RDATA | SK_WREG;
+    SK_IOCOM = SK_DOIO;
+
+    while (SK_PORT & SK_IORUN)
+       ;
+} /* End of SK_write_reg */
+
+\f
+
+/* 
+ * Debugging functions
+ * -------------------
+ */
+
+/*-
+ * Function       : SK_print_pos
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/25
+ *
+ * Description    : This function prints out the 4 POS (Programmable
+ *                  Option Select) Registers. Used mainly to debug operation.
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ *                  I : char * - Text which will be printed as title
+ * Return Value   : None
+ * Errors         : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_print_pos(struct device *dev, char *text)
+{
+    int ioaddr = dev->base_addr;
+
+    unsigned char pos0 = inb(SK_POS0),
+                 pos1 = inb(SK_POS1),
+                 pos2 = inb(SK_POS2),
+                 pos3 = inb(SK_POS3),
+                 pos4 = inb(SK_POS4);
+
+
+    printk("## %s: %s.\n"
+           "##   pos0=%#4x pos1=%#4x pos2=%#04x pos3=%#08x pos4=%#04x\n",
+           SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4);
+
+} /* End of SK_print_pos() */
+
+
+\f
+/*-
+ * Function       : SK_print_dev
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/05/25
+ *
+ * Description    : This function simply prints out the important fields
+ *                  of the device structure.
+ *
+ * Parameters     : I : struct device *dev  - SK_G16 device structure
+ *                  I : char *text - Title for printing
+ * Return Value   : None
+ * Errors         : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_print_dev(struct device *dev, char *text)
+{
+    if (dev == NULL)
+    {
+       printk("## %s: Device Structure. %s\n", SK_NAME, text);
+       printk("## DEVICE == NULL\n");
+    }
+    else
+    {
+       printk("## %s: Device Structure. %s\n", SK_NAME, text);
+       printk("## Device Name: %s Base Address: %#06x IRQ: %d\n", 
+               dev->name, dev->base_addr, dev->irq);
+              
+       printk("##   FLAGS: start: %d tbusy: %d int: %d\n", 
+               dev->start, dev->tbusy, dev->interrupt);
+
+       printk("## next device: %#08x init function: %#08x\n", 
+              (int) dev->next, (int) dev->init);
+    }
+
+} /* End of SK_print_dev() */
+
+
+\f
+/*-
+ * Function       : SK_print_ram
+ * Author         : Patrick J.D. Weichmann
+ * Date Created   : 94/06/02
+ *
+ * Description    : This function is used to check how are things set up
+ *                  in the 16KB RAM. Also the pointers to the receive and 
+ *                  transmit descriptor rings and rx und tx buffers locations.
+ *                  It contains a minor bug in printing, but has no effect to the values
+ *                  only newlines are not correct.
+ *
+ * Parameters     : I : struct device *dev - SK_G16 device structure
+ * Return Value   : None
+ * Errors         : None
+ * Globals        : None
+ * Update History :
+ *     YY/MM/DD  uid  Description
+-*/
+
+void SK_print_ram(struct device *dev)
+{
+
+    int i;    
+    struct priv *p = (struct priv *) dev->priv;
+
+    printk("## %s: RAM Details.\n"
+           "##   RAM at %#08x tmdhead: %#08x rmdhead: %#08x initblock: %#08x\n",
+           SK_NAME, 
+           (unsigned int) p->ram,
+           (unsigned int) p->tmdhead, 
+           (unsigned int) p->rmdhead, 
+           (unsigned int) &(p->ram)->ib);
+           
+    printk("##   ");
+
+    for(i = 0; i < TMDNUM; i++)
+    {
+           if (!(i % 3)) /* Every third line do a newline */
+           {
+               printk("\n##   ");
+           }
+        printk("tmdbufs%d: %#08x ", (i+1), (int) p->tmdbufs[i]);
+    }
+    printk("##   ");
+
+    for(i = 0; i < RMDNUM; i++)
+    {
+         if (!(i % 3)) /* Every third line do a newline */
+           {
+               printk("\n##   ");
+           }
+        printk("rmdbufs%d: %#08x ", (i+1), (int) p->rmdbufs[i]);
+    } 
+    printk("\n");
+
+} /* End of SK_print_ram() */
+
diff --git a/drivers/net/sk_g16.h b/drivers/net/sk_g16.h
new file mode 100644 (file)
index 0000000..96b70f9
--- /dev/null
@@ -0,0 +1,165 @@
+/*-
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module         : sk_g16.h
+ * Version        : $Revision$  
+ *
+ * Author         : M.Hipp (mhipp@student.uni-tuebingen.de)
+ * changes by     : Patrick J.D. Weichmann
+ *
+ * Date Created   : 94/05/25
+ *
+ * Description    : In here are all necessary definitions of  
+ *                  the am7990 (LANCE) chip used for writing a
+ *                  network device driver which uses this chip 
+ *
+ * $Log$
+-*/
+
+#ifndef SK_G16_H
+
+#define SK_G16_H
+
+
+/*
+ *     Control and Status Register 0 (CSR0) bit definitions
+ *
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR       0x8000  /* Error summary (R) */
+#define CSR0_BABL      0x4000  /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR      0x2000  /* Collision Error (RC) */
+#define CSR0_MISS      0x1000  /* Missed packet (RC) */
+#define CSR0_MERR      0x0800  /* Memory Error  (RC) */ 
+#define CSR0_RINT      0x0400  /* Reciever Interrupt (RC) */
+#define CSR0_TINT       0x0200 /* Transmit Interrupt (RC) */ 
+#define CSR0_IDON      0x0100  /* Initialization Done (RC) */
+#define CSR0_INTR      0x0080  /* Interrupt Flag (R) */
+#define CSR0_INEA      0x0040  /* Interrupt Enable (RW) */
+#define CSR0_RXON      0x0020  /* Receiver on (R) */
+#define CSR0_TXON      0x0010  /* Transmitter on (R) */
+#define CSR0_TDMD      0x0008  /* Transmit Demand (RS) */
+#define CSR0_STOP      0x0004  /* Stop (RS) */
+#define CSR0_STRT      0x0002  /* Start (RS) */
+#define CSR0_INIT      0x0001  /* Initialize (RS) */
+
+#define CSR0_CLRALL     0x7f00  /* mask for all clearable bits */
+
+/*
+ *    Control and Status Register 3 (CSR3) bit definitions
+ *
+ */
+
+#define CSR3_BSWAP     0x0004  /* Byte Swap (RW) */
+#define CSR3_ACON      0x0002  /* ALE Control (RW) */
+#define CSR3_BCON      0x0001  /* Byte Control (RW) */
+
+/*
+ *     Initialization Block Mode operation Bit Definitions.
+ */
+
+#define MODE_PROM      0x8000  /* Promiscuous Mode */
+#define MODE_INTL      0x0040  /* Internal Loopback */
+#define MODE_DRTY      0x0020  /* Disable Retry */ 
+#define MODE_COLL      0x0010  /* Force Collision */
+#define MODE_DTCR      0x0008  /* Disable Transmit CRC) */
+#define MODE_LOOP      0x0004  /* Loopback */
+#define MODE_DTX       0x0002  /* Disable the Transmitter */ 
+#define MODE_DRX       0x0001  /* Disable the Reciever */
+
+#define MODE_NORMAL    0x0000  /* Normal operation mode */
+
+/*
+ *     Receive message descriptor status bit definitions.
+ */
+
+#define RX_OWN         0x80    /* Owner bit 0 = host, 1 = lance */
+#define RX_ERR         0x40    /* Error Summary */
+#define RX_FRAM                0x20    /* Framing Error */
+#define RX_OFLO                0x10    /* Overflow Error */
+#define RX_CRC         0x08    /* CRC Error */ 
+#define RX_BUFF                0x04    /* Buffer Error */
+#define RX_STP         0x02    /* Start of Packet */
+#define RX_ENP         0x01    /* End of Packet */
+
+
+/*
+ *     Transmit message descriptor status bit definitions.
+ */
+
+#define TX_OWN         0x80    /* Owner bit 0 = host, 1 = lance */
+#define TX_ERR         0x40    /* Error Summary */
+#define TX_MORE                0x10    /* More the 1 retry needed to Xmit */
+#define TX_ONE         0x08    /* One retry needed to Xmit */
+#define TX_DEF         0x04    /* Deferred */
+#define TX_STP                 0x02    /* Start of Packet */
+#define TX_ENP         0x01    /* End of Packet */
+
+/*
+ *      Transmit status (2) (valid if TX_ERR == 1)
+ */
+
+#define TX_BUFF        0x8000  /* Buffering error (no ENP) */
+#define TX_UFLO        0x4000  /* Underflow (late memory) */
+#define TX_LCOL        0x1000  /* Late collision */
+#define TX_LCAR        0x0400  /* Loss of Carrier */
+#define TX_RTRY        0x0200  /* Failed after 16 retransmissions  */
+#define TX_TDR          0x003f  /* Time-domain-reflectometer-value */
+
+
+/* 
+ * Structures used for Communication with the LANCE 
+ */
+
+/* LANCE Initialize Block */
+
+struct init_block 
+{
+  unsigned short mode;     /* Mode Register */
+  unsigned char  paddr[6]; /* Physical Address (MAC) */
+  unsigned char  laddr[8]; /* Logical Filter Address (not used) */
+  unsigned int   rdrp;     /* Receive Descriptor Ring pointer */
+  unsigned int   tdrp;     /* Transmit Descriptor Ring pointer */
+};
+
+
+/* Receive Message Descriptor Entry */
+
+struct rmd 
+{ 
+  union
+  {
+    unsigned long buffer;     /* Address of buffer */
+    struct 
+    {
+      unsigned char unused[3]; 
+      unsigned volatile char status;   /* Status Bits */
+    } s;
+  } u;
+  volatile short blen;        /* Buffer Length (two's complement) */
+  unsigned short mlen;        /* Message Byte Count */
+};
+
+
+/* Transmit Message Descriptor Entry */
+
+struct tmd   
+{
+  union 
+  {
+    unsigned long  buffer;    /* Address of buffer */
+    struct 
+    {
+      unsigned char unused[3];
+      unsigned volatile char status;   /* Status Bits */
+    } s;
+  } u;
+  unsigned short blen;             /* Buffer Length (two's complement) */
+  unsigned volatile short status2; /* Error Status Bits */
+};
+
+#endif /* End of SK_G16_H */
index a1f8048000f9956436b154ea65832e2d447b89f6..373502828cba5a1779ecb56ece34bd9971d5a6d6 100644 (file)
@@ -36,6 +36,8 @@
  *                     allow zero or one slots
  *                     separate routines
  *                     status display
+ *     - Jul 1994      Dmitry Gorodchanin
+ *                     Fixes for memory leaks.
  *
  *
  *     This module is a difficult issue. Its clearly inet code but its also clearly
@@ -101,7 +103,10 @@ slhc_init(int rslots, int tslots)
                  (struct cstate *)kmalloc(rslots * sizeof(struct cstate),
                                           GFP_KERNEL);
                if (! comp->rstate)
+               {
+                       kfree((unsigned char *)comp);
                        return NULL;
+               }
                memset(comp->rstate, 0, rslots * sizeof(struct cstate));
                comp->rslot_limit = rslots - 1;
        }
@@ -111,7 +116,11 @@ slhc_init(int rslots, int tslots)
                  (struct cstate *)kmalloc(tslots * sizeof(struct cstate),
                                           GFP_KERNEL);
                if (! comp->tstate)
+               {
+                       kfree((unsigned char *)comp->rstate);
+                       kfree((unsigned char *)comp);
                        return NULL;
+               }
                memset(comp->tstate, 0, rslots * sizeof(struct cstate));
                comp->tslot_limit = tslots - 1;
        }
index 157de64dff97923527cc225f450d2bc5c82e1c52..0237b5694d127cc0293ebc044a4cf01e2dfbb29a 100644 (file)
@@ -23,6 +23,8 @@
  *             Alan Cox        :       Corrected non-IP cases of the above.
  *             Alan Cox        :       Now uses hardware type as per FvK.
  *             Alan Cox        :       Default to 192.168.0.0 (RFC 1597)
+ *             A.N.Kuznetsov   :       dev_tint() recursion fix.
+ *     Dmitry Gorodchanin      :       SLIP memory leaks
  *
  *
  *     FIXME:  This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
@@ -379,7 +381,7 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len)
   actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
   if (actual == count) {
          sl_unlock(sl);
-         dev_tint(sl->dev);
+         mark_bh(NET_BH);
   } else {
          sl->xhead = sl->xbuff + count;
          sl->xtail = sl->xbuff + actual;
@@ -420,7 +422,7 @@ static void slip_write_wakeup(struct tty_struct *tty)
                tty->flags &= ~TTY_DO_WRITE_WAKEUP;
 
                sl_unlock(sl);
-               dev_tint(sl->dev);
+               mark_bh(NET_BH);
        } else {
                sl->xtail += answer;
        }
@@ -566,7 +568,7 @@ sl_open(struct device *dev)
 
   p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
   if (p == NULL) {
-       kfree_s((void *)sl->dev->mem_start,l+4);
+       kfree((unsigned char *)sl->dev->mem_start);
        return(-ENOMEM);
   }
   sl->dev->rmem_start  = (unsigned long) p;
@@ -584,6 +586,7 @@ sl_open(struct device *dev)
   p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
   if (p == NULL) {
        kfree((unsigned char *)sl->dev->mem_start);
+       kfree((unsigned char *)sl->dev->rmem_start);
        return(-ENOMEM);
   }
   sl->cbuff            = p;
@@ -592,7 +595,7 @@ sl_open(struct device *dev)
   if (sl->slcomp == NULL) {
        kfree((unsigned char *)sl->dev->mem_start);
        kfree((unsigned char *)sl->dev->rmem_start);
-       kfree(sl->cbuff);
+       kfree((unsigned char *)sl->cbuff);
        return(-ENOMEM);
   }
 #endif
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
new file mode 100644 (file)
index 0000000..ca79a94
--- /dev/null
@@ -0,0 +1,767 @@
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+static char *version = "znet.c:v1.01 7/1/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 <linux/ioport.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 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 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(KERN_INFO "No Z-Note ethernet adaptor found.\n");
+               return ENODEV;
+       }
+       netinfo = (struct netidblk *)p;
+       dev->base_addr = netinfo->iobase1;
+       dev->irq = netinfo->irq1;
+
+       printk(KERN_INFO "%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(KERN_INFO "%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(KERN_INFO "%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("%s%s", KERN_INFO, 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(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
+               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;
+       dev->set_multicast_list = &set_multicast_list;
+
+       /* Fill in the 'dev' with ethernet-generic values. */
+       ether_setup(dev);
+
+       return 0;
+}
+
+\f
+static int znet_open(struct device *dev)
+{
+       int ioaddr = dev->base_addr;
+
+       if (znet_debug > 2)
+               printk(KERN_DEBUG "%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(KERN_WARNING "%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(KERN_DEBUG "%s: ZNet_send_packet(%d).\n", dev->name, dev->tbusy);
+
+       /* Transmitter timeout, likely just recovery after suspending the machine. */
+       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(KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
+                          " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+               if (tx_status == 0x0400)
+                 printk(KERN_WARNING "%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;
+       }
+
+       /* 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(KERN_WARNING "%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(KERN_WARNING "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(KERN_DEBUG "%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(KERN_WARNING "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(KERN_DEBUG "%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(NET_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(KERN_WARNING "%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(KERN_DEBUG "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(KERN_DEBUG "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(KERN_WARNING "%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(KERN_DEBUG "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(KERN_DEBUG "%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(KERN_DEBUG "%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;
+}
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+       outb(CMD0_PORT_1, ioaddr);
+       if (znet_debug > 5)
+         printk(KERN_DEBUG "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 2b21a20a4b7659fb9b106cd9564b2ea5b9224409..521570414a6f428270886a7e17ec2db327bd332c 100644 (file)
@@ -1,5 +1,46 @@
+Wed Jul  6 05:45:02 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.25 released.
+
+       * sd.c, sr.c: Use new check_media_change and revalidate
+       file_operations fields.
+
+       * st.c, st.h: Add changes from Kai Makisara, dated Jun 22.
+
+       * hosts.h: Change SG_ALL back to 0xff.  Apparently soft error
+       in /dev/brain resulted in having this bumped up.
+       Change first parameter in bios_param function to be Disk * instead
+       of index into rscsi_disks.
+
+       * sd_ioctl.c: Pass pointer to rscsi_disks element instead of index
+       to array.
+
+       * sd.h: Add struct name "scsi_disk" to typedef for Scsi_Disk.
+
+       * scsi.c: Remove redundant Maxtor XT8760S from blacklist.
+       In scsi_reset, add printk when DEBUG defined.
+
+       * All low level drivers: Modify definitions of bios_param in
+       appropriate way.
+
+Thu Jun 16 10:31:59 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.20 released.
+
+       * scsi_ioctl.c: Only pass down the actual number of characters
+       required to scsi_do_cmd, not the one rounded up to a even number
+       of sectors.
+
+       * ultrastor.c: Changes from Caleb Epstein for 24f cards.  Support
+       larger SG lists.
+
+       * ultrastor.c: Changes from me - use scsi_register to register
+       host.  Add some consistency checking, 
+
 Wed Jun  1 21:12:13 1994  Eric Youngdale  (eric@esp22)
 
+       * Linux 1.1.19 released.
+
        * scsi.h: Add new return code for reset() function:
        SCSI_RESET_PUNT.
 
index c750000b932384c9b38d4d33582524ec143fc5ad..f576038dc0172c5b905a59faa9b6be8d6c060b08 100644 (file)
@@ -1,5 +1,5 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Thu May  5 00:13:24 1994 by root@kai.home
+Last modified: Wed Jun 22 23:38:47 1994 by root@kai.home
 
 BASICS
 
@@ -10,6 +10,9 @@ variable block size (within buffer limits). Both the auto-rewind
 (minor equals device number) and non-rewind devices (minor is 128 +
 device number) are implemented.
 
+By default the driver writes one filemark when the device is closed after
+writing. Two filemarks can be optionally written. In both cases end
+of data is signified by returning zero bytes for two consecutive reads.
 
 BUFFERING
 
@@ -78,12 +81,13 @@ MTBSR   Space backward over count records.
 MTWEOF  Write count filemarks.
 MTREW   Rewind tape.
 MTOFFL  Set device off line (often rewind plus eject).
-MTNOP   Do nothing.
+MTNOP   Do nothing except flush the buffers.
 MTRETEN Retension tape.
 MTEOM   Space to end of recorded data.
 MTERASE Erase tape.
 MTSEEK Seek to tape block count. Uses Tandberg-compatible seek (QFA)
-        for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives.
+        for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives. The file and
+       block numbers in the status are not valid after a seek.
 MTSETBLK Set the drive block size. Setting to zero sets the drive into
         variable block mode (if applicable).
 MTSETDENSITY Sets the drive density code to arg. See drive
@@ -99,8 +103,9 @@ MTSETDRVBUFFER
            Sets the buffering options. The bits are the new states
            (enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES),
            asynchronous writes (MT_ST_ASYNC_WRITES), read ahead
-           (MT_ST_READ_AHEAD) and debugging (MT_ST_DEBUGGING;
-           (debugging must be compiled into the driver).
+           (MT_ST_READ_AHEAD), writing of two filemark (ST_TWO_FM), and
+          debugging (MT_ST_DEBUGGING ; debugging must be compiled into the
+          driver).
         MT_ST_WRITE_THRESHOLD
            Sets the write threshold for this device to kilobytes
            specified by the lowest bits.
@@ -120,6 +125,8 @@ MTIOCGET Returns some status information.
         The current block size and the density code are stored in the field
         mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
         MT_ST_DENSITY_SHIFT).
+       The write protect bit in mt_gstat is set if the tape is write
+       protected.
         The other fields are empty.
 
 
index d1877a7db72e0556edd799551b99fc55c6b46593..8a8a7e4745c6a94aa513be75d6323a458ab2d4e7 100644 (file)
  * General Public License for more details.
  
  *
- * $Id: aha152x.c,v 1.0 1994/03/25 12:52:00 root Exp $
+ * $Id: aha152x.c,v 1.2 1994/07/03 12:56:36 root Exp $
  *
 
  * $Log: aha152x.c,v $
+ * Revision 1.2  1994/07/03  12:56:36  root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1  1994/05/28  21:18:49  root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
  * Revision 1.0  1994/03/25  12:52:00  root
  * - Fixed "more data than expected" problem
  * - added new BIOS signatures
 
  **************************************************************************/
 
-#include "aha152x.h"
-
 #include <linux/sched.h>
 #include <asm/io.h>
 #include "../block/blk.h"
 #include "scsi.h"
+#include "sd.h" /* Reqd for biosparam definition */
 #include "hosts.h"
 #include "constants.h"
 #include <asm/system.h>
 #include <linux/wait.h>
 #include <linux/ioport.h>
 
+#include "aha152x.h"
+
 /* DEFINES */
 
 
@@ -500,10 +510,6 @@ int aha152x_detect(int hostno)
   struct sigaction    sa;
   int                 interrupt_level;
   
-#if defined(DEBUG_RACE)
-  enter_driver("detect");
-#endif
-  
   if(setup_called)
     {
       printk("aha152x: processing commandline: ");
@@ -568,13 +574,8 @@ int aha152x_detect(int hostno)
                      (int) signatures[j].sig_length);
 
       if(!ok)
-        {
-#if defined(DEBUG_RACE)
-          leave_driver("(1) detect");
-#endif
-          printk("failed\n");
-          return 0;
-        }
+        return 0;
+
       printk("aha152x: BIOS test: passed, ");
 #else
       printk("aha152x: ");
@@ -588,9 +589,6 @@ int aha152x_detect(int hostno)
       if(i==PORT_COUNT)
         {
           printk("failed\n");
-#if defined(DEBUG_RACE)
-          leave_driver("(2) detect");
-#endif
           return 0;
         }
       else
@@ -662,9 +660,9 @@ int aha152x_detect(int hostno)
 
   /* RESET OUT */
   SETBITS(SCSISEQ, SCSIRSTO );
-  do_pause(5);
+  do_pause(30);
   CLRBITS(SCSISEQ, SCSIRSTO );
-  do_pause(10);
+  do_pause(60);
 
   aha152x_reset(NULL);
 
@@ -677,10 +675,6 @@ int aha152x_detect(int hostno)
   SETPORT(SIMODE0, 0);
   SETPORT(SIMODE1, 0);
 
-#if defined(DEBUG_RACE)
-  leave_driver("(3) detect");
-#endif
-
   SETBITS( DMACNTRL0, INTEN);
   return 1;
 }
@@ -765,6 +759,10 @@ int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     }
   sti();
 
+#if defined(DEBUG_RACE)
+  leave_driver("queue");
+#endif
+
   return 0;
 }
 
@@ -791,7 +789,9 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
   printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt );
 #endif
 
+#if defined(DEBUG_ABORT)
   show_queues();
+#endif
 
   /* look for command in issue queue */
   for( ptr=issue_SC, prev=NULL;
@@ -814,12 +814,21 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
       return SCSI_ABORT_SUCCESS;
     }
 
-  /* Fail abortion, if we're on the bus */
   if (current_SC)
-    {
+    if( TESTLO(SSTAT1, BUSFREE) ) {
+       /* fail abortion, if current command is on the bus */
        sti();
        return SCSI_ABORT_BUSY;
     }
+    else
+    { 
+      /* target entered bus free before COMMAND COMPLETE, nothing to abort */
+      sti();
+      current_SC->result = DID_ERROR << 16;
+      current_SC->done(current_SC);
+      current_SC = (Scsi_Cmnd *) NULL;
+      return SCSI_ABORT_SUCCESS;
+    }
 
   /* look for command in disconnected queue */
   for( ptr=disconnected_SC, prev=NULL;
@@ -836,7 +845,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
       if(prev)
         prev->host_scribble = ptr->host_scribble;
       else
-        issue_SC = (Scsi_Cmnd *) ptr->host_scribble;
+        disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
 
       /* set command current and initiate selection,
          let the interrupt routine take care of the abortion */
@@ -903,11 +912,11 @@ static void aha152x_reset_ports(void)
 
 /*
  *  Reset registers, reset a hanging bus and
- *  kill active and disconnected commands
+ *  kill active and disconnected commands for target w/o soft reset
  */
 int aha152x_reset(Scsi_Cmnd * __unused)
 {
-  Scsi_Cmnd *ptr;
+  Scsi_Cmnd *ptr, *prev, *next;
 
   aha152x_reset_ports();
 
@@ -920,13 +929,11 @@ int aha152x_reset(Scsi_Cmnd * __unused)
        printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
 #endif
 
+#if defined( DEBUG_RESET )
        show_queues();
+#endif
 
-       /* FIXME - if the device implements soft resets, the command will still
-         be running after the bus reset. In this case we should do nothing
-         and let the command continue. -ERY */
-
-       if(current_SC)
+       if(current_SC && !current_SC->device->soft_reset)
          {
            current_SC->host_scribble = NULL;
            current_SC->result = DID_RESET << 16;
@@ -934,22 +941,45 @@ int aha152x_reset(Scsi_Cmnd * __unused)
            current_SC=NULL;
          }
 
-       while(disconnected_SC)
-         {
-           ptr = disconnected_SC;
-           disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
-           ptr->host_scribble = NULL;
-           ptr->result = DID_RESET << 16;
-           ptr->done(ptr);
-         }
+       cli();
+       prev=NULL; ptr=disconnected_SC;
+       while(ptr)
+        {
+          if(!ptr->device->soft_reset)
+            {
+              if(prev)
+                prev->host_scribble = ptr->host_scribble;
+              else
+                disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+              next = (Scsi_Cmnd *) ptr->host_scribble;
+  
+              ptr->host_scribble = NULL;
+              ptr->result        = DID_RESET << 16;
+              ptr->done(ptr);
+  
+              ptr = next; 
+            }
+          else
+            {
+              prev=ptr;
+              ptr = (Scsi_Cmnd *) ptr->host_scribble;
+            }
+        }
+       sti();
+
+#if defined( DEBUG_RESET )
+       printk("commands on targets w/ soft-resets:\n");
+       show_queues();
+#endif
 
        /* RESET OUT */
        SETPORT(SCSISEQ, SCSIRSTO);
-       do_pause(5);
+       do_pause(30);
        SETPORT(SCSISEQ, 0);
-       do_pause(10);
+       do_pause(60);
 
-       SETPORT(SIMODE0, 0 );
+       SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
        SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
 
        SETPORT( DMACNTRL0, INTEN );
@@ -961,8 +991,9 @@ int aha152x_reset(Scsi_Cmnd * __unused)
 /*
  * Return the "logical geometry"
  */
-int aha152x_biosparam( int size, int dev, int *info_array )
+int aha152x_biosparam(Scsi_Disk * disk, int dev, int *info_array )
 {
+  int size = disk->capacity;
 #if defined(DEBUG_BIOSPARAM)
   printk("aha152x_biosparam: dev=%x, size=%d, ", dev, size);
 #endif
index 092ae76a06fc90016ff706a3a8329dccdb4e9017..eda630864f6d66526a6f4ca45f1ca827fb44dd4e 100644 (file)
@@ -2,7 +2,7 @@
 #define _AHA152X_H
 
 /*
- * $Id: aha152x.h,v 1.0 1994/03/25 12:52:00 root Exp $
+ * $Id: aha152x.h,v 1.2 1994/07/03 13:01:47 root Exp $
  */
 
 #include "../block/blk.h"
@@ -16,13 +16,13 @@ int        aha152x_command(Scsi_Cmnd *);
 int        aha152x_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int        aha152x_abort(Scsi_Cmnd *);
 int        aha152x_reset(Scsi_Cmnd *);
-int        aha152x_biosparam(int, int, int*);
+int        aha152x_biosparam(Disk *, int, int*);
 
 /* number of queueable commands
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE       7               
 
-#define AHA152X_REVID  "Adaptec 152x SCSI driver; $Revision: 1.0 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.2 $"
 
 /* Initial value of Scsi_Host entry */
 #define AHA152X       { /* name */             AHA152X_REVID, \
index 3d860e4469ca4ecb823f7cf1aee05a7b74adfc7e..e78f76c6fa0556bde4d86d531301f74dede49889 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
-#include <linux/config.h>
 
 #include <linux/sched.h>
 #include <asm/dma.h>
@@ -1063,18 +1062,14 @@ int aha1542_reset(Scsi_Cmnd * SCpnt)
     return SCSI_RESET_PUNT;
 }
 
-#ifdef CONFIG_BLK_DEV_SD
 #include "sd.h"
-#endif
 
-int aha1542_biosparam(int size, int dev, int * ip)
+int aha1542_biosparam(Scsi_Disk * disk, int dev, int * ip)
 {
   int translation_algorithm;
-#ifdef CONFIG_BLK_DEV_SD
-  Scsi_Device *disk;
+  int size = disk->capacity;
 
-  disk = rscsi_disks[MINOR(dev) >> 4].device;
-  translation_algorithm = HOSTDATA(disk->host)->bios_translation;
+  translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
   /* Should this be > 1024, or >= 1024?  Enquiring minds want to know. */
   if((size>>11) > 1024 && translation_algorithm == 2) {
     /* Please verify that this is the same as what DOS returns */
@@ -1087,6 +1082,5 @@ int aha1542_biosparam(int size, int dev, int * ip)
     ip[2] = size >> 11;
   };
 /*  if (ip[2] >= 1024) ip[2] = 1024; */
-#endif
   return 0;
 }
index 5a2334294ad5fe80dae7bbb9feee6b009e363709..c818618262bfd55ccdab27886a8d8552ec5e0d55 100644 (file)
@@ -134,7 +134,7 @@ int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int aha1542_abort(Scsi_Cmnd *);
 const char *aha1542_info(void);
 int aha1542_reset(Scsi_Cmnd *);
-int aha1542_biosparam(int, int, int*);
+int aha1542_biosparam(Disk *, int, int*);
 
 #define AHA1542_MAILBOXES 8
 #define AHA1542_SCATTER 16
index 1174258e9af23530594c49c9738006aa38932373..0160dab39ba2b4b6eed5ee8296e7721d7acf6c2c 100644 (file)
@@ -31,6 +31,7 @@
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
+#include "sd.h"
 
 #include "aha1740.h"
 
@@ -489,8 +490,9 @@ int aha1740_reset(Scsi_Cmnd * SCpnt)
     return SCSI_RESET_PUNT;
 }
 
-int aha1740_biosparam(int size, int dev, int* ip)
+int aha1740_biosparam(Disk * disk, int dev, int* ip)
 {
+  int size = disk->capacity;
 DEB(printk("aha1740_biosparam\n"));
   ip[0] = 64;
   ip[1] = 32;
index 0ed314df3c34bce69103bb15a2a0f9ee14c7068c..efc61b0a6d031ff59fe7f9e39067497bf81da494 100644 (file)
@@ -158,7 +158,7 @@ int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int aha1740_abort(Scsi_Cmnd *);
 const char *aha1740_info(void);
 int aha1740_reset(Scsi_Cmnd *);
-int aha1740_biosparam(int, int, int*);
+int aha1740_biosparam(Disk *, int, int*);
 
 #define AHA1740_ECBS 32
 #define AHA1740_SCATTER 16
index 0f6ff587207f949d62369a9d255a0c9acb1850b6..e739fd0e33e9655e80838487d870607a22ab7439 100644 (file)
@@ -59,7 +59,6 @@
  *    unfinished, questionable, or wrong.
  */
 
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -74,9 +73,7 @@
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
-#ifdef CONFIG_BLK_DEV_SD
 # include "sd.h"
-#endif
 #define BUSLOGIC_PRIVATE_H     /* Get the "private" stuff */
 #include "buslogic.h"
 
@@ -1181,17 +1178,17 @@ int buslogic_reset(Scsi_Cmnd *SCpnt)
     return SCSI_RESET_PUNT;
 }
 
-int buslogic_biosparam(int size, int dev, int *ip)
+int buslogic_biosparam(Disk * disk, int dev, int *ip)
 {
-    /* ??? This is wrong if disk is configured for > 1G mapping.
-       Unfortunately, unlike UltraStor, I see know way of determining whether
-       > 1G mapping has been enabled. */
-#ifdef CONFIG_BLK_DEV_SD
-    int translation_algorithm;
-    Scsi_Device *disk;
-
-    disk = rscsi_disks[MINOR(dev) >> 4].device;
-    translation_algorithm = HOSTDATA(disk->host)->bios_translation;
+  int size = disk->capacity;
+  int translation_algorithm;
+
+  /* ??? This is wrong if disk is configured for > 1G mapping.
+     Unfortunately, unlike UltraStor, I see know way of determining whether
+     > 1G mapping has been enabled. */
+
+
+    translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
     /* ??? Should this be > 1024, or >= 1024?  Enquiring minds want to know. */
     if ((size >> 11) > 1024
        && translation_algorithm == BIOS_TRANSLATION_25563) {
@@ -1206,6 +1203,5 @@ int buslogic_biosparam(int size, int dev, int *ip)
     }
 /*    if (ip[2] > 1024)
       ip[2] = 1024; */
-#endif
     return 0;
 }
index cca1aeb74a45cca6f179137acf6d0d07d005a94a..fb9bbe3eb65dcf8a55dd020a12593a0a9284180f 100644 (file)
@@ -17,7 +17,7 @@ int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int buslogic_abort(Scsi_Cmnd *);
 const char *buslogic_info(void);
 int buslogic_reset(Scsi_Cmnd *);
-int buslogic_biosparam(int, int, int *);
+int buslogic_biosparam(Disk *, int, int *);
 
 #define BUSLOGIC_CMDLUN 1      /* ??? */
 
index cab209ad3b19a90499933c2309944172e82302dc..9c0f5672a18bfa2c4b659c384a8885b9c8018f8a 100644 (file)
 
  **************************************************************************/
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include "../block/blk.h"
@@ -1456,20 +1455,18 @@ int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
    return SCSI_RESET_WAKEUP;
 }
 
-#ifdef CONFIG_BLK_DEV_SD
-
 #include "sd.h"
 #include "scsi_ioctl.h"
 
-int fdomain_16x0_biosparam( int size, int dev, int *info_array )
+int fdomain_16x0_biosparam(Scsi_Disk * disk, int dev, int *info_array )
 {
    int              drive;
+   int             size = disk->capacity;
    unsigned char    buf[512 + sizeof( int ) * 2];
    int              *sizes    = (int *)buf;
    unsigned char    *data     = (unsigned char *)(sizes + 2);
    unsigned char    do_read[] = { READ_6, 0, 0, 0, 1, 0 };
    int              retcode;
-   Scsi_Device      *disk;
    struct drive_info {
       unsigned short cylinders;
       unsigned char  heads;
@@ -1509,7 +1506,6 @@ int fdomain_16x0_biosparam( int size, int dev, int *info_array )
     */
 
    drive = MINOR(dev) / 16;
-   disk  = rscsi_disks[ drive ].device;
 
    if (bios_major == 2) {
       i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
@@ -1527,7 +1523,7 @@ int fdomain_16x0_biosparam( int size, int dev, int *info_array )
       sizes[0] = 0;            /* zero bytes out */
       sizes[1] = 512;          /* one sector in */
       memcpy( data, do_read, sizeof( do_read ) );
-      retcode = kernel_scsi_ioctl( disk,
+      retcode = kernel_scsi_ioctl( disk->device,
                                   SCSI_IOCTL_SEND_COMMAND,
                                   (void *)buf );
       if (!retcode                                 /* SCSI command ok */
@@ -1588,5 +1584,3 @@ int fdomain_16x0_biosparam( int size, int dev, int *info_array )
    
    return 0;
 }
-
-#endif /* CONFIG_BLK_DEV_SD */
index 0cd09cffe86ecdc37ded0ad91834b7cfd0eb1ad6..ee9ddcb79a1cc46521cedb9f9988b2439d0bfc0a 100644 (file)
@@ -32,11 +32,7 @@ const char *fdomain_16x0_info( void );
 int        fdomain_16x0_reset( Scsi_Cmnd * ); 
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
 
-#ifdef CONFIG_BLK_DEV_SD
-int        fdomain_16x0_biosparam( int, int, int * );
-#else
-#define    fdomain_16x0_biosparam NULL
-#endif
+int        fdomain_16x0_biosparam(Disk *, int, int * );
 
 #define FDOMAIN_16X0 { "Future Domain TMC-16x0",          \
                        fdomain_16x0_detect,              \
index bf0a2732b3b0a1b07cee10ac57b917a5cdbe3274..442df859f8da2218d9155c2ce47ea0a86035457c 100644 (file)
 */
 
 
+/* It is senseless to set SG_ALL any higher than this - the performance
+   does not get any better, and it wastes memory */
 #define SG_NONE 0
-#define SG_ALL 0x7fff
+#define SG_ALL 0xff
 
 #define DISABLE_CLUSTERING 0
 #define ENABLE_CLUSTERING 1
@@ -42,6 +44,8 @@
        type of host adapter that is supported on the system.
 */
 
+typedef struct scsi_disk Disk;
+
 typedef struct     
        {
        /*
@@ -144,7 +148,7 @@ typedef struct
                size, device number, list (heads, sectors, cylinders)
        */ 
 
-       int (* bios_param)(int, int, int []);
+       int (* bios_param)(Disk *, int, int []);
        
        /*
                This determines if we will use a non-interrupt driven
index e7de2a17d3bb4f1068e3a93a429e6f16c9038e56..b57e8cbba9124ed6f38019650a2122103f1798c7 100644 (file)
@@ -91,7 +91,7 @@
 #define AUTOPROBE_IRQ
 #include "NCR5380.h"
 #include "constants.h"
-
+#include "sd.h"
 
 
 int scsi_irq_translate[] =
@@ -374,7 +374,7 @@ int pas16_detect(int hostno) {
 }
 
 /*
- * Function : int pas16_biosparam(int size, int dev, int *ip)
+ * Function : int pas16_biosparam(Disk *disk, int dev, int *ip)
  *
  * Purpose : Generates a BIOS / DOS compatable H-C-S mapping for 
  *     the specified device / size.
@@ -393,8 +393,9 @@ int pas16_detect(int hostno) {
  * and matching the H_C_S coordinates to what DOS uses.
  */
 
-int pas16_biosparam(int size, int dev, int * ip)
+int pas16_biosparam(Disk * disk, int dev, int * ip)
 {
+  int size = disk->capacity;
   ip[0] = 64;
   ip[1] = 32;
   ip[2] = size >> 11;
index d1e70189847339d13f8eacc3182bd747a6f1b3df..223316f551744964937d15ad1d24e2eab05849ba 100644 (file)
 
 #ifndef ASM
 int pas16_abort(Scsi_Cmnd *);
-int pas16_biosparam(int, int, int*);
+int pas16_biosparam(Disk *, int, int*);
 int pas16_detect(int);
 const char *pas16_info(void);
 int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
index 93b01764314de8fe14f8d967bc19d3ee6e40e8c6..621bdd9862515edb421219c63104c3fb9a429276 100644 (file)
@@ -142,7 +142,6 @@ 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 */
@@ -1493,6 +1492,9 @@ int scsi_reset (Scsi_Cmnd * SCpnt)
                                host->host_busy--;
                                }
 
+#ifdef DEBUG
+                       printk("scsi reset function returned %d\n", temp);
+#endif
                        switch(temp) {
                        case SCSI_RESET_SUCCESS:
                          cli();
index 3e878eb096589ed58c50d1a26b8efb001afb5a76..4a9469537d635154ff149e69186dfa4b3c0dca0b 100644 (file)
@@ -22,6 +22,8 @@
 #include "scsi.h"
 #include "hosts.h"
 
+#include "sd.h"
+
 /* A few options that we want selected */
 
 /* Do not attempt to use a timer to simulate a real disk with latency */
@@ -541,7 +543,8 @@ int scsi_debug_abort(Scsi_Cmnd * SCpnt)
     return 0;
 }
 
-int scsi_debug_biosparam(int size, int dev, int* info){
+int scsi_debug_biosparam(Disk * disk, int dev, int* info){
+  int size = disk->capacity;
   info[0] = 32;
   info[1] = 64;
   info[2] = (size + 2047) >> 11;
index f210e14761f4df4c8579bf032615b482b697de5a..93031b615608a433b54ac46ee8753a83b2b8e6ed 100644 (file)
@@ -6,7 +6,7 @@ int scsi_debug_detect(int);
 int scsi_debug_command(Scsi_Cmnd *);
 int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int scsi_debug_abort(Scsi_Cmnd *, int);
-int scsi_debug_biosparam(int, int, int[]);
+int scsi_debug_biosparam(Disk *, int, int[]);
 const char *scsi_debug_info(void);
 int scsi_debug_reset(Scsi_Cmnd *);
 
index b44b38d2b27097d38198594b0e414f5015741f09..7cc3b0159f10b83b54807c054e066c1300e63c89 100644 (file)
@@ -54,6 +54,9 @@ static int * sd_blocksizes;
 
 extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
+static int check_scsidisk_media_change(dev_t);
+static int fop_revalidate_scsidisk(dev_t);
+
 static sd_init_onedisk(int);
 
 static void requeue_sd_request (Scsi_Cmnd * SCpnt);
@@ -108,7 +111,10 @@ static struct file_operations sd_fops = {
        NULL,                   /* mmap */
        sd_open,                /* open code */
        sd_release,             /* release */
-       block_fsync             /* fsync */
+       block_fsync,            /* fsync */
+       NULL,                   /* fasync */
+       check_scsidisk_media_change,  /* Disk change */
+       fop_revalidate_scsidisk     /* revalidate */
 };
 
 static struct gendisk sd_gendisk = {
@@ -733,10 +739,11 @@ repeat:
                     MAX_RETRIES);
 }
 
-int check_scsidisk_media_change(int full_dev, int flag){
+static int check_scsidisk_media_change(dev_t full_dev){
         int retval;
        int target;
        struct inode inode;
+       int flag = 0;
 
        target =  DEVICE_NR(MINOR(full_dev));
 
@@ -1091,3 +1098,7 @@ int revalidate_scsidisk(int dev, int maxusage){
          return 0;
 }
 
+static int fop_revalidate_scsidisk(dev_t dev){
+  return revalidate_scsidisk(dev, 0);
+}
+
index efa63e315e0fad38f652158553a7ed66efe67c29..9656b01995d4f5a5b437ea54cecd46a13a6938ed 100644 (file)
@@ -31,7 +31,7 @@
 
 extern struct hd_struct * sd;
 
-typedef struct {
+typedef struct scsi_disk {
                unsigned capacity;              /* size in blocks */
                unsigned sector_size;           /* size in bytes */
                Scsi_Device  *device;           
index 385221d0ea9398120371db5e870904f53f838bfe..599a2a320b848580d673c0a950c303933958dca7 100644 (file)
@@ -33,7 +33,7 @@ int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne
                        diskinfo[1] = 0;
                        diskinfo[2] = 0;
                        if(host->hostt->bios_param != NULL)
-                             host->hostt->bios_param(rscsi_disks[MINOR(dev) >> 4].capacity,
+                             host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
                                                          dev,
                                                          &diskinfo[0]);
                        put_fs_byte(diskinfo[0],
index b5641fafb35544120914b2194179470a1552fb36..6ccf90f611ccee611e70c83f71489152558eed9e 100644 (file)
@@ -1590,34 +1590,28 @@ int seagate_st0x_reset (Scsi_Cmnd * SCpnt)
        return SCSI_RESET_PENDING;
        }
 
-#ifdef CONFIG_BLK_DEV_SD
-
 #include <asm/segment.h>
 #include "sd.h"
 #include "scsi_ioctl.h"
 
-int seagate_st0x_biosparam(int size, int dev, int* ip) {
+int seagate_st0x_biosparam(Disk * disk, int dev, int* ip) {
   unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
   int *sizes, result, formatted_sectors, total_sectors;
   int cylinders, heads, sectors;
 
-  Scsi_Device *disk;
-
-  disk = rscsi_disks[MINOR(dev) >> 4].device;
-
 /*
  * Only SCSI-I CCS drives and later implement the necessary mode sense 
  * pages.  
  */
 
-  if (disk->scsi_level < 2) 
+  if (disk->device->scsi_level < 2) 
        return -1;
 
   sizes = (int *) buf;
   data = (unsigned char *) (sizes + 2);
 
   cmd[0] = MODE_SENSE;
-  cmd[1] = (disk->lun << 5) & 0xe5;
+  cmd[1] = (disk->device->lun << 5) & 0xe5;
   cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
   cmd[3] = 0;
   cmd[4] = 255;
@@ -1633,7 +1627,7 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
 
   memcpy (data, cmd, 6);
 
-  if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+  if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
 /*
  * The mode page lies beyond the MODE SENSE header, with length 4, and 
  * the BLOCK DESCRIPTOR, with length header[3].
@@ -1646,7 +1640,7 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
     cmd[2] = 0x03; /* Read page 3, format page current values */
     memcpy (data, cmd, 6);
 
-    if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+    if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
       page = data + 4 + data[3];
       sectors = (page[10] << 8) | page[11];    
 
@@ -1701,7 +1695,5 @@ printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %
     
   return result;
 }
-#endif /* CONFIG_BLK_DEV_SD */
-
 #endif /* defined(CONFIG_SCSI_SEGATE) */
 
index 50cbddef9662ba2e64a05a83ce13270f287e5d5f..66537f245ebe2148629333c52ca9cb1ef16f4581 100644 (file)
@@ -24,11 +24,7 @@ int seagate_st0x_reset(Scsi_Cmnd *);
        #define NULL 0
 #endif
 
-#ifdef CONFIG_BLK_DEV_SD
-int seagate_st0x_biosparam(int, int, int*);
-#else
-#define seagate_st0x_biosparam NULL
-#endif
+int seagate_st0x_biosparam(Disk *, int, int*);
 
 #define SEAGATE_ST0X  {"Seagate ST-01/ST-02", seagate_st0x_detect,     \
                         seagate_st0x_info, seagate_st0x_command,       \
index 9177c8c99b61fe94e0fc93cfbf58ba847bd9a416..6e40a8dcd7e4eafaa90f789dd48b9371e59113c5 100644 (file)
@@ -45,6 +45,7 @@ static void get_sectorsize(int);
 extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
 void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(dev_t);
 
 static void sr_release(struct inode * inode, struct file * file)
 {
@@ -62,9 +63,12 @@ static struct file_operations sr_fops =
        NULL,                   /* select */
        sr_ioctl,               /* ioctl */
        NULL,                   /* mmap */
-       sr_open,                /* no special open code */
+       sr_open,                /* special open code */
        sr_release,             /* release */
-       NULL                    /* fsync */
+       NULL,                   /* fsync */
+       NULL,                   /* fasync */
+       check_cdrom_media_change,  /* Disk change */
+       NULL                    /* revalidate */
 };
 
 /*
@@ -77,9 +81,10 @@ static struct file_operations sr_fops =
  * an inode for that to work, and we do not always have one.
  */
 
-int check_cdrom_media_change(int full_dev, int flag){
+int check_cdrom_media_change(dev_t full_dev){
        int retval, target;
        struct inode inode;
+       int flag = 0;
 
        target =  MINOR(full_dev);
 
index faf4cdd96c36d31e6e5d732064e2a1b8c9d69d3d..21c3c530cb87474f71eb836c2ffc2e33b109f83c 100644 (file)
@@ -11,7 +11,7 @@
   Copyright 1992, 1993, 1994 Kai Makisara
                 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
 
-  Last modified: Wed May  4 20:29:42 1994 by root@kai.home
+  Last modified: Wed Jun 22 23:37:10 1994 by root@kai.home
 */
 
 #include <linux/fs.h>
@@ -40,6 +40,8 @@
 
 /* #define ST_RECOVERED_WRITE_FATAL */
 
+#define ST_TWO_FM 0
+
 #define ST_BUFFER_WRITES 1
 
 #define ST_ASYNC_WRITES 1
@@ -71,7 +73,7 @@ static int debugging = 1;
 #define MAX_READY_RETRIES 5
 #define NO_TAPE  NOT_READY
 
-#define ST_TIMEOUT 9000
+#define ST_TIMEOUT 27000
 #define ST_LONG_TIMEOUT 200000
 
 static int st_nbr_buffers;
@@ -237,6 +239,11 @@ back_over_eof(int dev)
 
   if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
   SCpnt->request.dev = -1;
+  if ((STp->buffer)->last_result != 0) {
+    printk("st%d: Backing over filemark failed.\n", dev);
+    (STp->mt_status)->mt_fileno += 1;
+    (STp->mt_status)->mt_blkno = 0;
+  }
 
   return (STp->buffer)->last_result_fatal;
 }
@@ -551,7 +558,8 @@ scsi_tape_open(struct inode * inode, struct file * filp)
             (STp->buffer)->buffer_blocks);
 #endif
 
-    if ((STp->buffer)->b_data[2] & 0x80) {
+    STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+    if (STp->drv_write_prot) {
       STp->write_prot = 1;
 #ifdef DEBUG
       if (debugging)
@@ -594,7 +602,7 @@ scsi_tape_close(struct inode * inode, struct file * filp)
        SCpnt->sense_buffer[0] = 0;
        memset(cmd, 0, 10);
        cmd[0] = WRITE_FILEMARKS;
-       cmd[4] = 1;
+       cmd[4] = 1 + STp->two_fm;
        SCpnt->request.dev = dev;
        scsi_do_cmd( SCpnt,
                    (void *) cmd, (void *) (STp->buffer)->b_data,
@@ -602,19 +610,23 @@ scsi_tape_close(struct inode * inode, struct file * filp)
 
        if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
-       if ((STp->buffer)->last_result_fatal != 0)
+       if ((STp->buffer)->last_result_fatal != 0) {
+         SCpnt->request.dev = -1;  /* Mark as not busy */
          printk("st%d: Error on write filemark.\n", dev);
+       }
        else {
+         SCpnt->request.dev = -1;  /* Mark as not busy */
           (STp->mt_status)->mt_fileno++ ;
          STp->drv_block = 0;
+         if (STp->two_fm)
+           back_over_eof(dev);
        }
 
-       SCpnt->request.dev = -1;  /* Mark as not busy */
       }
 
 #ifdef DEBUG
       if (debugging)
-       printk("st%d: Buffer flushed, EOF written\n", dev);
+       printk("st%d: Buffer flushed, %d EOF(s) written\n", dev, cmd[4]);
 #endif
     }
     else if (!rewind) {
@@ -670,6 +682,9 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
       STp->rw = ST_WRITING;
     }
 
+    if (STp->moves_after_eof < 255)
+      STp->moves_after_eof++;
+
     if ((STp->buffer)->writing) {
       write_behind_check(dev);
       if ((STp->buffer)->last_result_fatal) {
@@ -896,6 +911,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
        return transfer;
       STp->rw = ST_READING;
     }
+    if (STp->moves_after_eof < 255)
+      STp->moves_after_eof++;
 
 #ifdef DEBUG
     if (debugging && STp->eof != ST_NOEOF)
@@ -1010,8 +1027,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
                    "st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
                         dev, (STp->buffer)->buffer_bytes, total);
 #endif
-             } /* end of EOF, EOM, ILI test */
-           }
+             }
+           } /* end of EOF, EOM, ILI test */
            else { /* nonzero sense key */
 #ifdef DEBUG
              if (debugging)
@@ -1021,16 +1038,25 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
              STp->drv_block = (-1);
              if (total)
                return total;
+             else if (STp->moves_after_eof == 1 &&
+                      (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+#ifdef DEBUG
+               if (debugging)
+                 printk("st%d: Zero returned for first BLANK CHECK after EOF.\n",
+                        dev);
+#endif
+               return 0; /* First BLANK_CHECK after EOF */
+             }
              else
                return -EIO;
            }
-         }
+         } /* End of extended sense test */
          else {
            transfer = (STp->buffer)->last_result_fatal;
            SCpnt->request.dev = -1;  /* Mark as not busy */
            return transfer;
          }
-       }
+       } /* End of error handling */
        else /* Read successful */
          (STp->buffer)->buffer_bytes = bytes;
 
@@ -1066,6 +1092,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
        if (total == 0 && STp->eof == ST_FM) {
          STp->eof = 0;
          STp->drv_block = 0;
+         if (STp->moves_after_eof > 1)
+           STp->moves_after_eof = 0;
          (STp->mt_status)->mt_fileno++;
        }
        if (total == 0 && STp->eof == ST_EOM_OK)
@@ -1098,13 +1126,15 @@ st_set_options(struct inode * inode, long options)
     STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
     STp->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
     STp->do_read_ahead    = (options & MT_ST_READ_AHEAD) != 0;
+    STp->two_fm                  = (options & MT_ST_TWO_FM) != 0;
 #ifdef DEBUG
     debugging = (options & MT_ST_DEBUGGING) != 0;
     printk(
 "st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
           dev, STp->do_buffer_writes, STp->do_async_writes,
           STp->do_read_ahead);
-    printk("              debugging: %d\n", debugging);
+    printk("              two FMs: %d, debugging: %d\n", STp->two_fm,
+          debugging);
 #endif
   }
   else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
@@ -1397,8 +1427,16 @@ st_int_ioctl(struct inode * inode,struct file * file,
    SCpnt->request.dev = -1;  /* Mark as not busy */
 
    if (!ioctl_result) {
-     STp->drv_block = blkno;
-     (STp->mt_status)->mt_fileno = fileno;
+     if (cmd_in != MTSEEK) {
+       STp->drv_block = blkno;
+       (STp->mt_status)->mt_fileno = fileno;
+     }
+     else
+       STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
+     if (cmd_in == MTFSF)
+       STp->moves_after_eof = 0;
+     else
+       STp->moves_after_eof = 1;
      if (cmd_in == MTBSFM)
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
      else if (cmd_in == MTFSFM)
@@ -1419,10 +1457,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
         (STp->buffer)->read_pointer = 0;
      }
      else if (cmd_in == MTSETDRVBUFFER)
-       STp->drv_buffer = arg;
+       STp->drv_buffer = (arg & 7);
      else if (cmd_in == MTSETDENSITY)
        STp->density = arg;
-     else if (cmd_in == MTEOM || cmd_in == MTWEOF) {
+     else if (cmd_in == MTEOM) {
        STp->eof = ST_EOM_OK;
        STp->eof_hit = 0;
      }
@@ -1498,7 +1536,7 @@ st_ioctl(struct inode * inode,struct file * file,
 
      memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
 
-     i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
+     i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
                      mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
                      mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
      if (i < 0)
@@ -1530,6 +1568,9 @@ st_ioctl(struct inode * inode,struct file * file,
         (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
           STp->block_size - 1) / STp->block_size;
      }
+     (STp->mt_status)->mt_gstat = 0;
+     if (STp->drv_write_prot)
+       (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
 
      memcpy_tofs((char *)arg, (char *)(STp->mt_status),
                 sizeof(struct mtget));
@@ -1677,8 +1718,10 @@ unsigned long st_init(unsigned long mem_start, unsigned long mem_end)
     STp->do_buffer_writes = ST_BUFFER_WRITES;
     STp->do_async_writes = ST_ASYNC_WRITES;
     STp->do_read_ahead = ST_READ_AHEAD;
+    STp->two_fm = ST_TWO_FM;
     STp->write_threshold = st_write_threshold;
     STp->drv_block = 0;
+    STp->moves_after_eof = 1;
     STp->mt_status = (struct mtget *) mem_start;
     mem_start += sizeof(struct mtget);
     /* Initialize status */
index 9df6d7b8f7f0f53090721d4d044b17210bc6c7be..88787d3d4821953211ddcb9a476d04c94488f762 100644 (file)
@@ -25,16 +25,18 @@ typedef struct {
   unsigned capacity;
   struct wait_queue * waiting;
   Scsi_Device* device;
-  unsigned dirty:1;
-  unsigned rw:2;
-  unsigned eof:2;
-  unsigned write_prot:1;
-  unsigned in_use:1;
-  unsigned eof_hit:1;
-  unsigned drv_buffer:3;
-  unsigned do_buffer_writes:1;
-  unsigned do_async_writes:1;
-  unsigned do_read_ahead:1;
+  unsigned char dirty;
+  unsigned char rw;
+  unsigned char eof;
+  unsigned char write_prot;
+  unsigned char drv_write_prot;
+  unsigned char in_use;
+  unsigned char eof_hit;
+  unsigned char drv_buffer;
+  unsigned char do_buffer_writes;
+  unsigned char do_async_writes;
+  unsigned char do_read_ahead;
+  unsigned char two_fm;
   unsigned char density;
   ST_buffer * buffer;
   int block_size;
@@ -43,6 +45,7 @@ typedef struct {
   int write_threshold;
   int recover_count;
   int drv_block;       /* The block where the drive head is */
+  unsigned char moves_after_eof;
   struct mtget * mt_status;
   Scsi_Cmnd SCpnt;
 } Scsi_Tape;
index 55314056c70bbabe022c6ec92cf2f6bef3763a7a..0f50cf3cc075feb8c9c6496522aa3b942a20508c 100644 (file)
 #define AUTOPROBE_IRQ
 #include "NCR5380.h"
 #include "constants.h"
-
+#include "sd.h"
 
 
 
@@ -273,7 +273,7 @@ int t128_detect(int hostno) {
 }
 
 /*
- * Function : int t128_biosparam(int size, int dev, int *ip)
+ * Function : int t128_biosparam(Disk * disk, int dev, int *ip)
  *
  * Purpose : Generates a BIOS / DOS compatable H-C-S mapping for 
  *     the specified device / size.
@@ -292,8 +292,9 @@ int t128_detect(int hostno) {
  * and matching the H_C_S coordinates to what DOS uses.
  */
 
-int t128_biosparam(int size, int dev, int * ip)
+int t128_biosparam(Disk * disk, int dev, int * ip)
 {
+  int size = disk->capacity;
   ip[0] = 64;
   ip[1] = 32;
   ip[2] = size >> 11;
index 8ad288165cd3b314c519b174b1c4169e92f21544..54020dab44036933210a29d5d93fb6bc2c488119 100644 (file)
@@ -92,7 +92,7 @@
 
 #ifndef ASM
 int t128_abort(Scsi_Cmnd *);
-int t128_biosparam(int, int, int*);
+int t128_biosparam(Disk *, int, int*);
 int t128_detect(int);
 const char *t128_info(void);
 int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
index 43b2dc5313856c5a5eab08ea57624b19270e1aed..79924c06dc9d38c7cdd34118e78ea427e4aadf7d 100644 (file)
 #include "scsi.h"
 #include "hosts.h"
 #include "ultrastor.h"
+#include "sd.h"
 
 #define FALSE 0
 #define TRUE 1
@@ -996,8 +997,9 @@ int ultrastor_reset(Scsi_Cmnd * SCpnt)
 
 }
 
-int ultrastor_biosparam(int size, int dev, int * dkinfo)
+int ultrastor_biosparam(Disk * disk, int dev, int * dkinfo)
 {
+    int size = disk->capacity;
     unsigned int s = config.heads * config.sectors;
 
     dkinfo[0] = config.heads;
index fcc661d799804a4d5dc5271cafbf638063556f05..583eabb8c1e69a599ccec9eb876e87d3342f8a94 100644 (file)
@@ -18,7 +18,7 @@ const char *ultrastor_info(void);
 int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int ultrastor_abort(Scsi_Cmnd *);
 int ultrastor_reset(Scsi_Cmnd *);
-int ultrastor_biosparam(int, int, int *);
+int ultrastor_biosparam(Disk *, int, int *);
 
 #define ULTRASTOR_14F_MAX_SG 16
 #define ULTRASTOR_24F_MAX_SG 33
index 4e3d08be6fbe5d9f269fc4477bfc6cbed31a224d..6cb49b91603c6a5575c0a51047fef7ce8709c42c 100644 (file)
 #include "../block/blk.h"
 #include "scsi.h"
 #include "hosts.h"
+#include "sd.h"
 
 #define ANY2SCSI_INLINE    /* undef this to use old macros */
 #undef DEBUG
@@ -1225,8 +1226,9 @@ const char *wd7000_info(void)
  *  this way, so I think it will work OK.  Someone who is ambitious can
  *  borrow a newer or more complete version from another driver.
  */
-int wd7000_biosparam(int size, int dev, int* ip)
+int wd7000_biosparam(Disk * disk, int dev, int* ip)
 {
+  int size = disk->capacity;
   ip[0] = 64;
   ip[1] = 32;
   ip[2] = size >> 11;
index dbd6463ef2f6e7ed6bc9a3137a28bab3fb0f822a..f120a94628bfcba3b4adba378653b1cf3fca517f 100644 (file)
@@ -18,7 +18,7 @@ int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int wd7000_abort(Scsi_Cmnd *);
 const char *wd7000_info(void);
 int wd7000_reset(Scsi_Cmnd *);
-int wd7000_biosparam(int, int, int*);
+int wd7000_biosparam(Disk *, int, int*);
 
 #ifndef NULL
 #define NULL 0L
index dae8f0af0480e3efed869864881fdeae1f5ed523..e2f9884a1e676d15265ce6043e5ff3e6246f972e 100644 (file)
@@ -479,7 +479,7 @@ void set_blocksize(dev_t dev, int size)
                        
                        wait_on_buffer(bh);
                        if (bh->b_dev == dev && bh->b_size != size) {
-                               bh->b_uptodate = bh->b_dirt = 
+                               bh->b_uptodate = bh->b_dirt = bh->b_req =
                                         bh->b_flushtime = 0;
                        };
                        remove_from_hash_queue(bh);
index 88353596908500a4d7e337f20003a6cfe5b305da..16c098c9e747006915abd1abbeec272af70ac35f 100644 (file)
 #include <linux/fcntl.h>
 #include <linux/errno.h>
 
+#include <linux/config.h>
+
 /*
  * Ugly. We'll fix this once all the drivers use the f_ops->check_media_change()
  * stuff instead..
  */
-#ifdef CONFIG_SCSI
-#ifdef CONFIG_BLK_DEV_SR
-extern int check_cdrom_media_change(int, int);
-#endif
-#ifdef CONFIG_BLK_DEV_SD
-extern int check_scsidisk_media_change(int, int);
-extern int revalidate_scsidisk(int, int);
-#endif
-#endif
-#ifdef CONFIG_CDU31A
-extern int check_cdu31a_media_change(int, int);
-#endif
 #ifdef CONFIG_MCD
 extern int check_mcd_media_change(int, int);
 #endif
@@ -142,57 +132,37 @@ int unregister_blkdev(unsigned int major, const char * name)
  * People changing diskettes in the middle of an operation deserve
  * to loose :-)
  */
-void check_disk_change(dev_t dev)
+int check_disk_change(dev_t dev)
 {
        int i;
        struct file_operations * fops;
 
        i = MAJOR(dev);
        if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
-               return;
+               return 0;
        if (fops->check_media_change != NULL) {
                if (!fops->check_media_change(dev))
-                       return;
+                       return 0;
        } 
 #if 1 /* this will go soon.. */
        else switch(MAJOR(dev)){
-#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
-         case SCSI_DISK_MAJOR:
-               if (!check_scsidisk_media_change(dev, 0))
-                       return;
-               break;
-#endif
-
-#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
-        case SCSI_CDROM_MAJOR:
-               if (!check_cdrom_media_change(dev, 0))
-                       return;
-               break;
-#endif
-
-#if defined(CONFIG_CDU31A)
-         case CDU31A_CDROM_MAJOR:
-               if (!check_cdu31a_media_change(dev, 0))
-                       return;
-               break;
-#endif
 
 #if defined(CONFIG_MCD)
          case MITSUMI_CDROM_MAJOR:
                if (!check_mcd_media_change(dev, 0))
-                       return;
+                       return 0;
                break;
 #endif
 
 #if defined(CONFIG_SBPCD)
          case MATSUSHITA_CDROM_MAJOR:
                if (!check_sbpcd_media_change(dev, 0))
-                       return;
+                       return 0;
                break;
 #endif
 
          default:
-               return;
+               return 0;
        }
 #endif /* will go away */
 
@@ -206,13 +176,7 @@ void check_disk_change(dev_t dev)
 
        if (fops->revalidate)
                fops->revalidate(dev);
-
-#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
-/* This is trickier for a removable hardisk, because we have to invalidate
-   all of the partitions that lie on the disk. */
-       if (MAJOR(dev) == SCSI_DISK_MAJOR)
-               revalidate_scsidisk(dev, 0);
-#endif
+       return 1;
 }
 
 /*
index cf57fa6a9c84a4fed981aaf54b000b12bdc5417f..5387e34666d3441fe1571cf5e953c5c4fed75887 100644 (file)
@@ -305,35 +305,8 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
                printk("get root inode failed\n");
                return NULL;
        }
-#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
-       if (MAJOR(s->s_dev) == SCSI_CDROM_MAJOR) {
-               /* Check this one more time. */
-               if(check_cdrom_media_change(s->s_dev, 0))
-                 goto out;
-       }
-#endif
-#if defined(CONFIG_CDU31A)
-       if (MAJOR(s->s_dev) == CDU31A_CDROM_MAJOR) {
-               /* Check this one more time. */
-               if(check_cdu31a_media_change(s->s_dev, 0))
-                 goto out;
-       }
-#endif
-#if defined(CONFIG_MCD)
-       if (MAJOR(s->s_dev) == MITSUMI_CDROM_MAJOR) {
-               /* Check this one more time. */
-               if(check_mcd_media_change(s->s_dev, 0))
-                 goto out;
-       }
-#endif
-#if defined(CONFIG_SBPCD)
-       if (MAJOR(s->s_dev) == MATSUSHITA_CDROM_MAJOR) {
-               if (check_sbpcd_media_change(s->s_dev,0))
-                 goto out;
-       };
-#endif CONFIG_SBPCD
 
-       return s;
+       if(!check_disk_change(s->s_dev)) return s;
  out: /* Kick out for various error conditions */
        brelse(bh);
        s->s_dev = 0;
index 8edbdaa82bfb50febd4b2323a79d8ef26cfcfbe3..9e622775d485bdc19d3a8f2a4aacdb392239c45a 100644 (file)
@@ -200,8 +200,6 @@ static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l,
 
        if (!filp->f_inode)     /* just in case */
                return 0;
-       if (!S_ISREG(filp->f_inode->i_mode))
-               return 0;
        if (l->l_type != F_UNLCK && l->l_type != F_RDLCK && l->l_type != F_WRLCK
         && l->l_type != F_SHLCK && l->l_type != F_EXLCK)
                return 0;
@@ -378,7 +376,7 @@ next_lock:
 
        if (! added) {
                if (caller->fl_type == F_UNLCK)
-                       return -EINVAL;
+                       return 0;
                if (! (caller = alloc_lock(before, caller, fd)))
                        return -ENOLCK;
        }
index 801fdc57e9cd2337c8a7b17b4c46c93db3e404ef..9f97f62e3a2b80d4c4550a74fa44e6d04263f56b 100644 (file)
@@ -4,6 +4,13 @@
  *  Copyright (C) 1992, 1993  Rick Sladkey
  *
  *  low-level nfs remote procedure call interface
+ *
+ * FIXES
+ *
+ * 2/7/94 James Bottomley and Jon Peatfield DAMTP, Cambridge University
+ *
+ * An xid mismatch no longer causes the request to be trashed.
+ *
  */
 
 #include <linux/config.h>
 #include <asm/segment.h>
 #include <linux/in.h>
 #include <linux/net.h>
+#include <linux/mm.h>
+
+/* JEJB/JSP 2/7/94
+ * this must match the value of NFS_SLACK_SPACE in linux/fs/nfs/proc.c 
+ * ***FIXME*** should probably put this in nfs_fs.h */
+#define NFS_SLACK_SPACE 1024
 
 
 extern struct socket *socki_lookup(struct inode *inode);
@@ -51,6 +64,9 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
        int n;
        int addrlen;
        unsigned long old_mask;
+       /* JEJB/JSP 2/7/94
+        * This is for a 4 byte recv of the xid only */
+       int recv_xid;
 
        xid = start[0];
        len = ((char *) end) - ((char *) start);
@@ -92,8 +108,14 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
                current->state = TASK_INTERRUPTIBLE;
                if (!select(inode, file, SEL_IN, &wait_table)
                    && !select(inode, file, SEL_IN, NULL)) {
-                       if (timeout > max_timeout)
-                               timeout = max_timeout;
+                       if (timeout > max_timeout) {
+                         /* JEJB/JSP 2/7/94
+                          * This is useful to see if the system is
+                          * hanging */
+                         printk("NFS max timeout reached on %s\n",
+                                server_name);
+                         timeout = max_timeout;
+                       }
                        current->timeout = jiffies + timeout;
                        schedule();
                        remove_wait_queue(entry.wait_address, &entry.wait);
@@ -116,8 +138,8 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
                                timeout = init_timeout;
                                init_timeout <<= 1;
                                if (!major_timeout_seen) {
-                                       printk("NFS server %s not responding, "
-                                               "still trying\n", server_name);
+                                 printk("NFS server %s not responding, "
+                                        "still trying\n", server_name);
                                }
                                major_timeout_seen = 1;
                                continue;
@@ -129,8 +151,13 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
                        remove_wait_queue(entry.wait_address, &entry.wait);
                current->state = TASK_RUNNING;
                addrlen = 0;
-               result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0,
-                       NULL, &addrlen);
+               /* JEJB/JSP 2/7/94
+                * Get the xid from the next packet using a peek, so keep it
+                * on the recv queue.  If it is wrong, it will be some reply
+                * we don't now need, so discard it */
+               result = sock->ops->recvfrom(sock, (void *)&recv_xid,
+                                            sizeof(recv_xid), 1, MSG_PEEK,
+                                            NULL, &addrlen);
                if (result < 0) {
                        if (result == -EAGAIN) {
 #if 0
@@ -150,15 +177,41 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
                        }
                        break;
                }
-               if (*start == xid) {
+               if (recv_xid == xid) {
                        if (major_timeout_seen)
                                printk("NFS server %s OK\n", server_name);
                        break;
                }
+               /* JEJB/JSP 2/7/94
+                * we have xid mismatch, so discard the packet and start
+                * again.  What a hack! but I can't call recvfrom with
+                * a null buffer yet. */
+               (void)sock->ops->recvfrom(sock, (void *)&recv_xid,
+                                         sizeof(recv_xid), 1, 0, NULL,
+                                         &addrlen);
 #if 0
                printk("nfs_rpc_call: XID mismatch\n");
 #endif
        }
+       /* JEJB/JSP 2/7/94
+        *
+        * we have the correct xid, so read into the correct place and
+        * return it
+        *
+        * Here we need to know the size given to alloc, server->wsize for
+        * writes or server->rsize for reads.  In practice these are the
+        * same. 
+        *
+        * If they are not the same then a reply to a write request will be
+        * a small acknowledgement, so even if wsize < rsize we should never
+        * cause data to be written past the end of the buffer (unless some
+        * brain damaged implementation sends out a large write acknowledge).
+        *
+        * FIXME:  I should really know how big a packet was alloc'd --
+        *         should pass it to do_nfs_rpc. */
+       result=sock->ops->recvfrom(sock, (void *)start, 
+                                 server->rsize + NFS_SLACK_SPACE, 1, 0, NULL,
+                                 &addrlen);
        current->blocked = old_mask;
        set_fs(fs);
        return result;
index 646c5528e216bc957bed5c864cf665098391b6c7..893af39a96d71f03a10dd93da27b4529d61600a5 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -448,7 +448,7 @@ int close_fp(struct file *filp, unsigned int fd)
                return 0;
        }
        inode = filp->f_inode;
-       if (inode && S_ISREG(inode->i_mode))
+       if (inode)
                fcntl_remove_locks(current, filp, fd);
        if (filp->f_count > 1) {
                filp->f_count--;
index 79f151c3bced1894bc686aaf5f97d4710ec50f1b..3ed49018d02a806daf2d66b7942184003d74c51f 100644 (file)
@@ -413,7 +413,7 @@ extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag)
 }
 
 
-extern void check_disk_change(dev_t dev);
+extern int check_disk_change(dev_t dev);
 extern void invalidate_inodes(dev_t dev);
 extern void invalidate_buffers(dev_t dev);
 extern void sync_inodes(dev_t dev);
index 224cbdd3330adca2f6e4eb7e9387ef7408dc0fb3..975a3f840a565f156b2832159bcb35bcc3da7751 100644 (file)
@@ -28,7 +28,7 @@
 #define MCD_INTR_NR            10
 
 /* Increase this if you get lots of timeouts */
-#define MCD_STATUS_DELAY       100
+#define MCD_STATUS_DELAY       200
 
 /* number of times to retry a command before giving up */
 #define MCD_RETRY_ATTEMPTS      5
index 7328f3874e5ea6646ae36cdb0a744b7ff90e186d..5d417765fa7634d9eea62c7a1af4797883a00a1d 100644 (file)
@@ -203,6 +203,7 @@ struct      mtpos {
 #define MT_ST_ASYNC_WRITES     0x2
 #define MT_ST_READ_AHEAD       0x4
 #define MT_ST_DEBUGGING                0x8
+#define MT_ST_TWO_FM           0x10
 
 #endif /* _LINUX_MTIO_H */
 
index b8328554e1cedb9a7f9f9f1a8de68afd301d7d63..583817cdd205c08ee6b1d25decf942bffc031852 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/sys.h>
 #include <linux/utsname.h>
 #include <linux/interrupt.h>
+#include <linux/timer.h>
 #include <linux/binfmts.h>
 #include <linux/personality.h>
 #include <linux/module.h>
@@ -122,6 +123,8 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
        X(disable_irq),
        X(bh_active),
        X(bh_mask),
+       X(add_timer),
+       X(del_timer),
 
        /* dma handling */
        X(request_dma),
@@ -136,6 +139,7 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
        X(xtime),
 
        /* misc */
+       X(panic),
        X(printk),
        X(sprintf),
        X(vsprintf),
index f6062c5dabb0e7401b9a405d190c9fd47cb64080..1760fc15e36914890d62322e5a2b0692531c9182 100644 (file)
  *             Florian La Roche, <flla@stud.uni-sb.de>
  *             Alan Cox, <A.Cox@swansea.ac.uk>
  *
+ * Changes (see also sock.c)
+ *
+ *             A.N.Kuznetsov   :       Socket death error in accept().
+ *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; either version
@@ -975,6 +979,7 @@ static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
        {
                err = -sk2->err;
                sk2->err=0;
+               sk2->dead=1;    /* ANK */
                destroy_sock(sk2);
                newsock->data = NULL;
                return(err);
index 46caae53256a7c4734dfb642340cef36b3acfcfd..b928abaf40164fa3fbadc408b4cbe5a1405cd22a 100644 (file)
@@ -370,7 +370,7 @@ static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device
        skb2->free = 1;
 
        /* Build Layer 2-3 headers for message back to source */
-       offset = ip_build_header(skb2, daddr, saddr, &ndev,
+       offset = ip_build_header(skb2, daddr, dev->pa_addr, &ndev,
                IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
        if (offset < 0) 
        {
@@ -586,12 +586,6 @@ int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
        
        icmp_statistics.IcmpInMsgs++;
         
-       if (ip_chk_addr(daddr) == IS_BROADCAST) 
-       {
-               icmp_statistics.IcmpInErrors++;
-               kfree_skb(skb1, FREE_READ);
-               return(0);
-       }
        
        /*
         *      Grab the packet as an icmp object
@@ -617,6 +611,13 @@ int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
         *      Parse the ICMP message 
         */
 
+       if (ip_chk_addr(daddr) == IS_BROADCAST && icmph->type != ICMP_ECHO) 
+       {
+               icmp_statistics.IcmpInErrors++;
+               kfree_skb(skb1, FREE_READ);
+               return(0);
+       }
+
        switch(icmph->type) 
        {
                case ICMP_TIME_EXCEEDED: