patch-2.4.21 linux-2.4.21/drivers/scsi/3w-xxxx.c
Next file: linux-2.4.21/drivers/scsi/3w-xxxx.h
Previous file: linux-2.4.21/drivers/sbus/char/envctrl.c
Back to the patch index
Back to the overall index
- Lines: 653
- Date:
2003-06-13 07:51:36.000000000 -0700
- Orig file:
linux-2.4.20/drivers/scsi/3w-xxxx.c
- Orig date:
2002-11-28 15:53:14.000000000 -0800
diff -urN linux-2.4.20/drivers/scsi/3w-xxxx.c linux-2.4.21/drivers/scsi/3w-xxxx.c
@@ -6,7 +6,7 @@
Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com>
- Copyright (C) 1999-2002 3ware Inc.
+ Copyright (C) 1999-2003 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
@@ -160,6 +160,11 @@
Add support for mode sense opcode.
Add support for cache mode page.
Add support for synchronize cache opcode.
+ 1.02.00.032 - Fix small multicard rollcall bug.
+ Make driver stay loaded with no units for hot add/swap.
+ Add support for "twe" character device for ioctls.
+ Clean up request_id queueing code.
+ Fix tw_scsi_queue() spinlocks.
*/
#include <linux/module.h>
@@ -202,6 +207,9 @@
MODULE_LICENSE("GPL");
#endif
+static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int tw_chrdev_open(struct inode *inode, struct file *file);
+static int tw_chrdev_release(struct inode *inode, struct file *file);
static int tw_copy_info(TW_Info *info, char *fmt, ...);
static void tw_copy_mem_info(TW_Info *info, char *data, int len);
static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
@@ -215,10 +223,19 @@
tw_halt, NULL, 0
};
+/* File operations struct for character device */
+static struct file_operations tw_fops = {
+ owner: THIS_MODULE,
+ ioctl: tw_chrdev_ioctl,
+ open: tw_chrdev_open,
+ release: tw_chrdev_release
+};
+
/* Globals */
-char *tw_driver_version="1.02.00.031";
+char *tw_driver_version="1.02.00.032";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
+static int twe_major = -1;
/* Functions */
@@ -608,6 +625,191 @@
return 0;
} /* End tw_check_errors() */
+/* This function handles ioctl for the character device */
+static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int error, request_id;
+ dma_addr_t dma_handle;
+ unsigned short tw_aen_code;
+ unsigned long before;
+ unsigned long flags;
+ unsigned int data_buffer_length = 0;
+ unsigned long data_buffer_length_adjusted = 0;
+ unsigned long *cpu_addr;
+ TW_New_Ioctl *tw_ioctl;
+ TW_Passthru *passthru;
+ TW_Device_Extension *tw_dev = tw_device_extension_list[MINOR(inode->i_rdev)];
+ int retval = -EFAULT;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
+
+ /* Only let one of these through at a time */
+ if (down_interruptible(&tw_dev->ioctl_sem))
+ return -EINTR;
+
+ /* First copy down the buffer length */
+ error = copy_from_user(&data_buffer_length, (void *)arg, sizeof(unsigned int));
+ if (error)
+ goto out;
+
+ /* Check size */
+ if (data_buffer_length > TW_MAX_SECTORS * 512) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ /* Hardware can only do multiple of 512 byte transfers */
+ data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
+
+ /* Now allocate ioctl buf memory */
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle);
+ if (cpu_addr == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ tw_ioctl = (TW_New_Ioctl *)cpu_addr;
+
+ /* Now copy down the entire ioctl */
+ error = copy_from_user(tw_ioctl, (void *)arg, data_buffer_length + sizeof(TW_New_Ioctl) - 1);
+ if (error)
+ goto out2;
+
+ passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
+
+ /* 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, tw_ioctl->data_buffer_length);
+ spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ if (tw_dev->aen_head == tw_dev->aen_tail) {
+ tw_aen_code = TW_AEN_QUEUE_EMPTY;
+ } else {
+ tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
+ 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;
+ }
+ }
+ spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
+ break;
+ case TW_CMD_PACKET_WITH_DATA:
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+ spin_lock_irqsave(&tw_dev->tw_lock, flags);
+
+ tw_state_request_start(tw_dev, &request_id);
+
+ /* Flag internal command */
+ tw_dev->srb[request_id] = 0;
+
+ /* Flag chrdev ioctl */
+ tw_dev->chrdev_request_id = request_id;
+
+ tw_ioctl->firmware_command.request_id = request_id;
+
+ /* Load the sg list */
+ switch (tw_ioctl->firmware_command.byte0.sgl_offset) {
+ case 2:
+ tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
+ break;
+ case 3:
+ tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
+ break;
+ case 5:
+ passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ passthru->sg_list[0].length = data_buffer_length_adjusted;
+ break;
+ }
+
+ memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
+
+ /* Now post the command packet to the controller */
+ tw_post_command_packet(tw_dev, request_id);
+ spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+
+ /* Now wait for the command to complete */
+ before = jiffies;
+
+ while (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+ /* FIXME: we need to sleep here */
+ udelay(10);
+ if (time_after(jiffies, before + HZ *TW_IOCTL_CHRDEV_TIMEOUT)) {
+ /* 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);
+ spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+ tw_dev->posted_request_count--;
+ 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);
+ }
+ spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ if (signal_pending(current))
+ retval = -EINTR;
+ else
+ retval = -EIO;
+ 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 complete the io */
+ spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ break;
+ default:
+ retval = -ENOTTY;
+ goto out2;
+ }
+
+ /* Now copy the entire response to userspace */
+ error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1);
+ if (error == 0)
+ retval = 0;
+out2:
+ /* Now free ioctl buf memory */
+ pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+out:
+ up(&tw_dev->ioctl_sem);
+ return retval;
+} /* End tw_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int tw_chrdev_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor_number;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
+
+ minor_number = MINOR(inode->i_rdev);
+ if (minor_number >= tw_device_extension_count)
+ return -ENODEV;
+
+ return 0;
+} /* End tw_chrdev_open() */
+
+/* This function handles close for the character device */
+static int tw_chrdev_release(struct inode *inode, struct file *file)
+{
+ dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_release()\n");
+
+ return 0;
+} /* End tw_chrdev_release() */
+
/* This function will clear all interrupts on the controller */
void tw_clear_all_interrupts(TW_Device_Extension *tw_dev)
{
@@ -878,7 +1080,9 @@
/* Disable interrupts on the card */
tw_disable_interrupts(tw_dev);
-
+
+ tries = 0;
+
while (tries < TW_MAX_RESET_TRIES) {
/* Do soft reset */
tw_soft_reset(tw_dev);
@@ -924,10 +1128,6 @@
error = tw_initialize_units(tw_dev);
if (error) {
printk(KERN_WARNING "3w-xxxx: No valid units for card %d.\n", j);
- release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
}
error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
@@ -945,14 +1145,15 @@
/* Calculate max cmds per lun, and setup queues */
if (tw_dev->num_units > 0) {
if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
- tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-1)/tw_dev->num_units;
+ tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units;
tw_dev->free_head = TW_Q_START;
- tw_dev->free_tail = TW_MAX_BOUNCEBUF - 1;
+ tw_dev->free_tail = TW_Q_START;
tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
} else {
/* Use SHT cmd_per_lun here */
+ tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
tw_dev->free_head = TW_Q_START;
- tw_dev->free_tail = TW_Q_LENGTH - 1;
+ tw_dev->free_tail = TW_Q_START;
tw_dev->free_wrap = TW_Q_LENGTH - 1;
}
}
@@ -1013,13 +1214,7 @@
/* Tell the firmware we support shutdown notification*/
error = tw_setfeature(tw_dev2, 2, 1, &c);
if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Error setting features for card %d.\n", j);
- scsi_unregister(host);
- release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- numcards--;
- continue;
+ printk(KERN_WARNING "3w-xxxx: Unable to set features for card %d, old firmware or card.\n", j);
}
/* Now setup the interrupt handler */
@@ -1044,10 +1239,14 @@
}
}
- if (numcards == 0)
- printk(KERN_WARNING "3w-xxxx: No cards with valid units found.\n");
- else
- register_reboot_notifier(&tw_notifier);
+ if (numcards == 0) {
+ printk(KERN_WARNING "3w-xxxx: No cards found.\n");
+ } else {
+ register_reboot_notifier(&tw_notifier);
+ if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0) {
+ printk(KERN_WARNING "3w-xxxx: Unable to register \"twe\" character device, error = %d.\n", twe_major);
+ }
+ }
return numcards;
} /* End tw_findcards() */
@@ -1179,6 +1378,9 @@
tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START;
spin_lock_init(&tw_dev->tw_lock);
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ init_waitqueue_head(&tw_dev->ioctl_wqueue);
+ init_MUTEX(&tw_dev->ioctl_sem);
return 0;
} /* End tw_initialize_device_extension() */
@@ -1510,9 +1712,14 @@
/* Check for internal command completion */
if (tw_dev->srb[request_id] == 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
- 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);
+ /* 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;
}
} else {
switch (tw_dev->srb[request_id]->cmnd[0]) {
@@ -1524,6 +1731,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);
@@ -2187,12 +2398,13 @@
tw_dev->state[i] = TW_S_INITIAL;
}
tw_dev->free_head = TW_Q_START;
- tw_dev->free_tail = TW_Q_LENGTH - 1;
+ tw_dev->free_tail = TW_Q_START;
tw_dev->posted_request_count = 0;
tw_dev->pending_request_count = 0;
tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START;
tw_dev->reset_print = 0;
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
return 0;
} /* End tw_reset_device_extension() */
@@ -2475,7 +2687,6 @@
unsigned char *command = SCpnt->cmnd;
int request_id = 0;
int error = 0;
- unsigned long flags = 0;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
if (tw_dev == NULL) {
@@ -2485,14 +2696,14 @@
return 0;
}
- spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ spin_lock(&tw_dev->tw_lock);
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n");
/* Skip scsi command if it isn't for us */
- if ((tw_dev->is_unit_present[SCpnt->target] == FALSE) || (SCpnt->lun != 0)) {
+ if ((SCpnt->channel != 0) || (SCpnt->lun != 0)) {
SCpnt->result = (DID_BAD_TARGET << 16);
done(SCpnt);
- spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ spin_unlock(&tw_dev->tw_lock);
return 0;
}
@@ -2505,6 +2716,9 @@
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
+ /* Initialize phase to zero */
+ SCpnt->SCp.phase = 0;
+
switch (*command) {
case READ_10:
case READ_6:
@@ -2554,7 +2768,7 @@
SCpnt->result = (DID_ERROR << 16);
done(SCpnt);
}
- spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ spin_unlock(&tw_dev->tw_lock);
return 0;
} /* End tw_scsi_queue() */
@@ -2578,6 +2792,12 @@
/* Free up the IRQ */
free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+ /* Unregister character device */
+ if (twe_major >= 0) {
+ unregister_chrdev(twe_major, "twe");
+ twe_major = -1;
+ }
+
/* Free up device extension resources */
tw_free_device_extension(tw_dev);
@@ -2651,7 +2871,6 @@
{
unsigned char *is_unit_present;
unsigned char *request_buffer;
- int i;
TW_Param *param;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
@@ -2663,12 +2882,12 @@
}
request_buffer = tw_dev->srb[request_id]->request_buffer;
memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
- request_buffer[0] = TYPE_DISK; /* Peripheral device type */
- request_buffer[1] = 0; /* Device type modifier */
- request_buffer[2] = 0; /* No ansi/iso compliance */
- request_buffer[4] = 31; /* Additional length */
+ request_buffer[0] = TYPE_DISK; /* Peripheral device type */
+ request_buffer[1] = 0; /* Device type modifier */
+ request_buffer[2] = 0; /* No ansi/iso compliance */
+ request_buffer[4] = 31; /* Additional length */
memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */
- memcpy(&request_buffer[16], "3w-xxxx ", 16); /* Product ID */
+ sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->target);
memcpy(&request_buffer[32], tw_driver_version, 3);
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
@@ -2678,15 +2897,12 @@
}
is_unit_present = &(param->data[0]);
- for (i=0 ; i<TW_MAX_UNITS; i++) {
- if (is_unit_present[i] == 0) {
- tw_dev->is_unit_present[i] = FALSE;
- } else {
- if (is_unit_present[i] & TW_UNIT_ONLINE) {
- tw_dev->is_unit_present[i] = TRUE;
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
- }
- }
+ if (is_unit_present[tw_dev->srb[request_id]->target] & TW_UNIT_ONLINE) {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = TRUE;
+ } else {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = FALSE;
+ tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+ return TW_ISR_DONT_RESULT;
}
return 0;
@@ -2993,7 +3209,11 @@
/* Do this if there are no sg list entries for raid 5 */
if (tw_dev->srb[request_id]->use_sg == 0) {
dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]);
- memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
+ buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+ if (buffaddr == 0)
+ return 1;
+
+ memcpy(tw_dev->bounce_buffer[request_id], bus_to_virt(buffaddr), tw_dev->srb[request_id]->request_bufflen);
command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
command_packet->size+=2;
@@ -3002,9 +3222,13 @@
/* Do this if we have multiple sg list entries for raid 5 */
if (tw_dev->srb[request_id]->use_sg > 0) {
dprintk(KERN_WARNING "doing raid 5 write use_sg = %d, sglist[0].length = %d\n", tw_dev->srb[request_id]->use_sg, sglist[0].length);
- for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
- memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length);
- count+=sglist[i].length;
+ use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+ if (use_sg == 0)
+ return 1;
+
+ for (i=0;i<use_sg; i++) {
+ memcpy((char *)tw_dev->bounce_buffer[request_id]+count, bus_to_virt(sg_dma_address(&sglist[i])), sg_dma_len(&sglist[i]));
+ count+=sg_dma_len(&sglist[i]);
}
command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
command_packet->byte8.io.sgl[0].length = count;
@@ -3088,17 +3312,89 @@
/* This function will handle test unit ready scsi command */
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
{
+
+ TW_Param *param;
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ u32 command_que_addr;
+ unsigned long param_value;
+
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
- /* Tell the scsi layer were done */
- tw_dev->state[request_id] = TW_S_COMPLETED;
- tw_state_request_finish(tw_dev, request_id);
- tw_dev->srb[request_id]->result = (DID_OK << 16);
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ /* Initialize command packet */
+ command_que_addr = tw_dev->registers.command_que_addr;
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->byte0.opcode = TW_OP_GET_PARAM;
+ command_packet->byte0.sgl_offset = 2;
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->byte3.unit = 0;
+ command_packet->byte3.host_id = 0;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = 3; /* unit summary table */
+ param->parameter_id = 3; /* unitsstatus parameter */
+ param->parameter_size_bytes = TW_MAX_UNITS;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment physical address.\n");
+ return 1;
+ }
+
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command packet */
+ tw_post_command_packet(tw_dev, request_id);
return 0;
} /* End tw_scsiop_test_unit_ready() */
+/* This function is called by the isr to complete a testunitready command */
+int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ unsigned char *is_unit_present;
+ TW_Param *param;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
+
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ if (param == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ is_unit_present = &(param->data[0]);
+
+ if (is_unit_present[tw_dev->srb[request_id]->target] & TW_UNIT_ONLINE) {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = TRUE;
+ } else {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = FALSE;
+ tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+ return TW_ISR_DONT_RESULT;
+ }
+
+ return 0;
+} /* End tw_scsiop_test_unit_ready_complete() */
+
/* Set a value in the features table */
int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
unsigned char *val)
@@ -3233,17 +3529,13 @@
{
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n");
- do {
- if (tw_dev->free_tail == tw_dev->free_wrap) {
- tw_dev->free_tail = TW_Q_START;
- } else {
- tw_dev->free_tail = tw_dev->free_tail + 1;
- }
- } while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_tail]] != TW_S_COMPLETED));
-
tw_dev->free_queue[tw_dev->free_tail] = request_id;
-
tw_dev->state[request_id] = TW_S_FINISHED;
+ if (tw_dev->free_tail == tw_dev->free_wrap)
+ tw_dev->free_tail = TW_Q_START;
+ else
+ tw_dev->free_tail++;
+
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id);
return 0;
@@ -3257,20 +3549,16 @@
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
/* Obtain next free request_id */
- do {
- if (tw_dev->free_head == tw_dev->free_wrap) {
- tw_dev->free_head = TW_Q_START;
- } else {
- tw_dev->free_head = tw_dev->free_head + 1;
- }
- } while (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] & TW_START_MASK);
-
id = tw_dev->free_queue[tw_dev->free_head];
+ if (tw_dev->free_head == tw_dev->free_wrap)
+ tw_dev->free_head = TW_Q_START;
+ else
+ tw_dev->free_head++;
- dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
*request_id = id;
tw_dev->state[id] = TW_S_STARTED;
+ dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
return 0;
} /* End tw_state_request_start() */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)