Viewing file: megaraid.c (135.79 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*=================================================================== * * 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 <alan.cox@linux.org> * * Merged with 2.1.131 source tree. * 12 Dec 98 K. Baranowski <kgb@knm.org.pl> * * 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 <atulm@lsil.com> * * 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" <johnsom@home.com> * * 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 <atulm@lsil.com> * * 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 <atulm@lsil.com> * * 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 <linux/config.h> #include <linux/version.h> #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/fcntl.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/proc_fs.h> #include <linux/blk.h> #include <linux/wait.h> #include <linux/tqueue.h> #include <linux/interrupt.h> #include <linux/mm.h> #include <asm/pgtable.h>
#include <linux/sched.h> #include <linux/stat.h> #include <linux/slab.h> /* for kmalloc() */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ #include <linux/bios32.h> #else #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */ #include <asm/spinlock.h> #else #include <linux/spinlock.h> #endif #endif
#include <asm/io.h> #include <asm/irq.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */ #include <asm/uaccess.h> #endif
/* * These header files are required for Shutdown Notification routines */ #include <linux/notifier.h> #include <linux/reboot.h> #include <linux/init.h>
#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 <linux/smp.h> #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 <c=%d.t=%d.l=%d>, 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: */
|