


/****************************************************************************
;
; NAME: oswrap.c
;
;
; DESCRIPTION:
;
;       Linux interface wrap functions
;
;
; REVISION HISTORY:
;
;       $Log: /ESASHBA2/Linux/driver/oswrap.c $
; 
; 60    11/17/08 11:03a Jseba
; Clean up properly if adapter initialization fails
; 
; 59    11/05/08 2:45p Jseba
; OsAbortTarget is now OsAbortCommand; cleaned up some of the module
; paramter descriptions and added multimode_ports for ESAS2HBA
; 
; 58    10/20/08 10:31a Jseba
; Update 2.4 Vendor/Device ID tables
; 
; 57    10/07/08 4:50p Jseba
; ESAS2HBA support
; 
; 56    9/23/08 9:12a Jseba
; Add ESASHBA read-only device type
; 
; 55    9/17/08 2:37p Jseba
; Add addr_override parameter
; 
; 54    8/11/08 10:27a Jseba
; Another power management fix
; 
; 53    8/05/08 3:02p Jseba
; Fix power management for older kernels (eg. RH4)
; 
; 52    7/14/08 3:23p Jseba
; Add power management code; improve rescan code
; 
; 51    6/03/08 9:30a Jseba
; Remove atto_bios_param entirely; fix 2.4 builds
; 
; 50    6/02/08 11:11a Jseba
; Added R30F
; 
; 49    5/22/08 2:34p Jseba
; Use module_init/exit; change return value of attocfg register function
; 
; 48    4/30/08 5:04p Jseba
; Fix return value for unimplemented IOCTLs
; 
; 47    4/24/08 11:38a Jseba
; Fixes for kernel 2.6.25; embed version information with MODULE_VERSION;
; removed attcfg_read_version
; 
; 46    4/17/08 4:52p Jseba
; CELERITY8FC is now CELERITY8FC
; 
; 45    4/04/08 11:31a Jseba
; Fix atto_bios_param and remove it from 2.6 builds
; 
; 44    3/19/08 1:28p Jseba
; Oops, fix atto_bios_param for 32-bit builds
; 
; 43    3/19/08 1:21p Jseba
; Add support for H30F; add atto_bios_param for 2.6; enable
; change_notification by default
; 
; 42    3/05/08 1:53p Jseba
; CELERITYFC8 changes; add check for Celerity "dummy" devices
; 
; 41    2/19/08 9:44a Jseba
; For ESASHBA, if an ATA ioctl (HDIO_DRIVE_CMD or HDIO_DRIVE_TASK) comes
; in, wrap it in an ATA PASS-THROUGH 12 SCSI command and send it on down.
; 
; 40    11/27/07 5:06p Jseba
; Remove remove atto_allow_rescans and call OsAllowChangeNotification
; instead
; 
; 39    11/06/07 12:59p Jseba
; Rework rescan code (change_notification=1) to fix problems with older
; kernels; fix bug when starting commands without sg lists
; 
; 38    9/21/07 3:12p Jseba
; Cleaned up print statements
; 
; 37    9/13/07 11:15a Jseba
; Add support for Celerity parameters fab_alternate_exchanges, num_xcb,
; num_scb
; 
; 36    9/04/07 2:38p Jseba
; Fix 2.4 builds
; 
; 35    9/04/07 1:28p Jseba
; Fix device IDs for ESASHBA
; 
; 34    8/27/07 11:10a Jseba
; Use pci_register_driver instead of pci_module_init on newer kernels
; 
; 33    8/21/07 10:42a Jseba
; Removed change_notification from 2.4 and EXPRESS2 builds
; 
; 32    8/15/07 2:52p Jseba
; Use OsLikely/Unlikely; enable FS interface for ESASHBA; set command
; success before calling OsStartCommand
; 
; 31    8/06/07 3:24p Jseba
; Support for ESASHBA driver; enable use_clustering; changed interface to
; OsStartCommand to use command and SGL structure offets (see
; LinuxExchangeOffsets in osutil.c);  map SGLs ahead of calling
; OsStartCommand; added slave_ and change_queue functions, although they
; are not yet implemented.
; 
; 30    6/04/07 3:20p Jseba
; Changed device ID for read-only SCSI adapters
; 
; 29    4/30/07 4:11p Jseba
; Make atto_detect apply only to 2.4 builds
; 
; 28    4/27/07 4:56p Jseba
; Added "low_memory_mode" parameter to automatically reduce memory usage;
; check error code from pci_module_init; add debug statements for abort
; and reset completions
; 
; 27    11/14/06 2:31p Jseba
; Add "fs" handlers for ESASRAID FS_API
; 
; 26    11/08/06 11:38a Jseba
; Move VDA event notification socket code to attocfg
; 
; 25    10/31/06 5:03p Jseba
; Added speed_override and topology_override for Celerity
; 
; 24    10/09/06 9:47a Jseba
; Added OsGetNvramSize instead of hard-coded NVRAM size; added ESASRAID
; VDA node handler; fixed init error handling for 2.4 kernel
; 
; 23    9/29/06 3:57p Jseba
; Removed unused sgl_page_size parameter from EXPRESS2;  fixed typo in
; num_ioreq default
; 
; 22    9/27/06 9:43a Jseba
; Don't use cmd->data_cmnd; fix host->max_id and host->max_lun; fix some
; debug statements
; 
; 21    9/22/06 5:23p Jseba
; Increase default can_queue, cmd_per_lun, num_sg_lists, and num_ioreq
; settings; use new OsStartCommand return values
; 
; 20    9/20/06 10:24a Jseba
; Change max_sectors to atto_max_sectors to avoid object name collision
; with 2.4 kernel; support for EXPRESS2; OsStartCommand no longer returns
; a status - failed commands will be completed via LinuxCompleteRequest
; 
; 19    8/28/06 11:19a Jseba
; Support for ESASRAID driver
; 
; 18    7/24/06 4:14p Jseba
; Scrap GlobalFlags; expand module parameters (cmd_per_lun, max_sectors,
; etc) and add description strings; misc. cleanups
; 
; 17    1/03/06 10:27a Jseba
; Remove deprecated "intermodule" functions, and just export the
; connection function; move sysfs support to attocfg module; various
; cleanups
; 
; 16    8/04/05 11:53a Jseba
; Add support for 16-byte CDBs so we work with large (>2TB) devices.
; 
; 15    3/21/05 10:30a Jseba
; Use new MODULE_PARAM_INT and MODULE_PARAM_LONG; disable sysfs functions
; for newer kernels, where such functions are GPL only
; 
; 14    12/28/04 9:52a Jseba
; Improved PCI initialization;  fixed some bugs with cleanup after driver
; fails to load
; 
; 13    12/09/04 12:29p Jseba
; Fix debug statements for 64 bit
; 
; 12    11/19/04 11:25a Jseba
; 64-bit support; sysfs support
; 
; 11    10/15/04 2:24p Jseba
; Skip initialization of dummy channels.
; 
; 10    6/29/04 3:31p Jseba
; Minor cleanups, support for 4gig
; 
; 9     6/21/04 4:47p Jseba
; Oops... set driver_template.module = THIS_MODULE before registering the
; module, otherwise our usage count will not get handled correctly.
; 
; 8     6/16/04 11:42a Jseba
; Oops - remove the proc entry before unregistering the driver
; 
; 7     6/07/04 1:54p Jseba
; Fixes for RedHat 2.1
; 
; 6     3/31/04 3:54p Jseba
; Initial support for 2.6 kernel
; 
; 5     2/04/04 9:41a Jseba
; Check subsystem vendor ID; increase max LUN; save host IRQ
; 
; 4     12/09/03 9:52a Jseba
; Rename to celerityfc
; 
; 3     11/18/03 10:25a Jseba
; Fix device discovery; return 1 (instead of DID_BUS_BUSY) if we are
; unable to start a command.
; 
; 2     11/03/03 10:49a Jseba
; Support for DX2+ chips
; 
; 1     10/24/03 10:38a Jseba
; Initial entry
; 
; 
;
;
; THIS PROGRAM AND THE INFORMATION CONTAINED HEREIN IS THE PROPERTY OF
; ATTO TECHNOLOGY, INC. AND SHALL NOT BE REPRODUCED, COPIED, OR USED IN
; WHOLE OR IN PART OTHER THAN AS PROVIDED FOR IN THE LICENSE AGREEMENT
; PURSUANT TO WHICH IT WAS FURNISHED.
;
; COPYRIGHT (c) ATTO TECHNOLOGY, INC. 2003
; ALL RIGHTS RESERVED.
;
;***************************************************************************/

#include "os.h"
#include "oswrap.h"

extern int global_pci_dev_offset;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#error !!! This driver only works on version 2.4 or 2.6 of the Linux kernel !!!
#error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#endif

MODULE_DESCRIPTION (ATTO_DRVR_NAME ": " ATTO_LONGNAME " driver");
MODULE_AUTHOR ("ATTO Technology");
/* MODULE_LICENSE ("n/a"); */
#ifdef MODULE_VERSION
MODULE_VERSION (VER_FILEVERSION_STR);
#endif

/* Export the target mode entry point function */

#if defined (CELERITY8FC)
EXPORT_SYMBOL (celerity8fc_target_connect);
#elif defined (CELERITYFC)
EXPORT_SYMBOL (celerityfc_target_connect);
#elif defined (ESAS2HBA)
EXPORT_SYMBOL (esas2hba_target_connect);
#endif

/* Global definitions */

int found_adapters = 0;

/* Scsi_Host_Template defined in scsi/hosts.h (struct SHT) */

static Scsi_Host_Template driver_template =
{
/*  next:                    NULL,                  */
/*  module:                  NULL,                  */
/*  proc_dir:                NULL,                  */
    module:                  THIS_MODULE,
    proc_info:               atto_proc_info,
    name:                    ATTO_LONGNAME,
    release:                 atto_release,
    info:                    atto_info,
    ioctl:                   atto_ioctl,
/*  command:                 NULL,                  */
    queuecommand:            atto_queuecommand,
/*  eh_strategy_handler:     NULL,                  */
    eh_abort_handler:        atto_eh_abort,
    eh_device_reset_handler: atto_device_reset,
    eh_bus_reset_handler:    atto_bus_reset,
    eh_host_reset_handler:   atto_host_reset,
    can_queue:               128,
    this_id:                 -1,
    sg_tablesize:            SG_ALL,
    cmd_per_lun:             32,
    present:                 0,
    unchecked_isa_dma:       0,
    use_clustering:          ENABLE_CLUSTERING,
    emulated:                0,
    proc_name:               ATTO_DRVR_NAME,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
    detect:                  atto_detect,
    use_new_eh_code:         0,
#else
#if 0
    slave_configure:         atto_slave_configure,
    slave_alloc:             atto_slave_alloc,
    slave_destroy:           atto_slave_destroy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
    change_queue_depth:      atto_change_queue_depth,
    change_queue_type:       atto_change_queue_type,
#endif
#endif
    max_sectors:             0xFFFF,
#endif
};

/* Module parameter definitions */

#ifdef CELERITYFC
int                quick_init = 0;
MODULE_PARAM_INT  (quick_init);
MODULE_PARM_DESC  (quick_init,
                   "Enable quick link initialization.  Default is 0 (off).  "
                   "WARNING: See readme.txt before using.");

int                max_logins = 256;
MODULE_PARAM_INT  (max_logins);
MODULE_PARM_DESC  (max_logins,
                   "Defines the maximum number of ports that can be logged "
                   "in at once.  Minimum 8, maximum 2048, default 256.");

int                num_sest = 256;
MODULE_PARAM_INT  (num_sest);
MODULE_PARM_DESC  (num_sest,
                   "Number of entries in the SCSI Exchange State Table "
                   "(SEST).  One of these structures is used per active "
                   "Exchange, either Initiator or Target Mode.  Minimum 8, "
                   "maximum 2048, default 256.");

int                num_imq = 256;
MODULE_PARAM_INT  (num_imq);
MODULE_PARM_DESC  (num_imq,
                   "Incoming Message Queue (IMQ) entries.  These are used to "
                   "report completion of I/O requests and to notify the host "
                   "driver of certain other kinds of events detected by the "
                   "hardware.  Minimum 4, maximum 4096, default 256.");

int                num_erq = 256;
MODULE_PARAM_INT  (num_erq);
MODULE_PARM_DESC  (num_erq,
                   "Exchange Request Queue (ERQ) entries.  In general, one ERQ "
                   "entry is required for each SEST entry.  Minimum 4, maximum "
                   "4096, default 256.");

int                private_els_timeout = 2;
MODULE_PARAM_INT  (private_els_timeout);
MODULE_PARM_DESC  (private_els_timeout,
                   "Timeout, in seconds, for ELS commands when on a private "
                   "loop.  Minimum 1, maximum 60, default 2.");

int                port_retry_count = 10;
MODULE_PARAM_INT  (port_retry_count);
MODULE_PARM_DESC  (port_retry_count,
                   "Maximum number of retries allowed for a port which is "
                   "logged out.  Maximum 255, default 10.");

int                speed_override = -1;
MODULE_PARAM_INT  (speed_override);
MODULE_PARM_DESC  (speed_override,
                   "Override the NVRAM link speed setting.  0 for auto speed "
                   "negotiation, 1 for 1 GB/s, 2 for 2GB/s, 4 for 4GB/s"
#ifdef CELERITY8FC
                   ", 8 for 8GB/s"
#endif
                   ".  Invalid values will result in the use of the NVRAM "
                   "setting.  Default is -1 (use NVRAM setting).");

int                topology_override = -1;
MODULE_PARAM_INT  (topology_override);
MODULE_PARM_DESC  (topology_override,
                   "Override the NVRAM topology setting.  0 for loop, 1 for "
                   "PTP, 2 for loop preferred, 3 for PTP preferred.  Invalid "
                   "values will result in the use of the NVRAM setting.  "
                   "Default is -1 (use NVRAM setting).");

int                fab_alternate_exchanges = 0;
MODULE_PARAM_INT  (fab_alternate_exchanges);
MODULE_PARM_DESC  (fab_alternate_exchanges,
                   "Causes the chip to alternate between two exchanges when it "
                   "runs out of credits on a fabric.  This may improve "
                   "performance in multiple initiator/target environments.  "
                   "Default is 0 (off).");

#endif

#if defined (CELERITYFC) || defined (ESAS2HBA)
int                target_mode = 0;
MODULE_PARAM_INT  (target_mode);
MODULE_PARM_DESC  (target_mode,
                   "Enable target mode function.  Default is 0 (off).");

#ifdef CELERITYFC
int                initiator_mode = 1;
MODULE_PARAM_INT  (initiator_mode);
MODULE_PARM_DESC  (initiator_mode,
                   "Enable initiator mode function.  Default is 1 (on).");

int                num_xcb = 512;
MODULE_PARAM_INT  (num_xcb);
MODULE_PARM_DESC  (num_xcb,
                   "Number of Exchange Control Blocks (XCBs) available for "
                   "Target Mode requests.  Each request requires one XCB.  "
                   "Incoming commands above the set limit are dropped.  This "
                   "setting is only valid when Target Mode is enabled.  "
                   "Minimum 64, maximum 2048, default 512.");

int                num_scb = 512;
MODULE_PARAM_INT  (num_scb);
MODULE_PARM_DESC  (num_scb,
                   "Number of Sequence Control Blocks (SCBs) available for "
                   "Target Mode requests.  This setting is only valid when "
                   "Target Mode is enabled.  Minimum 64, maximum 2048, default "
                   "512.");

#else /* ESAS2HBA */
int                multimode_ports = 0;
MODULE_PARAM_INT  (multimode_ports);
MODULE_PARM_DESC  (multimode_ports,
                   "Enable initiator mode function if target_mode is enabled.  "
                   "Default is 0 (off).");

int                num_tgt_req = 1024;
MODULE_PARAM_INT  (num_tgt_req);
MODULE_PARM_DESC  (num_tgt_req,
                   "Number of Target Mode requests.  This setting is only "
                   "valid when Target Mode is enabled.  Minimum 128, maximum "
                   "2048, default 1024.");

int                num_sas_addr = 1;
MODULE_PARAM_INT  (num_sas_addr);
MODULE_PARM_DESC  (num_sas_addr,
                   "Number of SAS addresses to assign to the adapter's PHYs.  "
                   "Addresses are assigned sequentially from the base address. "
                   "Valid settings 1, 2, 4, 8, default 1.");
#endif
#endif

#ifndef EXPRESS2
int                sgl_page_size = 128;
MODULE_PARAM_INT  (sgl_page_size);
MODULE_PARM_DESC  (sgl_page_size,
                   "Scatter/Gather List (SGL) Page Size in number of S/G "
                   "entries.  If your application is doing a lot of very large "
                   "transfers, you may want to increase the SGL page size.  "
                   "Minimum 8, maximum 256, default 128.");
#endif

int                change_notification = 1;
/* change_notification is always off for 2.4 kernels and EXPRESS2 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && !defined(EXPRESS2)
MODULE_PARAM_INT  (change_notification);
MODULE_PARM_DESC  (change_notification,
                   "Enable notifying the OS of the arrival and departure of "
                   "target devices.  Default is 1 (on).");
#endif

long               event_log_mask = 0;
MODULE_PARAM_LONG (event_log_mask);
MODULE_PARM_DESC  (event_log_mask,
                   "Specify a bit mask of events to report to the system "
                   "log.  Default is 0 (none).");

int                cmd_per_lun = 32;
MODULE_PARAM_INT  (cmd_per_lun);
MODULE_PARM_DESC  (cmd_per_lun,
                   "Maximum number of commands per LUN.  Minimum 1, "
                   "maximum 2048, default 32.");

int                can_queue = 128;
MODULE_PARAM_INT  (can_queue);
MODULE_PARM_DESC  (can_queue,
                   "Maximum number of commands per adapter.  Minimum 1, "
                   "maximum 2048, default 128.");

int                num_sg_lists = 1024;
MODULE_PARAM_INT  (num_sg_lists);
MODULE_PARM_DESC  (num_sg_lists,
                   "Number of SGL pages.  Minimum 4, maximum 2048, default "
                   "1024.");

int                num_ioreq = 256;
MODULE_PARAM_INT  (num_ioreq);
MODULE_PARM_DESC  (num_ioreq,
                   "Number of I/O request structures allocated.  Each I/O "
                   "requires an ioreq, so if you are doing large amounts of "
                   " I/O at once, you may see a performance gain by "
                   "increasing this value.  Minimum 16, maximum 2048, default "
                   "256.");

int                sg_tablesize = SG_ALL;
MODULE_PARAM_INT  (sg_tablesize);
MODULE_PARM_DESC  (sg_tablesize,
                   "Maximum number of entries in a scatter/gather table.  A "
                   "value of 255 means that the S/G table can be any size.  "
                   "Minimum 1, Maximum 255, default 255.");

int                atto_max_sectors = 0xFFFF;
MODULE_PARAM_INT  (atto_max_sectors);
MODULE_PARM_DESC  (atto_max_sectors,
                   "Maximum number of disk sectors in a single data transfer.  "
                   "Minimum 1, maximum 65535, default 65535.");

int                cmd_retry_count = 20;
MODULE_PARAM_INT  (cmd_retry_count);
MODULE_PARM_DESC  (cmd_retry_count,
                   "Maximum number of retries allowed for a command.  Default "
                   "20.");

int                low_memory_mode = 0;
MODULE_PARAM_INT  (low_memory_mode);
MODULE_PARM_DESC  (low_memory_mode,
                   "Automatically adjust resource usage to allow the driver to "
                   "be used on systems with limited amounts of available "
                   "memory.  Use this if you receive memory allocation errors "
                   "when loading the driver.  This setting overrides all other "
                   "resource parameters.  Default is 0 (off).");

#if defined (ESASHBA) || defined (CELERITYFC)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
char               addr_override [ATTO_PARAM_STR_LEN];
MODULE_PARAM_STR  (addr_override);
#else
/* No support for older kernels */
char               addr_override [] = "";
#endif
#endif


/****************************************************************************

    FUNCTION:   void PcidwFromPcid (PPCIDW pcidw, struct pci_dev *pcid)

    PURPOSE:    Populate PCIDW wrap structure from a Linux pci_dev

****************************************************************************/

void PcidwFromPcid (PPCIDW pcidw, struct pci_dev *pcid)
{
    int i;

    pcidw->original = pcid;

    pcidw->bus_number = pcid->bus->number;
    pcidw->devfn      = pcid->devfn;
    pcidw->irq        = pcid->irq;
    pcidw->device     = pcid->device;
    pcidw->vendor     = pcid->vendor;

    for (i = 0; i < PCIDW_RESOURCE_COUNT; i++)
    {
        pcidw->resource[i].start = pcid->resource[i].start;
        pcidw->resource[i].end   = pcid->resource[i].end;
    }
}


/****************************************************************************

    FUNCTION:   void ShwFromScsiHost (PSHW shw, struct Scsi_Host *host)

    PURPOSE:    Populate SHW wrap structure from a Linux Scsi_Host

****************************************************************************/

void ShwFromScsiHost (PSHW shw, struct Scsi_Host *host)
{
    if (host)
    {
        shw->original = host;
        shw->host_no  = host->host_no;
    }
    else
    {
        shw->original = NULL;
        shw->host_no  = 0;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    shw->os_minor_version = 6;
#else
    shw->os_minor_version = 4;
#endif
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)

/* Driver probe method called once for any device matching these entries: */

static struct pci_device_id 
atto_pci_table [] = {
#ifdef CELERITYFC
#ifdef CELERITY8FC
  /*    VID     DID        SSVID         SSID     class  mask   driver data  */
    { 0x11F8, 0x8032, ATTO_VENDOR_ID,   0x003A,     0,    0,        0 },
    { 0x11F8, 0x8032, ATTO_VENDOR_ID,   0x003B,     0,    0,        0 },
    { 0x11F8, 0x8032, ATTO_VENDOR_ID,   0x003C,     0,    0,        0 },
    { 0x11F8, 0x8031, ATTO_VENDOR_ID,   0x003D,     0,    0,        0 },
    { 0x11F8, 0x8031, ATTO_VENDOR_ID,   0x003E,     0,    0,        0 },
    { 0x11F8, 0x8031, ATTO_VENDOR_ID,   0x003F,     0,    0,        0 },
#else
  /*    VID     DID        SSVID         SSID     class  mask   driver data  */
    { 0x103C, 0x1029, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0100, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0101, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0102, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0103, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0104, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x0105, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
    { 0x15BC, 0x1203, ATTO_VENDOR_ID, PCI_ANY_ID,   0,    0,        0 },
#endif
#endif

#ifdef ESASRAID
    { ATTO_VENDOR_ID, 0x002C, ATTO_VENDOR_ID, 0x002C,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x002C, ATTO_VENDOR_ID, 0x002D,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x002C, ATTO_VENDOR_ID, 0x0041,  0, 0,        0 },
#endif

#ifdef EXPRESS2
    { LSI_VENDOR_ID,  0x0030, ATTO_VENDOR_ID, 0x8013,  0, 0,        0 },
    { LSI_VENDOR_ID,  0x0030, ATTO_VENDOR_ID, 0x8014,  0, 0,        0 },
    { LSI_VENDOR_ID,  0x0030, ATTO_VENDOR_ID, 0x8027,  0, 0,        0 },
    { LSI_VENDOR_ID,  0x0030, ATTO_VENDOR_ID, 0x802F,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0030, ATTO_VENDOR_ID, 0x8013,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0030, ATTO_VENDOR_ID, 0x8014,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0030, ATTO_VENDOR_ID, 0x8027,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0030, ATTO_VENDOR_ID, 0x802F,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0032, ATTO_VENDOR_ID, 0x8013,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0032, ATTO_VENDOR_ID, 0x8014,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0032, ATTO_VENDOR_ID, 0x8027,  0, 0,        0 },
    { ATTO_VENDOR_ID, 0x0032, ATTO_VENDOR_ID, 0x802F,  0, 0,        0 },
#endif

#ifdef ESASHBA
#ifdef ESAS2HBA
    { ATTO_VENDOR_ID, 0x0042, ATTO_VENDOR_ID, 0x0042, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0042, ATTO_VENDOR_ID, 0x0043, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0042, ATTO_VENDOR_ID, 0x0044, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0042, ATTO_VENDOR_ID, 0x0045, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0042, ATTO_VENDOR_ID, 0x0046, 0, 0,     0 },
#else
    { ATTO_VENDOR_ID, 0x0033, ATTO_VENDOR_ID, 0x0033, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0033, ATTO_VENDOR_ID, 0x0034, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0033, ATTO_VENDOR_ID, 0x0040, 0, 0,     0 },
    { ATTO_VENDOR_ID, 0x0039, ATTO_VENDOR_ID, 0x0039, 0, 0,     0 },
#endif
#endif

    { 0, 0, 0, 0, 0, 0, 0 }  /* The required last entry */
};

MODULE_DEVICE_TABLE (pci, atto_pci_table);

static int __devinit
atto_probe_one (struct pci_dev *pdev, const struct pci_device_id *pcid);

static void __devexit
atto_remove_one (struct pci_dev *pdev);

#ifdef CONFIG_PM
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
static int atto_suspend (struct pci_dev *pdev, pm_message_t state);
#else
static int atto_suspend (struct pci_dev *pdev, u32 state);
#endif
static int atto_resume (struct pci_dev *pdev);
#endif

static struct pci_driver 
atto_pci_driver = {
        .name     = ATTO_DRVR_NAME,
        .id_table = atto_pci_table,
        .probe    = atto_probe_one,
        .remove   = __devexit_p(atto_remove_one),
#ifdef CONFIG_PM
        .suspend  = atto_suspend,
        .resume   = atto_resume,
#endif
};


/* Following functions add support for the "attocfg" driver, */
/* which discovers HBAs for the ATTO Configuration Tool.     */

/****************************************************************************

    FUNCTION:   atto_attocfg_nvram_handler

    PURPOSE:    Provide attocfg interface to read/write NVRAM.
                These functions just provide an alternate way to do the
                PARAM ioctls to manipulate firmware.
                The buffer size must always be exactly 512 bytes.

    ARGUMENTS:  kobj  - kernel object for this adapter
                buf   - data buffer to read or write
                off   - offset into firmware
                count - size of buffer

    RETURNS:    Actual amount of data read or written

****************************************************************************/

static int verify_nvram_range (int NvramSize, loff_t off, size_t count)
{
    /* Sometimes an app makes one last request for 0 bytes when they  */
    /* are done, so silently give it to 'em. Also, if someone tried   */
    /* to write to the file, the size is reset to 0, so the system    */
    /* will try to keep reading up to a page. This needs to be silen- */
    /* tly handled here too. Otherwise an error is emitted.           */

    if (off >= NvramSize || count == 0)
        return 0;

    /* It must be a full request for the nvram.   The caller can be   */
    /* shorted if they give too much space.                           */

    if (off != 0 || count < NvramSize)
        return -EINVAL;
    
    return 1;
}


ssize_t atto_attocfg_nvram_handler  (struct kobject *kobj,
                                     char *buf,
                                     loff_t off,
                                     size_t count,
                                     int operation)
{
    void *context = dev_to_shost (container_of (kobj, struct device, kobj))->hostdata;
    int err;
    PEXPRESS_IOCTL ioctl;
    int ioctl_code;
    int NvramSize = OsGetNvramSize (context);

    switch (operation)
    {
    case ATTOCFG_DEFAULT_NVRAM: ioctl_code = EXPRESS_IOCTL_DEFAULT_PARAMS; break;
    case ATTOCFG_READ_NVRAM:    ioctl_code = EXPRESS_IOCTL_READ_PARAMS;    break;
    case ATTOCFG_WRITE_NVRAM:   ioctl_code = EXPRESS_IOCTL_WRITE_PARAMS;   break;
    default:                    return -EFAULT;
    }

    if ((err = verify_nvram_range (NvramSize, off, count)) != 1)
        return err;

    if (!context)
        return -ENODEV;

    /* Allocate and setup the ioctl, based on  */
    /* the operation we are trying to perform. */

    if ((ioctl = (PEXPRESS_IOCTL) vmalloc (sizeof (EXPRESS_IOCTL))) == NULL)
        return -EFAULT;

    memset (ioctl, 0, sizeof (EXPRESS_IOCTL));

    memcpy (ioctl->Header.Signature,
            EXPRESS_IOCTL_SIGNATURE,
            EXPRESS_IOCTL_SIGNATURE_SIZE);

    ioctl->Header.Channel = 0xFF;
    ioctl->Header.ReturnCode = 0xFF;

    if (ioctl_code == EXPRESS_IOCTL_WRITE_PARAMS)
        memcpy (ioctl->data.prw.DataBuffer,
                buf,
                NvramSize);

    if ((err = OsIoctl (context, ioctl_code, ioctl)) < 0
    ||  ioctl->Header.ReturnCode != IOCTL_SUCCESS
    ||  ioctl->data.prw.Code     != 1)
    {
        vfree (ioctl);
        return 0;
    }

    if (ioctl_code != EXPRESS_IOCTL_WRITE_PARAMS)
        memcpy (buf,
                ioctl->data.prw.DataBuffer,
                NvramSize);

    vfree (ioctl);

    return NvramSize;
}


#ifdef ESASRAID
attocfg_fire_vda_event fire_vda_event = NULL;
#endif

/****************************************************************************

    FUNCTION:   static void atto_attocfg_unregister (struct device_driver* driver)


    PURPOSE:    Provide attocfg with driver version information

    ARGUMENTS:  kobj  - kernel object for this adapter
                buf   - data buffer to read or write
                off   - offset into firmware
                count - size of buffer

    RETURNS:    Actual amount of data read or written

****************************************************************************/

static void atto_attocfg_unregister (void)
{
#ifdef ESASRAID
    fire_vda_event = NULL;
#endif
}


/****************************************************************************

    FUNCTION:   static ssize_t atto_attocfg_fw_handler (struct kobject *kobj,
                                                        char *buf,
                                                        loff_t off,
                                                        size_t count,
                                                        int operation)


    PURPOSE:    Provide attocfg support to read/write firmware.

    ARGUMENTS:  kobj  - kernel object for this adapter
                buf   - data buffer to read or write
                off   - offset into firmware
                count - size of buffer
                operation - specifies read or write

    RETURNS:    Actual amount of data read or written

****************************************************************************/

static ssize_t atto_attocfg_fw_handler (struct kobject *kobj,
                                        char *buf,
                                        loff_t off,
                                        size_t count,
                                        int operation)
{
    void *context = dev_to_shost (container_of (kobj, struct device, kobj))->hostdata;

    if (operation == ATTOCFG_READ_FW)
        return OsReadFw (context, buf, off, count);
    else if (operation == ATTOCFG_WRITE_FW)
        return OsWriteFw (context, buf, off, count);
    else return -EFAULT;
}


#ifdef ESASRAID
/****************************************************************************

    FUNCTION:   static ssize_t atto_attocfg_vda_handler (struct kobject *kobj,
                                                         char *buf,
                                                         loff_t off,
                                                         size_t count,
                                                         int operation)

    PURPOSE:    Provide attocfg support to read/write VDA commands.

    ARGUMENTS:  kobj  - kernel object for this adapter
                buf   - data buffer to read or write
                off   - offset into firmware
                count - size of buffer

    RETURNS:    Actual amount of data read or written
                operation - specifies read or write

****************************************************************************/

static ssize_t atto_attocfg_vda_handler (struct kobject *kobj,
                                         char *buf,
                                         loff_t off,
                                         size_t count,
                                         int operation)
{
    void *context = dev_to_shost (container_of (kobj, struct device, kobj))->hostdata;

    if (operation == ATTOCFG_READ_VDA)
        return OsReadVda (context, buf, off, count);
    else if (operation == ATTOCFG_WRITE_VDA)
        return OsWriteVda (context, buf, off, count);
    else return -EFAULT;
}
#endif

#if defined(ESASRAID) || defined(ESASHBA)
/****************************************************************************

    FUNCTION:   static ssize_t atto_attocfg_fs_handler (struct kobject *kobj,
                                                        char *buf,
                                                        loff_t off,
                                                        size_t count,
                                                        int operation)

    PURPOSE:    Provide attocfg support to read/write FS_API commands.

    ARGUMENTS:  kobj  - kernel object for this adapter
                buf   - data buffer to read or write
                off   - offset into firmware
                count - size of buffer

    RETURNS:    Actual amount of data read or written
                operation - specifies read or write

****************************************************************************/

static ssize_t atto_attocfg_fs_handler (struct kobject *kobj,
                                        char *buf,
                                        loff_t off,
                                        size_t count,
                                        int operation)
{
    void *context = dev_to_shost (container_of (kobj, struct device, kobj))->hostdata;

    if (operation == ATTOCFG_READ_FS)
        return OsReadFs (context, buf, off, count);
    else if (operation == ATTOCFG_WRITE_FS)
        return OsWriteFs (context, buf, off, count);
    else return -EFAULT;
}
#endif


/****************************************************************************

    FUNCTION:   int celerityfc_attocfg_register (PATTOCFG_HANDLERS handlers)

    PURPOSE:    Called by attocfg driver to register this driver's 
                configuration functions.

    RETURNS:    Number of hosts registered, -1 on error

****************************************************************************/

#if defined (CELERITY8FC)
int celerity8fc_attocfg_register (PATTOCFG_REGISTER reg)
#elif defined (CELERITYFC)
int celerityfc_attocfg_register (PATTOCFG_REGISTER reg)
#elif defined (ESASRAID)
int esasraid_attocfg_register (PATTOCFG_REGISTER reg)
#elif defined (EXPRESS2)
int express2_attocfg_register (PATTOCFG_REGISTER reg)
#elif defined (ESAS2HBA)
int esas2hba_attocfg_register (PATTOCFG_REGISTER reg)
#elif defined (ESASHBA)
int esashba_attocfg_register (PATTOCFG_REGISTER reg)
#else
#error Need to define _attocfg_register function!
#endif
{
    ATTOCFG_HANDLERS handlers;
    int index;
    int success = 0;

        /* Structure version check */

    if (reg->version != ATTOCFG_VERSION)
    {
        printk (DEBUG_HEADER "attocfg version mismatch! expected %d, got %d.\n",
                ATTOCFG_VERSION, reg->version);

        reg->version = ATTOCFG_VERSION;
        return -1;
    }

        /* Pass along our unregister handler. */

    reg->attocfg_unregister = atto_attocfg_unregister;

#ifdef ESASRAID
    if (reg->init_vda_event_sockets)
    {
        fire_vda_event = reg->fire_vda_event;

        if (!(*reg->init_vda_event_sockets) ())
        {
            printk (DEBUG_HEADER "attocfg failed to init VDA events, "
                    "your adapter may not work as expected.\n");

            fire_vda_event = NULL;
        }
    }
    else
    {
        printk (DEBUG_HEADER "attocfg does not support VDA events, "
                "you may need to get a newer version.\n");
    }
#endif

       /* Register all of our hosts. */

    memset (&handlers, 0, sizeof (handlers));
    handlers.hba_nvram_handler = atto_attocfg_nvram_handler;
    handlers.hba_fw_handler    = atto_attocfg_fw_handler;
#ifdef ESASRAID
    handlers.hba_vda_handler   = atto_attocfg_vda_handler;
#endif
#if defined(ESASRAID) || defined(ESASHBA)
    handlers.hba_fs_handler    = atto_attocfg_fs_handler;
#endif

    for (index = 0; index < ATTO_MAX_DEVICES; index++)
    {
        struct Scsi_Host *host = OsGetAdapterHost (index);

        if (host)
        {
            if (!(*reg->attocfg_register_host) (reg->context,
                                                &host->shost_gendev.kobj,
                                                &handlers))
            {
#ifdef ESASRAID
                fire_vda_event = NULL;
#endif
                return -1;
            }

	    success++;
        }
    }

    return success;
}


/****************************************************************************

    FUNCTION:   static int __devinit
                atto_probe_one (struct pci_dev *pdev,
                                const struct pci_device_id *pcid)

                static void __devexit
                atto_remove_one (struct pci_dev *pdev)

    PURPOSE:    Device discovery and removal

    RETURNS:    0 on success, -errno on failure

****************************************************************************/

static int __devinit
atto_probe_one (struct pci_dev *pdev, const struct pci_device_id *pcid)
{
    Scsi_Host_Template *sht = NULL;
    int rez;

    if (initiator_mode)
        sht = &driver_template;

    rez = atto_init_device (sht, pdev);

    if (rez < 0)
        return rez;

    if (rez == 1)
        found_adapters++;

    return 0;
}


static void __devexit
atto_remove_one (struct pci_dev *pdev)
{
    struct Scsi_Host *host = pci_get_drvdata (pdev);
    int index;

    index = OsCleanup (host);

    if (index < 0)
        DEBUG_LOUD (("Unknown host %p (%p) in atto_remove_one\n",
                      host, pdev));

    found_adapters--;

    /* If this was the last adapter, clean up the rest of the driver */

    if (found_adapters == 0)
        OsCleanup (NULL);
}

#ifdef CONFIG_PM
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
static int atto_suspend (struct pci_dev *pdev, pm_message_t state)
#else
static int atto_suspend (struct pci_dev *pdev, u32 state)
#endif
{
    struct Scsi_Host *host = pci_get_drvdata (pdev);
    int rez = OsSuspend (host);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
    pci_save_state (pdev);
#endif
    pci_disable_device (pdev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
    pci_set_power_state (pdev, pci_choose_state (pdev, state));
#endif

    return rez;
}

#ifndef PCI_D0
#define PCI_D0 0
#endif

static int atto_resume (struct pci_dev *pdev)
{
    struct Scsi_Host *host = pci_get_drvdata (pdev);

    pci_set_power_state (pdev, PCI_D0);
    pci_enable_wake (pdev, PCI_D0, 0);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
    pci_restore_state (pdev);
#endif

    return OsResume (host);
}
#endif

#endif


/****************************************************************************

    FUNCTION:   int esashba_init (void)

    PURPOSE:    Called when the module loads.  Checks for module parameters,
                the registers us as a SCSI driver.

    ARGUMENTS:  none

    RETURNS:    0 on success
                -1 on error

****************************************************************************/

#if defined (CELERITY8FC)
static int __init celerity8fc_init (void)
#elif defined (CELERITYFC)
static int __init celerityfc_init (void)
#elif defined (ESASRAID)
static int __init esasraid_init (void)
#elif defined (EXPRESS2)
static int __init express2_init (void)
#elif defined (ESAS2HBA)
static int __init esas2hba_init (void)
#elif defined (ESASHBA)
static int __init esashba_init (void)
#else
#error Need to define _init function!
#endif
{
    DEBUG_LOUD (("module init\n"));

    /* Make sure core revision is correct */

    if (OsGetCoreRev () != ATTO_CORE_REV)
    {
        printk (DEBUG_HEADER "Mismatched core revision:  Expected %d, got %d.\n",
                ATTO_CORE_REV, OsGetCoreRev ());

        return -1;
    }

    /* Verify valid parameters */

#if defined (CELERITYFC) || defined (ESAS2HBA)
    if (target_mode == 0
    &&  initiator_mode == 0)
    {
        printk (DEBUG_HEADER "Must enable one of target_mode or initiator_mode.\n");

        return -1;
    }
#endif

    if (can_queue < 1)
    {
        printk (DEBUG_HEADER "Warning: can_queue must be at least 1, value forced.\n");
        can_queue = 1;
    }
    else if (can_queue > 2048)
    {
        printk (DEBUG_HEADER "Warning: can_queue must be no larger than 2048, value forced.\n");
        can_queue = 2048;
    }

    if (cmd_per_lun < 1)
    {
        printk (DEBUG_HEADER "Warning: cmd_per_lun must be at least 1, value forced.\n");
        cmd_per_lun = 1;
    }
    else if (cmd_per_lun > 2048)
    {
        printk (DEBUG_HEADER "Warning: cmd_per_lun must be no larger than 2048, value forced.\n");
        cmd_per_lun = 2048;
    }

    if (sg_tablesize < 1)
    {
        printk (DEBUG_HEADER "Warning: sg_tablesize must be at least 1, value forced.\n");
        sg_tablesize = 1;
    }
    else if (sg_tablesize > SG_ALL)
    {
        printk (DEBUG_HEADER "Warning: sg_tablesize must be no larger than %d, value forced.\n",
                SG_ALL);
        sg_tablesize = SG_ALL;
    }

    if (atto_max_sectors < 1)
    {
        printk (DEBUG_HEADER "Warning: atto_max_sectors must be at least 1, value forced.\n");
        atto_max_sectors = 1;
    }
    else if (atto_max_sectors > 0xffff)
    {
        printk (DEBUG_HEADER "Warning: atto_max_sectors must be no larger than 0xffff, value forced.\n");
        atto_max_sectors = 0xffff;
    }

    /* Initialize */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
    if (initiator_mode)
    {
        /* Register us as a SCSI module */

        driver_template.module = THIS_MODULE;
        scsi_register_module (MODULE_SCSI_HA, &driver_template);
    }
    else
    {
        /* Manually detect HBAs */

        atto_detect (NULL);

        if (!found_adapters)
        {
            printk (DEBUG_HEADER "Driver will not be loaded because no "ATTO_DRVR_NAME" devices were found.\n");
            OsCleanup (NULL);
            return -1;
        }
        else
            printk (DEBUG_HEADER "Found %d adapters.\n", found_adapters);
    }
#else
    /* Manually detect HBAs */

    driver_template.module = THIS_MODULE;

    /* The system will call the driver structure's */
    /* probe method for each device.               */

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
    if (pci_module_init (&atto_pci_driver) != 0)
        printk (DEBUG_HEADER "Warning, pci_module_init failed.\n");
#else
    if (pci_register_driver (&atto_pci_driver) != 0)
        printk (DEBUG_HEADER "Warning, pci_register_driver failed.\n");
#endif

    if (!found_adapters)
    {
        pci_unregister_driver (&atto_pci_driver);
        OsCleanup (NULL);
        printk (DEBUG_HEADER "Driver will not be loaded because no ATTO "
                ATTO_DRVR_NAME " devices were found.\n");
        return -1;
    }
    else
        printk (DEBUG_HEADER "Found %d adapters.\n", found_adapters);
#endif

    return 0;
}


/****************************************************************************

    FUNCTION:   int atto_proc_ioctl (struct inode *inode, struct file *fp,
                                     unsigned int cmd, unsigned long arg) 


    PURPOSE:    Handle ioctl calls to "/proc/scsi/celerityfc/ATTONode"

****************************************************************************/

#ifdef CONFIG_PROC_FS
static struct file_operations atto_proc_fops =
{
    .ioctl = atto_proc_ioctl,
};
static struct Scsi_Host *atto_proc_host = NULL;
static int atto_proc_major = 0;

int atto_proc_ioctl (struct inode *inode, struct file *fp,
                     unsigned int cmd, unsigned long arg)
{
    return atto_ioctl_handler (atto_proc_host->hostdata,
                               (int) cmd, (void *) arg);
}
#endif

/****************************************************************************

    FUNCTION:   void esashba_exit (void)

    PURPOSE:    Called when module unloads.  Unregisters us & free memory.

    ARGUMENTS:  none

    RETURNS:    Nothing

****************************************************************************/

#if defined (CELERITY8FC)
static void __exit celerity8fc_exit (void)
#elif defined (CELERITYFC)
static void __exit celerityfc_exit (void)
#elif defined (ESASRAID)
static void __exit esasraid_exit (void)
#elif defined (EXPRESS2)
static void __exit express2_exit (void)
#elif defined (ESAS2HBA)
static void __exit esas2hba_exit (void)
#elif defined (ESASHBA)
static void __exit esashba_exit (void)
#else
#error Need to define _exit function!
#endif
{
    DEBUG_ALL (("module exit\n"));

#ifdef CONFIG_PROC_FS
    if (atto_proc_major > 0)
    {
        DEBUG_ALL (("unregister proc\n"));
        remove_proc_entry (ATTONODE_NAME, atto_proc_host->hostt->proc_dir);
        unregister_chrdev (atto_proc_major, ATTO_DRVR_NAME);
        atto_proc_major = 0;
    }
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
    if (initiator_mode)
        scsi_unregister_module (MODULE_SCSI_HA, &driver_template);
#endif

    OsCleanup (NULL);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    pci_unregister_driver (&atto_pci_driver);
#endif
}


/****************************************************************************

    FUNCTION:   int atto_proc_info (char *buffer, char **start, off_t offset,
                                    int len, int host, int in)

    PURPOSE:    Support for /proc entry for this driver

    ARGUMENTS:  buffer - input/output buffer
                start  - start of valid data in buffer (when !in)
                offset - offset to begin buffer write (when !in)
                len    - size of buffer
                host   - host number
                in     - 1 if data is input, 0 if its output

    RETURNS:    If in, number of bytes written
                If !in, number of bytes read
                For error, return negative errno

****************************************************************************/

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)

int atto_proc_info (char *buffer, char **start, off_t offset,
                    int len, int host, int in)
{
    DEBUG_ALL (("proc_info (%p, %p, %d, %d, %d, %d)\n",
                buffer, start, (int) offset, len, host, in));

    if (in)
        return OsInProcInfo (buffer, len, host);
    else
        return OsOutProcInfo (buffer, start, offset, len, host);
}

#else

int atto_proc_info (struct Scsi_Host *sh, char *buffer,
                    char **start, off_t offset, int len, int in)
{
    DEBUG_ALL (("proc_info (%p, %p, %d, %d, %d, %d)\n",
                buffer, start, (int) offset, len, sh->host_no, in));

    if (in)
        return OsInProcInfo (buffer, len, sh->host_no);
    else
        return OsOutProcInfo (buffer, start, offset, len, sh->host_no);
}

#endif


/****************************************************************************

    FUNCTION:   int atto_init_device (Scsi_Host_Template *sht,
                                      struct pci_dev *pcid)

    PURPOSE:    Initialize a discovered HBA

    ARGUMENTS:  sht - Scsi Host Template passed to scsi_register_module
                pcid - PCI device detected

    RETURNS:    1 on success
                0 on if a dummy device was detected
                -errno if failure

****************************************************************************/

int atto_init_device (Scsi_Host_Template *sht, struct pci_dev *pcid)
{
    struct Scsi_Host *host = NULL;
    PCIDW pcidw;
    SHW shw;
    void **context;

    /* Make sure its ours, not some other vendor's HBA */

    if (pcid->subsystem_vendor != ATTO_VENDOR_ID)
        return -ENODEV;

    /* See if it is a dummy device */

    if (pcid->subsystem_device == 0x0024)
    {
        DEBUG_ALL (("not initing dummy channel\n"));
        return 0;
    }

    if (pci_enable_device (pcid))
    {
        DEBUG_ALL (("pci_enable_device failed\n"));
        return -ENODEV;
    }

    DEBUG_ALL (("found %04X device\n", pcid->device));

    if (initiator_mode)
    {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)

        if ((host = scsi_host_alloc (sht, sizeof (void *))) == NULL)
        {
            DEBUG_ALL (("scsi_host_alloc returned NULL\n"));
            return -ENODEV;
        }
#else
        if ((host = scsi_register (sht, sizeof (void *))) == NULL)
        {
            DEBUG_ALL (("scsi_register returned NULL\n"));
            return -ENODEV;
        }
#endif

        /* Override max LUN and max target id */

        host->max_id  = ATTO_MAX_ID + 1;
        host->max_lun = ATTO_MAX_LUN + 1;

        /* We can handle 16-byte CDBs */

        host->max_cmd_len  = 16;

        host->can_queue    = can_queue;
        host->cmd_per_lun  = cmd_per_lun;
        host->this_id      = 255;
        host->max_channel  = 0;
        host->unique_id    = found_adapters;
        host->sg_tablesize = sg_tablesize;
        host->max_sectors  = atto_max_sectors;

        context = (void **) host->hostdata;
    }
    else
    {
        if ((context = (void **) vmalloc (sizeof (void *))) == NULL)
        {
            DEBUG_ALL (("could not allocate adapter extension\n"));
            return -ENOMEM;
        }
    }

    /* Set to bus master for BIOSes that don't do it for us */

    pci_set_master (pcid);

    /* Fill in the pci_dev and Scsi_Host wrappers for the core portion */

    PcidwFromPcid (&pcidw, pcid);
    ShwFromScsiHost (&shw, host);

    if (!OsInitAdapter (&shw, context, &pcidw, found_adapters))
    {
        DEBUG_ALL (("unable to initialize device at PCI bus %x:%x\n",
                    pcid->bus->number, pcid->devfn));

        goto InitFailed;
    }

    /* If OsInitAdapter returned success, but the context is NULL,   */
    /* this channel is a dummy driver and no further init is needed. */

    if ((*context) == NULL)
    {
        /* Note - this should never happen, we should have caught */
        /* the dummy channel above based on the SS device ID.     */

        DEBUG_ALL (("dummy channel found at PCI bus %x:%x\n",
                    pcid->bus->number, pcid->devfn));

InitFailed:
        if (initiator_mode)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
            scsi_host_put (host);
#else
            scsi_unregister (host);
#endif
        else
            vfree (context);

        context = NULL;
    
        return 0;
    }

    if (initiator_mode)
    {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        /* Add this channel as a SCSI host */

        int error;

        pci_set_drvdata (pcid, context);

        error = scsi_add_host (host, &pcid->dev);

        if (error)
        {
            printk (DEBUG_HEADER "scsi_add_host returned %d.\n", error);
            scsi_host_put (host);
            pci_set_drvdata (pcid, NULL);
            return -ENODEV;
        }

        scsi_scan_host (host);

#ifdef ATTO_RESCANS_ENABLED
        OsAllowChangeNotification (found_adapters, 1);
#endif

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12)

        /* This function was removed in 2.6.13 */

        scsi_set_device (host, &pcid->dev);

#endif

#else
        scsi_set_pci_device (host, pcid);
#endif
        if (host)
        {
            host->irq     = pcid->irq;
            host->io_port = pcid->resource[0].start;
        }
    }

    return 1;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
/****************************************************************************

    FUNCTION:   int atto_detect (Scsi_Host_Template *sht)

    PURPOSE:    Detect & initialize installed HBAs

    ARGUMENTS:  sht - Scsi Host Template passed to scsi_register_module

    RETURNS:    Number of HBAs detected

****************************************************************************/

#ifdef CELERITYFC
#ifdef CELERITY8FC
WORD    VendorIds [] = {0x11F8, 0x11F8};
WORD    DeviceIds [] = {0x8032, 0x8031};
#else
WORD    VendorIds [] = {0x103C, 0x15BC, 0x15BC, 0x15BC,
                        0x15BC, 0x15BC, 0x15BC, 0x15BC};
WORD    DeviceIds [] = {0x1029, 0x0100, 0x0101, 0x0102,
                        0x0103, 0x0104, 0x0105, 0x1203};
#endif
#endif

#ifdef ESASRAID
WORD    VendorIds [] = {ATTO_VENDOR_ID};
WORD    DeviceIds [] = {0x002C};
#endif

#ifdef EXPRESS2
WORD    VendorIds [] = {LSI_VENDOR_ID, ATTO_VENDOR_ID, ATTO_VENDOR_ID};
WORD    DeviceIds [] = {0x0030,        0x0030,         0x0031};
#endif

#ifdef ESASHBA
#ifdef ESAS2HBA
WORD    VendorIds [] = {ATTO_VENDOR_ID};
WORD    DeviceIds [] = {0x0042};
#else
WORD    VendorIds [] = {ATTO_VENDOR_ID, ATTO_VENDOR_ID};
WORD    DeviceIds [] = {0x0033,         0x0039};
#endif
#endif

int atto_detect (Scsi_Host_Template *sht)
{
    int found_orig = found_adapters;
    struct pci_dev *pcid = NULL;
    int dev_index = 0;

    DEBUG_ALL (("detect (%p)\n", sht));

    if (!pci_present ())
    {
        printk (DEBUG_HEADER "PCI not present.\n");
        return 0;
    }

    while (found_adapters < ATTO_MAX_DEVICES
       &&  dev_index < numof (VendorIds))
    {
        /* Look for the next device matching our vendor/device IDs */

        if ((pcid = pci_find_device (VendorIds [dev_index],
                                     DeviceIds [dev_index],
                                     pcid)) == NULL)
        {
            dev_index++;
            pcid = NULL;
            continue;
        }

        if (atto_init_device (sht, pcid) == 1)
            found_adapters++;
    }

    /* NOW, set use_new_eh_code to 1.  We don't do it in the template     */
    /* because then the scsi module would disable ints for this function. */

    if (sht)
        sht->use_new_eh_code = 1;

    return (found_adapters - found_orig);
}
#endif


/****************************************************************************

    FUNCTION:   int atto_release (Scsi_Host *sh)

    PURPOSE:    Free memory for associated SCSI host

    ARGUMENTS:  sh - Scsi Host to free

    RETURNS:    0 always

****************************************************************************/

int atto_release (struct Scsi_Host *sh)
{
    DEBUG_ALL (("release (%p)\n", sh));

    OsCleanup ((void *) sh);

    return 0;
}


/****************************************************************************

    FUNCTION:   const char *atto_info (struct Scsi_Host *)

    PURPOSE:    Get a string describing the adapter

    ARGUMENTS:  sh - Scsi Host to get string for

    RETURNS:    Pointer to retreived string

****************************************************************************/

const char *atto_info (struct Scsi_Host *sh)
{
    DEBUG_ALL (("info (%p)\n", sh));

#ifdef CONFIG_PROC_FS
    /* If we haven't done so already, register as a char driver */
    /* and stick a node under "/proc/scsi/celerityfc/ATTONode"  */

    if (atto_proc_major <= 0)
    {
        atto_proc_host = sh;

        atto_proc_major = register_chrdev (0, ATTO_DRVR_NAME, &atto_proc_fops);

        DEBUG_LOUD (("register_chrdev (major %d)\n", atto_proc_major));

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)

        if (atto_proc_major > 0)
            proc_mknod (ATTONODE_NAME,
                        0777 + S_IFCHR,
                        sh->hostt->proc_dir,
                        (kdev_t) MKDEV (atto_proc_major, 0));

#else

        if (atto_proc_major > 0)
        {
            struct proc_dir_entry *pde;

            pde = create_proc_entry (ATTONODE_NAME, 0, sh->hostt->proc_dir);

            if (!pde)
            {
                DEBUG_ALL (("failed to create_proc_entry\n"));
                atto_proc_major = -1;
            }
            else
                pde->proc_fops = &atto_proc_fops;
        }

#endif
    }

#endif

    return OsDriverInfo (sh->hostdata);
}


/****************************************************************************

    FUNCTION:   int atto_ioctl_handler (void *hostdata, int cmd, void *arg)
                int atto_ioctl (Scsi_Device *sd, int cmd, void *arg)

    PURPOSE:    Handles IOCTL calls to the driver

    ARGUMENTS:  sd  - Scsi Device to handle the IOCTL
                cmd - command number for the IOCTL
                arg - raw buffer for the IOCTL data

    RETURNS:    0 on success, negative errno on failure

****************************************************************************/

int atto_ioctl_handler (void *hostdata, int cmd, void *arg)
{
    int rez;
    int size;
    void *ioctl_data = NULL;

#ifdef ESASHBA
        /* Check for an  ATA command that we need to   */
        /* wrap into an ATA PASS THROUGH SCSI request. */

    if (cmd == HDIO_DRIVE_CMD)
        size = 4;
    else if (cmd == HDIO_DRIVE_TASK)
        size = 7;
    else
#endif
    size = OsGetIoctlSize (cmd);

    DEBUG_LOUD (("ioctl (%p, %x, %p)\n", hostdata, cmd, arg));

    /* If OsGetIoctlSize return -1, that means its not an */
    /* ATTO-specific cmd, so we'll just return error.     */

    if (size == -1)
        return -EINVAL;

    /* Make sure we can r/w to the user-space memory */

    if (size)
    {
        if (!access_ok (VERIFY_WRITE, arg, size))
        {
            DEBUG_ALL (("ioctl_handler access_ok failed for cmd %d,"
                        " address %p, size %d\n",
                        cmd, arg, size));

            return -EFAULT;
        }

        /* Allocate a kernel memory buffer for the IOCTL data */

        if ((ioctl_data = vmalloc (size)) == NULL)
        {
            DEBUG_ALL (("ioctl_handler vmalloc failed for %d bytes\n", size));

            return -EFAULT;
        }

        /* Copy the user-space memory into the kernel memory buffer */

        if (__copy_from_user (ioctl_data, arg, size) != 0)
        {
            DEBUG_ALL (("copy_from_user didn't copy everything\n"));
            vfree (ioctl_data);

            return -EFAULT;
        }
    }

#ifdef ESASHBA
    if (cmd == HDIO_DRIVE_CMD
    ||  cmd == HDIO_DRIVE_TASK)
    {
        PBYTE ata_pass_thru_buffer = NULL;
        PBYTE ata_pass_thru_args;
        BYTE  ata_pass_thru_cmd [MAX_COMMAND_SIZE];
        int   ata_pass_thru_buffer_size = 0;

        /* hostdata is really the Scsi_Device */
        Scsi_Device *sd = (Scsi_Device *) hostdata; 

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)
        struct scsi_request *scsi_req;

        if (!(scsi_req = scsi_allocate_request (sd, GFP_KERNEL)))
        {
            DEBUG_ALL (("scsi_allocate_request failed\n"));
            return -EINTR;
        }
#endif

        memset (ata_pass_thru_cmd, 0, sizeof (ata_pass_thru_cmd));

        ata_pass_thru_args = (PBYTE) ioctl_data;

        /* Construct ATA PASS THROUGH command based on ioctl arguments */

        ata_pass_thru_cmd [0] = 0xa1; /* ATA PASS-THROUGH 12 */

        if (cmd == HDIO_DRIVE_CMD)
        {                                                   /* ATA Register:      */
            ata_pass_thru_cmd [9] = ata_pass_thru_args [0]; /* Command Code       */
            ata_pass_thru_cmd [5] = ata_pass_thru_args [1]; /* Sector # (LBA low) */
            ata_pass_thru_cmd [3] = ata_pass_thru_args [2]; /* Features           */
            ata_pass_thru_cmd [4] = ata_pass_thru_args [3]; /* Sector count       */

            ata_pass_thru_buffer_size = ata_pass_thru_args [3] * 512;

            if (ata_pass_thru_args [0] == 0xb0)
            {
                /* This is a SMART command, put     */
                /* magic numbers in LBA Med, LBA Hi */

                ata_pass_thru_cmd [6] = 0x4f;
                ata_pass_thru_cmd [7] = 0xc2;
            }

            /* Allocate a buffer, if we need one. */

            if (ata_pass_thru_buffer_size)
            {
                ata_pass_thru_buffer = kmalloc (ata_pass_thru_buffer_size, GFP_KERNEL);

                if (ata_pass_thru_buffer == NULL)
                {
                    rez = -ENOMEM;
                    DEBUG_ALL (("kmalloc (ata_pass_thru_buffer) failed\n"));
AtaPassThruError:
                    if (ioctl_data) vfree (ioctl_data);
                    if (ata_pass_thru_buffer) kfree (ata_pass_thru_buffer);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)
                    scsi_release_request (scsi_req);
#endif
                    return rez;
                }

                ata_pass_thru_cmd [1] = 0x08;  /* Protocol - PIO Data In */
                ata_pass_thru_cmd [2] = 0x0E;
            }
            else
                ata_pass_thru_cmd [1] = 0x06;  /* Protocol - Non-data */
        }
        else if (cmd == HDIO_DRIVE_TASK)
        {                                                   /* ATA Register:      */
            ata_pass_thru_cmd [9] = ata_pass_thru_args [0]; /* Command Code       */
            ata_pass_thru_cmd [3] = ata_pass_thru_args [1]; /* Features           */
            ata_pass_thru_cmd [4] = ata_pass_thru_args [2]; /* Sector count       */
            ata_pass_thru_cmd [5] = ata_pass_thru_args [3]; /* Sector # (LBA low) */
            ata_pass_thru_cmd [6] = ata_pass_thru_args [4]; /* Cyl low            */
            ata_pass_thru_cmd [7] = ata_pass_thru_args [5]; /* Cyl high           */
            ata_pass_thru_cmd [8] = ata_pass_thru_args [6]; /* Device head        */

            ata_pass_thru_cmd [1] = 0x06;  /* Protocol - Non-data */
        }

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)
        scsi_req->sr_data_direction = (ata_pass_thru_buffer_size)
                                    ? DMA_FROM_DEVICE : DMA_NONE;

        scsi_wait_req (scsi_req, ata_pass_thru_cmd,
                       ata_pass_thru_buffer, ata_pass_thru_buffer_size,
                       20 * HZ, 5);

        if (scsi_req->sr_result)
        {
            rez = -EIO;
            DEBUG_ALL (("scsi_wait_req failed (%X)\n", scsi_req->sr_result));
            goto AtaPassThruError;
        }
#else
        if (scsi_execute_req (sd, ata_pass_thru_cmd,
                              (ata_pass_thru_buffer_size) ? DMA_FROM_DEVICE : DMA_NONE,
                              ata_pass_thru_buffer, ata_pass_thru_buffer_size,
                              NULL, 20 * HZ, 5))
        {
            rez = -EIO;
            DEBUG_ALL (("scsi_execute_req failed\n"));
            goto AtaPassThruError;
        }
#endif

        if (ata_pass_thru_buffer_size)
        {
            /* Save the response data back to the user's buffer. */

            if (__copy_to_user ((void *) (((PBYTE) arg) + 4),
                                ata_pass_thru_buffer,
                                ata_pass_thru_buffer_size) != 0)
            {
                DEBUG_ALL (("ioctl_handler copy_to_user didn't copy everything\n"));
                rez = -EFAULT;
                goto AtaPassThruError;
            }

            kfree (ata_pass_thru_buffer);
        }
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)
        scsi_release_request (scsi_req);
#endif

        rez = OSIOCTL_ALL_DONE;
    }
    else
#endif
    /* Call the IOCTL handler */

    if ((rez = OsIoctl (hostdata, cmd, ioctl_data)) < 0)
    {
        if (ioctl_data) vfree (ioctl_data);
        return rez;
    }

    /* See if the handler needs us to copy the buffer back */

    if ((rez == OSIOCTL_COPY_BACK) && size)
    {
        if (__copy_to_user (arg, ioctl_data, size) != 0)
        {
            DEBUG_ALL (("ioctl_handler copy_to_user didn't copy everything\n"));
            if (ioctl_data) vfree (ioctl_data);

            return -EFAULT;
        }
    }

    if (ioctl_data) vfree (ioctl_data);

    return 0;
}

int atto_ioctl (Scsi_Device *sd, int cmd, void *arg)
{
#ifdef ESASHBA
    if (cmd == HDIO_DRIVE_CMD
    ||  cmd == HDIO_DRIVE_TASK)
    {
        if (arg == NULL)
            return -EINVAL;

        return atto_ioctl_handler (sd, cmd, arg);
    }
#endif

    return atto_ioctl_handler (sd->host->hostdata, cmd, arg);
}


/****************************************************************************

    FUNCTION:   int atto_queuecommand (Scsi_Cmnd *cmd,
                                       void (*done)(Scsi_Cmnd *))

    PURPOSE:    Enqueue a SCSI command

    ARGUMENTS:  cmd  - SCSI command to enqueue
                done - completion routine

    RETURNS:    0 on success, 1 on failure

****************************************************************************/

int atto_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
    int rez;
    void *hostdata = CMD_HOST(cmd)->hostdata;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
    int use_sg = cmd->use_sg;
    struct scatterlist *list = (struct scatterlist *) cmd->request_buffer;
    struct pci_dev *pdev     = GetPciDevFromHostData (hostdata);
    int dir = scsi_to_pci_dma_dir (cmd->sc_data_direction);
#else
    int use_sg = scsi_dma_map (cmd);
#endif

    /* Override allowed retry count */

    if (cmd->allowed < cmd_retry_count)
        cmd->allowed = cmd_retry_count;

    cmd->scsi_done = done;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
    /* Map SG list, if any. */

    if (use_sg)
    {
#ifdef ATTO_USE_KERNEL24_SCSI_TAPE_WORKAROUND
        /* There is a bug in the 2.4 SCSI Tape driver which can result in */
        /* a malformed scatterlist for tape requests. This leads to a     */
        /* kernel panic when the x86_64 version of pci_map_sg validates   */
        /* the request's scatterlist structure. As a workaround, we will  */
        /* just map and unmap the scatterlist entries individually.       */

        int i;
        struct scatterlist *tmp_list = list;

        for (i = 0; i < use_sg; i++, tmp_list++)
        {
            void *address = tmp_list->address;

            if (!address)
                address = page_address (tmp_list->page) + tmp_list->offset;

            tmp_list->dma_address = pci_map_single (pdev, address, 
                                                    tmp_list->length, dir);
        }
#else
        if (OsUnlikely (pci_map_sg (pdev, list, use_sg, dir) != use_sg))
        {
            DEBUG_ALL (("queuecommand: pci_map_sg returned unexpected value\n"));
            return 1;
        }
#endif
    }
#endif

    /* Assume success, if it fails we will fix the result later. */

    cmd->result = DID_OK << 16;

    /* Start it up. */

    rez = OsStartCommand (hostdata, cmd,
                          CMD_TARG(cmd), CMD_LUN(cmd), CMD_TAGS(cmd));

    if (OsLikely (rez == OSSC_SUCCESS))
    {
        /* All good */
        return 0;
    }

    /* Failed, unmap the SG if present. */

    if (use_sg)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        LinuxPciUnmapSg (cmd);
#else
        scsi_dma_unmap (cmd);
#endif

    if (rez == OSSC_BUSY)
    {
        /* Core is out of resources, the OS will retry later */
        return 1;
    }
    else if (rez == OSSC_SEL_TIMEOUT)
    {
        /* Core has immediately rejected the request, */
        /* because there is no device at that address */
        cmd->result = DID_NO_CONNECT << 16;
        cmd->scsi_done (cmd);
    }
#ifdef ATTO_DEBUG
    else
        DEBUG_ALL (("queuecommand: invalid return from OsStartCommand (%d)!\n", rez));
#endif

    return 0;
}


/****************************************************************************

    FUNCTION:   int atto_eh_abort (Scsi_Cmnd *cmd)

    PURPOSE:    Abort an outstanding command

    ARGUMENTS:  cmd - SCSI command to abort

    RETURNS:    SUCCESS or FAILED

****************************************************************************/

int atto_eh_abort (Scsi_Cmnd *cmd)
{
    DEBUG_LOUD (("eh_abort (%p)\n", cmd));

    if (OsAbortCommand (CMD_HOST(cmd)->hostdata, cmd))
    {
        DEBUG_LOUD (("eh_abort (%p) done\n", cmd));
        return SUCCESS;
    }

    DEBUG_LOUD (("eh_abort (%p) failed\n", cmd));
    return FAILED;
}


/****************************************************************************

    FUNCTION:   int atto_host_reset (Scsi_Cmnd *cmd)
                int atto_bus_reset (Scsi_Cmnd *cmd)
                int atto_device_reset (Scsi_Cmnd *cmd)

    PURPOSE:    Reset the host, bus, or device

    ARGUMENTS:  cmd   - SCSI command that caused the reset

    RETURNS:    SUCCESS or FAILED

****************************************************************************/

int atto_host_reset (Scsi_Cmnd *cmd)
{
    DEBUG_LOUD (("host_reset (%p)\n", cmd));

    if (OsHostReset (CMD_HOST(cmd)->hostdata))
        return SUCCESS;

    return FAILED;
}

int atto_bus_reset (Scsi_Cmnd *cmd)
{
    DEBUG_LOUD (("bus_reset (%p)\n", cmd));

    if (OsBusReset (CMD_HOST(cmd)->hostdata))
    {
        DEBUG_LOUD (("bus_reset (%p) done\n", cmd));
        return SUCCESS;
    }

    return FAILED;
}

int atto_device_reset (Scsi_Cmnd *cmd)
{
    DEBUG_LOUD (("device_reset (%p)\n", cmd));

    if (OsDeviceReset (CMD_HOST(cmd)->hostdata, CMD_TARG(cmd), CMD_LUN(cmd)))
    {
        DEBUG_LOUD (("device_reset (%p) done\n", cmd));
        return SUCCESS;
    }

    return FAILED;
}

#if 0
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)

/****************************************************************************

    FUNCTION:   int atto_slave_alloc (Scsi_Device *dev)
                int atto_slave_configure (Scsi_Device *dev)
                void atto_slave_destroy (Scsi_Device *dev)

    PURPOSE:    Allocate, configure, and destroy SCSI devices
                NOTE: we do not use these functions at present.

    ARGUMENTS:  dev - SCSI device

    RETURNS:    0 on success, -errno on error

****************************************************************************/

int atto_slave_alloc (Scsi_Device *dev)
{
    DEBUG_ALL (("slave_alloc %p (%d:%d:%d)\n",
                dev, dec->channel, dev->id. dev->lun));

    return 0;
}


int atto_slave_configure (Scsi_Device *dev)
{
    DEBUG_ALL (("slave_configure %p\n", dev));

    if (dev->tagged_supported)
        scsi_activate_tcq (sdev, 32);
    else
        scsi_deactivate_tcq (dev, 32);

    return 0;
}


void atto_slave_destroy (Scsi_Device *dev)
{
    DEBUG_ALL (("slave_destroy %p\n", dev));
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
int atto_change_queue_depth (Scsi_Device *dev, int depth)
{
    DEBUG_ALL (("change_queue_depth %p, %d\n", dev, depth));
}


int atto_change_queue_type (Scsi_Device *dev, int type)
{
    DEBUG_ALL (("change_queue_type %p, %d\n", dev, type));
}
#endif
#endif
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#define EXPORT_ATTOCFG_SYMBOL(_sym) EXPORT_SYMBOL (_sym)
#else
#define EXPORT_ATTOCFG_SYMBOL(_sym)
#endif

#if defined (CELERITY8FC)
EXPORT_ATTOCFG_SYMBOL (celerity8fc_attocfg_register);
module_init(celerity8fc_init);
module_exit(celerity8fc_exit);
#elif defined (CELERITYFC)
EXPORT_ATTOCFG_SYMBOL (celerityfc_attocfg_register);
module_init(celerityfc_init);
module_exit(celerityfc_exit);
#elif defined (ESASRAID)
EXPORT_ATTOCFG_SYMBOL (esasraid_attocfg_register);
module_init(esasraid_init);
module_exit(esasraid_exit);
#elif defined (EXPRESS2)
EXPORT_ATTOCFG_SYMBOL (express2_attocfg_register);
module_init(express2_init);
module_exit(express2_exit);
#elif defined (ESAS2HBA)
EXPORT_ATTOCFG_SYMBOL (esas2hba_attocfg_register);
module_init(esas2hba_init);
module_exit(esas2hba_exit);
#elif defined (ESASHBA)
EXPORT_ATTOCFG_SYMBOL (esashba_attocfg_register);
module_init(esashba_init);
module_exit(esashba_exit);
#endif
