Release 4.11 drivers/scsi/3w-9xxx.c
/*
3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
Written By: Adam Radford <aradford@gmail.com>
Modifications By: Tom Couch
Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
Copyright (C) 2010 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
Bugs/Comments/Suggestions should be mailed to:
aradford@gmail.com
Note: This version of the driver does not contain a bundled firmware
image.
History
-------
2.26.02.000 - Driver cleanup for kernel submission.
2.26.02.001 - Replace schedule_timeout() calls with msleep().
2.26.02.002 - Add support for PAE mode.
Add lun support.
Fix twa_remove() to free irq handler/unregister_chrdev()
before shutting down card.
Change to new 'change_queue_depth' api.
Fix 'handled=1' ISR usage, remove bogus IRQ check.
Remove un-needed eh_abort handler.
Add support for embedded firmware error strings.
2.26.02.003 - Correctly handle single sgl's with use_sg=1.
2.26.02.004 - Add support for 9550SX controllers.
2.26.02.005 - Fix use_sg == 0 mapping on systems with 4GB or higher.
2.26.02.006 - Fix 9550SX pchip reset timeout.
Add big endian support.
2.26.02.007 - Disable local interrupts during kmap/unmap_atomic().
2.26.02.008 - Free irq handler in __twa_shutdown().
Serialize reset code.
Add support for 9650SE controllers.
2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails.
2.26.02.010 - Add support for 9690SA controllers.
2.26.02.011 - Increase max AENs drained to 256.
Add MSI support and "use_msi" module parameter.
Fix bug in twa_get_param() on 4GB+.
Use pci_resource_len() for ioremap().
2.26.02.012 - Add power management support.
2.26.02.013 - Fix bug in twa_load_sgl().
2.26.02.014 - Force 60 second timeout default.
*/
#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-9xxx.h"
/* Globals */
#define TW_DRIVER_VERSION "2.26.02.014"
static DEFINE_MUTEX(twa_chrdev_mutex);
static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
static unsigned int twa_device_extension_count;
static int twa_major = -1;
extern struct timezone sys_tz;
/* Module parameters */
MODULE_AUTHOR ("LSI");
MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(TW_DRIVER_VERSION);
static int use_msi = 0;
module_param(use_msi, int, S_IRUGO);
MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0");
/* Function prototypes */
static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
static char *twa_aen_severity_lookup(unsigned char severity_code);
static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static int twa_chrdev_open(struct inode *inode, struct file *file);
static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
u32 set_features, unsigned short current_fw_srl,
unsigned short current_fw_arch_id,
unsigned short current_fw_branch,
unsigned short current_fw_build,
unsigned short *fw_on_ctlr_srl,
unsigned short *fw_on_ctlr_arch_id,
unsigned short *fw_on_ctlr_branch,
unsigned short *fw_on_ctlr_build,
u32 *init_connect_result);
static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg);
static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
/* Functions */
/* Show some statistics about the card */
static ssize_t twa_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-9xxx Driver version: %s\n"
"Current commands posted: %4d\n"
"Max commands posted: %4d\n"
"Current pending commands: %4d\n"
"Max pending commands: %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->pending_request_count,
tw_dev->max_pending_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 | 92.81% | 1 | 33.33% |
Tony Jones | 8 | 5.76% | 1 | 33.33% |
James Bottomley | 2 | 1.44% | 1 | 33.33% |
Total | 139 | 100.00% | 3 | 100.00% |
/* End twa_show_stats() */
/* Create sysfs 'stats' entry */
static struct device_attribute twa_host_stats_attr = {
.attr = {
.name = "stats",
.mode = S_IRUGO,
},
.show = twa_show_stats
};
/* Host attributes initializer */
static struct device_attribute *twa_host_attrs[] = {
&twa_host_stats_attr,
NULL,
};
/* File operations struct for character device */
static const struct file_operations twa_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = twa_chrdev_ioctl,
.open = twa_chrdev_open,
.release = NULL,
.llseek = noop_llseek,
};
/*
* The controllers use an inline buffer instead of a mapped SGL for small,
* single entry buffers. Note that we treat a zero-length transfer like
* a mapped SGL.
*/
static bool twa_command_mapped(struct scsi_cmnd *cmd)
{
return scsi_sg_count(cmd) != 1 ||
scsi_bufflen(cmd) >= TW_MIN_SGL_LENGTH;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christoph Hellwig | 26 | 100.00% | 1 | 100.00% |
Total | 26 | 100.00% | 1 | 100.00% |
/* This function will complete an aen request from the isr */
static int twa_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 (twa_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:
twa_aen_sync_time(tw_dev, request_id);
retval = 0;
goto out;
default:
twa_aen_queue_event(tw_dev, header);
/* If there are more aen's, keep reading the queue */
if (twa_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;
twa_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 | 210 | 98.13% | 2 | 66.67% |
Andrew Morton | 4 | 1.87% | 1 | 33.33% |
Total | 214 | 100.00% | 3 | 100.00% |
/* End twa_aen_complete() */
/* This function will drain aen queue */
static int twa_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 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));
sglist[0].length = TW_SECTOR_SIZE;
sglist[0].address = tw_dev->generic_buffer_phys[request_id];
if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");
goto out;
}
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
do {
/* Send command to the board */
if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense");
goto out;
}
/* Now poll for completion */
if (twa_poll_response(tw_dev, request_id, 30)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "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)
twa_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 | 413 | 99.52% | 2 | 66.67% |
James Bottomley | 2 | 0.48% | 1 | 33.33% |
Total | 415 | 100.00% | 3 | 100.00% |
/* End twa_aen_drain_queue() */
/* This function will queue an event */
static void twa_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];
/* Check for clobber */
host[0] = '\0';
if (tw_dev->host) {
sprintf(host, " scsi%d:", tw_dev->host->host_no);
if (event->retrieved == TW_AEN_NOT_RETRIEVED)
tw_dev->aen_clobber = 1;
}
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 + (error_str[0] == '\0' ? 0 : (1 + strlen(error_str))));
if (event->severity != TW_AEN_SEVERITY_DEBUG)
printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
host,
twa_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen,
error_str[0] == '\0' ? twa_string_lookup(twa_aen_table, aen) : error_str,
header->err_specific_desc);
else
tw_dev->aen_count--;
if ((tw_dev->error_index + 1) == TW_Q_LENGTH)
tw_dev->event_queue_wrapped = 1;
tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 322 | 88.71% | 2 | 50.00% |
James Bottomley | 33 | 9.09% | 1 | 25.00% |
Andrew Morton | 8 | 2.20% | 1 | 25.00% |
Total | 363 | 100.00% | 4 | 100.00% |
/* End twa_aen_queue_event() */
/* This function will read the aen queue from the isr */
static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry 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));
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 (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "Post failed while reading AEN queue");
goto out;
}
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 170 | 98.84% | 1 | 50.00% |
James Bottomley | 2 | 1.16% | 1 | 50.00% |
Total | 172 | 100.00% | 2 | 100.00% |
/* End twa_aen_read_queue() */
/* This function will look up an AEN severity string */
static char *twa_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 = twa_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 twa_aen_severity_lookup() */
/* This function will sync firmware time with the host time */
static void twa_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 = cpu_to_le32(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 */
twa_post_command_packet(tw_dev, request_id, 1);
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 268 | 97.45% | 2 | 66.67% |
Andrew Morton | 7 | 2.55% | 1 | 33.33% |
Total | 275 | 100.00% | 3 | 100.00% |
/* End twa_aen_sync_time() */
/* This function will allocate memory and check if it is correctly aligned */
static int twa_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_alloc_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 out;
}
if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory");
pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
goto out;
}
memset(cpu_addr, 0, size*TW_Q_LENGTH);
for (i = 0; i < TW_Q_LENGTH; i++) {
switch(which) {
case 0:
tw_dev->command_packet_phys[i] = dma_handle+(i*size);
tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size));
break;
case 1:
tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
break;
}
}
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 247 | 99.60% | 1 | 50.00% |
Andrew Morton | 1 | 0.40% | 1 | 50.00% |
Total | 248 | 100.00% | 2 | 100.00% |
/* End twa_allocate_memory() */
/* This function will check the status register for unexpected bits */
static int twa_check_bits(u32 status_reg_value)
{
int retval = 1;
if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS)
goto out;
if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0)
goto out;
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 49 | 100.00% | 1 | 100.00% |
Total | 49 | 100.00% | 1 | 100.00% |
/* End twa_check_bits() */
/* This function will check the srl and decide if we are compatible */
static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
{
int retval = 1;
unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
u32 init_connect_result = 0;
if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL,
TW_9000_ARCH_ID, TW_CURRENT_DRIVER_BRANCH,
TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl,
&fw_on_ctlr_arch_id, &fw_on_ctlr_branch,
&fw_on_ctlr_build, &init_connect_result)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "Initconnection failed while checking SRL");
goto out;
}
tw_dev->tw_compat_info.working_srl = fw_on_ctlr_srl;
tw_dev->tw_compat_info.working_branch = fw_on_ctlr_branch;
tw_dev->tw_compat_info.working_build = fw_on_ctlr_build;
/* Try base mode compatibility */
if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
TW_EXTENDED_INIT_CONNECT,
TW_BASE_FW_SRL, TW_9000_ARCH_ID,
TW_BASE_FW_BRANCH, TW_BASE_FW_BUILD,
&fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
&fw_on_ctlr_branch, &fw_on_ctlr_build,
&init_connect_result)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Initconnection (base mode) failed while checking SRL");
goto out;
}
if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
if (TW_CURRENT_DRIVER_SRL > fw_on_ctlr_srl) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x32, "Firmware and driver incompatibility: please upgrade firmware");
} else {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x33, "Firmware and driver incompatibility: please upgrade driver");
}
goto out;
}
tw_dev->tw_compat_info.working_srl = TW_BASE_FW_SRL;
tw_dev->tw_compat_info.working_branch = TW_BASE_FW_BRANCH;
tw_dev->tw_compat_info.working_build = TW_BASE_FW_BUILD;
}
/* Load rest of compatibility struct */
strlcpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION,
sizeof(tw_dev->tw_compat_info.driver_version));
tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL;
tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH;
tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD;
tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl;
tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch;
tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build;
retval = 0;
out:
return retval;
} Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adam Radford | 347 | 96.12% | 2 | 50.00% |
Rickard Strandqvist | 7 | 1.94% | 1 | 25.00% |
James Bottomley | 7 | 1.94% | 1 | 25.00% |
Total | 361 | 100.00% | 4 | 100.00% |
/* End twa_check_srl() */
/* This function handles ioctl for the character device */
static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(file);
long timeout;
unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
dma_addr_t dma_handle;
int request_id = 0;
unsigned int sequence_id = 0;
unsigned char event_index, start_index;
TW_Ioctl_Driver_Command driver_command;
TW_Ioctl_Buf_Apache *tw_ioctl;
TW_Lock *tw_lock;
TW_Command_Full *full_command_packet;
TW_Compatibility_Info *tw_compat_info;
TW_Event *event;
struct timeval current_time;
u32 current_time_ms;
TW_Device_Extension *tw_dev