patch-2.0.31 linux/drivers/scsi/wd7000.c

Next file: linux/drivers/scsi/wd7000.h
Previous file: linux/drivers/scsi/ultrastor.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
@@ -127,6 +127,10 @@
  *
  *  Thanks to Roger Scott for driver debugging.
  *
+ *  06/07/1997
+ *
+ *  Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ *  Now, driver can handle hard disks with capacity >1GB.
  */
 
 #ifdef MODULE
@@ -149,17 +153,23 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
 #define ANY2SCSI_INLINE		/* undef this to use old macros */
-#undef DEBUG
+#undef BIOSPARAM_DEBUG
+#undef DEBUG                    /* general debug */
 
 #include "wd7000.h"
-
 #include<linux/stat.h>
 
-struct proc_dir_entry proc_scsi_wd7000 = {
-    PROC_SCSI_7000FASST, 6, "wd7000",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+
+struct proc_dir_entry proc_scsi_wd7000 =
+{
+    PROC_SCSI_7000FASST,
+    6,
+    "wd7000",
+    S_IFDIR | S_IRUGO | S_IXUGO,
+    2
 };
 
 
@@ -210,12 +220,19 @@
 } Adapter;
 
 /*
+ * possible irq range
+ */
+#define IRQ_MIN   3
+#define IRQ_MAX   15
+#define IRQS      (IRQ_MAX-IRQ_MIN + 1)
+
+/*
  * The following is set up by wd7000_detect, and used thereafter by
  * wd7000_intr_handle to map the irq level to the corresponding Adapter.
  * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
  * changed to pick up the IRQ level correctly.
  */
-Adapter *irq2host[16] = {NULL};	/* Possible IRQs are 0-15 */
+static struct Scsi_Host *wd7000_host[IRQS];
 
 /*
  * (linear) base address for ROM BIOS
@@ -828,7 +845,8 @@
     cli();
 
     memset(scb, 0, sizeof(Scb));
-    scb->next = scbfree;  scbfree = scb;
+    scb->next = scbfree;
+    scbfree = scb;
     freescbs++;
 
     restore_flags(flags);
@@ -969,6 +987,7 @@
 #ifdef DEBUG
     printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
 #endif
+
     SCpnt->SCp.phase = 0;
 }
 
@@ -982,7 +1001,7 @@
     register Scb *scb;             /* for SCSI commands */
     register IcbAny *icb;          /* for host commands */
     register Scsi_Cmnd *SCpnt;
-    Adapter *host = irq2host[irq];  /* This MUST be set!!! */
+    Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */
     Mailbox *icmbs = host->mb.icmb;
 
 #ifdef DEBUG
@@ -990,6 +1009,7 @@
 #endif
 
     flag = inb(host->iobase+ASC_INTR_STAT);
+
 #ifdef DEBUG
     printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag);
 #endif
@@ -1264,6 +1284,144 @@
 }
 
 
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+{
+    unsigned long flags;
+
+    save_flags (flags);
+    cli ();
+
+#ifdef DEBUG
+    printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+#endif
+
+    /*
+     * Currently this is a no-op
+     */
+    printk ("Sorry, this function is currently out of order...\n");
+
+    restore_flags (flags);
+
+    return (length);
+}
+
+
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+    struct Scsi_Host *host = NULL;
+    Scsi_Device *scd = scsi_devices;
+    Adapter *adapter;
+    Mailbox *ogmbs, *icmbs;
+    unsigned long flags;
+    char *pos = buffer;
+    short i;
+
+#ifdef DEBUG
+    short count;
+#endif
+
+    /*
+     * Find the specified host board.
+     */
+    for (i = 0; i < IRQS; i++)
+        if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
+            host = wd7000_host[i];
+
+	    break;
+	}
+
+    /*
+     * Host not found!
+     */
+    if (! host)
+        return (-ESRCH);
+
+    /*
+     * Has data been written to the file ?
+     */ 
+    if (inout)
+        return (wd7000_set_info (buffer, length, host));
+
+    adapter = (Adapter *) host->hostdata;
+    ogmbs = adapter->mb.ogmb;
+    icmbs = adapter->mb.icmb;
+
+    save_flags (flags);
+    cli ();
+
+    SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
+    SPRINTF ("  IO base:     0x%x\n", adapter->iobase);
+    SPRINTF ("  IRQ:         %d\n", adapter->irq);
+    SPRINTF ("  DMA channel: %d\n", adapter->dma);
+
+#ifdef DEBUG
+    SPRINTF ("Control port value: 0x%x\n", adapter->control);
+    SPRINTF ("Incoming mailbox:\n");
+    SPRINTF ("  size: %d\n", ICMB_CNT);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < ICMB_CNT; i++)
+        if (icmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+
+    SPRINTF ("Outgoing mailbox:\n");
+    SPRINTF ("  size: %d\n", OGMB_CNT);
+    SPRINTF ("  next message: 0x%x\n", adapter->next_ogmb);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < OGMB_CNT; i++)
+        if (ogmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+#endif
+
+    /*
+     * Display driver information for each device attached to the board.
+     */
+    SPRINTF ("Attached devices: %s\n", scd ? "" : "none");
+
+    for ( ; scd; scd = scd->next)
+        if (scd->host->host_no == hostno) {
+	    SPRINTF ("  [Channel: %02d, Id: %02d, Lun: %02d]  ",
+		     scd->channel, scd->id, scd->lun);
+	    SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+		     scsi_device_types[(short) scd->type] : "Unknown device");
+
+	    for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->vendor[i]);
+	    SPRINTF (" ");
+
+	    for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->model[i]);
+	    SPRINTF ("\n");
+        }
+
+    restore_flags (flags);
+
+    /*
+     * Calculate start of next buffer, and return value.
+     */
+    *start = buffer + offset;
+
+    if ((pos - buffer) < offset)
+        return (0);
+    else if ((pos - buffer - offset) < length)
+        return (pos - buffer - offset);
+    else
+        return (length);
+}
+
+
 /*
  *  Returns the number of adapters this driver is supporting.
  *
@@ -1282,9 +1440,11 @@
     Adapter *host = NULL;
     struct Scsi_Host *sh;
 
+    for (i = 0; i < IRQS; wd7000_host[i++] = NULL);
     for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
 
     tpnt->proc_dir = &proc_scsi_wd7000;
+    tpnt->proc_info = &wd7000_proc_info;
 
     /*
      * Set up SCB free list, which is shared by all adapters
@@ -1292,6 +1452,9 @@
     init_scbs ();
 
     for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) {
+        /*
+	 * First, search for BIOS SIGNATURE...
+	 */
         for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
 	    for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
 	        for (i = 0; i < pass; i++)
@@ -1306,7 +1469,9 @@
 	    }
 
 bios_matched:
-
+	/*
+	 * BIOS SIGNATURE has been found.
+	 */
 #ifdef DEBUG
 	printk ("wd7000_detect: pass %d\n", pass + 1);
 
@@ -1335,7 +1500,6 @@
 #ifdef DEBUG
  	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
 #endif
-
  	    /*
   	     * ASC reset...
  	     */
@@ -1367,8 +1531,7 @@
 		host = (Adapter *) sh->hostdata;
 
 #ifdef DEBUG
-		printk ("wd7000_detect: adapter allocated at 0x%x\n",
-			(int) host);
+		printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
 #endif
 
 		memset (host, 0, sizeof (Adapter));
@@ -1382,9 +1545,8 @@
 		    host->dma = configs[cfg_ptr - 1].dma;
 		}
 
-	    	host->sh = sh;
+	    	host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
     		host->iobase = iobase;
-		irq2host[host->irq] = host;
 
 #ifdef DEBUG
 		printk ("wd7000_detect: Trying init WD-7000 card at IO "
@@ -1409,8 +1571,7 @@
 		request_region (host->iobase, 4, "wd7000");
 
 		/*
-		 *  For boards before rev 6.0, scatter/gather
-		 *  isn't supported.
+		 *  For boards before rev 6.0, scatter/gather isn't supported.
 		 */
 		if (host->rev1 < 6)
 		    sh->sg_tablesize = SG_NONE;
@@ -1429,8 +1590,7 @@
 
 #ifdef DEBUG
 	else
- 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n",
-		    iobase);
+ 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
 #endif
 
     }
@@ -1469,18 +1629,55 @@
 
 
 /*
- *  This was borrowed directly from aha1542.c, but my disks are organized
- *  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(Disk * disk, kdev_t dev, int* ip)
-{
-  int size = disk->capacity;
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+ *  This was borrowed directly from aha1542.c. (Zaga)
+ */
+int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
+{
+#ifdef BIOSPARAM_DEBUG
+    printk("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
+#endif
+
+    /*
+     * try default translation
+     */
+    ip[0] = 64;
+    ip[1] = 32;
+    ip[2] = disk->capacity / (64 * 32);
+
+    /*
+     * for disks >1GB do some guessing
+     */
+    if (ip[2] >= 1024) {
+	int info[3];
+
+	/*
+	 * try to figure out the geometry from the partition table
+	 */
+	if ((scsicam_bios_param (disk, dev, info) < 0) ||
+	    !(((info[0] == 64) && (info[1] == 32)) ||
+	    ((info[0] == 255) && (info[1] == 63)))) {
+	    printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n"
+		    "                  using extended translation.\n");
+
+	    ip[0] = 255;
+	    ip[1] = 63;
+	    ip[2] = disk->capacity / (255 * 63);
+	} else {
+	    ip[0] = info[0];
+	    ip[1] = info[1];
+	    ip[2] = info[2];
+
+	    if (info[0] == 255)
+		printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+	}
+    }
+
+#ifdef BIOSPARAM_DEBUG
+    printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+    printk ("WARNING: check, if the bios geometry is correct.\n");
+#endif
+
+    return (0);
 }
 
 #ifdef MODULE

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov