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

📄 ch13s04.html

📁 Linux设备驱动经典
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<html xmlns:cf="http://docbook.sourceforge.net/xmlns/chunkfast/1.0"><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>13.4.&#160;编写一个 USB 驱动-Linux设备驱动第三版(中文版)</title><meta name="description" content="驱动开发" /><meta name="keywords" content="Linux设备驱动,中文版,第三版,ldd,linux device driver,驱动开发,电子版,程序设计,软件开发,开发频道" /><meta name="verify-v1" content="5asbXwkS/Vv5OdJbK3Ix0X8osxBUX9hutPyUxoubhes=" /><link rel="stylesheet" href="docbook.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.0"><link rel="start" href="index.html" title="Linux 设备驱动 Edition 3"><link rel="up" href="ch13.html" title="第&#160;13&#160;章&#160;USB 驱动"><link rel="prev" href="ch13s03.html" title="13.3.&#160;USB 的 Urbs"><link rel="next" href="ch13s05.html" title="13.5.&#160;无 urb 的 USB 传送"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">13.4.&#160;编写一个 USB 驱动</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch13s03.html">上一页</a>&#160;</td><th width="60%" align="center">第&#160;13&#160;章&#160;USB 驱动</th><td width="20%" align="right">&#160;<a accesskey="n" href="ch13s05.html">下一页</a></td></tr></table><hr></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="WritingaUSBDriver.sect1"></a>13.4.&#160;编写一个 USB 驱动</h2></div></div></div><p>编写一个 USB 设备驱动的方法类似于一个 pci 驱动: 驱动注册它的驱动对象到 USB 子系统并且之后使用供应商和设备标识来告知是否它的硬件已经安装.</p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="WhatDevicesDoestheDriverSupport.sect2"></a>13.4.1.&#160;驱动支持什么设备</h3></div></div></div><p>struct usb_device_id 结构提供了这个驱动支持的一个不同类型 USB 设备的列表. 这个列表被USB 核心用来决定给设备哪个驱动, 并且通过热插拔脚本来决定哪个驱动自动加载, 当特定设备被插入系统时.</p><p>struct usb_device_id 结构定义有下面的成员:</p><div class="variablelist"><dl><dt><span class="term"><span>__u16 match_flags </span></span></dt><dd><p>决定设备应当匹配结构中下列的哪个成员. 这是一个位成员, 由在 include/linux/mod_devicetable.h 文件中指定的不同的 USB_DEVICE_ID_MATCH_* 值所定义. 这个成员常常从不直接设置, 但是由 USB_DEVICE 类型宏来初始化.</p></dd><dt><span class="term"><span>__u16 idVendor </span></span></dt><dd><p>这个设备的 USB 供应商 ID. 这个数由 USB 论坛分配给它的成员并且不能由任何别的构成.</p></dd><dt><span class="term"><span>__u16 idProduct </span></span></dt><dd><p>这个设备的 USB 产品 ID. 所有的有分配给他们的供应商 ID 的供应商可以随意管理它们的产品 ID.</p></dd><dt><span class="term"><span>__u16 bcdDevice_lo</span></span></dt><dd></dd><dt><span class="term"><span>__u16 bcdDevice_hi </span></span></dt><dd><p>定义供应商分配的产品版本号的高低范围. bcdDevice_hi 值包括其中; 它的值是最高编号的设备号. 这 2 个值以BCD 方式编码. 这些变量, 连同 idVendor 和 idProduct, 用来定义一个特定的设备版本.</p></dd><dt><span class="term"><span>__u8 bDeviceClass</span></span></dt><dd></dd><dt><span class="term"><span>__u8 bDeviceSubClass</span></span></dt><dd></dd><dt><span class="term"><span>__u8 bDeviceProtocol </span></span></dt><dd><p>定义类, 子类, 和设备协议, 分别地. 这些值被 USB 论坛分配并且定义在 USB 规范中. 这些值指定这个设备的行为, 包括设备上所有的接口.</p></dd><dt><span class="term"><span>__u8 bInterfaceClass</span></span></dt><dd></dd><dt><span class="term"><span>__u8 bInterfaceSubClass</span></span></dt><dd></dd><dt><span class="term"><span>__u8 bInterfaceProtocol </span></span></dt><dd><p>非常象上面的设备特定值, 这些定义了类, 子类, 和单个接口协议, 分别地. 这些值由 USB 论坛指定并且定义在 USB 规范中.</p></dd><dt><span class="term"><span>kernel_ulong_t driver_info </span></span></dt><dd><p>这个值不用来匹配, 但是它持有信息, 驱动可用来在 USB 驱动的探测回调函数区分不同的设备.</p></dd></dl></div><p>至于 PCI 设备, 有几个宏可用来初始化这个结构:</p><div class="variablelist"><dl><dt><span class="term"><span>USB_DEVICE(vendor, product)</span></span></dt><dd><p>创建一个 struct usb_device_id, 可用来只匹配特定供应商和产品 ID 值. 这是非常普遍用的, 对于需要特定驱动的 USB 设备.</p></dd><dt><span class="term"><span>USB_DEVICE_VER(vendor, product, lo, hi)</span></span></dt><dd><p>创建一个 struct usb_device_id, 用来在一个版本范围中只匹配特定供应商和产品 ID 值.</p></dd><dt><span class="term"><span>USB_DEVICE_INFO(class, subclass, protocol)</span></span></dt><dd><p>创建一个 struct usb_device_id, 可用来只匹配一个特定类的 USB 设备.</p></dd><dt><span class="term"><span>USB_INTERFACE_INFO(class, subclass, protocol)</span></span></dt><dd><p>创建一个 struct usb_device_id, 可用来只匹配一个特定类的 USB 接口.</p></dd></dl></div><p>对于一个简单的 USB 设备驱动, 只控制来自一个供应商的一个单一 USB 设备, struct usb_device_id 表可定义如:</p><pre class="programlisting">/* table of devices that work with this driver */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, skel_table);</pre><p>至于 PCI 驱动, MODULE_DEVICE_TABLE 宏有必要允许用户空间工具来发现这个驱动可控制什么设备. 但是对于 USB 驱动, 字符串 usb 必须是在这个宏中的第一个值.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RegisteringaUSBDriver.sect2"></a>13.4.2.&#160;注册一个 USB 驱动</h3></div></div></div><p>所有 USB 驱动必须创建的主要结构是 struct usb_driver. 这个结构必须被 USB 驱动填充并且包含多个函数回调和变量, 来向 USB 核心代码描述 USB 驱动:</p><div class="variablelist"><dl><dt><span class="term"><span>struct module *owner </span></span></dt><dd><p>指向这个驱动的模块拥有者的指针. USB 核心使用它正确地引用计数这个 USB 驱动, 以便它不被在不合适的时刻卸载. 这个变量应当设置到 THIS_MODULE 宏.</p></dd><dt><span class="term"><span>const char *name </span></span></dt><dd><p>指向驱动名子的指针. 它必须在内核 USB 驱动中是唯一的并且通常被设置为和驱动的模块名相同. 它出现在 sysfs 中在 /sys/bus/usb/drivers/ 之下, 当驱动在内核中时.</p></dd><dt><span class="term"><span>const struct usb_device_id *id_table </span></span></dt><dd><p>指向 struct usb_device_id 表的指针, 包含这个驱动可接受的所有不同类型 USB 设备的列表. 如果这个变量没被设置, USB 驱动中的探测回调函数不会被调用. 如果你需要你的驱动给系统中每个 USB 设备一直被调用, 创建一个只设置这个 driver_info 成员的入口项:</p><pre class="programlisting">static struct usb_device_id usb_ids[] = { {.driver_info = 42},    {} };</pre></dd><dt><span class="term"><span>int (*probe) (struct usb_interface *intf, const struct usb_device_id *id)</span></span></dt><dd><p>指向 USB 驱动中探测函数的指针. 这个函数(在"探测和去连接的细节"一节中描述)被 USB 核心调用当它认为它有一个这个驱动可处理的 struct usb_interface. 一个指向 USB 核心用来做决定的 struct usb_device_id 的指针也被传递到这个函数. 如果这个 USB 驱动主张传递给它的 struct usb_interface, 它应当正确地初始化设备并且返回 0. 如果驱动不想主张这个设备, 或者发生一个错误, 它应当返回一个负错误值.</p></dd><dt><span class="term"><span>void (*disconnect) (struct usb_interface *intf)</span></span></dt><dd><p>指向 USB 驱动的去连接函数的指针. 这个函数(在"探测和去连接的细节"一节中描述)被 USB 核心调用, 当 struct usb_interface 已被从系统中清除或者当驱动被从 USB 核心卸载.</p></dd></dl></div><p>为创建一个值 struct usb_driver 结构, 只有 5 个成员需要被初始化:</p><pre class="programlisting">static struct usb_driver skel_driver = { .owner = THIS_MODULE, .name = "skeleton", .id_table = skel_table, .probe = skel_probe, .disconnect = skel_disconnect, }; </pre><p>struct usb_driver 确实包含更多几个回调, 它们通常不经常用到, 并且不被要求使 USB 驱动正确工作:</p><div class="variablelist"><dl><dt><span class="term"><span>int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf) </span></span></dt><dd><p>指向 USB 驱动的 ioctl 函数的指针. 如果它出现, 在用户空间程序对一个关联到 USB 设备的 usbfs 文件系统设备入口, 做一个 ioctl 调用时被调用. 实际上, 只有 USB 集线器驱动使用这个 ioctl, 因为没有其他的真实需要对于任何其他 USB 驱动要使用.</p></dd><dt><span class="term"><span>int (*suspend) (struct usb_interface *intf, u32 state)</span></span></dt><dd><p>指向 USB 驱动中的悬挂函数的指针. 当设备要被 USB 核心悬挂时被调用.</p></dd><dt><span class="term"><span>int (*resume) (struct usb_interface *intf)</span></span></dt><dd><p>指向 USB 驱动中的恢复函数的指针. 当设备正被 USB 核心恢复时被调用.</p></dd></dl></div><p>为注册 struct usb_driver 到 USB 核心, 一个调用 usb_register_driver 带一个指向 struct usb_driver 的指针. 传统上在 USB 驱动的模块初始化代码做这个:</p><pre class="programlisting">static int __init usb_skel_init(void){        int result;        /* register this driver with the USB subsystem */        result = usb_register(&amp;skel_driver);        if (result)                err("usb_register failed. Error number %d", result);        return result;}</pre><p>当 USB 驱动被卸载, struct usb_driver 需要从内核注销. 使用对 usb_deregister_driver 的调用做这个. 当这个调用发生, 任何当前绑定到这个驱动的 USB 接口被去连接, 并且去连接函数为它们而被调用.</p><pre class="programlisting">static void __exit usb_skel_exit(void){        /* deregister this driver with the USB subsystem */        usb_deregister(&amp;skel_driver);}</pre><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="probeanddisconnectinDetail.sect3"></a>13.4.2.1.&#160;探测和去连接的细节</h4></div></div></div><p>在之前章节描述的 struct usb_driver 结构中, 驱动指定 2 个 USB 核心在合适的时候调用的函数. 探测函数被调用, 当设备被安装时, USB 核心认为这个驱动应当处理; 探测函数应当进行检查传递给它的关于设备的信息, 并且决定是否驱动真正合适那个设备. 去连接函数被调用当驱动应当不再控制设备, 由于某些理由, 并且可做清理.</p><p>探测和去连接函数回调都在 USB 集线器内核线程上下文中被调用, 因此它们中睡眠是合法的. 但是, 建议如果有可能大部分工作应当在设备被用户打开时完成. 为了保持 USB 探测时间为最小. 这是因为 USB 核心处理 USB 设备的添加和去除在一个线程中, 因此任何慢设备驱动可导致 USB 设备探测时间慢下来并且用户可注意到.</p><p>在探测函数回调中, USB 驱动应当初始化任何它可能使用来管理 USB 设备的本地结构. 它还应当保存任何它需要的关于设备的信息到本地结构, 因为在此时做这些通常更容易. 作为一个例子, USB 驱动常常想为设备探测端点地址和缓冲大小是什么, 因为和设备通讯需要它们. 这里是一些例子代码, 它探测 BULK 类型的 IN 和 OUT 端点, 并且保存一些关于它们的信息在一个本地设备结构中:</p><pre class="programlisting">/* set up the endpoint information *//* use only the first bulk-in and bulk-out endpoints */iface_desc = interface-&gt;cur_altsetting;for (i = 0; i &lt; iface_desc-&gt;desc.bNumEndpoints; ++i){        endpoint = &amp;iface_desc-&gt;endpoint[i].desc;        if (!dev-&gt;bulk_in_endpointAddr &amp;&amp;                        (endpoint-&gt;bEndpointAddress &amp; USB_DIR_IN) &amp;&amp;                        ((endpoint-&gt;bmAttributes &amp; USB_ENDPOINT_XFERTYPE_MASK)                         == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk in endpoint */ buffer_size = endpoint-&gt;wMaxPacketSize;                dev-&gt;bulk_in_size = buffer_size;                dev-&gt;bulk_in_endpointAddr = endpoint-&gt;bEndpointAddress;                dev-&gt;bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);                if (!dev-&gt;bulk_in_buffer) {                        err("Could not allocate bulk_in_buffer");                        goto error;                }        }        if (!dev-&gt;bulk_out_endpointAddr &amp;&amp;                        !(endpoint-&gt;bEndpointAddress &amp; USB_DIR_IN) &amp;&amp;                        ((endpoint-&gt;bmAttributes &amp; USB_ENDPOINT_XFERTYPE_MASK)                         == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */ dev-&gt;bulk_out_endpointAddr = endpoint-&gt;bEndpointAddress;        }}if (!(dev-&gt;bulk_in_endpointAddr &amp;&amp; dev-&gt;bulk_out_endpointAddr)){        err("Could not find both bulk-in and bulk-out endpoints");        goto error;}</pre><p>这块代码首先循环在这个接口中出现的每个端点, 并且分配一个本地指针到端点结构来使它之后容易存取:</p><pre class="programlisting">

⌨️ 快捷键说明

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