这一节我们来分析一个在很多企业的产品中都存在的bug.写设备驱动是一件很实在的事情,你得根据实实在在的硬件来编写你的代码,如果你的硬件存在某种bug,那么你就要去fix它.如果你希望成为通用的驱动程序,那么你就要兼顾各家企业,兼顾各种可能存在的bug.也许一百家企业的产品都可以很好的被你的程序所支持,但是如果地一百零一家的产品有问题,你就得尽量解决.usb-storage正是这样一个模块.所以它的代码里会涉及到很多不同的企业.当然我相信,有一个更重要的原因,那就是,没有企业的支持,Linux不可能像今天这样火.所以Linux内核代码中支持诸多企业的设备也是必然的,就像厉娜在给把票投给许飞而不是给我们复旦的尚雯婕的时候说的那句:于情于理于公于私,都应该这样.
很多年前,《商业周刊》断言:“自由软件业的开发者大部分水平不高,不可能制造高端的企业级产品”. 这是事实, 出身卑微的Linux真正有出人头地的机会, 的确是在各大名企大规模介入之后. 而任何一家企业支持Linux的目的都只是为了赚钱.IBM干嘛支持Linux?老板思想境界高?想为全人类服务?我告诉你,IBM自从2000年开始展开Linux战略,2002年他们家就从Linux市场上赚取了10亿美元.所有的公司支持Linux的目的无非就是想瓜分那些曾经属于微软的财富. 正如洪波所说的那样,大企业只不过是花钱为这次抢劫置办一件迷人的外衣,让所有人认同这样一个观点,那就是,张君拿着武器抢劫运钞车,是死罪,有钱人拿着Linux瓜分微软,是正义. (注:张君,我的老乡,也是我当年的偶像)更滑稽的是,时间长了,每一个学习Linux的人都有这样一种感觉,觉得自己正做着一件伟大的正义的,追求自由的大事.(郑重声明一下,我是例外,从未觉得Linux跟伟大有什么关系,学习Linux只是混口饭而已.)
Ok,下面让我们来仔细看看这段属于苹果,属于诺基亚,属于摩托罗拉,属于索尼爱立信的代码吧.US_FL_FIX_CAPACITY这个flag的设置,意味着这种设备存在这么一个bug.在scsi众多命令中,有一个命令叫做READ CAPACITY.它的作用很简单,就是读取磁盘的总容量.先来点直观的印象吧,还记得当初哥们给你推荐的那个工具sg_utils3么,当初用它执行了INQUIRY命令.现在咱们用它执行READ CAPACITY,具体命令名字叫做sg_readcap.你要是装了SUSE Linux的话,里边的rpm包可能不叫sg_utils,而是叫scsi-xx-xx,比如偶的就是scsi-1.7_2.36_1.19_0.17_0.97-12.4.这个包包含很多执行scsi命令的工具.而且都有man文档,不会用看看man就知道了.
先来个硬盘的,比如偶的scsi硬盘:
myhost: # sg_readcap -b /dev/sda
0x11040000 0x200
myhost: # fdisk -l /dev/sda

Disk /dev/sda: 146.1 GB, 146163105792 bytes
255 heads, 63 sectors/track, 17769 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1         266     2136613+  83  Linux
/dev/sda2            2879       17769   119611957+  83  Linux
/dev/sda3   *         267        1572    10490445   83  Linux
/dev/sda4            1573        2878    10490445   82  Linux swap / Solaris

myhost: #
看出sg_readcap读出来的信息了吗?我们可以传递不同的参数,如果像我们这里-b参数,那么将获得block数以及每个block的字节数.我们来计算一下,我们这里返回的两个值分别是0x11040000和0x200,0x11040000对应于十进制285474816,0x200就是十进制的512,N多设备的block大小都是512.这两个相乘就是我们的容量,相乘的结果是146163105792,看到了吗?和fdisk命令显示出来的一模一样,呵呵,其实fdisk就是这么干的.不一样就见鬼了.Ok,有了直观的印象可以继续往下看了.刚才我们知道对于有设置US_FL_FIX_CAPACITY这个flag的设备,就会执行fix_read_capacity(),这个函数定义于drivers/usb/storage/protocol.c中,所谓的这个bug在这个注释里说得很清楚.明明容量是N,偏偏要汇报说自己是N+1,你说这不是找抽么?当然这也很简单,我们处理起来不难.
     60 /*
     61  * Fix-up the return data from a READ CAPACITY command. My Feiya reader
     62  * returns a value that is 1 too large.
     63  */
     64 static void fix_read_capacity(struct scsi_cmnd *srb)
     65 {
     66         unsigned int index, offset;
     67         __be32 c;
     68         unsigned long capacity;
     69
     70         /* verify that it's a READ CAPACITY command */
     71         if (srb->cmnd[0] != READ_CAPACITY)
     72                 return;
     73
     74         index = offset = 0;
     75         if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
     76                         &index, &offset, FROM_XFER_BUF) != 4)
     77                 return;
     78
     79         capacity = be32_to_cpu(c);
     80         US_DEBUGP("US: Fixing capacity: from %ld to %ld/n",
     81                capacity+1, capacity);
     82         c = cpu_to_be32(capacity - 1);
     83
     84         index = offset = 0;
     85         usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
     86                         &index, &offset, TO_XFER_BUF);
     87 }
应该说看过前面我们如果处理INQRUIY命令那个bug的代码的同志们应该能够很容易看懂眼前这段代码.其中调用的最关键的函数就是usb_stor_access_xfer_buf(),对usb_stor_access_xfer_buf()函数陌生的同志们可以回过去看看当时咱们是如何分析的.这里75行的作用就是从request_buffer里边把数据copy到c里边去,而85行的作用就是反过来把c里边的数据copy到request_buffer里边去.而在这之间,最重要的一步自然是79行和82行,capacity被赋为c,而c再被赋为capacity-1.这样简简单单的几行代码就fix了这么一个bug.
当然,虽然我不喜欢Linux,但是我还是有必要为你解释一些事情.
你是不是看见好些次这样一系列函数了: be32_to_cpu/cpu_to_be32(),cpu_to_le16(),cpu_to_le32(),此前我们一直没有讲,所以让我们现在一并来讲吧.反正整个故事的大致走向你已经很清楚了,从现在开始的故事基本上就属于一些小打小闹小修小补式的细枝末节了.le叫做Little Endian,be叫做Big Endian,这是两种字节序.le就表示地址地位存储值的低位,地址高位存储值的高位.be就表示地址低位存储值的高位,地址高位存储值的低位.我们就以这里这个临时变量c为例.假设c是这样被存储在内存地址0x0000开始的地方:
0x0000     0x12
 0x0001     0x34
 0x0002     0xab
 0x0003     0xcd
如果你是采用le的字节序,那么读出来的值就是0xcdab3412,反之,如果你采用的是be的字节序,那么读出来的值就是0x1234abcd.同样的,如果你把0x1234abcd写入0x0000开始的内存中,那么结果就是:
             big-endian     little-endian
0x0000     0x12              0xcd
0x0001     0x23              0xab
0x0002     0xab              0x34
0x0003     0xcd              0x12
为什么这几个函数名字里面都一个”cpu”?谈到字节序不谈cpu那就好比神采飞扬的谈起超级女声却对张靓颖是何许人也茫然不知.不同的cpu采用不同的字节序.看生产商自己喜欢了.其中,big endian以Motolora的PowerPC系列cpu为代表,而little endian则以我家Intel的x86系列cpu为代表.所以这几个函数名字里边都会有cpu的字样,那么毫无疑问对于不同的cpu,这几个函数执行的代码是不一样的.但是,凡是xx_to_cpu就说明函数的结果是给cpu使用的,反之如果是cpu_to_xx就说明是从cpu的字节序转换成目标字节序.
那么目标字节序应该是什么样子?我们先来看usb的情况.usb spec 2.0第八章,白纸黑字的规定了这么一句,usb总线上使用的是little-endian的字节序.所以,当初我们在处理bcs/bcb的时候一直在调用lexx_to_cpu/cpu_to_lexx()这样的函数,或者准确地说,这样的宏.(usb spec 2.0, Chapter 8, 8.1 Byte/Bit Ordering:
Bits are sent out onto the bus least-significant bit (LSb) first, followed by the next LSb, through to the most significant bit (MSb) last. In the following diagrams, packets are displayed such that both individual bits and fields are represented (in a left to right reading order) as they would move across the bus. Multiple byte fields in standard descriptors, requests, and responses are interpreted as and moved over the bus in little-endian order, i.e., LSB to MSB.)
而与此同时,在灯火阑珊处,我们也依稀记得,在那份名为SCSI Primary Commands-4的规范,即那份在江湖上有着SCSI葵花宝典之美誉的SPC-4中,第三章,3.5, Bit and byte ordering那一段,是这般描述的: If a field consists of more than one byte and contains a single value, the byte containing the MSB is stored at the lowest address and the byte containing the LSB is stored at the highest address (i.e., big-endian byte ordering).所以request_buffer回来的数据是采用be的字节序,因此我们这里的c要通过be32_to_cpu()转换才能变成cpu使用的结果.反过来,当我们再次copy回request_buffer中的时候,要再使用cpu_to_be32()给转回去.

最后如果你还要问,为什么要采用两种字节序?多麻烦啊?那我没什么可说的,你问上帝问真主问释迦牟尼去吧,也许他们能告诉你,不管白老鼠黑老鼠,只要不给猫逮住的就是好老鼠.

Linux那些事儿之我是U盘(51)光荣属于苹果,属于诺基亚,属于摩托罗拉,属于索尼爱立信相关推荐

  1. linux 那些事儿之我是 u 盘,《Linux那些事儿之我是USB》.PDF

    <Linux 那些事儿之我是 USB> 作者:华清远见 第 1 章 Linux 那些事儿之我是 USB Core 专业始于专注 卓识源于远见 1 .引子 老夫子们痛心疾首地总结说,现代青年 ...

  2. Linux那些事儿之我是U盘(50)跟着感觉走(二)

    回到usb_stor_invoke_transport()中来,540行,还是老套路,又问是不是命令被放弃了,放弃了当然下面的就别执行了.goto Handle_Abort去. 546行,如果有错误, ...

  3. 转载本论坛 (fudan_abc ) :linux那些事儿之我是u盘(16)冰冻三尺非一日之寒

    不是一天建成的 . 在让 U 盘工作之前 , 其实我们的驱动作了很多准备工作 . 我们继续跟着感觉走,storage_probe(),943行至948行,一系列的以init_*命名的函数在此刻被调用, ...

  4. Linux那些事儿之我是U盘(5)外面的世界很精彩

    看代码之前,我曾经认真的思考过这么一个问题,我需要关注的仅仅是drivers/usb/storage/目录下面那相关的3000多行代码吗?就是这样几个文件就能让一个个不同的U盘在Linux下面工作起来 ...

  5. Linux那些事儿之我是U盘(4)想到达明天,现在就要启程

    既然知道了怎么编写一个模块,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块的人都不会觉得写设备驱动有困难.对自己行不行不确定的话,可以去问一下葛优,他准说:"(神州行),我看行. ...

  6. Linux那些事儿之我是U盘(1)小城故事

    这个故事中使用的是2.6.10的内核代码.Linux内核代码目录中, 所有去设备驱动程序有关的代码都在drivers/目录下面,在这个目录中我们用ls命令可以看到很多子目录. localhost:/u ...

  7. 【转】Linux那些事儿之我是U盘(4)想到达明天,现在就要启程

    既然知道了怎么编写一个模块,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块的人都不会觉得写设备驱动有困难.对自己行不行不确定的话,可以去问一下葛优,他准说:"(神州行),我看行. ...

  8. Linux那些事儿之我是U盘--引子

    也许是在复旦养成了昼伏夜出的坏习惯,工作之后也总是很晚也不愿意睡.来到北京之后,开始听广播听都市之声的北京不眠夜.这个节目是从23点直到第二天凌晨一点,我常常是听完了才会睡觉.无论是北京还是上海,对我 ...

  9. Linux那些事儿之我是U盘(29)将控制传输进行到底

    其实usb_stor_clear_halt这个函数的作用很简单,就是spec里边规定了,usb设备中,有两类端点,必须具有一个叫做Halt的特征,啥是Halt?查金山词霸去,中断,停止,暂停,怎么解释 ...

最新文章

  1. 在线作图|2分钟在线绘制RDA图
  2. CPLD/FPGA的UART接口设计之系统时钟(晶振)和波特率关系
  3. 【sublime text3】破解 最近破解码 /激活成功,但是过一会就提示激活码失效的 Build3143...
  4. linux基础面试题(46个汇总)
  5. java spring 服务器关闭连接_java springboot websocket 服务 服务器主动关闭连接 导致 抛出java.io.EOFException异常...
  6. Android studio的sdk tools下没有LLDB的解决办法
  7. python两人一碰_python运用pygame库实现双人弹球小游戏
  8. 微软为Win11用户更新了剪贴工具、计算器以及邮件和日历应用
  9. struct interface_GCTT | 接受 interface 参数,返回 struct 在 go 中意味着什么
  10. Windows 10 PC 安装 Docker CE
  11. url,href,src区别
  12. 数字信号处理-04- FPGA常用运算模块-除法器
  13. Eplan2.7 安装教程
  14. C++中类的三种继承方式public(公有继承)、protected(保护继承)、private(私有继承)之间的差别(附思维导图)
  15. golang 微信商户平台支付平台V3
  16. 基于VC++的WEB浏览器的实现
  17. 公安部中标十大身份证阅读器品牌型号
  18. linux配置dns心得体会,dns实训报告心得体会.doc
  19. Java微信公众平台开发(一)--接入微信公众平台
  20. mxgraph进阶(三)Web绘图——mxGraph项目实战(精华篇)

热门文章

  1. 玩客云刷armbian教程(适用于老版)
  2. 连续出错的概率_连续抛硬币问题+三个概率题
  3. 初学Verilog语言基础笔记整理(实例点灯代码分析)持续更新~
  4. 解决专利支付错误问题
  5. 基于部标jt808(天琴、谷米),809协议和Java Netty框架构建北斗GPS定位系统
  6. 安装wireshark中npcap无法安装以及winpcap无法安装(已解决【两个方法】)
  7. Python学习——shelve模块
  8. 数据库SQL(十二):分布式锁服务Chubby
  9. Ultra-Light-Fast-Generic-Face-Detector-1MB-master人脸检测算法的复现过程记录
  10. 从Tlink看企业接入物联网的紧迫性