现在设备的struct usb_device结构体已经准备好了,只是还不怎么饱满,hub接下来就会给它做做整容手术,往里边儿塞点什么,充实一些内容,比如:将设备的状态设置为Powered,也就是加电状态;因为此时还不知道设备支持的速度,于是将设备的speed成员暂时先设置为USB_SPEED_UNKNOWN;设备的级别level当然会被设置为hub的level加上1了;还有为设备能够从hub那里获得的电流赋值;为了保证通信畅通,hub还会为设备在总上选择一个独一无二的地址。

给张表吧,集中列了下到目前为止,设备结构体里成员的状况。里面的taken只是表示赋过值了,好像期末考试前你去突击自习,这时一个ppmm走到你旁边,用一个美妙的声音问你“Is this seat taken?”,你怎么回答?当然是“No, No, please.”对头,taken就是这个意思。

devnum

taken

devpath[16]

taken

state

USB_STATE_POWERED

speed

USB_SPEED_UNKNOWN

parent

设备连接的那个hub

bus

设备连接的那条总线

ep0

ep0.urb_list,描述符长度/类型

dev

dev.bus,dev.type,dev.dma_mask,dev.parent,dev.bus_id

ep_in[16]

ep_in[0]

ep_out[16]

ep_out[0]

bus_mA

hub->mA_per_port

portnum

设备连接在hub上的那个端口

level

hdev->level + 1

filelist

taken

pm_mutex

taken

autosuspend

taken

autosuspend_delay

2 * HZ

你的设备现在已经处在了Powered状态。前面讲过的,设备要想从Powered状态发展到下一个状态Default,必须收到一个复位信号并成功复位。那hub接下来的动作就很明显了,复位设备,复位成功后,设备就会进入Default状态。
设备复位顺利的话也就那么几十毫秒的功夫,不顺利的话,它会多尝试几次。不过如果试了几次都复位不成,那就不用试了,这条设备的生命线就算提前玩完儿了。
现在就算设备成功复位了,大步迈进了Default状态,同时,hub也会获得设备真正的速度,低速、全速也好,高速也罢,总算是浮出水面了,speed也终于知道了自己的真正身份,不用再是UNKNOWN了。那根据这个速度,咱们能知道些什么?起码能够知道端点0一次能够处理的最大数据长度啊,协议里说,对于高速设备,这个值为为64字节,对于低速设备为8字节,而对于全速设备可能为8,16,32,64其中的一个。所以hub还要通过一个蜿蜒曲折的过程去获得这个确定的值。
hub也辛苦的蛮久了,设备也该进入Address状态了。
只要hub使用core里定义的一个函数usb_control_msg,发送SET_ADDRESS请求给设备,设备就兴高采烈的迈进Address了。那么设备的这个address是什么,就是上面的devnum啊。
那现在咱就来说说这个usb_control_msg函数,它在drivers/usb/core/message.c里定义

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index, void *data,__u16 size, int timeout)
{struct usb_ctrlrequest *dr;int ret;dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);if (!dr)return -ENOMEM;dr->bRequestType = requesttype;dr->bRequest = request;dr->wValue = cpu_to_le16(value);dr->wIndex = cpu_to_le16(index);dr->wLength = cpu_to_le16(size);ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);kfree(dr);return ret;
}

这个函数主要目的是创建一个控制urb,并把它发送给usb设备,然后等待它完成。urb是什么?忘了么,前面提到过的,你要想和你的usb通信,就得创建一个urb,并且为它赋好值,交给usb core,它会找到合适的host controller,从而进行具体的数据传输。
8行,为一个struct usb_ctrlrequest结构体申请了内存。它在include/uapi/linux/usb/ch9.h文件里定义

struct usb_ctrlrequest {__u8 bRequestType;__u8 bRequest;__le16 wValue;__le16 wIndex;__le16 wLength;
} __attribute__ ((packed));

这个结构完全对应于spec里的Table 9-2,描述了主机通过控制传输发送给设备的请求(Device Requests)。主机向设备请求些信息必须得按照协议里规定好的格式,不然设备就会不明白主机是嘛意思。
这个结构描述的request都在Setup包里发送,Setup包是前面某处说到的Token PID类型中的一种,为了你好理解,这里细说一下控制传输底层的packet情况。控制传输最少要有两个阶段的transaction,SETUP和STATUS,SETUP和STATUS中间的那个DATA阶段是可有可无的。Transaction这个词儿在很多地方都有,在这里你称它为事务也好,会话也罢,我还是直呼它的原名transaction,可以理解为主机和设备之间形成的一次完整的交流,比如2004中华小姐环球大赛总决赛上评委蔡澜和陕西选手姚佳雯之间的对话:
要老公还是要钱?要钱。
这就可以算是一次transaction,usb的transaction要比上面的对话复杂,起码要过过脑子,它可以包括一个Token包、一个Data包和一个Handshake包。
Token、Data和Handshake都属于四种PID类型中的,前面说到时提到的一个包里的那些部分,如SYNC、PID、地址域、DATA、CRC,并不是所有PID类型的包都会全部包括的。Token包只包括SYNC、PID、地址域、CRC,并没有DATA字段,它的名字起的很形象,就是用来标记所在transaction里接下来动作的,对于Out和Setup Token包,里面的地址域指明了接下来要接收Data包的端点,对于In Token包,地址域指明了接下来哪个端点要发送Data包。还有,只有主机才有权利发送Token包,协议里就这么规定的。
与Token包相比,Data包里没了地址域,多了Data字段,这个Data字段对于低速设备最大为8字节,对于全速设备最大为1023字节,对于高速设备最大为1024字节。里里外外看过去,它就是躲在Token后边儿用来传输数据的。Handshake包的成分就非常的简单了,简直和那位姚佳雯的回答一样简单,除了SYNC,它就只包含了一个PID,通过PID取不同的值来报告一个transaction的状态,比如数据已经成功接收了等。
控制传输的SETUP transaction一般来说也有三个阶段,就是主机向设备发送Setup Token包、然后发送Data0包,如果一切顺利,设备回应ACK Handshake包表示OK,为什么加上一般?如果中间的那个Data0包由于某种不可知因素被损坏了,设备就什么都不会回应,这时就成俩阶段了。SETUP transaction之后,接下来如果控制传输有DATA transaction的话,那就Data0、Data1这样交叉的发送数据包,前面说过这是为了实现data toggle。最后是STATUS transaction,向主机汇报前面SETUP和DATA阶段的结果,比如表示主机下达的命令已经完成了,或者主机下达的命令没有完成,或者设备正忙着那没功夫去理会主机的那些命令。
这样经过SETUP、DATA、STATUS这三个transaction阶段,一个完整的控制传输完成了。主机接下来可以规划下一次的控制传输。
现在对隐藏在控制传输背后的是是非非摸了个底儿,群众的眼睛是雪亮的,咱们现在应该可以看出之前说requests都在Setup包里发送是有问题的,因为Setup包本身并没有数据字段,严格来说它们应该都是在SETUP transaction阶段里Setup包后的Data0包里发送的。

bRequestType,它的bit7就表示了控制传输中DATA transaction阶段的方向,当然,如果有DATA阶段的话。bit5~6表示request的类型,是标准的,class-specific的还是vendor-specific的。bit0~4表示了这个请求针对的是设备,接口,还是端点。内核为它们专门量身定做了一批掩码,也在ch9.h文件里,

/** USB directions** This bit flag is used in endpoint descriptors' bEndpointAddress field.* It's also one of three fields in control requests bRequestType.*/
#define USB_DIR_OUT         0       /* to device */
#define USB_DIR_IN          0x80        /* to host *//** USB types, the second of three bRequestType fields*/
#define USB_TYPE_MASK           (0x03 << 5)
#define USB_TYPE_STANDARD       (0x00 << 5)
#define USB_TYPE_CLASS          (0x01 << 5)
#define USB_TYPE_VENDOR         (0x02 << 5)
#define USB_TYPE_RESERVED       (0x03 << 5)/** USB recipients, the third of three bRequestType fields*/
#define USB_RECIP_MASK          0x1f
#define USB_RECIP_DEVICE        0x00
#define USB_RECIP_INTERFACE     0x01
#define USB_RECIP_ENDPOINT      0x02
#define USB_RECIP_OTHER         0x03
/* From Wireless USB 1.0 */
#define USB_RECIP_PORT          0x04
#define USB_RECIP_RPIPE     0x05

bRequest,表示具体是哪个request。
wValue,这个字段是request的参数,request不同,wValue就不同。
wIndex,也是request的参数,bRequestType指明request针对的是设备上的某个接口或端点的时候,wIndex就用来指明是哪个接口或端点。
wLength,控制传输中DATA transaction阶段的长度,方向已经在bRequestType那儿指明了。如果这个值为0,就表示没有DATA transaction阶段,bRequestType的方向位也就无效了。
和 struct usb_ctrlrequest 的约会暂时就到这里,回到usb_control_msg函数里。很明显要进行控制传输,得首先创建一个struct usb_ctrlrequest结构体,填上请求的内容。12到16行就是来使用传递过来的参数初始化这个结构体的。对于刚开始提到的SET_ADDRESS来说,bRequest的值就是USB_REQ_SET_ADDRESS,标准请求之一,ch9.h里定义有。因为SET_ADDRESS请求并不需要DATA阶段,所以wLength为0,而且这个请求是针对设备的,所以wIndex也为0。这么一来,bRequestType的值也只能为0了。因为是设置设备地址的,总得把要设置的地址发给设备,不然设备会比咱们还一头雾水不知道主机是嘛个意思,所以请求的参数wValue就是之前hub已经你的设备指定好的devnum。其实SET_ADDRESS请求各个部分的值spec 9.4.6里都有规定,就和我这里说的一样,不信你去看看。
接下来先看20行,走到这儿就表示成也好败也好,总之这次通信已经完成了,那么struct usb_ctrlrequest结构体也就没用了,没用的东西要好不犹豫的精简掉。
回头看18行,这是引领咱们往深处走了,不过不怕,路有多远,咱们看下去的决心就有多远。

Linux那些事儿 之 戏说USB(19)设备的生命线(二)相关推荐

  1. Linux那些事儿 之 戏说USB(23)设备的生命线(二)

    转载地址:http://blog.csdn.net/fudan_abc/article/details/1814938 现在设备的struct usb_device结构体已经准备好了,只是还不怎么饱满 ...

  2. 【转】Linux那些事儿 之 戏说USB(23)设备的生命线(二)

    现在设备的struct usb_device结构体已经准备好了,只是还不怎么饱满,hub接下来就会给它做做整容手术,往里边儿塞点什么,充实一些内容,比如:将设备的状态设置为Powered,也就是加电状 ...

  3. Linux那些事儿 之 戏说USB(19)设备

    转载地址:http://blog.csdn.net/fudan_abc/article/details/1807800 第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里 ...

  4. 【转】Linux那些事儿 之 戏说USB(19)设备

    第一眼看到struct usb_device这个结构,我仿佛置身于衡山路的酒吧里,盯着舞池里扭动的符号,眼神迷离. 交大里苟了几年,毕业了又是住在学校附近的徐虹北路上,沿着虹桥路走过去,到徐家汇不过1 ...

  5. Linux那些事儿 之 戏说USB(27)设备的生命线(十)

    跟着设备的生命线走到现在,我算是明白了,什么东西的发展都是越往后越高级越复杂.再给张小表,看看现在和上次那张表出现的时候有什么变化. state        USB_STATE_ADDRESS sp ...

  6. Linux那些事儿 之 戏说USB(24)设备的生命线(七)

    算是进入了HCD的片儿区,这里的老大不是帮派头目也不是巡逻片儿警,而是几个结构.在HCD这个片儿区,这个山头儿,王中之王就是include/linux/usb/hcd.h里定义的struct usb_ ...

  7. Linux那些事儿 之 戏说USB(22)设备的生命线(一)

    转载地址:http://blog.csdn.net/fudan_abc/article/details/1814891 李安告诉我们,每个人的心中都有一座断背山,每个人的手里都有一条生命线. Goog ...

  8. Linux那些事儿 之 戏说USB(28)设备的生命线(十一)

    现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都彼此的挤在一起,所以得想 ...

  9. Linux那些事儿 之 戏说USB(25)设备的生命线(八)

    回到struct usb_hcd,继续努力的往下看. 7行,又见kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看drivers/usb/core/hcd ...

最新文章

  1. linux下access函数的应用
  2. 宏基ec471g黑苹果_宏碁acer ec-471g 黑苹果配置教程
  3. “数据挖掘世界杯”KDD Cup不取消!全球顶尖AI团队必争之地!
  4. 【图像处理】——Python实现灰度特征提取
  5. Eclipse集成svn后出现Failed to load JavaHL Library的解决办法
  6. 佳士得于5月19日至27日以NFT形式拍卖安迪·沃霍尔作品
  7. html中iframe子页面与父页面元素的访问以及js变量的访问
  8. 转:VS2005 快捷键
  9. 终结者2显示天网服务器,《终结者2:审判日》天网觉醒秘测开服公告
  10. Hibernate缓存研究
  11. 电信光猫DDNS的设置经历
  12. U2000V200R018 的安装教程我们用微软的Windows7专业版示例
  13. 数据库索引选择的探索(二)之直方图
  14. 新闻分类:多分类问题
  15. html显示证书错误代码,Win7打开网页提示证书错误的原因及解决方法
  16. C# 地理信息系统GIS开源软件
  17. AE关键帧动画基础概念
  18. 手游自动化测试基础:方法及流程
  19. 【JavaSE】图书馆系统制作--JavaSE简易知识整合
  20. 2016京东校园招聘在线笔试回忆版

热门文章

  1. python list 的乘法
  2. Java--对象与类(三)
  3. ReentrantReadWriteLock读写锁的使用
  4. JAVA基础——编程练习(一)
  5. 每个程序员都需要学习 JavaScript 的7个理由
  6. 我已经喜欢上了Python
  7. java中名词概念的理解
  8. ASP.NET 4.0: 请求验证模式变化导致ValidateRequest=false失效
  9. android jni语法,Android NDK中的JNIEXPORT和JNICALL
  10. xdoj 1144 K叉哈弗曼树