/*=================================================================== * * Linux MegaRAID device driver * * Copyright 2001 American Megatrends Inc. * * 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 * 2 of the License, or (at your option) any later version. * * Version : v1.18 (Oct 11, 2001) * * Description: Linux device driver for LSI Logic MegaRAID controller * * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490 * 493. * History: * * Version 0.90: * Original source contributed by Dell; integrated it into the kernel and * cleaned up some things. Added support for 438/466 controllers. * Version 0.91: * Aligned mailbox area on 16-byte boundary. * Added schedule() at the end to properly clean up. * Made improvements for conformity to linux driver standards. * * Version 0.92: * Added support for 2.1 kernels. * Reads from pci_dev struct, so it's not dependent on pcibios. * Added some missing virt_to_bus() translations. * Added support for SMP. * Changed global cli()'s to spinlocks for 2.1, and simulated * spinlocks for 2.0. * Removed setting of SA_INTERRUPT flag when requesting Irq. * * Version 0.92ac: * Small changes to the comments/formatting. Plus a couple of * added notes. Returned to the authors. No actual code changes * save printk levels. * 8 Oct 98 Alan Cox * * Merged with 2.1.131 source tree. * 12 Dec 98 K. Baranowski * * Version 0.93: * Added support for vendor specific ioctl commands (M_RD_IOCTL_CMD+xxh) * Changed some fields in MEGARAID struct to better values. * Added signature check for Rp controllers under 2.0 kernels * Changed busy-wait loop to be time-based * Fixed SMP race condition in isr * Added kfree (sgList) on release * Added #include linux/version.h to megaraid.h for hosts.h * Changed max_id to represent max logical drives instead of targets. * * Version 0.94: * Got rid of some excess locking/unlocking * Fixed slight memory corruption problem while memcpy'ing into mailbox * Changed logical drives to be reported as luns rather than targets * Changed max_id to 16 since it is now max targets/chan again. * Improved ioctl interface for upcoming megamgr * * Version 0.95: * Fixed problem of queueing multiple commands to adapter; * still has some strange problems on some setups, so still * defaults to single. To enable parallel commands change * #define MULTI_IO in megaraid.h * Changed kmalloc allocation to be done in beginning. * Got rid of C++ style comments * * Version 0.96: * 762 fully supported. * * Version 0.97: * Changed megaraid_command to use wait_queue. * * Version 1.00: * Checks to see if an irq occurred while in isr, and runs through * routine again. * Copies mailbox to temp area before processing in isr * Added barrier() in busy wait to fix volatility bug * Uses separate list for freed Scbs, keeps track of cmd state * Put spinlocks around entire queue function for now... * Full multi-io commands working stablely without previous problems * Added skipXX LILO option for Madrona motherboard support * * Version 1.01: * Fixed bug in mega_cmd_done() for megamgr control commands, * the host_byte in the result code from the scsi request to * scsi midlayer is set to DID_BAD_TARGET when adapter's * returned codes are 0xF0 and 0xF4. * * Version 1.02: * Fixed the tape drive bug by extending the adapter timeout value * for passthrough command to 60 seconds in mega_build_cmd(). * * Version 1.03: * Fixed Madrona support. * Changed the adapter timeout value from 60 sec in 1.02 to 10 min * for bigger and slower tape drive. * Added driver version printout at driver loadup time * * Version 1.04 * Added code for 40 ld FW support. * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with * data area greater than 4 KB, which is the upper bound for data * tranfer through scsi_ioctl interface. * The additional 32 bit field for 64bit address in the newly defined * mailbox64 structure is set to 0 at this point. * * Version 1.05 * Changed the queing implementation for handling SCBs and completed * commands. * Added spinlocks in the interrupt service routine to enable the driver * function in the SMP environment. * Fixed the problem of unnecessary aborts in the abort entry point, which * also enables the driver to handle large amount of I/O requests for * long duration of time. * Version 1.06 * Intel Release * Version 1.07 * Removed the usage of uaccess.h file for kernel versions less than * 2.0.36, as this file is not present in those versions. * * Version 108 * Modified mega_ioctl so that 40LD megamanager would run * Made some changes for 2.3.XX compilation , esp wait structures * Code merge between 1.05 and 1.06 . * Bug fixed problem with ioctl interface for concurrency between * 8ld and 40ld firwmare * Removed the flawed semaphore logic for handling new config command * Added support for building own scatter / gather list for big user * mode buffers * Added /proc file system support ,so that information is available in * human readable format * * Version 1a08 * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag * * Version 1b08 * Include file changes. * Version 1b08b * Change PCI ID value for the 471 card, use #defines when searching * for megaraid cards. * * Version 1.10 * * I) Changes made to make following ioctl commands work in 0x81 interface * a)DCMD_DELETE_LOGDRV * b)DCMD_GET_DISK_CONFIG * c)DCMD_DELETE_DRIVEGROUP * d)NC_SUBOP_ENQUIRY3 * e)DCMD_CHANGE_LDNO * f)DCMD_CHANGE_LOOPID * g)DCMD_FC_READ_NVRAM_CONFIG * h)DCMD_WRITE_CONFIG * II) Added mega_build_kernel_sg function * III)Firmware flashing option added * * Version 1.10a * * I)Dell updates included in the source code. * Note: This change is not tested due to the unavailability of IA64 kernel * and it is in the #ifdef DELL_MODIFICATION macro which is not defined * * Version 1.10b * * I)In M_RD_IOCTL_CMD_NEW command the wrong way of copying the data * to the user address corrected * * Version 1.10c * * I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes. * * Version 1.11 * I) Version number changed from 1.10c to 1.11 * II) DCMD_WRITE_CONFIG(0x0D) command in the driver changed from * scatter/gather list mode to direct pointer mode.. * Fixed bug of undesirably detecting HP onboard controllers which * are disabled. * * Version 1.12 (Sep 21, 2000) * * I. Changes have been made for Dynamic DMA mapping in IA64 platform. * To enable all these changes define M_RD_DYNAMIC_DMA_SUPPORT in megaraid.h * II. Got rid of windows mode comments * III. Removed unwanted code segments * IV. Fixed bug of HP onboard controller information (commented with * MEGA_HP_FIX) * * Version 1a12 * I. reboot notifier and new ioctl changes ported from 1c09 * * Version 1b12 * I. Changes in new ioctl interface routines ( Nov 06, 2000 ) * * Version 1c12 * I. Changes in new ioctl interface routines ( Nov 07, 2000 ) * * Version 1d12 * I. Compilation error under kernel 2.4.0 for 32-bit machine in mega_ioctl * * Version 1e12, 1f12 * 1. Fixes for pci_map_single, pci_alloc_consistent along with mailbox * alignment * * Version 1.13beta * Added Support for Full 64bit address space support. If firmware * supports 64bit, it goes to 64 bit mode even on x86 32bit * systems. Data Corruption Issues while running on test9 kernel * on IA64 systems. This issue not seen on test11 on x86 system * * Version 1.13c * 1. Resolved Memory Leak when using M_RD_IOCTL_CMD interface * 2. Resolved Queuing problem when MailBox Blocks * 3. Added unregister_reboot_notifier support * * Version 1.13d * Experimental changes in interfacing with the controller in ISR * * Version 1.13e * Fixed Broken 2.2.XX compilation changes + misc changes * * Version 1.13f to 1.13i * misc changes + code clean up * Cleaned up the ioctl code and added set_mbox_xfer_addr() * Support for START_DEV (6) * * Version 1.13j * Moved some code to megaraid.h file, replaced some hard coded values * with respective macros. Changed some functions to static * * Version 1.13k * Only some idendation correction to 1.13j * * Version 1.13l , 1.13m, 1.13n, 1.13o * Minor Identation changes + misc changes * * Version 1.13q * Paded the new uioctl_t MIMD structure for maintaining alignment * and size across 32 / 64 bit platforms * Changed the way MIMD IOCTL interface used virt_to_bus() to use pci * memory location * * Version 1.13r * 2.4.xx SCSI Changes. * * Version 1.13s * Stats counter fixes * Temporary fix for some 64 bit firmwares in 2.4.XX kernels * * Version 1.13t * Support for 64bit version of READ/WRITE/VIEW DISK CONFIG * * Version 1.14 * Did away with MEGADEV_IOCTL flag. It is now standard part of driver * without need for a special #define flag * Disabled old scsi ioctl path for kernel versions > 2.3.xx. This is due * to the nature in which the new scsi code queues a new scsi command to * controller during SCSI IO Completion * Driver now checks for sub-system vendor id before taking ownership of * the controller * * Version 1.14a * Added Host re-ordering * * Version 1.14b * Corrected some issue which caused the older cards not to work * * Version 1.14c * IOCTL changes for not handling the non-64bit firmwares under 2.4.XX * kernel * * Version 1.14d * Fixed Various MIMD Synchronization Issues * * Version 1.14e * Fixed the error handling during card initialization * * Version 1.14f * Multiple invocations of mimd phase I ioctl stalls the cpu. Replaced * spinlock with semaphore(mutex) * * Version 1.14g * Fixed running out of scbs issues while running MIMD apps under heavy IO * * Version 1.14g-ac - 02/03/01 * Reformatted to Linux format so I could compare to old one and cross * check bug fixes * Re fixed the assorted missing 'static' cases * Removed some unneeded version checks * Cleaned up some of the VERSION checks in the code * Left 2.0 support but removed 2.1.x support. * Collected much of the compat glue into one spot * * 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.14m * Tue Mar 27 12:09:22 EST 2001 - AM * * Added support for extended CDBs ( > 10 bytes ) and OBDR ( One Button * Disaster Recovery ) feature. * * * Version 1.14n * Tue Apr 10 14:28:13 EDT 2001 - AM * * "modeversions.h" is no longer included in the code. * 2.4.xx style mutex initialization used for older kernels also * * Version 1.14o * Wed Apr 18 17:47:26 EDT 2001 - PJ * * Before returning status for 'inquiry', we first check if request buffer * is SG list, and then return appropriate status * * Version 1.14p * Wed Apr 25 13:44:48 EDT 2001 - PJ * * SCSI result made appropriate in case of check conditions for extended * passthru commands * * Do not support lun >7 for physically accessed devices * * * Version 1.15 * Thu Apr 19 09:38:38 EDT 2001 - AM * * 1.14l rollover to 1.15 - merged with main trunk after 1.15d * * 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 branch(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. * * Version 1.16pre * Fri Jun 1 19:40:48 EDT 2001 - AM * * 1.14p and 1.15d merged * ROMB support added * * Version 1.16-pre1 * Mon Jun 4 15:01:01 EDT 2001 - AM * * Non-ROMB firmware do no DMA support 0xA9 command. Value 0xFF * (all channels are raid ) is chosen for those firmware. * * Version 1.16-pre2 * Mon Jun 11 18:15:31 EDT 2001 - AM * * Changes for boot from any logical drive * * Version 1.16 * Tue Jun 26 18:07:02 EDT 2001 - AM * * branched at 1.14p * * Check added for HP 1M/2M controllers if having firmware H.01.07 or * H.01.08. If found, disable 64 bit support since these firmware have * limitations for 64 bit addressing * * * Version 1.17 * Thu Jul 12 11:14:09 EDT 2001 - AM * * 1.16pre2 and 1.16 merged. * * init_MUTEX and init_MUTEX_LOCKED are defined in 2.2.19. Pre-processor * statements are added for them * * Linus's 2.4.7pre3 kernel introduces a new field 'max_sectors' in Scsi_Host * structure, to improve IO performance. * * * Version 1.17a * Fri Jul 13 18:44:01 EDT 2001 - AM * * Starting from kernel 2.4.x, LUN is not < 8 - following SCSI-III. So to have * our current formula working to calculate logical drive number, return * failure for LUN > 7 * * * Version 1.17b * Mon Jul 30 19:24:02 EDT 2001 - AM * * Added support for random deletion of logical drives * * Version 1.17c * Tue Sep 25 09:37:49 EDT 2001 - Atul Mukker * * With single and dual channel controllers, some virtaul channels are * displayed negative. * * Version 1.17a-ac * Mon Aug 6 14:59:29 BST 2001 - "Michael Johnson" * * Make the HP print formatting and check for buggy firmware runtime not * ifdef dependant. * * * Version 1.17d * Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker * * Driver 1.17c oops when loaded without controller. * * Special case for "use_sg == 1" removed while building the scatter gather * list. * * Version 1.18 * Thu Oct 11 15:02:53 EDT 2001 - Atul Mukker * * References to AMI have been changed to LSI Logic. * * * 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. * * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT * value must be increased to (30 * HZ) otherwise false timeouts * will occur in the upper layer. * * Never set skip_id. The existing PCI code the megaraid uses fails * to properly check the vendor subid in some cases. Setting this then * makes it steal other i960's and crashes some boxes * * Far too many ifdefs for versions. * *===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for kmalloc() */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ #include #else #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */ #include #else #include #endif #endif #include #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */ #include #endif /* * These header files are required for Shutdown Notification routines */ #include #include #include #include "sd.h" #include "scsi.h" #include "hosts.h" #include "megaraid.h" /* *================================================================ * #Defines *================================================================ */ #define MAX_SERBUF 160 #define COM_BASE 0x2f8 static ulong RDINDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x20); } static void WRINDOOR (mega_host_config * megaCfg, ulong value) { writel (value, megaCfg->base + 0x20); } static ulong RDOUTDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x2C); } static void WROUTDOOR (mega_host_config * megaCfg, ulong value) { writel (value, megaCfg->base + 0x2C); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ #include #define cpuid smp_processor_id() #endif #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 * * No driver private lock * Use the io_request_lock not cli/sti * queue task is a simple api without irq forms */ MODULE_AUTHOR ("LSI Logic Corporation"); MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); MODULE_LICENSE ("GPL"); #define DRIVER_LOCK_T #define DRIVER_LOCK_INIT(p) #define DRIVER_LOCK(p) #define DRIVER_UNLOCK(p) #define IO_LOCK_T unsigned long io_flags = 0 #define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); #define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); #define queue_task_irq(a,b) queue_task(a,b) #define queue_task_irq_off(a,b) queue_task(a,b) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ /* * Linux 2.2 and higher * * No driver private lock * Use the io_request_lock not cli/sti * No pci region api * queue_task is now a single simple API */ static char kernel_version[] = UTS_RELEASE; MODULE_AUTHOR ("LSI Logic Corporation"); MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); #define DRIVER_LOCK_T #define DRIVER_LOCK_INIT(p) #define DRIVER_LOCK(p) #define DRIVER_UNLOCK(p) #define IO_LOCK_T unsigned long io_flags = 0 #define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); #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) #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) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ #define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) #define init_MUTEX(x) (*(x)=MUTEX) #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL #endif #else /* * Linux 2.0 macros. Here we have to provide some of our own * functionality. We also only work little endian 32bit. * Again no pci_alloc/free api * IO_LOCK/IO_LOCK_T were never used in 2.0 so now are empty */ #define cpuid 0 #define DRIVER_LOCK_T long cpu_flags; #define DRIVER_LOCK_INIT(p) #define DRIVER_LOCK(p) \ save_flags(cpu_flags); \ cli(); #define DRIVER_UNLOCK(p) \ restore_flags(cpu_flags); #define IO_LOCK_T #define IO_LOCK(p) #define IO_UNLOCK(p) #define le32_to_cpu(x) (x) #define cpu_to_le32(x) (x) #define pci_free_consistent(a,b,c,d) #define pci_unmap_single(a,b,c,d) #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 */ #define ioremap vremap #define iounmap vfree /* simulate spin locks */ typedef struct { volatile char lock; } spinlock_t; #define spin_lock_init(x) { (x)->lock = 0;} #define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\ (x)->lock=1; save_flags(flags);\ cli();} #define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);} #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL #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 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ typedef unsigned long dma_addr_t; #endif 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 #if SERDEBUG static void ser_init (void); static void ser_puts (char *str); static void ser_putc (char c); static int ser_printk (const char *fmt, ...); #endif #ifdef CONFIG_PROC_FS #define COPY_BACK if (offset > megaCfg->procidx) { \ *eof = TRUE; \ megaCfg->procidx = 0; \ megaCfg->procbuf[0] = 0; \ return 0;} \ if ((count + offset) > megaCfg->procidx) { \ count = megaCfg->procidx - offset; \ *eof = TRUE; } \ memcpy(page, &megaCfg->procbuf[offset], count); \ megaCfg->procidx = 0; \ megaCfg->procbuf[0] = 0; #endif /* * ================================================================ * Global variables *================================================================ */ /* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE processor id cannot be scanned */ static char *megaraid; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) /* 0x20100 */ #ifdef MODULE MODULE_PARM (megaraid, "s"); #endif #endif static int skip_id = -1; static int numCtlrs = 0; static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = { 0 }; static struct proc_dir_entry *mega_proc_dir_entry; #if DEBUG static u32 maxCmdTime = 0; #endif static mega_scb *pLastScb = NULL; static struct notifier_block mega_notifier = { megaraid_reboot_notify, NULL, 0 }; /* For controller re-ordering */ struct mega_hbas mega_hbas[MAX_CONTROLLERS]; /* * The File Operations structure for the serial/ioctl interface of the driver */ /* For controller re-ordering */ static struct file_operations megadev_fops = { ioctl:megadev_ioctl_entry, open:megadev_open, release:megadev_close, }; /* * Array to structures for storing the information about the controllers. This * information is sent to the user level applications, when they do an ioctl * for this information. */ static struct mcontroller mcontroller[MAX_CONTROLLERS]; /* The current driver version */ static u32 driver_ver = 114; /* major number used by the device for character interface */ static int major; static struct semaphore mimd_ioctl_sem; static struct semaphore mimd_entry_mtx; #if SERDEBUG volatile static spinlock_t serial_lock; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x20300 */ static struct proc_dir_entry proc_scsi_megaraid = { PROC_SCSI_MEGARAID, 8, "megaraid", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; #endif #ifdef CONFIG_PROC_FS extern struct proc_dir_entry proc_root; #endif static char mega_ch_class; /* channels are raid or scsi */ #define IS_RAID_CH(ch) ( (mega_ch_class >> (ch)) & 0x01 ) #if SERDEBUG static char strbuf[MAX_SERBUF + 1]; static void ser_init (void) { unsigned port = COM_BASE; outb (0x80, port + 3); outb (0, port + 1); /* 9600 Baud, if 19200: outb(6,port) */ outb (12, port); outb (3, port + 3); outb (0, port + 1); } static void ser_puts (char *str) { char *ptr; ser_init (); for (ptr = str; *ptr; ++ptr) ser_putc (*ptr); } static void ser_putc (char c) { unsigned port = COM_BASE; while ((inb (port + 5) & 0x20) == 0) ; outb (c, port); if (c == 0x0a) { while ((inb (port + 5) & 0x20) == 0) ; outb (0x0d, port); } } static int ser_printk (const char *fmt, ...) { va_list args; int i; long flags; spin_lock_irqsave (&serial_lock, flags); va_start (args, fmt); i = vsprintf (strbuf, fmt, args); ser_puts (strbuf); va_end (args); spin_unlock_irqrestore (&serial_lock, flags); return i; } #define TRACE(a) { ser_printk a;} #else #define TRACE(A) #endif #define TRACE1(a) static void callDone (Scsi_Cmnd * SCpnt) { if (SCpnt->result) { TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->result)); } SCpnt->scsi_done (SCpnt); } /*------------------------------------------------------------------------- * * Local functions * *-------------------------------------------------------------------------*/ /*======================= * Free a SCB structure *======================= */ static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb) { mega_scb *pScbtmp; if ((pScb == NULL) || (pScb->idx >= 0xFE)) { return; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) switch (pScb->dma_type) { case M_RD_DMA_TYPE_NONE: break; case M_RD_PTHRU_WITH_BULK_DATA: pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, pScb->pthru->dataxferlen, pScb->dma_direction); break; case M_RD_EPTHRU_WITH_BULK_DATA: pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, pScb->epthru->dataxferlen, pScb->dma_direction); break; case M_RD_PTHRU_WITH_SGLIST: { int count; for (count = 0; count < pScb->sglist_count; count++) { pci_unmap_single (megaCfg->dev, pScb->dma_h_sglist[count], pScb->sgList[count].length, pScb->dma_direction); } break; } case M_RD_BULK_DATA_ONLY: pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, pScb->iDataSize, pScb->dma_direction); break; case M_RD_SGLIST_ONLY: pci_unmap_sg (megaCfg->dev, pScb->SCpnt->request_buffer, pScb->SCpnt->use_sg, pScb->dma_direction); break; default: break; } #endif /* Unlink from pending queue */ if (pScb == megaCfg->qPendingH) { if (megaCfg->qPendingH == megaCfg->qPendingT) megaCfg->qPendingH = megaCfg->qPendingT = NULL; else megaCfg->qPendingH = megaCfg->qPendingH->next; megaCfg->qPcnt--; } else { for (pScbtmp = megaCfg->qPendingH; pScbtmp; pScbtmp = pScbtmp->next) { if (pScbtmp->next == pScb) { pScbtmp->next = pScb->next; if (pScb == megaCfg->qPendingT) { megaCfg->qPendingT = pScbtmp; } megaCfg->qPcnt--; break; } } } /* Link back into free list */ pScb->state = SCB_FREE; pScb->SCpnt = NULL; if (megaCfg->qFreeH == (mega_scb *) NULL) { megaCfg->qFreeH = megaCfg->qFreeT = pScb; } else { megaCfg->qFreeT->next = pScb; megaCfg->qFreeT = pScb; } megaCfg->qFreeT->next = NULL; megaCfg->qFcnt++; } /*=========================== * Allocate a SCB structure *=========================== */ static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) { mega_scb *pScb; /* Unlink command from Free List */ if ((pScb = megaCfg->qFreeH) != NULL) { megaCfg->qFreeH = pScb->next; megaCfg->qFcnt--; pScb->isrcount = jiffies; pScb->next = NULL; pScb->state = SCB_ACTIVE; pScb->SCpnt = SCpnt; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pScb->dma_type = M_RD_DMA_TYPE_NONE; #endif return pScb; } printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n"); return NULL; } /* Run through the list of completed requests and finish it */ static void mega_rundoneq (mega_host_config * megaCfg) { Scsi_Cmnd *SCpnt; while ((SCpnt = megaCfg->qCompletedH) != NULL) { megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; megaCfg->qCcnt--; SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */ /* Callback */ callDone (SCpnt); } megaCfg->qCompletedH = megaCfg->qCompletedT = NULL; } /* * Runs through the list of pending requests * Assumes that mega_lock spin_lock has been acquired. */ static int mega_runpendq (mega_host_config * megaCfg) { mega_scb *pScb; int rc; /* Issue any pending commands to the card */ for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { if (pScb->state == SCB_ACTIVE) { if ((rc = megaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) == -1) return rc; } } return 0; } /* Add command to the list of completed requests */ static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status) { int islogical; Scsi_Cmnd *SCpnt; mega_passthru *pthru; mega_ext_passthru *epthru; mega_mailbox *mbox; struct scatterlist *sgList; u8 c; if (pScb == NULL) { TRACE (("NULL pScb in mega_cmd_done!")); printk(KERN_CRIT "NULL pScb in mega_cmd_done!"); } SCpnt = pScb->SCpnt; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pthru = pScb->pthru; epthru = pScb->epthru; #else pthru = &pScb->pthru; epthru = &pScb->epthru; #endif mbox = (mega_mailbox *) & pScb->mboxData; if (SCpnt == NULL) { TRACE (("NULL SCpnt in mega_cmd_done!")); TRACE (("pScb->idx = ", pScb->idx)); TRACE (("pScb->state = ", pScb->state)); TRACE (("pScb->state = ", pScb->state)); panic(KERN_ERR "megaraid:Problem...!\n"); } islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) && (SCpnt->channel <= megaCfg->host->max_channel) ); #if 0 islogical = (SCpnt->channel == megaCfg->host->max_channel); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* Special Case to handle PassThrough->XferAddrress > 4GB */ switch (SCpnt->cmnd[0]) { case INQUIRY: case READ_CAPACITY: memcpy (SCpnt->request_buffer, pScb->bounce_buffer, SCpnt->request_bufflen); break; } #endif mega_freeSCB (megaCfg, pScb); /* * Do not return the presence of hard disk on the channel so, inquiry * sent, and returned data==hard disk or removable hard disk and not * logical, request should return failure! - PJ */ #if 0 if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) { status = 0xF0; } #endif if (SCpnt->cmnd[0] == INQUIRY && !islogical) { if ( SCpnt->use_sg ) { sgList = (struct scatterlist *)SCpnt->request_buffer; memcpy(&c, sgList[0].address, 0x1); } else { memcpy(&c, SCpnt->request_buffer, 0x1); } #if 0 if( (c & 0x1F ) == TYPE_DISK ) { status = 0xF0; } #endif if( IS_RAID_CH(SCpnt->channel) && ((c & 0x1F ) == TYPE_DISK) ) { status = 0xF0; } } /* clear result; otherwise, success returns corrupt value */ SCpnt->result = 0; if ((SCpnt->cmnd[0] & M_RD_IOCTL_CMD)) { /* i.e. ioctl cmd such as M_RD_IOCTL_CMD, M_RD_IOCTL_CMD_NEW of megamgr */ switch (status) { case 2: case 0xF0: case 0xF4: SCpnt->result = (DID_BAD_TARGET << 16) | status; break; default: SCpnt->result |= status; } /*end of switch */ } else { /* Convert MegaRAID status to Linux error code */ switch (status) { case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ SCpnt->result |= (DID_OK << 16); break; case 0x02: /* ERROR_ABORTED, i.e. SCSI_STATUS_CHECK_CONDITION */ /*set sense_buffer and result fields */ if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU) { memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14); } else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) { SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1); memcpy( SCpnt->sense_buffer, epthru->reqsensearea, 14 ); SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1); /*SCpnt->result = (DRIVER_SENSE << 24) | (DID_ERROR << 16) | status;*/ } else { SCpnt->sense_buffer[0] = 0x70; SCpnt->sense_buffer[2] = ABORTED_COMMAND; SCpnt->result |= (CHECK_CONDITION << 1); } break; case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. SCSI_STATUS_BUSY */ SCpnt->result |= (DID_BUS_BUSY << 16) | status; break; default: SCpnt->result |= (DID_BAD_TARGET << 16) | status; break; } } /* Add Scsi_Command to end of completed queue */ if (megaCfg->qCompletedH == NULL) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; } else { megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; megaCfg->qCompletedT = SCpnt; } megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; megaCfg->qCcnt++; } /*------------------------------------------------------------------- * * Build a SCB from a Scsi_Cmnd * * Returns a SCB pointer, or NULL * If NULL is returned, the scsi_done function MUST have been called * *-------------------------------------------------------------------*/ static mega_scb *mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) { mega_scb *pScb; mega_mailbox *mbox; mega_passthru *pthru; mega_ext_passthru *epthru; long seg; char islogical; int lun = SCpnt->lun; int max_lun; if ((SCpnt->cmnd[0] == MEGADEVIOC)) return megadev_doioctl (megaCfg, SCpnt); if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD) || (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->productInfo.SCSIChanPresent) && (SCpnt->channel <= megaCfg->host->max_channel) ); #if 0 islogical = (IS_RAID_CH(SCpnt->channel) && /* virtual ch is raid - AM */ (SCpnt->channel == megaCfg->host->max_channel)); #endif if ( ! megaCfg->support_ext_cdb ) { if (!islogical && lun != 0) { SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } } if (!islogical && SCpnt->target == skip_id) { SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } if (islogical) { /* have just LUN 0 for each target on virtual channels */ if( SCpnt->lun != 0 ) { SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } lun = mega_get_lun(megaCfg, SCpnt); max_lun = (megaCfg->flag & BOARD_40LD) ? FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; /* * max_lun increases by 0x80 if some logical drive was deleted. */ if(megaCfg->read_ldidmap) { max_lun += 0x80; } if( lun > max_lun ) { SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } /* * If we have a logical drive with boot enabled, project it first */ if( megaCfg->boot_ldrv_enabled ) { if( lun == 0 ) { lun = megaCfg->boot_ldrv; } else { if( lun <= megaCfg->boot_ldrv ) { lun--; } } } } else { if ( lun > 7) { /* Do not support lun >7 for physically accessed devices */ SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } } /*----------------------------------------------------- * * Logical drive commands * *-----------------------------------------------------*/ if (islogical) { switch (SCpnt->cmnd[0]) { case TEST_UNIT_READY: memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen); SCpnt->result = (DID_OK << 16); callDone (SCpnt); return NULL; case MODE_SENSE: memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]); SCpnt->result = (DID_OK << 16); callDone (SCpnt); return NULL; case READ_CAPACITY: case INQUIRY: /* Allocate a SCB and initialize passthru */ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pthru = pScb->pthru; #else pthru = &pScb->pthru; #endif mbox = (mega_mailbox *) & pScb->mboxData; memset (mbox, 0, sizeof (pScb->mboxData)); memset (pthru, 0, sizeof (mega_passthru)); pthru->timeout = 0; pthru->ars = 1; pthru->reqsenselen = 14; pthru->islogical = 1; pthru->logdrv = lun; pthru->cdblen = SCpnt->cmd_len; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*Not sure about the direction */ pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; #if 0 /* Normal Code w/o the need for bounce buffer */ pScb->dma_h_bulkdata = pci_map_single (megaCfg->dev, SCpnt->request_buffer, SCpnt->request_bufflen, pScb->dma_direction); pthru->dataxferaddr = pScb->dma_h_bulkdata; #else /* Special Code to use bounce buffer for READ_CAPA/INQ */ pthru->dataxferaddr = pScb->dma_bounce_buffer; pScb->dma_type = M_RD_DMA_TYPE_NONE; #endif #else pthru->dataxferaddr = virt_to_bus (SCpnt->request_buffer); #endif pthru->dataxferlen = SCpnt->request_bufflen; memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); /* Initialize mailbox area */ mbox->cmd = MEGA_MBOXCMD_PASSTHRU; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) mbox->xferaddr = pScb->dma_passthruhandle64; TRACE1 (("M_RD_PTHRU_WITH_BULK_DATA Enabled \n")); #else mbox->xferaddr = virt_to_bus (pthru); #endif return pScb; case READ_6: case WRITE_6: case READ_10: case WRITE_10: /* Allocate a SCB and initialize mailbox */ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } mbox = (mega_mailbox *) & pScb->mboxData; memset (mbox, 0, sizeof (pScb->mboxData)); mbox->logdrv = lun; if (megaCfg->flag & BOARD_64BIT) { mbox->cmd = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ? MEGA_MBOXCMD_LREAD64 : MEGA_MBOXCMD_LWRITE64; } else { mbox->cmd = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ? MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE; } /* 6-byte */ if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) { mbox->numsectors = (u32) SCpnt->cmnd[4]; mbox->lba = ((u32) SCpnt->cmnd[1] << 16) | ((u32) SCpnt->cmnd[2] << 8) | (u32) SCpnt->cmnd[3]; mbox->lba &= 0x1FFFFF; if (*SCpnt->cmnd == READ_6) { megaCfg->nReads[(int) lun]++; megaCfg->nReadBlocks[(int) lun] += mbox->numsectors; } else { megaCfg->nWrites[(int) lun]++; megaCfg->nWriteBlocks[(int) lun] += mbox->numsectors; } } /* 10-byte */ if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) { mbox->numsectors = (u32) SCpnt->cmnd[8] | ((u32) SCpnt->cmnd[7] << 8); mbox->lba = ((u32) SCpnt->cmnd[2] << 24) | ((u32) SCpnt->cmnd[3] << 16) | ((u32) SCpnt->cmnd[4] << 8) | (u32) SCpnt->cmnd[5]; if (*SCpnt->cmnd == READ_10) { megaCfg->nReads[(int) lun]++; megaCfg->nReadBlocks[(int) lun] += mbox->numsectors; } else { megaCfg->nWrites[(int) lun]++; megaCfg->nWriteBlocks[(int) lun] += mbox->numsectors; } } /* 12-byte */ if (*SCpnt->cmnd == READ_12 || *SCpnt->cmnd == WRITE_12) { mbox->lba = ((u32) SCpnt->cmnd[2] << 24) | ((u32) SCpnt->cmnd[3] << 16) | ((u32) SCpnt->cmnd[4] << 8) | (u32) SCpnt->cmnd[5]; mbox->numsectors = ((u32) SCpnt->cmnd[6] << 24) | ((u32) SCpnt->cmnd[7] << 16) | ((u32) SCpnt->cmnd[8] << 8) | (u32) SCpnt->cmnd[9]; if (*SCpnt->cmnd == READ_12) { megaCfg->nReads[(int) lun]++; megaCfg->nReadBlocks[(int) lun] += mbox->numsectors; } else { megaCfg->nWrites[(int) lun]++; megaCfg->nWriteBlocks[(int) lun] += mbox->numsectors; } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10 || *SCpnt->cmnd == READ_12) { pScb->dma_direction = PCI_DMA_FROMDEVICE; } else { /*WRITE_6 or WRITE_10 */ pScb->dma_direction = PCI_DMA_TODEVICE; } #endif /* Calculate Scatter-Gather info */ mbox->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *)&mbox->xferaddr, (u32 *)&seg); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pScb->iDataSize = seg; if (mbox->numsgelements) { pScb->dma_type = M_RD_SGLIST_ONLY; TRACE1 (("M_RD_SGLIST_ONLY Enabled \n")); } else { pScb->dma_type = M_RD_BULK_DATA_ONLY; TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); } #endif return pScb; default: SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; } } /*----------------------------------------------------- * * Passthru drive commands * *-----------------------------------------------------*/ else { /* Allocate a SCB and initialize passthru */ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } mbox = (mega_mailbox *) pScb->mboxData; memset (mbox, 0, sizeof (pScb->mboxData)); if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) { epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt); mbox->cmd = MEGA_MBOXCMD_EXTPASSTHRU; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) mbox->xferaddr = pScb->dma_ext_passthruhandle64; if(epthru->numsgelements) { pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; } else { pScb->dma_type = M_RD_EPTHRU_WITH_BULK_DATA; } #else mbox->xferaddr = virt_to_bus(epthru); #endif } else { pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt); /* Initialize mailbox */ mbox->cmd = MEGA_MBOXCMD_PASSTHRU; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) mbox->xferaddr = pScb->dma_passthruhandle64; if (pthru->numsgelements) { pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; } else { pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; } #else mbox->xferaddr = virt_to_bus(pthru); #endif } return pScb; } return NULL; } static int mega_get_lun(mega_host_config *this_hba, Scsi_Cmnd *sc) { int tgt; int lun; int virt_chan; tgt = sc->target; if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */ virt_chan = sc->channel - this_hba->productInfo.SCSIChanPresent; lun = (virt_chan * 15) + tgt; /* * If "delete logical drive" feature is enabled on this controller. * Do only if at least one delete logical drive operation was done. * * Also, after logical drive deletion, instead of logical drive number, * the value returned should be 0x80+logical drive id. * * These is valid only for IO commands. */ if( this_hba->support_random_del && this_hba->read_ldidmap ) { switch(sc->cmnd[0]) { case READ_6: /* fall through */ case WRITE_6: /* fall through */ case READ_10: /* fall through */ case WRITE_10: lun += 0x80; } } return lun; } static mega_passthru * mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc) { mega_passthru *pthru; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pthru = scb->pthru; #else pthru = &scb->pthru; #endif memset (pthru, 0, sizeof (mega_passthru)); /* set adapter timeout value to 10 min. for tape drive */ /* 0=6sec/1=60sec/2=10min/3=3hrs */ pthru->timeout = 2; pthru->ars = 1; pthru->reqsenselen = 14; pthru->islogical = 0; pthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : sc->channel; pthru->target = (megacfg->flag & BOARD_40LD) ? (sc->channel << 4) | sc->target : sc->target; pthru->cdblen = sc->cmd_len; pthru->logdrv = sc->lun; memcpy (pthru->cdb, sc->cmnd, sc->cmd_len); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* Not sure about the direction */ scb->dma_direction = PCI_DMA_BIDIRECTIONAL; /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ switch (sc->cmnd[0]) { case INQUIRY: case READ_CAPACITY: pthru->numsgelements = 0; pthru->dataxferaddr = scb->dma_bounce_buffer; pthru->dataxferlen = sc->request_bufflen; break; default: pthru->numsgelements = mega_build_sglist( megacfg, scb, (u32 *)&pthru->dataxferaddr, (u32 *)&pthru->dataxferlen ); break; } #else pthru->numsgelements = mega_build_sglist( megacfg, scb, (u32 *)&pthru->dataxferaddr, (u32 *)&pthru->dataxferlen ); #endif return pthru; } static mega_ext_passthru * mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc) { mega_ext_passthru *epthru; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) epthru = scb->epthru; #else epthru = &scb->epthru; #endif memset(epthru, 0, sizeof(mega_ext_passthru)); /* set adapter timeout value to 10 min. for tape drive */ /* 0=6sec/1=60sec/2=10min/3=3hrs */ epthru->timeout = 2; epthru->ars = 1; epthru->reqsenselen = 14; epthru->islogical = 0; epthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : sc->channel; epthru->target = (megacfg->flag & BOARD_40LD) ? (sc->channel << 4) | sc->target : sc->target; epthru->cdblen = sc->cmd_len; epthru->logdrv = sc->lun; memcpy(epthru->cdb, sc->cmnd, sc->cmd_len); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* Not sure about the direction */ scb->dma_direction = PCI_DMA_BIDIRECTIONAL; /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ switch (sc->cmnd[0]) { case INQUIRY: case READ_CAPACITY: epthru->numsgelements = 0; epthru->dataxferaddr = scb->dma_bounce_buffer; epthru->dataxferlen = sc->request_bufflen; break; default: epthru->numsgelements = mega_build_sglist( megacfg, scb, (u32 *)&epthru->dataxferaddr, (u32 *)&epthru->dataxferlen ); break; } #else epthru->numsgelements = mega_build_sglist( megacfg, scb, (u32 *)&epthru->dataxferaddr, (u32 *)&epthru->dataxferlen ); #endif return epthru; } /* Handle Driver Level IOCTLs * Return value of 0 indicates this function could not handle , so continue * processing */ static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) { unsigned char *data = (unsigned char *) SCpnt->request_buffer; mega_driver_info driver_info; /* If this is not our command dont do anything */ if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE) return 0; switch (SCpnt->cmnd[1]) { case GET_DRIVER_INFO: if (SCpnt->request_bufflen < sizeof (driver_info)) { SCpnt->result = DID_BAD_TARGET << 16; callDone (SCpnt); return 1; } driver_info.size = sizeof (driver_info) - sizeof (int); driver_info.version = MEGARAID_IOCTL_VERSION; memcpy (data, &driver_info, sizeof (driver_info)); break; default: SCpnt->result = DID_BAD_TARGET << 16; } callDone (SCpnt); return 1; } static void inline set_mbox_xfer_addr (mega_host_config * megaCfg, mega_scb * pScb, mega_ioctl_mbox * mbox, u32 direction) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) switch (direction) { case TO_DEVICE: pScb->dma_direction = PCI_DMA_TODEVICE; break; case FROM_DEVICE: pScb->dma_direction = PCI_DMA_FROMDEVICE; break; case FROMTO_DEVICE: pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; break; } pScb->dma_h_bulkdata = pci_map_single (megaCfg->dev, pScb->buff_ptr, pScb->iDataSize, pScb->dma_direction); mbox->xferaddr = pScb->dma_h_bulkdata; pScb->dma_type = M_RD_BULK_DATA_ONLY; TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); #else mbox->xferaddr = virt_to_bus (pScb->buff_ptr); #endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*-------------------------------------------------------------------- * build RAID commands for controller, passed down through ioctl() *--------------------------------------------------------------------*/ static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) { mega_scb *pScb; mega_ioctl_mbox *mbox; mega_mailbox *mailbox; mega_passthru *pthru; u8 *mboxdata; long seg, i = 0; unsigned char *data = (unsigned char *) SCpnt->request_buffer; if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } pthru = &pScb->pthru; mboxdata = (u8 *) & pScb->mboxData; mbox = (mega_ioctl_mbox *) & pScb->mboxData; mailbox = (mega_mailbox *) & pScb->mboxData; memset (mailbox, 0, sizeof (pScb->mboxData)); if (data[0] == 0x03) { /* passthrough command */ unsigned char cdblen = data[2]; memset (pthru, 0, sizeof (mega_passthru)); pthru->islogical = (data[cdblen + 3] & 0x80) ? 1 : 0; pthru->timeout = data[cdblen + 3] & 0x07; pthru->reqsenselen = 14; pthru->ars = (data[cdblen + 3] & 0x08) ? 1 : 0; pthru->logdrv = data[cdblen + 4]; pthru->channel = data[cdblen + 5]; pthru->target = data[cdblen + 6]; pthru->cdblen = cdblen; memcpy (pthru->cdb, &data[3], cdblen); mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; pthru->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & pthru-> dataxferaddr, (u32 *) & pthru-> dataxferlen); mailbox->xferaddr = virt_to_bus (pthru); for (i = 0; i < (SCpnt->request_bufflen - cdblen - 7); i++) { data[i] = data[i + cdblen + 7]; } return pScb; } /* else normal (nonpassthru) command */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /*0x020024 */ /* *usage of the function copy from user is used in case of data more than *4KB.This is used only with adapters which supports more than 8 logical * drives.This feature is disabled on kernels earlier or same as 2.0.36 * as the uaccess.h file is not available with those kernels. */ if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { /* use external data area for large xfers */ /* If cmnd[0] is set to M_RD_IOCTL_CMD_NEW then * * cmnd[4..7] = external user buffer * * cmnd[8..11] = length of buffer * * */ char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]); u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]); switch (data[0]) { case FW_FIRE_WRITE: case FW_FIRE_FLASH: if ((ulong) user_area & (PAGE_SIZE - 1)) { printk ("megaraid:user address not aligned on 4K boundary.Error.\n"); SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } break; default: break; } if (!(pScb->buff_ptr = kmalloc (xfer_size, GFP_KERNEL))) { printk ("megaraid: Insufficient mem for M_RD_IOCTL_CMD_NEW.\n"); SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } copy_from_user (pScb->buff_ptr, user_area, xfer_size); pScb->iDataSize = xfer_size; switch (data[0]) { case DCMD_FC_CMD: switch (data[1]) { case DCMD_FC_READ_NVRAM_CONFIG: case DCMD_GET_DISK_CONFIG: { if ((ulong) pScb-> buff_ptr & (PAGE_SIZE - 1)) { printk ("megaraid:user address not sufficient Error.\n"); SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } /*building SG list */ mega_build_kernel_sg (pScb->buff_ptr, xfer_size, pScb, mbox); break; } default: break; } /*switch (data[1]) */ break; } } #endif mbox->cmd = data[0]; mbox->channel = data[1]; mbox->param = data[2]; mbox->pad[0] = data[3]; mbox->logdrv = data[4]; if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { switch (data[0]) { case FW_FIRE_WRITE: mbox->cmd = FW_FIRE_WRITE; mbox->channel = data[1]; /* Current Block Number */ set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); mbox->numsgelements = 0; break; case FW_FIRE_FLASH: mbox->cmd = FW_FIRE_FLASH; mbox->channel = data[1] | 0x80; /* Origin */ set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); mbox->numsgelements = 0; break; case DCMD_FC_CMD: *(mboxdata + 0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD */ *(mboxdata + 2) = data[1]; /*sub command */ switch (data[1]) { case DCMD_FC_READ_NVRAM_CONFIG: case DCMD_FC_READ_NVRAM_CONFIG_64: /* number of elements in SG list */ *(mboxdata + 3) = mbox->numsgelements; if (megaCfg->flag & BOARD_64BIT) *(mboxdata + 2) = DCMD_FC_READ_NVRAM_CONFIG_64; break; case DCMD_WRITE_CONFIG: case DCMD_WRITE_CONFIG_64: if (megaCfg->flag & BOARD_64BIT) *(mboxdata + 2) = DCMD_WRITE_CONFIG_64; set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); mbox->numsgelements = 0; break; case DCMD_GET_DISK_CONFIG: case DCMD_GET_DISK_CONFIG_64: if (megaCfg->flag & BOARD_64BIT) *(mboxdata + 2) = DCMD_GET_DISK_CONFIG_64; *(mboxdata + 3) = data[2]; /*number of elements in SG list */ /*nr of elements in SG list */ *(mboxdata + 4) = mbox->numsgelements; break; case DCMD_DELETE_LOGDRV: case DCMD_DELETE_DRIVEGROUP: case NC_SUBOP_ENQUIRY3: *(mboxdata + 3) = data[2]; set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); mbox->numsgelements = 0; break; case DCMD_CHANGE_LDNO: case DCMD_CHANGE_LOOPID: *(mboxdata + 3) = data[2]; *(mboxdata + 4) = data[3]; set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); mbox->numsgelements = 0; break; default: set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); mbox->numsgelements = 0; break; } /*switch */ break; default: set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); mbox->numsgelements = 0; break; } } else { mbox->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & mbox-> xferaddr, (u32 *) & seg); /* Handling some of the fw special commands */ switch (data[0]) { case 6: /* START_DEV */ mbox->xferaddr = *((u32 *) & data[i + 6]); break; default: break; } for (i = 0; i < (SCpnt->request_bufflen - 6); i++) { data[i] = data[i + 6]; } } return (pScb); } static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox) { ulong i, buffer_area, len, end, end_page, x, idx = 0; buffer_area = (ulong) barea; i = buffer_area; end = buffer_area + xfersize; end_page = (end) & ~(PAGE_SIZE - 1); do { len = PAGE_SIZE - (i % PAGE_SIZE); x = pScb->sgList[idx].address = virt_to_bus ((volatile void *) i); pScb->sgList[idx].length = len; i += len; idx++; } while (i < end_page); if ((end - i) < 0) { printk ("megaraid:Error in user address\n"); } if (end - i) { pScb->sgList[idx].address = virt_to_bus ((volatile void *) i); pScb->sgList[idx].length = end - i; idx++; } mbox->xferaddr = virt_to_bus (pScb->sgList); mbox->numsgelements = idx; } #endif #if DEBUG static unsigned int cum_time = 0; static unsigned int cum_time_cnt = 0; static void showMbox (mega_scb * pScb) { mega_mailbox *mbox; if (pScb == NULL) return; mbox = (mega_mailbox *) pScb->mboxData; printk ("%u cmd:%x id:%x #scts:%x lba:%x addr:%x logdrv:%x #sg:%x\n", pScb->SCpnt->pid, mbox->cmd, mbox->cmdid, mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv, mbox->numsgelements); } #endif /*-------------------------------------------------------------------- * Interrupt service routine *--------------------------------------------------------------------*/ static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) { IO_LOCK_T; mega_host_config * megaCfg; u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; u32 dword = 0; mega_mailbox *mbox; mega_scb *pScb; u_char qCnt, qStatus; u_char completed[MAX_FIRMWARE_STATUS]; Scsi_Cmnd *SCpnt; megaCfg = (mega_host_config *) devp; mbox = (mega_mailbox *) tmpBox; if (megaCfg->host->irq == irq) { if (megaCfg->flag & IN_ISR) { TRACE (("ISR called reentrantly!!\n")); printk ("ISR called reentrantly!!\n"); } megaCfg->flag |= IN_ISR; if (mega_busyWaitMbox (megaCfg)) { printk (KERN_WARNING "Error: mailbox busy in isr!\n"); } /* Check if a valid interrupt is pending */ if (megaCfg->flag & BOARD_QUARTZ) { dword = RDOUTDOOR (megaCfg); if (dword != 0x10001234) { /* Spurious interrupt */ megaCfg->flag &= ~IN_ISR; return; } } else { byte = READ_PORT (megaCfg->host->io_port, INTR_PORT); if ((byte & VALID_INTR_BYTE) == 0) { /* Spurious interrupt */ megaCfg->flag &= ~IN_ISR; return; } WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); } for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++) completed[idx] = 0; IO_LOCK; megaCfg->nInterrupts++; qCnt = 0xff; while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ; qStatus = 0xff; while ((qStatus = megaCfg->mbox->status) == 0xFF) ; /* Get list of completed requests */ for (idx = 0; idx < qCnt; idx++) { while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) { printk ("p"); } completed[idx] = sIdx; sIdx = 0xFF; } if (megaCfg->flag & BOARD_QUARTZ) { WROUTDOOR (megaCfg, dword); /* Acknowledge interrupt */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* In this case mbox contains physical address */ #if 0 WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2); #else WRINDOOR (megaCfg, 0x2); #endif #else #if 0 WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); #else WRINDOOR (megaCfg, 0x2); #endif #endif #if 0 while (RDINDOOR (megaCfg) & 0x02) ; #endif } else { CLEAR_INTR (megaCfg->host->io_port); } #if DEBUG if (qCnt >= MAX_FIRMWARE_STATUS) { printk ("megaraid_isr: cmplt=%d ", qCnt); } #endif for (idx = 0; idx < qCnt; idx++) { sIdx = completed[idx]; if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) { pScb = &megaCfg->scbList[sIdx - 1]; /* ASSERT(pScb->state == SCB_ISSUED); */ #if DEBUG if (((jiffies) - pScb->isrcount) > maxCmdTime) { maxCmdTime = (jiffies) - pScb->isrcount; printk ("megaraid_isr : cmd time = %u\n", maxCmdTime); } #endif /* * Assuming that the scsi command, for which * an abort request was received earlier, has * completed. */ if (pScb->state == SCB_ABORTED) { SCpnt = pScb->SCpnt; } if (pScb->state == SCB_RESET) { SCpnt = pScb->SCpnt; mega_freeSCB (megaCfg, pScb); SCpnt->result = (DID_RESET << 16); if (megaCfg->qCompletedH == NULL) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; } else { megaCfg->qCompletedT-> host_scribble = (unsigned char *) SCpnt; megaCfg->qCompletedT = SCpnt; } megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; megaCfg->qCcnt++; continue; } /* We don't want the ISR routine to touch M_RD_IOCTL_CMD_NEW commands, so * don't mark them as complete, instead we pop their semaphore so * that the queue routine can finish them off */ if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { /* save the status byte for the queue routine to use */ pScb->SCpnt->result = qStatus; up (&pScb->ioctl_sem); } else { /* Mark command as completed */ mega_cmd_done (megaCfg, pScb, qStatus); } } else { printk ("megaraid: wrong cmd id completed from firmware:id=%x\n", sIdx); } } mega_rundoneq (megaCfg); megaCfg->flag &= ~IN_ISR; /* Loop through any pending requests */ mega_runpendq (megaCfg); IO_UNLOCK; } } /*==================================================*/ /* Wait until the controller's mailbox is available */ /*==================================================*/ static int mega_busyWaitMbox (mega_host_config * megaCfg) { mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; long counter; for (counter = 0; counter < 10000; counter++) { if (!mbox->busy) { return 0; } udelay (100); barrier (); } return -1; /* give up after 1 second */ } /*===================================================== * Post a command to the card * * Arguments: * mega_host_config *megaCfg - Controller structure * u_char *mboxData - Mailbox area, 16 bytes * mega_scb *pScb - SCB posting (or NULL if N/A) * int intr - if 1, interrupt, 0 is blocking * Return Value: (added on 7/26 for 40ld/64bit) * -1: the command was not actually issued out * other cases: * intr==0, return ScsiStatus, i.e. mbox->status * intr==1, return 0 *===================================================== */ static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, mega_scb * pScb, int intr) { volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64; #endif u_char byte; #ifdef __LP64__ u64 phys_mbox; #else u32 phys_mbox; #endif u8 retval = -1; mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */ mboxData[0xF] = 1; /* Set busy */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* In this case mbox contains physical address */ phys_mbox = megaCfg->adjdmahandle64; #else phys_mbox = virt_to_bus (megaCfg->mbox); #endif #if DEBUG ShowMbox (pScb); #endif /* Wait until mailbox is free */ if (mega_busyWaitMbox (megaCfg)) { printk ("Blocked mailbox......!!\n"); udelay (1000); #if DEBUG showMbox (pLastScb); #endif /* Abort command */ if (pScb == NULL) { TRACE (("NULL pScb in megaIssue\n")); printk ("NULL pScb in megaIssue\n"); } mega_cmd_done (megaCfg, pScb, 0x08); return -1; } pLastScb = pScb; /* Copy mailbox data into host structure */ megaCfg->mbox64->xferSegment_lo = 0; megaCfg->mbox64->xferSegment_hi = 0; memcpy ((char *) mbox, mboxData, 16); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) switch (mboxData[0]) { case MEGA_MBOXCMD_LREAD64: case MEGA_MBOXCMD_LWRITE64: mbox64->xferSegment_lo = mbox->xferaddr; mbox64->xferSegment_hi = 0; mbox->xferaddr = 0xFFFFFFFF; break; } #endif /* Kick IO */ if (intr) { /* Issue interrupt (non-blocking) command */ if (megaCfg->flag & BOARD_QUARTZ) { mbox->mraid_poll = 0; mbox->mraid_ack = 0; WRINDOOR (megaCfg, phys_mbox | 0x1); } else { ENABLE_INTR (megaCfg->host->io_port); ISSUE_COMMAND (megaCfg->host->io_port); } pScb->state = SCB_ISSUED; retval = 0; } else { /* Issue non-ISR (blocking) command */ disable_irq (megaCfg->host->irq); if (megaCfg->flag & BOARD_QUARTZ) { mbox->mraid_poll = 0; mbox->mraid_ack = 0; mbox->numstatus = 0xFF; mbox->status = 0xFF; WRINDOOR (megaCfg, phys_mbox | 0x1); while (mbox->numstatus == 0xFF) ; while (mbox->status == 0xFF) ; while (mbox->mraid_poll != 0x77) ; mbox->mraid_poll = 0; mbox->mraid_ack = 0x77; /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); WROUTDOOR (megaCfg, cmdDone); */ if (pScb) { mega_cmd_done (megaCfg, pScb, mbox->status); } WRINDOOR (megaCfg, phys_mbox | 0x2); while (RDINDOOR (megaCfg) & 0x2) ; } else { DISABLE_INTR (megaCfg->host->io_port); ISSUE_COMMAND (megaCfg->host->io_port); while (! ((byte = READ_PORT (megaCfg->host->io_port, INTR_PORT)) & INTR_VALID)) ; WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); ENABLE_INTR (megaCfg->host->io_port); CLEAR_INTR (megaCfg->host->io_port); if (pScb) { mega_cmd_done (megaCfg, pScb, mbox->status); } else { TRACE (("Error: NULL pScb!\n")); } } enable_irq (megaCfg->host->irq); retval = mbox->status; } #if DEBUG while (mega_busyWaitMbox (megaCfg)) { printk(KERN_ERR "Blocked mailbox on exit......!\n"); udelay (1000); } #endif return retval; } /*------------------------------------------------------------------- * Copies data to SGLIST *-------------------------------------------------------------------*/ /* Note: For 64 bit cards, we need a minimum of one SG element for read/write */ static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, u32 * buffer, u32 * length) { struct scatterlist *sgList; int idx; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) int sgcnt; #endif mega_mailbox *mbox = NULL; mbox = (mega_mailbox *) scb->mboxData; /* Scatter-gather not used */ if (scb->SCpnt->use_sg == 0) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, scb->SCpnt->request_buffer, scb->SCpnt->request_bufflen, scb->dma_direction); /* We need to handle special commands like READ64, WRITE64 as they need a minimum of 1 SG irrespective of actually SG */ if ((megaCfg->flag & BOARD_64BIT) && ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { scb->sg64List[0].address = scb->dma_h_bulkdata; scb->sg64List[0].length = scb->SCpnt->request_bufflen; *buffer = scb->dma_sghandle64; *length = 0; scb->sglist_count = 1; return 1; } else { *buffer = scb->dma_h_bulkdata; *length = (u32) scb->SCpnt->request_bufflen; } #else *buffer = virt_to_bus (scb->SCpnt->request_buffer); *length = (u32) scb->SCpnt->request_bufflen; #endif return 0; } sgList = (struct scatterlist *) scb->SCpnt->request_buffer; #if 0 if (scb->SCpnt->use_sg == 1) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, sgList[0].address, sgList[0].length, scb->dma_direction); if ((megaCfg->flag & BOARD_64BIT) && ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { scb->sg64List[0].address = scb->dma_h_bulkdata; scb->sg64List[0].length = scb->SCpnt->request_bufflen; *buffer = scb->dma_sghandle64; *length = 0; scb->sglist_count = 1; return 1; } else { *buffer = scb->dma_h_bulkdata; *length = (u32) sgList[0].length; } #else *buffer = virt_to_bus (sgList[0].address); *length = (u32) sgList[0].length; #endif return 0; } #endif /* Copy Scatter-Gather list info into controller structure */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) sgcnt = pci_map_sg (megaCfg->dev, sgList, scb->SCpnt->use_sg, scb->dma_direction); /* Determine the validity of the new count */ if (sgcnt == 0) printk ("pci_map_sg returned zero!!! "); for (idx = 0; idx < sgcnt; idx++, sgList++) { if ((megaCfg->flag & BOARD_64BIT) && ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { scb->sg64List[idx].address = sg_dma_address (sgList); scb->sg64List[idx].length = sg_dma_len (sgList); } else { scb->sgList[idx].address = sg_dma_address (sgList); scb->sgList[idx].length = sg_dma_len (sgList); } } #else for (idx = 0; idx < scb->SCpnt->use_sg; idx++) { scb->sgList[idx].address = virt_to_bus (sgList[idx].address); scb->sgList[idx].length = (u32) sgList[idx].length; } #endif /* Reset pointer and length fields */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) *buffer = scb->dma_sghandle64; scb->sglist_count = scb->SCpnt->use_sg; #else *buffer = virt_to_bus (scb->sgList); #endif *length = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* Return count of SG requests */ return sgcnt; #else /* Return count of SG requests */ return scb->SCpnt->use_sg; #endif } /*-------------------------------------------------------------------- * Initializes the address of the controller's mailbox register * The mailbox register is used to issue commands to the card. * Format of the mailbox area: * 00 01 command * 01 01 command id * 02 02 # of sectors * 04 04 logical bus address * 08 04 physical buffer address * 0C 01 logical drive # * 0D 01 length of scatter/gather list * 0E 01 reserved * 0F 01 mailbox busy * 10 01 numstatus byte * 11 01 status byte *--------------------------------------------------------------------*/ static int mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) { /* align on 16-byte boundary */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) megaCfg->mbox = &megaCfg->mailbox64ptr->mailbox; #else megaCfg->mbox = &megaCfg->mailbox64.mailbox; #endif #ifdef __LP64__ megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ((u64) (-1) ^ 0x0F)); megaCfg->adjdmahandle64 = (megaCfg->dma_handle64 + 16) & ((u64) (-1) ^ 0x0F); megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - sizeof (u64)); paddr = (paddr + 4 + 16) & ((u64) (-1) ^ 0x0F); #else megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0); #endif megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8); paddr = (paddr + 4 + 16) & 0xFFFFFFF0; #endif /* Register mailbox area with the firmware */ if (!(megaCfg->flag & BOARD_QUARTZ)) { WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, (paddr >> 8) & 0xFF); WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF); WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF); WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE); CLEAR_INTR (megaCfg->host->io_port); ENABLE_INTR (megaCfg->host->io_port); } return 0; } /*--------------------------------------------------------------------------- * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and * puts it into ProductInfo and Enquiry3 structures for later use *---------------------------------------------------------------------------*/ static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, mega_Enquiry3 * enquiry3, megaRaidProductInfo * productInfo) { int i; productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds; enquiry3->rbldRate = inquiry->AdpInfo.RbldRate; productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent; for (i = 0; i < 4; i++) { productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i]; productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i]; } enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval; productInfo->DramSize = inquiry->AdpInfo.DramSize; enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv; for (i = 0; i < MAX_LOGICAL_DRIVES; i++) { enquiry3->lDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i]; enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i]; enquiry3->lDrvState[i] = inquiry->LogdrvInfo.LDrvState[i]; } for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) { enquiry3->pDrvState[i] = inquiry->PhysdrvInfo.PDrvState[i]; } } /*------------------------------------------------------------------- * Issue an adapter info query to the controller *-------------------------------------------------------------------*/ static int mega_i_query_adapter (mega_host_config * megaCfg) { mega_Enquiry3 *enquiry3Pnt; mega_mailbox *mbox; u_char mboxData[16]; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) dma_addr_t raid_inq_dma_handle = 0, prod_info_dma_handle = 0, enquiry3_dma_handle = 0; #endif u8 retval; /* Initialize adapter inquiry mailbox */ mbox = (mega_mailbox *) mboxData; memset ((void *) megaCfg->mega_buffer, 0, sizeof (megaCfg->mega_buffer)); memset (mbox, 0, 16); /* * Try to issue Enquiry3 command * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and * update enquiry3 structure */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) enquiry3_dma_handle = pci_map_single (megaCfg->dev, (void *) megaCfg->mega_buffer, (2 * 1024L), PCI_DMA_FROMDEVICE); mbox->xferaddr = enquiry3_dma_handle; #else /*Taken care */ mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer); #endif /* Initialize mailbox databuffer addr */ enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer; /* point mega_Enguiry3 to the data buf */ mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ mboxData[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ mboxData[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ /* Issue a blocking command to the card */ if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) { /* the adapter does not support 40ld */ mega_RAIDINQ adapterInquiryData; mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) raid_inq_dma_handle = pci_map_single (megaCfg->dev, (void *) adapterInquiryPnt, sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE); mbox->xferaddr = raid_inq_dma_handle; #else /*taken care */ mbox->xferaddr = virt_to_bus ((void *) adapterInquiryPnt); #endif mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter */ /* Issue a blocking command to the card */ ; retval = megaIssueCmd (megaCfg, mboxData, NULL, 0); pci_unmap_single (megaCfg->dev, raid_inq_dma_handle, sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE); /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/ mega_Convert8ldTo40ld (adapterInquiryPnt, enquiry3Pnt, (megaRaidProductInfo *) & megaCfg-> productInfo); } else { /* adapter supports 40ld */ megaCfg->flag |= BOARD_40LD; pci_unmap_single (megaCfg->dev, enquiry3_dma_handle, (2 * 1024L), PCI_DMA_FROMDEVICE); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*get productInfo, which is static information and will be unchanged*/ prod_info_dma_handle = pci_map_single (megaCfg->dev, (void *) &megaCfg->productInfo, sizeof (megaRaidProductInfo), PCI_DMA_FROMDEVICE); mbox->xferaddr = prod_info_dma_handle; #else /*taken care */ mbox->xferaddr = virt_to_bus ((void *) &megaCfg->productInfo); #endif mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) printk ("megaraid: Product_info cmd failed with error: %d\n", retval); pci_unmap_single (megaCfg->dev, prod_info_dma_handle, sizeof (megaRaidProductInfo), PCI_DMA_FROMDEVICE); } /* * kernel scans the channels from 0 to <= max_channel */ megaCfg->host->max_channel = megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1; megaCfg->host->max_id = 16; /* max targets per channel */ /*(megaCfg->flag & BOARD_40LD)?FC_MAX_TARGETS_PER_CHANNEL:MAX_TARGET+1; */ #if 0 megaCfg->host->max_lun = /* max lun */ (megaCfg->flag & BOARD_40LD) ? FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; #endif megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */ megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN; megaCfg->numldrv = enquiry3Pnt->numLDrv; megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; if (megaCfg->max_cmds > MAX_COMMANDS) megaCfg->max_cmds = MAX_COMMANDS - 1; megaCfg->host->can_queue = megaCfg->max_cmds - 1; #if 0 if (megaCfg->host->can_queue >= MAX_COMMANDS) { megaCfg->host->can_queue = MAX_COMMANDS - 16; } #endif /* use HP firmware and bios version encoding */ if (megaCfg->productInfo.subSystemVendorID == HP_SUBSYS_ID) { sprintf (megaCfg->fwVer, "%c%d%d.%d%d", megaCfg->productInfo.FwVer[2], megaCfg->productInfo.FwVer[1] >> 8, megaCfg->productInfo.FwVer[1] & 0x0f, megaCfg->productInfo.FwVer[0] >> 8, megaCfg->productInfo.FwVer[0] & 0x0f); sprintf (megaCfg->biosVer, "%c%d%d.%d%d", megaCfg->productInfo.BiosVer[2], megaCfg->productInfo.BiosVer[1] >> 8, megaCfg->productInfo.BiosVer[1] & 0x0f, megaCfg->productInfo.BiosVer[0] >> 8, megaCfg->productInfo.BiosVer[0] & 0x0f); } else { memcpy (megaCfg->fwVer, (char *) megaCfg->productInfo.FwVer, 4); megaCfg->fwVer[4] = 0; memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4); megaCfg->biosVer[4] = 0; } megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg); printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR, megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv); if ( megaCfg->support_ext_cdb ) { printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); } /* * I hope that I can unmap here, reason DMA transaction is not required any more * after this */ return 0; } /*------------------------------------------------------------------------- * * Driver interface functions * *-------------------------------------------------------------------------*/ /*---------------------------------------------------------- * Returns data to be displayed in /proc/scsi/megaraid/X *----------------------------------------------------------*/ int megaraid_proc_info (char *buffer, char **start, off_t offset, int length, int host_no, int inout) { *start = buffer; return 0; } static int mega_findCard (Scsi_Host_Template * pHostTmpl, u16 pciVendor, u16 pciDev, long flag) { mega_host_config *megaCfg = NULL; struct Scsi_Host *host = NULL; u_char pciBus, pciDevFun, megaIrq; u16 magic; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) u32 magic64; #endif int i; #ifdef __LP64__ u64 megaBase; #else u32 megaBase; #endif u16 pciIdx = 0; u16 numFound = 0; u16 subsysid, subsysvid; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ while (!pcibios_find_device (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { #else #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ struct pci_dev *pdev = NULL; #else struct pci_dev *pdev = pci_devices; #endif while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) { if(pci_enable_device (pdev)) continue; pciBus = pdev->bus->number; pciDevFun = pdev->devfn; #endif if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { pcibios_read_config_word (pciBus, pciDevFun, PCI_CONF_AMISIG, &magic); if ((magic != AMI_SIGNATURE) && (magic != AMI_SIGNATURE_471)) { pciIdx++; continue; /* not an AMI board */ } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pcibios_read_config_dword (pciBus, pciDevFun, PCI_CONF_AMISIG64, &magic64); if (magic64 == AMI_64BIT_SIGNATURE) flag |= BOARD_64BIT; #endif } /* Hmmm...Should we not make this more modularized so that in future we dont add for each firmware */ if (flag & BOARD_QUARTZ) { /* Check to see if this is a Dell PERC RAID controller model 466 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ pcibios_read_config_word (pciBus, pciDevFun, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); pcibios_read_config_word (pciBus, pciDevFun, PCI_SUBSYSTEM_ID, &subsysid); #else pci_read_config_word (pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); pci_read_config_word (pdev, PCI_SUBSYSTEM_ID, &subsysid); #endif #if 0 /* * This routine is called with well know values and we * should not be getting what we have not asked. * Also, the check is not right. It should have been for * pci_vendor_id not subsysvid - AM */ /* If we dont detect this valid subsystem vendor id's we refuse to load the driver PART of PC200X compliance */ if ((subsysvid != AMI_SUBSYS_ID) && (subsysvid != DELL_SUBSYS_ID) && (subsysvid != HP_SUBSYS_ID)) continue; #endif } 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)); /* Read the base port and IRQ from PCI */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ pcibios_read_config_dword (pciBus, pciDevFun, PCI_BASE_ADDRESS_0, (u_int *) & megaBase); pcibios_read_config_byte (pciBus, pciDevFun, PCI_INTERRUPT_LINE, &megaIrq); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ megaBase = pdev->base_address[0]; megaIrq = pdev->irq; #else megaBase = pci_resource_start (pdev, 0); megaIrq = pdev->irq; #endif pciIdx++; if (flag & BOARD_QUARTZ) { megaBase &= PCI_BASE_ADDRESS_MEM_MASK; megaBase = (long) ioremap (megaBase, 128); if (!megaBase) continue; } else { megaBase &= PCI_BASE_ADDRESS_IO_MASK; megaBase += 0x10; } /* Initialize SCSI Host structure */ host = scsi_register (pHostTmpl, sizeof (mega_host_config)); if (!host) goto err_unmap; /* * Comment the following initialization if you know 'max_sectors' is * not defined for this kernel. * This field was introduced in Linus's kernel 2.4.7pre3 and it * greatly increases the IO performance - AM */ host->max_sectors = 1024; scsi_set_pci_device(host, pdev); megaCfg = (mega_host_config *) host->hostdata; memset (megaCfg, 0, sizeof (mega_host_config)); 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_NOTICE "scsi%d : Enabling 64 bit support\n", host->host_no); /* Copy resource info into structure */ megaCfg->qCompletedH = NULL; megaCfg->qCompletedT = NULL; megaCfg->qPendingH = NULL; megaCfg->qPendingT = NULL; megaCfg->qFreeH = NULL; megaCfg->qFreeT = NULL; megaCfg->qFcnt = 0; megaCfg->qPcnt = 0; megaCfg->qCcnt = 0; megaCfg->lock_free = SPIN_LOCK_UNLOCKED; megaCfg->lock_pend = SPIN_LOCK_UNLOCKED; megaCfg->lock_scsicmd = SPIN_LOCK_UNLOCKED; megaCfg->flag = flag; megaCfg->int_qh = NULL; megaCfg->int_qt = NULL; megaCfg->int_qlen = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) megaCfg->dev = pdev; #endif megaCfg->host = host; megaCfg->base = megaBase; megaCfg->host->irq = megaIrq; megaCfg->host->io_port = megaBase; megaCfg->host->n_io_port = 16; megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; megaCtlrs[numCtlrs] = megaCfg; if (!(flag & BOARD_QUARTZ)) { /* Request our IO Range */ 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 */ if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ, "megaraid", megaCfg)) { printk (KERN_WARNING "megaraid: Couldn't register IRQ %d!\n", megaIrq); goto err_release; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* * unmap while releasing the driver, Is it required to be * PCI_DMA_BIDIRECTIONAL */ megaCfg->mailbox64ptr = pci_alloc_consistent (megaCfg->dev, sizeof (mega_mailbox64), &(megaCfg->dma_handle64)); mega_register_mailbox (megaCfg, virt_to_bus ((void *) megaCfg-> mailbox64ptr)); #else mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg-> mailbox64)); #endif mega_i_query_adapter (megaCfg); if ((subsysid == 0x1111) && (subsysvid == 0x1111)) { /* * Which firmware */ if( strcmp(megaCfg->fwVer, "3.00") == 0 || strcmp(megaCfg->fwVer, "3.01") == 0 ) { printk( KERN_WARNING "megaraid: Your card is a Dell PERC 2/SC RAID controller " "with firmware\nmegaraid: 3.00 or 3.01. This driver is " "known to have corruption issues\nmegaraid: with those " "firmware versions on this specific card. In order\n" "megaraid: to protect your data, please upgrade your " "firmware to version\nmegaraid: 3.10 or later, available " "from the Dell Technical Support web\nmegaraid: site at\n" "http://support.dell.com/us/en/filelib/download/" "index.asp?fileid=2940\n" ); } } /* * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with * firmware H.01.07 or H.01.08, disable 64 bit support, * since this firmware cannot handle 64 bit addressing */ if( (subsysvid == HP_SUBSYS_ID) && ((subsysid == 0x60E7)||(subsysid == 0x60E8)) ) { /* * which firmware */ if( strcmp(megaCfg->fwVer, "H01.07") == 0 || strcmp(megaCfg->fwVer, "H01.08") == 0 ) { printk(KERN_WARNING "megaraid: Firmware H.01.07 or H.01.08 on 1M/2M " "controllers\nmegaraid: do not support 64 bit " "addressing.\n" "megaraid: DISABLING 64 bit support.\n"); megaCfg->flag &= ~BOARD_64BIT; } } if (mega_is_bios_enabled (megaCfg)) { mega_hbas[numCtlrs].is_bios_enabled = 1; } /* * Find out which channel is raid and which is scsi */ mega_enum_raid_scsi(megaCfg); for( i = 0; i < megaCfg->productInfo.SCSIChanPresent; i++ ) { if(IS_RAID_CH(i)) printk(KERN_NOTICE"megaraid: channel[%d] is raid.\n", i+1); else printk(KERN_NOTICE"megaraid: channel[%d] is scsi.\n", i+1); } /* * Find out if a logical drive is set as the boot drive. If there is * one, will make that as the first logical drive. */ mega_get_boot_ldrv(megaCfg); mega_hbas[numCtlrs].hostdata_addr = megaCfg; /* * Do we support random deletion and addition of logical drives */ megaCfg->read_ldidmap = 0; /* set it after first logdrv delete cmd */ megaCfg->support_random_del = mega_support_random_del(megaCfg); /* Initialize SCBs */ if (mega_init_scb (megaCfg)) { pci_free_consistent (megaCfg->dev, sizeof (mega_mailbox64), (void *) megaCfg->mailbox64ptr, megaCfg->dma_handle64); scsi_unregister (host); continue; } /* * Fill in the structure which needs to be passed back to the * application when it does an ioctl() for controller related * information. */ i = numCtlrs; numCtlrs++; mcontroller[i].base = megaBase; mcontroller[i].irq = megaIrq; mcontroller[i].numldrv = megaCfg->numldrv; mcontroller[i].pcibus = pciBus; mcontroller[i].pcidev = pciDev; mcontroller[i].pcifun = PCI_FUNC (pciDevFun); mcontroller[i].pciid = pciIdx; mcontroller[i].pcivendor = pciVendor; mcontroller[i].pcislot = PCI_SLOT (pciDevFun); mcontroller[i].uid = (pciBus << 8) | pciDevFun; numFound++; /* Set the Mode of addressing to 64 bit */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if ((megaCfg->flag & BOARD_64BIT) && BITS_PER_LONG == 64) #ifdef __LP64__ pdev->dma_mask = 0xffffffffffffffff; #else pdev->dma_mask = 0xffffffff; #endif #endif continue; err_release: if (flag & BOARD_QUARTZ) release_region (megaBase, 16); err_unregister: scsi_unregister (host); err_unmap: if (flag & BOARD_QUARTZ) iounmap ((void *) megaBase); } return numFound; } /*--------------------------------------------------------- * Detects if a megaraid controller exists in this system *---------------------------------------------------------*/ int megaraid_detect (Scsi_Host_Template * pHostTmpl) { int ctlridx = 0, count = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ pHostTmpl->proc_dir = &proc_scsi_megaraid; #else pHostTmpl->proc_name = "megaraid"; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ if (!pcibios_present ()) { printk (KERN_WARNING "megaraid: PCI bios not present." M_RD_CRLFSTR); return 0; } #endif skip_id = -1; if (megaraid && !strncmp (megaraid, "skip", strlen ("skip"))) { if (megaraid[4] != '\0') { skip_id = megaraid[4] - '0'; if (megaraid[5] != '\0') { skip_id = (skip_id * 10) + (megaraid[5] - '0'); } } skip_id = (skip_id > 15) ? -1 : skip_id; } printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); memset (mega_hbas, 0, sizeof (mega_hbas)); count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID, 0); count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2, 0); count += mega_findCard (pHostTmpl, 0x8086, PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); mega_reorder_hosts (); #ifdef CONFIG_PROC_FS if (count) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ mega_proc_dir_entry = proc_mkdir ("megaraid", &proc_root); #else mega_proc_dir_entry = create_proc_entry ("megaraid", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); #endif if (!mega_proc_dir_entry) printk ("megaraid: failed to create megaraid root\n"); else for (ctlridx = 0; ctlridx < count; ctlridx++) mega_create_proc_entry (ctlridx, mega_proc_dir_entry); } #endif /* * Register the driver as a character device, for applications to access * it for ioctls. * Ideally, this should go in the init_module() routine, but since it is * hidden in the file "scsi_module.c" ( included in the end ), we define * it here * First argument (major) to register_chrdev implies a dynamic major * number allocation. */ if (count) { major = register_chrdev (0, "megadev", &megadev_fops); /* * Register the Shutdown Notification hook in kernel */ if (register_reboot_notifier (&mega_notifier)) { printk ("MegaRAID Shutdown routine not registered!!\n"); } init_MUTEX (&mimd_entry_mtx); } return count; } /*--------------------------------------------------------------------- * Release the controller's resources *---------------------------------------------------------------------*/ int megaraid_release (struct Scsi_Host *pSHost) { mega_host_config *megaCfg; mega_mailbox *mbox; u_char mboxData[16]; int i; megaCfg = (mega_host_config *) pSHost->hostdata; mbox = (mega_mailbox *) mboxData; /* Flush cache to disk */ memset (mbox, 0, 16); mboxData[0] = 0xA; free_irq (megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise extra interrupt is generated */ /* Issue a blocking (interrupts disabled) command to the card */ megaIssueCmd (megaCfg, mboxData, NULL, 0); /* Free our resources */ if (megaCfg->flag & BOARD_QUARTZ) { iounmap ((void *) megaCfg->base); } else { release_region (megaCfg->host->io_port, 16); } mega_freeSgList (megaCfg); pci_free_consistent (megaCfg->dev, sizeof (mega_mailbox64), (void *) megaCfg->mailbox64ptr, megaCfg->dma_handle64); #ifdef CONFIG_PROC_FS if (megaCfg->controller_proc_dir_entry) { remove_proc_entry ("stat", megaCfg->controller_proc_dir_entry); remove_proc_entry ("status", megaCfg->controller_proc_dir_entry); remove_proc_entry ("config", megaCfg->controller_proc_dir_entry); remove_proc_entry ("mailbox", megaCfg->controller_proc_dir_entry); for (i = 0; i < numCtlrs; i++) { char buf[12] = { 0 }; sprintf (buf, "%d", i); remove_proc_entry (buf, mega_proc_dir_entry); } remove_proc_entry ("megaraid", &proc_root); } #endif /* * Release the controller memory. A word of warning this frees * hostdata and that includes megaCfg-> so be careful what you * dereference beyond this point */ scsi_unregister (pSHost); /* * Unregister the character device interface to the driver. Ideally this * should have been done in cleanup_module routine. Since this is hidden * in file "scsi_module.c", we do it here. * major is the major number of the character device returned by call to * register_chrdev() routine. */ unregister_chrdev (major, "megadev"); unregister_reboot_notifier (&mega_notifier); return 0; } static int mega_is_bios_enabled (mega_host_config * megacfg) { mega_mailbox *mboxpnt; unsigned char mbox[16]; int ret; mboxpnt = (mega_mailbox *) mbox; memset (mbox, 0, sizeof (mbox)); memset ((void *) megacfg->mega_buffer, 0, sizeof (megacfg->mega_buffer)); /* * issue command to find out if the BIOS is enabled for this controller */ mbox[0] = IS_BIOS_ENABLED; mbox[2] = GET_BIOS; mboxpnt->xferaddr = virt_to_bus ((void *) megacfg->mega_buffer); ret = megaIssueCmd (megacfg, mbox, NULL, 0); return (*(char *) megacfg->mega_buffer); } /* * Find out what channels are RAID/SCSI */ void mega_enum_raid_scsi(mega_host_config *megacfg) { mega_mailbox *mboxp; unsigned char mbox[16]; int i; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) dma_addr_t dma_handle; #endif mboxp = (mega_mailbox *)mbox; memset(mbox, 0, sizeof(mbox)); /* * issue command to find out what channels are raid/scsi */ mbox[0] = CHNL_CLASS; mbox[2] = GET_CHNL_CLASS; memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, (2 * 1024L), PCI_DMA_FROMDEVICE); mboxp->xferaddr = dma_handle; #else mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); #endif /* * Non-ROMB firware fail this command, so all channels * must be shown RAID */ if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { mega_ch_class = *((char *)megacfg->mega_buffer); for( i = 0; i < NVIRT_CHAN; i++ ) { /* logical drives channel is RAID */ mega_ch_class |= (0x01 << (megacfg->productInfo.SCSIChanPresent+i)); } } else { mega_ch_class = 0xFF; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pci_unmap_single(megacfg->dev, dma_handle, (2 * 1024L), PCI_DMA_FROMDEVICE); #endif } /* * get the boot logical drive number if enabled */ void mega_get_boot_ldrv(mega_host_config *megacfg) { mega_mailbox *mboxp; unsigned char mbox[16]; struct private_bios_data *prv_bios_data; u16 cksum = 0; char *cksum_p; int i; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) dma_addr_t dma_handle; #endif mboxp = (mega_mailbox *)mbox; memset(mbox, 0, sizeof(mbox)); mbox[0] = BIOS_PVT_DATA; mbox[2] = GET_BIOS_PVT_DATA; memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, (2 * 1024L), PCI_DMA_FROMDEVICE); mboxp->xferaddr = dma_handle; #else mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); #endif megacfg->boot_ldrv_enabled = 0; megacfg->boot_ldrv = 0; if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { prv_bios_data = (struct private_bios_data *)megacfg->mega_buffer; cksum = 0; cksum_p = (char *)prv_bios_data; for( i = 0; i < 14; i++ ) { cksum += (u16)(*cksum_p++); } if( prv_bios_data->cksum == (u16)(0-cksum) ) { megacfg->boot_ldrv_enabled = 1; megacfg->boot_ldrv = prv_bios_data->boot_ldrv; } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pci_unmap_single(megacfg->dev, dma_handle, (2 * 1024L), PCI_DMA_FROMDEVICE); #endif } static void mega_reorder_hosts (void) { struct Scsi_Host *shpnt; struct Scsi_Host *shone; struct Scsi_Host *shtwo; mega_host_config *boot_host; int i; /* * Find the (first) host which has it's BIOS enabled */ boot_host = NULL; for (i = 0; i < MAX_CONTROLLERS; i++) { if (mega_hbas[i].is_bios_enabled) { boot_host = mega_hbas[i].hostdata_addr; break; } } if (boot_host == NULL) { printk (KERN_WARNING "megaraid: no BIOS enabled.\n"); return; } /* * Traverse through the list of SCSI hosts for our HBA locations */ shone = shtwo = NULL; for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { /* Is it one of ours? */ for (i = 0; i < MAX_CONTROLLERS; i++) { if ((mega_host_config *) shpnt->hostdata == mega_hbas[i].hostdata_addr) { /* Does this one has BIOS enabled */ if (mega_hbas[i].hostdata_addr == boot_host) { /* Are we first */ if (shtwo == NULL) /* Yes! */ return; else { /* :-( */ shone = shpnt; } } else { if (!shtwo) { /* were we here before? xchng first */ shtwo = shpnt; } } break; } } /* * Have we got the boot host and one which does not have the bios * enabled. */ if (shone && shtwo) break; } if (shone && shtwo) { mega_swap_hosts (shone, shtwo); } return; } static void mega_swap_hosts (struct Scsi_Host *shone, struct Scsi_Host *shtwo) { struct Scsi_Host *prevtoshtwo; struct Scsi_Host *prevtoshone; struct Scsi_Host *save = NULL;; /* Are these two nodes adjacent */ if (shtwo->next == shone) { if (shtwo == scsi_hostlist && shone->next == NULL) { /* just two nodes */ scsi_hostlist = shone; shone->next = shtwo; shtwo->next = NULL; } else if (shtwo == scsi_hostlist) { /* first two nodes of the list */ scsi_hostlist = shone; shtwo->next = shone->next; scsi_hostlist->next = shtwo; } else if (shone->next == NULL) { /* last two nodes of the list */ prevtoshtwo = scsi_hostlist; while (prevtoshtwo->next != shtwo) prevtoshtwo = prevtoshtwo->next; prevtoshtwo->next = shone; shone->next = shtwo; shtwo->next = NULL; } else { prevtoshtwo = scsi_hostlist; while (prevtoshtwo->next != shtwo) prevtoshtwo = prevtoshtwo->next; prevtoshtwo->next = shone; shtwo->next = shone->next; shone->next = shtwo; } } else if (shtwo == scsi_hostlist && shone->next == NULL) { /* shtwo at head, shone at tail, not adjacent */ prevtoshone = scsi_hostlist; while (prevtoshone->next != shone) prevtoshone = prevtoshone->next; scsi_hostlist = shone; shone->next = shtwo->next; prevtoshone->next = shtwo; shtwo->next = NULL; } else if (shtwo == scsi_hostlist && shone->next != NULL) { /* shtwo at head, shone is not at tail */ prevtoshone = scsi_hostlist; while (prevtoshone->next != shone) prevtoshone = prevtoshone->next; scsi_hostlist = shone; prevtoshone->next = shtwo; save = shtwo->next; shtwo->next = shone->next; shone->next = save; } else if (shone->next == NULL) { /* shtwo not at head, shone at tail */ prevtoshtwo = scsi_hostlist; prevtoshone = scsi_hostlist; while (prevtoshtwo->next != shtwo) prevtoshtwo = prevtoshtwo->next; while (prevtoshone->next != shone) prevtoshone = prevtoshone->next; prevtoshtwo->next = shone; shone->next = shtwo->next; prevtoshone->next = shtwo; shtwo->next = NULL; } else { prevtoshtwo = scsi_hostlist; prevtoshone = scsi_hostlist; save = NULL;; while (prevtoshtwo->next != shtwo) prevtoshtwo = prevtoshtwo->next; while (prevtoshone->next != shone) prevtoshone = prevtoshone->next; prevtoshtwo->next = shone; save = shone->next; shone->next = shtwo->next; prevtoshone->next = shtwo; shtwo->next = save; } return; } static inline void mega_freeSgList (mega_host_config * megaCfg) { int i; for (i = 0; i < megaCfg->max_cmds; i++) { if (megaCfg->scbList[i].sgList) pci_free_consistent (megaCfg->dev, sizeof (mega_64sglist) * MAX_SGLIST, megaCfg->scbList[i].sgList, megaCfg->scbList[i]. dma_sghandle64); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x020400 */ kfree (megaCfg->scbList[i].sgList); /* free sgList */ #endif } } /*---------------------------------------------- * Get information about the card/driver *----------------------------------------------*/ const char *megaraid_info (struct Scsi_Host *pSHost) { static char buffer[512]; mega_host_config *megaCfg; megaCfg = (mega_host_config *) pSHost->hostdata; sprintf (buffer, "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", megaCfg->fwVer, megaCfg->productInfo.MaxConcCmds, megaCfg->host->max_id, megaCfg->host->max_channel, megaCfg->host->max_lun); return buffer; } /*----------------------------------------------------------------- * Perform a SCSI command * Mailbox area: * 00 01 command * 01 01 command id * 02 02 # of sectors * 04 04 logical bus address * 08 04 physical buffer address * 0C 01 logical drive # * 0D 01 length of scatter/gather list * 0E 01 reserved * 0F 01 mailbox busy * 10 01 numstatus byte * 11 01 status byte *-----------------------------------------------------------------*/ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) { DRIVER_LOCK_T mega_host_config * megaCfg; mega_scb *pScb; char *user_area = NULL; megaCfg = (mega_host_config *) SCpnt->host->hostdata; DRIVER_LOCK (megaCfg); if (!(megaCfg->flag & (1L << SCpnt->channel))) { if (SCpnt->channel < megaCfg->productInfo.SCSIChanPresent) printk ( KERN_NOTICE "scsi%d: scanning channel %d for devices.\n", megaCfg->host->host_no, SCpnt->channel); else printk ( KERN_NOTICE "scsi%d: scanning virtual channel %d for logical drives.\n", megaCfg->host->host_no, SCpnt->channel-megaCfg->productInfo.SCSIChanPresent+1); megaCfg->flag |= (1L << SCpnt->channel); } SCpnt->scsi_done = pktComp; if (mega_driver_ioctl (megaCfg, SCpnt)) return 0; /* If driver in abort or reset.. cancel this command */ if (megaCfg->flag & IN_ABORT) { SCpnt->result = (DID_ABORT << 16); /* Add Scsi_Command to end of completed queue */ if (megaCfg->qCompletedH == NULL) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; } else { megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; megaCfg->qCompletedT = SCpnt; } megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; megaCfg->qCcnt++; DRIVER_UNLOCK (megaCfg); return 0; } else if (megaCfg->flag & IN_RESET) { SCpnt->result = (DID_RESET << 16); /* Add Scsi_Command to end of completed queue */ if (megaCfg->qCompletedH == NULL) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; } else { megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; megaCfg->qCompletedT = SCpnt; } megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; megaCfg->qCcnt++; DRIVER_UNLOCK (megaCfg); return 0; } megaCfg->flag |= IN_QUEUE; /* Allocate and build a SCB request */ if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { /* * Check if the HBA is in quiescent state, e.g., during a delete * logical drive opertion. If it is, queue the commands in the * internal queue until the delete operation is complete. */ if( ! megaCfg->quiescent ) { /* Add SCB to the head of the pending queue */ if (megaCfg->qPendingH == NULL) { megaCfg->qPendingH = megaCfg->qPendingT = pScb; } else { megaCfg->qPendingT->next = pScb; megaCfg->qPendingT = pScb; } megaCfg->qPendingT->next = NULL; megaCfg->qPcnt++; if (mega_runpendq (megaCfg) == -1) { DRIVER_UNLOCK (megaCfg); return 0; } } else { /* Add SCB to the internal queue */ if (megaCfg->int_qh == NULL) { megaCfg->int_qh = megaCfg->int_qt = pScb; } else { megaCfg->int_qt->next = pScb; megaCfg->int_qt = pScb; } megaCfg->int_qt->next = NULL; megaCfg->int_qlen++; } if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { init_MUTEX_LOCKED (&pScb->ioctl_sem); spin_unlock_irq (&io_request_lock); down (&pScb->ioctl_sem); user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]); if (copy_to_user (user_area, pScb->buff_ptr, pScb->iDataSize)) { printk ("megaraid: Error copying ioctl return value to user buffer.\n"); pScb->SCpnt->result = (DID_ERROR << 16); } spin_lock_irq (&io_request_lock); DRIVER_LOCK (megaCfg); kfree (pScb->buff_ptr); pScb->buff_ptr = NULL; mega_cmd_done (megaCfg, pScb, pScb->SCpnt->result); mega_rundoneq (megaCfg); mega_runpendq (megaCfg); DRIVER_UNLOCK (megaCfg); } megaCfg->flag &= ~IN_QUEUE; } DRIVER_UNLOCK (megaCfg); return 0; } /*---------------------------------------------------------------------- * Issue a blocking command to the controller *----------------------------------------------------------------------*/ volatile static int internal_done_flag = 0; volatile static int internal_done_errcode = 0; static DECLARE_WAIT_QUEUE_HEAD (internal_wait); static void internal_done (Scsi_Cmnd * SCpnt) { internal_done_errcode = SCpnt->result; internal_done_flag++; wake_up (&internal_wait); } /* shouldn't be used, but included for completeness */ int megaraid_command (Scsi_Cmnd * SCpnt) { internal_done_flag = 0; /* Queue command, and wait until it has completed */ megaraid_queue (SCpnt, internal_done); while (!internal_done_flag) { interruptible_sleep_on (&internal_wait); } return internal_done_errcode; } /*--------------------------------------------------------------------- * Abort a previous SCSI request *---------------------------------------------------------------------*/ int megaraid_abort (Scsi_Cmnd * SCpnt) { mega_host_config *megaCfg; int rc; /*, idx; */ mega_scb *pScb; rc = SCSI_ABORT_NOT_RUNNING; megaCfg = (mega_host_config *) SCpnt->host->hostdata; megaCfg->flag |= IN_ABORT; for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { if (pScb->SCpnt == SCpnt) { /* Found an aborting command */ #if DEBUG showMbox (pScb); #endif /* * If the command is queued to be issued to the firmware, abort the scsi cmd, * If the command is already aborted in a previous call to the _abort entry * point, return SCSI_ABORT_SNOOZE, suggesting a reset. * If the command is issued to the firmware, which might complete after * some time, we will mark the scb as aborted, and return to the mid layer, * that abort could not be done. * In the ISR, when this command actually completes, we will perform a normal * completion. * * Oct 27, 1999 */ switch (pScb->state) { case SCB_ABORTED: /* Already aborted */ rc = SCSI_ABORT_SNOOZE; break; case SCB_ISSUED: /* Waiting on ISR result */ rc = SCSI_ABORT_NOT_RUNNING; pScb->state = SCB_ABORTED; break; case SCB_ACTIVE: /* still on the pending queue */ mega_freeSCB (megaCfg, pScb); SCpnt->result = (DID_ABORT << 16); if (megaCfg->qCompletedH == NULL) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; } else { megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; megaCfg->qCompletedT = SCpnt; } megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; megaCfg->qCcnt++; rc = SCSI_ABORT_SUCCESS; break; default: printk ("megaraid_abort: unknown command state!!\n"); rc = SCSI_ABORT_NOT_RUNNING; break; } break; } } megaCfg->flag &= ~IN_ABORT; #if DEBUG if (megaCfg->flag & IN_QUEUE) printk ("ma:flag is in queue\n"); if (megaCfg->qCompletedH == NULL) printk ("ma:qchead == null\n"); #endif /* * This is required here to complete any completed requests to be communicated * over to the mid layer. * Calling just mega_rundoneq() did not work. */ if (megaCfg->qCompletedH) { SCpnt = megaCfg->qCompletedH; megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; megaCfg->qCcnt--; SCpnt->host_scribble = (unsigned char *) NULL; /* Callback */ callDone (SCpnt); } mega_rundoneq (megaCfg); return rc; } /*--------------------------------------------------------------------- * Reset a previous SCSI request *---------------------------------------------------------------------*/ int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags) { mega_host_config *megaCfg; int idx; int rc; mega_scb *pScb; rc = SCSI_RESET_NOT_RUNNING; megaCfg = (mega_host_config *) SCpnt->host->hostdata; megaCfg->flag |= IN_RESET; printk ("megaraid_RESET: %.08lx cmd=%.02x , flag = %x\n", SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, rstflags); TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n", SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun)); /* * Walk list of SCBs for any that are still outstanding */ for (idx = 0; idx < megaCfg->max_cmds; idx++) { if (megaCfg->scbList[idx].state != SCB_FREE) { SCpnt = megaCfg->scbList[idx].SCpnt; pScb = &megaCfg->scbList[idx]; if (SCpnt != NULL) { pScb->state = SCB_RESET; break; } } } megaCfg->flag &= ~IN_RESET; mega_rundoneq (megaCfg); return rc; } #ifdef CONFIG_PROC_FS /* Following code handles /proc fs */ static int proc_printf (mega_host_config * megaCfg, const char *fmt, ...) { va_list args; int i; if (megaCfg->procidx > PROCBUFSIZE) return 0; va_start (args, fmt); i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args); va_end (args); megaCfg->procidx += i; return i; } static int proc_read_config (char *page, char **start, off_t offset, int count, int *eof, void *data) { mega_host_config *megaCfg = (mega_host_config *) data; *start = page; if (megaCfg->productInfo.ProductName[0] != 0) proc_printf (megaCfg, "%s\n", megaCfg->productInfo.ProductName); proc_printf (megaCfg, "Controller Type: "); if (megaCfg->flag & BOARD_QUARTZ) proc_printf (megaCfg, "438/466/467/471/493\n"); else proc_printf (megaCfg, "418/428/434\n"); if (megaCfg->flag & BOARD_40LD) proc_printf (megaCfg, "Controller Supports 40 Logical Drives\n"); if (megaCfg->flag & BOARD_64BIT) proc_printf (megaCfg, "Controller / Driver uses 64 bit memory addressing\n"); proc_printf (megaCfg, "Base = %08x, Irq = %d, ", megaCfg->base, megaCfg->host->irq); proc_printf (megaCfg, "Logical Drives = %d, Channels = %d\n", megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent); proc_printf (megaCfg, "Version =%s:%s, DRAM = %dMb\n", megaCfg->fwVer, megaCfg->biosVer, megaCfg->productInfo.DramSize); proc_printf (megaCfg, "Controller Queue Depth = %d, Driver Queue Depth = %d\n", megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds); COPY_BACK; return count; } static int proc_read_stat (char *page, char **start, off_t offset, int count, int *eof, void *data) { int i; mega_host_config *megaCfg = (mega_host_config *) data; *start = page; proc_printf (megaCfg, "Statistical Information for this controller\n"); proc_printf (megaCfg, "Interrupts Collected = %lu\n", megaCfg->nInterrupts); for (i = 0; i < megaCfg->numldrv; i++) { proc_printf (megaCfg, "Logical Drive %d:\n", i); proc_printf (megaCfg, "\tReads Issued = %lu, Writes Issued = %lu\n", megaCfg->nReads[i], megaCfg->nWrites[i]); proc_printf (megaCfg, "\tSectors Read = %lu, Sectors Written = %lu\n\n", megaCfg->nReadBlocks[i], megaCfg->nWriteBlocks[i]); } COPY_BACK; return count; } static int proc_read_status (char *page, char **start, off_t offset, int count, int *eof, void *data) { mega_host_config *megaCfg = (mega_host_config *) data; *start = page; proc_printf (megaCfg, "TBD\n"); COPY_BACK; return count; } static int proc_read_mbox (char *page, char **start, off_t offset, int count, int *eof, void *data) { mega_host_config *megaCfg = (mega_host_config *) data; volatile mega_mailbox *mbox = megaCfg->mbox; *start = page; proc_printf (megaCfg, "Contents of Mail Box Structure\n"); proc_printf (megaCfg, " Fw Command = 0x%02x\n", mbox->cmd); proc_printf (megaCfg, " Cmd Sequence = 0x%02x\n", mbox->cmdid); proc_printf (megaCfg, " No of Sectors= %04d\n", mbox->numsectors); proc_printf (megaCfg, " LBA = 0x%02x\n", mbox->lba); proc_printf (megaCfg, " DTA = 0x%08x\n", mbox->xferaddr); proc_printf (megaCfg, " Logical Drive= 0x%02x\n", mbox->logdrv); proc_printf (megaCfg, " No of SG Elmt= 0x%02x\n", mbox->numsgelements); proc_printf (megaCfg, " Busy = %01x\n", mbox->busy); proc_printf (megaCfg, " Status = 0x%02x\n", mbox->status); /* proc_printf(megaCfg, "Dump of MailBox\n"); for (i = 0; i < 16; i++) proc_printf(megaCfg, "%02x ",*(mbox + i)); proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus); for (i = 0; i < 46; i++) { proc_printf(megaCfg,"%02d ",*(mbox + 16 + i)); if (i%16) proc_printf(megaCfg,"\n"); } if (!mbox->numsgelements) { dta = phys_to_virt(mbox->xferaddr); for (i = 0; i < mbox->numsgelements; i++) if (dta) { proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n", (ulong)*(dta + i + 4)); } }*/ COPY_BACK; return count; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ #define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \ S_IRUSR | S_IFREG,\ controller_proc_dir_entry,\ fxn, megaCfg) #else #define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg) static struct proc_dir_entry * create_proc_read_entry (const char *string, int mode, struct proc_dir_entry *parent, read_proc_t * fxn, mega_host_config * megaCfg) { struct proc_dir_entry *temp = NULL; temp = kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL); if (!temp) return NULL; memset (temp, 0, sizeof (struct proc_dir_entry)); if ((temp->name = kmalloc (strlen (string) + 1, GFP_KERNEL)) == NULL) { kfree (temp); return NULL; } strcpy ((char *) temp->name, string); temp->namelen = strlen (string); temp->mode = mode; /*S_IFREG | S_IRUSR */ ; temp->data = (void *) megaCfg; temp->read_proc = fxn; proc_register (parent, temp); return temp; } #endif static void mega_create_proc_entry (int index, struct proc_dir_entry *parent) { u_char string[64] = { 0 }; mega_host_config *megaCfg = megaCtlrs[index]; struct proc_dir_entry *controller_proc_dir_entry = NULL; sprintf (string, "%d", index); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ controller_proc_dir_entry = megaCfg->controller_proc_dir_entry = proc_mkdir (string, parent); #else controller_proc_dir_entry = megaCfg->controller_proc_dir_entry = create_proc_entry (string, S_IFDIR | S_IRUGO | S_IXUGO, parent); #endif if (!controller_proc_dir_entry) printk ("\nmegaraid: proc_mkdir failed\n"); else { megaCfg->proc_read = CREATE_READ_PROC ("config", proc_read_config); megaCfg->proc_status = CREATE_READ_PROC ("status", proc_read_status); megaCfg->proc_stat = CREATE_READ_PROC ("stat", proc_read_stat); megaCfg->proc_mbox = CREATE_READ_PROC ("mailbox", proc_read_mbox); } } #endif /* CONFIG_PROC_FS */ /*------------------------------------------------------------- * Return the disk geometry for a particular disk * Input: * Disk *disk - Disk geometry * kdev_t dev - Device node * int *geom - Returns geometry fields * geom[0] = heads * geom[1] = sectors * geom[2] = cylinders *-------------------------------------------------------------*/ int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) { int heads, sectors, cylinders; mega_host_config *megaCfg; /* Get pointer to host config structure */ megaCfg = (mega_host_config *) disk->device->host->hostdata; if( IS_RAID_CH(disk->device->channel)) { /* Default heads (64) & sectors (32) */ heads = 64; sectors = 32; cylinders = disk->capacity / (heads * sectors); /* Handle extended translation size for logical drives > 1Gb */ if (disk->capacity >= 0x200000) { heads = 255; sectors = 63; cylinders = disk->capacity / (heads * sectors); } /* return result */ geom[0] = heads; geom[1] = sectors; geom[2] = cylinders; } else { if( mega_partsize(disk, dev, geom) == 0 ) return 0; printk(KERN_WARNING "megaraid: invalid partition on this disk on channel %d\n", disk->device->channel); /* Default heads (64) & sectors (32) */ heads = 64; sectors = 32; cylinders = disk->capacity / (heads * sectors); /* Handle extended translation size for logical drives > 1Gb */ if (disk->capacity >= 0x200000) { heads = 255; sectors = 63; cylinders = disk->capacity / (heads * sectors); } /* return result */ geom[0] = heads; geom[1] = sectors; geom[2] = cylinders; } return 0; } /* * Function : static int mega_partsize(Disk * disk, kdev_t dev, int *geom) * * Purpose : to determine the BIOS mapping used to create the partition * table, storing the results (cyls, hds, and secs) in geom * * Note: Code is picked from scsicam.h * * Returns : -1 on failure, 0 on success. */ static int mega_partsize(Disk * disk, kdev_t dev, int *geom) { struct buffer_head *bh; struct partition *p, *largest = NULL; int i, largest_cyl; int heads, cyls, sectors; int capacity = disk->capacity; if(!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, block_size(dev)))) return -1; if( *(unsigned short *)(bh->b_data + 510) == 0xAA55 ) { for( largest_cyl = -1, p = (struct partition *)(0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) { if (!p->sys_ind) continue; cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); if(cyls >= largest_cyl) { largest_cyl = cyls; largest = p; } } } if (largest) { heads = largest->end_head + 1; sectors = largest->end_sector & 0x3f; if (heads == 0 || sectors == 0) { brelse(bh); return -1; } cyls = capacity/(heads * sectors); geom[0] = heads; geom[1] = sectors; geom[2] = cyls; brelse(bh); return 0; } brelse(bh); return -1; } /* * This routine will be called when the use has done a forced shutdown on the * system. Flush the Adapter cache, that's the most we can do. */ static int megaraid_reboot_notify (struct notifier_block *this, unsigned long code, void *unused) { struct Scsi_Host *pSHost; mega_host_config *megaCfg; mega_mailbox *mbox; u_char mboxData[16]; int i; if (code == SYS_DOWN || code == SYS_HALT) { for (i = 0; i < numCtlrs; i++) { pSHost = megaCtlrs[i]->host; megaCfg = (mega_host_config *) pSHost->hostdata; mbox = (mega_mailbox *) mboxData; /* Flush cache to disk */ memset (mbox, 0, 16); mboxData[0] = 0xA; /* * Free irq, otherwise extra interrupt is generated */ free_irq (megaCfg->host->irq, megaCfg); /* * Issue a blocking (interrupts disabled) command to * the card */ megaIssueCmd (megaCfg, mboxData, NULL, 0); } } return NOTIFY_DONE; } static int mega_init_scb (mega_host_config * megacfg) { int idx; #if DEBUG if (megacfg->max_cmds >= MAX_COMMANDS) { printk ("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", megacfg->max_cmds, MAX_COMMANDS); } #endif for (idx = megacfg->max_cmds - 1; idx >= 0; idx--) { megacfg->scbList[idx].idx = idx; /* * ISR will make this flag zero to indicate the command has been * completed. This is only for user ioctl calls. Rest of the driver * and the mid-layer operations are not connected with this flag. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) megacfg->scbList[idx].sgList = pci_alloc_consistent (megacfg->dev, sizeof (mega_64sglist) * MAX_SGLIST, &(megacfg->scbList[idx]. dma_sghandle64)); megacfg->scbList[idx].sg64List = (mega_64sglist *) megacfg->scbList[idx].sgList; #else megacfg->scbList[idx].sgList = kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA); #endif if (megacfg->scbList[idx].sgList == NULL) { printk (KERN_WARNING "Can't allocate sglist for id %d\n", idx); mega_freeSgList (megacfg); return -1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) megacfg->scbList[idx].pthru = pci_alloc_consistent (megacfg->dev, sizeof (mega_passthru), &(megacfg->scbList[idx]. dma_passthruhandle64)); if (megacfg->scbList[idx].pthru == NULL) { printk (KERN_WARNING "Can't allocate passthru for id %d\n", idx); } megacfg->scbList[idx].epthru = pci_alloc_consistent( megacfg->dev, sizeof(mega_ext_passthru), &(megacfg->scbList[idx].dma_ext_passthruhandle64) ); if (megacfg->scbList[idx].epthru == NULL) { printk (KERN_WARNING "Can't allocate extended passthru for id %d\n", idx); } /* * Allocate a 256 Byte Bounce Buffer for handling INQ/RD_CAPA */ megacfg->scbList[idx].bounce_buffer = pci_alloc_consistent (megacfg->dev, 256, &(megacfg->scbList[idx]. dma_bounce_buffer)); if (!megacfg->scbList[idx].bounce_buffer) printk ("megaraid: allocation for bounce buffer failed\n"); megacfg->scbList[idx].dma_type = M_RD_DMA_TYPE_NONE; #endif if (idx < MAX_COMMANDS) { /* * Link to free list * lock not required since we are loading the driver, so no * commands possible right now. */ enq_scb_freelist (megacfg, &megacfg->scbList[idx], NO_LOCK, INTR_ENB); } } return 0; } /* * Enqueues a SCB */ static void enq_scb_freelist (mega_host_config * megacfg, mega_scb * scb, int lock, int intr) { if (lock == INTERNAL_LOCK || intr == INTR_DIS) { if (intr == INTR_DIS) spin_lock_irq (&megacfg->lock_free); else spin_lock (&megacfg->lock_free); } scb->state = SCB_FREE; scb->SCpnt = NULL; if (megacfg->qFreeH == (mega_scb *) NULL) { megacfg->qFreeH = megacfg->qFreeT = scb; } else { megacfg->qFreeT->next = scb; megacfg->qFreeT = scb; } megacfg->qFreeT->next = NULL; megacfg->qFcnt++; if (lock == INTERNAL_LOCK || intr == INTR_DIS) { if (intr == INTR_DIS) spin_unlock_irq (&megacfg->lock_free); else spin_unlock (&megacfg->lock_free); } } /* * Routines for the character/ioctl interface to the driver */ static int megadev_open (struct inode *inode, struct file *filep) { MOD_INC_USE_COUNT; return 0; /* success */ } static int megadev_ioctl_entry (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { int ret = -1; /* * We do not allow parallel ioctls to the driver as of now. */ down (&mimd_entry_mtx); ret = megadev_ioctl (inode, filep, cmd, arg); up (&mimd_entry_mtx); return ret; } static int megadev_ioctl (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { int adapno; kdev_t dev; u32 inlen; struct uioctl_t ioc; char *kvaddr = NULL; int nadap = numCtlrs; u8 opcode; u32 outlen; int ret; u8 subopcode; Scsi_Cmnd *scsicmd; struct Scsi_Host *shpnt; char *uaddr; struct uioctl_t *uioc; dma_addr_t dma_addr; u32 length; mega_host_config *megacfg = NULL; #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)) return -EINVAL; if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC) return (-EINVAL); /* * Get the user ioctl structure */ ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t)); if (ret) return ret; if(copy_from_user (&ioc, (char *) arg, sizeof (struct uioctl_t))) 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. */ /* * Get the opcode and subopcode for the commands */ opcode = ioc.ui.fcs.opcode; subopcode = ioc.ui.fcs.subopcode; switch (opcode) { case M_RD_DRIVER_IOCTL_INTERFACE: switch (subopcode) { case MEGAIOC_QDRVRVER: /* Query driver version */ put_user (driver_ver, (u32 *) ioc.data); return 0; case MEGAIOC_QNADAP: /* Get # of adapters */ put_user (nadap, (int *) ioc.data); return nadap; case MEGAIOC_QADAPINFO: /* Get adapter information */ /* * which adapter? */ adapno = ioc.ui.fcs.adapno; /* * The adapter numbers do not start with 0, at least in * the user space. This is just to make sure, 0 is not the * default value which will refer to adapter 1. So the * user needs to make use of macros MKADAP() and GETADAP() * (See megaraid.h) while making ioctl() call. */ adapno = GETADAP (adapno); if (adapno >= numCtlrs) return (-ENODEV); ret = verify_area (VERIFY_WRITE, ioc.data, sizeof (struct mcontroller)); if (ret) return ret; /* * Copy struct mcontroller to user area */ copy_to_user (ioc.data, mcontroller + adapno, sizeof (struct mcontroller)); return 0; default: return (-EINVAL); } /* inner switch */ break; case M_RD_IOCTL_CMD_NEW: /* * Deletion of logical drives is only handled in 0x80 commands */ if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { return -EINVAL; } /* which adapter? */ adapno = ioc.ui.fcs.adapno; /* See comment above: MEGAIOC_QADAPINFO */ adapno = GETADAP(adapno); if (adapno >= numCtlrs) return(-ENODEV); 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. */ outlen = ioc.outlen; inlen = ioc.inlen; 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; } /* * 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 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)); #endif 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; init_MUTEX_LOCKED(&mimd_ioctl_sem); IO_LOCK; megaraid_queue(scsicmd, megadev_ioctl_done); IO_UNLOCK; down(&mimd_ioctl_sem); if( !scsicmd->result && outlen ) { copy_to_user(uaddr, kvaddr, length); } /* * copyout the result */ uioc = (struct uioctl_t *)arg; if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) { put_user( scsicmd->result, &uioc->pthru.scsistatus ); } else { put_user(1, &uioc->mbox[16]); /* numstatus */ /* status */ put_user (scsicmd->result, &uioc->mbox[17]); } 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)); #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); if (adapno >= numCtlrs) return (-ENODEV); /* save the user address */ uaddr = ioc.data; outlen = ioc.outlen; inlen = ioc.inlen; 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; } if (inlen) { ret = verify_area (VERIFY_READ, ioc.data, inlen); 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; /* * ioctls for deleting logical drives is a special case, so check * for it first */ if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { if( !megacfg->support_random_del ) { printk("megaraid: logdrv delete on non supporting f/w.\n"); return -EINVAL; } uioc = (struct uioctl_t *)arg; ret = mega_del_logdrv(megacfg, ioc.mbox[3]); put_user(1, &uioc->mbox[16]); /* numstatus */ put_user(ret, &uioc->mbox[17]); /* status */ /* if deletion failed, let the user know by failing ioctl */ return ret; } #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. */ kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &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)); #endif return -ENOMEM; } ioc.data = kvaddr; if (inlen) { if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { /* copyin the user data */ copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen); } else { copy_from_user (kvaddr, uaddr, inlen); } } } scsicmd->cmnd[0] = MEGADEVIOC; scsicmd->request_buffer = (void *) &ioc; init_MUTEX_LOCKED (&mimd_ioctl_sem); IO_LOCK; megaraid_queue (scsicmd, megadev_ioctl_done); IO_UNLOCK; down (&mimd_ioctl_sem); if (!scsicmd->result && outlen) { if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen); } else { copy_to_user (uaddr, kvaddr, outlen); } } /* * copyout the result */ uioc = (struct uioctl_t *) arg; if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { put_user (scsicmd->result, &uioc->pthru.scsistatus); } else { put_user (1, &uioc->mbox[16]); /* numstatus */ put_user (scsicmd->result, &uioc->mbox[17]); /* status */ } 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)); #endif /* restore user pointer */ ioc.data = uaddr; return ret; default: return (-EINVAL); }/* Outer switch */ return 0; } static void megadev_ioctl_done(Scsi_Cmnd *sc) { up (&mimd_ioctl_sem); } static mega_scb * megadev_doioctl (mega_host_config * megacfg, Scsi_Cmnd * sc) { u8 cmd; struct uioctl_t *ioc = NULL; mega_mailbox *mbox = NULL; mega_ioctl_mbox *mboxioc = NULL; struct mbox_passthru *mboxpthru = NULL; mega_scb *scb = NULL; mega_passthru *pthru = NULL; if ((scb = mega_allocateSCB (megacfg, sc)) == NULL) { sc->result = (DID_ERROR << 16); callDone (sc); return NULL; } ioc = (struct uioctl_t *) sc->request_buffer; memcpy (scb->mboxData, ioc->mbox, sizeof (scb->mboxData)); /* The generic mailbox */ mbox = (mega_mailbox *) ioc->mbox; /* * Get the user command */ cmd = ioc->mbox[0]; switch (cmd) { case MEGA_MBOXCMD_PASSTHRU: /* * prepare the SCB with information from the user ioctl structure */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) pthru = scb->pthru; #else pthru = &scb->pthru; #endif memcpy (pthru, &ioc->pthru, sizeof (mega_passthru)); mboxpthru = (struct mbox_passthru *) scb->mboxData; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if (megacfg->flag & BOARD_64BIT) { /* This is just a sample with one element * This if executes onlu on 2.4 kernels */ mboxpthru->dataxferaddr = scb->dma_passthruhandle64; scb->sg64List[0].address = pci_map_single (megacfg->dev, ioc->data, 4096, PCI_DMA_BIDIRECTIONAL); scb->sg64List[0].length = 4096; // TODO: Check this pthru->dataxferaddr = scb->dma_sghandle64; pthru->numsgelements = 1; mboxpthru->cmd = 0xC3; } else { mboxpthru->dataxferaddr = scb->dma_passthruhandle64; pthru->dataxferaddr = pci_map_single (megacfg->dev, ioc->data, 4096, PCI_DMA_BIDIRECTIONAL); pthru->numsgelements = 0; } #else { mboxpthru->dataxferaddr = virt_to_bus (&scb->pthru); pthru->dataxferaddr = virt_to_bus (ioc->data); pthru->numsgelements = 0; } #endif pthru->reqsenselen = 14; break; default: /* Normal command */ mboxioc = (mega_ioctl_mbox *) scb->mboxData; if (ioc->ui.fcs.opcode == M_RD_IOCTL_CMD_NEW) { scb->buff_ptr = ioc->ui.fcs.buffer; scb->iDataSize = ioc->ui.fcs.length; } else { scb->buff_ptr = ioc->data; scb->iDataSize = 4096; // TODO:check it } set_mbox_xfer_addr (megacfg, scb, mboxioc, FROMTO_DEVICE); mboxioc->numsgelements = 0; break; } return scb; } static int megadev_close (struct inode *inode, struct file *filep) { #ifdef MODULE MOD_DEC_USE_COUNT; #endif return 0; } static int mega_support_ext_cdb(mega_host_config *this_hba) { mega_mailbox *mboxpnt; unsigned char mbox[16]; int ret; mboxpnt = (mega_mailbox *) mbox; memset(mbox, 0, sizeof (mbox)); /* * issue command to find out if controller supports extended CDBs. */ mbox[0] = 0xA4; mbox[2] = 0x16; ret = megaIssueCmd(this_hba, mbox, NULL, 0); return !ret; } /* * Find out if this controller supports random deletion and addition of * logical drives */ static int mega_support_random_del(mega_host_config *this_hba) { mega_mailbox *mboxpnt; unsigned char mbox[16]; int ret; mboxpnt = (mega_mailbox *)mbox; memset(mbox, 0, sizeof(mbox)); /* * issue command */ mbox[0] = FC_DEL_LOGDRV; mbox[2] = OP_SUP_DEL_LOGDRV; ret = megaIssueCmd(this_hba, mbox, NULL, 0); return !ret; } static int mega_del_logdrv(mega_host_config *this_hba, int logdrv) { int rval; IO_LOCK_T; DECLARE_WAIT_QUEUE_HEAD(wq); mega_scb *scbp; /* * Stop sending commands to the controller, queue them internally. * When deletion is complete, ISR will flush the queue. */ IO_LOCK; this_hba->quiescent = 1; IO_UNLOCK; while( this_hba->qPcnt ) { sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ } rval = mega_do_del_logdrv(this_hba, logdrv); IO_LOCK; /* * Attach the internal queue to the pending queue */ if( this_hba->qPendingH == NULL ) { /* * If pending queue head is null, make internal queue as * pending queue */ this_hba->qPendingH = this_hba->int_qh; this_hba->qPendingT = this_hba->int_qt; this_hba->qPcnt = this_hba->int_qlen; } else { /* * Append pending queue to internal queue */ if( this_hba->int_qt ) { this_hba->int_qt->next = this_hba->qPendingH; this_hba->qPendingH = this_hba->int_qh; this_hba->qPcnt += this_hba->int_qlen; } } this_hba->int_qh = this_hba->int_qt = NULL; this_hba->int_qlen = 0; /* * If delete operation was successful, add 0x80 to the logical drive * ids for commands in the pending queue. */ if( this_hba->read_ldidmap) { for( scbp = this_hba->qPendingH; scbp; scbp = scbp->next ) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if( scbp->pthru->logdrv < 0x80 ) scbp->pthru->logdrv += 0x80; #else if( scbp->pthru.logdrv < 0x80 ) scbp->pthru.logdrv += 0x80; #endif } } this_hba->quiescent = 0; IO_UNLOCK; return rval; } static int mega_do_del_logdrv(mega_host_config *this_hba, int logdrv) { mega_mailbox *mboxpnt; unsigned char mbox[16]; int rval; mboxpnt = (mega_mailbox *)mbox; memset(mbox, 0, sizeof(mbox)); mbox[0] = FC_DEL_LOGDRV; mbox[2] = OP_DEL_LOGDRV; mbox[3] = logdrv; rval = megaIssueCmd(this_hba, mbox, NULL, 0); /* log this event */ if( rval != 0 ) { printk("megaraid: Attempt to delete logical drive %d failed.", logdrv); return rval; } printk("megaraid: logical drive %d deleted.\n", logdrv); /* * After deleting first logical drive, the logical drives must be * addressed by adding 0x80 to the logical drive id. */ this_hba->read_ldidmap = 1; return rval; } #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 #endif /* LINUX VERSION 2.4.XX */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE) Scsi_Host_Template driver_template = MEGARAID; #include "scsi_module.c" #endif /* LINUX VERSION 2.4.XX || MODULE */ /* vi: set ts=4: */