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

原创 2007年10月08日 11:04:00

李安告诉我们,每个人的心中都有一座断背山,每个人的手里都有一条生命线。

Google一下,找到这么一句:通常生命线都起自姆指和食指的中央,如果比这个位置还要上方的人,主进取心和克已心都很强,只要奋斗努力,事业终有可成。再仔细看看自己的手,果然位于姆指与食指的中间靠上08微米,也算是搭了个08年奥运的好兆头,继海南那个万里长跑迎奥运的小女孩儿之后,俺也终于有了奥运题材,有了乐观的理由。什么叫乐观派的人?这个就像茶壶一样,屁股都烧的红红的,他还有心情吹口哨。俺可不想做茶壶,还是赶紧的言归正传吧。

BH的人生有BH的活法,设备的人生有设备的过法。设备也有它自己的生命线,自你把它插到hub上始,自你把它从hub上拔下来终,它的一生是勤勉努力、朴实无华的一生,它的一生是埋头苦干、默默奉献的一生。BH的人生不需要解释,设备的人生值得我们去分析。

我相信科学不相信迷信,港剧里俺最喜欢的罗嘉良在迷离档案里说,要用科学来研究迷信。咱们现在就沿着设备的生命线走一走,看看其中都发生了什么。

首先当然是你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它也知道有朋自远方来不亦乐乎,于是精神头儿就上来了,就觉得有必要为设备做点什么。它会为设备分配一个struct usb_device结构的对象并初始化,并调用设备模型提供的接口将设备添加到usb总线的设备列表里,然后usb总线会遍历驱动列表里的每个驱动,调用自己的match函数看它们和你的设备或接口是否匹配。这不,又走到match函数了,那接下来那,先看看前面的,等真正遇到它的时候再说。

这么说是不是很不过瘾?本来满怀期待的耗费一个匹萨的钱去上海影城看大片儿,结果只看到了一个小馒头和一个圆环套圆环娱乐城。为了看清楚那一个馒头背后的故事,接下来只要你不嫌烦,咱就往细里去说,说到你烦为止。

hub检测到自己的某个端口有设备连接了进来后,它会调用core里的usb_alloc_dev函数为struct usb_device结构的对象申请内存,这个函数在drivers/usb/core/usb.c文件里定义

226 /**
227  * usb_alloc_dev - usb device constructor (usbcore-internal)
228  * @parent: hub to which device is connected; null to allocate a root hub
229  * @bus: bus used to access the device
230  * @port1: one-based index of port; ignored for root hubs
231  * Context: !in_interrupt()
232  *
233  * Only hub drivers (including virtual root hub drivers for host
234  * controllers) should ever call this.
235  *
236  * This call may not be used in a non-sleeping context.
237  */
238 struct usb_device *
239 usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
240 {
241         struct usb_device *dev;
242
243         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
244         if (!dev)
245                 return NULL;
246
247         if (!usb_get_hcd(bus_to_hcd(bus))) {
248                 kfree(dev);
249                 return NULL;
250         }
251
252         device_initialize(&dev->dev);
253         dev->dev.bus = &usb_bus_type;
254         dev->dev.type = &usb_device_type;
255         dev->dev.dma_mask = bus->controller->dma_mask;
256         dev->state = USB_STATE_ATTACHED;
257
258         INIT_LIST_HEAD(&dev->ep0.urb_list);
259         dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
260         dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
261         /* ep0 maxpacket comes later, from device descriptor */
262         dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
263
264         /* Save readable and stable topology id, distinguishing devices
265          * by location for diagnostics, tools, driver model, etc.  The
266          * string is a path along hub ports, from the root.  Each device's
267          * dev->devpath will be stable until USB is re-cabled, and hubs
268          * are often labeled with these port numbers.  The bus_id isn't
269          * as stable:  bus->busnum changes easily from modprobe order,
270          * cardbus or pci hotplugging, and so on.
271          */
272         if (unlikely(!parent)) {
273                 dev->devpath[0] = '';
274
275                 dev->dev.parent = bus->controller;
276                 sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
277         } else {
278                 /* match any labeling on the hubs; it's one-based */
279                 if (parent->devpath[0] == '')
280                         snprintf(dev->devpath, sizeof dev->devpath,
281                                 "%d", port1);
282                 else
283                         snprintf(dev->devpath, sizeof dev->devpath,
284                                 "%s.%d", parent->devpath, port1);
285
286                 dev->dev.parent = &parent->dev;
287                 sprintf(&dev->dev.bus_id[0], "%d-%s",
288                         bus->busnum, dev->devpath);
289
290                 /* hub driver sets up TT records */
291         }
292
293         dev->portnum = port1;
294         dev->bus = bus;
295         dev->parent = parent;
296         INIT_LIST_HEAD(&dev->filelist);
297
298 #ifdef  CONFIG_PM
299         mutex_init(&dev->pm_mutex);
300         INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
301         dev->autosuspend_delay = usb_autosuspend_delay * HZ;
302 #endif
303         return dev;
304 }

usb_alloc_dev函数就相当于usb设备的构造函数,参数里边儿,parent是设备连接的那个hubbus是设备连接的那条总线,port1就是设备连接在hub上的那个端口。

243行,为一个struct usb_device结构的对象申请内存并初始化为0。直到在看到这一行的前一天,我还仍在使用kmallocmemset这对儿最佳拍档来申请内存和初始化,但是在看到kzalloc之后,我知道了江山代代有人出,眉先生,须后生,先生不及后生长的道理,没看那些90后的都在嘲笑咱们80后的不知道kzalloc了么。kzalloc直接取代了kmalloc/memset,一个函数起到了两个函数的作用。这种角色当然是最讨人喜欢的了,所以说现在那些简历上搞什么艺术照片儿性感写真什么的完全毫无必要,只要写上个能够做牛做马,白天做牛晚上做马,那些老板就乐翻天了,哪还用玩儿那些虚头。

然后是判断内存申请成功了没,不成功就不用往下走了。那么通过这么几行,咱们应该记住两个凡是,凡是你想用kmalloc/memset组合申请内存的时候,就使用kzalloc代替吧,凡是申请内存的,不要忘了判断是不是申请成功了。我们的心里应该把这两个凡是提高到和四项基本原则一样的地位。

247行,这里的两个函数是hcd,主机控制器驱动里的,具体咱不讲,只要知道usb的世界里一个主机控制器对应着一条usb总线,主机控制器驱动用struct usb_hcd结构表示,一条总线用struct usb_bus结构表示,函数bus_to_hcd是为了获得总线对应的主机控制器驱动,也就是struct usb_hcd结构对象,函数usb_get_hcd只是将得到的这个usb_hcd结构对象的引用计数加1,为什么?因为总线上多了一个设备,设备在主机控制器的数据结构就得在,当然得为它增加引用计数。如果这俩函数没有很好的完成自己的任务,那整个usb_alloc_dev函数也就没有继续执行下去的必要了,将之前为struct usb_device结构对象申请的内存释放掉就稍息去吧。

252行,device_initialize是设备模型里的函数,这里就是将struct usb_device结构里嵌入的那个struct device结构体初始化掉,以后好方便用,初始化些什么?以前资本家宁可把牛奶倒掉也不给穷人喝,现在房产商宁可把房子空着也不给百姓住,而我宁可自己不知道也不告诉你。

253行,将设备所在的总线类型设置为usb_bus_typeusb_bus_type咱们很早很早就遇到它了,usb子系统的初始化函数usb_init里就把它给注册掉了,还记得聊到模型的时候说过的那个著名的三角关系不,这里就是把设备和总线这条边儿给搭上了。如果您忘了,那就回头看看吧。

254行,将设备的设备类型初始化为usb_device_type,这是咱们上节第二次遇到usb_device_match函数,走设备那条路,使用is_usb_device判断是不是usb设备时留下的疑问,就是在这儿把设备的类型给初始化成usb_device_type了。

255行,这个就是与DMA传输相关的了,设备能不能进行dma传输,得看主机控制器的脸色,主机控制器不支持的话设备自作多情那也没有用。所以这里dma_mask被设置为host controllerdma_mask

256行,将usb设备的状态设置为Attached,表示设备已经连接到usb接口上了,是hub检测到设备时的初始状态。咱们前面说了,USB设备从生到死都要按照那么几个状态,这里随着设备生命线的逐渐深入,咱们会看到设备的状态也在逐渐的变化。

258行,端点0实在是太太太特殊了,这个咱们是一而再再而三的感叹,struct usb_device里直接就有这么一个成员ep0,这行就将ep0urb_list给初始化掉。因为接下来遇到的那些主要角色的成员前面集中都说过了,咱们就不再说它们是嘛意思了,忘了的话可以到前面看看。

259行,260行,分别初始化了端点0的描述符长度和描述符类型。

260行,使struct usb_device结构里的ep_inep_out指针数组的第一个成员指向ep0ep_in[0]ep_out[0]本来表示的就是端点0

272行,这里平白无故的多出了个unlikely,不知道什么意思?先看看它们在include/linux/compiler.h的定义

54 /*
55  * Generic compiler-dependent macros required for kernel
56  * build go below this comment. Actual compiler/compiler version
57  * specific implementations come from the above header files
58  */
59
60 #define likely(x)       __builtin_expect(!!(x), 1)
61 #define unlikely(x)     __builtin_expect(!!(x), 0)

unlikely不是一个人在奋斗,还有个likely。定义里那个怪怪的__builtin_expectGCC里内建的一个函数,具体是做嘛用的可以看看GCC的手册

long __builtin_expect (long exp, long c)
You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.
The return value is the value of exp, which should be an integral expression. The value of c must be a compile-time constant. The semantics of the built-in are that it is expected that exp == c. For example:
if (__builtin_expect (x, 0))
         foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since
you are limited to integral expressions for exp, you should use constructions such
as
if (__builtin_expect (ptr != NULL, 1))
         error ();
when testing pointer or floating-point values.

大致意思就是由于大部分写代码的在分支预测方面做的比较的糟糕,所以GCC提供了这个内建的函数来帮助处理分支预测,优化程序,它的第一个参数exp为一个整型的表达式,返回值也是这个exp,它的第二个参数c的值必须是一个编译期的常量,那这个内建函数的意思就是exp的预期值为c,编译器可以根据这个信息适当的重排条件语句块的顺序,将符合这个条件的分支放在合适的地方。具体到unlikely(x)就是告诉编译器条件x发生的可能性不大,那么这个条件块儿里语句的目标码可能就会被放在一个比较远的为止,以保证经常执行的目标码更紧凑。likely则相反。在一个月高风黑的夜晚,你向你从穿开裆裤就开始暗恋的mm表白,她回复你

      if(unlikely(你以后会有房子,不是按揭的,会有车子,不是奥拓))

           咱们明天可以去领证儿

你觉得你是应该高兴还是不应该高兴?她这明摆着就是告你别做梦了,你有房子车子的可能性太小了,往远处排吧,她要先照顾有房有车的。如果没看过内核,你说不定还兴高采烈觉得成了那,你会觉得以后房子车子还不是小case。所以,这里要感叹一下,读内核是多么重要啊。

使用的时候还是很简单的,就是,if语句你照用,只是如果你觉得if条件为1的可能性非常大的时候,可以在条件表达式外面包装一个likely(),如果可能性非常小,则用unlikely()包装。那么这里272行的意思就很明显了,就是说写内核的哥们儿觉得你的usb设备直接连接到root hub上的可能性比较小,因为parent指的就是你的设备连接的那个hub

272~291行整个的代码就是首先判断你的设备是不是直接连到root hub上的,如果是,将dev->devpath[0]赋值为‘0,以示特殊,然后父设备设为controller,同时把dev->bus_id[]设置为像usb1/usb2/usb3/usb4这样的字符串。如果你的设备不是直接连到root hub上的,有两种情况,如果你的设备连接的那个hub是直接连到root hub上的,则dev->devpath就等于端口号,否则dev->devpath就等于在父hubdevpath基础上加一个‘.’再加一个端口号,最后把bus_id[]设置成1-/2-/3-/4-这样的字符串后面连接上devpath

293~295行,没什么说的,轻轻的飘过。

296行,初始化一个队列,usbfs用的。

298~302行,电源管理的,仍然飘过。

 
版权声明:本文为博主原创文章,未经博主允许不得转载。

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

首先当然是你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它也知道有朋自远方来不亦乐乎,于是精神头儿就上来了,就觉得有必要为设备做点什么。它会为设备分配一个struct usb_...
  • zhqh100
  • zhqh100
  • 2015年03月25日 13:56
  • 678

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

下面接着看那三个基本点。 第一个基本点,usb_alloc_urb函数,创建urb的专用函数,为一个urb申请内存并做初始化,在drviers/usb/core/urb.c里定义。 struct ur...
  • zhqh100
  • zhqh100
  • 2015年03月25日 19:04
  • 702

Linux那些事儿 之 戏说USB(29)驱动的生命线(一)

现在开始就沿着usb_generic_driver的成名之路走一走,设备的生命线你可以想当然的认为是从你的usb设备连接到hub的某个端口时开始,驱动的生命线就必须得回溯到usb子系统的初始化函数us...
  • zhqh100
  • zhqh100
  • 2015年03月26日 19:42
  • 620

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

回到struct usb_hcd,继续努力的往下看。 7行,又见kref,usb主机控制器的引用计数。struct usb_hcd也有自己专用的引用计数函数,看hcd.c文件 static void...
  • zhqh100
  • zhqh100
  • 2015年03月26日 10:48
  • 701

Linux那些事儿 之 戏说USB(21)设备的生命线(四)

继续urb urb_list,还记得每个端点都会有的那个urb队列么?那个队列就是由这里的urb_list一个一个的链接起来的。HCD每收到一个urb,就会将它添加到这个urb指定的那个端点的urb...
  • zhqh100
  • zhqh100
  • 2015年03月25日 16:40
  • 1035

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

drivers/usb/core/urb.c int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { static int pipetyp...
  • zhqh100
  • zhqh100
  • 2015年03月25日 20:24
  • 946

Linux那些事儿 之 戏说USB(20)设备的生命线(三)

函数usb_control_msg调用了usb_internal_control_msg之后就去一边儿睡大觉了,脏活儿累活儿,全部留给usb_internal_control_msg去做了,这才叫骨干...
  • zhqh100
  • zhqh100
  • 2015年03月25日 15:37
  • 692

Linux那些事儿 之 戏说USB(26)设备的生命线(九)

聊完了struct usb_hcd和struct usb_bus,算是已经向HCD片儿区的老大们拜过山头了,接下来就该看看usb_submit_urb()最后的那个遗留问题usb_hcd_submit...
  • zhqh100
  • zhqh100
  • 2015年03月26日 14:15
  • 346

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

现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符、接口描述符还是端点描述符都不分男女不分彼此的挤在一...
  • zhqh100
  • zhqh100
  • 2015年03月26日 17:04
  • 913

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

跟着设备的生命线走到现在,我算是明白了,什么东西的发展都是越往后越高级越复杂。再给张小表,看看现在和上次那张表出现的时候有什么变化。 state        USB_STATE_ADDRESS s...
  • zhqh100
  • zhqh100
  • 2015年03月26日 15:41
  • 402
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux那些事儿 之 戏说USB(22)设备的生命线(一)
举报原因:
原因补充:

(最多只允许输入30个字)