upgrade to linux 2.6.10-1.12_FC2
[linux-2.6.git] / drivers / scsi / scsi_debug.c
index b950984..829c445 100644 (file)
@@ -55,8 +55,8 @@
 #include "scsi_logging.h"
 #include "scsi_debug.h"
 
-#define SCSI_DEBUG_VERSION "1.73"
-static const char * scsi_debug_version_date = "20040518";
+#define SCSI_DEBUG_VERSION "1.74"
+static const char * scsi_debug_version_date = "20040829";
 
 /* Additional Sense Code (ASC) used */
 #define NO_ADDED_SENSE 0x0
@@ -82,8 +82,9 @@ static const char * scsi_debug_version_date = "20040518";
 #define DEF_EVERY_NTH   0
 #define DEF_NUM_PARTS   0
 #define DEF_OPTS   0
-#define DEF_SCSI_LEVEL   3
+#define DEF_SCSI_LEVEL   4    /* SPC-2 */
 #define DEF_PTYPE   0
+#define DEF_D_SENSE   0
 
 /* bit mask values for scsi_debug_opts */
 #define SCSI_DEBUG_OPT_NOISE   1
@@ -114,6 +115,7 @@ static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
 static int scsi_debug_opts = DEF_OPTS;
 static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
 static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
+static int scsi_debug_dsense = DEF_D_SENSE;
 
 static int scsi_debug_cmnd_count = 0;
 
@@ -277,7 +279,7 @@ static
 int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
 {
        unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
-       int block, upper_blk, num;
+       int block, upper_blk, num, k;
        unsigned char *buff;
        int errsts = 0;
        int target = SCpnt->device->id;
@@ -305,7 +307,12 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
                bufflen = SDEBUG_SENSE_LEN;
        }
 
-
+       if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
+               printk(KERN_INFO "scsi_debug: cmd ");
+               for (k = 0, num = SCpnt->cmd_len; k < num; ++k)
+                       printk("%02x ", (int)cmd[k]);
+               printk("\n");
+       }
         if(target == sdebug_driver_template.this_id) {
                printk(KERN_INFO "scsi_debug: initiator's id used as "
                       "target!\n");
@@ -331,14 +338,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
         }
 
        switch (*cmd) {
-       case INQUIRY:     /* mandatory */
+       case INQUIRY:     /* mandatory, ignore unit attention */
                errsts = resp_inquiry(cmd, target, buff, bufflen, devip);
                break;
-       case REQUEST_SENSE:     /* mandatory */
-               /* Since this driver indicates autosense by placing the
-                * sense buffer in the scsi_cmnd structure in the response
-                * (when SAM_STAT_CHECK_CONDITION is set), the mid level
-                * shouldn't need to call REQUEST_SENSE */
+       case REQUEST_SENSE:     /* mandatory, ignore unit attention */
                if (devip) {
                        sbuff = devip->sense_buff;
                        memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ?
@@ -349,20 +352,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
                        buff[0] = 0x70;
                }
                break;
+       case REZERO_UNIT:       /* actually this is REWIND for SSC */
        case START_STOP:
                errsts = check_reset(SCpnt, devip);
+               memset(buff, 0, bufflen);
                break;
        case ALLOW_MEDIUM_REMOVAL:
                if ((errsts = check_reset(SCpnt, devip)))
                        break;
                if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
-                       printk("\tMedium removal %s\n",
-                              cmd[4] ? "inhibited" : "enabled");
+                       printk(KERN_INFO "scsi_debug: Medium removal %s\n",
+                               cmd[4] ? "inhibited" : "enabled");
                break;
        case SEND_DIAGNOSTIC:     /* mandatory */
+               errsts = check_reset(SCpnt, devip);
                memset(buff, 0, bufflen);
                break;
        case TEST_UNIT_READY:     /* mandatory */
+               errsts = check_reset(SCpnt, devip);
                memset(buff, 0, bufflen);
                break;
         case RESERVE:
@@ -429,7 +436,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
                        errsts = check_condition_result;
                }
                break;
-       case REPORT_LUNS:
+       case REPORT_LUNS:       /* mandatory, ignore unit attention */
                errsts = resp_report_luns(cmd, buff, bufflen, devip);
                break;
        case WRITE_16:
@@ -469,14 +476,20 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
                break;
        case MODE_SENSE:
        case MODE_SENSE_10:
+               if ((errsts = check_reset(SCpnt, devip)))
+                       break;
                errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
                break;
        case SYNCHRONIZE_CACHE:
+               errsts = check_reset(SCpnt, devip);
                memset(buff, 0, bufflen);
                break;
        default:
+               if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+                       printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
+                              "supported\n", *cmd);
                if ((errsts = check_reset(SCpnt, devip)))
-                       break;
+                       break;  /* Unit attention takes precedence */
                mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0, 18);
                errsts = check_condition_result;
                break;
@@ -496,6 +509,9 @@ static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
 static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
 {
        if (devip->reset) {
+               if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+                       printk(KERN_INFO "scsi_debug: Reporting Unit "
+                              "attention: power on reset\n");
                devip->reset = 0;
                mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0, 18);
                return check_condition_result;
@@ -503,7 +519,7 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
        return 0;
 }
 
-#define SDEBUG_LONG_INQ_SZ 58
+#define SDEBUG_LONG_INQ_SZ 96
 #define SDEBUG_MAX_INQ_ARR_SZ 128
 
 static const char * vendor_id = "Linux   ";
@@ -593,11 +609,21 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
        /* drops through here for a standard inquiry */
        arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;     /* Removable disk */
        arr[2] = scsi_debug_scsi_level;
+       arr[3] = 2;    /* response_data_format==2 */
        arr[4] = SDEBUG_LONG_INQ_SZ - 5;
+       arr[6] = 0x1; /* claim: ADDR16 */
        arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
        memcpy(&arr[8], vendor_id, 8);
        memcpy(&arr[16], product_id, 16);
        memcpy(&arr[32], product_rev, 4);
+       /* version descriptors (2 bytes each) follow */
+       arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */
+       arr[60] = 0x2; arr[61] = 0x60; /* SPC-2 */
+       if (scsi_debug_ptype == 0) {
+           arr[62] = 0x1; arr[63] = 0x80; /* SBC */
+       } else if (scsi_debug_ptype == 1) {
+           arr[62] = 0x2; arr[63] = 0x00; /* SSC */
+       }
        memcpy(buff, arr, min_len);
        return 0;
 }
@@ -660,6 +686,8 @@ static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
        unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
                                     0, 0, 0x2, 0x4b};
 
+       if (scsi_debug_dsense)
+               ctrl_m_pg[2] |= 0x4;
        memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
        if (1 == pcontrol)
                memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
@@ -683,7 +711,7 @@ static int resp_mode_sense(unsigned char * cmd, int target,
                           struct sdebug_dev_info * devip)
 {
        unsigned char dbd;
-       int pcontrol, pcode;
+       int pcontrol, pcode, subpcode;
        unsigned char dev_spec;
        int alloc_len, msense_6, offset, len;
        unsigned char * ap;
@@ -695,11 +723,9 @@ static int resp_mode_sense(unsigned char * cmd, int target,
        dbd = cmd[1] & 0x8;
        pcontrol = (cmd[2] & 0xc0) >> 6;
        pcode = cmd[2] & 0x3f;
+       subpcode = cmd[3];
        msense_6 = (MODE_SENSE == cmd[0]);
        alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
-       /* printk(KERN_INFO "msense: dbd=%d pcontrol=%d pcode=%d "
-               "msense_6=%d alloc_len=%d\n", dbd, pcontrol, pcode, "
-               "msense_6, alloc_len); */
        if (bufflen < alloc_len)
                printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d "
                       "< alloc_length=%d\n", bufflen, alloc_len);
@@ -720,6 +746,11 @@ static int resp_mode_sense(unsigned char * cmd, int target,
        }
        ap = arr + offset;
 
+       if (0 != subpcode) { /* TODO: Control Extension page */
+               mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
+                               0, 18);
+               return check_condition_result;
+       }
        switch (pcode) {
        case 0x1:       /* Read-Write error recovery page, direct access */
                len = resp_err_recov_pg(ap, pcontrol, target);
@@ -1023,13 +1054,23 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
 
        sbuff = devip->sense_buff;
        memset(sbuff, 0, SDEBUG_SENSE_LEN);
-       if (inbandLen > SDEBUG_SENSE_LEN)
-               inbandLen = SDEBUG_SENSE_LEN;
-       sbuff[0] = 0x70;
-       sbuff[2] = key;
-       sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
-       sbuff[12] = asc;
-       sbuff[13] = asq;
+       if (scsi_debug_dsense) {
+               sbuff[0] = 0x72;  /* descriptor, current */
+               sbuff[1] = key;
+               sbuff[2] = asc;
+               sbuff[3] = asq;
+       } else {
+               if (inbandLen > SDEBUG_SENSE_LEN)
+                       inbandLen = SDEBUG_SENSE_LEN;
+               sbuff[0] = 0x70;  /* fixed, current */
+               sbuff[2] = key;
+               sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
+               sbuff[12] = asc;
+               sbuff[13] = asq;
+       }
+       if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+               printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
+                     "[0x%x,0x%x,0x%x]\n", key, asc, asq);
 }
 
 static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
@@ -1230,17 +1271,11 @@ 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) && cmnd) {
-               printk(KERN_INFO "scsi_debug: cmd ");
-               for (k = 0, num = cmnd->cmd_len; k < num; ++k)
-                       printk("%02x ", (int)cmnd->cmnd[k]);
-               printk("\n");
                if (scsi_result) {
                        struct scsi_device * sdp = cmnd->device;
 
-                       printk(KERN_INFO "scsi_debug: ... <%u %u %u %u> "
+                       printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
                               "non-zero result=0x%x\n", sdp->host->host_no,
                               sdp->channel, sdp->id, sdp->lun, scsi_result);
                }
@@ -1296,6 +1331,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd,
 module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */
 module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */
 module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0);
+module_param_named(dsense, scsi_debug_dsense, int, 0);
 module_param_named(every_nth, scsi_debug_every_nth, int, 0);
 module_param_named(max_luns, scsi_debug_max_luns, int, 0);
 module_param_named(num_parts, scsi_debug_num_parts, int, 0);
@@ -1312,13 +1348,14 @@ MODULE_VERSION(SCSI_DEBUG_VERSION);
 MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
 MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
 MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs");
+MODULE_PARM_DESC(dsense, "use descriptor sense format(def: fixed)");
 MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
 MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate");
 MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
 MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate");
 MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
 MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
-MODULE_PARM_DESC(scsi_level, "SCSI level to simulate");
+MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=4[SPC-2])");
 
 
 static char sdebug_info[256];
@@ -1452,6 +1489,24 @@ static ssize_t sdebug_ptype_store(struct device_driver * ddp,
 }
 DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
 
+static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
+{
+        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
+}
+static ssize_t sdebug_dsense_store(struct device_driver * ddp,
+                                 const char * buf, size_t count)
+{
+        int n;
+
+       if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+               scsi_debug_dsense = n;
+               return count;
+       }
+       return -EINVAL;
+}
+DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
+           sdebug_dsense_store);
+
 static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
 {
         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);