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

📄 drivers.html

📁 USB驱动使用比较详细的一个例子
💻 HTML
📖 第 1 页 / 共 4 页
字号:
    载的实体。推断这个函数可能就是“最底层”的调用,不属于USB CORE,果然在    uhci/ehci/ohci以及sl811等主控的驱动程序中找到了submit_urb的具体实    现。</PRE>      <PRE>    不能指望通过一次地毯式搜索就能建立USB CORE的整体框架,因为这是一种自底    向上的学习方法,只有配合其它文本和再一次的搜索,慢慢建立概念。    Linux USB子系统分为三个层次,处于核心地位的是USB CORE,它提供一个USB    “库”,主要由usb.c实现(device.c/devio.c还实现了usbdevfs);之下是各    种USB主控芯片的驱动程序;之上是各类设备的驱动程序。USB主控通常是PCI设    备(sl811等嵌入芯片除外),不在本文讨论范围之内。    下面重点分析USB CORE:usb.c中的一些函数实现。</PRE>      <PRE>    首先看MN的驱动中使用到的几个。usb_control_msg这个函数前面已经做了详细    分析,再看usb_register,其实现为	</PRE>      <font SIZE="2">      <blockquote>        <p>int usb_register(struct usb_driver *new_driver)<br>        {<br>        &nbsp;&nbsp;&nbsp; if (new_driver-&gt;fops != NULL) {<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if         (usb_minors[new_driver-&gt;minor/16]) {<br>        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         err(&quot;error registering %s driver&quot;, new_driver-&gt;name);<br>        &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">return         -EINVAL;<br>        &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">}<br>        &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">usb_minors[new_driver-&gt;minor/16]         = new_driver;<br>        }</p>        <p>&nbsp;&nbsp;&nbsp; info(&quot;registered new driver %s&quot;,         new_driver-&gt;name);<br>        &nbsp;&nbsp;&nbsp; init_MUTEX(&amp;new_driver-&gt;serialize);</p>        <p>&nbsp;&nbsp;&nbsp; /* Add it to the list of known drivers */<br>        &nbsp;&nbsp;&nbsp; list_add_tail(&amp;new_driver-&gt;driver_list,         &amp;usb_driver_list);</p>        <p>&nbsp;&nbsp;&nbsp; usb_scan_devices();</p>        <p>&nbsp;&nbsp;&nbsp; return 0;<br>        }</p>      </font>      <p>首先检查是检查设备是否过多,如果成功做一些入链等操作后,就<br>      调用了另外一个函数usb_scan_devices。让我们跟踪这个函数:</p>      <font SIZE="2">      <p>void usb_scan_devices(void)<br>      {<br>      &nbsp;&nbsp; </font> <font SIZE="2">struct list_head *tmp;<br>      <br>      &nbsp;&nbsp;&nbsp; down (&amp;usb_bus_list_lock);<br>      &nbsp;&nbsp;&nbsp; tmp = usb_bus_list.next;<br>      &nbsp;&nbsp; </font> <font SIZE="2">while (tmp != &amp;usb_bus_list) {<br>      &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">struct usb_bus       *bus = list_entry(tmp,struct usb_bus, bus_list);<br>      <br>      &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">tmp =       tmp-&gt;next;<br>      &nbsp;&nbsp; </font>&nbsp;&nbsp;&nbsp;&nbsp; <font SIZE="2">usb_check_support(bus-&gt;root_hub);<br>      &nbsp;&nbsp; </font> <font SIZE="2">}<br>      &nbsp;&nbsp; </font> <font SIZE="2">up (&amp;usb_bus_list_lock);<br>      }</p>      </font>      <p>这个函数遍历所有未被驱动起来的USB接口,usb_check_support是深<br>      度优先遍历算法,对每个未驱动的接口调用usb_find_interface_driver。<br>      判断一个新接口的方法是,用USB驱动队列中的每一个成员比对这个接口。<br>      对于老式的驱动,这个函数直接调用驱动的probe函数,让这个函数自行<br>      比对;而新的驱动增加了id的概念,该函数会首先比对这个id值,然后调<br>      用probe函数。<br>      如果比对成功则调用usb_driver_claim_interface,向该接口填写数据并<br>      返回0,说明成功匹配,如果没有则使用驱动队列的下一个成员,至到所有<br>      成员比对过所有接口。</p>      <p>usb_deregister做了和usb_register虽然调用函数有所不同,但几乎做了<br>      完全相反的事情,在此不论。</p>      <p>从这个角度分析,看上去没有任何问题。然而,对于一个刚刚启动的计算<br>      机,它怎么会顺着刚才的思路,先调用MN驱动中的usb_register,从而引<br>      发一系列的内核动作呢,换句话说,Linux是如何知道MN的驱动就是MN的<br>      驱动呢?</p>      <p>如果我们使用一些“标准”的USB设备,像USB鼠标或者USB接口的闪存,只<br>      要设备上电,相应的驱动模块就会自动加载。MN当然也可以实现这种全状态<br>      的即插即用,前提是编译内核是要选中&quot;HOT-PLUG&quot;一项。</p>      <p>当一个USB设备插入到总线上时,HUB的电路会监测到D+/-线上的变化,它会<br>      记录下新设备所在的位置。HUB具有一个中断型端点,当主控的下一个查询周<br>      期到来时,HUB会告之有新设备。主控随后向操作系统转发这一情况。接下来<br>      发生的事件就被称为枚举。<br>      Linux首先会从设备号位图中选择一个合适的地址,准备发送给设备。有两种<br>      算法,一种每次都从1开始寻找可用的设备号,另一种是从上次分配的设备号<br>      之后开始寻找。<br>      然后调用usb_new_device。根据USB规范,这个函数尝试使用最小包大小发送<br>      数据,首先是把计算好的设备号送出去,如果失败,要回收设备号。然后读取<br>      设备描述符的前8个字节,以获得设备最大的包传送能力。用这个值获取设备<br>      其它描述符,并填充相应的数据结构。<br>      现在枚举基本上完成了。之后向USB设备文件系统中添加关于这个设备的节点。<br>      调用usb_find_drivers,这个函数为这个新的设备在驱动列表中比对是否有合<br>      适的成员。</p>      <p>最后,有一个重要的调用<br>      &nbsp;&nbsp;&nbsp; call_policy(&quot;add&quot;,dev);<br>      它的注释上说,用户空间可以借此完成加载模块或者做进一步的配置。是的,<br>      Linux把即插即用的实现放在内核空间。完成这个任务的程序通常是<br>      /sbin/hotplug,或者看看/proc/sys/kernel/hotplug,如果修改可以定位自己<br>      实现的加载策略,有的Linux发行版本在拔插设备时会让系统扬声器叫唤一下。</p>      <p>以前,要想使用MN,除了把她插在USB总线上外,还要用insmod手工加载她的驱<br>      动模块。但是只要对hotplug的配置文件做一些简单的修改,MN也能实现全状态<br>      即插即用。</p>      <p>hotplug的配置文件通常在/etc/hotplug/。这个目录下包含了scsi/usb/1394等<br>      一些处理各种即插即用的脚本和产品信息。我们只关心USB。找到所有看上去和<br>      USB有关的文件,阅读它们。最后可以发现usb.distmap和usb.usermap中包含了<br>      大量产品信息,在这些文件尾部按格式添加MN的识别码。最后把MN的驱动模块复<br>      制到/lib/modules/2.4.25/kernel/drivers/usb/里,这样就可以了。<br>      </p>      </blockquote><PRE><b><a name="— 更多内核设施和机制 —"><font size="3" color="#000080">— 更多内核设施和机制 —</font></a></b></PRE><PRE>    这部分讨论前面提到的“后面讨论”,却还没有讨论的几个问题:	* 设备文件系统(devfs)	* ioctl的一个技术细节	* 机制的实现</PRE><PRE>    老式的*nix将所有的设备文件放在/dev目录里,采用平面方式组织,就是说    没有子目录。如果细心还会发现,这个目录里经常会数个软盘驱动器,而实    际上却只有一台,这些尚未使用的文件占用磁盘空间,降低检索速度。</PRE><PRE>    老式的*nix使用静态的设备号,即所有设备按照事项约定的主设备号工作。    正如互联网初期使用hosts文件分配机器IP与主机名那样,这种方法很快被    越来越多的设备搞的混乱不堪。</PRE><PRE>    现在Linux实现了一种新的设备文件管理方法:devfs。这个方法把设备文件    的管理权转移到内核,内核在内存中建立与机器情况相同的设备文件列表并    像procfs那样映射到结构化文件系统上。其命名空间也要比以前的方案大得    多。</PRE><PRE>    MN在0.1.0中就使用了devfs,在0.1.1中使用了devfs的一个更好的特性:动    态分配主、次设备号。这也就是为什么前面说probe的部分代码在0.1.1后就    变得冗余。此外devfs_fs_kernel.h中也准备了大量可用的工具,比如MN就    使用了其中读取主次设备号的函数。</PRE><PRE>    在使用ioctl时,为了防止正确的命令下在错误的设备上(如对让软驱执行    eject),ioctl的命令号被建议服从全局的分配。在&lt;kernels&gt;/Documents里    有一份ioctl-number.txt,记录了已经分配的ioctl号。通常使用_    IO/_IOR/_IOW/_IOWR宏构筑命令号,因为较新的Linux使用了命令号的分段。    通常_IOR为从设备读的命令,_IOW相反。但是似乎这是无关紧要的,不过为    了符合更好的命名规则,还是按照建议中的办吧。</PRE><PRE>    关于机制实现的问题,可以参考&lt;The Art of Unix Programming&gt;。</PRE><PRE><b><a name="— 一些讨论 —"><font size="3" color="#000080">— 一些讨论 —</font></a></b></PRE><PRE>    关于内核的调试。说起Linux内核调试,那方法可真多了去了。尽管教科书    上说宏核是不容易在线调试的。可是就算是一个新手,也能通过很舒服的操    作获得丰富的调试信息。如果是黑客,使用更高级的工具,调试真的跟玩一    样。</PRE><PRE>    在MN的驱动和应用程序完成最初的编写之后,难免会有一些疏忽造成代码无    法工作。我使用了两种最简单的调试方法,然而却迅速地找到了错误,快得    几乎难以置信。这两钟方法,一种就是之前说的strace程序。它让我迅速定    位了ioctl参数的问题。另外一个就是靠输出内核的调试信息以及在驱动中    添加一些语句。    这些信息都使用printk()这个内核函数,不过通常用info()/err()等宏更加    方便。printk()输出的信息是有级别的,如果低于当前控制台的默认级别信    息就只能输出到系统日志了。使用这个命令可以提升控制台级别:	echo 8 &gt; /proc/sys/kernel/printk    这样一来,所有的信息都会直接显示。用这个方法,我几乎查出了其它所有    的错误。</PRE><PRE>    顺便说一下,用这个方法还查出了MN固件上的一个小bug:所有厂商请求后    没有向主控发送ack,造成每次usb_control_msg()后都产生超时错误,这    是一个小小的遗憾,但并不影响使用。</PRE>      <PRE>    关于学习方式。在*nix里学习编程或别的什么,感觉很舒服,并且能够学习    到高超的编程技巧和极好的思想。你不用为了一个特简单的要求,而花费大    量的时间精力记住那些为了众所周知的目的而经过层层封装的API;你可以    和整个iNET分享你的成果和快乐。    就说编写MN驱动,真的是看着源代码学习内核,编写驱动。一种黑客的感觉。</PRE><PRE>						.end</PRE><PRE>    </PRE>    </blockquote>  </blockquote></blockquote>

⌨️ 快捷键说明

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