patch-2.4.7 linux/drivers/scsi/megaraid.c

Next file: linux/drivers/scsi/megaraid.h
Previous file: linux/drivers/scsi/hosts.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.6/linux/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c
@@ -9,7 +9,7 @@
  *              as published by the Free Software Foundation; either version
  *              2 of the License, or (at your option) any later version.
  *
- * Version : v1.14g (Feb 5, 2001)
+ * Version : v1.15d(May 30, 2001)
  *
  * Description: Linux device driver for AMI MegaRAID controller
  *
@@ -296,6 +296,63 @@
  *	Version 1.14g-ac2 - 22/03/01
  *	Fixed a non obvious dereference after free in the driver unload path
  *
+ *	Version 1.14i
+ *	changes for making 32bit application run on IA64
+ *
+ *	Version 1.14j
+ *	Tue Mar 13 14:27:54 EST 2001 - AM
+ *	Changes made in the driver to be able to run applications if the
+ *	system has memory >4GB.
+ *
+ *
+ *	Version 1.14k
+ *	Thu Mar 15 18:38:11 EST 2001 - AM
+ *
+ *	Firmware version check removed if subsysid==0x1111 and
+ *	subsysvid==0x1111, since its not yet initialized.
+ *
+ *	changes made to correctly calculate the base in mega_findCard.
+ *
+ *	Driver informational messages now appear on the console as well as
+ *	with dmesg
+ *
+ *	Older ioctl interface is returned failure on newer(2.4.xx) kernels.
+ *
+ *	Inclusion of "modversions.h" is still a debatable question. It is
+ *	included anyway with this release.
+ *
+ *	Version 1.14l
+ *	Mon Mar 19 17:39:46 EST 2001 - AM
+ *
+ *	Assorted changes to remove compilation error in 1.14k when compiled
+ *	with kernel < 2.4.0
+ *
+ *	Version 1.15
+ *	Thu Apr 19 09:38:38 EDT 2001 - AM
+ *
+ *	1.14l rollover to 1.15
+ *
+ *	Version 1.15b
+ *  Wed May 16 20:10:01 EDT 2001 - AM
+ *
+ *	"modeversions.h" is no longer included in the code.
+ *	2.4.xx style mutex initialization used for older kernels also
+ *	Brought in-sync with Alan's changes in 2.4.4
+ *	Note: 1.15a is on OBDR brabch(main trunk), and is not merged with yet.
+ *
+ * Version 1.15c
+ * Mon May 21 23:10:42 EDT 2001 - AM
+ *
+ * ioctl interface uses 2.4.x conforming pci dma calls
+ * similar calls used for older kernels
+ *
+ * Version 1.15d
+ * Wed May 30 17:30:41 EDT 2001 - AM
+ *
+ * NULL is not a valid first argument for pci_alloc_consistent() on
+ * IA64(2.4.3-2.10.1). Code shuffling done in ioctl interface to get
+ * "pci_dev" before making calls to pci interface routines.
+
  * BUGS:
  *     Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that
  *     fails to detect the controller as a pci device on the system.
@@ -399,7 +456,11 @@
 #define cpuid smp_processor_id()
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020100 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+#define scsi_set_pci_device(x,y)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020400 */
 
 /*
  *	Linux 2.4 and higher
@@ -447,10 +508,12 @@
 #define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags);
 
 #define pci_free_consistent(a,b,c,d)
-#define pci_unmap_single(a,b,c,d,e)
+#define pci_unmap_single(a,b,c,d)
+
+#define init_MUTEX_LOCKED(x)    (*(x)=MUTEX_LOCKED)
+#define init_MUTEX(x)           (*(x)=MUTEX)
 
-#define init_MUTEX_LOCKED(x)	((x)=MUTEX_LOCKED)
-#define init_MUTEX(x)		((x)=MUTEX)
+#define pci_enable_device(x) (0)
 
 #define queue_task_irq(a,b)     queue_task(a,b)
 #define queue_task_irq_off(a,b) queue_task(a,b)
@@ -480,10 +543,12 @@
 #define cpu_to_le32(x) (x)
 
 #define pci_free_consistent(a,b,c,d)
-#define pci_unmap_single(a,b,c,d,e)
+#define pci_unmap_single(a,b,c,d)
+
+#define init_MUTEX_LOCKED(x)    (*(x)=MUTEX_LOCKED)
+#define init_MUTEX(x)           (*(x)=MUTEX)
 
-#define init_MUTEX_LOCKED(x)	((x)=MUTEX_LOCKED)
-#define init_MUTEX(x)		((x)=MUTEX)
+#define pci_enable_device(x) (0)
 
 /*
  *	2.0 lacks spinlocks, iounmap/ioremap
@@ -508,6 +573,16 @@
 #endif
 
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020400 */
+#define dma_alloc_consistent pci_alloc_consistent
+#define dma_free_consistent pci_free_consistent
+#else
+typedef unsigned long dma_addr_t;
+void *dma_alloc_consistent(void *, size_t, dma_addr_t *);
+void dma_free_consistent(void *, size_t, void *, dma_addr_t);
+int mega_get_order(int);
+int pow_2(int);
+#endif
 
 /* set SERDEBUG to 1 to enable serial debugging */
 #define SERDEBUG 0
@@ -973,10 +1048,16 @@
 	if ((SCpnt->cmnd[0] == MEGADEVIOC))
 		return megadev_doioctl (megaCfg, SCpnt);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
 	if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD)
-	    || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW))
+		    || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW))
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)  
 		return mega_ioctl (megaCfg, SCpnt);	/* Handle IOCTL command */
+#else
+	{
+		printk(KERN_WARNING "megaraid ioctl: older interface - "
+				"not supported.\n");
+		return NULL;
+	}
 #endif
 
 	islogical = (SCpnt->channel == megaCfg->host->max_channel);
@@ -1407,7 +1488,7 @@
 		 *   cmnd[4..7] = external user buffer     *
 		 *   cmnd[8..11] = length of buffer        *
 		 *                                         */
-		char *user_area = *((char **) &SCpnt->cmnd[4]);
+      	char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]);
 		u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]);
 		switch (data[0]) {
 		case FW_FIRE_WRITE:
@@ -1598,8 +1679,8 @@
 	mbox->xferaddr = virt_to_bus (pScb->sgList);
 	mbox->numsgelements = idx;
 }
+#endif
 
-#endif				/* KERNEL_VERSION(2,3,0) */
 
 #if DEBUG
 static unsigned int cum_time = 0;
@@ -2348,7 +2429,7 @@
 	megaCfg->biosVer[4] = 0;
 #endif
 
-	printk (KERN_INFO "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR,
+	printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR,
 		megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv);
 	/*
 	 * I hope that I can unmap here, reason DMA transaction is not required any more
@@ -2368,7 +2449,7 @@
  * Returns data to be displayed in /proc/scsi/megaraid/X
  *----------------------------------------------------------*/
 
-static int megaraid_proc_info (char *buffer, char **start, off_t offset,
+int megaraid_proc_info (char *buffer, char **start, off_t offset,
 		    int length, int host_no, int inout)
 {
 	*start = buffer;
@@ -2411,7 +2492,7 @@
 #endif
 
 	while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
-		if (pci_enable_device (pdev))
+		if(pci_enable_device (pdev))
 			continue;
 		pciBus = pdev->bus->number;
 		pciDevFun = pdev->devfn;
@@ -2425,11 +2506,16 @@
 				continue;	/* not an AMI board */
 			}
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#if 0
+/*
+ *	This leads to corruption on some HP boards so disable it
+ */
 			pcibios_read_config_dword (pciBus, pciDevFun,
 						   PCI_CONF_AMISIG64, &magic64);
 
 			if (magic64 == AMI_64BIT_SIGNATURE)
 				flag |= BOARD_64BIT;
+#endif				
 #endif
 		}
 
@@ -2451,9 +2537,7 @@
 			pci_read_config_word (pdev,
 					      PCI_SUBSYSTEM_ID, &subsysid);
 #endif
-			if ((subsysid == 0x1111) && (subsysvid == 0x1111) &&
-			    (!strcmp (megaCfg->fwVer, "3.00")
-			     || !strcmp (megaCfg->fwVer, "3.01"))) {
+			if ((subsysid == 0x1111) && (subsysvid == 0x1111)) {
 				printk (KERN_WARNING
 					"megaraid: Your card is a Dell PERC 2/SC RAID controller with firmware\n"
 					"megaraid: 3.00 or 3.01.  This driver is known to have corruption issues\n"
@@ -2476,7 +2560,7 @@
 				continue;
 		}
 
-		printk (KERN_INFO
+		printk (KERN_NOTICE
 			"megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n",
 			pciVendor, pciDev, pciIdx, pciBus, PCI_SLOT (pciDevFun),
 			PCI_FUNC (pciDevFun));
@@ -2499,11 +2583,14 @@
 		pciIdx++;
 
 		if (flag & BOARD_QUARTZ) {
+			megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
 			megaBase = (long) ioremap (megaBase, 128);
 			if (!megaBase)
 				continue;
-		} else
+		} else {
+			megaBase &= PCI_BASE_ADDRESS_IO_MASK;
 			megaBase += 0x10;
+		}
 
 		/* Initialize SCSI Host structure */
 		host = scsi_register (pHostTmpl, sizeof (mega_host_config));
@@ -2514,11 +2601,11 @@
 		megaCfg = (mega_host_config *) host->hostdata;
 		memset (megaCfg, 0, sizeof (mega_host_config));
 
-		printk (KERN_INFO "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d"
+		printk (KERN_NOTICE "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d"
 			M_RD_CRLFSTR, host->host_no, (u_int) megaBase, megaIrq);
 
 		if (flag & BOARD_64BIT)
-			printk (KERN_INFO "scsi%d : Enabling 64 bit support\n",
+			printk (KERN_NOTICE "scsi%d : Enabling 64 bit support\n",
 				host->host_no);
 
 		/* Copy resource info into structure */
@@ -2549,10 +2636,11 @@
 
 		if (!(flag & BOARD_QUARTZ)) {
 			/* Request our IO Range */
-			if (!request_region(megaBase, 16, "megaraid")) {
-				printk (KERN_WARNING "megaraid: Couldn't register I/O range!" M_RD_CRLFSTR);
+			if (check_region (megaBase, 16)) {
+				printk(KERN_WARNING "megaraid: Couldn't register I/O range!\n");
 				goto err_unregister;
 			}
+			request_region(megaBase, 16, "megaraid");
 		}
 
 		/* Request our IRQ */
@@ -2678,7 +2766,7 @@
 		skip_id = (skip_id > 15) ? -1 : skip_id;
 	}
 
-	printk (KERN_INFO "megaraid: " MEGARAID_VERSION M_RD_CRLFSTR);
+	printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION M_RD_CRLFSTR);
 
 	memset (mega_hbas, 0, sizeof (mega_hbas));
 
@@ -2736,7 +2824,7 @@
 /*---------------------------------------------------------------------
  * Release the controller's resources
  *---------------------------------------------------------------------*/
-static int megaraid_release (struct Scsi_Host *pSHost)
+int megaraid_release (struct Scsi_Host *pSHost)
 {
 	mega_host_config *megaCfg;
 	mega_mailbox *mbox;
@@ -3022,7 +3110,7 @@
 /*----------------------------------------------
  * Get information about the card/driver
  *----------------------------------------------*/
-static const char *megaraid_info (struct Scsi_Host *pSHost)
+const char *megaraid_info (struct Scsi_Host *pSHost)
 {
 	static char buffer[512];
 	mega_host_config *megaCfg;
@@ -3052,7 +3140,7 @@
  *   10 01 numstatus byte
  *   11 01 status byte
  *-----------------------------------------------------------------*/
-static int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *))
+int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *))
 {
 	DRIVER_LOCK_T mega_host_config * megaCfg;
 	mega_scb *pScb;
@@ -3063,11 +3151,11 @@
 
 	if (!(megaCfg->flag & (1L << SCpnt->channel))) {
 		if (SCpnt->channel < SCpnt->host->max_channel)
-			printk ( /*KERN_INFO */
+			printk ( KERN_NOTICE
 				"scsi%d: scanning channel %c for devices.\n",
 				megaCfg->host->host_no, SCpnt->channel + '1');
 		else
-			printk ( /*KERN_INFO */
+			printk ( KERN_NOTICE
 				"scsi%d: scanning virtual channel for logical drives.\n",
 				megaCfg->host->host_no);
 
@@ -3136,7 +3224,7 @@
 			init_MUTEX_LOCKED (&pScb->ioctl_sem);
 			spin_unlock_irq (&io_request_lock);
 			down (&pScb->ioctl_sem);
-			user_area = *((char **) &pScb->SCpnt->cmnd[4]);
+    		user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]);
 			if (copy_to_user
 			    (user_area, pScb->buff_ptr, pScb->iDataSize)) {
 				printk
@@ -3178,7 +3266,7 @@
 
 /* shouldn't be used, but included for completeness */
 
-static int megaraid_command (Scsi_Cmnd * SCpnt)
+int megaraid_command (Scsi_Cmnd * SCpnt)
 {
 	internal_done_flag = 0;
 
@@ -3195,7 +3283,7 @@
 /*---------------------------------------------------------------------
  * Abort a previous SCSI request
  *---------------------------------------------------------------------*/
-static int megaraid_abort (Scsi_Cmnd * SCpnt)
+int megaraid_abort (Scsi_Cmnd * SCpnt)
 {
 	mega_host_config *megaCfg;
 	int rc;			/*, idx; */
@@ -3293,7 +3381,7 @@
  * Reset a previous SCSI request
  *---------------------------------------------------------------------*/
 
-static int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags)
+int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags)
 {
 	mega_host_config *megaCfg;
 	int idx;
@@ -3404,7 +3492,7 @@
 	*start = page;
 
 	proc_printf (megaCfg, "Statistical Information for this controller\n");
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020100 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020400 */
 	proc_printf (megaCfg, "Interrupts Collected = %Lu\n",
 		     megaCfg->nInterrupts);
 #else
@@ -3574,7 +3662,7 @@
  *     geom[1] = sectors
  *     geom[2] = cylinders
  *-------------------------------------------------------------*/
-static int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom)
+int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom)
 {
 	int heads, sectors, cylinders;
 	mega_host_config *megaCfg;
@@ -3788,11 +3876,9 @@
 	kdev_t dev;
 	u32 inlen;
 	struct uioctl_t ioc;
-	char *kphysaddr = NULL;
+	char *kvaddr = NULL;
 	int nadap = numCtlrs;
-	int npages;
 	u8 opcode;
-	int order = 0;
 	u32 outlen;
 	int ret;
 	u8 subopcode;
@@ -3800,6 +3886,15 @@
 	struct Scsi_Host *shpnt;
 	char *uaddr;
 	struct uioctl_t *uioc;
+	dma_addr_t	dma_addr;
+	u32		length;
+	mega_host_config *megacfg;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020400 */
+	struct pci_dev pdev;
+	struct pci_dev *pdevp = &pdev;
+#else
+	char *pdevp = NULL;
+#endif
 	IO_LOCK_T;
 
 	if (!inode || !(dev = inode->i_rdev))
@@ -3809,12 +3904,6 @@
 		return (-EINVAL);
 
 	/*
-	 * We do not transfer more than IOCTL_MAX_DATALEN (see megaraid.h) with
-	 * this interface.If the user needs to transfer more than this,he should
-	 * use 0x81 command op-code.
-	 */
-
-	/*
 	 * Get the user ioctl structure
 	 */
 	ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t));
@@ -3826,10 +3915,10 @@
 		return -EFAULT;
 
 	/*
-	 * The first call the applications should make is to find out the number
-	 * of controllers in the system. The next logical call should be for
-	 * getting the list of controllers in the system as detected by the
-	 * driver.
+	 * The first call the applications should make is to find out the
+	 * number of controllers in the system. The next logical call should
+	 * be for getting the list of controllers in the system as detected
+	 * by the driver.
 	 */
 
 	/*
@@ -3892,173 +3981,135 @@
 		adapno = ioc.ui.fcs.adapno;
 
 		/* See comment above: MEGAIOC_QADAPINFO */
-		adapno = GETADAP (adapno);
+		adapno = GETADAP(adapno);
 
 		if (adapno >= numCtlrs)
-			return (-ENODEV);
+			return(-ENODEV);
 
-		/* Check for zero length buffer. */
-		if (!ioc.ui.fcs.length)
+		length = ioc.ui.fcs.length;
+
+		/* Check for zero length buffer or very large buffers */
+		if( !length || length > 32*1024 )
 			return -EINVAL;
 
 		/* save the user address */
 		uaddr = ioc.ui.fcs.buffer;
 
-/*
-* For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of uioctl_t
-* structure are treated as flags. If outlen is 1, the data is
-* transferred from the device and if inlen is 1, the data is
-* transferred to the device.
-*/
+		/*
+		 * For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of
+		 * uioctl_t structure are treated as flags. If outlen is 1, the
+		 * data is transferred from the device and if inlen is 1, the data
+		 * is transferred to the device.
+		 */
 		outlen = ioc.outlen;
 		inlen = ioc.inlen;
-#if 0
-		if (inlen && outlen)
-			return -EINVAL;
-#endif
-		if (outlen) {
-			ret = verify_area (VERIFY_WRITE,
-					   (char *) ioc.ui.fcs.buffer,
-					   ioc.ui.fcs.length);
-			if (ret)
-				return ret;
-		} else if (inlen) {
-			ret = verify_area (VERIFY_READ,
-					   (char *) ioc.ui.fcs.buffer,
-					   ioc.ui.fcs.length);
 
-			if (ret)
-				return ret;
+		if(outlen) {
+			ret = verify_area(VERIFY_WRITE, (char *)ioc.ui.fcs.buffer, length);
+			if (ret) return ret;
+		}
+		if(inlen) {
+			ret = verify_area(VERIFY_READ, (char *) ioc.ui.fcs.buffer, length);
+			if (ret) return ret;
 		}
 
-		/* How many pages required of size PAGE_SIZE */
-		npages = ioc.ui.fcs.length / PAGE_SIZE;
-		/* Do we need one more? */
-
-		if (ioc.ui.fcs.length % PAGE_SIZE)
-			npages++;
-
-		/* ioctl does not support data xfer > 32KB */
-		if (npages == 1)
-			order = 0;
-		else if (npages == 2)
-			order = 1;
-		else if (npages <= 4)
-			order = 2;
-		else if (npages <= 8)
-			order = 3;
-		else
-			return -EINVAL;
-
-		if (outlen || inlen) {
-			/*
-			 * Allocate kernel space for npages.
-			 *
-			 * Since we need the memory for DMA, it needs to be physically
-			 * contiguous. __get_free_pags() return consecutive free pages
-			 * in kernel space.
-			 * Note: We don't do __get_dma_pages(), since for PCI devices,
-			 * the DMA memory is not restriceted to 16M, which is ensured
-			 * by __get_dma_pages()
-			 */
-
-			if ((kphysaddr = (char *) __get_free_pages (GFP_KERNEL,
-								    order)) ==
-			    0) {
-				printk (KERN_INFO
-					"megaraid:allocation failed\n");
-				return -ENOMEM;
-			}
-
-			memset (kphysaddr, 0, npages * PAGE_SIZE);
-			ioc.ui.fcs.buffer = kphysaddr;
-
-			if (inlen) {
-				/* copyin the user data */
-				copy_from_user (kphysaddr,
-						(char *) uaddr,
-						ioc.ui.fcs.length);
+		/*
+		 * Find this host
+		 */
+		for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) {
+			if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) {
+				megacfg = (mega_host_config *)shpnt->hostdata;
+				break;
 			}
 		}
+		if(shpnt == NULL)  return -ENODEV;
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-		scsicmd = (Scsi_Cmnd *) kmalloc (sizeof (Scsi_Cmnd),
-						 GFP_KERNEL | GFP_DMA);
-		memset (scsicmd, 0, sizeof (Scsi_Cmnd));
+		scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA);
 #else
-		scsicmd = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd),
+		scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd),
 							  GFP_ATOMIC | GFP_DMA);
 #endif
-		if (!scsicmd) {
-			if (kphysaddr)
-				free_pages ((unsigned long) kphysaddr, order);
-			return -ENOMEM;
-		}
+		if(scsicmd == NULL) return -ENOMEM;
 
-		scsicmd->host = NULL;
+		memset(scsicmd, 0, sizeof(Scsi_Cmnd));
+		scsicmd->host = shpnt;
 
-		/*
-		 * Find this host
-		 */
-		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
-			if (shpnt->hostdata ==
-			    (unsigned long *) megaCtlrs[adapno])
-				scsicmd->host = shpnt;
-		}
-
-		if (scsicmd->host == NULL) {
+		if( outlen || inlen ) {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-			kfree (scsicmd);
+			pdevp = &pdev;
+			memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev));
+			pdevp->dma_mask = 0xffffffff;
+#else
+			pdevp = NULL;
+#endif
+			kvaddr = dma_alloc_consistent(pdevp, length, &dma_addr);
+
+			if( kvaddr == NULL ) {
+				printk(KERN_WARNING "megaraid:allocation failed\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/*0x20400 */
+				kfree(scsicmd);
 #else
-			scsi_init_free ((char *) scsicmd, sizeof (Scsi_Cmnd));
+				scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
 #endif
-			if (kphysaddr)
-				free_pages ((unsigned long) kphysaddr, order);
-			return -ENODEV;
+				return -ENOMEM;
+			}
+
+			ioc.ui.fcs.buffer = kvaddr;
+
+			if (inlen) {
+				/* copyin the user data */
+				copy_from_user(kvaddr, (char *)uaddr, length );
+			}
 		}
 
 		scsicmd->cmnd[0] = MEGADEVIOC;
-		scsicmd->request_buffer = (void *) &ioc;
+		scsicmd->request_buffer = (void *)&ioc;
 
-		init_MUTEX_LOCKED (&mimd_ioctl_sem);
+		init_MUTEX_LOCKED(&mimd_ioctl_sem);
 
 		IO_LOCK;
-		megaraid_queue (scsicmd, megadev_ioctl_done);
+		megaraid_queue(scsicmd, megadev_ioctl_done);
 
 		IO_UNLOCK;
 
-		down (&mimd_ioctl_sem);
+		down(&mimd_ioctl_sem);
 
-		if (!scsicmd->result && outlen) {
-			copy_to_user (uaddr, kphysaddr, ioc.ui.fcs.length);
+		if( !scsicmd->result && outlen ) {
+			copy_to_user(uaddr, kvaddr, length);
 		}
 
 		/*
 		 * copyout the result
 		 */
-		uioc = (struct uioctl_t *) arg;
+		uioc = (struct uioctl_t *)arg;
 
-		if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
-			put_user (scsicmd->result, &uioc->pthru.scsistatus);
+		if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) {
+			put_user( scsicmd->result, &uioc->pthru.scsistatus );
 		} else {
-			put_user (1, &uioc->mbox[16]);	/* numstatus */
+			put_user(1, &uioc->mbox[16]);	/* numstatus */
 			/* status */
 			put_user (scsicmd->result, &uioc->mbox[17]);
 		}
 
-		if (kphysaddr) {
-			free_pages ((ulong) kphysaddr, order);
+		if (kvaddr) {
+			dma_free_consistent(pdevp, length, kvaddr, dma_addr);
 		}
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/*0x20400 */
 		kfree (scsicmd);
 #else
-		scsi_init_free ((char *) scsicmd, sizeof (Scsi_Cmnd));
+		scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
 #endif
 
+		/* restore the user address */
+		ioc.ui.fcs.buffer = uaddr;
+
 		return ret;
 
 	case M_RD_IOCTL_CMD:
 		/* which adapter?  */
 		adapno = ioc.ui.fcs.adapno;
+
 		/* See comment above: MEGAIOC_QADAPINFO */
 		adapno = GETADAP (adapno);
 
@@ -4070,85 +4121,74 @@
 		outlen = ioc.outlen;
 		inlen = ioc.inlen;
 
-		if ((outlen >= IOCTL_MAX_DATALEN)
-		    || (inlen >= IOCTL_MAX_DATALEN))
+		if ((outlen >= IOCTL_MAX_DATALEN) || (inlen >= IOCTL_MAX_DATALEN))
 			return (-EINVAL);
 
 		if (outlen) {
 			ret = verify_area (VERIFY_WRITE, ioc.data, outlen);
-			if (ret)
-				return ret;
-		} else if (inlen) {
+			if (ret) return ret;
+		}
+		if (inlen) {
 			ret = verify_area (VERIFY_READ, ioc.data, inlen);
+			if (ret) return ret;
+		}
 
-			if (ret)
-				return ret;
+		/*
+		 * Find this host
+		 */
+		for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) {
+			if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) {
+				megacfg = (mega_host_config *)shpnt->hostdata;
+				break;
+			}
 		}
+		if(shpnt == NULL)  return -ENODEV;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA);
+#else
+		scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd),
+							  GFP_ATOMIC | GFP_DMA);
+#endif
+		if(scsicmd == NULL) return -ENOMEM;
+
+		memset(scsicmd, 0, sizeof(Scsi_Cmnd));
+		scsicmd->host = shpnt;
 
 		if (outlen || inlen) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+			pdevp = &pdev;
+			memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev));
+			pdevp->dma_mask = 0xffffffff;
+#else
+			pdevp = NULL;
+#endif
 			/*
 			 * Allocate a page of kernel space.
 			 */
-			if ((kphysaddr =
-			     (char *) __get_free_pages (GFP_KERNEL, 0)) == 0) {
+			kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &dma_addr);
 
-				printk (KERN_INFO
-					"megaraid:allocation failed\n");
+			if( kvaddr == NULL ) {
+				printk (KERN_WARNING "megaraid:allocation failed\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/*0x20400 */
+				kfree(scsicmd);
+#else
+				scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
+#endif
 				return -ENOMEM;
 			}
 
-			memset (kphysaddr, 0, PAGE_SIZE);
-			ioc.data = kphysaddr;
+			ioc.data = kvaddr;
 
 			if (inlen) {
 				if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
 					/* copyin the user data */
-					copy_from_user (kphysaddr,
-							uaddr,
-							ioc.pthru.dataxferlen);
+					copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen);
 				} else {
-					copy_from_user (kphysaddr,
-							uaddr, inlen);
+					copy_from_user (kvaddr, uaddr, inlen);
 				}
 			}
 		}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020400 */
-		scsicmd = (Scsi_Cmnd *) kmalloc (sizeof (Scsi_Cmnd),
-						 GFP_KERNEL | GFP_DMA);
-		memset (scsicmd, 0, sizeof (Scsi_Cmnd));
-#else
-		scsicmd = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd),
-							  GFP_ATOMIC | GFP_DMA);
-#endif
-
-		if (!scsicmd) {
-			if (kphysaddr)
-				free_pages ((unsigned long) kphysaddr, 0);
-			return -ENOMEM;
-		}
-
-		scsicmd->host = NULL;
-
-		/*
-		 * Find this host in the hostlist
-		 */
-		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
-			if (shpnt->hostdata ==
-			    (unsigned long *) megaCtlrs[adapno])
-				scsicmd->host = shpnt;
-		}
-
-		if (scsicmd->host == NULL) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-			kfree (scsicmd);
-#else
-			scsi_init_free ((char *) scsicmd, sizeof (Scsi_Cmnd));
-#endif
-			if (kphysaddr)
-				free_pages ((unsigned long) kphysaddr, 0);
-
-			return -ENODEV;
-		}
 
 		scsicmd->cmnd[0] = MEGADEVIOC;
 		scsicmd->request_buffer = (void *) &ioc;
@@ -4163,10 +4203,9 @@
 
 		if (!scsicmd->result && outlen) {
 			if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
-				copy_to_user (uaddr,
-					      kphysaddr, ioc.pthru.dataxferlen);
+				copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen);
 			} else {
-				copy_to_user (uaddr, kphysaddr, outlen);
+				copy_to_user (uaddr, kvaddr, outlen);
 			}
 		}
 
@@ -4183,26 +4222,31 @@
 			put_user (scsicmd->result, &uioc->mbox[17]);
 		}
 
-		if (kphysaddr)
-			free_pages ((unsigned long) kphysaddr, 0);
+		if (kvaddr) {
+			dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr );
+		}
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 		kfree (scsicmd);
 #else
-		scsi_init_free ((char *) scsicmd, sizeof (Scsi_Cmnd));
+		scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
 #endif
+
+		/* restore user pointer */
+		ioc.data = uaddr;
+
 		return ret;
 
 	default:
 		return (-EINVAL);
 
-	}			/* Outer switch */
+	}/* Outer switch */
 
 	return 0;
 }
 
 static void
-megadev_ioctl_done (Scsi_Cmnd * sc)
+megadev_ioctl_done(Scsi_Cmnd *sc)
 {
 	up (&mimd_ioctl_sem);
 }
@@ -4309,6 +4353,94 @@
 #endif
 	return 0;
 }
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+void *
+dma_alloc_consistent(void *dev, size_t size, dma_addr_t *dma_addr)
+{
+	void	*_tv;
+	int		npages;
+	int		order = 0;
+
+	/*
+	 * How many pages application needs
+	 */
+	npages = size / PAGE_SIZE;
+
+	/* Do we need one more page */
+	if(size % PAGE_SIZE)
+		npages++;
+
+	order = mega_get_order(npages);
+
+	_tv = (void *)__get_free_pages(GFP_DMA, order);
+
+	if( _tv != NULL ) {
+		memset(_tv, 0, size);
+		*(dma_addr) = virt_to_bus(_tv);
+	}
+
+	return _tv;
+}
+
+/*
+ * int mega_get_order(int)
+ *
+ * returns the order to be used as 2nd argument to __get_free_pages() - which
+ * return pages equal to pow(2, order) - AM
+ */
+int
+mega_get_order(int n)
+{
+	int		i = 0;
+
+	while( pow_2(i++) < n )
+		; /* null statement */
+
+	return i-1;
+}
+
+/*
+ * int pow_2(int)
+ *
+ * calculates pow(2, i)
+ */
+int
+pow_2(int i)
+{
+	unsigned int	v = 1;
+	
+	while(i--)
+		v <<= 1;
+
+	return v;
+}
+
+void
+dma_free_consistent(void *dev, size_t size, void *vaddr, dma_addr_t dma_addr)
+{
+	int		npages;
+	int		order = 0;
+
+	npages = size / PAGE_SIZE;
+
+	if(size % PAGE_SIZE)
+		npages++;
+
+	if (npages == 1)
+		order = 0;
+	else if (npages == 2)
+		order = 1;
+	else if (npages <= 4)
+		order = 2;
+	else
+		order = 3;
+
+	free_pages((unsigned long)vaddr, order);
+
+}
+#endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 static

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