⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 drivers.html

📁 USB驱动使用比较详细的一个例子
💻 HTML
📖 第 1 页 / 共 4 页
字号:
    的设备接口,如果返回NULL,则这个驱动并不适用新的设备。</PRE><PRE>    调用参数结构usb_device代表一个USB硬件,同样定义于usb.h,在此不列出。    这个结构体保存了一些USB规范上的信息,和一些链表用于把usb_device链    成树型,其中非叶子节点是一种特殊的USB设备:HUB。因为USB是可以热拔    插的,所以软件也必须做足够的健壮性处理。    </PRE><PRE>    结构体usb_device_id为热拔插策略的部署提供信息,定义于usb.h。第一个    成员为匹配规则,后面的成员为idVendor/idProduct等。通常一个驱动可以    带一堆同类的设备,所以通常实现出这个结构体的数组,并且数组的最后一    个元素要为零,作为停止匹配的标记。usb.h中同时定义了用于构筑id条目    的宏,使用很方便。</PRE><PRE>    函数指针void (*disconnect)(struct usb_device *, void *)实现和probe    正好相反的功能,在设备被拔走时调用。</PRE><PRE>    MN的usb_driver定义在usb-mininurse.h的L102-109。USB CORE提供了注册    和注销函数:	extern int usb_register(struct usb_driver *);	extern void usb_deregister(struct usb_driver *);    usb-mininurse.c在usb_mn_init和usb_mn_exit中分别调用这两个函数,也    就是说每当模块被加载时就向USB CORE注册MN驱动,模块被移除时向USB    CORE注销MN驱动。</PRE><PRE>    至此已经为全面分析MN驱动做了足够准备。下面来看看MN是如何实现那三个    系统调用以及两个为USB新增的函数。</PRE><PRE>    通常probe总是第一个被调用,所以也先讨论,它位于usb-mininurse.c的    L134-201。它的参数刚才介绍过了。</PRE><PRE>	L142-145	if ((udev-&gt;descriptor.idVendor != USB_MN_VENDOR_ID) ||            (udev-&gt;descriptor.idProduct != USB_MN_PRODUCT_ID)) {          return NULL;	}</PRE><PRE>    这段代码用于检测新设备的各种ID值是否适用于这个驱动。    USB_MNVENDOR_ID和USB_MN_PRODUCT_ID是在usb_mininurse.h定义的宏,其    值分别为0x0471(这是Philips在USB-IF申请的厂商代码)和0x0666,不过似    乎到2003年1月为止,Philips并没有启用0x0666这个产品代码。</PRE><PRE>    L147-156,这段代码为新插入的MN选择一个合适的次设备号。数组    minor_table则保存了所有在线的MN。不过,自从驱动版本到0.1.1后,这段    代码和相关的变量不仅显得多余而且迫害了整个代码的清晰度。把它保留在    这里的唯一理由是只要做少量修改,代码就可以有更好的兼容性。以后会说    到版本0.1.1到底增加了神奇的东西。</PRE><PRE>    L158-163,这段代码做常规的内存分配。其中的kmalloc函数和malloc函数    在使用上基本相同,但是它的第二个参数使得kamlloc有更多行为方式。在    这里使用的是GFP_KERNEL,通常在系统调用级的函数中会用这个标记分配内    存,它要求调用函数必须可重入。此外kmalloc获得内存后不会清空原有内容,    通常为了安全用memeset将其置空。</PRE><PRE>    之后的一段代码,在填充dev结构,但是不要被它的名字误导,它不是前面    说的代表USB设备的usb_device结构,在这里代表它的是udev,况且这个结    构也不是能随便改动的。dev的结构是usb_mn,定义于usb-mininurse.h。填    充这个结构的目的处了返回一个指针供disconnect日后调用,更重要的是为    日后填写file-&gt;private_data做准备,前面说过这个的用处。</PRE><PRE>    L173-178    这段内容后面再说,它的功能是在/dev中创建设备文件。</PRE><PRE>    后面直到本函数末尾,这段代码让MN在加载驱动程序时可以更眩些。不过代    码的意义却是重大的。看看调用参数,应该能猜到点儿什么,后面会重点分    析它。</PRE><PRE>    传统的字符设备驱动程序,通常在open系统调用中初始化设备。在USB设备    中probe函数替代了这个功能,并且MN也并不需要特殊的初始化动作。所以    open的实现相对简单,比较重要的是对file_private_data的填写,并返回0    告知系统open文件成功,可以继续。    </PRE><PRE>    disconnect函数和release调用和probe/open相似,只是做相反的事情,不    论。</PRE><PRE>    就MN的功能而言,mn_ioctl(系统调用ioctl)实现了其全部。做完常规的技    术处理后,再次碰到函数usb_control_msg。在分析它的调用参数前,我们    要再次明确一下MN的固件设计和一点儿USB协议:    MN使用USB厂商请求发送所有的功能控制;MN使用厂商请求的bRequest域传    送控制命令(LEDon/LEDoff),用wIndex域传送命令对象(LED0/LED1/LEDA);    使用端点0传送全部信息。</PRE><PRE>    以点亮LED为例:</PRE><PRE>	usb_control_msg(dev-&gt;udev,usb_sndctrlpipe(dev-&gt;udev,0),		        LEDon,                        USB_TYPE_VENDOR | USB_DIR_OUT |		        USB_RECIP_ENDPOINT,                        0,LEDnum,                        NULL,0,.1*HZ);</PRE><PRE>    函数usb_control_msg的声明如下:	extern 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);</PRE>      <PRE>    该函数发送控制型消息到给定的端点。一共9个参数,逐一介绍:    @dev:	欲发送命令的设备,这里就是由内核传递给ioctl的代表MN的		usb_device结构。    @pipe:	与USB协议中的管道概念对应。USB CORE的开发者对这个概念		进行了调侃,在usb.h里写了一段非常搞笑的注释,此处不便引		出。这里的管道被实现为一个无符号整型,用位图描述出必要		的信息。不过多数情况,没有必要手工创建这个位图,而是使		用创建管道的宏。usb_sndctrlpipe(dev-&gt;udev,0)的意思就是		创建主机到udev设备在端点0上的输出控制型管道。    @request:	与USB协议中的bRequest域对应。在刚才的调用中为宏LEDon,		其值为0。这个数字的成因参考《固件》。    @requesttype:	与USB协议中的bmRequestType域对应。在刚才的调用			中为USB_TYPE_VENDOR | USB_DIR_OUT |			USB_RECIP_ENDPOINT,可以参考USB协议。    @value:	与USB协议的wValue域对应。MN目前没有用到,赋为0。    @index:	与USB协议的wIndex域对应。在ioctl里,将附加参数做类型转		换后,传递到这里。正确的值应是0x04/0x08/0x0C之一。    @data:	USB协议允许传递用户附加的信息,指针data指向待传送的信		息。MN目前没有用到,赋为NULL。    @size:	与USB协议的wLength域对应。没有data,就没有size。赋为0。    @time:	设定多少时间后没有ack就算做超时,0为永远等待。MN为等待		0.1秒种。    如果成功,该函数返回实际传送的字节数,如果失败返回一个负的错误代码。</PRE><PRE>    至此驱动程序代码分析完毕。这是一个能用的驱动程序,并且在我的机器上    还没有测试到有什么致命的问题。不过过于简单的设计,使它会受到一些潜    在因素的威胁。比如这个驱动肯定不能工作在SMP或者NUAM机器上,也不能    工作在打过抢占式内核的Linux2.4或者Linux2.6的抢占方式下。对前一个问    题或许可以使用spin_lock机制,后一个问题则应该可以用down/up机制解决。    在0.2.0中可能会解决这些问题。</PRE><PRE>    除了传统的ioctl(以及read/write)方法外Linux还提供了另一种更加便捷的    机制进行核内/核外的信息交换:procfs。在未来的0.2.0中,将会增加包含    procfs在内的更多内核特性。</PRE><PRE><b><a name="— 第一次跟踪内核 —"><font color="#000080" size="3">— 第一次跟踪内核 —</font></a></b></PRE><PRE>    将usb_control_msg做为侵入内核的镉棒。这部分试图一直跟踪这个函数,    直到我们绝对相信足够的信息和命令已经提交给USB主控的驱动程序为止(暂    时信任主控驱动的真理性吧)。    现在打开usb_control_msg所在的文件:&lt;kernels&gt;/drivers/usb/usb.c</PRE><PRE>    usb_control_msg()看上去是由另一个函数全权实现的:    usb_internal_control_msg()。在调用这个“核心”之前,分配了一个    usb_ctrlrequest结构体,该结构体定义于usb.h,实际上就是USB协议中的    Setup报头,然后把request/requesttype/value/index/size封装进这个结    构体。用6个参数调用“核心”函数。最后释放临时分配的结构体。</PRE><PRE>    然而仅凭这个所谓的核心函数,尚不能使我们相信数据能被传送。我们继续    跟踪,找到usb_internal_control_msg()的实现,它就在镉棒的上面。用写    注释的方法阅读这段函数:	int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,				     struct usb_ctrlrequest *cmd,  void *data,				     int len, int timeout)        {		struct urb *urb;		int retv;		int length;</PRE><PRE>		urb = usb_alloc_urb(0);	/* 准备继续跟踪 */		if (!urb)		   return -ENOMEM;</PRE><PRE>		/* 这个宏定义在usb.h中,从语法上看是将第二个以后的参数填充到第一个参数中去 */		FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, usb_api_blocking_completion, 0);</PRE><PRE>		/* 准备继续跟踪 */		retv = usb_start_wait_urb(urb, timeout, &amp;length);		if (retv &lt; 0)		   return retv;		else		   return length;	}</PRE><PRE>    这里面有一个重要的数据结构和隐藏在其后的概念:Usb Reqest    Block(URB)。我们暂且放下代码,先看看    &lt;kernels&gt;/Documents/usb/URB.txt。这个文件描述了URB的概念和用法。对    usb_alloc_urb()的说明是分配一个URB并返回该指针(返回0为错误),对于    控制型请求参数应该是0。它的实现并不复杂,只是分配一块内存区域。调    用下面的宏会填充这段内存。    对usb_start_wait_urb()的说明只能看实现了。不过看它的命名和出现的位    置应该能猜出大意了:发送刚才填充的URB结构。看来,这可能会是一个真    正的“核心”函数。它的注释对它的描述是:启动一个URB并且等待完成直    到超时。</PRE><PRE>    因为能力的问题,不能逐行分析了,只看大意。首先是一些队列的处理。之    后调用了函数usb_submit_urb()并测试其返回值。通过后,就是对超时的检    查。因此usb_submit_urb()是一个更加核心的函数。我们继续跟踪。</PRE><PRE>        int usb_submit_urb(struct urb *urb)	{		if (urb &amp;&amp; urb-&gt;dev &amp;&amp; urb-&gt;dev-&gt;bus &amp;&amp; urb-&gt;dev-&gt;bus-&gt;op)		   return urb-&gt;dev-&gt;bus-&gt;op-&gt;submit_urb(urb);		else		   return -ENODEV;	}</PRE><PRE>    看它在正确情况下的返回值:urb-&gt;dev-&gt;bus-&gt;op-&gt;submit_urb(urb)。通过    在usb.h里反复跟踪查找,发现op原型是struct usb_operations,里面确实    有个域为submit_urb,为函数指针。但是在usb.c/usb.h中都无法再找到重

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -