lkml.org 
[lkml]   [2005]   [May]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2.6.12-rc4-mm1 3/4] megaraid_sas: updating the driver
    Date
    Patch 3 of 4:

    Signed-off by: Sreenivas Bagalkote <sreenivas.bagalkote@lsil.com>

    diff -Naurp linux-2.6.12-rc4-mm1.c/drivers/scsi/megaraid/megaraid_sas.c
    linux-2.6.12-rc4-mm1.d/drivers/scsi/megaraid/megaraid_sas.c
    --- linux-2.6.12-rc4-mm1.c/drivers/scsi/megaraid/megaraid_sas.c 1969-12-31
    19:00:00.000000000 -0500
    +++ linux-2.6.12-rc4-mm1.d/drivers/scsi/megaraid/megaraid_sas.c 2005-05-15
    22:04:04.579228792 -0400
    @@ -0,0 +1,3398 @@
    +/*
    + *
    + * Linux MegaRAID driver for SAS based RAID controllers
    + *
    + * Copyright (c) 2003-2005 LSI Logic Corporation.
    + *
    + * 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.
    + *
    + * FILE : megaraid_sas.c
    + * Version : v00.00.01.02
    + *
    + * Authors:
    + * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com>
    + *
    + * List of supported controllers
    + *
    + * OEM Product Name VID DID SSVID SSID
    + * --- ------------ --- --- ---- ----
    + */
    +
    +#include "megaraid_sas.h"
    +
    +MODULE_LICENSE ("GPL");
    +MODULE_VERSION (MEGASAS_VERSION);
    +MODULE_AUTHOR ("sreenivas.bagalkote@lsil.com");
    +MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver");
    +
    +/*
    + * PCI ID table for all supported controllers
    + */
    +static struct pci_device_id megasas_pci_table_g[] = {
    +
    + {
    + PCI_VENDOR_ID_LSI_LOGIC,
    + PCI_DEVICE_LSI_1064,
    + PCI_ANY_ID,
    + PCI_ANY_ID,
    + },
    + {
    + PCI_VENDOR_ID_DELL,
    + PCI_DEVICE_ID_DELL_PERC5,
    + PCI_VENDOR_ID_DELL,
    + PCI_SUBSYSTEM_DELL_PERC5E,
    + },
    + {
    + PCI_VENDOR_ID_DELL,
    + PCI_DEVICE_ID_DELL_PERC5,
    + PCI_VENDOR_ID_DELL,
    + PCI_SUBSYSTEM_DELL_PERC5I,
    + },
    + {
    + PCI_VENDOR_ID_DELL,
    + PCI_DEVICE_ID_DELL_PERC5,
    + PCI_VENDOR_ID_DELL,
    + PCI_SUBSYSTEM_DELL_PERC5I_INTEGRATED,
    + },
    + {
    + PCI_VENDOR_ID_LSI_LOGIC,
    + PCI_DEVICE_ID_DELL_SAS5,
    + PCI_VENDOR_ID_DELL,
    + PCI_SUBSYSTEM_DELL_SAS5I,
    + },
    + {
    + PCI_VENDOR_ID_LSI_LOGIC,
    + PCI_DEVICE_ID_DELL_SAS5,
    + PCI_VENDOR_ID_DELL,
    + PCI_SUBSYSTEM_DELL_SAS5I_INTEGRATED,
    + },
    + { 0 } /* Terminating entry */
    +};
    +
    +MODULE_DEVICE_TABLE(pci, megasas_pci_table_g);
    +
    +static int megasas_mgmt_majorno;
    +static struct megasas_mgmt_info megasas_mgmt_info;
    +static struct fasync_struct* megasas_async_queue;
    +static DECLARE_MUTEX (megasas_async_queue_mutex);
    +
    +/**
    + * megasas_get_cmd - Get a command from the free pool
    + * @instance: Adapter soft state
    + *
    + * Returns a free command from the pool
    + */
    +static inline struct megasas_cmd*
    +megasas_get_cmd( struct megasas_instance* instance )
    +{
    + unsigned long flags;
    + struct megasas_cmd* cmd;
    +
    + spin_lock_irqsave(&instance->cmd_pool_lock, flags);
    +
    + if ( list_empty(&instance->cmd_pool)) {
    + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
    + return NULL;
    + }
    +
    + cmd = list_entry((&instance->cmd_pool)->next, struct megasas_cmd,
    list);
    +
    + list_del_init( &cmd->list );
    +
    + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
    +
    + return cmd;
    +}
    +
    +/**
    + * megasas_return_cmd - Return a cmd to free command pool
    + * @instance: Adapter soft state
    + * @cmd: Command packet to be returned to free command pool
    + */
    +static inline void
    +megasas_return_cmd( struct megasas_instance* instance, struct megasas_cmd*
    cmd )
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave( &instance->cmd_pool_lock, flags );
    +
    + list_add( &cmd->list, &instance->cmd_pool );
    +
    + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
    +}
    +
    +
    +/**
    + * megasas_issue_polled - Issues a polling command
    + * @instance: Adapter soft state
    + * @cmd: Command packet to be issued
    + *
    + * For polling, MFI requires the cmd_status to be set to 0xFF before
    posting.
    + */
    +static int
    +megasas_issue_polled(struct megasas_instance* instance, struct megasas_cmd*
    cmd)
    +{
    + int i;
    + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
    +
    + struct megasas_header* frame_hdr = (struct
    megasas_header*)cmd->frame;
    +
    + frame_hdr->cmd_status = 0xFF;
    + frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
    +
    + /*
    + * Issue the frame using inbound queue port
    + */
    + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
    +
    + /*
    + * Wait for cmd_status to change
    + */
    + for( i=0; i < msecs && (frame_hdr->cmd_status == 0xff); i++ ) {
    + rmb();
    + msleep(1);
    + }
    +
    + if (frame_hdr->cmd_status == 0xff)
    + return -ETIME;
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds
    + * @instance: Adapter soft state
    + * @cmd: Command to be issued
    + *
    + * This function waits on an event for the command to be returned from ISR.
    + * Used to issue ioctl commands.
    + */
    +static int
    +megasas_issue_blocked_cmd( struct megasas_instance* instance,
    + struct megasas_cmd* cmd )
    +{
    + u32 msg_frame;
    +
    + cmd->cmd_status = ENODATA;
    + msg_frame = (cmd->frame_phys_addr >> 3);
    +
    + WR_IN_QPORT( msg_frame, instance->reg_set );
    +
    + wait_event( instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA));
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd
    + * @instance: Adapter soft state
    + * @cmd_to_abort: Previously issued cmd to be aborted
    + *
    + * MFI firmware can abort previously issued AEN comamnd (automatic event
    + * notification). The megasas_issue_blocked_abort_cmd() issues such abort
    + * cmd and blocks till it is completed.
    + */
    +static int
    +megasas_issue_blocked_abort_cmd( struct megasas_instance* instance,
    + struct megasas_cmd* cmd_to_abort )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_abort_frame* abort_fr;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd)
    + return -1;
    +
    + abort_fr = &cmd->frame->abort;
    +
    + /*
    + * Prepare and issue the abort frame
    + */
    + abort_fr->cmd = MFI_CMD_ABORT;
    + abort_fr->cmd_status = 0xFF;
    + abort_fr->flags = 0;
    + abort_fr->abort_context = cmd_to_abort->index;
    + abort_fr->abort_mfi_phys_addr_lo =
    cmd_to_abort->frame_phys_addr;
    + abort_fr->abort_mfi_phys_addr_hi = 0;
    +
    + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
    +
    + /*
    + * Wait for this cmd to complete
    + */
    + cmd->sync_cmd = 1;
    + wait_event( instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF));
    +
    + megasas_return_cmd( instance, cmd );
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_make_sgl32 - Prepares 32-bit SGL
    + * @instance: Adapter soft state
    + * @scp: SCSI command from the mid-layer
    + * @mfi_sgl: SGL to be filled in
    + *
    + * If successful, this function returns the number of SG elements.
    Otherwise,
    + * it returnes -1.
    + */
    +static inline int
    +megasas_make_sgl32( struct megasas_instance* instance, struct scsi_cmnd*
    scp,
    + union megasas_sgl* mfi_sgl )
    +{
    + int i;
    + int sge_count;
    + struct scatterlist* os_sgl;
    + struct page* page;
    + unsigned long offset;
    +
    + /*
    + * Return 0 if there is no data transfer
    + */
    + if (!scp->request_buffer || !scp->request_bufflen)
    + return 0;
    +
    + if (!scp->use_sg) {
    + page = virt_to_page( scp->request_buffer );
    + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
    +
    + mfi_sgl->sge32[0].phys_addr =
    pci_map_page(instance->pdev,
    + page, offset,
    +
    scp->request_bufflen,
    +
    scp->sc_data_direction);
    + mfi_sgl->sge32[0].length = scp->request_bufflen;
    +
    + return 1;
    + }
    +
    + os_sgl = (struct scatterlist*) scp->request_buffer;
    + sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
    + scp->sc_data_direction );
    +
    + for( i = 0; i < sge_count; i++, os_sgl++ ) {
    + mfi_sgl->sge32[i].length = sg_dma_len( os_sgl );
    + mfi_sgl->sge32[i].phys_addr = sg_dma_address( os_sgl );
    + }
    +
    + return sge_count;
    +}
    +
    +/**
    + * megasas_make_sgl64 - Prepares 64-bit SGL
    + * @instance: Adapter soft state
    + * @scp: SCSI command from the mid-layer
    + * @mfi_sgl: SGL to be filled in
    + *
    + * If successful, this function returns the number of SG elements.
    Otherwise,
    + * it returnes -1.
    + */
    +static inline int
    +megasas_make_sgl64( struct megasas_instance* instance, struct scsi_cmnd*
    scp,
    + union megasas_sgl* mfi_sgl )
    +{
    + int i;
    + int sge_count;
    + struct scatterlist* os_sgl;
    + struct page* page;
    + unsigned long offset;
    +
    + /*
    + * Return 0 if there is no data transfer
    + */
    + if (!scp->request_buffer || !scp->request_bufflen)
    + return 0;
    +
    + if (!scp->use_sg) {
    + page = virt_to_page( scp->request_buffer );
    + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
    +
    + mfi_sgl->sge64[0].phys_addr =
    pci_map_page(instance->pdev,
    + page, offset,
    +
    scp->request_bufflen,
    +
    scp->sc_data_direction);
    +
    + mfi_sgl->sge64[0].length = scp->request_bufflen;
    +
    + return 1;
    + }
    +
    + os_sgl = (struct scatterlist*) scp->request_buffer;
    + sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
    + scp->sc_data_direction );
    +
    + for( i = 0; i < sge_count; i++, os_sgl++ ) {
    + mfi_sgl->sge64[i].length = sg_dma_len( os_sgl );
    + mfi_sgl->sge64[i].phys_addr = sg_dma_address( os_sgl );
    + }
    +
    + return sge_count;
    +}
    +
    +/**
    + * megasas_build_dcdb - Prepares a direct cdb (DCDB) command
    + * @instance: Adapter soft state
    + * @scp: SCSI command
    + * @cmd: Command to be prepared in
    + *
    + * This function prepares CDB commands. These are typcially pass-through
    + * commands to the devices.
    + */
    +static inline int
    +megasas_build_dcdb( struct megasas_instance* instance, struct scsi_cmnd*
    scp,
    + struct megasas_cmd* cmd )
    +{
    + u32 sge_sz;
    + int sge_bytes;
    + u32 is_logical;
    + u32 device_id;
    + u16 flags = 0;
    + struct megasas_pthru_frame* pthru;
    +
    + is_logical = MEGASAS_IS_LOGICAL(scp);
    + device_id = MEGASAS_DEV_INDEX(instance, scp);
    + pthru = (struct megasas_pthru_frame*) cmd->frame;
    +
    + if (scp->sc_data_direction == PCI_DMA_TODEVICE )
    + flags = MFI_FRAME_DIR_WRITE;
    + else if( scp->sc_data_direction == PCI_DMA_FROMDEVICE )
    + flags = MFI_FRAME_DIR_READ;
    + else if( scp->sc_data_direction == PCI_DMA_NONE )
    + flags = MFI_FRAME_DIR_NONE;
    +
    + /*
    + * Prepare the DCDB frame
    + */
    + pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO :
    + MFI_CMD_PD_SCSI_IO;
    + pthru->cmd_status = 0x0;
    + pthru->scsi_status = 0x0;
    + pthru->target_id = device_id;
    + pthru->lun = SCP2LUN(scp);
    + pthru->cdb_len = scp->cmd_len;
    + pthru->timeout = 0;
    + pthru->flags = flags;
    + pthru->data_xfer_len = scp->request_bufflen;
    +
    + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
    +
    + /*
    + * Construct SGL
    + */
    + sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
    + sizeof(struct megasas_sge32);
    +
    + if (IS_DMA64) {
    + pthru->flags |= MFI_FRAME_SGL64;
    + pthru->sge_count = megasas_make_sgl64( instance, scp,
    + &pthru->sgl
    );
    + }
    + else
    + pthru->sge_count = megasas_make_sgl32( instance, scp,
    + &pthru->sgl
    );
    +
    + /*
    + * Sense info specific
    + */
    + pthru->sense_len = SCSI_SENSE_BUFFERSIZE;
    + pthru->sense_buf_phys_addr_hi = 0;
    + pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
    +
    + sge_bytes = sge_sz * pthru->sge_count;
    +
    + /*
    + * TBD: Comment the code below
    + */
    + cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
    + ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) +
    1;
    +
    + if (cmd->frame_count > 7)
    + cmd->frame_count = 8;
    +
    + return cmd->frame_count;
    +}
    +
    +/**
    + * megasas_build_ldio - Prepares IOs to logical devices
    + * @instance: Adapter soft state
    + * @scp: SCSI command
    + * @cmd: Command to to be prepared
    + *
    + * Frames (and accompanying SGLs) for regular SCSI IOs use this function.
    + */
    +static inline int
    +megasas_build_ldio( struct megasas_instance* instance, struct scsi_cmnd*
    scp,
    + struct megasas_cmd* cmd )
    +{
    + u32 sge_sz;
    + int sge_bytes;
    + u32 device_id;
    + u8 sc = scp->cmnd[0];
    + u16 flags = 0;
    +
    + struct megasas_io_frame* ldio;
    +
    + device_id = MEGASAS_DEV_INDEX(instance, scp);
    + ldio = (struct megasas_io_frame*) cmd->frame;
    +
    + if (scp->sc_data_direction == PCI_DMA_TODEVICE )
    + flags = MFI_FRAME_DIR_WRITE;
    + else if( scp->sc_data_direction == PCI_DMA_FROMDEVICE )
    + flags = MFI_FRAME_DIR_READ;
    +
    + /*
    + * Preare the Logical IO frame: 2nd bit is zero for all read cmds
    + */
    + ldio->cmd = (sc &
    0x02)?MFI_CMD_LD_WRITE:MFI_CMD_LD_READ;
    + ldio->cmd_status = 0x0;
    + ldio->scsi_status = 0x0;
    + ldio->target_id = device_id;
    + ldio->timeout = 0;
    + ldio->reserved_0 = 0;
    + ldio->pad_0 = 0;
    + ldio->flags = flags;
    + ldio->start_lba_hi = 0;
    + ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0;
    +
    + /*
    + * 6-byte READ(0x08) or WRITE(0x0A) cdb
    + */
    + if (scp->cmd_len == 6) {
    + ldio->lba_count = (u32)scp->cmnd[4];
    + ldio->start_lba_lo = ((u32)scp->cmnd[1] << 16)|
    + ((u32)scp->cmnd[2] << 8) |
    + (u32)scp->cmnd[3];
    +
    + ldio->start_lba_lo &= 0x1FFFFF;
    + }
    +
    + /*
    + * 10-byte READ(0x28) or WRITE(0x2A) cdb
    + */
    + else if (scp->cmd_len == 10) {
    + ldio->lba_count = (u32)scp->cmnd[8] |
    + ((u32)scp->cmnd[7] << 8);
    + ldio->start_lba_lo = ((u32)scp->cmnd[2] << 24)|
    + ((u32)scp->cmnd[3] << 16)|
    + ((u32)scp->cmnd[4] << 8)|
    + (u32)scp->cmnd[5];
    + }
    +
    + /*
    + * 12-byte READ(0xA8) or WRITE(0xAA) cdb
    + */
    + else if (scp->cmd_len == 12) {
    + ldio->lba_count = ((u32)scp->cmnd[6] << 24)|
    + ((u32)scp->cmnd[7] << 16)|
    + ((u32)scp->cmnd[8] << 8) |
    + (u32)scp->cmnd[9];
    +
    + ldio->start_lba_lo = ((u32)scp->cmnd[2] << 24)|
    + ((u32)scp->cmnd[3] << 16)|
    + ((u32)scp->cmnd[4] << 8) |
    + (u32)scp->cmnd[5];
    + }
    +
    + /*
    + * 16-byte READ(0x88) or WRITE(0x8A) cdb
    + */
    + else if (scp->cmd_len == 16) {
    + ldio->lba_count = ((u32)scp->cmnd[10] << 24)|
    + ((u32)scp->cmnd[11] << 16)|
    + ((u32)scp->cmnd[12] << 8) |
    + (u32)scp->cmnd[13];
    +
    + ldio->start_lba_lo = ((u32)scp->cmnd[6] << 24)|
    + ((u32)scp->cmnd[7] << 16)|
    + ((u32)scp->cmnd[8] << 8) |
    + (u32)scp->cmnd[9];
    +
    + ldio->start_lba_hi = ((u32)scp->cmnd[2] << 24)|
    + ((u32)scp->cmnd[3] << 16)|
    + ((u32)scp->cmnd[4] << 8) |
    + (u32)scp->cmnd[5];
    +
    + }
    +
    + /*
    + * Construct SGL
    + */
    + sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
    + sizeof(struct megasas_sge32);
    +
    + if (IS_DMA64) {
    + ldio->flags |= MFI_FRAME_SGL64;
    + ldio->sge_count = megasas_make_sgl64( instance, scp,
    + &ldio->sgl
    );
    + }
    + else
    + ldio->sge_count = megasas_make_sgl32( instance, scp,
    + &ldio->sgl
    );
    +
    + /*
    + * Sense info specific
    + */
    + ldio->sense_len = SCSI_SENSE_BUFFERSIZE;
    + ldio->sense_buf_phys_addr_hi = 0;
    + ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
    +
    + sge_bytes = sge_sz * ldio->sge_count;
    +
    + cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
    + ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) +
    1;
    +
    + if (cmd->frame_count > 7)
    + cmd->frame_count = 8;
    +
    + return cmd->frame_count;
    +}
    +
    +/**
    + * megasas_build_cmd - Prepares a command packet
    + * @instance: Adapter soft state
    + * @scp: SCSI command
    + * @frame_count: [OUT] Number of frames used to prepare this command
    + */
    +static inline struct megasas_cmd*
    +megasas_build_cmd( struct megasas_instance* instance, struct scsi_cmnd*
    scp,
    + int* frame_count )
    +{
    + u32 logical_cmd;
    + struct megasas_cmd* cmd;
    +
    + /*
    + * Find out if this is logical or physical drive command.
    + */
    + logical_cmd = MEGASAS_IS_LOGICAL(scp);
    +
    + /*
    + * Logical drive command
    + */
    + if (logical_cmd) {
    +
    + if (SCP2TARGET(scp) >= MEGASAS_MAX_LD) {
    + scp->result = DID_BAD_TARGET << 16;
    + return NULL;
    + }
    +
    + switch(scp->cmnd[0]) {
    +
    + case READ_10:
    + case WRITE_10:
    + case READ_12:
    + case WRITE_12:
    + case READ_6:
    + case WRITE_6:
    + case READ_16:
    + case WRITE_16:
    + /*
    + * Fail for LUN > 0
    + */
    + if (SCP2LUN(scp)) {
    + scp->result = DID_BAD_TARGET << 16;
    + return NULL;
    + }
    +
    + cmd = megasas_get_cmd(instance);
    +
    + if (!cmd) {
    + scp->result = DID_IMM_RETRY << 16;
    + return NULL;
    + }
    +
    + *frame_count = megasas_build_ldio(instance, scp,
    cmd);
    +
    + if (! (*frame_count) ) {
    + megasas_return_cmd( instance, cmd );
    + return NULL;
    + }
    +
    + return cmd;
    +
    + default:
    + /*
    + * Fail for LUN > 0
    + */
    + if (SCP2LUN(scp)) {
    + scp->result = DID_BAD_TARGET << 16;
    + return NULL;
    + }
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + scp->result = DID_IMM_RETRY << 16;
    + return NULL;
    + }
    +
    + *frame_count = megasas_build_dcdb(instance, scp,
    cmd);
    +
    + if (! (*frame_count) ) {
    + megasas_return_cmd( instance, cmd );
    + return NULL;
    + }
    +
    + return cmd;
    + }
    + }
    + else {
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + scp->result = DID_IMM_RETRY << 16;
    + return NULL;
    + }
    +
    + *frame_count = megasas_build_dcdb(instance, scp, cmd);
    +
    + if (! (*frame_count) ) {
    + megasas_return_cmd( instance, cmd );
    + return NULL;
    + }
    +
    + return cmd;
    + }
    +
    + return NULL;
    +}
    +
    +/**
    + * megasas_queue_command - Queue entry point
    + * @scmd: SCSI command to be queued
    + * @done: Callback entry point
    + */
    +static int
    +megasas_queue_command( struct scsi_cmnd* scmd, void (*done)(struct
    scsi_cmnd*) )
    +{
    + u32 frame_count;
    + struct megasas_cmd* cmd;
    + struct megasas_instance* instance;
    + u32 msg_frame;
    +
    + instance = SCP2ADAPTER(scmd);
    + scmd->scsi_done = done;
    + scmd->result = 0;
    +
    + cmd = megasas_build_cmd( instance, scmd, &frame_count );
    +
    + if (!cmd) {
    + done(scmd);
    + return 0;
    + }
    +
    + cmd->scmd = scmd;
    +
    + /*
    + * Issue the command to the FW
    + */
    + msg_frame = (cmd->frame_phys_addr >> 3) | (cmd->frame_count - 1);
    +
    + instance->fw_outstanding++;
    + WR_IN_QPORT( msg_frame, instance->reg_set );
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_abort_handler - Abort entry point
    + * @scmd: SCSI command to be aborted
    + */
    +static int
    +megasas_abort_handler( struct scsi_cmnd* scmd )
    +{
    + printk( KERN_NOTICE "megasas: ABORT -%ld cmd=%x <c=%d t=%d l=%d>\n",
    + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
    + SCP2TARGET(scmd), SCP2LUN(scmd));
    +
    + return FAILED;
    +}
    +
    +/**
    + * megasas_wait_for_outstanding - Wait for all outstanding cmds
    + * @instance: Adapter soft state
    + *
    + * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to
    + * complete all its outstanding commands. Returns error if one or more IOs
    + * are pending after this time period. It also marks the controller dead.
    + */
    +static int
    +megasas_wait_for_outstanding( struct megasas_instance* instance )
    +{
    + int i;
    + u32 wait_time = MEGASAS_RESET_WAIT_TIME;
    +
    + for( i = 0; i < wait_time; i++ ) {
    +
    + if (!instance->fw_outstanding)
    + break;
    +
    + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
    + printk( KERN_NOTICE "megasas: [%2d]waiting for %d \
    + commands to complete\n", i, instance->fw_outstanding
    );
    + }
    +
    + msleep(1000);
    + }
    +
    +
    + if (instance->fw_outstanding) {
    + instance->hw_crit_error = 1;
    + return FAILED;
    + }
    +
    + return SUCCESS;
    +}
    +
    +/**
    + * megasas_generic_reset - Generic reset routine
    + * @scmd: Mid-layer SCSI command
    + *
    + * This routine implements a generic reset handler for device, bus and host
    + * reset requests. Device, bus and host specific reset handlers can use
    this
    + * function after they do their specific tasks.
    + */
    +static int
    +megasas_generic_reset( struct scsi_cmnd* scmd )
    +{
    + int ret_val;
    + struct megasas_instance* instance = SCP2ADAPTER(scmd);
    +
    + spin_unlock( instance->host_lock );
    +
    + printk( KERN_NOTICE "megasas: RESET -%ld cmd=%x <c=%d t=%d l=%d>\n",
    + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
    + SCP2TARGET(scmd), SCP2LUN(scmd));
    +
    + if (instance->hw_crit_error) {
    + printk( KERN_ERR "megasas: cannot recover from previous
    reset \
    + failures\n"
    );
    + spin_lock( instance->host_lock );
    + return FAILED;
    + }
    +
    + ret_val = megasas_wait_for_outstanding( instance );
    +
    + if (ret_val == SUCCESS)
    + printk( KERN_NOTICE "megasas: reset successful \n" );
    + else
    + printk( KERN_ERR "megasas: failed to do reset\n");
    +
    + spin_lock( instance->host_lock );
    +
    + return ret_val;
    +}
    +
    +/**
    + * megasas_reset_device - Device reset handler entry point
    + *
    + * Issues CLUSTER_RESET_LD (FW direct cmd) before calling generic reset fn.
    + */
    +static int
    +megasas_reset_device( struct scsi_cmnd* scmd )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    + struct megasas_instance* instance = SCP2ADAPTER(scmd);
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (cmd) {
    +
    + dcmd = &cmd->frame->dcmd;
    +
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 0;
    + dcmd->flags = MFI_FRAME_DIR_NONE;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = 0;
    + dcmd->opcode = MR_DCMD_CLUSTER_RESET_LD;
    + dcmd->mbox[0] = MEGASAS_DEV_INDEX(instance, scmd);
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + megasas_return_cmd( instance, cmd );
    + }
    +
    + return megasas_generic_reset( scmd );
    +}
    +
    +/**
    + * megasas_reset_bus_host - Bus & host reset handler entry point
    + *
    + * Issues CLUSTER_RESET_ALL (FW direct cmd) before calling generic reset
    fn.
    + */
    +static int
    +megasas_reset_bus_host( struct scsi_cmnd* scmd )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    + struct megasas_instance* instance = SCP2ADAPTER(scmd);
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (cmd) {
    +
    + dcmd = &cmd->frame->dcmd;
    +
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 0;
    + dcmd->flags = MFI_FRAME_DIR_NONE;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = 0;
    + dcmd->opcode = MR_DCMD_CLUSTER_RESET_ALL;
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + megasas_return_cmd( instance, cmd );
    + }
    +
    + return megasas_generic_reset( scmd );
    +}
    +
    +/**
    + * megasas_service_aen - Processes an event notification
    + * @instance: Adapter soft state
    + * @cmd: AEN command completed by the ISR
    + *
    + * For AEN, driver sends a command down to FW that is held by the FW till
    an
    + * event occurs. When an event of interest occurs, FW completes the command
    + * that it was previously holding.
    + *
    + * This routines sends SIGIO signal to processes that have registered with
    the
    + * driver for AEN.
    + */
    +static void
    +megasas_service_aen(struct megasas_instance* instance, struct megasas_cmd*
    cmd)
    +{
    + /*
    + * Don't signal app if it is just an aborted previously registered
    aen
    + */
    + if (!cmd->abort_aen)
    + kill_fasync( &megasas_async_queue, SIGIO, POLL_IN );
    + else
    + cmd->abort_aen = 0;
    +
    + instance->aen_cmd = NULL;
    + megasas_return_cmd( instance, cmd );
    +}
    +
    +/**
    + * megasas_sysfs_show_app_hndl - Exports adapter handle via sysfs
    + *
    + * User space applications _don't_ address the controllers using zero based
    + * indices. Instead driver exports a unique 16-bit handle for each
    controller
    + * (refer to comments under MR_LINUX_GET_ADAPTER_MAP ioctl).
    + *
    + * Applications use this handle to delete or add logical drives (via FW
    + * commands). To make these logical driver appear or disappear to SCSI
    layer,
    + * applications have to do a delete or scan on a SCSI host in sysfs tree.
    + * The applications have to have a way to find out the SCSI host number
    + * corresponding to the unique 16-bit handle.
    + *
    + * This function exports the unique 16-bit handle in sysfs under the SCSI
    + * host. Applications can traverse the list of hosts till they find a host
    + * that has the required handle.
    + */
    +static ssize_t
    +megasas_sysfs_show_app_hndl( struct class_device* cdev, char* buf )
    +{
    + int i;
    + u32 hndl = 0;
    + struct Scsi_Host* shost;
    + struct megasas_instance* instance;
    +
    + shost = class_to_shost( cdev );
    + instance = (struct megasas_instance*)SCSIHOST2ADAP( shost );
    +
    + for (i = 0; i < megasas_mgmt_info.max_index; i++ ) {
    +
    + if (instance == megasas_mgmt_info.instance[i])
    + hndl = ((i + 1) << 4) | 0xF;
    + }
    +
    + return snprintf( buf, 8, "%u\n", hndl );
    +}
    +
    +/*
    + * Sysfs attribute definition: Exports driver specific controller handle
    + */
    +CLASS_DEVICE_ATTR(megaraid_sas_app_hndl, S_IRUSR,
    megasas_sysfs_show_app_hndl,
    +
    NULL);
    +/*
    + * Host template initializer for sysfs attributes
    + */
    +static struct class_device_attribute* megasas_shost_attrs[] = {
    + &class_device_attr_megaraid_sas_app_hndl,
    + NULL,
    +};
    +
    +/*
    + * Scsi host template for megaraid_sas driver
    + */
    +static struct scsi_host_template megasas_template_g = {
    +
    + .module = THIS_MODULE,
    + .name = "LSI Logic SAS based MegaRAID
    driver",
    + .queuecommand = megasas_queue_command,
    + .eh_abort_handler = megasas_abort_handler,
    + .eh_device_reset_handler = megasas_reset_device,
    + .eh_bus_reset_handler = megasas_reset_bus_host,
    + .eh_host_reset_handler = megasas_reset_bus_host,
    + .use_clustering = ENABLE_CLUSTERING,
    + .shost_attrs = megasas_shost_attrs,
    +};
    +
    +/**
    + * megasas_complete_int_cmd - Completes an internal command
    + * @instance: Adapter soft state
    + * @cmd: Command to be completed
    + *
    + * The megasas_issue_blocked_cmd() function waits for a command to complete
    + * after it issues a command. This function wakes up that waiting routine
    by
    + * calling wake_up() on the wait queue.
    + */
    +static void
    +megasas_complete_int_cmd(struct megasas_instance* instance,
    + struct megasas_cmd* cmd)
    +{
    + cmd->cmd_status = cmd->frame->io.cmd_status;
    +
    + if (cmd->cmd_status == ENODATA) {
    + cmd->cmd_status = 0;
    + }
    + wake_up( &instance->int_cmd_wait_q );
    +}
    +
    +/**
    + * megasas_complete_abort - Completes aborting a command
    + * @instance: Adapter soft state
    + * @cmd: Cmd that was issued to abort another cmd
    + *
    + * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q
    + * after it issues an abort on a previously issued command. This function
    + * wakes up all functions waiting on the same wait queue.
    + */
    +static void
    +megasas_complete_abort( struct megasas_instance* instance,
    + struct megasas_cmd* cmd )
    +{
    + if (cmd->sync_cmd) {
    + cmd->sync_cmd = 0;
    + wake_up( &instance->abort_cmd_wait_q );
    + }
    +
    + return;
    +}
    +
    +/**
    + * megasas_sync_buffers - Synchronozies CPU caches after IO transfer
    + * @instance: Adapter soft state
    + * @cmd: Completed command
    + */
    +static inline void
    +megasas_sync_buffers(struct megasas_instance* instance, struct megasas_cmd*
    cmd)
    +{
    + dma_addr_t buf_h;
    + u8 opcode;
    +
    + if (cmd->scmd->use_sg) {
    + pci_unmap_sg( instance->pdev, cmd->scmd->request_buffer,
    + cmd->scmd->use_sg, cmd->scmd->sc_data_direction );
    + return;
    + }
    +
    + if (!cmd->scmd->request_bufflen)
    + return;
    +
    + opcode = cmd->frame->hdr.cmd;
    +
    + if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) {
    + if (IS_DMA64)
    + buf_h = cmd->frame->io.sgl.sge64[0].phys_addr;
    + else
    + buf_h = cmd->frame->io.sgl.sge32[0].phys_addr;
    + }
    + else {
    + if (IS_DMA64)
    + buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr;
    + else
    + buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr;
    + }
    +
    + pci_unmap_page( instance->pdev, buf_h, cmd->scmd->request_bufflen,
    + cmd->scmd->sc_data_direction
    );
    + return;
    +}
    +
    +/**
    + * megasas_complete_cmd - Completes a command
    + * @instance: Adapter soft state
    + * @cmd: Command to be completed
    + * @alt_status: If non-zero, use this value as
    status to
    + * SCSI mid-layer instead of the value returned
    + * by the FW. This should be used if caller
    wants
    + * an alternate status (as in the case of
    aborted
    + * commands)
    + */
    +static inline void
    +megasas_complete_cmd(struct megasas_instance* instance, struct megasas_cmd*
    cmd,
    + u8
    alt_status)
    +{
    + int exception = 0;
    + struct megasas_header* hdr = &cmd->frame->hdr;
    +
    + switch( hdr->cmd ) {
    +
    + case MFI_CMD_PD_SCSI_IO:
    + case MFI_CMD_LD_SCSI_IO:
    +
    + /*
    + * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been
    + * issued either through an IO path or an IOCTL path. If it
    + * was via IOCTL, we will send it to internal completion.
    + */
    + if (cmd->sync_cmd) {
    + cmd->sync_cmd = 0;
    + megasas_complete_int_cmd( instance, cmd );
    + break;
    + }
    +
    + /*
    + * Don't export physical disk devices to mid-layer.
    + */
    + if ( !MEGASAS_IS_LOGICAL(cmd->scmd) &&
    + (hdr->cmd_status == MFI_STAT_OK) &&
    + (cmd->scmd->cmnd[0] == INQUIRY) ) {
    +
    + if (((*(u8*) cmd->scmd->request_buffer) & 0x1F) ==
    + TYPE_DISK) {
    + cmd->scmd->result = DID_BAD_TARGET << 16;
    + exception = 1;
    + }
    + }
    +
    + case MFI_CMD_LD_READ:
    + case MFI_CMD_LD_WRITE:
    +
    + if (alt_status) {
    + cmd->scmd->result = alt_status << 16;
    + exception = 1;
    + }
    +
    +
    + if (exception) {
    +
    + spin_lock( instance->host_lock );
    + cmd->scmd->scsi_done( cmd->scmd );
    + spin_unlock( instance->host_lock );
    +
    + instance->fw_outstanding--;
    + megasas_return_cmd( instance, cmd );
    + megasas_sync_buffers( instance, cmd );
    + break;
    + }
    +
    +
    + switch (hdr->cmd_status) {
    +
    + case MFI_STAT_OK:
    + cmd->scmd->result = DID_OK << 16;
    + break;
    +
    + case MFI_STAT_SCSI_IO_FAILED:
    + cmd->scmd->result = (DID_ERROR << 16)
    |hdr->scsi_status;
    + break;
    +
    + case MFI_STAT_SCSI_DONE_WITH_ERROR:
    +
    + cmd->scmd->result = (DID_OK << 16) |
    hdr->scsi_status;
    +
    + if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) {
    + memset( cmd->scmd->sense_buffer, 0,
    +
    SCSI_SENSE_BUFFERSIZE );
    + memcpy( cmd->scmd->sense_buffer, cmd->sense,
    + hdr->sense_len );
    +
    + cmd->scmd->result |= DRIVER_SENSE << 24;
    + }
    +
    + break;
    +
    + case MFI_STAT_DEVICE_NOT_FOUND:
    + cmd->scmd->result = DID_BAD_TARGET << 16;
    + break;
    +
    + default:
    + printk( KERN_DEBUG "megasas: unhandled status
    %#x\n",
    + hdr->cmd_status);
    + cmd->scmd->result = DID_ERROR << 16;
    + }
    +
    + spin_lock( instance->host_lock );
    + cmd->scmd->scsi_done( cmd->scmd );
    + spin_unlock( instance->host_lock );
    +
    + instance->fw_outstanding--;
    +
    + megasas_return_cmd( instance, cmd );
    +
    + megasas_sync_buffers( instance, cmd );
    + break;
    +
    + case MFI_CMD_SMP:
    + case MFI_CMD_STP:
    + case MFI_CMD_DCMD:
    +
    + /*
    + * See if got an event notification
    + */
    + if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT)
    + megasas_service_aen( instance, cmd );
    + else
    + megasas_complete_int_cmd( instance, cmd );
    +
    + break;
    +
    + case MFI_CMD_ABORT:
    + /*
    + * Cmd issued to abort another cmd returned
    + */
    + megasas_complete_abort( instance, cmd );
    + break;
    +
    + default:
    + break;
    + }
    +}
    +
    +/**
    + * megasas_deplete_reply_queue - Processes all completed commands
    + * @instance: Adapter soft state
    + * @alt_status: Alternate status to be
    returned to
    + * SCSI mid-layer instead of the status
    + * returned by the FW
    + */
    +static inline int
    +megasas_deplete_reply_queue( struct megasas_instance* instance, u8
    alt_status )
    +{
    + u32 status;
    + u32 producer;
    + u32 consumer;
    + u32 context;
    +
    + struct megasas_register_set* reg_set;
    + struct megasas_cmd* cmd;
    +
    + reg_set = instance->reg_set;
    +
    + /*
    + * Check if it is our interrupt
    + */
    + status = RD_OB_INTR_STATUS(reg_set);
    +
    + if (! (status & MFI_OB_INTR_STATUS_MASK)) {
    + return IRQ_NONE;
    + }
    +
    + /*
    + * Clear the interrupt by writing back the same value
    + */
    + WR_OB_INTR_STATUS(status, reg_set);
    +
    + producer = instance->producer;
    + consumer = instance->consumer;
    +
    + while( consumer != producer ) {
    + context = instance->reply_queue[consumer];
    +
    + cmd = instance->cmd_list[context];
    +
    + megasas_complete_cmd( instance, cmd, alt_status );
    +
    + consumer++;
    + if (consumer == (instance->max_fw_cmds + 1)) {
    + consumer = 0;
    + }
    + }
    +
    + wmb();
    + instance->consumer = producer;
    +
    + return IRQ_HANDLED;
    +}
    +
    +/**
    + * megasas_isr - isr entry point
    + */
    +static irqreturn_t
    +megasas_isr(int irq, void *devp, struct pt_regs *regs)
    +{
    + return megasas_deplete_reply_queue( (struct megasas_instance*)devp,
    + DID_OK );
    +}
    +
    +/**
    + * megasas_transition_to_ready - Move the FW to READY state
    + * @reg_set: MFI register set
    + *
    + * During the initialization, FW passes can potentially be in any one of
    + * several possible states. If the FW in operational, waiting-for-handshake
    + * states, driver must take steps to bring it to ready state. Otherwise, it
    + * has to wait for the ready state.
    + */
    +static int
    +megasas_transition_to_ready( struct megasas_register_set* reg_set )
    +{
    + int i;
    + u8 max_wait;
    + u32 fw_state;
    + u32 cur_state;
    +
    + fw_state = RD_OB_MSG_0(reg_set) & MFI_STATE_MASK;
    +
    + while( fw_state != MFI_STATE_READY ) {
    +
    + printk( KERN_INFO "megasas: Waitinf for FW to come to ready
    \
    + state\n" );
    +
    + switch( fw_state ) {
    +
    + case MFI_STATE_FAULT:
    +
    + printk(KERN_DEBUG "megasas: FW in FAULT state!!\n");
    + return -ENODEV;
    +
    + case MFI_STATE_WAIT_HANDSHAKE:
    + /*
    + * Set the CLR bit in inbound doorbell
    + */
    + WR_IN_DOORBELL( MFI_INIT_CLEAR_HANDSHAKE, reg_set );
    +
    + max_wait = 2;
    + cur_state = MFI_STATE_WAIT_HANDSHAKE;
    + break;
    +
    + case MFI_STATE_OPERATIONAL:
    + /*
    + * Bring it to READY state; assuming max wait 2 secs
    + */
    + MFI_DISABLE_INTR(reg_set);
    + WR_IN_DOORBELL( MFI_INIT_READY, reg_set );
    +
    + max_wait = 10;
    + cur_state = MFI_STATE_OPERATIONAL;
    + break;
    +
    + case MFI_STATE_UNDEFINED:
    + /*
    + * This state should not last for more than 2
    seconds
    + */
    + max_wait = 2;
    + cur_state = MFI_STATE_UNDEFINED;
    + break;
    +
    + case MFI_STATE_BB_INIT:
    + max_wait = 2;
    + cur_state = MFI_STATE_BB_INIT;
    + break;
    +
    + case MFI_STATE_FW_INIT:
    + max_wait = 20;
    + cur_state = MFI_STATE_FW_INIT;
    + break;
    +
    + case MFI_STATE_FW_INIT_2:
    + max_wait = 20;
    + cur_state = MFI_STATE_FW_INIT_2;
    + break;
    +
    + case MFI_STATE_DEVICE_SCAN:
    + max_wait = 20;
    + cur_state = MFI_STATE_DEVICE_SCAN;
    + break;
    +
    + case MFI_STATE_FLUSH_CACHE:
    + max_wait = 20;
    + cur_state = MFI_STATE_FLUSH_CACHE;
    + break;
    +
    + default:
    + printk(KERN_DEBUG "megasas: Unknown state 0x%x\n",
    + fw_state);
    + return -ENODEV;
    + }
    +
    + /*
    + * The cur_state should not last for more than max_wait secs
    + */
    + for( i = 0; i < (max_wait * 1000); i++ ) {
    + fw_state = RD_OB_MSG_0(reg_set) & MFI_STATE_MASK;
    +
    + if (fw_state == cur_state) {
    + msleep(1);
    + }
    + else
    + break;
    + }
    +
    + /*
    + * Return error if fw_state hasn't changed after max_wait
    + */
    + if (fw_state == cur_state) {
    + printk(KERN_DEBUG "FW state [%d] hasn't changed \
    + in %d secs\n", fw_state, max_wait);
    + return -ENODEV;
    + }
    + };
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool
    + * @instance: Adapter soft state
    + */
    +static void
    +megasas_teardown_frame_pool( struct megasas_instance* instance )
    +{
    + int i;
    + u32 max_cmd = instance->max_fw_cmds;
    + struct megasas_cmd* cmd;
    +
    + if (!instance->frame_dma_pool)
    + return;
    +
    + /*
    + * Return all frames to pool
    + */
    + for( i = 0; i < max_cmd; i++ ) {
    +
    + cmd = instance->cmd_list[i];
    +
    + if( cmd->frame)
    + pci_pool_free( instance->frame_dma_pool, cmd->frame,
    + cmd->frame_phys_addr );
    +
    + if (cmd->sense)
    + pci_pool_free( instance->sense_dma_pool, cmd->frame,
    + cmd->sense_phys_addr );
    + }
    +
    + /*
    + * Now destroy the pool itself
    + */
    + pci_pool_destroy( instance->frame_dma_pool );
    + pci_pool_destroy( instance->sense_dma_pool );
    +
    + instance->frame_dma_pool = NULL;
    + instance->sense_dma_pool = NULL;
    +}
    +
    +/**
    + * megasas_create_frame_pool - Creates DMA pool for cmd frames
    + * @instance: Adapter soft state
    + *
    + * Each command packet has an embedded DMA memory buffer that is used for
    + * filling MFI frame and the SG list that immediately follows the frame.
    This
    + * function creates those DMA memory buffers for each command packet by
    using
    + * PCI pool facility.
    + */
    +static int
    +megasas_create_frame_pool( struct megasas_instance* instance )
    +{
    + int i;
    + u32 max_cmd;
    + u32 sge_sz;
    + u32 sgl_sz;
    + u32 total_sz ;
    + u32 frame_count;
    + struct megasas_cmd* cmd;
    +
    + max_cmd = instance->max_fw_cmds;
    +
    + /*
    + * Size of our frame is 64 bytes for MFI frame, followed by max SG
    + * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer
    + */
    + sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
    + sizeof(struct megasas_sge32);
    +
    + /*
    + * Calculated the number of 64byte frames required for SGL
    + */
    + sgl_sz = sge_sz * instance->max_num_sge;
    + frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE -
    1)/MEGAMFI_FRAME_SIZE;
    +
    + /*
    + * We need one extra frame for the MFI command
    + */
    + frame_count++;
    +
    + total_sz = MEGAMFI_FRAME_SIZE * frame_count;
    + /*
    + * Use DMA pool facility provided by PCI layer
    + */
    + instance->frame_dma_pool = pci_pool_create( "megasas frame pool",
    + instance->pdev, total_sz, 64, 0 );
    +
    + if (!instance->frame_dma_pool) {
    + printk( KERN_DEBUG "megasas: failed to setup frame pool\n"
    );
    + return -ENOMEM;
    + }
    +
    + instance->sense_dma_pool = pci_pool_create( "megasas sense pool",
    + instance->pdev, 128, 4, 0 );
    +
    + if (!instance->sense_dma_pool) {
    + printk( KERN_DEBUG "megasas: failed to setup sense pool\n"
    );
    +
    + pci_pool_destroy( instance->frame_dma_pool );
    + instance->frame_dma_pool = NULL;
    +
    + return -ENOMEM;
    + }
    +
    + /*
    + * Allocate and attach a frame to each of the commands in cmd_list.
    + * By making cmd->index as the context instead of the &cmd, we can
    + * always use 32bit context regardless of the architecture
    + */
    + for( i = 0; i < max_cmd; i++ ) {
    +
    + cmd = instance->cmd_list[i];
    +
    + cmd->frame = pci_pool_alloc( instance->frame_dma_pool,
    + GFP_KERNEL, &cmd->frame_phys_addr );
    +
    + cmd->sense = pci_pool_alloc( instance->sense_dma_pool,
    + GFP_KERNEL, &cmd->sense_phys_addr );
    +
    + /*
    + * megasas_teardown_frame_pool() takes care of freeing
    + * whatever has been allocated
    + */
    + if (!cmd->frame || !cmd->sense) {
    + printk(KERN_DEBUG "megasas: pci_pool_alloc failed
    \n");
    + megasas_teardown_frame_pool( instance );
    + return -ENOMEM;
    + }
    +
    + cmd->frame->io.context = cmd->index;
    + }
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_free_cmds - Free all the cmds in the free cmd pool
    + * @instance: Adapter soft state
    + */
    +static void
    +megasas_free_cmds( struct megasas_instance* instance )
    +{
    + int i;
    + /* First free the MFI frame pool */
    + megasas_teardown_frame_pool( instance );
    +
    + /* Free all the commands in the cmd_list */
    + for (i = 0; i < instance->max_fw_cmds; i++ )
    + kfree( instance->cmd_list[i] );
    +
    + /* Free the cmd_list buffer itself */
    + kfree( instance->cmd_list );
    + instance->cmd_list = NULL;
    +
    + INIT_LIST_HEAD( &instance->cmd_pool );
    +}
    +
    +/**
    + * megasas_alloc_cmds - Allocates the command packets
    + * @instance: Adapter soft state
    + *
    + * Each command that is issued to the FW, whether IO commands from the OS
    or
    + * internal commands like IOCTLs, are wrapped in local data structure
    called
    + * megasas_cmd. The frame embedded in this megasas_cmd is actually issued
    to
    + * the FW.
    + *
    + * Each frame has a 32-bit field called context (tag). This context is used
    + * to get back the megasas_cmd from the frame when a frame gets completed
    in
    + * the ISR. Typically the address of the megasas_cmd itself would be used
    as
    + * the context. But we wanted to keep the differences between 32 and 64 bit
    + * systems to the mininum. We always use 32 bit integers for the context.
    In
    + * this driver, the 32 bit values are the indices into an array cmd_list.
    + * This array is used only to look up the megasas_cmd given the context.
    The
    + * free commands themselves are maintained in a linked list called
    cmd_pool.
    + */
    +static int
    +megasas_alloc_cmds( struct megasas_instance* instance )
    +{
    + int i;
    + u32 max_cmd;
    + struct megasas_cmd* cmd;
    +
    + max_cmd = instance->max_fw_cmds;
    +
    + /*
    + * instance->cmd_list is an array of struct megasas_cmd pointers.
    + * Allocate the dynamic array first and then allocate individual
    + * commands.
    + */
    + instance->cmd_list = kmalloc( sizeof(struct megasas_cmd*) * max_cmd,
    + GFP_KERNEL
    );
    +
    + if (!instance->cmd_list) {
    + printk( KERN_DEBUG "megasas: out of memory\n" );
    + return -ENOMEM;
    + }
    +
    + memset( instance->cmd_list, 0, sizeof(struct megasas_cmd*) * max_cmd
    );
    +
    + for( i = 0; i < max_cmd; i++ ) {
    + instance->cmd_list[i] = kmalloc( sizeof(struct megasas_cmd),
    + GFP_KERNEL
    );
    +
    + if (!instance->cmd_list[i]) {
    + int j;
    +
    + for (j = 0; j < i; j++)
    + kfree( instance->cmd_list[j] );
    +
    + kfree( instance->cmd_list );
    + instance->cmd_list = NULL;
    +
    + return -ENOMEM;
    + }
    + }
    +
    + /*
    + * Add all the commands to command pool (instance->cmd_pool)
    + */
    + for( i = 0; i < max_cmd; i++ ) {
    + cmd = instance->cmd_list[i];
    + memset( cmd, 0, sizeof(struct megasas_cmd));
    + cmd->index = i;
    +
    + list_add_tail( &cmd->list, &instance->cmd_pool );
    + }
    +
    + /*
    + * Create a frame pool and assign one frame to each cmd
    + */
    + if (megasas_create_frame_pool( instance )) {
    + printk(KERN_DEBUG "megasas: Error creating frame DMA
    pool\n");
    + megasas_free_cmds( instance );
    + }
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_get_controller_info - Returns FW's controller structure
    + * @instance: Adapter soft state
    + * @ctrl_info: Controller information structure
    + *
    + * Issues an internal command (DCMD) to get the FW's controller structure.
    + * This information is mainly used to find out the maximum IO transfer per
    + * command supported by the FW.
    + */
    +static int
    +megasas_get_ctrl_info( struct megasas_instance* instance,
    + struct megasas_ctrl_info* ctrl_info )
    +{
    + int ret = 0;
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    + struct megasas_ctrl_info* ci;
    + dma_addr_t ci_h;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + printk( KERN_DEBUG "megasas: Failed to get a free cmd\n");
    + return -ENOMEM;
    + }
    +
    + dcmd = &cmd->frame->dcmd;
    +
    + ci = pci_alloc_consistent( instance->pdev,
    + sizeof(struct megasas_ctrl_info), &ci_h );
    +
    + if (!ci) {
    + printk( KERN_DEBUG "Failed to alloc mem for ctrl info\n" );
    + megasas_return_cmd( instance, cmd );
    + return -ENOMEM;
    + }
    +
    + memset( ci, 0, sizeof(*ci));
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0xFF;
    + dcmd->sge_count = 1;
    + dcmd->flags = MFI_FRAME_DIR_READ;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info);
    + dcmd->opcode = MR_DCMD_CTRL_GET_INFO;
    + dcmd->sgl.sge32[0].phys_addr = ci_h;
    + dcmd->sgl.sge32[0].length = sizeof(struct megasas_ctrl_info);
    +
    + if (!megasas_issue_polled( instance, cmd )) {
    + ret = 0;
    + memcpy( ctrl_info, ci, sizeof(struct megasas_ctrl_info));
    + }
    + else {
    + ret = -1;
    + }
    +
    + pci_free_consistent( instance->pdev, sizeof(struct
    megasas_ctrl_info),
    + ci, ci_h );
    +
    + megasas_return_cmd( instance, cmd );
    + return ret;
    +}
    +
    +/**
    + * megasas_init_mfi - Initializes the FW
    + * @instance: Adapter soft state
    + *
    + * This is the main function for initializing MFI firmware.
    + */
    +static int
    +megasas_init_mfi( struct megasas_instance* instance )
    +{
    + u32 context_sz;
    + u32 reply_q_sz;
    + u32 max_sectors_1;
    + u32 max_sectors_2;
    + struct megasas_register_set* reg_set;
    +
    + struct megasas_cmd* cmd;
    + struct megasas_ctrl_info* ctrl_info;
    +
    + struct megasas_init_frame* init_frame;
    + struct megasas_init_queue_info* initq_info;
    + dma_addr_t init_frame_h;
    + dma_addr_t initq_info_h;
    + dma_addr_t instance_h;
    +
    + /*
    + * Map the message registers
    + */
    + instance->base_addr = pci_resource_start(instance->pdev, 0);
    +
    + if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) {
    + printk( KERN_DEBUG "megasas: IO memory region busy!\n");
    + return -EBUSY;
    + }
    +
    + instance->reg_set = (struct megasas_register_set*) ioremap_nocache(
    + instance->base_addr, 8192);
    +
    + if (!instance->reg_set) {
    + printk( KERN_DEBUG "megasas: Failed to map IO mem\n" );
    + goto fail_ioremap;
    + }
    +
    + reg_set = instance->reg_set;
    +
    + /*
    + * We expect the FW state to be READY
    + */
    + if (megasas_transition_to_ready(instance->reg_set))
    + goto fail_ready_state;
    +
    + /*
    + * Get various operational parameters from status register
    + */
    + instance->max_num_sge = MFI_MAX_SUPP_SGES(reg_set);
    + instance->max_fw_cmds = MFI_MAX_SUPP_CMDS(reg_set);
    +
    + /*
    + * Create a pool of commands
    + */
    + if (megasas_alloc_cmds(instance))
    + goto fail_alloc_cmds;
    +
    + /*
    + * Allocate memory for reply queue. Length of reply queue should
    + * be _one_ more than the maximum commands handled by the firmware.
    + *
    + * Note: When FW completes commands, it places corresponding contex
    + * values in this circular reply queue. This circular queue is a
    fairly
    + * typical producer-consumer queue. FW is the producer (of completed
    + * commands) and the driver is the consumer.
    + */
    + context_sz = sizeof(u32);
    + reply_q_sz = context_sz * (instance->max_fw_cmds + 1);
    +
    + instance->reply_queue = pci_alloc_consistent( instance->pdev,
    + reply_q_sz, &instance->reply_queue_phys_addr
    );
    +
    + if (!instance->reply_queue) {
    + printk( KERN_DEBUG "megasas: Out of DMA mem for reply
    queue\n");
    + goto fail_reply_queue;
    + }
    +
    + /*
    + * Prepare a init frame. Note the init frame points to queue info
    + * structure. Each frame has SGL allocated after first 64 bytes. For
    + * this frame - since we don't need any SGL - we use SGL's space as
    + * queue info structure
    + *
    + * We will not get a NULL command below. We just created the pool.
    + */
    + cmd = megasas_get_cmd( instance );
    +
    + init_frame = (struct megasas_init_frame*) cmd->frame;
    + initq_info = (struct megasas_init_queue_info*)
    + ((unsigned long)init_frame + 64);
    +
    + instance_h = instance->phys_addr;
    + init_frame_h = cmd->frame_phys_addr;
    + initq_info_h = init_frame_h + 64;
    +
    + memset( init_frame, 0, MEGAMFI_FRAME_SIZE );
    + memset( initq_info, 0, sizeof(struct megasas_init_queue_info));
    +
    + initq_info->reply_queue_entries = instance->max_fw_cmds + 1;
    + initq_info->reply_queue_start_phys_addr_lo =
    +
    instance->reply_queue_phys_addr;
    +
    + initq_info->producer_index_phys_addr_lo = instance_h + offsetof(
    + struct
    megasas_instance,
    + producer);
    +
    + initq_info->consumer_index_phys_addr_lo = instance_h + offsetof(
    + struct
    megasas_instance,
    + consumer);
    +
    + init_frame->cmd = MFI_CMD_INIT;
    + init_frame->cmd_status = 0xFF;
    + init_frame->queue_info_new_phys_addr_lo = initq_info_h;
    +
    + init_frame->data_xfer_len = sizeof( struct megasas_init_queue_info);
    +
    + /*
    + * Issue the init frame in polled mode
    + */
    + if (megasas_issue_polled(instance, cmd )) {
    + printk( KERN_DEBUG "megasas: Failed to init firmware\n" );
    + goto fail_fw_init;
    + }
    +
    + megasas_return_cmd( instance, cmd );
    +
    + ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
    +
    + /*
    + * Compue the max allowed sectors per IO: The controller info has
    two
    + * limits on max sectors. Driver should use the minimum of these
    two.
    + *
    + * 1 << stripe_sz_ops.min = max sectors per strip
    + *
    + * Note that older firmwares ( < FW ver 30) didn't report
    information
    + * to calculate max_sectors_1. So the number ended up as zero
    always.
    + */
    + if (ctrl_info && !megasas_get_ctrl_info( instance, ctrl_info )) {
    +
    + max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *
    + ctrl_info->max_strips_per_io;
    + max_sectors_2 = ctrl_info->max_request_size;
    +
    + /*
    + * Reasonable size to support older firmwares
    + */
    + if (!max_sectors_1)
    + max_sectors_1 = 2048; /* 1 MB */
    +
    + instance->max_sectors_per_req = (max_sectors_1 <
    max_sectors_2)
    + ? max_sectors_1 : max_sectors_2;
    + }
    + else
    + instance->max_sectors_per_req = instance->max_num_sge *
    + PAGE_SIZE / 512;
    +
    + kfree( ctrl_info );
    +
    + return 0;
    +
    +fail_fw_init:
    + megasas_return_cmd( instance, cmd );
    +
    + pci_free_consistent( instance->pdev, reply_q_sz,
    + instance->reply_queue,
    + instance->reply_queue_phys_addr );
    +fail_reply_queue:
    + megasas_free_cmds( instance );
    +
    +fail_alloc_cmds:
    +fail_ready_state:
    + iounmap( instance->reg_set );
    +
    +fail_ioremap:
    + pci_release_regions( instance->pdev );
    +
    + return -EINVAL;
    +}
    +
    +/**
    + * megasas_release_mfi - Reverses the FW initialization
    + * @intance: Adapter soft state
    + */
    +static void
    +megasas_release_mfi( struct megasas_instance* instance )
    +{
    + u32 reply_q_sz = sizeof(u32) * instance->max_fw_cmds;
    +
    + pci_free_consistent( instance->pdev, reply_q_sz,
    + instance->reply_queue,
    + instance->reply_queue_phys_addr );
    +
    + megasas_free_cmds( instance );
    +
    + iounmap( instance->reg_set );
    +
    + pci_release_regions( instance->pdev );
    +}
    +
    +/**
    + * megasas_get_seq_num - Gets latest event sequence numbers
    + * @instance: Adapter soft state
    + * @eli: FW event log sequence numbers information
    + *
    + * FW maintains a log of all events in a non-volatile area. Upper layers
    would
    + * usually find out the latest sequence number of the events, the seq
    number at
    + * the boot etc. They would "read" all the events below the latest seq
    number
    + * by issuing a direct fw cmd (DCMD). For the future events (beyond latest
    seq
    + * number), they would subsribe to AEN (asynchronous event notification)
    and
    + * wait for the events to happen.
    + */
    +static int
    +megasas_get_seq_num( struct megasas_instance* instance,
    + struct megasas_evt_log_info* eli)
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    + struct megasas_evt_log_info* el_info;
    + dma_addr_t el_info_h;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + return -ENOMEM;
    + }
    +
    + dcmd = &cmd->frame->dcmd;
    + el_info = pci_alloc_consistent( instance->pdev,
    + sizeof(struct megasas_evt_log_info),
    + &el_info_h );
    +
    + if (!el_info) {
    + megasas_return_cmd( instance, cmd );
    + return -ENOMEM;
    + }
    +
    + memset( el_info, 0, sizeof(*el_info) );
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 1;
    + dcmd->flags = MFI_FRAME_DIR_READ;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = sizeof(struct
    megasas_evt_log_info);
    + dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO;
    + dcmd->sgl.sge32[0].phys_addr = el_info_h;
    + dcmd->sgl.sge32[0].length = sizeof(struct
    megasas_evt_log_info);
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + /*
    + * Copy the data back into callers buffer
    + */
    + memcpy( eli, el_info, sizeof(struct megasas_evt_log_info) );
    +
    + pci_free_consistent(instance->pdev, sizeof(struct
    megasas_evt_log_info),
    + el_info, el_info_h);
    +
    + megasas_return_cmd( instance, cmd );
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_register_aen - Registers for asynchronous event
    notification
    + * @instance: Adapter soft state
    + * @seq_num: The starting sequence number
    + * @locale: Class of the event
    + *
    + * This function subscribes for AEN for events beyond the @seq_num. It
    requests
    + * to be notified if and only if the event is of type @locale
    + */
    +static int
    +megasas_register_aen( struct megasas_instance* instance, u32 seq_num,
    + u32 locale )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    + struct megasas_evt_detail* evt_detail;
    + dma_addr_t evt_detail_h;
    + u32* mbox_word;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + return -ENOMEM;
    + }
    +
    + dcmd = &cmd->frame->dcmd;
    + mbox_word = (u32*) dcmd->mbox;
    + evt_detail = &instance->evt_detail;
    + evt_detail_h = instance->phys_addr +
    + offsetof(struct megasas_instance,
    evt_detail);
    +
    + memset( evt_detail, 0, sizeof(*evt_detail));
    +
    + /*
    + * Prepare DCMD for aen registration
    + */
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 1;
    + dcmd->flags = MFI_FRAME_DIR_READ;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);
    + dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;
    + mbox_word[0] = seq_num;
    + mbox_word[1] = locale;
    + dcmd->sgl.sge32[0].phys_addr = (u32)(evt_detail_h & 0xFFFF);
    + dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
    +
    + /*
    + * Store reference to the cmd used to register for AEN. When an
    + * application wants us to register for AEN, we have to abort this
    + * cmd and re-register with a new EVENT LOCALE supplied by that app
    + */
    + instance->aen_cmd = cmd;
    +
    + /*
    + * Issue the aen registration frame
    + */
    + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_start_aen - Subscribes to AEN during driver load time
    + * @instance: Adapter soft state
    + */
    +static int
    +megasas_start_aen( struct megasas_instance* instance )
    +{
    + struct megasas_evt_log_info eli;
    + union megasas_evt_class_locale class_locale;
    +
    + /*
    + * Get the latest sequence number from FW
    + */
    + memset( &eli, 0, sizeof(eli) );
    +
    + if (megasas_get_seq_num( instance, &eli ))
    + return -1;
    +
    + /*
    + * Register AEN with FW for latest sequence number plus 1
    + */
    + class_locale.members.reserved = 0;
    + class_locale.members.locale = MR_EVT_LOCALE_ALL;
    + class_locale.members.class = MR_EVT_CLASS_DEBUG;
    +
    + return megasas_register_aen( instance, eli.newest_seq_num + 1,
    + class_locale.word );
    +}
    +
    +/**
    + * megasas_io_attach - Attaches this driver to SCSI mid-layer
    + * @instance: Adapter soft state
    + */
    +static int
    +megasas_io_attach( struct megasas_instance* instance )
    +{
    + struct Scsi_Host* host;
    +
    + host = scsi_host_alloc(&megasas_template_g, sizeof(void*));
    +
    + if (!host) {
    + printk( KERN_DEBUG "megasas: scsi_host_alloc failed\n" );
    + return -ENODEV;
    + }
    +
    + SCSIHOST2ADAP(host) = (caddr_t) instance;
    + instance->host = host;
    +
    + /*
    + * Export parameters required by SCSI mid-layer
    + */
    + scsi_assign_lock( host, instance->host_lock );
    + scsi_set_device( host, &instance->pdev->dev );
    +
    + host->irq = instance->pdev->irq;
    + host->unique_id = instance->unique_id;
    + host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
    + host->this_id = instance->init_id;
    + host->sg_tablesize = instance->max_num_sge;
    + host->max_sectors = instance->max_sectors_per_req;
    + host->cmd_per_lun = instance->max_fw_cmds - MEGASAS_INT_CMDS;
    + host->max_channel = MEGASAS_MAX_CHANNELS - 1;
    + host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL;
    + host->max_lun = MEGASAS_MAX_LUN;
    +
    + /*
    + * Notify the mid-layer about the new controller
    + */
    + if (scsi_add_host(host, &instance->pdev->dev)) {
    +
    + printk( KERN_DEBUG "megasas: scsi_add_host failed\n" );
    + scsi_host_put( host );
    +
    + return -ENODEV;
    + }
    +
    + /*
    + * Trigger SCSI to scan our drives
    + */
    + scsi_scan_host( host );
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_io_detach - Detach the driver from SCSI mid-layer
    + * @instance: Adapter soft state
    + */
    +static void
    +megasas_io_detach( struct megasas_instance* instance )
    +{
    + if (instance->host)
    + scsi_remove_host( instance->host );
    +}
    +
    +/**
    + * megasas_probe_one - PCI hotplug entry point
    + * @pdev: PCI device structure
    + * @id: PCI ids of supported hotplugged adapter
    + */
    +static int __devinit
    +megasas_probe_one( struct pci_dev *pdev, const struct pci_device_id *id )
    +{
    + int rval;
    + dma_addr_t instance_h;
    + struct megasas_instance* instance;
    +
    + /*
    + * Announce PCI information
    + */
    + printk( KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
    + pdev->vendor, pdev->device, pdev->subsystem_vendor,
    + pdev->subsystem_device);
    +
    + printk( KERN_INFO "megasas: bus %d:slot %d:func %d\n",
    + pdev->bus->number,
    PCI_SLOT(pdev->devfn),PCI_FUNC(pdev->devfn));
    +
    + /*
    + * PCI prepping: enable device set bus mastering and dma mask
    + */
    + rval = pci_enable_device(pdev);
    +
    + if (rval) {
    + return rval;
    + }
    +
    + pci_set_master(pdev);
    +
    + /*
    + * All our contollers are capable of performing 64-bit DMA
    + */
    + if (IS_DMA64) {
    + if (pci_set_dma_mask( pdev, DMA_64BIT_MASK) != 0) {
    +
    + if (pci_set_dma_mask( pdev, DMA_32BIT_MASK ) != 0)
    + goto fail_set_dma_mask;
    + }
    + }
    + else {
    + if (pci_set_dma_mask( pdev, DMA_32BIT_MASK ) != 0)
    + goto fail_set_dma_mask;
    + }
    +
    + /*
    + * We allocate DMA memory for instance soft state so that we can
    + * can directly pass adp->{member variable} to FW to get FW data.
    + * E.g, product information, configuration data etc.
    + */
    + instance = pci_alloc_consistent( pdev, sizeof(struct
    megasas_instance),
    + &instance_h
    );
    +
    + if (!instance) {
    + printk(KERN_DEBUG "megasas: Failed to allocate memory \
    + for controller soft state \n" );
    + goto fail_alloc_instance;
    + }
    +
    + memset( instance, 0, sizeof(*instance) );
    +
    + /*
    + * Initialize locks and queues
    + */
    + INIT_LIST_HEAD( &instance->cmd_pool );
    +
    + init_waitqueue_head( &instance->int_cmd_wait_q );
    + init_waitqueue_head( &instance->abort_cmd_wait_q );
    +
    + spin_lock_init( &instance->cmd_pool_lock );
    + spin_lock_init( &instance->lock );
    +
    + instance->host_lock = &instance->lock;
    +
    + /*
    + * Initialize PCI related and misc parameters
    + */
    + instance->phys_addr = instance_h;
    + instance->pdev = pdev;
    + instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
    + instance->init_id = MEGASAS_DEFAULT_INIT_ID;
    + instance->aen_cmd = NULL;
    +
    + /*
    + * Initialize MFI Firmware
    + */
    + if (megasas_init_mfi( instance ))
    + goto fail_init_mfi;
    +
    + /*
    + * Register IRQ
    + */
    + if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas",
    + instance)) {
    + printk( KERN_DEBUG "megasas: Failed to register IRQ\n" );
    + goto fail_irq;
    + }
    +
    + MFI_ENABLE_INTR( instance->reg_set );
    +
    + /*
    + * Store instance in PCI softstate
    + */
    + pci_set_drvdata( pdev, instance );
    +
    + /*
    + * Add this controller to megasas_mgmt_info structure so that it
    + * can be exported to management applications
    + */
    + megasas_mgmt_info.count++;
    + megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance;
    + megasas_mgmt_info.max_index++;
    +
    + /*
    + * Initiate AEN (Asynchronous Event Notification)
    + */
    + megasas_start_aen(instance);
    +
    + /*
    + * Register with SCSI mid-layer
    + */
    + if (megasas_io_attach( instance ))
    + goto fail_io_attach;
    +
    + return 0;
    +
    +fail_io_attach:
    + megasas_mgmt_info.count--;
    + megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
    + megasas_mgmt_info.max_index--;
    +
    + pci_set_drvdata( pdev, NULL );
    + MFI_DISABLE_INTR(instance->reg_set);
    + free_irq( instance->pdev->irq, instance );
    +
    + megasas_release_mfi( instance );
    +
    +fail_irq:
    +fail_init_mfi:
    + pci_free_consistent( pdev, sizeof(struct megasas_instance),
    + instance, instance_h );
    +fail_alloc_instance:
    +fail_set_dma_mask:
    + pci_disable_device( pdev );
    +
    + return -ENODEV;
    +}
    +
    +/**
    + * megasas_flush_cache - Requests FW to flush all its caches
    + * @instance: Adapter soft state
    + */
    +static void
    +megasas_flush_cache( struct megasas_instance* instance )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd)
    + return;
    +
    + dcmd = &cmd->frame->dcmd;
    +
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 0;
    + dcmd->flags = MFI_FRAME_DIR_NONE;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = 0;
    + dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH;
    + dcmd->mbox[0] = MR_FLUSH_CTRL_CACHE |
    + MR_FLUSH_DISK_CACHE;
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + megasas_return_cmd( instance, cmd );
    +
    + return;
    +}
    +
    +/**
    + * megasas_shutdown_controller - Instructs FW to shutdown the
    controller
    + * @instance: Adapter soft state
    + */
    +static void
    +megasas_shutdown_controller( struct megasas_instance* instance )
    +{
    + struct megasas_cmd* cmd;
    + struct megasas_dcmd_frame* dcmd;
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd);
    + return;
    +
    + if (instance->aen_cmd)
    + megasas_issue_blocked_abort_cmd( instance, instance->aen_cmd
    );
    +
    + dcmd = &cmd->frame->dcmd;
    +
    + memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
    +
    + dcmd->cmd = MFI_CMD_DCMD;
    + dcmd->cmd_status = 0x0;
    + dcmd->sge_count = 0;
    + dcmd->flags = MFI_FRAME_DIR_NONE;
    + dcmd->timeout = 0;
    + dcmd->data_xfer_len = 0;
    + dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN;
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + megasas_return_cmd( instance, cmd );
    +
    + return;
    +}
    +
    +/**
    + * megasas_detach_one - PCI hot"un"plug entry point
    + * @pdev: PCI device structure
    + */
    +static void
    +megasas_detach_one( struct pci_dev *pdev )
    +{
    + int i;
    + struct Scsi_Host* host;
    + struct megasas_instance* instance;
    + dma_addr_t instance_h;
    +
    + instance = pci_get_drvdata( pdev );
    + host = instance->host;
    + instance_h = instance->phys_addr;
    +
    + megasas_io_detach( instance );
    +
    + megasas_flush_cache( instance );
    + megasas_shutdown_controller( instance );
    +
    + /*
    + * Take the instance off the instance array. Note that we will not
    + * decrement the max_index. We let this array be sparse array
    + */
    + for (i = 0; i < megasas_mgmt_info.max_index; i++ ) {
    + if (megasas_mgmt_info.instance[i] == instance) {
    + megasas_mgmt_info.count--;
    + megasas_mgmt_info.instance[i] = NULL;
    +
    + break;
    + }
    + }
    +
    + pci_set_drvdata( instance->pdev, NULL );
    +
    + MFI_DISABLE_INTR(instance->reg_set);
    +
    + free_irq( instance->pdev->irq, instance );
    +
    + megasas_release_mfi( instance );
    +
    + pci_free_consistent( instance->pdev, sizeof(struct
    megasas_instance),
    + instance, instance_h
    );
    + scsi_host_put( host );
    +
    + pci_set_drvdata( pdev, NULL );
    +
    + pci_disable_device( pdev );
    +
    + return;
    +}
    +
    +/**
    + * megasas_shutdown - Shutdown entry point
    + * @device: Generic device structure
    + */
    +static void
    +megasas_shutdown( struct device* device )
    +{
    + int i;
    + struct megasas_instance* instance;
    +
    + for( i = 0; i < megasas_mgmt_info.max_index; i++ ) {
    + instance = megasas_mgmt_info.instance[i];
    +
    + if (instance)
    + megasas_shutdown_controller( instance );
    + }
    +}
    +
    +/**
    + * megasas_mgmt_open - char node "open" entry point
    + */
    +static int
    +megasas_mgmt_open( struct inode* inode, struct file* filep )
    +{
    + /*
    + * Allow only those users with admin rights
    + */
    + if (!capable(CAP_SYS_ADMIN))
    + return -EACCES;
    +
    + return 0;
    +}
    +
    +/**
    + * megasas_mgmt_release - char node "release" entry point
    + */
    +static int
    +megasas_mgmt_release( struct inode* inode, struct file* filep )
    +{
    + return 0;
    +}
    +
    +/**
    + * megasas_mgmt_fasync - Async notifier registration from
    applications
    + *
    + * This function adds the calling process to a driver global queue. When an
    + * event occurs, SIGIO will be sent to all processes in this queue.
    + */
    +static int
    +megasas_mgmt_fasync( int fd, struct file* filep, int mode )
    +{
    + int rc;
    +
    + down( &megasas_async_queue_mutex );
    +
    + rc = fasync_helper( fd, filep, mode, &megasas_async_queue );
    +
    + up( &megasas_async_queue_mutex );
    +
    + if (rc >0)
    + return 0;
    +
    + printk( KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc );
    +
    + return rc;
    +}
    +
    +/**
    + * megasas_mgmt_fw_dcmd - Issues DCMD to the FW
    + * @instance: Adapter soft state
    + * @uioc: User's ioctl packet copied into kernel addr
    + * @argp: User's ioctl packet in user address
    + * @cmd: Command to be prepared and issued
    + *
    + * This function prepares direct command (DCMD) to FW from user's ioctl
    packet.
    + * The driver allocates temporary buffer (if needed) for data transfer
    + *
    + * Note: The suffixes 'k' & 'u' mean 'kerne' and 'user' respectively
    + */
    +static int
    +megasas_mgmt_fw_dcmd( struct megasas_instance* instance, struct iocpacket*
    uioc,
    + void __user* argp, struct megasas_cmd* cmd )
    +{
    + int rc = 0;
    + void __user* ubuff;
    + struct megasas_dcmd_frame* kdcmd;
    + struct megasas_dcmd_frame __user* udcmd;
    + struct megasas_dcmd_frame* cmd_dcmd;
    + caddr_t kbuff;
    + dma_addr_t kbuff_h;
    + u32 xferlen;
    + u8 user_64bit_sgl = 0;
    +
    + cmd_dcmd = &cmd->frame->dcmd;
    + kdcmd = (struct megasas_dcmd_frame*) &uioc->frame;
    + udcmd = (struct megasas_dcmd_frame*)
    + (((struct iocpacket*)argp)->frame);
    +
    + if (kdcmd->flags & MFI_FRAME_SGL64 )
    + user_64bit_sgl = 1;
    +
    + if (!user_64bit_sgl) {
    + xferlen = kdcmd->sgl.sge32[0].length;
    + ubuff = (void __user*) (ulong)
    udcmd->sgl.sge32[0].phys_addr;
    + }
    + else {
    + xferlen = kdcmd->sgl.sge64[0].length;
    + ubuff = (void __user*) (ulong)
    udcmd->sgl.sge64[0].phys_addr;
    + }
    +
    + /*
    + * Allocate internal buffer for data transfer
    + */
    + if (xferlen)
    + kbuff = pci_alloc_consistent(instance->pdev, xferlen,
    &kbuff_h);
    + else
    + kbuff = NULL;
    +
    + if (xferlen && !kbuff) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory for \
    + DCMD internal buffer \n" );
    + return -ENOMEM;
    + }
    +
    + if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_WRITE)) {
    +
    + if (copy_from_user(kbuff, ubuff, xferlen)) {
    + printk( KERN_DEBUG "megasas: Failed to copy from \
    + application
    buffer\n");
    + rc = -EFAULT;
    + goto exit_label;
    + }
    + }
    +
    + /*
    + * Copy the frame sent by user into driver's frame
    + */
    + cmd_dcmd->cmd = kdcmd->cmd;
    + cmd_dcmd->cmd_status = kdcmd->cmd_status;
    + cmd_dcmd->sge_count = kdcmd->sge_count;
    + cmd_dcmd->timeout = kdcmd->timeout;
    + cmd_dcmd->data_xfer_len = kdcmd->data_xfer_len;
    + cmd_dcmd->opcode = kdcmd->opcode;
    +
    + memcpy( cmd_dcmd->mbox, kdcmd->mbox, MFI_MBOX_SIZE );
    +
    + if (!user_64bit_sgl) {
    + cmd_dcmd->flags = kdcmd->flags;
    + cmd_dcmd->sgl.sge32[0].length =
    kdcmd->sgl.sge32[0].length;
    + cmd_dcmd->sgl.sge32[0].phys_addr= kbuff_h;
    + }
    + else {
    + cmd_dcmd->flags = kdcmd->flags
    |MFI_FRAME_SGL64;
    + cmd_dcmd->sgl.sge64[0].length =
    kdcmd->sgl.sge64[0].length;
    + cmd_dcmd->sgl.sge64[0].phys_addr= kbuff_h;
    + }
    +
    + megasas_issue_blocked_cmd( instance, cmd );) {
    +
    + if (copy_to_user( ubuff, kbuff, xferlen)) {
    +
    + printk( KERN_DEBUG "megasas: Failed to copy \
    + to application buffer\n");
    + rc = -EFAULT;
    + goto exit_label;
    + }
    + }
    +
    + if (copy_to_user( &udcmd->cmd_status, &cmd_dcmd->cmd_status,
    + sizeof(u8))) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer\n");
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    +exit_label:
    + pci_free_consistent( instance->pdev, xferlen, kbuff, kbuff_h );
    + return rc;
    +}
    +
    +/**
    + * megasas_mgmt_fw_dcdb - Prepares and issues DCDB
    + * @instance: Adapter soft state
    + * @uioc: User's ioctl packet copied into kernel addr
    + * @argp: User's ioctl packet in user addr
    + * @cmd: Free command from the command pool
    + *
    + * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
    respectively.
    + */
    +static int
    +megasas_mgmt_fw_dcdb( struct megasas_instance* instance, struct iocpacket*
    uioc,
    + void __user* argp, struct megasas_cmd* cmd )
    +{
    + int rc = 0;
    + void __user* ubuff;
    + void __user* usense;
    + struct megasas_pthru_frame* kdcdb;
    + struct megasas_pthru_frame __user* udcdb;
    + struct megasas_pthru_frame* cmd_dcdb;
    + caddr_t kbuff;
    + dma_addr_t kbuff_h;
    + caddr_t ksense;
    + dma_addr_t ksense_h;
    + u32 xferlen;
    + u8 user_64bit_sgl = 0;
    + u64 temp;
    +
    + ksense = NULL;
    + usense = NULL;
    + cmd_dcdb = &cmd->frame->pthru;
    + kdcdb = (struct megasas_pthru_frame*) &uioc->frame;
    + udcdb = (struct megasas_pthru_frame*)
    + (((struct iocpacket*)argp)->frame);
    +
    + if (kdcdb->flags & MFI_FRAME_SGL64 )
    + user_64bit_sgl = 1;
    +
    + if (!user_64bit_sgl) {
    + xferlen = kdcdb->sgl.sge32[0].length;
    + ubuff = (void __user*)(ulong)
    (udcdb->sgl.sge32[0].phys_addr);
    + }
    + else {
    + xferlen = kdcdb->sgl.sge64[0].length;
    + ubuff = (void __user*) (ulong)
    udcdb->sgl.sge64[0].phys_addr;
    + }
    +
    + /*
    + * Allocate internal buffer for data transfer
    + */
    + if (xferlen)
    + kbuff = pci_alloc_consistent(instance->pdev, xferlen,
    &kbuff_h);
    + else
    + kbuff = NULL;
    +
    + if (xferlen && !kbuff) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for DCDB internal buffer\n"
    );
    + return -ENOMEM;
    + }
    +
    + memset( kbuff, 0, xferlen );
    +
    + /*
    + * Allocate internal buffer for request sense
    + */
    + if (kdcdb->sense_len) {
    + ksense = pci_alloc_consistent(instance->pdev,
    kdcdb->sense_len,
    + &ksense_h);
    + if (!ksense) {
    + rc = -ENOMEM;
    + goto exit_label;
    + }
    +
    + temp = kdcdb->sense_buf_phys_addr_hi;
    + temp = temp << 32 | kdcdb->sense_buf_phys_addr_lo;
    +
    + usense = (void __user*)(ulong) temp;
    +
    + }
    +
    + if (xferlen && (kdcdb->flags & MFI_FRAME_DIR_WRITE)) {
    +
    + if (copy_from_user(kbuff, ubuff, xferlen)) {
    + printk( KERN_DEBUG "megasas: Failed to copy from \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    + }
    +
    + memcpy( cmd_dcdb, kdcdb, MEGAMFI_FRAME_SIZE );
    + cmd_dcdb->context = cmd->index;
    + cmd_dcdb->sge_count = 1;
    +
    + if (!user_64bit_sgl) {
    + cmd_dcdb->flags = kdcdb->flags;
    + cmd_dcdb->sgl.sge32[0].length =
    kdcdb->sgl.sge32[0].length;
    + cmd_dcdb->sgl.sge32[0].phys_addr= kbuff_h;
    + }
    + else {
    + cmd_dcdb->flags = kdcdb->flags
    |MFI_FRAME_SGL64;
    + cmd_dcdb->sgl.sge64[0].length =
    kdcdb->sgl.sge64[0].length;
    + cmd_dcdb->sgl.sge64[0].phys_addr= kbuff_h;
    + }
    +
    + cmd_dcdb->sense_buf_phys_addr_hi = 0;
    + cmd_dcdb->sense_buf_phys_addr_lo = ksense_h ;
    +
    + cmd->sync_cmd = 1;
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + if (xferlen && (kdcdb->flags & MFI_FRAME_DIR_READ)) {
    +
    + if (copy_to_user( ubuff, kbuff, xferlen)) {
    +
    + printk(KERN_DEBUG "megasas: Failed to copy \
    + to application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    + }
    +
    + if (copy_to_user( &udcdb->cmd_status, &cmd_dcdb->cmd_status,
    + sizeof(u8))) {
    + printk(KERN_DEBUG "megasas: Failed to copy to \
    + application buffer\n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    + if (kdcdb->sense_len) {
    + if (copy_to_user( usense, ksense, kdcdb->sense_len)) {
    + printk( KERN_DEBUG "megasas: Failed to copy \
    + to application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    + }
    +
    +exit_label:
    + if (ksense)
    + pci_free_consistent( instance->pdev, kdcdb->sense_len,
    ksense,
    + ksense_h );
    + if (kbuff)
    + pci_free_consistent( instance->pdev, xferlen, kbuff, kbuff_h
    );
    +
    + return rc;
    +}
    +
    +/**
    + * megasas_mgmt_fw_smp - Issues passthrough cmds to SAS devices
    + * @instance: Adapter soft state
    + * @uioc: User's ioctl packet in kernel address
    + * @argp: User's ioctl packet in user address
    + * @cmd: Command from free pool
    + *
    + * SMP frames have two SG elements at the end - response and request ( in
    that
    + * order). This function allocates temporary DMA buffers for these SG
    elements.
    + *
    + * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
    respectively.
    + */
    +static int
    +megasas_mgmt_fw_smp( struct megasas_instance* instance, struct iocpacket*
    uioc,
    + void __user* argp, struct megasas_cmd* cmd )
    +{
    + int rc = 0;
    + struct megasas_smp_frame* ksmp;
    + struct megasas_smp_frame __user* usmp;
    + struct megasas_smp_frame* cmd_smp;
    +
    + caddr_t kreq;
    + caddr_t kresp;
    + dma_addr_t kreq_h;
    + dma_addr_t kresp_h;
    + void __user* ureq;
    + void __user* uresp;
    + u32 req_len;
    + u32 resp_len;
    +
    + u8 user_64bit_sgl = 0;
    +
    + cmd_smp = &cmd->frame->smp;
    + ksmp = (struct megasas_smp_frame*) &uioc->frame;
    + usmp = (struct megasas_smp_frame*)
    + (((struct iocpacket*)argp)->frame);
    +
    + if (ksmp->flags & MFI_FRAME_SGL64 )
    + user_64bit_sgl = 1;
    +
    + if (!user_64bit_sgl) {
    + resp_len = ksmp->sgl.sge32[0].length;
    + req_len = ksmp->sgl.sge32[1].length;
    +
    + uresp = (void __user*) ((ulong)
    usmp->sgl.sge32[0].phys_addr);
    + ureq = (void __user*) ((ulong)
    usmp->sgl.sge32[1].phys_addr);
    + }
    + else {
    + resp_len = ksmp->sgl.sge64[0].length;
    + req_len = ksmp->sgl.sge64[1].length;
    +
    + uresp = (void __user*) ((ulong)
    usmp->sgl.sge64[0].phys_addr);
    + ureq = (void __user*) ((ulong)
    usmp->sgl.sge64[1].phys_addr);
    + }
    +
    + if (!req_len || !resp_len) {
    + return -EINVAL;
    + }
    +
    + /*
    + * Allocate kernel buffers for SMP request and response
    + */
    + kreq = NULL;
    + kresp = NULL;
    +
    + kreq = pci_alloc_consistent(instance->pdev, req_len, &kreq_h);
    +
    + if (!kreq) {
    +
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for SMP request \n"
    );
    + rc = -ENOMEM;
    + goto exit_label;
    + }
    +
    + kresp = pci_alloc_consistent(instance->pdev, resp_len, &kresp_h);
    +
    + if(!kresp) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for SMP response \n"
    );
    + rc = -ENOMEM;
    + goto exit_label;
    + }
    +
    + if (copy_from_user(kreq, ureq, req_len)) {
    + printk( KERN_DEBUG "megasas: Failed to copy from \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    + memcpy (cmd_smp, ksmp, MEGAMFI_FRAME_SIZE );
    + cmd_smp->context = cmd->index;
    +
    + if (!user_64bit_sgl) {
    + cmd_smp->flags = ksmp->flags;
    + cmd_smp->sgl.sge32[0].length = resp_len;
    + cmd_smp->sgl.sge32[0].phys_addr = kresp_h;
    + cmd_smp->sgl.sge32[1].length = req_len;
    + cmd_smp->sgl.sge32[1].phys_addr = kreq_h;
    + }
    + else {
    + cmd_smp->flags = ksmp->flags |
    + MFI_FRAME_SGL64;
    + cmd_smp->sgl.sge64[0].length = resp_len;
    + cmd_smp->sgl.sge64[0].phys_addr = kresp_h;
    + cmd_smp->sgl.sge64[1].length = req_len;
    + cmd_smp->sgl.sge64[1].phys_addr = kreq_h;
    + }
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + if (copy_to_user( uresp, kresp, resp_len)) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    + if (copy_to_user( &usmp->cmd_status, &cmd_smp->cmd_status,
    + sizeof(u8))) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    +exit_label:
    +
    + if (kreq)
    + pci_free_consistent(instance->pdev, req_len, kreq, kreq_h);
    + if (kresp)
    + pci_free_consistent(instance->pdev, resp_len, kresp,
    kresp_h);
    +
    + return rc;
    +}
    +
    +/**
    + * megasas_mgmt_fw_stp - Issues passthrough cmds to SATA drives
    + * @instance: Adapter soft state
    + * @uioc: User ioctl packet in kernel address
    + * @argp: User ioctl packet in user address
    + * @cmd: Command from free pool
    + *
    + * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
    respectively.
    + */
    +static int
    +megasas_mgmt_fw_stp( struct megasas_instance* instance, struct iocpacket*
    uioc,
    + void __user* argp, struct megasas_cmd* cmd )
    +{
    + int rc = 0;
    + struct megasas_stp_frame* kstp;
    + struct megasas_stp_frame __user* ustp;
    + struct megasas_stp_frame* cmd_stp;
    +
    + caddr_t kdata;
    + caddr_t kresp;
    + dma_addr_t kdata_h;
    + dma_addr_t kresp_h;
    + void __user* udata;
    + void __user* uresp;
    + u32 data_len;
    + u32 resp_len;
    +
    + u8 user_64bit_sgl = 0;
    +
    + cmd_stp = &cmd->frame->stp;
    + kstp = (struct megasas_stp_frame *) &uioc->frame;
    + ustp = (struct megasas_stp_frame *)
    + (((struct iocpacket *)argp)->frame);
    +
    + if (kstp->flags & MFI_FRAME_SGL64 )
    + user_64bit_sgl = 1;
    +
    + if (!user_64bit_sgl) {
    +
    + resp_len = ustp->sgl.sge32[0].length;
    + data_len = ustp->sgl.sge32[1].length;
    +
    + uresp = (void __user*) ((ulong)
    ustp->sgl.sge32[0].phys_addr);
    + udata = (void __user*) ((ulong)
    ustp->sgl.sge32[1].phys_addr);
    + }
    + else {
    + resp_len = ustp->sgl.sge64[0].length;
    + data_len = ustp->sgl.sge64[1].length;
    +
    + uresp = (void __user*) ((ulong)
    ustp->sgl.sge64[0].phys_addr);
    + udata = (void __user*) ((ulong)
    ustp->sgl.sge64[1].phys_addr);
    + }
    +
    + if (!data_len || !resp_len) {
    + return -EINVAL;
    + }
    +
    + /*
    + * Allocate kernel buffers for SMP request and response
    + */
    +
    + kdata = NULL;
    + kresp = NULL;
    +
    + kdata = pci_alloc_consistent(instance->pdev, data_len, &kdata_h);
    +
    + if(!kdata) {
    +
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for STP request \n"
    );
    + rc = -ENOMEM;
    + goto exit_label;
    + }
    +
    + kresp = pci_alloc_consistent(instance->pdev, resp_len, &kresp_h);
    +
    + if(!kresp) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for STP response \n"
    );
    + rc = -ENOMEM;
    + goto exit_label;
    + }
    +
    + memcpy (cmd_stp, kstp, MEGAMFI_FRAME_SIZE );
    + cmd_stp->context = cmd->index;
    +
    + if (!user_64bit_sgl) {
    + cmd_stp->flags = kstp->flags;
    + cmd_stp->sgl.sge32[0].length = resp_len;
    + cmd_stp->sgl.sge32[0].phys_addr = kresp_h;
    + cmd_stp->sgl.sge32[1].length = data_len;
    + cmd_stp->sgl.sge32[1].phys_addr = kdata_h;
    + }
    + else {
    + cmd_stp->flags = kstp->flags |
    + MFI_FRAME_SGL64;
    + cmd_stp->sgl.sge64[0].length = resp_len;
    + cmd_stp->sgl.sge64[0].phys_addr = kresp_h;
    + cmd_stp->sgl.sge64[1].length = data_len;
    + cmd_stp->sgl.sge64[1].phys_addr = kdata_h;
    + }
    +
    + megasas_issue_blocked_cmd( instance, cmd );
    +
    + if (copy_to_user( uresp, kresp, resp_len)) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    + if (copy_to_user( udata, kdata, data_len)) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    + if (copy_to_user( &ustp->cmd_status, &cmd_stp->cmd_status,
    + sizeof(u8))) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + rc = -EFAULT;
    + goto exit_label;
    + }
    +
    +exit_label:
    +
    + if (kdata)
    + pci_free_consistent(instance->pdev, data_len, kdata,
    kdata_h);
    + if (kresp)
    + pci_free_consistent(instance->pdev, resp_len, kresp,
    kresp_h);
    +
    + return rc;
    +}
    +
    +/**
    + * megasas_mgmt_fw_ioctl - Issues management ioctls to FW
    + * @instance: Adapter soft state
    + * @argp: User's ioctl packet
    + */
    +static int
    +megasas_mgmt_fw_ioctl( struct megasas_instance* instance, void __user* argp
    )
    +{
    + int ret;
    + struct iocpacket* uioc;
    + struct megasas_header* hdr;
    + struct megasas_cmd* cmd;
    +
    + uioc = kmalloc( sizeof(struct iocpacket), GFP_KERNEL );
    +
    + if (!uioc) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for application IOCTL packet\n" );
    + return -ENOMEM;
    + }
    +
    + if (copy_from_user(uioc, argp, sizeof(struct iocpacket))) {
    + printk( KERN_DEBUG "megasas: Failed to copy from \
    + application buffer \n" );
    + return -EFAULT;
    + }
    +
    + cmd = megasas_get_cmd( instance );
    +
    + if (!cmd) {
    + printk( KERN_DEBUG "megasas: Failed to get a cmd packet\n"
    );
    + return -ENOMEM;
    + }
    +
    + hdr = (struct megasas_header*) uioc->frame;
    +
    + switch( hdr->cmd ) {
    +
    + case MFI_CMD_DCMD:
    + ret = megasas_mgmt_fw_dcmd(instance, uioc, argp,
    cmd);
    + break;
    +
    + case MFI_CMD_PD_SCSI_IO:
    + case MFI_CMD_LD_SCSI_IO:
    + ret = megasas_mgmt_fw_dcdb(instance, uioc, argp,
    cmd);
    + break;
    +
    + case MFI_CMD_SMP:
    + ret = megasas_mgmt_fw_smp(instance, uioc, argp,
    cmd);
    + break;
    +
    + case MFI_CMD_STP:
    + ret = megasas_mgmt_fw_stp(instance, uioc, argp,
    cmd);
    + break;
    +
    + default:
    + ret = -EINVAL;
    + break;
    + }
    +
    + megasas_return_cmd( instance, cmd );
    + return ret;
    +}
    +
    +/**
    + * megasas_fill_drv_ver - Fills the driver version info for
    application
    + * @dv: Driver version information
    + */
    +static void
    +megasas_fill_drv_ver( struct megasas_drv_ver* dv )
    +{
    + memset( dv, 0, sizeof(*dv) );
    +
    + memcpy(dv->signature, "$LSI LOGIC$", strlen("$LSI LOGIC$") );
    + memcpy(dv->os_name, "Linux", strlen("Linux") );
    + memcpy(dv->os_ver, "Ver Indpndt", strlen("ver indpndt") );
    + memcpy(dv->drv_name, "megaraid_sas", strlen("megaraid_sas") );
    + memcpy(dv->drv_ver, MEGASAS_VERSION,strlen(MEGASAS_VERSION) );
    + memcpy(dv->drv_rel_date,MEGASAS_RELDATE,strlen(MEGASAS_RELDATE) );
    +}
    +
    +/**
    + * megasas_mgmt_ioctl - char node ioctl entry point
    + *
    + * Few ioctl commands should be handled by driver itself (driver ioctls)
    and
    + * the rest should be converted into appropriate commands for FW and
    issued.
    + */
    +static int
    +megasas_mgmt_ioctl( struct inode* inode, struct file* filep,
    + unsigned int cmd, unsigned long arg )
    +{
    + int i;
    + int j;
    + int rc;
    + u8 fw_status;
    + struct iocpacket* uioc;
    + void __user* argp;
    + void __user* udata_addr;
    + u8 user_64bit_sgl = 0;
    + u32 opcode;
    + u32 locale;
    + u32 seq_num;
    + u32* mbox_word;
    + struct megasas_cmd* old_cmd;
    +
    + struct megasas_instance* instance;
    + struct megasas_dcmd_frame* kdcmd;
    + struct megasas_dcmd_frame __user* udcmd;
    + struct megasas_drv_ver* dv;
    + struct pci_dev* pdev;
    +
    + argp = (void __user*) arg;
    + uioc = kmalloc( sizeof(struct iocpacket), GFP_KERNEL );
    +
    + if (!uioc) {
    + printk( KERN_DEBUG "megasas: Failed to allocate memory \
    + for application IOCTL packet\n" );
    + return -ENOMEM;
    + }
    +
    + if (copy_from_user( uioc, argp, sizeof(struct iocpacket))) {
    + printk( KERN_DEBUG "megasas: Failed to copy from \
    + application buffer \n" );
    + return -EINVAL;
    + }
    +
    + if (strncmp(uioc->signature, IOC_SIGNATURE, strlen(IOC_SIGNATURE))
    !=0){
    + printk( KERN_DEBUG "megasas: Invalid ioctl signature\n" );
    + return -EINVAL;
    + }
    +
    + if (uioc->version != 0) {
    + printk( KERN_DEBUG "megasas: Invalid ioctl version %d\n",
    +
    uioc->version );
    + return -EINVAL;
    + }
    +
    + instance = NULL;
    + kdcmd = (struct megasas_dcmd_frame*) uioc->frame;
    + udcmd = (struct megasas_dcmd_frame*)
    + (((struct iocpacket*)argp)->frame);
    +
    + /*
    + * Find out if user has used 32 or 64 bit SGL
    + */
    + if (kdcmd->flags & MFI_FRAME_SGL64 )
    + user_64bit_sgl = 1;
    +
    + if (!user_64bit_sgl)
    + udata_addr = (void __user *)
    +
    ((ulong)kdcmd->sgl.sge32[0].phys_addr);
    + else
    + udata_addr = (void __user *)
    + ((ulong)
    kdcmd->sgl.sge64[0].phys_addr);
    +
    + i = ((uioc->controller_id & 0xF0) >> 4) - 1;
    +
    + if (i < megasas_mgmt_info.max_index)
    + instance = megasas_mgmt_info.instance[i];
    + else
    + instance = NULL;
    +
    + if ((uioc->control_code == MR_DRIVER_IOCTL_LINUX) ||
    + (uioc->control_code == MR_DRIVER_IOCTL_COMMON)) {
    + /*
    + * If MR_DRIVER_IOCTL_LINUX or MR_DRIVER_IOCTL_COMMON
    + * look at dcmd->opcode for the actual operation
    + */
    + opcode = kdcmd->opcode;
    + }
    + else {
    + /* FW Command */
    + opcode = uioc->control_code;
    + }
    +
    + switch (opcode) {
    +
    + case MR_DRIVER_IOCTL_DRIVER_VERSION:
    +
    + dv = kmalloc( sizeof(struct megasas_drv_ver), GFP_KERNEL );
    +
    + if (!dv) {
    + printk( KERN_DEBUG "megasas: Failed to allocate \
    + memory for driver version\n"
    );
    + return -ENOMEM;
    + }
    +
    + megasas_fill_drv_ver( dv );
    +
    + if (copy_to_user(udata_addr, dv,
    + sizeof(struct megasas_drv_ver))) {
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + return -EFAULT;
    + }
    +
    + rc = 0;
    + fw_status = MFI_STAT_OK;
    +
    + if (copy_to_user( &udcmd->cmd_status, &fw_status,
    + sizeof(u8))) {
    + rc = -EFAULT;
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + }
    +
    + break;
    +
    + case MR_LINUX_GET_ADAPTER_COUNT:
    +
    + if (copy_to_user(udata_addr, &megasas_mgmt_info.count,
    + sizeof(u16))) {
    + return -EFAULT;
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + }
    +
    + rc = 0;
    + fw_status = MFI_STAT_OK;
    +
    + if (copy_to_user( &udcmd->cmd_status, &fw_status,
    + sizeof(u8))) {
    + rc = -EFAULT;
    + printk( KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + }
    +
    + break;
    +
    + case MR_LINUX_GET_ADAPTER_MAP:
    + /*
    + * The applications _don't_ address our controllers using
    zero
    + * based index. We give them an array of 16-bit unique
    handles.
    + * These unique handles are simple encryptions of the
    indices
    + *
    + * Encrypting logic - which converts a controller index into
    a
    + * 16-bit value - is simply (index + 1) << 4 | 0x0F.
    + *
    + * Note also when controllers are hot plugged, our
    controller
    + * array (megasas_mgmt_info) becomes sparse. We don't reuse
    the
    + * vacated slots.
    + */
    + memset(megasas_mgmt_info.map, 0,
    + sizeof(u16) * MAX_MGMT_ADAPTERS);
    +
    + j = 0;
    + for (i = 0; i < megasas_mgmt_info.max_index; i++) {
    + if (megasas_mgmt_info.instance[i]) {
    + megasas_mgmt_info.map[j].unique_hndl =
    + ((i + 1) << 4) | 0xF;
    +
    + pdev = megasas_mgmt_info.instance[i]->pdev;
    +
    + megasas_mgmt_info.map[j].bus_devfn =
    + ((pdev->bus->number) << 16 |
    + (PCI_SLOT(pdev->devfn)) << 8 |
    + (PCI_FUNC(pdev->devfn))) & 0xFFFFFF;
    +
    + j++;
    + }
    + }
    +
    + if ((j) && (copy_to_user(udata_addr, megasas_mgmt_info.map,
    + sizeof(struct megasas_adp_map) * j))) {
    +
    + printk(KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + return -EFAULT;
    + }
    +
    + fw_status = MFI_STAT_OK;
    + rc = 0;
    +
    + if (copy_to_user( &udcmd->cmd_status, &fw_status,
    + sizeof(u8))) {
    + rc = -EFAULT;
    + printk(KERN_DEBUG "megasas: Failed to copy to \
    + application buffer \n" );
    + }
    +
    + break;
    +
    + case MR_LINUX_GET_AEN:
    +
    + if (!instance) {
    + printk( KERN_DEBUG "megasas: Invalid instance \n" );
    + return -ENODEV;
    + }
    +
    + mbox_word = (u32*) kdcmd->mbox;
    + seq_num = mbox_word[0];
    + locale = mbox_word[1];
    + old_cmd = instance->aen_cmd;
    +
    + /*
    + * If there is an AEN pending already (old_cmd), abort that
    + * before issuing a fresh subsricption for AEN
    + */
    + if (old_cmd) {
    + old_cmd->abort_aen = 1;
    + rc = megasas_issue_blocked_abort_cmd(instance,
    old_cmd);
    +
    + if (rc) {
    + printk(KERN_DEBUG "megasas: Failed to abort
    \
    + previous AEN\n" );
    + break;
    + }
    + }
    +
    + rc = megasas_register_aen( instance, seq_num, locale );
    +
    + break;
    +
    + case IOC_CMD_FIRMWARE:
    +
    + if (!instance) {
    + printk( KERN_DEBUG "megasas: Invalid instance \n" );
    + return -ENODEV;
    + }
    +
    + rc = megasas_mgmt_fw_ioctl( instance, argp );
    +
    + break;
    +
    + default:
    + return -ENOTTY;
    + }
    +
    + return rc;
    +}
    +
    +#ifdef CONFIG_COMPAT
    +/**
    + * megasas_compat_ioctl - Handles conversions from 32-bit apps
    + */
    +static int
    +megasas_compat_ioctl( struct file* filep, unsigned int cmd, unsigned long
    arg,
    + struct file* filep )
    +{
    + return megasas_mgmt_ioctl( NULL, filep, cmd, arg );
    +}
    +#endif
    +
    +/*
    + * File operations structure for management interface
    + */
    +static struct file_operations megasas_mgmt_fops = {
    + .owner = THIS_MODULE,
    + .open = megasas_mgmt_open,
    + .release = megasas_mgmt_release,
    + .fasync = megasas_mgmt_fasync,
    + .ioctl = megasas_mgmt_ioctl,
    +#ifdef CONFIG_COMPAT
    + .compat_ioctl = megasas_compat_ioctl,
    +#endif
    +};
    +
    +/*
    + * PCI hotplug support registration structure
    + */
    +static struct pci_driver megasas_pci_driver = {
    +
    + .name = "megaraid_sas",
    + .id_table = megasas_pci_table_g,
    + .probe = megasas_probe_one,
    + .remove = __devexit_p(megasas_detach_one),
    + .driver = {
    + .shutdown = megasas_shutdown,
    + }
    +};
    +
    +/**
    + * megasas_init - Driver load entry point
    + */
    +static int __init
    +megasas_init( void )
    +{
    + int rval;
    +
    + /*
    + * Announce driver version and other information
    + */
    + printk( KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
    + MEGASAS_EXT_VERSION);
    +
    + /*
    + * Register character device node
    + */
    + rval = register_chrdev(0, "megaraid_sas_ioctl",
    &megasas_mgmt_fops);
    +
    + if (rval < 0) {
    + printk( KERN_DEBUG "megasas: failed to open device node\n"
    );
    + return rval;
    + }
    +
    + megasas_mgmt_majorno = rval;
    +
    + /*
    + * Register ourselves as PCI hotplug module
    + */
    + rval = pci_module_init( &megasas_pci_driver );
    +
    + if( rval ) {
    + printk(KERN_DEBUG "megasas: PCI hotplug regisration failed
    \n");
    + unregister_chrdev(megasas_mgmt_majorno,
    "megaraid_sas_ioctl");
    + }
    +
    + return rval;
    +}
    +
    +/**
    + * megasas_exit - Driver unload entry point
    + */
    +static void __exit
    +megasas_exit( void )
    +{
    + pci_unregister_driver( &megasas_pci_driver );
    + unregister_chrdev( megasas_mgmt_majorno, "megaraid_sas_ioctl" );
    +
    + return;
    +}
    +
    +module_init( megasas_init );
    +module_exit( megasas_exit );
    +
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-05-16 10:33    [W:0.209 / U:3.324 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site