发布时间:2014-09-05 16:47:21作者:知识屋
罗马不是一天建成的。在让U盘工作之前,其实我们的驱动做了很多准备工作。
我们继续跟着感觉走,storage_probe(),972行至975行,一系列的以init_*命名的函数在此刻被调用,这里涉及了一些锁机制,等待机制,不过只是初始化,暂且不理睬,到后面用到时再细说,不过请记住,这几行每一行都是有用的。后面自然会用得着。
此时,我们先往下走,978行的associate_dev()和989行的get_device_info(),这两个函数是我们目前需要看的。
先看associate_dev()函数,定义于drivers/usb/storage/usb.c中。
438 /* Associate our private data with the USBdevice */
439 static intassociate_dev(struct us_data *us, struct usb_interface *intf)
440 {
441 US_DEBUGP("--%s/n", __FUNCTION__);
442
443 /* Fill inthe device-related fields */
444 us->pusb_dev= interface_to_usbdev(intf);
445 us->pusb_intf= intf;
446 us->ifnum= intf->cur_altsetting->desc.bInterfaceNumber;
447 US_DEBUGP("Vendor:0x%04x, Product: 0x%04x, Revision: 0x%04x/n",
448 le16_to_cpu(us->pusb_dev->descriptor.idVendor),
449 le16_to_cpu(us->pusb_dev->descriptor.idProduct),
450 le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
451 US_DEBUGP("InterfaceSubclass: 0x%02x, Protocol: 0x%02x/n",
452 intf->cur_altsetting->desc.bInterfaceSubClass,
453 intf->cur_altsetting->desc.bInterfaceProtocol);
454
455 /* Store our private data in theinterface */
456 usb_set_intfdata(intf,us);
457
458 /*Allocate the device-related DMA-mapped buffers */
459 us->cr= usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),
460 GFP_KERNEL, &us->cr_dma);
461 if(!us->cr) {
462 US_DEBUGP("usb_ctrlrequest allocation failed/n");
463 return-ENOMEM;
464 }
465
466 us->iobuf= usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,
467 GFP_KERNEL, &us->iobuf_dma);
468 if(!us->iobuf) {
469 US_DEBUGP("I/O buffer allocation failed/n");
470 return -ENOMEM;
471 }
472
473 us->sensebuf =kmalloc(US_SENSE_SIZE, GFP_KERNEL);
474 if (!us->sensebuf) {
475 US_DEBUGP("Sense buffer allocation failed/n");
476 return -ENOMEM;
477 }
478 return0;
479 }
我们首先来关注函数associate_dev的参数,struct us_data *us,传递给它的是us,这个不用多说了吧,此前刚刚为它申请了内存,并且初始化各成员为0。这个us将一直陪伴我们走下去,直到我们的故事结束。所以其重要性不言而喻。struct usb_interface *intf,storage_probe()函数传进来的两个参数之一。
总之,此处郑重申明一次,structus_data的结构体指针us,struct usb_interface结构体的指针intf,以及struct usb_device结构体和struct usb_device_id结构体在整个U盘驱动的故事中是唯一的,每次提到都是那个。而以后我们会遇上的几个重要的数据结构,struct urb urb,struct scsi_cmnd srb也非常重要,但是它们并不唯一,也许每次遇上都不一样,就像演戏一样。前边这几个数据结构的变量就像那些主角,而之后遇见的urb、srb,虽然频繁露面,但是只是群众演员,只不过这次是路人甲,下次是路人乙。所以,以后我们将只说us,不再说structus_data *us,structusb_interface * intf也将只用intf来代替。
us之所以重要,是因为接下来很多函数都要用到它,以及它的各个成员。实际上目前这个函数associate_dev所做的事情就是为us的各个成员赋值,毕竟此刻us和我们之前提到的那些函数struct usb_device,struct usb_interface,还没有一点关系。因而,这个函数,以及这之后的好几个函数都是为了给us的各成员赋上适当的值,之所以如此兴师动众去为它赋值,主要就是因为后面要利用它。所谓天下没有免费的午餐。
441行,本来无须多讲,因为只是一个debug语句,不过提一下__FUNCTION__这个宏,GCC 2.95以后的版本支持这个宏在编译时会被转换为函数名(字符串),这里自然就是“associate_dev”这个字符串,于是函数执行到这里就会打印一句话告诉世人我们执行到这个函数来了,这种做法显然会有利于调试程序。不过这个东西实际上不是宏,因为预处理器对它一无所知。它的心只有编译器才懂。
444行,pusb_dev,意思是point of usb device,struct us_data中的一个成员。按照我们刚才约定的规矩,此刻我将说它是us的一个成员,us->pusb_dev=interface_to_usbdev(intf),interface_to_usbdev我们前面已经讲过,其含义是把一个struct interface结构体的指针转换成一个struct usb_device的结构体指针。前面说过,struct usb_device对我们没有什么用,但是USB Core层的一些函数要求使用这个参数,所以我们不得已而为止,正所谓人在江湖身不由己。
445行,把intf赋给us的pusb_intf。
446行,us的ifnum,先看intf的cur_altsetting,这个容易令外行混淆。USB设备有一个配置,这个我们前面讲协议时讲了,而这里又有一个setting(设置)。咋一看有些奇怪,这两个词不是一回事吗?还是拿我们最熟悉的手机来打比方,配置不说了,只说设置。一个手机可能各种配置都确定了,如是振动还是铃声,各种功能都确定了,但是声音的大小还可以改变,通常手机的音量是一格一格地变动,大概也就五六格,那么这个可以算一个设置。这里cur_altsetting就是表示的当前的这个设置。cur_altsetting是一个struct usb_host_interface的指针,这个结构体定义于include/linux/usb.h:
69 /* host-side wrapper for one interfacesetting's parsed descriptors */
70 struct usb_host_interface {
71 struct usb_interface_descriptordesc;
72
73 /* arrayof desc.bNumEndpoint endpoints associated with this
74 * interface setting. these will be in no particular order.
75 */
76 struct usb_host_endpoint*endpoint;
77
78 char *string; /*iInterface string, if present */
79 unsignedchar *extra; /* Extradescriptors */
80 intextralen;
81 };
它的成员desc是一个struct usb_interface_descriptor结构体变量,这个结构体的定义是和USB协议直接对应的,定义于include/linux/usb/ch9.h。(这里取名为ch9是因为这个文件中很多东西对应于USB spec 2.0中的第9章,chapter 9。)
294 /* USB_DT_INTERFACE: Interface descriptor */
295 struct usb_interface_descriptor {
296 __u8 bLength;
297 __u8 bDescriptorType;
298
299 __u8 bInterfaceNumber;
300 __u8 bAlternateSetting;
301 __u8 bNumEndpoints;
302 __u8 bInterfaceClass;
303 __u8 bInterfaceSubClass;
304 __u8 bInterfaceProtocol;
305 __u8 iInterface;
306 } __attribute__ ((packed));
而其中我们这里提到的是bInterfaceNumber,一个设备可以有多个接口,于是每一个接口当然就得用一个编号了,要不然怎么区分啊?所有这些描述符里的东西都是出厂时就固化在设备中边的,而我们这里之所以可以用bInterfaceNumber来赋值,是因为USB Core在为设备初始化时就已经做足了这些功课,否则的话,我们真是寸步难行。
总之,us->ifnum就是这样,最终就是等于咱们眼下这个接口的编号。
447行到453行就是两句调试语句,打印更多一些描述符信息,包括设备描述符和接口描述符。
447行,usb_set_intfdata(),这其实是一个内联函数,就一行代码,也是定义于include/linux/usb.h中:
168 staticinline void usb_set_intfdata (struct usb_interface *intf, void *data)
169 {
170 dev_set_drvdata(&intf->dev,data);
171 }
有趣的是,dev_set_drvdata这个函数也是内联函数,也只有一行代码,它定义于include/linux/device.h中:
491 static inline void
492dev_set_drvdata (struct device *dev, void *data)
493{
494 dev->driver_data = data;
495}
所以,结合来看,最终做的事情就是让&intf->dev->driver_data=data,即&intf->dev->driver_data=us。
再往下走,就是申请内存了,us->cr和us->iobuf都是指针,这里让它们指向两段内存空间,下面会用得到。需要注意的是,usb_buffer_alloc()这个函数是USB Core提供的,我们只管调用即可。从名字上就能知道它是用来申请内存的,第一个参数就是struct usb_device结构体的指针,所以我们要传递一个pusb_dev。第三个参数,GFP_KERNEL是一个内存申请的flag,通常内存申请都用这个flag,除非是中断上下文,不能睡眠,那就得用GPF_ATOMIC,这里没那么多要求。第二个参数申请的buffer的大小,对于cr,传递的是sizeof(*us->cr),而对于iobuf,传递的是US_IOBUF_SIZE,这是一个宏,大小是64,是我们自己定义的,来自drivers/usb/storage/usb.h:
90 #define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */
91 #define US_SENSE_SIZE 18 /* Size of the autosense data buffer */
而usb_buffer_alloc()的第四个参数很有意思,第一次我们传递的是&us->cr_dma,第二次传递的是&us->iobuf_dma,这涉及DMA传输。这两个参数此前我们都没有赋过值,相反它们是在这个函数调用之后被赋上值的。cr_dma和iobuf_dma都是dma_addr_t类型的变量,这个数据类型是Linux内核中专门为DMA传输而准备的。为了支持DMA传输,usb_buffer_alloc不仅仅是申请了地址,并且建立了DMA映射,cr_dma和iobuf_dma就是记录着cr和iobuf的dma地址。关于什么是cr,关于这些DMA地址究竟有什么用,我们稍后就会遇到,那时候再讲也不迟。现在需要知道的就是usb_buffer_alloc申请的空间分别返回给了cr和iobuf。顺便提一下,用usb_buffer_alloc申请的内存空间需要用它的搭档usb_buffer_free()来释放。
461行和468行,每一次申请完内存就要检查成功与否,这是惯例。驱动程序能否驱动设备,关键就是看能否申请到内存空间,任何一处内存空间申请失败,整个驱动程序就没法正常工作。
此外我们还看到473行,我们还为us->sensebuf申请了内存,关于sense buffer,等到讲SCSI命令数据传输时我们再来看,现在还不需要了解
linux一键安装web环境全攻略 在linux系统中怎么一键安装web环境方法
Linux网络基本网络配置方法介绍 如何配置Linux系统的网络方法
Linux下DNS服务器搭建详解 Linux下搭建DNS服务器和配置文件
对Linux进行详细的性能监控的方法 Linux 系统性能监控命令详解
linux系统root密码忘了怎么办 linux忘记root密码后找回密码的方法
Linux基本命令有哪些 Linux系统常用操作命令有哪些
Linux必学的网络操作命令 linux网络操作相关命令汇总
linux系统从入侵到提权的详细过程 linux入侵提权服务器方法技巧
linux系统怎么用命令切换用户登录 Linux切换用户的命令是什么
在linux中添加普通新用户登录 如何在Linux中添加一个新的用户
2012-07-10
CentOS 6.3安装(详细图解教程)
Linux怎么查看网卡驱动?Linux下查看网卡的驱动程序
centos修改主机名命令
Ubuntu或UbuntuKyKin14.04Unity桌面风格与Gnome桌面风格的切换
FEDORA 17中设置TIGERVNC远程访问
StartOS 5.0相关介绍,新型的Linux系统!
解决vSphere Client登录linux版vCenter失败
LINUX最新提权 Exploits Linux Kernel <= 2.6.37
nginx在网站中的7层转发功能