/* This function will complete an aen request from the isr */ staticint 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; unsignedshort aen; int retval = 1;
/* 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;
} /* End twa_aen_complete() */
/* This function will drain aen queue */ staticint twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{ int request_id = 0; unsignedchar 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; unsignedshort aen; int first_reset = 0, queue = 0, retval = 1;
if (no_check_reset)
first_reset = 0; else
first_reset = 1;
if (tw_dev->generic_buffer_phys[request_id] & 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;
}
/* This function will read the aen queue from the isr */ staticint twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar cdb[TW_MAX_CDB_LEN];
TW_SG_Entry sglist[1];
TW_Command_Full *full_command_packet; int retval = 1;
/* This function will sync firmware time with the host time */ staticvoid twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
time64_t local_time;
/* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime);
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
/* Now post the command */
twa_post_command_packet(tw_dev, request_id, 1);
} /* End twa_aen_sync_time() */
/* This function will allocate memory and check if it is correctly aligned */ staticint twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{ int i;
dma_addr_t dma_handle; unsignedlong *cpu_addr; int retval = 1;
/* This function will check the srl and decide if we are compatible */ staticint twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
{ int retval = 1; unsignedshort fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; unsignedshort fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
u32 init_connect_result = 0;
/* Now copy down the entire ioctl */ if (copy_from_user(tw_ioctl, argp, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length)) goto out3;
/* See which ioctl we are doing */ switch (cmd) { case TW_IOCTL_FIRMWARE_PASS_THROUGH:
spin_lock_irqsave(tw_dev->host->host_lock, flags);
twa_get_request_id(tw_dev, &request_id);
/* Flag internal command */
tw_dev->srb[request_id] = NULL;
/* Flag chrdev ioctl */
tw_dev->chrdev_request_id = request_id;
/* Load request id and sglist for both command types */
twa_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
/* Now post the command packet to the controller */
twa_post_command_packet(tw_dev, request_id, 1);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
/* Now wait for command to complete */
timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
/* We timed out, and didn't get an interrupt */ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */
printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
tw_dev->host->host_no, TW_DRIVER, 0x37,
cmd);
retval = TW_IOCTL_ERROR_OS_EIO;
twa_reset_device_extension(tw_dev); goto out3;
}
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
/* Now copy the entire response to userspace */ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0)
retval = 0;
out3: /* Now free ioctl buf memory */
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Ioctl_Buf_Apache) + data_buffer_length_adjusted,
cpu_addr, dma_handle);
out2:
mutex_unlock(&tw_dev->ioctl_lock);
out:
mutex_unlock(&twa_chrdev_mutex); return retval;
} /* End twa_chrdev_ioctl() */
/* This function handles open for the character device */ /* NOTE that this function will race with remove. */ staticint twa_chrdev_open(struct inode *inode, struct file *file)
{ unsignedint minor_number; int retval = TW_IOCTL_ERROR_OS_ENODEV;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EACCES; goto out;
}
minor_number = iminor(inode); if (minor_number >= twa_device_extension_count) goto out;
retval = 0;
out: return retval;
} /* End twa_chrdev_open() */
/* This function will print readable messages from status register errors */ staticint twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
{ int retval = 1;
/* Check for various error conditions and handle them appropriately */ if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "PCI Parity Error: clearing");
writel(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
}
/* This function will empty the response queue */ staticint twa_empty_response_queue(TW_Device_Extension *tw_dev)
{
u32 status_reg_value; int count = 0, retval = 1;
/* This function will clear the pchip/response queue on 9550SX */ staticint twa_empty_response_queue_large(TW_Device_Extension *tw_dev)
{
u32 response_que_value = 0; unsignedlong before; int retval = 1;
if (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9000) {
before = jiffies; while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) {
response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev));
msleep(1); if (time_after(jiffies, before + HZ * 30)) goto out;
} /* P-chip settle time */
msleep(500);
retval = 0;
} else
retval = 0;
out: return retval;
} /* End twa_empty_response_queue_large() */
/* This function passes sense keys from firmware to scsi layer */ staticint twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
{
TW_Command_Full *full_command_packet; unsignedshort error; int retval = 1; char *error_str;
/* This function will free up device extension resources */ staticvoid twa_free_device_extension(TW_Device_Extension *tw_dev)
{ if (tw_dev->command_packet_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Command_Full) * TW_Q_LENGTH,
tw_dev->command_packet_virt[0],
tw_dev->command_packet_phys[0]);
if (tw_dev->generic_buffer_virt[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev,
TW_SECTOR_SIZE * TW_Q_LENGTH,
tw_dev->generic_buffer_virt[0],
tw_dev->generic_buffer_phys[0]);
kfree(tw_dev->event_queue[0]);
} /* End twa_free_device_extension() */
/* This function will free a request id */ staticvoid twa_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;
} /* End twa_free_request_id() */
/* This function will get parameter table entries from the firmware */ staticvoid *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
{
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param; void *retval = NULL;
/* Post the command packet to the board */
twa_post_command_packet(tw_dev, request_id, 1);
/* Poll for completion */ if (twa_poll_response(tw_dev, request_id, 30))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param") else
retval = (void *)&(param->data[0]);
/* This function will assign an available request id */ staticvoid twa_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;
} /* End twa_get_request_id() */
/* This function will send an initconnection command to controller */ staticint twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
u32 set_features, unsignedshort current_fw_srl, unsignedshort current_fw_arch_id, unsignedshort current_fw_branch, unsignedshort current_fw_build, unsignedshort *fw_on_ctlr_srl, unsignedshort *fw_on_ctlr_arch_id, unsignedshort *fw_on_ctlr_branch, unsignedshort *fw_on_ctlr_build,
u32 *init_connect_result)
{
TW_Command_Full *full_command_packet;
TW_Initconnect *tw_initconnect; int request_id = 0, retval = 1;
/* This function will initialize the fields of a device extension */ staticint twa_initialize_device_extension(TW_Device_Extension *tw_dev)
{ int i, retval = 1;
/* Handle command interrupt */ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
TW_MASK_COMMAND_INTERRUPT(tw_dev); /* Drain as many pending commands as we can */ while (tw_dev->pending_request_count > 0) {
request_id = tw_dev->pending_queue[tw_dev->pending_head]; if (tw_dev->state[request_id] != TW_S_PENDING) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
} if (twa_post_command_packet(tw_dev, request_id, 1)==0) {
tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;
tw_dev->pending_request_count--;
} else { /* If we get here, we will continue re-posting on the next command interrupt */ break;
}
}
}
/* Handle response interrupt */ if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
/* Drain the response queue from the board */ while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) { /* Complete the response */
response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_que.response_id);
full_command_packet = tw_dev->command_packet_virt[request_id];
error = 0; /* Check for command packet errors */ if (full_command_packet->command.newcommand.status != 0) { if (tw_dev->srb[request_id] != NULL) {
error = twa_fill_sense(tw_dev, request_id, 1, 1);
} else { /* Skip ioctl error prints */ if (request_id != tw_dev->chrdev_request_id) {
error = twa_fill_sense(tw_dev, request_id, 0, 1);
}
}
}
/* Check for correct state */ if (tw_dev->state[request_id] != TW_S_POSTED) { if (tw_dev->srb[request_id] != NULL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
}
}
/* Check for internal command completion */ if (tw_dev->srb[request_id] == NULL) { if (request_id != tw_dev->chrdev_request_id) { if (twa_aen_complete(tw_dev, request_id))
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
wake_up(&tw_dev->ioctl_wqueue);
}
} else { struct scsi_cmnd *cmd;
cmd = tw_dev->srb[request_id];
twa_scsiop_execute_scsi_complete(tw_dev, request_id); /* If no error command was a success */ if (error == 0) {
cmd->result = (DID_OK << 16);
}
/* If error, command failed */ if (error == 1) { /* Ask for a host reset */
cmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
}
/* Report residual bytes for single sgl */ if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
u32 length = le32_to_cpu(full_command_packet->command.newcommand.sg_list[0].length);
if (length < scsi_bufflen(cmd))
scsi_set_resid(cmd, scsi_bufflen(cmd) - length);
}
/* Now complete the io */ if (twa_command_mapped(cmd))
scsi_dma_unmap(cmd);
scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
}
/* Check for valid status after each drain */
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); if (twa_check_bits(status_reg_value)) { if (twa_decode_bits(tw_dev, status_reg_value)) {
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto twa_interrupt_bail;
}
}
}
}
twa_interrupt_bail:
spin_unlock(tw_dev->host->host_lock); return IRQ_RETVAL(handled);
} /* End twa_interrupt() */
/* This function will load the request id and various sgls for ioctls */ staticvoid twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
{
TW_Command *oldcommand;
TW_Command_Apache *newcommand;
TW_SG_Entry *sgl; unsignedint pae = 0;
if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { /* Load the sg list */ if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)
sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry)/4) + pae); else
sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));
sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
sgl->length = cpu_to_le32(length);
oldcommand->size += pae;
}
}
} /* End twa_load_sgl() */
/* This function will poll for a response interrupt of a request */ staticint twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{ int retval = 1, found = 0, response_request_id;
TW_Response_Queue response_queue;
TW_Command_Full *full_command_packet = tw_dev->command_packet_virt[request_id];
if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {
response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
response_request_id = TW_RESID_OUT(response_queue.response_id); if (request_id != response_request_id) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response"); goto out;
} if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { if (full_command_packet->command.newcommand.status != 0) { /* bad response */
twa_fill_sense(tw_dev, request_id, 0, 0); goto out;
}
found = 1;
} else { if (full_command_packet->command.oldcommand.status != 0) { /* bad response */
twa_fill_sense(tw_dev, request_id, 0, 0); goto out;
}
found = 1;
}
}
if (found)
retval = 0;
out: return retval;
} /* End twa_poll_response() */
/* This function will poll the status register for a flag */ staticint twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
while ((status_reg_value & flag) != flag) {
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will poll the status register for disappearance of a flag */ staticint twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
while ((status_reg_value & flag) != 0) {
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); if (twa_check_bits(status_reg_value))
twa_decode_bits(tw_dev, status_reg_value);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will attempt to post a command packet to the board */ staticint twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal)
{
u32 status_reg_value;
dma_addr_t command_que_value; int retval = 1;
/* Only pend internal driver commands */ if (!internal) {
retval = SCSI_MLQUEUE_HOST_BUSY; goto out;
}
/* Couldn't post the command packet, so we do it later */ if (tw_dev->state[request_id] != TW_S_PENDING) {
tw_dev->state[request_id] = TW_S_PENDING;
tw_dev->pending_request_count++; if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
tw_dev->max_pending_request_count = tw_dev->pending_request_count;
}
tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;
}
TW_UNMASK_COMMAND_INTERRUPT(tw_dev); goto out;
} else { if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) ||
(tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { /* Now write upper 4 bytes */
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4);
} else { if (sizeof(dma_addr_t) > 4) {
command_que_value += TW_COMMAND_OFFSET;
writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4);
} else {
writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_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;
}
}
retval = 0;
out: return retval;
} /* End twa_post_command_packet() */
/* This function will reset a device extension */ staticint twa_reset_device_extension(TW_Device_Extension *tw_dev)
{ int i = 0; int retval = 1; unsignedlong flags = 0;
/* Abort all requests that are in progress */ for (i = 0; i < TW_Q_LENGTH; i++) { if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) { if (tw_dev->srb[i]) { struct scsi_cmnd *cmd = tw_dev->srb[i];
/* This function will reset a controller */ staticint twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
{ int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;
while (tries < TW_MAX_RESET_TRIES) { if (do_soft_reset) {
TW_SOFT_RESET(tw_dev); /* Clear pchip/response queue on 9550SX */ if (twa_empty_response_queue_large(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x36, "Response queue (large) empty failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
}
/* Make sure controller is in a good state */ if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 60)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
/* Check for compatibility/flash */ if (twa_check_srl(tw_dev, &flashed)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Compatibility check failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
} else { if (flashed) {
tries++; continue;
}
}
/* Drain the AEN queue */ if (twa_aen_drain_queue(tw_dev, soft_reset)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x22, "AEN drain failed during reset sequence");
do_soft_reset = 1;
tries++; continue;
}
/* If we got here, controller is in a good state */
retval = 0; goto out;
}
out: return retval;
} /* End twa_reset_sequence() */
/* This funciton returns unit geometry in cylinders/heads/sectors */ staticint twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
{ int heads, sectors, cylinders;
/* This is the new scsi eh reset function */ staticint twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
{
TW_Device_Extension *tw_dev = NULL; int retval = FAILED;
/* Make sure we are not issuing an ioctl or resetting from ioctl */
mutex_lock(&tw_dev->ioctl_lock);
/* Now reset the card and some of the device extension data */ if (twa_reset_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); goto out;
}
/* This is the main scsi queue function to handle scsi opcodes */ staticint twa_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{ void (*done)(struct scsi_cmnd *) = scsi_done; int request_id, retval;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
/* If we are resetting due to timed out ioctl, report as busy */ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
retval = SCSI_MLQUEUE_HOST_BUSY; goto out;
}
/* Check if this FW supports luns */ if ((SCpnt->device->lun != 0) && (tw_dev->tw_compat_info.working_srl < TW_FW_SRL_LUNS_SUPPORTED)) {
SCpnt->result = (DID_BAD_TARGET << 16);
done(SCpnt);
retval = 0; goto out;
}
/* Get a free request id */
twa_get_request_id(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
/* This function hands scsi cdb's to the firmware */ staticint twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, unsignedchar *cdb, int use_sg,
TW_SG_Entry *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet;
u32 num_sectors = 0x0; int i, sg_count; struct scsi_cmnd *srb = NULL; struct scatterlist *sg; int retval = 1;
if (tw_dev->srb[request_id])
srb = tw_dev->srb[request_id];
/* Update SG statistics */ if (srb) {
tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]); 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 */ if (srb) {
retval = twa_post_command_packet(tw_dev, request_id, 0);
} else {
twa_post_command_packet(tw_dev, request_id, 1);
retval = 0;
}
out: return retval;
} /* End twa_scsiop_execute_scsi() */
/* This function completes an execute scsi operation */ staticvoid twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
{ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
if (!twa_command_mapped(cmd) &&
(cmd->sc_data_direction == DMA_FROM_DEVICE ||
cmd->sc_data_direction == DMA_BIDIRECTIONAL)) { if (scsi_sg_count(cmd) == 1) { void *buf = tw_dev->generic_buffer_virt[request_id];
scsi_sg_copy_from_buffer(cmd, buf, TW_SECTOR_SIZE);
}
}
} /* End twa_scsiop_execute_scsi_complete() */
/* This function tells the controller to shut down */ staticvoid __twa_shutdown(TW_Device_Extension *tw_dev)
{ /* Disable interrupts */
TW_DISABLE_INTERRUPTS(tw_dev);
/* Free up the IRQ */
free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
/* Tell the card we are shutting down */ if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x31, "Connection shutdown failed");
} else {
printk(KERN_WARNING "3w-9xxx: Shutdown complete.\n");
}
/* Clear all interrupts just before exit */
TW_CLEAR_ALL_INTERRUPTS(tw_dev);
} /* End __twa_shutdown() */
__twa_shutdown(tw_dev);
} /* End twa_shutdown() */
/* This function will look up a string */ staticchar *twa_string_lookup(twa_message_type *table, unsignedint code)
{ int index;
for (index = 0; ((code != table[index].code) &&
(table[index].text != (char *)0)); index++); return(table[index].text);
} /* End twa_string_lookup() */
/* This function gets called when a disk is coming on-line */ staticint twa_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{ /* Force 60 second timeout */
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
/* This function will probe and initialize a card */ staticint twa_probe(struct pci_dev *pdev, conststruct pci_device_id *dev_id)
{ struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev; unsignedlong mem_addr, mem_len; int retval;
retval = pci_enable_device(pdev); if (retval) {
TW_PRINTK(host, TW_DRIVER, 0x34, "Failed to enable pci device"); return -ENODEV;
}
pci_set_master(pdev);
pci_try_set_mwi(pdev);
retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (retval)
retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (retval) {
TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
retval = -ENODEV; goto out_disable_device;
}
host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) {
TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
retval = -ENOMEM; goto out_disable_device;
}
tw_dev = (TW_Device_Extension *)host->hostdata;
/* Save values to device extension */
tw_dev->host = host;
tw_dev->tw_pci_dev = pdev;
if (twa_initialize_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
retval = -ENOMEM; goto out_free_device_extension;
}
/* Request IO regions */
retval = pci_request_regions(pdev, "3w-9xxx"); if (retval) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Failed to get mem region"); goto out_free_device_extension;
}
/* This function is called to remove a device */ staticvoid twa_remove(struct pci_dev *pdev)
{ struct Scsi_Host *host = pci_get_drvdata(pdev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
scsi_remove_host(tw_dev->host);
/* Unregister character device */ if (twa_major >= 0) {
unregister_chrdev(twa_major, "twa");
twa_major = -1;
}
/* Shutdown the card */
__twa_shutdown(tw_dev);
/* Disable MSI if enabled */ if (test_bit(TW_USING_MSI, &tw_dev->flags))
pci_disable_msi(pdev);
/* This function is called on driver initialization */ staticint __init twa_init(void)
{
printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
return pci_register_driver(&twa_driver);
} /* End twa_init() */
/* This function is called on driver exit */ staticvoid __exit twa_exit(void)
{
pci_unregister_driver(&twa_driver);
} /* End twa_exit() */
module_init(twa_init);
module_exit(twa_exit);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.58 Sekunden
(vorverarbeitet am 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.