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

📄 ch13s04.html

📁 Linux设备驱动经典
💻 HTML
📖 第 1 页 / 共 2 页
字号:
for (i = 0; i &lt; iface_desc-&gt;desc.bNumEndpoints; ++i) { endpoint = &amp;iface_desc-&gt;endpoint[i].desc;</pre><p>那么, 在我们有了一个端点后, 我们还没有发现一个块 IN 类型端点, 我们看是否这个端点的方向是 IN. 那个可被测试通过看是否位掩码 USB_DIR_IN 被包含在 bEndpointAddress 端点变量中. 如果这是真, 我们决定是否端点类型是块, 通过使用 USB_ENDPOINT_XFERTYPE_MASK 位掩码首先掩去 bmAttributes 变量, 并且接着检查是否它匹配值 USB_ENDPOINT_XFER_BULK:</p><pre class="programlisting">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)){</pre><p>如果所有的这些检查都是真, 驱动知道它发现了正确的端点类型, 并且可保存关于端点的信息到本地结构中, 它后来将需要这些信息和它通讯.</p><pre class="programlisting">/* 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;}</pre><p>因为 USB 驱动需要获取在设备的生命周期后期和这个 struct usb_interface 关联的本地数据结构, 函数 usb_set_intfdata 可被调用:</p><pre class="programlisting">/* save our data pointer in this interface device */usb_set_intfdata(interface, dev);</pre><p>这个函数接受一个指向任何数据类型的指针, 并且保存它到 struct usb_interface 结构为后面的存取. 为获取这个数据, 函数 usb_get_intfdata 应当被调用:</p><pre class="programlisting">struct usb_skel *dev;struct usb_interface *interface;int subminor;int retval = 0;subminor = iminor(inode);interface = usb_find_interface(&amp;skel_driver, subminor);if (!interface){        err ("%s - error, can't find device for minor %d",             __FUNCTION__, subminor);        retval = -ENODEV;        goto exit;}dev = usb_get_intfdata(interface);if (!dev){        retval = -ENODEV;        goto exit;}</pre><p>usb_get_intfdata 常常被调用, 在 USB 驱动的 open 函数和在去连接函数. 感谢这 2 个函数, USB 驱动不需要保持一个静态指针数组来保存单个设备结构为系统中所有当前的设备. 对设备信息的非直接引用允许一个无限数目的设备被任何 USB 驱动支持.</p><p>如果 USB 驱动没有和另一种处理用户和设备交互的子系统(例如 input, tty, video, 等待)关联, 驱动可使用 USB 主编号为了使用传统的和用户空间之间的字符驱动接口. 为此, USB 驱动必须在探测函数中调用 usb_register_dev 函数, 当它想注册一个设备到 USB 核心. 确认设备和驱动处于正确的状态, 来处理一个想在调用这个函数时尽快存取这个设备的用户.</p><pre class="programlisting">/* we can register the device now, as it is ready */retval = usb_register_dev(interface, &amp;skel_class);if (retval){        /* something prevented us from registering this driver */        err("Not able to get a minor for this device.");        usb_set_intfdata(interface, NULL);        goto error;}</pre><p>usb_register_dev 函数要求一个指向 struct usb_interface 的指针和指向 struct usb_class_driver 的指针. struct -usb_class_driver 用来定义许多不同的参数, 当注册一个次编号USB 驱动要 USB 核心知道这些参数. 这个结构包括下列变量:.</p><div class="variablelist"><dl><dt><span class="term"><span>char *name </span></span></dt><dd><p>sysfs 用来描述设备的名子. 一个前导路径名, 如果存在, 只用在 devfs 并且本书不涉及. 如果设备号需要在这个名子中, 字符 %d 应当在名子串中. 例如, 位创建 devfs 名子 usb/foo1 和 sysfs 类名 foo1, 名子串应当设置为 usb/foo%d.</p></dd><dt><span class="term"><span>struct file_operations *fops; </span></span></dt><dd><p>指向 struct file_operations 的结构的指针, 这个驱动已定义来注册为字符设备. 这个结构的更多信息见第 3 章.</p></dd><dt><span class="term"><span>mode_t mode; </span></span></dt><dd><p>给这个驱动的要被创建的 devfs 文件的模式; 否则不使用. 这个变量的典型设置是值 S_IRUSR 和 值 S_IWUSR 的结合, 它将只提供这个设备文件的拥有者读和写存取.</p></dd><dt><span class="term"><span>int minor_base; </span></span></dt><dd><p>这是给这个驱动安排的次编号的开始. 所有和这个驱动相关的设备被创建为从这个值开始的唯一的, 递增的次编号. 只有 16 个设备被允许在任何时刻和这个驱动关联, 除非 CONFIG_USB_DYNAMIC_MINORS 配置选项被打开. 如果这样, 忽略这个变量, 并且这个设备的所有的次编号被以先来先服务的方式分配. 建议打开了这个选项的系统使用一个程序例如 udev 来关联系统中的设备节点, 因为一个静态的 /dev 树不会正确工作.</p></dd></dl></div><p>当 USB 设备断开, 所有的关联到这个设备的资源应当被清除, 如果可能. 在此时, 如果 usb_register_dev 已被在探测函数中调用来分配一个 USB 设备的次编号, 函数 usb_deregister_dev 必须被调用来将次编号给回 USB 核心.</p><p>在断开函数中, 也重要的是从接口获取之前调用 usb_set_intfdata 所设置的数据. 接着设置数据指针在 struct us_interface 结构为 NULL 来阻止在不正确存取数据中的任何进一步的错误.</p><pre class="programlisting">static void skel_disconnect(struct usb_interface *interface){        struct usb_skel *dev;        int minor = interface-&gt;minor;        /* prevent skel_open() from racing skel_disconnect( ) */        lock_kernel();        dev = usb_get_intfdata(interface);        usb_set_intfdata(interface, NULL);        /* give back our minor */        usb_deregister_dev(interface, &amp;skel_class);        unlock_kernel(); /* decrement our usage count */        kref_put(&amp;dev-&gt;kref, skel_delete);        info("USB Skeleton #%d now disconnected", minor);}</pre><p>注意在之前代码片段中的调用 lock_kernel. 它获取了 bigkernel 锁, 以至于 disconnect 回调不会遇到一个竞争情况, 在使用 open 调用试图获取一个指向正确接口数据结构的指针. 因为 open 在 bigkernel 锁获取情况下被调用, 如果 disconnect 也获取同一个锁, 只有驱动的一部分可存取并且接着设置接口数据指针.</p><p>就在 disconnect 函数为一个 USB 设备被调用, 所有的当前在被传送的 urb 可被 USB 核心取消, 因此驱动不必明确为这些 urb 调用 usb_kill_urb. 如果一个驱动试图提交一个 urb 给 USB 设备, 在调用 usb_submit_urb 被断开之后, 这个任务会失败, 错误值为-EPIPE.</p></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="SubmittingandControllingaUrb.sect2"></a>13.4.3.&#160;提交和控制一个 urb</h3></div></div></div><p>当驱动有数据发送到 USB 设备(如同在驱动的 write 函数中发生的), 一个 urb 必须被分配来传送数据到设备.</p><pre class="programlisting">urb = usb_alloc_urb(0, GFP_KERNEL);if (!urb){        retval = -ENOMEM;        goto error;}</pre><p>在 urb 被成功分配后, 一个 DMA 缓冲也应当被创建来发送数据到设备以最有效的方式, 并且被传递到驱动的数据应当被拷贝到缓冲:</p><pre class="programlisting">buf = usb_buffer_alloc(dev-&gt;udev, count, GFP_KERNEL, &amp;urb-&gt;transfer_dma);if (!buf){        retval = -ENOMEM;        goto error;}if (copy_from_user(buf, user_buffer, count)){        retval = -EFAULT;        goto error;}</pre><p>应当数据被正确地从用户空间拷贝到本地缓冲, urb 在它可被提交给 USB 核心之前必须被正确初始化:</p><pre class="programlisting">/* initialize the urb properly */usb_fill_bulk_urb(urb, dev-&gt;udev,                  usb_sndbulkpipe(dev-&gt;udev, dev-&gt;bulk_out_endpointAddr),                  buf, count, skel_write_bulk_callback, dev);urb-&gt;transfer_flags |= URB_NO_TRANSFER_DMA_MAP;</pre><p>现在 urb 被正确分配, 数据被正确拷贝, 并且 urb 被正确初始化, 它可被提交给 USB 核心来传递给设备.</p><pre class="programlisting">/* send the data out the bulk port */retval = usb_submit_urb(urb, GFP_KERNEL);if (retval){        err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);        goto error;}</pre><p>在urb被成功传递到 USB 设备(或者在传输中发生了什么), urb 回调被 USB 核心调用. 在我们的例子中, 我们初始化 urb 来指向函数 skel_write_bulk_callback, 并且那就是被调用的函数:</p><pre class="programlisting">static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs){        /* sync/async unlink faults aren't errors */        if (urb-&gt;status &amp;&amp;                        !(urb-&gt;status == -ENOENT ||                          urb-&gt;status == -ECONNRESET ||                          urb-&gt;status == -ESHUTDOWN)){                dbg("%s - nonzero write bulk status received: %d",                    __FUNCTION__, urb-&gt;status);        }        /* free up our allocated buffer */        usb_buffer_free(urb-&gt;dev, urb-&gt;transfer_buffer_length,                        urb-&gt;transfer_buffer, urb-&gt;transfer_dma);}</pre><p>回调函数做的第一件事是检查 urb 的状态来决定是否这个 urb 成功完成或没有. 错误值, -ENOENT, -ECONNRESET, 和 -ESHUTDOWN 不是真正的传送错误, 只是报告伴随成功传送的情况. (见 urb 的可能错误的列表, 在"结构 struct urb"一节中详细列出). 接着这个回调释放安排给这个 urb 传送的已分配的缓冲.</p><p>在 urb 的回调函数在运行时另一个 urb 被提交给设备是普遍的. 当流数据到设备时是有用的. 记住 urb 回调是在中断上下文运行, 因此它不应当做任何内存分配, 持有任何旗标, 或者任何可导致进程睡眠的事情. 当从回调中提交 urb, 使用 GFP_ATOMIC 标志来告知 USB 核心不要睡眠, 如果它需要分配新内存块在提交过程中.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch13s03.html">上一页</a>&#160;</td><td width="20%" align="center"><a accesskey="u" href="ch13.html">上一级</a></td><td width="40%" align="right">&#160;<a accesskey="n" href="ch13s05.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">13.3.&#160;USB 的 Urbs&#160;</td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top">&#160;13.5.&#160;无 urb 的 USB 传送</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>

⌨️ 快捷键说明

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