`
dato0123
  • 浏览: 914869 次
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是Hub(14)没完没了的判断

 
阅读更多

看着这代码,空虚的代码,麻木的走在崩溃边缘.最讨厌这种没完没了的判断了.记得有一次在中信泰富广场去摩托罗拉中国研发中心面试,也是问了一道挺简单的题目,我就把基本的算法说了一下,然后面试官就说为什么没有错误判断.你说像我这种根本不怎么懂编程的人好不容易能回答出一道题,已经很不错了,为何那些企业要求都这么高呢?一个人因为没有工作经验而不能得到一个工作,但是他又因为没有一个工作而得不到工作经验.算了,现实充满残忍,何必太认真.算法没错,程序没错,世界没错,我错了.

2645行我们返回了,前面说了,正常都是返回0或者正数,如果小于0那就说明失败了,前面我们还说了,我们在使用interface之前会调用usb_get_intf()来增加引用计数,而与之对应的是usb_put_intf(),这里我们就调用了usb_put_intf()来减少引用计数.continue的意思开始新的一轮while循环,就是踏上奈何桥,喝下孟婆汤,卸下前世的情仇,忘却今生的爱人,睁开眼又是一生,如果hub_event_list里还有东西的话就继续处理,没有那就歇息吧,日子还是像原来那样一天天的过着.

2649,usb_get_intfdata(),判断一下,得到的是不是hub,你问为什么得到的是hub?回过去看hub_probe(),别忘了那时候我们调用过usb_set_intfdata从而把intfhub联系了起来的,这两个函数当年我们在usb-storage里面就这样用的,不过当时我们不会判断usb_get_intfdata()得到的到底是什么,而这里我们却要判断,因为在hub_disconnect(),又这么一句,usb_set_intfdata(intf,NULL),hub_events()hub_disconnect()是异步执行的,就是说你执行你的,我执行我的,换言之,当咱们这里hub_events()正执行着呢,hub_disconnect()那边可能就已经取消了intfhub之间建立起来的那层美好的关系,所以咱们这里需要判断一下.

2653,现在是时候该说一说USB_STATE_NOTATTACHED这个宏了,include/linux/usb/ch9.h:

557 enum usb_device_state {

558 /* NOTATTACHED isn't in the USB spec, and this state acts

559 * the same as ATTACHED ... but it's clearer this way.

560 */

561 USB_STATE_NOTATTACHED = 0,

562

563 /* chapter 9 and authentication (wireless) device states */

564 USB_STATE_ATTACHED,

565 USB_STATE_POWERED, /* wired */

566 USB_STATE_UNAUTHENTICATED, /* auth */

567 USB_STATE_RECONNECTING, /* auth */

568 USB_STATE_DEFAULT, /* limited function */

569 USB_STATE_ADDRESS,

570 USB_STATE_CONFIGURED, /* most functions */

571

572 USB_STATE_SUSPENDED

573

574 /* NOTE: there are actually four different SUSPENDED

575 * states, returning to POWERED, DEFAULT, ADDRESS, or

576 * CONFIGURED respectively when SOF tokens flow again.

577 */

578 };

定义了这么一堆的宏,其中USB_STATE_NOTATTACHED的意思很明显,设备没有插在端口上,在代码里,有几个函数会把设备的状态设置成这个,一个是汇报Host Controller异常死机的函数,usb_hc_died(),一个是咱们hub驱动自己提供的函数,hub_port_disable(),用于关掉一个端口的函数,还有就是用来断开设备的函数usb_disconnect(),总之这几个函数没一个是好鸟,只要它们执行了,那么咱们的设备肯定就没法工作了,所以这里在干正经事之前,先判断设备的状态是不是USB_STATE_NOTATTACHED,如果是的那么就设置错误代码为-ENODEV,然后调用hub_pre_reset(),这个函数是与reset相关的,咱们先不理睬.以后再来看.

2660,usb_autopm_get_interface(),这个函数是usb core提供的,又是一个电源管理的函数,这个函数所做的事情就是让这个usb interface的电源引用计数加一,也就是说,只要这个引用计数大于0,这个设备就不允许autosuspend.autosuspend就是当用户在指定的时间内没有什么活动的话,就自动挂起.应该说,usb中引入autosuspend/autoresume这还是最近的事情了,最初有这个想法是在去年,2006年的5月底,Alan Stern大侠在开源社区提出,同志们,俺最近打算开始在USB里面实现对autosuspend/autoresume的支持.所谓的autosuspend/autoresume,实际上是一种运行时的电源管理方式.而这些事情将由驱动程序来负责,,当驱动程序觉得它的设备闲置了,它就会触发suspend事件,而当驱动程序要使用一个设备了,但该设备正处于suspended状态,那么驱动程序就会触发一个resume事件,suspend事件和resume事件很显然是相对应的,一个挂起一个恢复.这里有一个很关键的理念,又涉及到了前面讲的那个设备树,,当一个设备挂起的时候,它必须通知它的parent,parent就会决定看parent是不是也自动挂起,反过来,如果一个设备需要resume,那么它必须要求它的parentresume,就是说这里有这么一种逻辑关系,一个parent要想suspend,只有在它的childrensuspend它才可以suspend,而一个child想要resume,只有在它的parentresume了它才可以resume.还不明白?举个例子,我和我的室友放寒假在复旦南区澡堂洗澡,每人一个水龙头,洗着洗着,管理员说国家下发了文件说要建设节约型社会,考虑到寒假期间洗澡的人数比较少,决定把水龙头的总闸调小,但是也不能让我们正在洗的人洗不了,所以,它就得先问我们,只有满足了我们在洗的人的那几个水龙头的水量,才可以关小总闸,否则我们肯定得跟他急.同样,开学了以后,大家都来洗澡,可是总闸还是那么小,那大家不干了,但是不干了你得跟管理员说调整总闸,你不能说每个人就调整自己的那个开关,那样肯定没用,总的流量就那么小,时不时还来点侧漏,你说你能洗吗?所以这种情况下就得先开了总闸你单个的开关的调节才有意义.

对于hub来说,hub_events()处于运行的状态,那么这个hub interface就是在使用,这种情况下是不可以进行autosuspend.对于咱们这个上下文来说,usb_autopm_get_interface()返回的就是0.但是如果咱们的hub是处于suspended状态,那么这里首先就会把hub唤醒,即会执行resume.先不多说了,继续往下看吧.

2667,判断quiescing,以前咱们说过,struct usb_hub里面有两个成员,quiescingactiviating,并且咱们在hub_activate()中已经看到了,我们把quiescing设置成了0,而把activating设置成了1.现在是时候来说一说这两个变量的含义了.我们说了quiescing是停止的意思,reset的时候我们会设置它为1,suspend的时候我们也会把它设置为1,一旦把它设置成了1,那么hub驱动程序就不会再提交任何URB,而如果我们把activating,那么hub驱动程序就会给每个端口发送一个叫做Get Port Status的请求,通常情况下,hub驱动只有在一个端口发生了状态变化的情况下才会去发送Get Port Status从而去获得端口的状态.所以就是说,正常情况下,这两个flag都是不会设置的.即正常情况下这两个flag都应该是0.

2671,以咱们这个情景来到这里,hub->error当然是0,但是如果今后我们正式工作以后,再次来到这里的话,hub->error可能就不再是0.对于那种情况,咱们需要调用usb_reset_composite_device(),这个函数是咱们自己定义的,目的就是把设备reset,我们来具体看一下,来自drivers/usb/core/hub.c:

3041 /**

3042 * usb_reset_composite_device - warn interface drivers and perform a USB port reset

3043 * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)

3044 * @iface: interface bound to the driver making the request (optional)

3045 *

3046 * Warns all drivers bound to registered interfaces (using their pre_reset

3047 * method), performs the port reset, and then lets the drivers know that

3048 * the reset is over (using their post_reset method).

3049 *

3050 * Return value is the same as for usb_reset_device().

3051 *

3052 * The caller must own the device lock. For example, it's safe to use

3053 * this from a driver probe() routine after downloading new firmware.

3054 * For calls that might not occur during probe(), drivers should lock

3055 * the device using usb_lock_device_for_reset().

3056 *

3057 * The interface locks are acquired during the pre_reset stage and released

3058 * during the post_reset stage. However if iface is not NULL and is

3059 * currently being probed, we assume that the caller already owns its

3060 * lock.

3061 */

3062 int usb_reset_composite_device(struct usb_device *udev,

3063 struct usb_interface *iface)

3064 {

3065 int ret;

3066 struct usb_host_config *config = udev->actconfig;

3067

3068 if (udev->state == USB_STATE_NOTATTACHED ||

3069 udev->state == USB_STATE_SUSPENDED) {

3070 dev_dbg(&udev->dev, "device reset not allowed in state %d/n",

3071 udev->state);

3072 return -EINVAL;

3073 }

3074

3075 /* Prevent autosuspend during the reset */

3076 usb_autoresume_device(udev);

3077

3078 if (iface && iface->condition != USB_INTERFACE_BINDING)

3079 iface = NULL;

3080

3081 if (config) {

3082 int i;

3083 struct usb_interface *cintf;

3084 struct usb_driver *drv;

3085

3086 for (i = 0; i < config->desc.bNumInterfaces; ++i) {

3087 cintf = config->interface[i];

3088 if (cintf != iface)

3089 down(&cintf->dev.sem);

3090 if (device_is_registered(&cintf->dev) &&

3091 cintf->dev.driver) {

3092 drv = to_usb_driver(cintf->dev.driver);

3093 if (drv->pre_reset)

3094 (drv->pre_reset)(cintf);

3095 }

3096 }

3097 }

3098

3099 ret = usb_reset_device(udev);

3100

3101 if (config) {

3102 int i;

3103 struct usb_interface *cintf;

3104 struct usb_driver *drv;

3105

3106 for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {

3107 cintf = config->interface[i];

3108 if (device_is_registered(&cintf->dev) &&

3109 cintf->dev.driver) {

3110 drv = to_usb_driver(cintf->dev.driver);

3111 if (drv->post_reset)

3112 (drv->post_reset)(cintf);

3113 }

3114 if (cintf != iface)

3115 up(&cintf->dev.sem);

3116 }

3117 }

3118

3119 usb_autosuspend_device(udev);

3120 return ret;

3121 }

usb_autoresume_device()增加device的引用计数,禁止设备autosuspend的发生.

后面的那个usb_autosuspend_device()则刚好相反,减少设备的引用计数,并且使得设备可以被autosuspend.

3068,在设备处于USB_STATE_NOTATTACHED或者USB_STATE_SUSPENDED的状态时,reset是不被允许的.

3078,别忘了我们传递给usb_reset_composite_device的有两个参数,一个是设备,一个是interface.USB_INTERFACE_BINDING是一个宏,来自include/linux/usb.h:

83 enum usb_interface_condition {

84 USB_INTERFACE_UNBOUND = 0,

85 USB_INTERFACE_BINDING,

86 USB_INTERFACE_BOUND,

87 USB_INTERFACE_UNBINDING,

88 };

这是表征interface的状态的,BINDING就表示正在和driver绑定,有些事情不是我故意瞒着你,而是我的确不想多说,确切的说,我也是一言难尽哪.最开始,usb core发现初始化设备的时候,但是在hub_probe被调用之前,INTERFACE是处于USB_INTERFACE_BINDING状态的,直到hub_probe结束了之后,INTERFACE则是处于USB_INTERFACE_BOUND状态,即所谓的绑定好了,而如果hub_probe出了错,那么INTERFACE就将处于USB_INTERFACE_UNBOUND状态.我们这里configstruct usb_host_config结构体指针,被赋为udev->actconfig,对于一个设备来说,它使用的是什么配置,这个在初始化的时候就设好了的.对于Root Hub我们先不管它究竟设置为什么了,对于普通的Hub我们以后会看到的.struct usb_host_config结构体中有一个结构体struct usb_config_descriptor desc,表示configuration描述符,还有一个struct usb_interface *interface[USB_MAXINTERFACES]数组,USB_MAXINTERFACES定义为32,这些我们当年在usb-storage里面也都见过.所以config->desc.bNumInterfaces以及config->interface[]数组的意思就很明确了,3086行开始的这个for循环就是说,这个device有几个interface就一个一个遍历,cintf作为临时变量来表征每一个struct usb_interface.首先我们要明白, usb_reset_composite_device()这个函数我们可是既指定了设备又指定了interface,那么3088行判断cintf等不等于iface是什么意思呢?回到刚才那个3078,我们设置了,如果iface不处在BINDING的情况下,我们将iface设置为NULL,而这里cintf是从config->interface[]数组里得出来的值,它肯定不为NULL,那么这里如果cintf不等于iface,那就说明iface之前是不处于BINDING的状态,对于这种情况我们需要执行3089,down(&cintf->dev.sem),这样做的原因是为了等待,devstruct device结构体,struct usb_interface结构体的一个成员,semstruct device结构体的一个信号量,struct semaphore sem,这个信号量专门用于同步,之所以这里需要信号量,原因如下:既然我们现在针对的是一个device多个interface的情况,那么势必就有这么一种可能,一个设备多个interface,每个interface对应一个driver,那么当设备fail的时候,有可能每个driver都希望能够reset,那么对于这种情况,我们当然需要保证这个过程不要出现混乱,于是设置一个信号量就ok,reset的时候我就不能reset,reset的时候你就不能reset.那么为什么要单独把USB_INTERFACE_BINDING列出来呢?注释里说得很清楚了,当一个interface的状态处于BINDING的时候,其实就是这个interface对应的driverprobe()函数正在执行,这个时候实际上是已经获得了锁的.你问为什么?看代码去,probe函数是咱们提供给usb core调用的,咱们自己不会调用它,usb core,调用probe()的前后也有这么一对down()/up().因为probe()这个操作也忌讳被别人影响.所以说这里对于正处于BINDING状态的interface,就不需要再获得锁了,或者说不需要获得信号量了.否则就将是一道经典的死锁问题,我第一次遇到内核Bug就是一次死锁问题,8250串口驱动中,明明已经有锁了,还要再次去获取锁,结果系统就挂了.而这正是锁的哲学,即走别人的路,让别人无路可走.

另一个问题需要清楚的是,usb_reset_composite_device()这个函数的诞生是因为目前越来越多的设备都是复合设备,即一个设备里面有多个interface的那种.内核中引入这个函数是在2006年夏天,在德国世界杯开幕前不久,在我离开Intel的那一天,确切的说是,2006526,Alan Stern大侠又一次向社区里提出一个新的理念.,原本,对于每个设备来说,都可以调用函数usb_reset_device来执行reset操作,而当今天下发展趋势是让一个设备包含多个接口,即一个device包含多个interface,而我们知道一个driver对应一个interface,于是就出现了多个drivers对应一个设备的情况,那么一个usb_reset_device()函数就有可能对所有的interface都造成影响,于是,Alan利用这样两个函数,struct usb_driver结构体中两个成员函数pre_resetpost_reset,我们知道每个struct usb_driver都有两个成员pre_resetpost_reset,Alan的理念是每个driver定义自己的pre_resetpost_reset,当我们在调用usb_reset_device之前,先遍历调用每个driverpre_reset,Alan称这些个pre_reset为给每个绑定在该设备上的drivers一个警告,告诉它们,reset.在执行完usb_reset_device之后,再遍历调用每个driverpost_reset,post_reset的作用是让每个driver知道,reset完成了,另一方面,post_reset还有一个作用,因为reset会把设备原来的状态都给清除掉,所以post_reset就担负了这么一个使命,即重新初始化设备.但是你得明白,并不是每一个设备驱动都定义了pre_resetpost_reset,大家有没有必要执行这两个操作那都是自己决定,你要是无所谓,觉得别人reset整个device对你这个interface没什么影响,那就不为这两个指针赋值,那也ok.usb core为你提供了这么一个机制,你用不用自己看着办.这就相当于Intel允许报销医药费,但是可能我没有买药的需求,那么我就不用报销.这也就是为什么在3093行和3111行这两处要判断,判断这两个指针是否被赋了值,只有赋了值才去执行相应的函数,否则就没有必要瞎起哄.

继续看,device_is_registered()是一个内联函数,就是判断struct device结构体指针的一个成员is_registered是否为1,这个值对于Root Hub来说,Host Controller驱动程序中初始化时把它设置为1,对于普通的设备来说,以后我们会看到,Hub驱动为其作初始化的时候也会设置为1.dev.driver也是在初始化的时候会赋好值,特别的,对于咱们的hub,这个dev.driver就是与之对应的struct device_driver结构体,3092行这个to_usb_driver()是一个宏,它得到的就是与之对应的struct usb_driver结构体,而对于hub来说,这就是struct usb_driver hub_driver,所以,我们就不难知道,3094行以及后面3112行所做的就是调用hub_driver里面的两个成员函数,它们是hub_pre_reset()hub_post_reset().

Ok,暂时打住,越来越偏离主线了.总之,3094,3099,3012,这三个函数的调用,就是真正的完成了一次hubreset.就相当于计算机的一次重起,重起的原因是因为我们遇见了hub->error.这三个函数的细节我们先暂时不看,以后再看.需要强调的一点是,31013117行这一段,30813097行这一段,基本上是对称的.

3120,return ret返回了.返回值就是usb_reset_device的返回值.

回到hub_events()之后,立刻把hub->nerrorshub->error给复位了,设置为0,想开心就要舍得伤心,想忘记就要一切归零.其中nerrors是记录发生错误的次数的,nerrors就是number of errors,要是连续发生错误就每次让nerrors加一.

从下面开始就进入hub驱动中真正干正经事的代码了.可以说hub_events(),此前的每一行代码都显得非常的枯燥,让人根本看不明白hub驱动究竟是干嘛的,直到下面这些代码才真正诠释了一个hub驱动应该做的事情.我们需要明白,Hub的存在不是为了它自己,我们不是为了用Hub而买Hub,我们是为了让Hub连接我们真正想用的设备.设备是Hub生命中的过客,Hub点缀了设备的人生.

分享到:
评论

相关推荐

    Linux那些事儿之我是Hub

    Linux那些事儿之我是Hub,是前面Linux那些事之我是U盘,usb等的姐妹篇

    Linux那些事儿之我是HUB.pdf

    非常不错的一本书,LinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinux

    Linux那些事儿

    Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我...

    Linux那些事儿1-9合集

    linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿...

    Linux那些事儿之我是USB(第2版)

    Linux那些事儿第二版我是Hub一章,比较第一版修正了一些错误,增加了电源管理部分的分析,推荐阅读

    Linux那些事儿之全集

    导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿之我是USB(包括第一版和第二版完整文字)

    本文件包括第一第二完成版,第二版基于2.6.22内核,对USB子系统的大部分源代码逐行进行分析,系统地阐释了Linux内核中USB子系统是如何运转的,子系统内部的各个模块之间是如何互相协作、配合的。本次改版修改了第1版...

    Linux那些事儿之我是USB(第2版).pdf

    本书基于2.6.22内核,对USB子系统的大部分源代码逐行进行分析,系统地阐释了Linux内核中USB子系统是如何运转的,子系统内部的各个模块之间是如何互相协作、配合的。本次改版修改了第1版中出现的错误,增加了一个附录...

    linux的那些事儿全集

    Linux那些事儿之我是Block层 ...Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf ...Linux那些事儿之我是Hub.pdf Linux那些事儿之我是UHCI.pdf

    linux那些事全集

    Linux那些事儿之我是U盘 Linux那些事儿之我是USB_core Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs ...Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿.rar

    包括:Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是...

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

Global site tag (gtag.js) - Google Analytics