Commit f9969cbe authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-scsi.bkbits.net/scsi-for-linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents ad2d842b 909a019a
...@@ -1360,6 +1360,8 @@ NCR_700_flush_fifo(struct Scsi_Host *host) { ...@@ -1360,6 +1360,8 @@ NCR_700_flush_fifo(struct Scsi_Host *host) {
} }
/* The queue lock with interrupts disabled must be held on entry to
* this function */
STATIC int STATIC int
NCR_700_start_command(Scsi_Cmnd *SCp) NCR_700_start_command(Scsi_Cmnd *SCp)
{ {
...@@ -1367,17 +1369,13 @@ NCR_700_start_command(Scsi_Cmnd *SCp) ...@@ -1367,17 +1369,13 @@ NCR_700_start_command(Scsi_Cmnd *SCp)
(struct NCR_700_command_slot *)SCp->host_scribble; (struct NCR_700_command_slot *)SCp->host_scribble;
struct NCR_700_Host_Parameters *hostdata = struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0];
unsigned long flags;
__u16 count = 1; /* for IDENTIFY message */ __u16 count = 1; /* for IDENTIFY message */
save_flags(flags);
cli();
if(hostdata->state != NCR_700_HOST_FREE) { if(hostdata->state != NCR_700_HOST_FREE) {
/* keep this inside the lock to close the race window where /* keep this inside the lock to close the race window where
* the running command finishes on another CPU while we don't * the running command finishes on another CPU while we don't
* change the state to queued on this one */ * change the state to queued on this one */
slot->state = NCR_700_SLOT_QUEUED; slot->state = NCR_700_SLOT_QUEUED;
restore_flags(flags);
DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n",
SCp->host->host_no, slot->cmnd, slot)); SCp->host->host_no, slot->cmnd, slot));
...@@ -1444,12 +1442,6 @@ NCR_700_start_command(Scsi_Cmnd *SCp) ...@@ -1444,12 +1442,6 @@ NCR_700_start_command(Scsi_Cmnd *SCp)
NCR_700_writel(slot->temp, SCp->host, TEMP_REG); NCR_700_writel(slot->temp, SCp->host, TEMP_REG);
NCR_700_writel(slot->resume_offset, SCp->host, DSP_REG); NCR_700_writel(slot->resume_offset, SCp->host, DSP_REG);
/* allow interrupts here so that if we're selected we can take
* a selection interrupt. The script start may not be
* effective in this case, but the selection interrupt will
* save our command in that case */
restore_flags(flags);
return 1; return 1;
} }
......
...@@ -7,11 +7,18 @@ Tested in single and dual HBA configuration, 32 and 64bit busses, ...@@ -7,11 +7,18 @@ Tested in single and dual HBA configuration, 32 and 64bit busses,
SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc() SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc()
max of 128k bytes contiguous. max of 128k bytes contiguous.
Ver 2.5.3 Aug 01, 2002
* fix the passthru ioctl to handle the Scsi_Cmnd->request being a pointer
Ver 2.5.1 Jul 30, 2002
* fix ioctl to pay attention to the specified LUN.
Ver 2.5.0 Nov 29, 2001 Ver 2.5.0 Nov 29, 2001
* eliminated io_request_lock. This change makes the driver specific * eliminated io_request_lock. This change makes the driver specific
to the 2.5.x kernels. to the 2.5.x kernels.
* silenced excessively noisy printks. * silenced excessively noisy printks.
Ver 2.1.2 July 23, 2002
* initialize DumCmnd->lun in cpqfcTS_ioctl (used in fcFindLoggedInPorts as LUN index)
Ver 2.1.1 Oct 18, 2001 Ver 2.1.1 Oct 18, 2001
* reinitialize Cmnd->SCp.sent_command (used to identify commands as * reinitialize Cmnd->SCp.sent_command (used to identify commands as
passthrus) on calling scsi_done, since the scsi mid layer does not passthrus) on calling scsi_done, since the scsi mid layer does not
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
/* Embedded module documentation macros - see module.h */ /* Embedded module documentation macros - see module.h */
MODULE_AUTHOR("Compaq Computer Corporation"); MODULE_AUTHOR("Compaq Computer Corporation");
MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.1.1"); MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.5.3");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags); int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags);
...@@ -105,15 +105,16 @@ static struct proc_dir_entry proc_scsi_cpqfcTS = ...@@ -105,15 +105,16 @@ static struct proc_dir_entry proc_scsi_cpqfcTS =
# define CPQFC_WAIT_FOR_COMPLETION(x) down(x) # define CPQFC_WAIT_FOR_COMPLETION(x) down(x)
#endif #endif
static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba);
/* local function to load our per-HBA (local) data for chip /* local function to load our per-HBA (local) data for chip
registers, FC link state, all FC exchanges, etc. registers, FC link state, all FC exchanges, etc.
We allocate space and compute address offsets for the We allocate space and compute address offsets for the
most frequently accessed addresses; others (like World Wide most frequently accessed addresses; others (like World Wide
Name) are not necessary. Name) are not necessary.
*/ */
static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) static void Cpqfc_initHBAdata(CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
{ {
cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
...@@ -226,8 +227,11 @@ static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) ...@@ -226,8 +227,11 @@ static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN; cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM; cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
if (cpqfc_alloc_private_data_pool(cpqfcHBAdata) != 0) {
printk(KERN_WARNING
"cpqfc: unable to allocate pool for passthru ioctls. "
"Passthru ioctls disabled.\n");
}
} }
...@@ -483,6 +487,75 @@ static void my_ioctl_done (Scsi_Cmnd * SCpnt) ...@@ -483,6 +487,75 @@ static void my_ioctl_done (Scsi_Cmnd * SCpnt)
} }
static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba)
{
hba->private_data_bits = NULL;
hba->private_data_pool = NULL;
hba->private_data_bits =
kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
BITS_PER_LONG)*sizeof(unsigned long),
GFP_KERNEL);
if (hba->private_data_bits == NULL)
return -1;
memset(hba->private_data_bits, 0,
((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
BITS_PER_LONG)*sizeof(unsigned long));
hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) *
CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL);
if (hba->private_data_pool == NULL) {
kfree(hba->private_data_bits);
hba->private_data_bits = NULL;
return -1;
}
return 0;
}
static void cpqfc_free_private_data_pool(CPQFCHBA *hba)
{
kfree(hba->private_data_bits);
kfree(hba->private_data_pool);
}
int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer)
{
/* Is pointer within our private data pool?
We use Scsi_Request->upper_private_data (normally
reserved for upper layer drivers, e.g. the sg driver)
We check to see if the pointer is ours by looking at
its address. Is this ok? Hmm, it occurs to me that
a user app might do something bad by using sg to send
a cpqfc passthrough ioctl with upper_data_private
forged to be somewhere in our pool..., though they'd
normally have to be root already to do this. */
return (pointer != NULL &&
pointer >= (void *) hba->private_data_pool &&
pointer < (void *) hba->private_data_pool +
sizeof(*hba->private_data_pool) *
CPQFC_MAX_PASSTHRU_CMDS);
}
cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba)
{
int i;
do {
i = find_first_zero_bit(hba->private_data_bits,
CPQFC_MAX_PASSTHRU_CMDS);
if (i == CPQFC_MAX_PASSTHRU_CMDS)
return NULL;
} while ( test_and_set_bit(i & (BITS_PER_LONG - 1),
hba->private_data_bits+(i/BITS_PER_LONG)) != 0);
return &hba->private_data_pool[i];
}
void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data)
{
int i;
i = data - hba->private_data_pool;
clear_bit(i&(BITS_PER_LONG-1),
hba->private_data_bits+(i/BITS_PER_LONG));
}
int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
{ {
...@@ -490,35 +563,19 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) ...@@ -490,35 +563,19 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
struct Scsi_Host *HostAdapter = ScsiDev->host; struct Scsi_Host *HostAdapter = ScsiDev->host;
CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
PTACHYON fcChip = &cpqfcHBAdata->fcChip; PTACHYON fcChip = &cpqfcHBAdata->fcChip;
PFC_LOGGEDIN_PORT pLoggedInPort; PFC_LOGGEDIN_PORT pLoggedInPort = NULL;
Scsi_Cmnd DumCmnd; Scsi_Cmnd DumCmnd;
int i, j; int i, j;
VENDOR_IOCTL_REQ ioc; VENDOR_IOCTL_REQ ioc;
cpqfc_passthru_t *vendor_cmd; cpqfc_passthru_t *vendor_cmd;
Scsi_Device *SDpnt; Scsi_Device *SDpnt;
Scsi_Cmnd *ScsiPassThruCmnd; Scsi_Request *ScsiPassThruReq;
cpqfc_passthru_private_t *privatedata;
ENTER("cpqfcTS_ioctl "); ENTER("cpqfcTS_ioctl ");
// can we find an FC device mapping to this SCSI target?
DumCmnd.channel = ScsiDev->channel; // For searching
DumCmnd.target = ScsiDev->id;
pLoggedInPort = fcFindLoggedInPort( fcChip,
&DumCmnd, // search Scsi Nexus
0, // DON'T search linked list for FC port id
NULL, // DON'T search linked list for FC WWN
NULL); // DON'T care about end of list
if( pLoggedInPort == NULL ) // not found!
{
result = -ENXIO;
}
else // we know what FC device to operate on...
{
// printk("ioctl CMND %d", Cmnd); // printk("ioctl CMND %d", Cmnd);
switch (Cmnd) switch (Cmnd) {
{
// Passthrough provides a mechanism to bypass the RAID // Passthrough provides a mechanism to bypass the RAID
// or other controller and talk directly to the devices // or other controller and talk directly to the devices
// (e.g. physical disk drive) // (e.g. physical disk drive)
...@@ -528,6 +585,10 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) ...@@ -528,6 +585,10 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
{ {
void *buf = NULL; // for kernel space buffer for user data void *buf = NULL; // for kernel space buffer for user data
/* Check that our pool got allocated ok. */
if (cpqfcHBAdata->private_data_pool == NULL)
return -ENOMEM;
if( !arg) if( !arg)
return -EINVAL; return -EINVAL;
...@@ -549,83 +610,70 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) ...@@ -549,83 +610,70 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
if( !buf) if( !buf)
return -ENOMEM; return -ENOMEM;
} }
// Now build a Scsi_Request to pass down...
ScsiPassThruReq = scsi_allocate_request(ScsiDev);
if (ScsiPassThruReq == NULL) {
kfree(buf);
return -ENOMEM;
}
ScsiPassThruReq->upper_private_data =
cpqfc_alloc_private_data(cpqfcHBAdata);
if (ScsiPassThruReq->upper_private_data == NULL) {
kfree(buf);
scsi_release_request(ScsiPassThruReq); // "de-allocate"
return -ENOMEM;
}
// Now build a SCSI_CMND to pass down... if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) {
// This function allocates and sets Scsi_Cmnd ptrs such as if (vendor_cmd->len) { // Need data from user?
// ->channel, ->target, ->host if (copy_from_user(buf, vendor_cmd->bufp,
ScsiPassThruCmnd = scsi_allocate_device(ScsiDev, 1, 1); vendor_cmd->len)) {
kfree(buf);
// Need data from user? cpqfc_free_private_data(cpqfcHBAdata,
// make sure caller's buffer is in kernel space. ScsiPassThruReq->upper_private_data);
if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) && scsi_release_request(ScsiPassThruReq);
vendor_cmd->len)
if( copy_from_user( buf, vendor_cmd->bufp, vendor_cmd->len))
return( -EFAULT); return( -EFAULT);
// copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below)
memcpy( &ScsiPassThruCmnd->cmnd[0],
&vendor_cmd->cdb[0],
MAX_COMMAND_SIZE);
// we want to copy all 16 bytes into the FCP-SCSI CDB,
// although the actual passthru only uses up to the
// first 12.
ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB
// Unfortunately, the SCSI command cmnd[] field has only
// 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased
// to 16 for newer Fibre Channel and SCSI-3 larger CDBs.
// However, to avoid a mandatory kernel rebuild, we use the SCp
// spare field to store the extra 4 bytes ( ugly :-(
if( MAX_COMMAND_SIZE < 16)
{
memcpy( &ScsiPassThruCmnd->SCp.buffers_residual,
&vendor_cmd->cdb[12], 4);
} }
}
ScsiPassThruReq->sr_data_direction = SCSI_DATA_WRITE;
} else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) {
ScsiPassThruReq->sr_data_direction = SCSI_DATA_READ;
} else
// maybe this means a bug in the user app
ScsiPassThruReq->sr_data_direction = SCSI_DATA_NONE;
ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req()
ScsiPassThruReq->sr_sense_buffer[0] = 0;
ScsiPassThruReq->sr_sense_buffer[2] = 0;
ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU! // We copy the scheme used by sd.c:spinup_disk() to submit commands
// suppress LUN masking
// and VSA logic
// Use spare fields to copy FCP-SCSI LUN address info...
ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus;
ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive;
// We copy the scheme used by scsi.c to submit commands
// to our own HBA. We do this in order to stall the // to our own HBA. We do this in order to stall the
// thread calling the IOCTL until it completes, and use // thread calling the IOCTL until it completes, and use
// the same "_quecommand" function for synchronizing // the same "_quecommand" function for synchronizing
// FC Link events with our "worker thread". // FC Link events with our "worker thread".
{ privatedata = ScsiPassThruReq->upper_private_data;
CPQFC_DECLARE_COMPLETION(wait); privatedata->bus = vendor_cmd->bus;
ScsiPassThruCmnd->request->CPQFC_WAITING = &wait; privatedata->pdrive = vendor_cmd->pdrive;
// eventually gets us to our own _quecommand routine
scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0],
buf,
vendor_cmd->len,
my_ioctl_done,
10*HZ, 1);// timeout,retries
// Other I/Os can now resume; we wait for our ioctl
// command to complete
CPQFC_WAIT_FOR_COMPLETION(&wait);
ScsiPassThruCmnd->request->CPQFC_WAITING = NULL;
}
result = ScsiPassThruCmnd->result; // eventually gets us to our own _quecommand routine
scsi_wait_req(ScsiPassThruReq,
&vendor_cmd->cdb[0], buf, vendor_cmd->len,
10*HZ, // timeout
1); // retries
result = ScsiPassThruReq->sr_result;
// copy any sense data back to caller // copy any sense data back to caller
if( result != 0 ) if( result != 0 )
{ {
memcpy( vendor_cmd->sense_data, // see struct def - size=40 memcpy( vendor_cmd->sense_data, // see struct def - size=40
ScsiPassThruCmnd->sense_buffer, ScsiPassThruReq->sr_sense_buffer,
sizeof(ScsiPassThruCmnd->sense_buffer)); sizeof(ScsiPassThruReq->sr_sense_buffer));
} }
SDpnt = ScsiPassThruCmnd->device; SDpnt = ScsiPassThruReq->sr_device;
scsi_release_command(ScsiPassThruCmnd); // "de-allocate" /* upper_private_data is already freed in call_scsi_done() */
ScsiPassThruCmnd = NULL; scsi_release_request(ScsiPassThruReq); // "de-allocate"
ScsiPassThruReq = NULL;
// if (!SDpnt->was_reset && SDpnt->scsi_request_fn) // if (!SDpnt->was_reset && SDpnt->scsi_request_fn)
// (*SDpnt->scsi_request_fn)(); // (*SDpnt->scsi_request_fn)();
...@@ -679,10 +727,21 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) ...@@ -679,10 +727,21 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
case CPQFC_IOCTL_FC_TARGET_ADDRESS: case CPQFC_IOCTL_FC_TARGET_ADDRESS:
result = // can we find an FC device mapping to this SCSI target?
verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)); DumCmnd.channel = ScsiDev->channel; // For searching
if (result) DumCmnd.target = ScsiDev->id;
DumCmnd.lun = ScsiDev->lun;
pLoggedInPort = fcFindLoggedInPort( fcChip,
&DumCmnd, // search Scsi Nexus
0, // DON'T search linked list for FC port id
NULL, // DON'T search linked list for FC WWN
NULL); // DON'T care about end of list
if (pLoggedInPort == NULL) {
result = -ENXIO;
break; break;
}
result = verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
if (result) break;
put_user(pLoggedInPort->port_id, put_user(pLoggedInPort->port_id,
&((Scsi_FCTargAddress *) arg)->host_port_id); &((Scsi_FCTargAddress *) arg)->host_port_id);
...@@ -709,7 +768,6 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) ...@@ -709,7 +768,6 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
result = -EINVAL; result = -EINVAL;
break; break;
} }
}
LEAVE("cpqfcTS_ioctl"); LEAVE("cpqfcTS_ioctl");
return result; return result;
...@@ -747,6 +805,7 @@ int cpqfcTS_release(struct Scsi_Host *HostAdapter) ...@@ -747,6 +805,7 @@ int cpqfcTS_release(struct Scsi_Host *HostAdapter)
} }
cpqfc_free_private_data_pool(cpqfcHBAdata);
// free Linux resources // free Linux resources
DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n")); DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
free_irq( HostAdapter->irq, HostAdapter); free_irq( HostAdapter->irq, HostAdapter);
...@@ -1527,6 +1586,12 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, ...@@ -1527,6 +1586,12 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
Scsi_Cmnd * SCpnt; Scsi_Cmnd * SCpnt;
Scsi_Device * SDpnt; Scsi_Device * SDpnt;
// FIXME, cpqfcTS_TargetDeviceReset needs to be fixed
// similarly to how the passthrough ioctl was fixed
// around the 2.5.30 kernel. Scsi_Cmnd replaced with
// Scsi_Request, etc.
// For now, so people don't fall into a hole...
return -ENOTSUPP;
// printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags); // printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags);
...@@ -1543,6 +1608,7 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, ...@@ -1543,6 +1608,7 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
SCpnt->SCp.buffers_residual = FCP_TARGET_RESET; SCpnt->SCp.buffers_residual = FCP_TARGET_RESET;
// FIXME: this would panic, SCpnt->request would be NULL.
SCpnt->request->CPQFC_WAITING = &wait; SCpnt->request->CPQFC_WAITING = &wait;
scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries); scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries);
CPQFC_WAIT_FOR_COMPLETION(&wait); CPQFC_WAIT_FOR_COMPLETION(&wait);
...@@ -1701,6 +1767,18 @@ void cpqfcTS_intr_handler( int irq, ...@@ -1701,6 +1767,18 @@ void cpqfcTS_intr_handler( int irq,
UCHAR IntStat; UCHAR IntStat;
printk(" cpqfcTS adapter PCI error detected\n"); printk(" cpqfcTS adapter PCI error detected\n");
IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address); IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address);
printk("cpqfc: ISR = 0x%02x\n", IntStat);
if (IntStat & 0x1) {
__u16 pcistat;
/* read the pci status register */
pci_read_config_word(cpqfcHBA->PciDev, 0x06, &pcistat);
printk("PCI status register is 0x%04x\n", pcistat);
if (pcistat & 0x8000) printk("Parity Error Detected.\n");
if (pcistat & 0x4000) printk("Signalled System Error\n");
if (pcistat & 0x2000) printk("Received Master Abort\n");
if (pcistat & 0x1000) printk("Received Target Abort\n");
if (pcistat & 0x0800) printk("Signalled Target Abort\n");
}
if (IntStat & 0x4) printk("(INT)\n"); if (IntStat & 0x4) printk("(INT)\n");
if (IntStat & 0x8) if (IntStat & 0x8)
printk("CRS: PCI master address crossed 46 bit bouandary\n"); printk("CRS: PCI master address crossed 46 bit bouandary\n");
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
// don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c // don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c
#define VER_MAJOR 2 #define VER_MAJOR 2
#define VER_MINOR 5 #define VER_MINOR 5
#define VER_SUBMINOR 0 #define VER_SUBMINOR 3
// Macros for kernel (esp. SMP) tracing using a PCI analyzer // Macros for kernel (esp. SMP) tracing using a PCI analyzer
// (e.g. x86). // (e.g. x86).
...@@ -907,9 +907,17 @@ typedef struct ...@@ -907,9 +907,17 @@ typedef struct
} FC_SCSI_QUE, *PFC_SCSI_QUE; } FC_SCSI_QUE, *PFC_SCSI_QUE;
typedef struct {
/* This is tacked on to a Scsi_Request in upper_private_data
for pasthrough ioctls, as a place to hold data that can't
be stashed anywhere else in the Scsi_Request. We differentiate
this from _real_ upper_private_data by checking if the virt addr
is within our special pool. */
ushort bus;
ushort pdrive;
} cpqfc_passthru_private_t;
#define CPQFC_MAX_PASSTHRU_CMDS 100
#define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST #define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
...@@ -949,6 +957,8 @@ typedef struct ...@@ -949,6 +957,8 @@ typedef struct
PFC_LINK_QUE fcLQ; // the WorkerThread operates on this PFC_LINK_QUE fcLQ; // the WorkerThread operates on this
spinlock_t hba_spinlock; // held/released by WorkerThread spinlock_t hba_spinlock; // held/released by WorkerThread
cpqfc_passthru_private_t *private_data_pool;
unsigned long *private_data_bits;
} CPQFCHBA; } CPQFCHBA;
...@@ -1405,6 +1415,7 @@ struct ext_sg_entry_t { ...@@ -1405,6 +1415,7 @@ struct ext_sg_entry_t {
__u32 lba; /* lower bus address bits 0-31 */ __u32 lba; /* lower bus address bits 0-31 */
}; };
// J. McCarty's LINK.H // J. McCarty's LINK.H
// //
// LS_RJT Reason Codes // LS_RJT Reason Codes
......
...@@ -2887,15 +2887,22 @@ static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd) ...@@ -2887,15 +2887,22 @@ static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
Done: Done:
} }
extern int is_private_data_of_cpqfc(CPQFCHBA *hba, void * pointer);
extern void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data);
static void static void
call_scsi_done(Scsi_Cmnd *Cmnd) call_scsi_done(Scsi_Cmnd *Cmnd)
{ {
// We have to reinitialize sent_command here, so the scsi-mid CPQFCHBA *hba;
// layer won't re-use the scsi command leaving it set incorrectly. hba = (CPQFCHBA *) Cmnd->host->hostdata;
// (incorrectly for our purposes...it's normally unused.) // Was this command a cpqfc passthru ioctl ?
if (Cmnd->sc_request != NULL && Cmnd->host != NULL &&
if (Cmnd->SCp.sent_command != 0) { // was it a passthru? Cmnd->host->hostdata != NULL &&
Cmnd->SCp.sent_command = 0; is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata,
Cmnd->sc_request->upper_private_data)) {
cpqfc_free_private_data(hba,
Cmnd->sc_request->upper_private_data);
Cmnd->sc_request->upper_private_data = NULL;
Cmnd->result &= 0xff00ffff; Cmnd->result &= 0xff00ffff;
Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry
} }
...@@ -3293,6 +3300,7 @@ static int GetLoopID( ULONG al_pa ) ...@@ -3293,6 +3300,7 @@ static int GetLoopID( ULONG al_pa )
} }
#endif #endif
extern cpqfc_passthru_private_t *cpqfc_private(Scsi_Request *sr);
// Search the singly (forward) linked list "fcPorts" looking for // Search the singly (forward) linked list "fcPorts" looking for
// either the SCSI target (if != -1), port_id (if not NULL), // either the SCSI target (if != -1), port_id (if not NULL),
...@@ -3366,8 +3374,18 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort( ...@@ -3366,8 +3374,18 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort(
{ {
// For "passthru" modes, the IOCTL caller is responsible // For "passthru" modes, the IOCTL caller is responsible
// for setting the FCP-LUN addressing // for setting the FCP-LUN addressing
if( !Cmnd->SCp.sent_command ) // NOT passthru? if (Cmnd->sc_request != NULL && Cmnd->host != NULL &&
{ Cmnd->host->hostdata != NULL &&
is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata,
Cmnd->sc_request->upper_private_data)) {
/* This is a passthru... */
cpqfc_passthru_private_t *pd;
pd = Cmnd->sc_request->upper_private_data;
Cmnd->SCp.phase = pd->bus;
// Cmnd->SCp.have_data_in = pd->pdrive;
Cmnd->SCp.have_data_in = Cmnd->lun;
} else {
/* This is not a passthru... */
// set the FCP-LUN addressing type // set the FCP-LUN addressing type
Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing; Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;
...@@ -3380,6 +3398,8 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort( ...@@ -3380,6 +3398,8 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort(
// Report Luns command // Report Luns command
if( pLoggedInPort->ScsiNexus.LunMasking == 1) if( pLoggedInPort->ScsiNexus.LunMasking == 1)
{ {
if (Cmnd->lun > sizeof(pLoggedInPort->ScsiNexus.lun))
return NULL;
// we KNOW all the valid LUNs... 0xFF is invalid! // we KNOW all the valid LUNs... 0xFF is invalid!
Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun]; Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun];
if (pLoggedInPort->ScsiNexus.lun[Cmnd->lun] == 0xFF) if (pLoggedInPort->ScsiNexus.lun[Cmnd->lun] == 0xFF)
...@@ -3504,7 +3524,6 @@ static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, ...@@ -3504,7 +3524,6 @@ static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
{ {
printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n", printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
pLoggedInPort->port_id); pLoggedInPort->port_id);
Cmnd->SCp.sent_command = 0;
} }
else else
call_scsi_done(Cmnd); call_scsi_done(Cmnd);
...@@ -5232,7 +5251,6 @@ static ULONG build_SEST_sgList( ...@@ -5232,7 +5251,6 @@ static ULONG build_SEST_sgList(
sgl = (struct scatterlist*)Cmnd->request_buffer; sgl = (struct scatterlist*)Cmnd->request_buffer;
sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg, sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg,
scsi_to_pci_dma_dir(Cmnd->sc_data_direction)); scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
// printk("sgl = %p, sg_count = %d\n", (void *) sgl, sg_count);
if( sg_count <= 3 ) { if( sg_count <= 3 ) {
// we need to be careful here that no individual mapping // we need to be careful here that no individual mapping
...@@ -5261,7 +5279,6 @@ static ULONG build_SEST_sgList( ...@@ -5261,7 +5279,6 @@ static ULONG build_SEST_sgList(
// printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count); // printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count);
} }
// printk("totalsgs = %d, sgcount=%d\n", totalsgs, sg_count);
if( totalsgs <= 3 ) // can (must) use "local" SEST list if( totalsgs <= 3 ) // can (must) use "local" SEST list
{ {
while( bytes_to_go) while( bytes_to_go)
...@@ -6164,13 +6181,11 @@ void cpqfcTSCompleteExchange( ...@@ -6164,13 +6181,11 @@ void cpqfcTSCompleteExchange(
} }
else else
{ {
Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0;
// printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n", // printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
} }
} }
else{ else{
Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0;
printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID, printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
Exchanges->fcExchange[ x_ID ].type, Exchanges->fcExchange[ x_ID ].type,
Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]); Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);
...@@ -6463,10 +6478,10 @@ static int build_FCP_payload( Scsi_Cmnd *Cmnd, ...@@ -6463,10 +6478,10 @@ static int build_FCP_payload( Scsi_Cmnd *Cmnd,
for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++) for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
*payload++ = Cmnd->cmnd[i]; *payload++ = Cmnd->cmnd[i];
if( Cmnd->cmd_len == 16 ) // if( Cmnd->cmd_len == 16 )
{ // {
memcpy( payload, &Cmnd->SCp.buffers_residual, 4); // memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
} // }
payload+= (16 - i); payload+= (16 - i);
// FCP_DL is largest number of expected data bytes // FCP_DL is largest number of expected data bytes
......
/* $Id: scsi_debug.c,v 1.1 1992/07/24 06:27:38 root Exp root $ /*
* linux/kernel/scsi_debug.c * linux/kernel/scsi_debug.c
* *
* Copyright (C) 1992 Eric Youngdale * Copyright (C) 1992 Eric Youngdale
...@@ -12,10 +12,11 @@ ...@@ -12,10 +12,11 @@
* *
* For documentation see http://www.torque.net/sg/sdebug.html * For documentation see http://www.torque.net/sg/sdebug.html
* *
* D. Gilbert (dpg) work for MOD device test [20010421] * D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
* dpg, work for devfs large number of disks [20010809] * dpg: work for devfs large number of disks [20010809]
* dpg, forked for lk 2.5 series [20011216, 20020101] * forked for lk 2.5 series [20011216, 20020101]
* dpg, use vmalloc() more inquiry+mode_sense [20020302] * use vmalloc() more inquiry+mode_sense [20020302]
* add timers for delayed responses [20020721]
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -46,7 +47,12 @@ ...@@ -46,7 +47,12 @@
#include <linux/version.h> #include <linux/version.h>
#endif #endif
static char scsi_debug_version_str[] = "Version: 1.59 (20020302)"; #include "scsi_debug.h"
static const char * scsi_debug_version_str = "Version: 1.62 (20020812)";
#define DRIVERFS_SUPPORT 1 /* comment out whole line to disable */
#ifndef SCSI_CMD_READ_16 #ifndef SCSI_CMD_READ_16
#define SCSI_CMD_READ_16 0x88 #define SCSI_CMD_READ_16 0x88
...@@ -54,29 +60,36 @@ static char scsi_debug_version_str[] = "Version: 1.59 (20020302)"; ...@@ -54,29 +60,36 @@ static char scsi_debug_version_str[] = "Version: 1.59 (20020302)";
#ifndef SCSI_CMD_WRITE_16 #ifndef SCSI_CMD_WRITE_16
#define SCSI_CMD_WRITE_16 0x8a #define SCSI_CMD_WRITE_16 0x8a
#endif #endif
#ifndef REPORT_LUNS
#define REPORT_LUNS 0xa0
#endif
/* A few options that we want selected */ /* A few options that we want selected */
#define DEF_NR_FAKE_DEVS 1 #define DEF_NR_FAKE_DEVS 1
#define DEF_DEV_SIZE_MB 8 #define DEF_DEV_SIZE_MB 8
#define DEF_FAKE_BLK0 0 #define DEF_FAKE_BLK0 0
#define DEF_EVERY_NTH 100
#define DEF_DELAY 1
#define DEF_OPTS 0 #define DEF_OPTS 0
#define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_NOISE 1
#define SCSI_DEBUG_OPT_MEDIUM_ERR 2 #define SCSI_DEBUG_OPT_MEDIUM_ERR 2
#define SCSI_DEBUG_OPT_EVERY_NTH 4
#define OPT_MEDIUM_ERR_ADDR 0x1234 #define OPT_MEDIUM_ERR_ADDR 0x1234
static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS; static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
static int scsi_debug_opts = DEF_OPTS; static int scsi_debug_opts = DEF_OPTS;
static int scsi_debug_every_nth = DEF_EVERY_NTH;
static int scsi_debug_cmnd_count = 0;
static int scsi_debug_delay = DEF_DELAY;
#define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1) #define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1)
#define N_HEAD 8 #define N_HEAD 8
#define N_SECTOR 32 #define N_SECTOR 32
#define DEV_READONLY(TGT) (0) #define DEV_READONLY(TGT) (0)
#define DEV_REMOVEABLE(TGT) (0) #define DEV_REMOVEABLE(TGT) (0)
#define DEVICE_TYPE(TGT) (TYPE_DISK); #define PERIPH_DEVICE_TYPE(TGT) (TYPE_DISK);
#define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1)
static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
#define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024) #define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024)
...@@ -87,81 +100,79 @@ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; ...@@ -87,81 +100,79 @@ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
#define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD)) #define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD))
/* Do not attempt to use a timer to simulate a real disk with latency */
/* Only use this in the actual kernel, not in the simulator. */
#define IMMEDIATE
#define START_PARTITION 4
/* Time to wait before completing a command */ /* Time to wait before completing a command */
#define DISK_SPEED (HZ/10) /* 100ms */
#define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER) #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER)
#define SECT_SIZE_PER(TGT) SECT_SIZE #define SECT_SIZE_PER(TGT) SECT_SIZE
static int starts[] =
{N_SECTOR,
N_HEAD * N_SECTOR, /* Single cylinder */
N_HEAD * N_SECTOR * 4,
0 /* CAPACITY */, 0};
static unsigned char * fake_storep; #define SDEBUG_SENSE_LEN 32
typedef struct sdebug_dev_info { struct sdebug_dev_info {
Scsi_Device * sdp; Scsi_Device * sdp;
unsigned short host_no; unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */
unsigned short id;
char reset; char reset;
char sb_index; };
} Sdebug_dev_info; static struct sdebug_dev_info * devInfop;
static Sdebug_dev_info * devInfop;
typedef void (* done_funct_t) (Scsi_Cmnd *);
struct sdebug_queued_cmd {
int in_use;
struct timer_list cmnd_timer;
done_funct_t done_funct;
struct scsi_cmnd * a_cmnd;
int scsi_result;
};
static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char broken_buff[SDEBUG_SENSE_LEN];
static int num_aborts = 0; static int num_aborts = 0;
static int num_dev_resets = 0; static int num_dev_resets = 0;
static int num_bus_resets = 0; static int num_bus_resets = 0;
static int num_host_resets = 0; static int num_host_resets = 0;
static spinlock_t mailbox_lock = SPIN_LOCK_UNLOCKED; static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED;
static rwlock_t sdebug_atomic_rw = RW_LOCK_UNLOCKED; static rwlock_t atomic_rw = RW_LOCK_UNLOCKED;
#include "scsi_debug.h"
typedef void (*done_fct_t) (Scsi_Cmnd *); #ifdef DRIVERFS_SUPPORT
static struct device_driver sdebug_driverfs_driver;
static volatile done_fct_t * do_done = 0; #endif
static struct Scsi_Host * SHpnt = NULL;
static int scsi_debug_inquiry(unsigned char * cmd, int target, /* function declarations */
unsigned char * buff, int bufflen, static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
Sdebug_dev_info * devip); int bufflen, struct sdebug_dev_info * devip);
static int scsi_debug_mode_sense(unsigned char * cmd, int target, static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen, unsigned char * buff, int bufflen,
Sdebug_dev_info * devip); struct sdebug_dev_info * devip);
static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, int * errstsp, Sdebug_dev_info * devip); int num, struct sdebug_dev_info * devip);
static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
int num, int * errstsp, Sdebug_dev_info * devip); struct sdebug_dev_info * devip);
static void scsi_debug_intr_handle(unsigned long); static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp); int bufflen, struct sdebug_dev_info * devip);
static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, static void timer_intr_handler(unsigned long);
static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen); int asc, int asq, int inbandLen);
static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip); static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip);
static int schedule_resp(struct scsi_cmnd * cmnd,
static struct timer_list * timeout = 0; struct sdebug_dev_info * devip,
static Scsi_Cmnd ** SCint = 0; done_funct_t done, int scsi_result, int delta_jiff);
static void init_all_queued(void);
/* static void stop_all_queued(void);
* Semaphore used to simulate bus lockups. static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
*/ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
static int scsi_debug_lockup = 0; const char * dev_id_str, int dev_id_str_len);
#ifdef DRIVERFS_SUPPORT
#define NUM_SENSE_BUFFS 4 static void do_create_driverfs_files(void);
#define SENSE_BUFF_LEN 32 static void do_remove_driverfs_files(void);
static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN]; #endif
static inline static unsigned char * scatg2virt(const struct scatterlist * sclp)
unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
{ {
if (NULL == sclp) if (NULL == sclp)
return NULL; return NULL;
...@@ -173,61 +184,47 @@ unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp) ...@@ -173,61 +184,47 @@ unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
} }
static static
int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, done_funct_t done)
{ {
unsigned char *cmd = (unsigned char *) SCpnt->cmnd; unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
int block; int block;
int upper_blk; int upper_blk;
unsigned char *buff; unsigned char *buff;
int scsi_debug_errsts; int errsts = 0;
int target = SCpnt->target; int target = SCpnt->target;
int bufflen = SCpnt->request_bufflen; int bufflen = SCpnt->request_bufflen;
unsigned long iflags; int num, capac;
int i, num, capac; struct sdebug_dev_info * devip = NULL;
Sdebug_dev_info * devip = NULL; unsigned char * sbuff;
char * sbuff;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { if (done == NULL)
printk(KERN_INFO "scsi_debug: queue_command: cmd "); return 0; /* assume mid level reprocessing command */
for (i = 0, num = SCpnt->cmd_len; i < num; ++i)
printk("%02x ", cmd[i]);
printk(" use_sg=%d\n", SCpnt->use_sg);
}
/*
* If we are being notified of the mid-level reposessing a command
* due to timeout, just return.
*/
if (done == NULL) {
return 0;
}
if (SCpnt->use_sg) { /* just use first element */ if (SCpnt->use_sg) { /* just use first element */
struct scatterlist *sgpnt = (struct scatterlist *) struct scatterlist *sgpnt = (struct scatterlist *)
SCpnt->request_buffer; SCpnt->request_buffer;
buff = sdebug_scatg2virt(&sgpnt[0]); buff = scatg2virt(&sgpnt[0]);
bufflen = sgpnt[0].length; bufflen = sgpnt[0].length;
/* READ and WRITE process scatterlist themselves */ /* READ and WRITE process scatterlist themselves */
} }
else else
buff = (unsigned char *) SCpnt->request_buffer; buff = (unsigned char *) SCpnt->request_buffer;
if (NULL == buff) {
/* printk(KERN_WARNING "scsi_debug:qc: buff was NULL??\n");
* If a command comes for the ID of the host itself, just print buff = broken_buff; /* just point at dummy */
* a silly message and return. bufflen = SDEBUG_SENSE_LEN;
*/
if(target == 7) {
printk(KERN_WARNING "How do you do!\n");
SCpnt->result = 0;
done(SCpnt);
return 0;
} }
if ((target > 7) || (SCpnt->lun != 0)) { if(target == driver_template.this_id) {
SCpnt->result = DID_NO_CONNECT << 16; printk(KERN_WARNING
done(SCpnt); "scsi_debug: initiator's id used as target!\n");
return 0; return schedule_resp(SCpnt, NULL, done, 0, 0);
} }
if ((target > driver_template.this_id) || (SCpnt->lun != 0))
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
#if 0 #if 0
printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n", printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n",
(int)SCpnt->device->host->host_no, (int)SCpnt->device->id, (int)SCpnt->device->host->host_no, (int)SCpnt->device->id,
...@@ -235,101 +232,93 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) ...@@ -235,101 +232,93 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
#endif #endif
if (NULL == SCpnt->device->hostdata) { if (NULL == SCpnt->device->hostdata) {
devip = devInfoReg(SCpnt->device); devip = devInfoReg(SCpnt->device);
if (NULL == devip) { if (NULL == devip)
SCpnt->result = DID_NO_CONNECT << 16; return schedule_resp(SCpnt, NULL, done,
done(SCpnt); DID_NO_CONNECT << 16, 0);
return 0;
}
SCpnt->device->hostdata = devip; SCpnt->device->hostdata = devip;
} }
devip = SCpnt->device->hostdata; devip = SCpnt->device->hostdata;
if ((SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) &&
(scsi_debug_every_nth > 0) &&
(++scsi_debug_cmnd_count >= scsi_debug_every_nth)) {
scsi_debug_cmnd_count =0;
return 0; /* ignore command causing timeout */
}
switch (*cmd) { switch (*cmd) {
case INQUIRY: /* mandatory */ case INQUIRY: /* mandatory */
scsi_debug_errsts = scsi_debug_inquiry(cmd, target, buff,
bufflen, devip);
/* assume INQUIRY called first so setup max_cmd_len */ /* assume INQUIRY called first so setup max_cmd_len */
if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
errsts = resp_inquiry(cmd, target, buff, bufflen, devip);
break; break;
case REQUEST_SENSE: /* mandatory */ case REQUEST_SENSE: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Request sense...\n")); /* Since this driver indicates autosense by placing the
* sense buffer in the scsi_cmnd structure in the response
* (when CHECK_CONDITION is set), the mid level shouldn't
* need to call REQUEST_SENSE */
if (devip) { if (devip) {
sbuff = &sense_buffers[(int)devip->sb_index][0]; sbuff = devip->sense_buff;
devip->sb_index = 0; memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ?
bufflen : SDEBUG_SENSE_LEN);
mk_sense_buffer(devip, 0, 0x0, 0, 7);
} else {
memset(buff, 0, bufflen);
buff[0] = 0x70;
} }
else break;
sbuff = &sense_buffers[0][0];
memcpy(buff, sbuff, (bufflen < SENSE_BUFF_LEN) ?
bufflen : SENSE_BUFF_LEN);
memset(sbuff, 0, SENSE_BUFF_LEN);
sbuff[0] = 0x70;
SCpnt->result = 0;
done(SCpnt);
return 0;
case START_STOP: case START_STOP:
if (check_reset(SCpnt, devip)) { errsts = check_reset(SCpnt, devip);
done(SCpnt);
return 0;
}
SCSI_LOG_LLQUEUE(3, printk("START_STOP\n"));
scsi_debug_errsts = 0;
break; break;
case ALLOW_MEDIUM_REMOVAL: case ALLOW_MEDIUM_REMOVAL:
if (check_reset(SCpnt, devip)) { if ((errsts = check_reset(SCpnt, devip)))
done(SCpnt); break;
return 0; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
} printk("\tMedium removal %s\n",
if (cmd[4]) { cmd[4] ? "inhibited" : "enabled");
SCSI_LOG_LLQUEUE(2, printk(
"Medium removal inhibited..."));
} else {
SCSI_LOG_LLQUEUE(2,
printk("Medium removal enabled..."));
}
scsi_debug_errsts = 0;
break; break;
case SEND_DIAGNOSTIC: /* mandatory */ case SEND_DIAGNOSTIC: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n"));
if (buff)
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
scsi_debug_errsts = 0;
break; break;
case TEST_UNIT_READY: /* mandatory */ case TEST_UNIT_READY: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n",
buff, bufflen));
if (buff)
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
scsi_debug_errsts = 0; break;
case RESERVE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RESERVE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case READ_CAPACITY: case READ_CAPACITY:
if (check_reset(SCpnt, devip)) { errsts = check_reset(SCpnt, devip);
done(SCpnt);
return 0;
}
SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n"));
SHpnt = SCpnt->host;
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
if (bufflen > 7) {
capac = CAPACITY - 1; capac = CAPACITY - 1;
buff[0] = (capac >> 24); buff[0] = (capac >> 24);
buff[1] = (capac >> 16) & 0xff; buff[1] = (capac >> 16) & 0xff;
buff[2] = (capac >> 8) & 0xff; buff[2] = (capac >> 8) & 0xff;
buff[3] = capac & 0xff; buff[3] = capac & 0xff;
buff[4] = 0;
buff[5] = 0;
buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
buff[7] = SECT_SIZE_PER(target) & 0xff; buff[7] = SECT_SIZE_PER(target) & 0xff;
}
scsi_debug_errsts = 0;
break; break;
case SCSI_CMD_READ_16: /* SBC-2 */ case SCSI_CMD_READ_16: /* SBC-2 */
case READ_12: case READ_12:
case READ_10: case READ_10:
case READ_6: case READ_6:
if (check_reset(SCpnt, devip)) { if ((errsts = check_reset(SCpnt, devip)))
done(SCpnt); break;
return 0;
}
upper_blk = 0; upper_blk = 0;
if ((*cmd) == SCSI_CMD_READ_16) { if ((*cmd) == SCSI_CMD_READ_16) {
upper_blk = cmd[5] + (cmd[4] << 8) + upper_blk = cmd[5] + (cmd[4] << 8) +
...@@ -338,38 +327,31 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) ...@@ -338,38 +327,31 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
(cmd[7] << 16) + (cmd[6] << 24); (cmd[7] << 16) + (cmd[6] << 24);
num = cmd[13] + (cmd[12] << 8) + num = cmd[13] + (cmd[12] << 8) +
(cmd[11] << 16) + (cmd[10] << 24); (cmd[11] << 16) + (cmd[10] << 24);
} } else if ((*cmd) == READ_12) {
else if ((*cmd) == READ_12) {
block = cmd[5] + (cmd[4] << 8) + block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24); (cmd[3] << 16) + (cmd[2] << 24);
num = cmd[9] + (cmd[8] << 8) + num = cmd[9] + (cmd[8] << 8) +
(cmd[7] << 16) + (cmd[6] << 24); (cmd[7] << 16) + (cmd[6] << 24);
} } else if ((*cmd) == READ_10) {
else if ((*cmd) == READ_10) {
block = cmd[5] + (cmd[4] << 8) + block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24); (cmd[3] << 16) + (cmd[2] << 24);
num = cmd[8] + (cmd[7] << 8); num = cmd[8] + (cmd[7] << 8);
} } else {
else {
block = cmd[3] + (cmd[2] << 8) + block = cmd[3] + (cmd[2] << 8) +
((cmd[1] & 0x1f) << 16); ((cmd[1] & 0x1f) << 16);
num = cmd[4]; num = cmd[4];
} }
if (scsi_debug_read(SCpnt, upper_blk, block, num, errsts = resp_read(SCpnt, upper_blk, block, num, devip);
&scsi_debug_errsts, devip)) break;
case REPORT_LUNS:
errsts = resp_report_luns(cmd, buff, bufflen, devip);
break; break;
SCpnt->result = 0;
/* calls bottom half in upper layers before return from scsi_do_...() */
(done) (SCpnt);
return 0;
case SCSI_CMD_WRITE_16: /* SBC-2 */ case SCSI_CMD_WRITE_16: /* SBC-2 */
case WRITE_12: case WRITE_12:
case WRITE_10: case WRITE_10:
case WRITE_6: case WRITE_6:
if (check_reset(SCpnt, devip)) { if ((errsts = check_reset(SCpnt, devip)))
done(SCpnt); break;
return 0;
}
upper_blk = 0; upper_blk = 0;
if ((*cmd) == SCSI_CMD_WRITE_16) { if ((*cmd) == SCSI_CMD_WRITE_16) {
upper_blk = cmd[5] + (cmd[4] << 8) + upper_blk = cmd[5] + (cmd[4] << 8) +
...@@ -378,97 +360,38 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) ...@@ -378,97 +360,38 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
(cmd[7] << 16) + (cmd[6] << 24); (cmd[7] << 16) + (cmd[6] << 24);
num = cmd[13] + (cmd[12] << 8) + num = cmd[13] + (cmd[12] << 8) +
(cmd[11] << 16) + (cmd[10] << 24); (cmd[11] << 16) + (cmd[10] << 24);
} } else if ((*cmd) == WRITE_12) {
else if ((*cmd) == WRITE_12) {
block = cmd[5] + (cmd[4] << 8) + block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24); (cmd[3] << 16) + (cmd[2] << 24);
num = cmd[9] + (cmd[8] << 8) + num = cmd[9] + (cmd[8] << 8) +
(cmd[7] << 16) + (cmd[6] << 24); (cmd[7] << 16) + (cmd[6] << 24);
} } else if ((*cmd) == WRITE_10) {
else if ((*cmd) == WRITE_10) {
block = cmd[5] + (cmd[4] << 8) + block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24); (cmd[3] << 16) + (cmd[2] << 24);
num = cmd[8] + (cmd[7] << 8); num = cmd[8] + (cmd[7] << 8);
} } else {
else {
block = cmd[3] + (cmd[2] << 8) + block = cmd[3] + (cmd[2] << 8) +
((cmd[1] & 0x1f) << 16); ((cmd[1] & 0x1f) << 16);
num = cmd[4]; num = cmd[4];
} }
if (scsi_debug_write(SCpnt, upper_blk, block, num, errsts = resp_write(SCpnt, upper_blk, block, num, devip);
&scsi_debug_errsts, devip))
break; break;
SCpnt->result = 0;
/* calls bottom half in upper layers before return from scsi_do_...() */
(done) (SCpnt);
return 0;
case MODE_SENSE: case MODE_SENSE:
case MODE_SENSE_10: case MODE_SENSE_10:
scsi_debug_errsts = errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
scsi_debug_mode_sense(cmd, target, buff, bufflen, devip);
break; break;
default: default:
#if 0 #if 0
printk(KERN_INFO "scsi_debug: Unsupported command, " printk(KERN_INFO "scsi_debug: Unsupported command, "
"opcode=0x%x\n", (int)cmd[0]); "opcode=0x%x\n", (int)cmd[0]);
#endif #endif
if (check_reset(SCpnt, devip)) { if ((errsts = check_reset(SCpnt, devip)))
done(SCpnt);
return 0;
}
scsi_debug_errsts = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14);
break; break;
} mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x20, 0, 14);
errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
spin_lock_irqsave(&mailbox_lock, iflags);
for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
if (timeout[i].function == NULL)
break; break;
} }
return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay);
/*
* If all of the slots are full, just return 1. The new error
* handling scheme allows this, and the mid-level should queue things.
*/
if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) {
SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n"));
spin_unlock_irqrestore(&mailbox_lock, iflags);
return 1;
}
SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i));
#ifdef IMMEDIATE
if (!scsi_debug_lockup) {
SCpnt->result = scsi_debug_errsts;
SCint[i] = SCpnt;
do_done[i] = done;
scsi_debug_intr_handle(i); /* No timer - do this one right away */
}
spin_unlock_irqrestore(&mailbox_lock, iflags);
#else
SCpnt->result = scsi_debug_errsts;
timeout[i].function = scsi_debug_intr_handle;
timeout[i].data = i;
timeout[i].expires = jiffies + DISK_SPEED;
SCint[i] = SCpnt;
do_done[i] = done;
spin_unlock_irqrestore(&mailbox_lock, iflags);
add_timer(&timeout[i]);
if (!done)
printk(KERN_ERR "scsi_debug_queuecommand: "
"done can't be NULL\n");
#if 0
printk(KERN_INFO "Sending command (%d %x %d %d)...", i, done,
timeout[i].expires, jiffies);
#endif
#endif
return 0;
} }
static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg) static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg)
...@@ -476,58 +399,101 @@ static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg) ...@@ -476,58 +399,101 @@ static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg)
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd); printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
} }
return -ENOTTY; return -EINVAL;
/* return -ENOTTY; // correct return but upsets fdisk */
} }
static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip) static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip)
{ {
if (devip->reset) { if (devip->reset) {
devip->reset = 0; devip->reset = 0;
mk_sense_buffer(devip, 3, UNIT_ATTENTION, 0x29, 0, 14); mk_sense_buffer(devip, UNIT_ATTENTION, 0x29, 0, 14);
SCpnt->result = (COMMAND_COMPLETE << 8) | return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
(CHECK_CONDITION << 1);
return 1;
} }
return 0; return 0;
} }
#define SDEBUG_MAX_INQ_SZ 58 #define SDEBUG_LONG_INQ_SZ 58
#define SDEBUG_MAX_INQ_ARR_SZ 128
static int scsi_debug_inquiry(unsigned char * cmd, int target, static const char * vendor_id = "Linux ";
unsigned char * buff, int bufflen, static const char * product_id = "scsi_debug ";
Sdebug_dev_info * devip) static const char * product_rev = "0004";
static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
const char * dev_id_str, int dev_id_str_len)
{
int num;
/* Two identification descriptors: */
/* T10 vendor identifier field format (faked) */
arr[0] = 0x2; /* ASCII */
arr[1] = 0x1;
arr[2] = 0x0;
memcpy(&arr[4], vendor_id, 8);
memcpy(&arr[12], product_id, 16);
memcpy(&arr[28], dev_id_str, dev_id_str_len);
num = 8 + 16 + dev_id_str_len;
arr[3] = num;
num += 4;
/* NAA IEEE registered identifier (faked) */
arr[num] = 0x1; /* binary */
arr[num + 1] = 0x3;
arr[num + 2] = 0x0;
arr[num + 3] = 0x8;
arr[num + 4] = 0x51; /* ieee company id=0x123456 (faked) */
arr[num + 5] = 0x23;
arr[num + 6] = 0x45;
arr[num + 7] = 0x60;
arr[num + 8] = (dev_id_num >> 24);
arr[num + 9] = (dev_id_num >> 16) & 0xff;
arr[num + 10] = (dev_id_num >> 8) & 0xff;
arr[num + 11] = dev_id_num & 0xff;
return num + 12;
}
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip)
{ {
unsigned char pq_pdt; unsigned char pq_pdt;
unsigned char arr[SDEBUG_MAX_INQ_SZ]; unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
int min_len = bufflen > SDEBUG_MAX_INQ_SZ ? int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ?
SDEBUG_MAX_INQ_SZ : bufflen; SDEBUG_MAX_INQ_ARR_SZ : bufflen;
SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
if (bufflen < cmd[4]) if (bufflen < cmd[4])
printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d " printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "
"< alloc_length=%d\n", bufflen, (int)cmd[4]); "< alloc_length=%d\n", bufflen, (int)cmd[4]);
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_INQ_SZ); memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
pq_pdt = DEVICE_TYPE(target); pq_pdt = PERIPH_DEVICE_TYPE(target);
arr[0] = pq_pdt; arr[0] = pq_pdt;
if (0x2 & cmd[1]) { /* CMDDT bit set */ if (0x2 & cmd[1]) { /* CMDDT bit set */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
} } else if (0x1 & cmd[1]) { /* EVPD bit set */
else if (0x1 & cmd[1]) { /* EVPD bit set */ int dev_id_num, len;
char dev_id_str[6];
dev_id_num = ((devip->sdp->host->host_no + 1) * 1000) +
devip->sdp->id;
len = snprintf(dev_id_str, 6, "%d", dev_id_num);
len = (len > 6) ? 6 : len;
if (0 == cmd[2]) { /* supported vital product data pages */ if (0 == cmd[2]) { /* supported vital product data pages */
arr[3] = 1; arr[3] = 3;
arr[4] = 0x80; /* ... only unit serial number */ arr[4] = 0x0; /* this page */
} arr[5] = 0x80; /* unit serial number */
else if (0x80 == cmd[2]) { /* unit serial number */ arr[6] = 0x83; /* device identification */
} else if (0x80 == cmd[2]) { /* unit serial number */
arr[1] = 0x80; arr[1] = 0x80;
arr[3] = 4; arr[3] = len;
arr[4] = '1'; arr[5] = '2'; arr[6] = '3'; memcpy(&arr[4], dev_id_str, len);
arr[7] = '4'; } else if (0x83 == cmd[2]) { /* device identification */
} arr[1] = 0x83;
else { arr[3] = inquiry_evpd_83(&arr[4], dev_id_num,
dev_id_str, len);
} else {
/* Illegal request, invalid field in cdb */ /* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
} }
memcpy(buff, arr, min_len); memcpy(buff, arr, min_len);
...@@ -536,18 +502,18 @@ static int scsi_debug_inquiry(unsigned char * cmd, int target, ...@@ -536,18 +502,18 @@ static int scsi_debug_inquiry(unsigned char * cmd, int target,
/* drops through here for a standard inquiry */ /* drops through here for a standard inquiry */
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
arr[2] = 3; /* claim SCSI 3 */ arr[2] = 3; /* claim SCSI 3 */
arr[4] = SDEBUG_MAX_INQ_SZ - 5; arr[4] = SDEBUG_LONG_INQ_SZ - 5;
arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */ arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
memcpy(&arr[8], "Linux ", 8); memcpy(&arr[8], vendor_id, 8);
memcpy(&arr[16], "scsi_debug ", 16); memcpy(&arr[16], product_id, 16);
memcpy(&arr[32], "0003", 4); memcpy(&arr[32], product_rev, 4);
memcpy(buff, arr, min_len); memcpy(buff, arr, min_len);
return 0; return 0;
} }
/* <<Following mode page info copied from ST318451LW>> */ /* <<Following mode page info copied from ST318451LW>> */
static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target) static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
{ /* Read-Write Error Recovery page for mode_sense */ { /* Read-Write Error Recovery page for mode_sense */
unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
5, 0, 0xff, 0xff}; 5, 0, 0xff, 0xff};
...@@ -558,7 +524,7 @@ static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target) ...@@ -558,7 +524,7 @@ static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target)
return sizeof(err_recov_pg); return sizeof(err_recov_pg);
} }
static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target) static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
{ /* Disconnect-Reconnect page for mode_sense */ { /* Disconnect-Reconnect page for mode_sense */
unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0}; 0, 0, 0, 0, 0, 0, 0, 0};
...@@ -569,7 +535,25 @@ static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target) ...@@ -569,7 +535,25 @@ static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target)
return sizeof(disconnect_pg); return sizeof(disconnect_pg);
} }
static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target) static int resp_format_pg(unsigned char * p, int pcontrol, int target)
{ /* Format device page for mode_sense */
unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x40, 0, 0, 0};
memcpy(p, format_pg, sizeof(format_pg));
p[10] = (N_SECTOR >> 8) & 0xff;
p[11] = N_SECTOR & 0xff;
p[12] = (SECT_SIZE >> 8) & 0xff;
p[13] = SECT_SIZE & 0xff;
if (DEV_REMOVEABLE(target))
p[20] |= 0x20; /* should agree with INQUIRY */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(format_pg) - 2);
return sizeof(format_pg);
}
static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
{ /* Caching page for mode_sense */ { /* Caching page for mode_sense */
unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
...@@ -580,7 +564,7 @@ static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target) ...@@ -580,7 +564,7 @@ static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target)
return sizeof(caching_pg); return sizeof(caching_pg);
} }
static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target) static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
{ /* Control mode page for mode_sense */ { /* Control mode page for mode_sense */
unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
0, 0, 0x2, 0x4b}; 0, 0, 0x2, 0x4b};
...@@ -594,9 +578,9 @@ static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target) ...@@ -594,9 +578,9 @@ static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
#define SDEBUG_MAX_MSENSE_SZ 256 #define SDEBUG_MAX_MSENSE_SZ 256
static int scsi_debug_mode_sense(unsigned char * cmd, int target, static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen, unsigned char * buff, int bufflen,
Sdebug_dev_info * devip) struct sdebug_dev_info * devip)
{ {
unsigned char dbd; unsigned char dbd;
int pcontrol, pcode; int pcontrol, pcode;
...@@ -622,15 +606,14 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target, ...@@ -622,15 +606,14 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
memset(buff, 0, bufflen); memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
if (0x3 == pcontrol) { /* Saving values not supported */ if (0x3 == pcontrol) { /* Saving values not supported */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x39, 0, 14); mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x39, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
} }
dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
if (msense_6) { if (msense_6) {
arr[2] = dev_spec; arr[2] = dev_spec;
offset = 4; offset = 4;
} } else {
else {
arr[3] = dev_spec; arr[3] = dev_spec;
offset = 8; offset = 8;
} }
...@@ -638,30 +621,34 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target, ...@@ -638,30 +621,34 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
switch (pcode) { switch (pcode) {
case 0x1: /* Read-Write error recovery page, direct access */ case 0x1: /* Read-Write error recovery page, direct access */
len = sdebug_err_recov_pg(ap, pcontrol, target); len = resp_err_recov_pg(ap, pcontrol, target);
offset += len; offset += len;
break; break;
case 0x2: /* Disconnect-Reconnect page, all devices */ case 0x2: /* Disconnect-Reconnect page, all devices */
len = sdebug_disconnect_pg(ap, pcontrol, target); len = resp_disconnect_pg(ap, pcontrol, target);
offset += len;
break;
case 0x3: /* Format device page, direct access */
len = resp_format_pg(ap, pcontrol, target);
offset += len; offset += len;
break; break;
case 0x8: /* Caching page, direct access */ case 0x8: /* Caching page, direct access */
len = sdebug_caching_pg(ap, pcontrol, target); len = resp_caching_pg(ap, pcontrol, target);
offset += len; offset += len;
break; break;
case 0xa: /* Control Mode page, all devices */ case 0xa: /* Control Mode page, all devices */
len = sdebug_ctrl_m_pg(ap, pcontrol, target); len = resp_ctrl_m_pg(ap, pcontrol, target);
offset += len; offset += len;
break; break;
case 0x3f: /* Read all Mode pages */ case 0x3f: /* Read all Mode pages */
len = sdebug_err_recov_pg(ap, pcontrol, target); len = resp_err_recov_pg(ap, pcontrol, target);
len += sdebug_disconnect_pg(ap + len, pcontrol, target); len += resp_disconnect_pg(ap + len, pcontrol, target);
len += sdebug_caching_pg(ap + len, pcontrol, target); len += resp_caching_pg(ap + len, pcontrol, target);
len += sdebug_ctrl_m_pg(ap + len, pcontrol, target); len += resp_ctrl_m_pg(ap + len, pcontrol, target);
offset += len; offset += len;
break; break;
default: default:
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
} }
if (msense_6) if (msense_6)
...@@ -675,8 +662,8 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target, ...@@ -675,8 +662,8 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
return 0; return 0;
} }
static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
int num, int * errstsp, Sdebug_dev_info * devip) struct sdebug_dev_info * devip)
{ {
unsigned char *buff = (unsigned char *) SCpnt->request_buffer; unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount; int nbytes, sgcount;
...@@ -685,30 +672,17 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, ...@@ -685,30 +672,17 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
unsigned long iflags; unsigned long iflags;
if (upper_blk || (block + num > CAPACITY)) { if (upper_blk || (block + num > CAPACITY)) {
*errstsp = (COMMAND_COMPLETE << 8) | mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14);
(CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
return 1;
}
#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
{
int delay = SCSI_SETUP_LATENCY;
delay += SCpnt->request->nr_sectors * SCSI_DATARATE;
if (delay)
usleep(delay);
} }
#endif
if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
(block >= OPT_MEDIUM_ERR_ADDR) && (block >= OPT_MEDIUM_ERR_ADDR) &&
(block < (OPT_MEDIUM_ERR_ADDR + num))) { (block < (OPT_MEDIUM_ERR_ADDR + num))) {
*errstsp = (COMMAND_COMPLETE << 8) | mk_sense_buffer(devip, MEDIUM_ERROR, 0x11, 0, 14);
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, MEDIUM_ERROR, 0x11, 0, 14);
/* claim unrecoverable read error */ /* claim unrecoverable read error */
return 1; return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
} }
read_lock_irqsave(&sdebug_atomic_rw, iflags); read_lock_irqsave(&atomic_rw, iflags);
sgcount = 0; sgcount = 0;
nbytes = bufflen; nbytes = bufflen;
/* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n", /* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n",
...@@ -716,46 +690,29 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, ...@@ -716,46 +690,29 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
if (SCpnt->use_sg) { if (SCpnt->use_sg) {
sgcount = 0; sgcount = 0;
sgpnt = (struct scatterlist *) buff; sgpnt = (struct scatterlist *) buff;
buff = sdebug_scatg2virt(&sgpnt[sgcount]); buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length; bufflen = sgpnt[sgcount].length;
} }
*errstsp = 0;
do { do {
memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen); memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen);
#if 0
/* Simulate a disk change */
if (block == 0xfff0) {
sense_buffer[0] = 0x70;
sense_buffer[2] = UNIT_ATTENTION;
starts[0] += 10;
starts[1] += 10;
starts[2] += 10;
*errstsp = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
return 1;
} /* End phony disk change code */
#endif
nbytes -= bufflen; nbytes -= bufflen;
if (SCpnt->use_sg) { if (SCpnt->use_sg) {
block += bufflen >> POW2_SECT_SIZE; block += bufflen >> POW2_SECT_SIZE;
sgcount++; sgcount++;
if (nbytes) { if (nbytes) {
buff = sdebug_scatg2virt(&sgpnt[sgcount]); buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length; bufflen = sgpnt[sgcount].length;
} }
} } else if (nbytes > 0)
else if (nbytes > 0) printk(KERN_WARNING "scsi_debug:resp_read: unexpected "
printk(KERN_WARNING "sdebug_read: unexpected "
"nbytes=%d\n", nbytes); "nbytes=%d\n", nbytes);
} while (nbytes); } while (nbytes);
read_unlock_irqrestore(&sdebug_atomic_rw, iflags); read_unlock_irqrestore(&atomic_rw, iflags);
return 0; return 0;
} }
static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
int num, int * errstsp, Sdebug_dev_info * devip) struct sdebug_dev_info * devip)
{ {
unsigned char *buff = (unsigned char *) SCpnt->request_buffer; unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount; int nbytes, sgcount;
...@@ -764,22 +721,19 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, ...@@ -764,22 +721,19 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
unsigned long iflags; unsigned long iflags;
if (upper_blk || (block + num > CAPACITY)) { if (upper_blk || (block + num > CAPACITY)) {
*errstsp = (COMMAND_COMPLETE << 8) | mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14);
(CHECK_CONDITION << 1); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
return 1;
} }
write_lock_irqsave(&sdebug_atomic_rw, iflags); write_lock_irqsave(&atomic_rw, iflags);
sgcount = 0; sgcount = 0;
nbytes = bufflen; nbytes = bufflen;
if (SCpnt->use_sg) { if (SCpnt->use_sg) {
sgcount = 0; sgcount = 0;
sgpnt = (struct scatterlist *) buff; sgpnt = (struct scatterlist *) buff;
buff = sdebug_scatg2virt(&sgpnt[sgcount]); buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length; bufflen = sgpnt[sgcount].length;
} }
*errstsp = 0;
do { do {
memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen); memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen);
...@@ -788,104 +742,64 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, ...@@ -788,104 +742,64 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
block += bufflen >> POW2_SECT_SIZE; block += bufflen >> POW2_SECT_SIZE;
sgcount++; sgcount++;
if (nbytes) { if (nbytes) {
buff = sdebug_scatg2virt(&sgpnt[sgcount]); buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length; bufflen = sgpnt[sgcount].length;
} }
} } else if (nbytes > 0)
else if (nbytes > 0) printk(KERN_WARNING "scsi_debug:resp_write: "
printk(KERN_WARNING "sdebug_write: "
"unexpected nbytes=%d\n", nbytes); "unexpected nbytes=%d\n", nbytes);
} while (nbytes); } while (nbytes);
write_unlock_irqrestore(&sdebug_atomic_rw, iflags); write_unlock_irqrestore(&atomic_rw, iflags);
return 0; return 0;
} }
/* A "high" level interrupt handler. This should be called once per jiffy static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
* to simulate a regular scsi disk. We use a timer to do this. */ int bufflen, struct sdebug_dev_info * devip)
static void scsi_debug_intr_handle(unsigned long indx)
{ {
Scsi_Cmnd *SCtmp; unsigned int alloc_len;
void (*my_done) (Scsi_Cmnd *); int select_report = (int)cmd[2];
#if 0
del_timer(&timeout[indx]);
#endif
SCtmp = (Scsi_Cmnd *) SCint[indx];
my_done = do_done[indx];
do_done[indx] = NULL;
timeout[indx].function = NULL;
SCint[indx] = NULL;
if (!my_done) { alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
printk(KERN_ERR "scsi_debug_intr_handle: Unexpected " if ((alloc_len < 16) || (select_report > 2)) {
"interrupt\n"); mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return; return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
if (bufflen > 3) {
memset(buff, 0, bufflen);
buff[3] = 8;
} }
#if 0
printk(KERN_INFO "In intr_handle...");
printk(KERN_INFO "...done %d %x %d %d\n", i, my_done, to, jiffies);
printk(KERN_INFO "In intr_handle: %d %x %x\n", i, SCtmp, my_done);
#endif
my_done(SCtmp);
#if 0
printk(KERN_INFO "Called done.\n");
#endif
}
static int initialized = 0;
static int do_init(void)
{
int sz = STORE_SIZE;
starts[3] = CAPACITY;
fake_storep = vmalloc(sz);
if (NULL == fake_storep)
return 1;
memset(fake_storep, 0, sz);
sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES;
do_done = kmalloc(sz, GFP_ATOMIC);
if (NULL == do_done)
goto out;
memset((void *)do_done, 0, sz);
sz = sizeof(struct timer_list) * SCSI_DEBUG_MAILBOXES;
timeout = kmalloc(sz, GFP_ATOMIC);
if (NULL == timeout)
goto out;
memset(timeout, 0, sz);
sz = sizeof(Scsi_Cmnd *) * SCSI_DEBUG_MAILBOXES;
SCint = kmalloc(sz, GFP_ATOMIC);
if (NULL == SCint)
goto out;
memset(SCint, 0, sz);
return 0; return 0;
out:
if (fake_storep)
vfree(fake_storep);
if (do_done)
kfree((void *)do_done);
if (timeout)
kfree(timeout);
if (SCint)
kfree(SCint);
return 1;
} }
static void do_end(void) /* When timer goes off this function is called. */
static void timer_intr_handler(unsigned long indx)
{ {
kfree(SCint); struct sdebug_queued_cmd * sqcp;
kfree(timeout); unsigned int iflags;
kfree((void *)do_done);
vfree(fake_storep); if (indx >= SCSI_DEBUG_CANQUEUE) {
printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
"large\n");
return;
}
spin_lock_irqsave(&queued_arr_lock, iflags);
sqcp = &queued_arr[(int)indx];
if (! sqcp->in_use) {
printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
"interrupt\n");
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return;
}
sqcp->in_use = 0;
if (sqcp->done_funct)
sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
sqcp->done_funct = NULL;
spin_unlock_irqrestore(&queued_arr_lock, iflags);
} }
static int initialized = 0;
static int num_present = 0;
static const char * sdebug_proc_name = "scsi_debug";
static int scsi_debug_detect(Scsi_Host_Template * tpnt) static int scsi_debug_detect(Scsi_Host_Template * tpnt)
{ {
...@@ -895,28 +809,41 @@ static int scsi_debug_detect(Scsi_Host_Template * tpnt) ...@@ -895,28 +809,41 @@ static int scsi_debug_detect(Scsi_Host_Template * tpnt)
printk(KERN_INFO "scsi_debug: detect\n"); printk(KERN_INFO "scsi_debug: detect\n");
if (0 == initialized) { if (0 == initialized) {
++initialized; ++initialized;
sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs; sz = sizeof(struct sdebug_dev_info) * scsi_debug_num_devs;
devInfop = kmalloc(sz, GFP_ATOMIC); devInfop = vmalloc(sz);
if (NULL == devInfop) { if (NULL == devInfop) {
printk(KERN_ERR "scsi_debug_detect: out of " printk(KERN_ERR "scsi_debug_detect: out of "
"memory, 0.5\n"); "memory, 0.5\n");
return 0; return 0;
} }
memset(devInfop, 0, sz); memset(devInfop, 0, sz);
if (do_init()) { sz = STORE_SIZE;
fake_storep = vmalloc(sz);
if (NULL == fake_storep) {
printk(KERN_ERR "scsi_debug_detect: out of memory" printk(KERN_ERR "scsi_debug_detect: out of memory"
", 0\n"); ", 0\n");
return 0; return 0;
} }
for (k = 0; k < NUM_SENSE_BUFFS; ++k) memset(fake_storep, 0, sz);
sense_buffers[k][0] = 0x70; init_all_queued();
for (k = 0; k < NR_HOSTS_PRESENT; k++) { tpnt->proc_name = (char *)sdebug_proc_name;
tpnt->proc_name = "scsi_debug"; /* In the loop??? */ for (num_present = 0, k = 0; k < NR_HOSTS_PRESENT; k++) {
scsi_register(tpnt, 0); if (NULL == scsi_register(tpnt, 0))
printk(KERN_ERR "scsi_debug_detect: "
"scsi_register failed k=%d\n", k);
else
++num_present;
} }
return NR_HOSTS_PRESENT; #ifdef DRIVERFS_SUPPORT
if (num_present) {
sdebug_driverfs_driver.name = (char *)sdebug_proc_name;
sdebug_driverfs_driver.bus = &scsi_driverfs_bus_type;
driver_register(&sdebug_driverfs_driver);
do_create_driverfs_files();
} }
else { #endif
return num_present;
} else {
printk(KERN_WARNING "scsi_debug_detect: called again\n"); printk(KERN_WARNING "scsi_debug_detect: called again\n");
return 0; return 0;
} }
...@@ -929,51 +856,52 @@ static int scsi_debug_release(struct Scsi_Host * hpnt) ...@@ -929,51 +856,52 @@ static int scsi_debug_release(struct Scsi_Host * hpnt)
{ {
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: release\n"); printk(KERN_INFO "scsi_debug: release\n");
stop_all_queued();
scsi_unregister(hpnt); scsi_unregister(hpnt);
if (++num_releases != NR_HOSTS_PRESENT) if (++num_releases == num_present) {
return 0; #ifdef DRIVERFS_SUPPORT
do_end(); do_remove_driverfs_files();
kfree(devInfop); remove_driver(&sdebug_driverfs_driver);
// driver_unregister(&sdebug_driverfs_driver);
#endif
vfree(fake_storep);
vfree(devInfop);
}
return 0; return 0;
} }
static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp) static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp)
{ {
int k; int k;
unsigned short host_no, id; struct sdebug_dev_info * devip;
Sdebug_dev_info * devip;
host_no = sdp->host->host_no;
id = (unsigned short)sdp->id;
for (k = 0; k < scsi_debug_num_devs; ++k) { for (k = 0; k < scsi_debug_num_devs; ++k) {
devip = &devInfop[k]; devip = &devInfop[k];
if (devip->sdp && (host_no == devip->host_no) && if (devip->sdp == sdp)
(id == devip->id)) {
devip->sdp = sdp; /* overwrite previous sdp */
return devip; return devip;
} }
for (k = 0; k < scsi_debug_num_devs; ++k) {
devip = &devInfop[k];
if (NULL == devip->sdp) { if (NULL == devip->sdp) {
devip->sdp = sdp; devip->sdp = sdp;
devip->host_no = host_no;
devip->id = id;
devip->reset = 1; devip->reset = 1;
devip->sb_index = 0; memset(devip->sense_buff, 0, SDEBUG_SENSE_LEN);
devip->sense_buff[0] = 0x70;
return devip; return devip;
} }
} }
return NULL; return NULL;
} }
static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen) int asc, int asq, int inbandLen)
{ {
char * sbuff; unsigned char * sbuff;
if ((index < 0) || (index >= NUM_SENSE_BUFFS))
return; sbuff = devip->sense_buff;
if (devip) memset(sbuff, 0, SDEBUG_SENSE_LEN);
devip->sb_index = index; if (inbandLen > SDEBUG_SENSE_LEN)
sbuff = &sense_buffers[index][0]; inbandLen = SDEBUG_SENSE_LEN;
memset(sbuff, 0, SENSE_BUFF_LEN);
sbuff[0] = 0x70; sbuff[0] = 0x70;
sbuff[2] = key; sbuff[2] = key;
sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0; sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
...@@ -985,30 +913,13 @@ static int scsi_debug_abort(Scsi_Cmnd * SCpnt) ...@@ -985,30 +913,13 @@ static int scsi_debug_abort(Scsi_Cmnd * SCpnt)
{ {
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: abort\n"); printk(KERN_INFO "scsi_debug: abort\n");
#if 1
++num_aborts; ++num_aborts;
stop_queued_cmnd(SCpnt);
return SUCCESS; return SUCCESS;
#else
int j;
void (*my_done) (Scsi_Cmnd *);
unsigned long iflags;
SCpnt->result = SCpnt->abort_reason << 16;
for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) {
if (SCpnt == SCint[j]) {
my_done = do_done[j];
my_done(SCpnt);
spin_lock_irqsave(&mailbox_lock, iflags);
timeout[j] = 0;
SCint[j] = NULL;
do_done[j] = NULL;
spin_unlock_irqrestore(&mailbox_lock, iflags);
}
}
return SCSI_ABORT_SNOOZE;
#endif
} }
static int scsi_debug_biosparam(Disk * disk, struct block_device *dev, int *info) static int scsi_debug_biosparam(Disk * disk, struct block_device * bdev,
int *info)
{ {
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: biosparam\n"); printk(KERN_INFO "scsi_debug: biosparam\n");
...@@ -1067,12 +978,124 @@ static int scsi_debug_host_reset(Scsi_Cmnd * SCpnt) ...@@ -1067,12 +978,124 @@ static int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
++num_host_resets; ++num_host_resets;
for (k = 0; k < scsi_debug_num_devs; ++k) for (k = 0; k < scsi_debug_num_devs; ++k)
devInfop[k].reset = 1; devInfop[k].reset = 1;
stop_all_queued();
return SUCCESS; return SUCCESS;
} }
/* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
del_timer_sync(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
break;
}
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
}
/* Deletes (stops) timers of all queued commands */
static void stop_all_queued(void)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (sqcp->in_use && sqcp->a_cmnd) {
del_timer_sync(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
}
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
/* Initializes timers in queued array */
static void init_all_queued(void)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
init_timer(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
static int schedule_resp(struct scsi_cmnd * cmnd,
struct sdebug_dev_info * devip,
done_funct_t done, int scsi_result, int delta_jiff)
{
int k, num;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = cmnd->cmd_len; k < num; ++k)
printk("%02x ", (int)cmnd->cmnd[k]);
printk("result=0x%x\n", scsi_result);
}
if (cmnd && devip) {
/* simulate autosense by this driver */
if (CHECK_CONDITION == status_byte(scsi_result))
memcpy(cmnd->sense_buffer, devip->sense_buff,
(SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
}
if (delta_jiff <= 0) {
if (cmnd)
cmnd->result = scsi_result;
if (done)
done(cmnd);
return 0;
} else {
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp = NULL;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (! sqcp->in_use)
break;
}
if (k >= SCSI_DEBUG_CANQUEUE) {
spin_unlock_irqrestore(&queued_arr_lock, iflags);
printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
return 1; /* report busy to mid level */
}
sqcp->in_use = 1;
sqcp->a_cmnd = cmnd;
sqcp->scsi_result = scsi_result;
sqcp->done_funct = done;
sqcp->cmnd_timer.function = timer_intr_handler;
sqcp->cmnd_timer.data = k;
sqcp->cmnd_timer.expires = jiffies + delta_jiff;
add_timer(&sqcp->cmnd_timer);
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return 0;
}
}
#ifndef MODULE #ifndef MODULE
static int __init scsi_debug_num_devs_setup(char *str) static int __init num_devs_setup(char *str)
{ {
int tmp; int tmp;
...@@ -1086,10 +1109,9 @@ static int __init scsi_debug_num_devs_setup(char *str) ...@@ -1086,10 +1109,9 @@ static int __init scsi_debug_num_devs_setup(char *str)
return 0; return 0;
} }
} }
__setup("scsi_debug_num_devs=", num_devs_setup);
__setup("scsi_debug_num_devs=", scsi_debug_num_devs_setup); static int __init dev_size_mb_setup(char *str)
static int __init scsi_debug_dev_size_mb_setup(char *str)
{ {
int tmp; int tmp;
...@@ -1104,10 +1126,9 @@ static int __init scsi_debug_dev_size_mb_setup(char *str) ...@@ -1104,10 +1126,9 @@ static int __init scsi_debug_dev_size_mb_setup(char *str)
return 0; return 0;
} }
} }
__setup("scsi_debug_dev_size_mb=", dev_size_mb_setup);
__setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup); static int __init opts_setup(char *str)
static int __init scsi_debug_opts_setup(char *str)
{ {
int tmp; int tmp;
...@@ -1122,8 +1143,41 @@ static int __init scsi_debug_opts_setup(char *str) ...@@ -1122,8 +1143,41 @@ static int __init scsi_debug_opts_setup(char *str)
return 0; return 0;
} }
} }
__setup("scsi_debug_opts=", opts_setup);
static int __init every_nth_setup(char *str)
{
int tmp;
if (get_option(&str, &tmp) == 1) {
if (tmp > 0)
scsi_debug_every_nth = tmp;
return 1;
} else {
printk(KERN_INFO "scsi_debug_every_nth: usage "
"scsi_debug_every_nth=<n>\n"
" timeout every nth command (when ...)\n");
return 0;
}
}
__setup("scsi_debug_every_nth=", every_nth_setup);
static int __init delay_setup(char *str)
{
int tmp;
if (get_option(&str, &tmp) == 1) {
scsi_debug_delay = tmp;
return 1;
} else {
printk(KERN_INFO "scsi_debug_delay: usage "
"scsi_debug_delay=<n>\n"
" delay response <n> jiffies\n");
return 0;
}
}
__setup("scsi_debug_delay=", delay_setup);
__setup("scsi_debug_opts=", scsi_debug_opts_setup);
#endif #endif
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
...@@ -1134,10 +1188,11 @@ MODULE_PARM(scsi_debug_dev_size_mb, "i"); ...@@ -1134,10 +1188,11 @@ MODULE_PARM(scsi_debug_dev_size_mb, "i");
MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs"); MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
MODULE_PARM(scsi_debug_opts, "i"); MODULE_PARM(scsi_debug_opts, "i");
MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->..."); MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->...");
MODULE_PARM(scsi_debug_every_nth, "i");
#ifdef MODULE_LICENSE MODULE_PARM_DESC(scsi_debug_every_nth, "timeout every nth command(def=100)");
MODULE_PARM(scsi_debug_delay, "i");
MODULE_PARM_DESC(scsi_debug_delay, "# of jiffies to delay response(def=1)");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#endif
static char sdebug_info[256]; static char sdebug_info[256];
...@@ -1172,17 +1227,21 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, ...@@ -1172,17 +1227,21 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
if (1 != sscanf(arr, "%d", &pos)) if (1 != sscanf(arr, "%d", &pos))
return -EINVAL; return -EINVAL;
scsi_debug_opts = pos; scsi_debug_opts = pos;
if (SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts)
scsi_debug_cmnd_count = 0;
return length; return length;
} }
begin = 0; begin = 0;
pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n" pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
"num_devs=%d, shared (ram) size=%d MB, opts=0x%x\n" "num_devs=%d, shared (ram) size=%d MB, opts=0x%x, "
"sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n" "every_nth=%d(curr:%d)\n"
"number of aborts=%d, device_reset=%d, bus_resets=%d, " "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d, "
"delay=%d\nnumber of aborts=%d, device_reset=%d, bus_resets=%d, "
"host_resets=%d\n", "host_resets=%d\n",
scsi_debug_version_str, scsi_debug_num_devs, scsi_debug_version_str, scsi_debug_num_devs,
scsi_debug_dev_size_mb, scsi_debug_opts, SECT_SIZE, scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
N_CYLINDER, N_HEAD, N_SECTOR, scsi_debug_cmnd_count,
SECT_SIZE, N_CYLINDER, N_HEAD, N_SECTOR, scsi_debug_delay,
num_aborts, num_dev_resets, num_bus_resets, num_host_resets); num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
if (pos < offset) { if (pos < offset) {
len = 0; len = 0;
...@@ -1196,8 +1255,86 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, ...@@ -1196,8 +1255,86 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
return (len); return (len);
} }
/* Eventually this will go into an include file, but this will be later */ #ifdef DRIVERFS_SUPPORT
static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE; static ssize_t sdebug_delay_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_delay);
}
#include "scsi_module.c" static ssize_t sdebug_delay_write(struct device_driver * ddp,
const char * buf, size_t count, loff_t off)
{
int delay;
char work[20];
if (off)
return 0;
if (1 == sscanf(buf, "%10s", work)) {
if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
scsi_debug_delay = delay;
return count;
}
}
return -EINVAL;
}
DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_read,
sdebug_delay_write)
static ssize_t sdebug_opts_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "0x%x\n", scsi_debug_opts);
}
static ssize_t sdebug_opts_write(struct device_driver * ddp,
const char * buf, size_t count, loff_t off)
{
int opts;
char work[20];
if (off)
return 0;
if (1 == sscanf(buf, "%10s", work)) {
if (0 == strnicmp(work,"0x", 2)) {
if (1 == sscanf(&work[2], "%x", &opts))
goto opts_done;
} else {
if (1 == sscanf(work, "%d", &opts))
goto opts_done;
}
}
return -EINVAL;
opts_done:
scsi_debug_opts = opts;
return count;
}
DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_read,
sdebug_opts_write)
static ssize_t sdebug_num_devs_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_num_devs);
}
DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_read, NULL)
static void do_create_driverfs_files()
{
driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_devs);
}
static void do_remove_driverfs_files()
{
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_devs);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
}
#endif
#include "scsi_module.c"
#ifndef _SCSI_DEBUG_H #ifndef _SCSI_DEBUG_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/kdev_t.h>
static int scsi_debug_detect(Scsi_Host_Template *); static int scsi_debug_detect(Scsi_Host_Template *);
static int scsi_debug_release(struct Scsi_Host *);
/* static int scsi_debug_command(Scsi_Cmnd *); */ /* static int scsi_debug_command(Scsi_Cmnd *); */
static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int scsi_debug_abort(Scsi_Cmnd *); static int scsi_debug_ioctl(Scsi_Device *, int, void *);
static int scsi_debug_biosparam(Disk *, struct block_device *, int[]); static int scsi_debug_biosparam(Disk *, struct block_device *, int[]);
static int scsi_debug_abort(Scsi_Cmnd *);
static int scsi_debug_bus_reset(Scsi_Cmnd *); static int scsi_debug_bus_reset(Scsi_Cmnd *);
static int scsi_debug_device_reset(Scsi_Cmnd *); static int scsi_debug_device_reset(Scsi_Cmnd *);
static int scsi_debug_host_reset(Scsi_Cmnd *); static int scsi_debug_host_reset(Scsi_Cmnd *);
...@@ -20,29 +23,29 @@ static const char * scsi_debug_info(struct Scsi_Host *); ...@@ -20,29 +23,29 @@ static const char * scsi_debug_info(struct Scsi_Host *);
/* /*
* This driver is written for the lk 2.5 series * This driver is written for the lk 2.5 series
*/ */
#define SCSI_DEBUG_CANQUEUE 255 #define SCSI_DEBUG_CANQUEUE 255 /* needs to be >= 1 */
#define SCSI_DEBUG_MAX_CMD_LEN 16 #define SCSI_DEBUG_MAX_CMD_LEN 16
#define SCSI_DEBUG_TEMPLATE \ static Scsi_Host_Template driver_template = {
{proc_info: scsi_debug_proc_info, \ .proc_info = scsi_debug_proc_info,
name: "SCSI DEBUG", \ .name = "SCSI DEBUG",
info: scsi_debug_info, \ .info = scsi_debug_info,
detect: scsi_debug_detect, \ .detect = scsi_debug_detect,
release: scsi_debug_release, \ .release = scsi_debug_release,
ioctl: scsi_debug_ioctl, \ .ioctl = scsi_debug_ioctl,
queuecommand: scsi_debug_queuecommand, \ .queuecommand = scsi_debug_queuecommand,
eh_abort_handler: scsi_debug_abort, \ .eh_abort_handler = scsi_debug_abort,
eh_bus_reset_handler: scsi_debug_bus_reset, \ .eh_bus_reset_handler = scsi_debug_bus_reset,
eh_device_reset_handler: scsi_debug_device_reset, \ .eh_device_reset_handler = scsi_debug_device_reset,
eh_host_reset_handler: scsi_debug_host_reset, \ .eh_host_reset_handler = scsi_debug_host_reset,
bios_param: scsi_debug_biosparam, \ .bios_param = scsi_debug_biosparam,
can_queue: SCSI_DEBUG_CANQUEUE, \ .can_queue = SCSI_DEBUG_CANQUEUE,
this_id: 7, \ .this_id = 7,
sg_tablesize: 64, \ .sg_tablesize = 64,
cmd_per_lun: 3, \ .cmd_per_lun = 3,
unchecked_isa_dma: 0, \ .unchecked_isa_dma = 0,
use_clustering: ENABLE_CLUSTERING, \ .use_clustering = ENABLE_CLUSTERING,
} }; /* the name 'driver_template' is used by scsi_module.c */
#endif #endif
...@@ -34,12 +34,15 @@ scsi_module.c is normally included at the end of a lower ...@@ -34,12 +34,15 @@ scsi_module.c is normally included at the end of a lower
level driver. For it to work a declaration like this is needed before level driver. For it to work a declaration like this is needed before
it is included: it is included:
static Scsi_Host_Template driver_template = DRIVER_TEMPLATE; static Scsi_Host_Template driver_template = DRIVER_TEMPLATE;
/* DRIVER_TEMPLATE should contain pointers to supported interface
functions. Scsi_Host_Template is defined in hosts.h */
#include "scsi_module.c" #include "scsi_module.c"
In this case "DRIVER_TEMPLATE" is defined to be a structure initializer
that is placed in the driver header file by convention. It contains
pointers to supported interface functions and other values.
Scsi_Host_Template is defined in hosts.h .
The scsi_module.c assumes the name "driver_template" is appropriately The scsi_module.c assumes the name "driver_template" is appropriately
defined. It contains 2 functions: defined. scsi_module.c contains 2 functions:
1) init_this_scsi_driver() called during builtin and module driver 1) init_this_scsi_driver() called during builtin and module driver
initialization: invokes mid level's scsi_register_host() initialization: invokes mid level's scsi_register_host()
2) exit_this_scsi_driver() called during closedown: invokes 2) exit_this_scsi_driver() called during closedown: invokes
...@@ -68,7 +71,7 @@ The interface functions are listed below in alphabetical order. ...@@ -68,7 +71,7 @@ The interface functions are listed below in alphabetical order.
/** /**
* bios_param - fetch head, sector, cylinder info for a disk * bios_param - fetch head, sector, cylinder info for a disk
* @sdkp: pointer to disk structure (defined in sd.h) * @sdkp: pointer to disk structure (defined in sd.h)
* @dev: corresponds to dev_t of device file name (e.g. /dev/sdb) * @bdev: pointer to block device context (defined in fs.h)
* @params: three element array to place output: * @params: three element array to place output:
* params[0] number of heads * params[0] number of heads
* params[1] number of sectors * params[1] number of sectors
...@@ -267,6 +270,8 @@ The interface functions are listed below in alphabetical order. ...@@ -267,6 +270,8 @@ The interface functions are listed below in alphabetical order.
* unsupported ioctl() 'cmd' numbers should return -ENOTTY. * unsupported ioctl() 'cmd' numbers should return -ENOTTY.
* However the mid level returns -EINVAL for unrecognized 'cmd' * However the mid level returns -EINVAL for unrecognized 'cmd'
* numbers when this function is not supplied by the driver. * numbers when this function is not supplied by the driver.
* Unfortunately some applications expect -EINVAL and react badly
* when -ENOTTY is returned; stick with -EINVAL.
**/ **/
int ioctl(Scsi_Device *sdp, int cmd, void *arg); int ioctl(Scsi_Device *sdp, int cmd, void *arg);
...@@ -304,7 +309,10 @@ int proc_info(char * buffer, char ** start, off_t offset, ...@@ -304,7 +309,10 @@ int proc_info(char * buffer, char ** start, off_t offset,
* @scp: pointer to scsi command object * @scp: pointer to scsi command object
* @done: function pointer to be invoked on completion * @done: function pointer to be invoked on completion
* *
* Returns 1 if the adapter is busy, else returns 0. * Returns 1 if the adapter (host) is busy, else returns 0. One
* reason for an adapter to be busy is that the number
* of outstanding queued commands is already equal to
* Scsi_Host::can_queue .
* *
* Required: if Scsi_Host::can_queue is ever non-zero * Required: if Scsi_Host::can_queue is ever non-zero
* then this function is required. * then this function is required.
...@@ -324,6 +332,9 @@ int proc_info(char * buffer, char ** start, off_t offset, ...@@ -324,6 +332,9 @@ int proc_info(char * buffer, char ** start, off_t offset,
* return value should be generated by this function. However, in * return value should be generated by this function. However, in
* this case, it should be placed in scp->result before this function * this case, it should be placed in scp->result before this function
* returns. * returns.
* If a status of CHECK CONDITION is placed in "result" when the
* 'done' callback is invoked, then the lower level driver should
* perform autosense and fill in the Scsi_Cmnd::sense_buffer array.
**/ **/
int queuecommand(Scsi_Cmnd * scp, void (*done)(Scsi_Cmnd *)); int queuecommand(Scsi_Cmnd * scp, void (*done)(Scsi_Cmnd *));
...@@ -505,6 +516,28 @@ lock of equal granularity (i.e. per host). Using finer grain locks ...@@ -505,6 +516,28 @@ lock of equal granularity (i.e. per host). Using finer grain locks
(e.g. per scsi device) may be possible by juggling locks in (e.g. per scsi device) may be possible by juggling locks in
queuecommand(). queuecommand().
Autosense
=========
Autosense (or auto-sense) is defined in the SAM-2 document as "the
automatic return of sense data to the application client coincident
with the completion of a SCSI command" when a status of CHECK CONDITION
occurs. Lower level drivers should perform autosense. This should be
done when the lower level driver detects a CHECK CONDITION status by either:
a) instructing the SCSI protocol (e.g. SCSI Parallel Interface (SPI))
to perform an extra data in phase on such responses
b) or, the lower level driver issuing a REQUEST SENSE command itself
Either way, the mid level decides whether the lower level driver has
performed autosense by checking Scsi_Cmnd::sense_buffer[0] . If this
byte has an upper nibble of 7 then autosense is assumed to have taken
place. If it has another value (and this byte is initialized to 0 before
each command) then the mid level will issue a REQUEST SENSE command.
In the presence of queued commands the "nexus" that maintains sense
buffer data from the command that failed until a following REQUEST SENSE
may get out of synchronization. This is why it is best for the lower
level driver to perform autosense.
Changes since lk 2.4 series Changes since lk 2.4 series
=========================== ===========================
...@@ -514,6 +547,7 @@ per scsi host. ...@@ -514,6 +547,7 @@ per scsi host.
The older error handling mechanism has been removed. This means the The older error handling mechanism has been removed. This means the
lower level interface functions abort() and reset() have been removed. lower level interface functions abort() and reset() have been removed.
The Scsi_Host_Template::use_new_eh_code flag has been removed.
In the 2.4 series the scsi subsystem configuration descriptions were In the 2.4 series the scsi subsystem configuration descriptions were
aggregated with the configuration descriptions from all other Linux aggregated with the configuration descriptions from all other Linux
...@@ -532,4 +566,4 @@ The following people have contributed to this document: ...@@ -532,4 +566,4 @@ The following people have contributed to this document:
Douglas Gilbert Douglas Gilbert
dgilbert@interlog.com dgilbert@interlog.com
27th April 2002 13th August 2002
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -11,9 +11,13 @@ Original driver (sg.h): ...@@ -11,9 +11,13 @@ Original driver (sg.h):
Version 2 and 3 extensions to driver: Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2002 Douglas Gilbert * Copyright (C) 1998 - 2002 Douglas Gilbert
Version: 3.5.26 (20020708) Version: 3.5.27 (20020812)
This version is for 2.5 series kernels. This version is for 2.5 series kernels.
Changes since 3.5.26 (20020708)
- re-add direct IO using Kai Makisara's work
- re-tab to 8, start using C99-isms
- simplify memory management
Changes since 3.5.25 (20020504) Changes since 3.5.25 (20020504)
- driverfs additions - driverfs additions
- copy_to/from_user() fixes [William Stinson] - copy_to/from_user() fixes [William Stinson]
......
...@@ -129,6 +129,7 @@ EXPORT_SYMBOL(highmem_start_page); ...@@ -129,6 +129,7 @@ EXPORT_SYMBOL(highmem_start_page);
EXPORT_SYMBOL(kmap_prot); EXPORT_SYMBOL(kmap_prot);
EXPORT_SYMBOL(kmap_pte); EXPORT_SYMBOL(kmap_pte);
#endif #endif
EXPORT_SYMBOL(get_user_pages);
/* filesystem internal functions */ /* filesystem internal functions */
EXPORT_SYMBOL(def_blk_fops); EXPORT_SYMBOL(def_blk_fops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment