/* Function prototypes */ staticint tw_reset_device_extension(TW_Device_Extension *tw_dev);
/* Functions */
/* This function will check the status register for unexpected bits */ staticint tw_check_bits(u32 status_reg_value)
{ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value); return1;
} if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value); return1;
}
return0;
} /* End tw_check_bits() */
/* This function will print readable messages from status register errors */ staticint tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host)
{ char host[16];
/* This function will poll the status register for a flag */ staticint tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (tw_check_bits(status_reg_value))
tw_decode_bits(tw_dev, status_reg_value, 0);
while ((status_reg_value & flag) != flag) {
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
if (tw_check_bits(status_reg_value))
tw_decode_bits(tw_dev, status_reg_value, 0);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will poll the status register for disappearance of a flag */ staticint tw_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
u32 status_reg_value; unsignedlong before; int retval = 1;
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
before = jiffies;
if (tw_check_bits(status_reg_value))
tw_decode_bits(tw_dev, status_reg_value, 0);
while ((status_reg_value & flag) != 0) {
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
if (tw_check_bits(status_reg_value))
tw_decode_bits(tw_dev, status_reg_value, 0);
if (time_after(jiffies, before + HZ * seconds)) goto out;
/* This function will attempt to post a command packet to the board */ staticint tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
{
u32 status_reg_value; unsignedlong command_que_value;
if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) { /* We successfully posted the command packet */
outl(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;
}
} else { /* Couldn't post the command packet, so we do it in the isr */ 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; if (tw_dev->pending_tail == TW_Q_LENGTH-1) {
tw_dev->pending_tail = TW_Q_START;
} else {
tw_dev->pending_tail = tw_dev->pending_tail + 1;
}
}
TW_UNMASK_COMMAND_INTERRUPT(tw_dev); return1;
} return0;
} /* End tw_post_command_packet() */
/* This function will return valid sense buffer information for failed cmds */ staticint tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
{ int i;
TW_Command *command;
printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, TW_UNIT_OUT(command->unit__hostid));
/* Attempt to return intelligent sense information */ if (fill_sense) { if ((command->status == 0xc7) || (command->status == 0xcb)) { for (i = 0; i < ARRAY_SIZE(tw_sense_table); i++) { if (command->flags == tw_sense_table[i][0]) {
/* Valid bit and 'current errors' */
tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
/* Sense key */
tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
/* Additional sense code */
tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
/* Additional sense code qualifier */
tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION; return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
}
}
}
/* If no table match, error so we get a reset */ return1;
}
return0;
} /* End tw_decode_sense() */
/* This function will report controller error status */ staticint tw_check_errors(TW_Device_Extension *tw_dev)
{
u32 status_reg_value;
while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
}
} /* End tw_empty_response_que() */
/* This function will free a request_id */ staticvoid tw_state_request_finish(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 tw_state_request_finish() */
/* This function will assign an available request_id */ staticvoid tw_state_request_start(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 tw_state_request_start() */
/* Show some statistics about the card */ static ssize_t tw_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; unsignedlong flags = 0;
ssize_t len;
/* This function will read the aen queue from the isr */ staticint tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command *command_packet;
TW_Param *param; unsignedlong command_que_value;
u32 status_reg_value; unsignedlong param_value = 0;
/* Now post the command packet */ if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n");
tw_dev->srb[request_id] = NULL; /* Flag internal command */
tw_dev->state[request_id] = TW_S_POSTED;
outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
} else {
printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n"); return1;
}
return0;
} /* End tw_aen_read_queue() */
/* This function will complete an aen request from the isr */ staticint tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Param *param; unsignedshort aen; int error = 0, table_max = 0;
/* This function will drain the aen queue after a soft reset */ staticint tw_aen_drain_queue(TW_Device_Extension *tw_dev)
{
TW_Command *command_packet;
TW_Param *param; int request_id = 0; unsignedlong command_que_value; unsignedlong param_value;
TW_Response_Queue response_queue; unsignedshort aen; unsignedshort aen_code; int finished = 0; int first_reset = 0; int queue = 0; int found = 0, table_max = 0;
/* Now setup the param */ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n"); return1;
}
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
memset(param, 0, sizeof(TW_Sector));
param->table_id = 0x401; /* AEN table */
param->parameter_id = 2; /* Unit code */
param->parameter_size_bytes = 2;
param_value = tw_dev->alignment_physical_address[request_id]; if (param_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n"); return1;
}
command_packet->byte8.param.sgl[0].address = param_value;
command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
/* Now drain the controller's aen queue */ do { /* Post command packet */
outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
/* Now poll for completion */ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_queue.response_id);
if (request_id != 0) { /* Unexpected request id */
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n"); return1;
}
if (command_packet->status != 0) { if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) { /* Bad response */
tw_decode_sense(tw_dev, request_id, 0); return1;
} else { /* We know this is a 3w-1x00, and doesn't support aen's */ return0;
}
}
/* Now put the aen on the aen_queue */ if (queue == 1) {
tw_dev->aen_queue[tw_dev->aen_tail] = aen; if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
tw_dev->aen_tail = TW_Q_START;
} else {
tw_dev->aen_tail = tw_dev->aen_tail + 1;
} if (tw_dev->aen_head == tw_dev->aen_tail) { if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
tw_dev->aen_head = TW_Q_START;
} else {
tw_dev->aen_head = tw_dev->aen_head + 1;
}
}
}
found = 1;
} if (found == 0) {
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n"); return1;
}
} while (finished == 0);
return0;
} /* End tw_aen_drain_queue() */
/* This function will allocate memory */ staticint tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{ int i;
dma_addr_t dma_handle; unsignedlong *cpu_addr = NULL;
mutex_lock(&tw_mutex); /* Only let one of these through at a time */ if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
mutex_unlock(&tw_mutex); return -EINTR;
}
/* First copy down the buffer length */ if (copy_from_user(&data_buffer_length, argp, sizeof(unsignedint))) goto out;
/* See which ioctl we are doing */ switch (cmd) { case TW_OP_NOP:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n"); break; case TW_OP_AEN_LISTEN:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
memset(tw_ioctl->data_buffer, 0, data_buffer_length);
/* Now post the command packet to the controller */
tw_post_command_packet(tw_dev, request_id);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
/* Now wait for the 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-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
retval = -EIO; if (tw_reset_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
} goto out2;
}
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
/* Now copy the response to userspace */ if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length)) goto out2;
retval = 0;
out2: /* Now free ioctl buf memory */
dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_New_Ioctl), cpu_addr, dma_handle);
out:
mutex_unlock(&tw_dev->ioctl_lock);
mutex_unlock(&tw_mutex); return retval;
} /* End tw_chrdev_ioctl() */
/* This function handles open for the character device */ /* NOTE that this function races with remove. */ staticint tw_chrdev_open(struct inode *inode, struct file *file)
{ unsignedint minor_number;
/* This function will free up device extension resources */ staticvoid tw_free_device_extension(TW_Device_Extension *tw_dev)
{
dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
/* Free command packet and generic buffer memory */ if (tw_dev->command_packet_virtual_address[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Command) * TW_Q_LENGTH,
tw_dev->command_packet_virtual_address[0],
tw_dev->command_packet_physical_address[0]);
if (tw_dev->alignment_virtual_address[0])
dma_free_coherent(&tw_dev->tw_pci_dev->dev, sizeof(TW_Sector) * TW_Q_LENGTH,
tw_dev->alignment_virtual_address[0],
tw_dev->alignment_physical_address[0]);
} /* End tw_free_device_extension() */
/* This function will send an initconnection command to controller */ staticint tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
{ unsignedlong command_que_value;
TW_Command *command_packet;
TW_Response_Queue response_queue; int request_id = 0;
if (command_que_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n"); return1;
}
/* Send command packet to the board */
outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
/* Poll for completion */ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_queue.response_id);
if (request_id != 0) { /* unexpected request id */
printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected request id.\n"); return1;
} if (command_packet->status != 0) { /* bad response */
tw_decode_sense(tw_dev, request_id, 0); return1;
}
} return0;
} /* End tw_initconnection() */
/* Set a value in the features table */ staticint tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, unsignedchar *val)
{
TW_Param *param;
TW_Command *command_packet;
TW_Response_Queue response_queue; int request_id = 0; unsignedlong command_que_value; unsignedlong param_value;
command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n"); return1;
}
/* Send command packet to the board */
outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
/* Poll for completion */ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_queue.response_id);
if (request_id != 0) { /* unexpected request id */
printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n"); return1;
} if (command_packet->status != 0) { /* bad response */
tw_decode_sense(tw_dev, request_id, 0); return1;
}
}
return0;
} /* End tw_setfeature() */
/* This function will reset a controller */ staticint tw_reset_sequence(TW_Device_Extension *tw_dev)
{ int error = 0; int tries = 0; unsignedchar c = 1;
/* Reset the board */ while (tries < TW_MAX_RESET_TRIES) {
TW_SOFT_RESET(tw_dev);
error = tw_setfeature(tw_dev, 2, 1, &c); if (error) {
printk(KERN_WARNING "3w-xxxx: Unable to set features for card, probable old firmware or card.\n");
}
return0;
} /* End tw_reset_sequence() */
/* This function will initialize the fields of a device extension */ staticint tw_initialize_device_extension(TW_Device_Extension *tw_dev)
{ int i, error=0;
return0;
} /* End tw_initialize_device_extension() */
/* This function will reset a device extension */ staticint tw_reset_device_extension(TW_Device_Extension *tw_dev)
{ int i = 0; struct scsi_cmnd *srb; unsignedlong flags = 0;
/* This funciton returns unit geometry in cylinders/heads/sectors */ staticint tw_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 tw_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 (tw_reset_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Reset failed.\n", tw_dev->host->host_no); goto out;
}
/* This function is called by the isr to complete an inquiry command */ staticint tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar *is_unit_present; unsignedchar request_buffer[36];
TW_Param *param;
/* Now read firmware cache setting for this unit */
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; if (command_packet == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet virtual address.\n"); return1;
}
/* Now try to post the command packet */
tw_post_command_packet(tw_dev, request_id);
return0;
} /* End tw_scsiop_mode_sense() */
/* This function is called by the isr to complete a mode sense command */ staticint tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Param *param; unsignedchar *flags; unsignedchar request_buffer[8];
/* Now try to post the command to the board */
tw_post_command_packet(tw_dev, request_id);
return0;
} /* End tw_scsiop_read_capacity() */
/* This function is called by the isr to complete a readcapacity command */ staticint tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar *param_data;
u32 capacity; char buff[8];
TW_Param *param;
command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n"); return1;
}
/* Now try to post the command to the board */
tw_post_command_packet(tw_dev, request_id);
return0;
} /* End tw_scsiop_read_write() */
/* This function will handle the request sense scsi command */ staticint tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
{ char request_buffer[18];
/* If we got a request_sense, we probably want a reset, return error */
tw_dev->srb[request_id]->result = (DID_ERROR << 16);
scsi_done(tw_dev->srb[request_id]);
return0;
} /* End tw_scsiop_request_sense() */
/* This function will handle synchronize cache scsi command */ staticint tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command *command_packet; unsignedlong command_que_value;
/* Now try to post the command packet */
tw_post_command_packet(tw_dev, request_id);
return0;
} /* End tw_scsiop_synchronize_cache() */
/* This function will handle test unit ready scsi command */ staticint tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
{
TW_Param *param;
TW_Command *command_packet; unsignedlong command_que_value; unsignedlong param_value;
/* Now try to post the command packet */
tw_post_command_packet(tw_dev, request_id);
return0;
} /* End tw_scsiop_test_unit_ready() */
/* This function is called by the isr to complete a testunitready command */ staticint tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
{ unsignedchar *is_unit_present;
TW_Param *param;
/* This function is the interrupt service routine */ static irqreturn_t tw_interrupt(int irq, void *dev_instance)
{ int request_id;
u32 status_reg_value;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
TW_Response_Queue response_que; int error = 0, retval = 0;
TW_Command *command_packet; int handled = 0;
/* Get the host lock for io completions */
spin_lock(tw_dev->host->host_lock);
/* Read the registers */
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
/* Check if this is our interrupt, otherwise bail */ if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT)) goto tw_interrupt_bail;
handled = 1;
/* If we are resetting, bail */ if (test_bit(TW_IN_RESET, &tw_dev->flags)) goto tw_interrupt_bail;
/* Check controller for errors */ if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto tw_interrupt_bail;
}
}
/* Handle host interrupt */ if (status_reg_value & TW_STATUS_HOST_INTERRUPT) {
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
TW_CLEAR_HOST_INTERRUPT(tw_dev);
}
/* Handle command interrupt */ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) { /* 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) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Found request id that wasn't pending.\n", tw_dev->host->host_no); break;
} if (tw_post_command_packet(tw_dev, request_id)==0) { if (tw_dev->pending_head == TW_Q_LENGTH-1) {
tw_dev->pending_head = TW_Q_START;
} else {
tw_dev->pending_head = tw_dev->pending_head + 1;
}
tw_dev->pending_request_count--;
} else { /* If we get here, we will continue re-posting on the next command interrupt */ break;
}
} /* If there are no more pending requests, we mask command interrupt */ if (tw_dev->pending_request_count == 0)
TW_MASK_COMMAND_INTERRUPT(tw_dev);
}
/* 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) { /* Read response queue register */
response_que.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
request_id = TW_RESID_OUT(response_que.response_id);
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
error = 0;
/* Check for bad response */ if (command_packet->status != 0) { /* If internal command, don't error, don't fill sense */ if (tw_dev->srb[request_id] == NULL) {
tw_decode_sense(tw_dev, request_id, 0);
} else {
error = tw_decode_sense(tw_dev, request_id, 1);
}
}
/* Check for correct state */ if (tw_dev->state[request_id] != TW_S_POSTED) { if (tw_dev->srb[request_id] != NULL) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id that wasn't posted.\n", tw_dev->host->host_no);
error = 1;
}
}
/* Check for internal command completion */ if (tw_dev->srb[request_id] == NULL) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n"); /* Check for chrdev ioctl completion */ if (request_id != tw_dev->chrdev_request_id) {
retval = tw_aen_complete(tw_dev, request_id); if (retval) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
}
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
wake_up(&tw_dev->ioctl_wqueue);
}
} else { switch (tw_dev->srb[request_id]->cmnd[0]) { case READ_10: case READ_6:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10/READ_6\n"); break; case WRITE_10: case WRITE_6:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n"); break; case TEST_UNIT_READY:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id); break; case INQUIRY:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
error = tw_scsiop_inquiry_complete(tw_dev, request_id); break; case READ_CAPACITY:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
error = tw_scsiop_read_capacity_complete(tw_dev, request_id); break; case MODE_SENSE:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught MODE_SENSE\n");
error = tw_scsiop_mode_sense_complete(tw_dev, request_id); break; case SYNCHRONIZE_CACHE:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught SYNCHRONIZE_CACHE\n"); break; default:
printk(KERN_WARNING "3w-xxxx: case slip in tw_interrupt()\n");
error = 1;
}
/* If no error command was a success */ if (error == 0) {
tw_dev->srb[request_id]->result = (DID_OK << 16);
}
/* If error, command failed */ if (error == 1) { /* Ask for a host reset */
tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
}
/* Now complete the io */ if ((error != TW_ISR_DONT_COMPLETE)) {
scsi_dma_unmap(tw_dev->srb[request_id]);
scsi_done(tw_dev->srb[request_id]);
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->posted_request_count--;
}
}
/* Check for valid status after each drain */
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev)); if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
TW_CLEAR_ALL_INTERRUPTS(tw_dev); goto tw_interrupt_bail;
}
}
}
}
tw_interrupt_bail:
spin_unlock(tw_dev->host->host_lock); return IRQ_RETVAL(handled);
} /* End tw_interrupt() */
/* This function tells the controller to shut down */ staticvoid __tw_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-xxxx: Shutting down host %d.\n", tw_dev->host->host_no);
/* Tell the card we are shutting down */ if (tw_initconnection(tw_dev, 1)) {
printk(KERN_WARNING "3w-xxxx: Connection shutdown failed.\n");
} else {
printk(KERN_WARNING "3w-xxxx: Shutdown complete.\n");
}
/* Clear all interrupts just before exit */
TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
} /* End __tw_shutdown() */
/* This function gets called when a disk is coming online */ staticint tw_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 tw_probe(struct pci_dev *pdev, conststruct pci_device_id *dev_id)
{ struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev; int retval;
retval = pci_enable_device(pdev); if (retval) {
printk(KERN_WARNING "3w-xxxx: Failed to enable pci device."); goto out_disable_device;
}
pci_set_master(pdev);
retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (retval) {
printk(KERN_WARNING "3w-xxxx: Failed to set dma mask."); goto out_disable_device;
}
host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) {
printk(KERN_WARNING "3w-xxxx: 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 (tw_initialize_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: Failed to initialize device extension.");
retval = -ENOMEM; goto out_free_device_extension;
}
/* Request IO regions */
retval = pci_request_regions(pdev, "3w-xxxx"); if (retval) {
printk(KERN_WARNING "3w-xxxx: Failed to get mem region."); goto out_free_device_extension;
}
/* Save base address */
tw_dev->base_addr = pci_resource_start(pdev, 0); if (!tw_dev->base_addr) {
printk(KERN_WARNING "3w-xxxx: Failed to get io address.");
retval = -ENOMEM; goto out_release_mem_region;
}
/* Disable interrupts on the card */
TW_DISABLE_INTERRUPTS(tw_dev);
/* Initialize the card */ if (tw_reset_sequence(tw_dev)) {
retval = -EINVAL; goto out_release_mem_region;
}
/* Set host specific parameters */
host->max_id = TW_MAX_UNITS;
host->max_cmd_len = TW_MAX_CDB_LEN;
/* Luns and channels aren't supported by adapter */
host->max_lun = 0;
host->max_channel = 0;
/* Register the card with the kernel SCSI layer */
retval = scsi_add_host(host, &pdev->dev); if (retval) {
printk(KERN_WARNING "3w-xxxx: scsi add host failed"); goto out_release_mem_region;
}
pci_set_drvdata(pdev, host);
printk(KERN_WARNING "3w-xxxx: scsi%d: Found a 3ware Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, tw_dev->base_addr, pdev->irq);
/* Now setup the interrupt handler */
retval = request_irq(pdev->irq, tw_interrupt, IRQF_SHARED, "3w-xxxx", tw_dev); if (retval) {
printk(KERN_WARNING "3w-xxxx: Error requesting IRQ."); goto out_remove_host;
}
/* This function is called to remove a device */ staticvoid tw_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 (twe_major >= 0) {
unregister_chrdev(twe_major, "twe");
twe_major = -1;
}
/* Shutdown the card */
__tw_shutdown(tw_dev);
/* Free up the mem region */
pci_release_regions(pdev);
/* Free up device extension resources */
tw_free_device_extension(tw_dev);
scsi_host_put(tw_dev->host);
pci_disable_device(pdev);
tw_device_extension_count--;
} /* End tw_remove() */
/* This function is called on driver initialization */ staticint __init tw_init(void)
{
printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
return pci_register_driver(&tw_driver);
} /* End tw_init() */
/* This function is called on driver exit */ staticvoid __exit tw_exit(void)
{
pci_unregister_driver(&tw_driver);
} /* End tw_exit() */
module_init(tw_init);
module_exit(tw_exit);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.37 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.