Linux那些事儿之我是SCSI硬盘(5)三座大山(二)

第二座大山,sd_read_write_protect_flag.

1327 /*

1328 * read write protect setting, if possible - called only in sd_revalidate_disk()

1329 * called with buffer of length SD_BUF_SIZE

1330 */

1331 static void

1332 sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)

1333 {

1334 int res;

1335 struct scsi_device *sdp = sdkp->device;

1336 struct scsi_mode_data data;

1337

1338 set_disk_ro(sdkp->disk, 0);

1339 if (sdp->skip_ms_page_3f) {

1340 sd_printk(KERN_NOTICE, sdkp, "Assuming Write Enabled/n");

1341 return;

1342 }

1343

1344 if (sdp->use_192_bytes_for_3f) {

1345 res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 192, &data, NULL);

1346 } else {

1347 /*

1348 * First attempt: ask for all pages (0x3F), but only 4 bytes.

1349 * We have to start carefully: some devices hang if we ask

1350 * for more than is available.

1351 */

1352 res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 4, &data, NULL);

1353

1354 /*

1355 * Second attempt: ask for page 0 When only page 0 is

1356 * implemented, a request for page 3F may return Sense Key

1357 * 5: Illegal Request, Sense Code 24: Invalid field in

1358 * CDB.

1359 */

1360 if (!scsi_status_is_good(res))

1361 res = sd_do_mode_sense(sdp, 0, 0, buffer, 4, &data, NULL);

1362

1363 /*

1364 * Third attempt: ask 255 bytes, as we did earlier.

1365 */

1366 if (!scsi_status_is_good(res))

1367 res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 255,

1368 &data, NULL);

1369 }

1370

1371 if (!scsi_status_is_good(res)) {

1372 sd_printk(KERN_WARNING, sdkp,

1373 "Test WP failed, assume Write Enabled/n");

1374 } else {

1375 sdkp->write_prot = ((data.device_specific & 0x80) != 0);

1376 set_disk_ro(sdkp->disk, sdkp->write_prot);

1377 sd_printk(KERN_NOTICE, sdkp, "Write Protect is %s/n",

1378 sdkp->write_prot ? "on" : "off");

1379 sd_printk(KERN_DEBUG, sdkp,

1380 "Mode Sense: %02x %02x %02x %02x/n",

1381 buffer[0], buffer[1], buffer[2], buffer[3]);

1382 }

1383 }

这个函数看似很长,其实有意义的就是一行,那就是1376,调用set_disk_ro()从而确定本磁盘是否是写保护的.

1338,set_disk_ro就是设置磁盘只读,0就是可读可写,1才是设置为只读.但是咱们这只是软件意义上的作个记录而已,硬件上还得听磁盘自己的.所以我们通过下面一大段代码最终得到这一信息,最终在1376行再次设置.

那么如何得知写保护是否设置了呢?发送命令给设备,这个命令就是MODE SENSE.MODE SENSE这个命令的目的在于获得设备内部很多潜在的信息,这其中包括设备是否设置了写保护,当然还有更多SCSI特有的信息.只不过我们此时此刻只关注写保护设了没有.这些特性就像设备的天性一样,在它出生的时候就设置好了,当然有些天性也是可以改变的,就比如范冰冰,可能她生下来的时候长相平平,但是经过整容,变成了美女.又比如何丽秀,原本是男人,后来却变成了女人.而对于SCSI设备来说,很多特性可以改变,但是有些特性就不可以改变了,比如medium type,即它属于哪种类型的设备,对于SCSI Block设备,其内部保存MEDIUM TYPE的这个byte一定是00h.

在咱们的驱动中为了发送这个命令,还作了两次包装,先调用sd_do_mode_sense().

1316 /* called with buffer of length 512 */

1317 static inline int

1318 sd_do_mode_sense(struct scsi_device *sdp, int dbd, int modepage,

1319 unsigned char *buffer, int len, struct scsi_mode_data *data,

1320 struct scsi_sense_hdr *sshdr)

1321 {

1322 return scsi_mode_sense(sdp, dbd, modepage, buffer, len,

1323 SD_TIMEOUT, SD_MAX_RETRIES, data,

1324 sshdr);

1325 }

sd_do_mode_sense调用来自scsi核心层统一提供的scsi_mode_sense(),关于后者我们就不详细介绍了,总之执行之后,结果就是保存在了data,datastruct scsi_mode_data结构体变量.

16 struct scsi_mode_data {

17 __u32 length;

18 __u16 block_descriptor_length;

19 __u8 medium_type;

20 __u8 device_specific;

21 __u8 header_length;

22 __u8 longlba:1;

23 };

这里每一个成员都在scsi协议中能够找到对应物.就比如刚才说得medium_type,对于SCSI磁盘,它一定是00h.这是没得商量的.

我们最终是在1375行作的判断,1375,为啥判断device_specific0x80相与呢?SBC-2中有一幅图描述了这个Device Specific的玩意儿.

Linux那些事儿之我是SCSI硬盘(5)三座大山(二)

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1027" style="WIDTH: 415.5pt; HEIGHT: 63pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

这里bit7叫做WP,Write Protect,写保护位.N年前当咱们刚开始用软盘的时候就听说了写保护,所以对这个概念我们并不陌生.如果这一位为1就说明设置了写保护,反之则是没有设置.如果没有设置写保护,那么在日志文件里我们就能看到类似下面这行的一句话:

Dec 6 08:47:05 localhost kernel: sdb: Write Protect is off

因此, sd_read_write_protect_flag这一个函数的流程就是:

1. 软件问:磁盘磁盘你设置了写保护吗?

2. 如果磁盘说:是的我设置了.

3. 软件打印: sdb: Write Protect is on

4. 如果磁盘说:,我没有设置.

5. 软件打印: sdb: Write Protect is off

最后说一下,1344,判断有没有设置use_192_bytes_for_3f,这是因为实践表明,很多磁盘只能接受MODE SENSEpage=0x3f时传输长度为192bytes,所以咱们在定义struct scsi_device的时候为这些设备准备了这么一个flag,scsi总线扫描设备初始化的时候就可以设置这么一个flag.相应的我们发送命令的时候就设置好192.

另一个1339,skip_ms_page_3f,这也是一个类似的flag,MODE SENSE命令有一个参数page,同样是实践表明,某些愚蠢的设备在page=0x3f的时候会出错.所以写代码的做出让步,又准备了一个flag.

如果你还不是很明白这个page是啥意思,那么让我们来看一下SPC-4MODE SENSE命令的格式是如何的.

首先是6字节的.

Linux那些事儿之我是SCSI硬盘(5)三座大山(二)

<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 132pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image003.emz"></imagedata></shape>

然后是10字节的.

Linux那些事儿之我是SCSI硬盘(5)三座大山(二)

<shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 177pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image005.emz"></imagedata></shape>

这其中,PAGE CODE就是我们上面说的page,很明显,它一共占6bits.因此它的取值范围就是00h3Fh(11 1111).而我们上面说到3fh,就是说当你发送MODE SENSE命令的时候,设置PAGE CODE3fh的时候,因为3fh是最后一个page,很多设备都会有一些莫名其妙的错误,搞得我们很没面子,于是我们需要设置种种flag来处理这些情况.

当然,你可能还想知道为什么需要PAGE这么一个概念.Ok,其实这样来的,众所周知,开源社区有很多寂寞男,但是很少有女人,毕竟亚里士多德曾经说过:”女人做程序,既毁了女人,也毁了程序.”SCSI设计者作为同样是IT工作者,他们对开源社区的兄弟们也很同情,所以他们在设计SCSI的时候一直希望把对开源社区兄弟们美好的祝愿寄托在设备中,他们想,开源社区缺女人,而我们经常说,女人就象一本书,(当然了,胖女人就象一本辞海.除了必要时,没有愿意去翻她.),于是他们在设备内部保存了一本书,这本书就是设备的<<我的自白书>>,或者用更加时尚的话说,这本书就是设备的性感写真集,而你要阅读这本书,你就必须发送MODE SENSE命令,但是就像你读别的书一样,你必须一页一页的读,所以你需要给定一个PAGE CODE,或者说页码,同时我们看到Byte3叫做SUBPAGE CODE,这就是子页号码,你索性就理解为一页中某一个段落好了,即设备允许你一页一页的读,也允许你一段一段的读.很显然,由于SUBPAGE CODE8bits,因此其最大值就是255.即一个page可以有最多255subpage.

你可能感兴趣的:(linux)