Chinaunix首页 | 论坛 | 博客
  • 博客访问: 784621
  • 博文数量: 127
  • 博客积分: 2669
  • 博客等级: 少校
  • 技术积分: 1680
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-23 11:39
文章分类

全部博文(127)

文章存档

2014年(5)

2013年(19)

2012年(25)

2011年(9)

2010年(25)

2009年(44)

分类: LINUX

2013-08-04 20:09:05

3.3.1 INQUIRY命令

参照SPC-3 revision23(以下简称SPC-3)标准的第5.2.3小节,INQUIRY指令是客户应用获取LUN信息的指令,scsi设备可以返回包括设备类型、版本号等信息,也可以包含厂商ID、产品型号等其他信息。该命令的定义格式如图9所示:

9 INQUIRY命令格式

针对INQUIRY命令其中的EVPD位如果为0,则返回标准INQUIRY信息,否则scsi设备需要返回设备的VPD(Vital product data)信息。从如下代码可以看出scsi_debug对于INQUIRY命令的处理交由resp_inquiry函数完成,

       switch (*cmd) {

       case INQUIRY:     /* mandatory, ignore unit attention */

              delay_override = 1;

              errsts = resp_inquiry(SCpnt, target, devip);

              break;

该函数定义如下:

static int resp_inquiry(struct scsi_cmnd * scp, int target,

                     struct sdebug_dev_info * devip)

{

       unsigned char pq_pdt;

       unsigned char * arr;

       unsigned char *cmd = (unsigned char *)scp->cmnd;

       int alloc_len, n, ret;

 

       alloc_len = (cmd[3] << 8) + cmd[4];

       arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);

       if (! arr)

              return << 16;

       if (devip->wlun)

              pq_pdt = 0x1e;       /* present, wlun */

       else if (scsi_debug_no_lun_0 && (0 == devip->lun))

              pq_pdt = 0x7f;       /* not present, no device type */

       else

              pq_pdt = (scsi_debug_ptype & 0x1f);

       arr[0] = pq_pdt;

对应代码,可见程序首先获得了INQUIRY命令中下标为34中代表的allocation length,之后按照SDEBUG_MAX_INQ_ARR_SZ调用kzalloc分配空间,如果分配失败则通知上层,返回DID_REQUEUE错误。由标准可知VPD的第一个字节是由(3)PERIPHERAL DEVICE TYPE(5位组成),定义分别如图10和图11,参照图中内容,0x1e代表本LUN与其他LUN的设备类型相同(高3位为000,低5位为1e),0x7f代表该device无法支持,0x7f代表这个LUN无法被使用(高3位为011,低5位为1f)。


10 PERIPHERAL QUALIFIER定义


11 PERIPHERAL DEVICE TYPE定义

       if (0x2 & cmd[1]) {  /* CMDDT bit set */

              mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,

                              0);

              kfree(arr);

              return check_condition_result;

cmd[1]&0x2判断了cmd[1]的第2bit是否为1,如果为1,则返回LLEGAL_REQUEST,因为标准中已经规定了这个选项是过时的。

} else if (0x1 & cmd[1]) {  /* EVPD bit set */

否则判断EVPD是否设置,如果设置则返回VPD,接下来的代码就是在设置VPD的各种信息:

       

       if (0 == cmd[2]) { /* supported vital product data pages */

              

       } else if (0x80 == cmd[2]) { /* unit serial number */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = len;

                     memcpy(&arr[4], lu_id_str, len);

       } else if (0x83 == cmd[2]) { /* device identification */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = inquiry_evpd_83(&arr[4], port_group_id,target_dev_id, lu_id_num,

                                           lu_id_str, len);

       } else if (0x84 == cmd[2]) { /* Software interface ident. */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = inquiry_evpd_84(&arr[4]);

       } else if (0x85 == cmd[2]) { /* Management network addresses */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = inquiry_evpd_85(&arr[4]);

       } else if (0x86 == cmd[2]) { /* extended inquiry */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = 0x3c; /* number of following entries */

                     if (scsi_debug_dif == SD_DIF_TYPE3_PROTECTION)

                            arr[4] = 0x4;   /* SPT: GRD_CHK:1 */

                     else if (scsi_debug_dif)

                            arr[4] = 0x5;   /* SPT: GRD_CHK:1, REF_CHK:1 */

                     else

                            arr[4] = 0x0;   /* no protection stuff */

                     arr[5] = 0x7;   /* head of q, ordered + simple q's */

       } else if (0x87 == cmd[2]) { /* mode page policy */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = 0x8;   /* number of following entries */

                     arr[4] = 0x2;   /* disconnect-reconnect mp */

                     arr[6] = 0x80; /* mlus, shared */

                     arr[8] = 0x18; /* protocol specific lu */

                     arr[10] = 0x82;      /* mlus, per initiator port */

              } else if (0x88 == cmd[2]) { /* SCSI Ports */

                     arr[1] = cmd[2];     /*sanity */

                     arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);

              } else if (0x89 == cmd[2]) { /* ATA information */

                     arr[1] = cmd[2];        /*sanity */

                     n = inquiry_evpd_89(&arr[4]);

                     arr[2] = (n >> 8);

                     arr[3] = (n & 0xff);

              } else if (0xb0 == cmd[2]) { /* Block limits (SBC) */

                     arr[1] = cmd[2];        /*sanity */

                     arr[3] = inquiry_evpd_b0(&arr[4]);

              } else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */

                     arr[1] = cmd[2];        /*sanity */

                     arr[3] = inquiry_evpd_b1(&arr[4]);

              } else if (0xb2 == cmd[2]) { /* Logical Block Prov. (SBC) */

                     arr[1] = cmd[2];        /*sanity */

                     arr[3] = inquiry_evpd_b2(&arr[4]);

              } else {

                     /* Illegal request, invalid field in cdb */

                     mk_sense_buffer(devip, ILLEGAL_REQUEST,

                                   INVALID_FIELD_IN_CDB, 0);

                     kfree(arr);

                     return check_condition_result;

              }

这段代码中对于VPD的设置参考SPC-3的第7.6小节,针对INQUIRY的下标为2page code进行判断继而填充相关信息,page code种类如图12所示。若page code0,则需要返回所有支持的page code,由于scsi_debug是个虚拟的设备,所以支持比较全面,图12中的page code都支持,返回数据格式定义参见SPC-37.6.9小节。设置完成page code之后需要在返回数据下标为3的位置中填充page length。若page code为其他类型,scsi_debug中对调用的函数进行了细化,正如代码中的inquiry_evpd_83inquiry_evpd_84inquiry_evpd_b2等等所示。

 

12 INQUIRY命令page code种类

此处以inquiry_evpd_84为例,从图12中可以看出应该返回Software Interface Indentification信息,该函数定义如下,SPC-3标准的7.6.8小节中规定每个Software Interface identifier应该返回6个字节长度的LUN信息,Software Interface identifier的内容需要遵循EUI-48格式(参见http://standards.ieee.org/develop/regauth/tut/eui48.pdf)

static unsigned char vpd84_data[] = {

/* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,

    0x22,0x22,0x22,0x0,0xbb,0x1,

    0x22,0x22,0x22,0x0,0xbb,0x2,

};

 

static int inquiry_evpd_84(unsigned char * arr)

{

       memcpy(arr, vpd84_data, sizeof(vpd84_data));

       return sizeof(vpd84_data);

}

如果没有找到正确的page code则生成sense buffer并返回错误,接下来的代码:

len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);

ret = fill_from_dev_buffer(scp, arr, min(len, SDEBUG_MAX_INQ_ARR_SZ));

kfree(arr);

return ret;

此处针对已经填充的信息获取真实的返回数据的长度,然后调用fill_from_dev_buffer,该函数定义如下:

阅读(2409) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~