`
javatome
  • 浏览: 823134 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

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;

243 dev = kzalloc(sizeof(*dev), GFP_KERNEL);

244 if (!dev)

245 return NULL;

249 return NULL;

250 }

256 dev->state = USB_STATE_ATTACHED;

258 INIT_LIST_HEAD(&dev->ep0.urb_list);

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;

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 */

273 dev->devpath[0] = '';

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

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;

296 INIT_LIST_HEAD(&dev->filelist);

298 #ifdef CONFIG_PM

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 */

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]赋值为‘<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="’"><span lang="EN-US" style="FONT-FAMILY: Verdana">0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">’</span></chmetcnv>,以示特殊,然后父设备设为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行,电源管理的,仍然飘过。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics