Release 4.11 drivers/scsi/3w-sas.c
/*
3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
Written By: Adam Radford <aradford@gmail.com>
Copyright (C) 2009 LSI 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; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
NO WARRANTY
THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
solely responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement, including but not limited to
the risks and costs of program errors, damage to or loss of data,
programs or equipment, and unavailability or interruption of operations.
DISCLAIMER OF LIABILITY
NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Controllers supported by this driver:
LSI 3ware 9750 6Gb/s SAS/SATA-RAID
Bugs/Comments/Suggestions should be mailed to:
aradford@gmail.com
History
-------
3.26.02.000 - Initial driver release.
*/
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_cmnd.h>
#include "3w-sas.h"
/* Globals */
#define TW_DRIVER_VERSION "3.26.02.000"
static DEFINE_MUTEX(twl_chrdev_mutex);
static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT];
static unsigned int twl_device_extension_count;
static int twl_major = -1;
extern struct timezone sys_tz;
/* Module parameters */
MODULE_AUTHOR ("LSI");
MODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(TW_DRIVER_VERSION);
static int use_msi;
module_param(use_msi, int, S_IRUGO);
MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0");
/* Function prototypes */
static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
/* Functions */
/* This function returns AENs through sysfs */
static ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *outbuf, loff_t offset, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct Scsi_Host *shost = class_to_shost(dev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
unsigned long flags = 0;
ssize_t ret;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return ret;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 135 | 96.43% | 1 | 50.00% |
Chris Wright | 5 | 3.57% | 1 | 50.00% |
Total | 140 | 100.00% | 2 | 100.00% |
/* End twl_sysfs_aen_read() */
/* aen_read sysfs attribute initializer */
static struct bin_attribute twl_sysfs_aen_read_attr = {
.attr = {
.name = "3ware_aen_read",
.mode = S_IRUSR,
},
.size = 0,
.read = twl_sysfs_aen_read
};
/* This function returns driver compatibility info through sysfs */
static ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *outbuf, loff_t offset, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct Scsi_Host *shost = class_to_shost(dev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
unsigned long flags = 0;
ssize_t ret;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return ret;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 131 | 96.32% | 1 | 50.00% |
Chris Wright | 5 | 3.68% | 1 | 50.00% |
Total | 136 | 100.00% | 2 | 100.00% |
/* End twl_sysfs_compat_info() */
/* compat_info sysfs attribute initializer */
static struct bin_attribute twl_sysfs_compat_info_attr = {
.attr = {
.name = "3ware_compat_info",
.mode = S_IRUSR,
},
.size = 0,
.read = twl_sysfs_compat_info
};
/* Show some statistics about the card */
static ssize_t twl_show_stats(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *host = class_to_shost(dev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
unsigned long flags = 0;
ssize_t len;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n"
"Current commands posted: %4d\n"
"Max commands posted: %4d\n"
"Last sgl length: %4d\n"
"Max sgl length: %4d\n"
"Last sector count: %4d\n"
"Max sector count: %4d\n"
"SCSI Host Resets: %4d\n"
"AEN's: %4d\n",
TW_DRIVER_VERSION,
tw_dev->posted_request_count,
tw_dev->max_posted_request_count,
tw_dev->sgl_entries,
tw_dev->max_sgl_entries,
tw_dev->sector_count,
tw_dev->max_sector_count,
tw_dev->num_resets,
tw_dev->aen_count);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return len;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 129 | 100.00% | 1 | 100.00% |
Total | 129 | 100.00% | 1 | 100.00% |
/* End twl_show_stats() */
/* stats sysfs attribute initializer */
static struct device_attribute twl_host_stats_attr = {
.attr = {
.name = "3ware_stats",
.mode = S_IRUGO,
},
.show = twl_show_stats
};
/* Host attributes initializer */
static struct device_attribute *twl_host_attrs[] = {
&twl_host_stats_attr,
NULL,
};
/* This function will look up an AEN severity string */
static char *twl_aen_severity_lookup(unsigned char severity_code)
{
char *retval = NULL;
if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
(severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
goto out;
retval = twl_aen_severity_table[severity_code];
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 54 | 100.00% | 1 | 100.00% |
Total | 54 | 100.00% | 1 | 100.00% |
/* End twl_aen_severity_lookup() */
/* This function will queue an event */
static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
{
u32 local_time;
struct timeval time;
TW_Event *event;
unsigned short aen;
char host[16];
char *error_str;
tw_dev->aen_count++;
/* Fill out event info */
event = tw_dev->event_queue[tw_dev->error_index];
host[0] = '\0';
if (tw_dev->host)
sprintf(host, " scsi%d:", tw_dev->host->host_no);
aen = le16_to_cpu(header->status_block.error);
memset(event, 0, sizeof(TW_Event));
event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
do_gettimeofday(&time);
local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
event->time_stamp_sec = local_time;
event->aen_code = aen;
event->retrieved = TW_AEN_NOT_RETRIEVED;
event->sequence_id = tw_dev->error_sequence_id;
tw_dev->error_sequence_id++;
/* Check for embedded error string */
error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
event->parameter_len = strlen(header->err_specific_desc);
memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
if (event->severity != TW_AEN_SEVERITY_DEBUG)
printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
host,
twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str,
header->err_specific_desc);
else
tw_dev->aen_count--;
tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 301 | 100.00% | 1 | 100.00% |
Total | 301 | 100.00% | 1 | 100.00% |
/* End twl_aen_queue_event() */
/* This function will attempt to post a command packet to the board */
static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
{
dma_addr_t command_que_value;
command_que_value = tw_dev->command_packet_phys[request_id];
command_que_value += TW_COMMAND_OFFSET;
/* First write upper 4 bytes */
writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev));
/* Then the lower 4 bytes */
writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev));
tw_dev->state[request_id] = TW_S_POSTED;
tw_dev->posted_request_count++;
if (tw_dev->posted_request_count > tw_dev->max_posted_request_count)
tw_dev->max_posted_request_count = tw_dev->posted_request_count;
return 0;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 103 | 100.00% | 1 | 100.00% |
Total | 103 | 100.00% | 1 | 100.00% |
/* End twl_post_command_packet() */
/* This function hands scsi cdb's to the firmware */
static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet;
int i, sg_count;
struct scsi_cmnd *srb = NULL;
struct scatterlist *sglist = NULL, *sg;
int retval = 1;
if (tw_dev->srb[request_id]) {
srb = tw_dev->srb[request_id];
if (scsi_sglist(srb))
sglist = scsi_sglist(srb);
}
/* Initialize command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
full_command_packet->header.header_desc.size_header = 128;
full_command_packet->header.status_block.error = 0;
full_command_packet->header.status_block.severity__reserved = 0;
command_packet = &full_command_packet->command.newcommand;
command_packet->status = 0;
command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI);
/* We forced 16 byte cdb use earlier */
if (!cdb)
memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
else
memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
if (srb) {
command_packet->unit = srb->device->id;
command_packet->request_id__lunl =
cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
} else {
command_packet->request_id__lunl =
cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
command_packet->unit = 0;
}
command_packet->sgl_offset = 16;
if (!sglistarg) {
/* Map sglist from scsi layer to cmd packet */
if (scsi_sg_count(srb)) {
sg_count = scsi_dma_map(srb);
if (sg_count <= 0)
goto out;
scsi_for_each_sg(srb, sg, sg_count, i) {
command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg));
command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg));
}
command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id])));
}
} else {
/* Internal cdb post */
for (i = 0; i < use_sg; i++) {
command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length);
}
command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
}
/* Update some stats */
if (srb) {
tw_dev->sector_count = scsi_bufflen(srb) / 512;
if (tw_dev->sector_count > tw_dev->max_sector_count)
tw_dev->max_sector_count = tw_dev->sector_count;
tw_dev->sgl_entries = scsi_sg_count(srb);
if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
tw_dev->max_sgl_entries = tw_dev->sgl_entries;
}
/* Now post the command to the board */
retval = twl_post_command_packet(tw_dev, request_id);
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 508 | 99.41% | 1 | 50.00% |
Christoph Hellwig | 3 | 0.59% | 1 | 50.00% |
Total | 511 | 100.00% | 2 | 100.00% |
/* End twl_scsiop_execute_scsi() */
/* This function will read the aen queue from the isr */
static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry_ISO sglist[1];
TW_Command_Full *full_command_packet;
int retval = 1;
full_command_packet = tw_dev->command_packet_virt[request_id];
memset(full_command_packet, 0, sizeof(TW_Command_Full));
/* Initialize cdb */
memset(&cdb, 0, TW_MAX_CDB_LEN);
cdb[0] = REQUEST_SENSE; /* opcode */
cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
/* Initialize sglist */
memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
sglist[0].length = TW_SECTOR_SIZE;
sglist[0].address = tw_dev->generic_buffer_phys[request_id];
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command packet */
if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue");
goto out;
}
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 172 | 100.00% | 1 | 100.00% |
Total | 172 | 100.00% | 1 | 100.00% |
/* End twl_aen_read_queue() */
/* This function will sync firmware time with the host time */
static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
struct timeval utc;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
u32 local_time;
/* Fill out the command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
memset(full_command_packet, 0, sizeof(TW_Command_Full));
command_packet = &full_command_packet->command.oldcommand;
command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
command_packet->request_id = request_id;
command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
command_packet->size = TW_COMMAND_SIZE;
command_packet->byte6_offset.parameter_count = cpu_to_le16(1);
/* Setup the param */
param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
memset(param, 0, TW_SECTOR_SIZE);
param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */
param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
param->parameter_size_bytes = cpu_to_le16(4);
/* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
do_gettimeofday(&utc);
local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
schedulertime = local_time - (3 * 86400);
schedulertime = cpu_to_le32(schedulertime % 604800);
memcpy(param->data, &schedulertime, sizeof(u32));
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command */
twl_post_command_packet(tw_dev, request_id);
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 273 | 100.00% | 1 | 100.00% |
Total | 273 | 100.00% | 1 | 100.00% |
/* End twl_aen_sync_time() */
/* This function will assign an available request id */
static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
{
*request_id = tw_dev->free_queue[tw_dev->free_head];
tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
tw_dev->state[*request_id] = TW_S_STARTED;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 50 | 100.00% | 1 | 100.00% |
Total | 50 | 100.00% | 1 | 100.00% |
/* End twl_get_request_id() */
/* This function will free a request id */
static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id)
{
tw_dev->free_queue[tw_dev->free_tail] = request_id;
tw_dev->state[request_id] = TW_S_FINISHED;
tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 47 | 100.00% | 1 | 100.00% |
Total | 47 | 100.00% | 1 | 100.00% |
/* End twl_free_request_id() */
/* This function will complete an aen request from the isr */
static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Command_Apache_Header *header;
unsigned short aen;
int retval = 1;
header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
tw_dev->posted_request_count--;
aen = le16_to_cpu(header->status_block.error);
full_command_packet = tw_dev->command_packet_virt[request_id];
command_packet = &full_command_packet->command.oldcommand;
/* First check for internal completion of set param for time sync */
if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {
/* Keep reading the queue in case there are more aen's */
if (twl_aen_read_queue(tw_dev, request_id))
goto out2;
else {
retval = 0;
goto out;
}
}
switch (aen) {
case TW_AEN_QUEUE_EMPTY:
/* Quit reading the queue if this is the last one */
break;
case TW_AEN_SYNC_TIME_WITH_HOST:
twl_aen_sync_time(tw_dev, request_id);
retval = 0;
goto out;
default:
twl_aen_queue_event(tw_dev, header);
/* If there are more aen's, keep reading the queue */
if (twl_aen_read_queue(tw_dev, request_id))
goto out2;
else {
retval = 0;
goto out;
}
}
retval = 0;
out2:
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 214 | 100.00% | 1 | 100.00% |
Total | 214 | 100.00% | 1 | 100.00% |
/* End twl_aen_complete() */
/* This function will poll for a response */
static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{
unsigned long before;
dma_addr_t mfa;
u32 regh, regl;
u32 response;
int retval = 1;
int found = 0;
before = jiffies;
while (!found) {
if (sizeof(dma_addr_t) > 4) {
regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
mfa = ((u64)regh << 32) | regl;
} else
mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
response = (u32)mfa;
if (TW_RESID_OUT(response) == request_id)
found = 1;
if (time_after(jiffies, before + HZ * seconds))
goto out;
msleep(50);
}
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 157 | 100.00% | 1 | 100.00% |
Total | 157 | 100.00% | 1 | 100.00% |
/* End twl_poll_response() */
/* This function will drain the aen queue */
static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{
int request_id = 0;
char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry_ISO sglist[1];
int finished = 0, count = 0;
TW_Command_Full *full_command_packet;
TW_Command_Apache_Header *header;
unsigned short aen;
int first_reset = 0, queue = 0, retval = 1;
if (no_check_reset)
first_reset = 0;
else
first_reset = 1;
full_command_packet = tw_dev->command_packet_virt[request_id];
memset(full_command_packet, 0, sizeof(TW_Command_Full));
/* Initialize cdb */
memset(&cdb, 0, TW_MAX_CDB_LEN);
cdb[0] = REQUEST_SENSE; /* opcode */
cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
/* Initialize sglist */
memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
sglist[0].length = TW_SECTOR_SIZE;
sglist[0].address = tw_dev->generic_buffer_phys[request_id];
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
do {
/* Send command to the board */
if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense");
goto out;
}
/* Now poll for completion */
if (twl_poll_response(tw_dev, request_id, 30)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue");
tw_dev->posted_request_count--;
goto out;
}
tw_dev->posted_request_count--;
header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
aen = le16_to_cpu(header->status_block.error);
queue = 0;
count++;
switch (aen) {
case TW_AEN_QUEUE_EMPTY:
if (first_reset != 1)
goto out;
else
finished = 1;
break;
case TW_AEN_SOFT_RESET:
if (first_reset == 0)
first_reset = 1;
else
queue = 1;
break;
case TW_AEN_SYNC_TIME_WITH_HOST:
break;
default:
queue = 1;
}
/* Now queue an event info */
if (queue)
twl_aen_queue_event(tw_dev, header);
} while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
if (count == TW_MAX_AEN_DRAIN)
goto out;
retval = 0;
out:
tw_dev->state[request_id] = TW_S_INITIAL;
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 386 | 100.00% | 1 | 100.00% |
Total | 386 | 100.00% | 1 | 100.00% |
/* End twl_aen_drain_queue() */
/* This function will allocate memory and check if it is correctly aligned */
static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{
int i;
dma_addr_t dma_handle;
unsigned long *cpu_addr;
int retval = 1;
cpu_addr = pci_zalloc_consistent(tw_dev->tw_pci_dev, size * TW_Q_LENGTH,
&dma_handle);
if (!cpu_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
goto