!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/scsi/   drwxr-xr-x
Free 318.37 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


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: */

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0196 ]--