patch-2.4.9 linux/drivers/scsi/aic7xxx/aic7xxx_linux.c

Next file: linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c
Previous file: linux/drivers/scsi/aic7xxx/aic7xxx_inline.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.8/linux/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
@@ -1,7 +1,7 @@
 /*
  * Adaptec AIC7xxx device driver for Linux.
  *
- * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux.c#66 $
+ * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux.c#72 $
  *
  * Copyright (c) 1994 John Aycock
  *   The University of Calgary Department of Computer Science.
@@ -52,7 +52,7 @@
  *    derived from this software without specific prior written permission.
  *
  * Alternatively, this software may be distributed under the terms of the
- * GNU Public License ("GPL").
+ * GNU General Public License ("GPL").
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -113,8 +113,6 @@
  *
  */
 
-#include <linux/config.h>
-
 /*
  * The next three defines are user configurable.  These should be the only
  * defines a user might need to get in here and change.  There are other
@@ -122,14 +120,10 @@
  * under normal conditions.
  */
 
-#if defined(MODULE) || defined(PCMCIA)
+#if defined(MODULE)
 #include <linux/module.h>
 #endif
 
-#if defined(PCMCIA)
-#undef MODULE
-#endif
-
 #include "aic7xxx_osm.h"
 #include "aic7xxx_inline.h"
 
@@ -139,6 +133,8 @@
 
 #include "../sd.h"		/* For geometry detection */
 
+#include <linux/mm.h>		/* For fetching system memory size */
+
 /*
  * To generate the correct addresses for the controller to issue
  * on the bus.  Originally added for DEC Alpha support.
@@ -483,7 +479,8 @@
 static void ahc_linux_handle_scsi_status(struct ahc_softc *,
 					 struct ahc_linux_device *,
 					 struct scb *);
-static void ahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd);
+static void ahc_linux_filter_command(struct ahc_softc*, Scsi_Cmnd*,
+				     struct scb*);
 static void ahc_linux_sem_timeout(u_long arg);
 static void ahc_linux_freeze_sim_queue(struct ahc_softc *ahc);
 static void ahc_linux_release_sim_queue(u_long arg);
@@ -517,9 +514,14 @@
 						  struct ahc_cmd *acmd);
 static __inline void ahc_linux_check_device_queue(struct ahc_softc *ahc,
 						  struct ahc_linux_device *dev);
-static __inline void ahc_linux_sniff_command(struct ahc_softc*, Scsi_Cmnd*);
+static __inline void ahc_linux_sniff_command(struct ahc_softc*, Scsi_Cmnd*,
+					     struct scb*);
 static __inline void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
 
+static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+		 		      struct ahc_dma_seg *sg,
+				      bus_addr_t addr, bus_size_t len);
+
 static __inline struct ahc_linux_device*
 ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
 	       u_int lun, int alloc)
@@ -628,15 +630,15 @@
 
 	while ((ahc->flags & AHC_RESOURCE_SHORTAGE) == 0
 	    && ahc->platform_data->qfrozen == 0
-	    && (dev = LIST_FIRST(&ahc->platform_data->device_runq)) != NULL) {
-		LIST_REMOVE(dev, links);
+	    && (dev = TAILQ_FIRST(&ahc->platform_data->device_runq)) != NULL) {
+		TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
 		dev->flags &= ~AHC_DEV_ON_RUN_LIST;
 		ahc_linux_check_device_queue(ahc, dev);
 	}
 }
 
 static __inline void
-ahc_linux_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+ahc_linux_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
 {
 	/*
 	 * Determine whether we care to filter
@@ -645,7 +647,7 @@
 	 * heavy weight processing.
 	 */
 	if (cmd->cmnd[0] == INQUIRY)
-		ahc_linux_filter_command(ahc, cmd);
+		ahc_linux_filter_command(ahc, cmd, scb);
 }
 
 static __inline void
@@ -654,6 +656,7 @@
 	Scsi_Cmnd *cmd;
 
 	cmd = scb->io_ctx;
+	ahc_sync_sglist(ahc, scb, BUS_DMASYNC_POSTWRITE);
 	if (cmd->use_sg != 0) {
 		struct scatterlist *sg;
 
@@ -667,6 +670,48 @@
 				 scsi_to_pci_dma_dir(cmd->sc_data_direction));
 }
 
+static __inline int
+ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+		  struct ahc_dma_seg *sg, bus_addr_t addr, bus_size_t len)
+{
+	int	 consumed;
+
+	if ((scb->sg_count + 1) > AHC_NSEG)
+		panic("Too few segs for dma mapping.  "
+		      "Increase AHC_NSEG\n");
+
+	consumed = 1;
+	sg->addr = ahc_htole32(addr & 0xFFFFFFFF);
+	scb->platform_data->xfer_len += len;
+	if (sizeof(bus_addr_t) > 4
+	 && (ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+		/*
+		 * Due to DAC restrictions, we can't
+		 * cross a 4GB boundary.
+		 */
+		if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) {
+			struct	 ahc_dma_seg *next_sg;
+			uint32_t next_len;
+
+			printf("Crossed Seg\n");
+			if ((scb->sg_count + 2) > AHC_NSEG)
+				panic("Too few segs for dma mapping.  "
+				      "Increase AHC_NSEG\n");
+
+			consumed++;
+			next_sg = sg + 1;
+			next_sg->addr = 0;
+			next_len = 0x100000000 - (addr & 0xFFFFFFFF);
+			len -= next_len;
+			next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000;
+			next_sg->len = ahc_htole32(next_len);
+		}
+		len |= (addr >> 8) & 0x7F000000;
+	}
+	sg->len = ahc_htole32(len);
+	return (consumed);
+}
+
 /************************ Shutdown/halt/reboot hook ***************************/
 #include <linux/notifier.h>
 #include <linux/reboot.h>
@@ -736,9 +781,26 @@
 	map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
 	if (map == NULL)
 		return (ENOMEM);
+	/*
+	 * Although we can dma data above 4GB, our
+	 * "consistent" memory is below 4GB for
+	 * space efficiency reasons (only need a 4byte
+	 * address).  For this reason, we have to reset
+	 * our dma mask when doing allocations.
+	 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+	pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF);
+#else
+	ahc->dev_softc->dma_mask = 0xFFFFFFFF;
+#endif
 	*vaddr = pci_alloc_consistent(ahc->dev_softc,
 				      dmat->maxsize, &map->bus_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+	pci_set_dma_mask(ahc->dev_softc, ahc->platform_data->hw_dma_mask);
 #else
+	ahc->dev_softc->dma_mask = ahc->platform_data->hw_dma_mask;
+#endif
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */
 	/*
 	 * At least in 2.2.14, malloc is a slab allocator so all
 	 * allocations are aligned.  We assume, for these kernel versions
@@ -1131,6 +1193,15 @@
 	return (0);
 }
 
+uint64_t
+ahc_linux_get_memsize()
+{
+	struct sysinfo si;
+
+	si_meminfo(&si);
+	return (si.totalram << PAGE_SHIFT);
+}
+
 /*
  * Find the smallest available unit number to use
  * for a new device.  We don't just use a static
@@ -1240,7 +1311,8 @@
 		return (ENOMEM);
 	memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
 	TAILQ_INIT(&ahc->platform_data->completeq);
-	LIST_INIT(&ahc->platform_data->device_runq);
+	TAILQ_INIT(&ahc->platform_data->device_runq);
+	ahc->platform_data->hw_dma_mask = 0xFFFFFFFF;
 	ahc_lockinit(ahc);
 	ahc_done_lockinit(ahc);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
@@ -1305,6 +1377,8 @@
 	dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
 				   devinfo->target,
 				   devinfo->lun, /*alloc*/FALSE);
+	if (dev == NULL)
+		return;
 	was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
 	now_queuing = alg != AHC_QUEUE_NONE;
 	if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
@@ -1516,7 +1590,11 @@
 	}
 	cmd->result = CAM_REQ_INPROG << 16;
 	TAILQ_INSERT_TAIL(&dev->busyq, (struct ahc_cmd *)cmd, acmd_links.tqe);
-	ahc_linux_run_device_queue(ahc, dev);
+	if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+		TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+		dev->flags |= AHC_DEV_ON_RUN_LIST;
+		ahc_linux_run_device_queues(ahc);
+	}
 	ahc_unlock(ahc, &flags);
 	return (0);
 }
@@ -1532,6 +1610,9 @@
 	struct	 ahc_tmode_tstate *tstate;
 	uint16_t mask;
 
+	if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+		panic("running device on run list");
+
 	while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
 	    && dev->openings > 0 && dev->qfrozen == 0) {
 
@@ -1540,11 +1621,9 @@
 		 * running is because the whole controller Q is frozen.
 		 */
 		if (ahc->platform_data->qfrozen != 0) {
-			if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
-				return;
 
-			LIST_INSERT_HEAD(&ahc->platform_data->device_runq,
-					 dev, links);
+			TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+					  dev, links);
 			dev->flags |= AHC_DEV_ON_RUN_LIST;
 			return;
 		}
@@ -1552,14 +1631,10 @@
 		 * Get an scb to use.
 		 */
 		if ((scb = ahc_get_scb(ahc)) == NULL) {
-			if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
-				panic("running device on run list");
-			LIST_INSERT_HEAD(&ahc->platform_data->device_runq,
+			TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
 					 dev, links);
 			dev->flags |= AHC_DEV_ON_RUN_LIST;
 			ahc->flags |= AHC_RESOURCE_SHORTAGE;
-			printf("%s: Temporary Resource Shortage\n",
-			       ahc_name(ahc));
 			return;
 		}
 		TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
@@ -1611,6 +1686,8 @@
 		}
 
 		scb->platform_data->xfer_len = 0;
+		ahc_set_residual(scb, 0);
+		ahc_set_sense_residual(scb, 0);
 		if (cmd->use_sg != 0) {
 			struct	ahc_dma_seg *sg;
 			struct	scatterlist *cur_seg;
@@ -1623,13 +1700,22 @@
 			end_seg = cur_seg + nseg;
 			/* Copy the segments into the SG list. */
 			sg = scb->sg_list;
+			/*
+			 * The sg_count may be larger than nseg if
+			 * a transfer crosses a 32bit page.
+			 */ 
+			scb->sg_count = 0;
 			while(cur_seg < end_seg) {
-				sg->addr = ahc_htole32(sg_dma_address(cur_seg));
-/* XXX Add in the 5th byte of the address later.*/
-				sg->len = ahc_htole32(sg_dma_len(cur_seg));
-				scb->platform_data->xfer_len +=
-				    sg_dma_len(cur_seg);
-				sg++;
+				bus_addr_t addr;
+				bus_size_t len;
+				int consumed;
+
+				addr = sg_dma_address(cur_seg);
+				len = sg_dma_len(cur_seg);
+				consumed = ahc_linux_map_seg(ahc, scb,
+							     sg, addr, len);
+				sg += consumed;
+				scb->sg_count += consumed;
 				cur_seg++;
 			}
 			sg--;
@@ -1647,24 +1733,19 @@
 			 */
 			scb->hscb->dataptr = scb->sg_list->addr;
 			scb->hscb->datacnt = scb->sg_list->len;
-
-			/*
-			 * Remember the number of segments for later
-			 * residual calculations.
-			 */
-			scb->sg_count = nseg;
 		} else if (cmd->request_bufflen != 0) {
 			struct	 ahc_dma_seg *sg;
-			uint32_t baddr;
+			bus_addr_t addr;
 
 			sg = scb->sg_list;
-			baddr = pci_map_single(ahc->dev_softc,
+			addr = pci_map_single(ahc->dev_softc,
 			       cmd->request_buffer,
 			       cmd->request_bufflen,
 			       scsi_to_pci_dma_dir(cmd->sc_data_direction));
-			sg->addr = ahc_htole32(baddr);
-			sg->len = ahc_htole32(cmd->request_bufflen
-					      | AHC_DMA_LAST_SEG);
+			scb->sg_count = ahc_linux_map_seg(ahc, scb,
+							  sg, addr,
+							  cmd->request_bufflen);
+			sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
 
 			/*
 			 * Reset the sg list pointer.
@@ -1678,13 +1759,6 @@
 			 */
 			scb->hscb->dataptr = sg->addr;
 			scb->hscb->datacnt = sg->len;
-			scb->platform_data->xfer_len = cmd->request_bufflen;
-
-			/*
-			 * Remember the number of segments for later
-			 * residual calculations.
-			 */
-			scb->sg_count = 1;
 		} else {
 			scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
 			scb->hscb->dataptr = 0;
@@ -1692,6 +1766,7 @@
 			scb->sg_count = 0;
 		}
 
+		ahc_sync_sglist(ahc, scb, BUS_DMASYNC_PREWRITE);
 		LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
 		dev->openings--;
 		dev->active++;
@@ -1936,7 +2011,10 @@
 		break;
         case AC_BUS_RESET:
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
-		scsi_report_bus_reset(ahc->platform_data->host, channel - 'A');
+		if (ahc->platform_data->host != NULL) {
+			scsi_report_bus_reset(ahc->platform_data->host,
+					      channel - 'A');
+		}
 #endif
                 break;
         default:
@@ -1978,6 +2056,15 @@
 		memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb),
 		       MIN(sizeof(struct scsi_sense_data),
 			   sizeof(cmd->sense_buffer)));
+		cmd->result |= (DRIVER_SENSE << 24);
+	} else {
+		/*
+		 * Guard against stale sense data.
+		 * The Linux mid-layer assumes that sense
+		 * was retrieved anytime the first byte of
+		 * the sense buffer looks "sane".
+		 */
+		cmd->sense_buffer[0] = 0;
 	}
 	if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
 		uint32_t amount_xferred;
@@ -1992,7 +2079,7 @@
 			ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
 		} else {
 			ahc_set_transaction_status(scb, CAM_REQ_CMP);
-			ahc_linux_sniff_command(ahc, cmd);
+			ahc_linux_sniff_command(ahc, cmd, scb);
 		}
 	} else if (ahc_get_transaction_status(scb) == DID_OK) {
 		ahc_linux_handle_scsi_status(ahc, dev, scb);
@@ -2031,7 +2118,7 @@
 		 && dev->active == 0)
 			ahc_linux_free_device(ahc, dev);
 	} else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
-		LIST_INSERT_HEAD(&ahc->platform_data->device_runq, dev, links);
+		TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
 		dev->flags |= AHC_DEV_ON_RUN_LIST;
 	}
 
@@ -2127,12 +2214,13 @@
 }
 
 static void
-ahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+ahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
 {
 	switch (cmd->cmnd[0]) {
 	case INQUIRY:
 	{
 		struct	ahc_devinfo devinfo;
+		struct	scsi_inquiry *inq;
 		struct	scsi_inquiry_data *sid;
 		struct	ahc_initiator_tinfo *targ_info;
 		struct	ahc_tmode_tstate *tstate;
@@ -2140,17 +2228,30 @@
 		struct	ahc_linux_device *dev;
 		u_int	scsiid;
 		u_int	maxsync;
+		int	transferred_len;
 		int	minlen;
 		u_int	width;
 		u_int	period;
 		u_int	offset;
 		u_int	ppr_options;
 
+		 /*
+		  * Validate the command.  We only want to filter
+		  * standard inquiry commands, not those querying
+		  * Vital Product Data.
+		  */
+		inq = (struct scsi_inquiry *)cmd->cmnd;
+		if ((inq->byte2 & SI_EVPD) != 0
+		 || inq->page_code != 0)
+			break;
+
 		if (cmd->use_sg != 0) {
 			printf("%s: SG Inquiry response ignored\n",
 			       ahc_name(ahc));
 			break;
 		}
+		transferred_len = ahc_get_transfer_length(scb)
+				- ahc_get_residual(scb);
 		sid = (struct scsi_inquiry_data *)cmd->request_buffer;
 
 		/*
@@ -2163,7 +2264,7 @@
 		dev = ahc_linux_get_device(ahc, cmd->channel,
 					   cmd->target, cmd->lun,
 					   /*alloc*/FALSE);
-		if (cmd->request_bufflen >= 1
+		if (transferred_len >= 1
 		 && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
 
 			dev->flags &= ~AHC_DEV_UNCONFIGURED;
@@ -2184,13 +2285,12 @@
 		targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
 						devinfo.our_scsiid,
 						devinfo.target, &tstate);
-		/* Structure copy */
 		width = targ_info->user.width;
 		period = targ_info->user.period;
 		offset = targ_info->user.offset;
 		ppr_options = targ_info->user.ppr_options;
 		minlen = offsetof(struct scsi_inquiry_data, version) + 1;
-		if (cmd->request_bufflen >= minlen) {
+		if (transferred_len >= minlen) {
 			targ_info->curr.protocol_version = SID_ANSI_REV(sid);
 
 			/*
@@ -2206,7 +2306,7 @@
 		}
 
 		minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
-		if (cmd->request_bufflen >= minlen
+		if (transferred_len >= minlen
 		 && (sid->additional_length + 4) >= minlen) {
 			if ((sid->flags & SID_WBus16) == 0)
 				width = MSG_EXT_WDTR_BUS_8_BIT;
@@ -2231,7 +2331,7 @@
 		 * available.
 		 */
 		if ((sid->additional_length + 4) >= minlen) {
-			if (cmd->request_bufflen >= minlen
+			if (transferred_len >= minlen
 			 && (sid->spi3data & SID_SPI_CLOCK_DT) == 0)
 				ppr_options = 0;
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)