学习目标:分析USB驱动源码结构。


一、Windows下USB驱动理论问题

1. 当usb设备接入PC时,右下角弹出"发现AAA",并弹出对话框,提示安装驱动程序。没有驱动程序,Windows是怎样知道是AAA设备?

--> Windows有USB的总线驱动程序,接入USB设备后,"总线驱动程序"就会知道该设备是"AAA",提示安装的是”AAA的设备驱动程序"。这里USB总线驱动程序负责:识别USB设备, 给USB设备找到对应的驱动程序。

USB总线驱动程序的作用:(1) 识别USB设备 (2)查找并安装对应的设备驱动程序 (3)提供USB读写函数。

2.  Windows是怎样识别出该USB设备的种类/名称?

--> PC和USB设备都得遵守一些规范。USB总线驱动程序会发出某些命令想获取设备信息(描述符),USB设备必须返回"描述符"给PC。

3. PC机是怎样分辨USB设备?

--> 每一个USB设备接入PC时,USB总线驱动程序都会给它分配一个编号(地址),PC机想访问某个USB设备时,发出的命令都含有对应的编号(地址);新接入的USB设备的默认编号是0,在未分配新编号前,PC使用0编号和它通信。

4. 为什么一接入USB设备,PC机就能发现它?

--> 由于硬件接口连接问题提示的。USB接口只有4条线: 5V,GND,D-,D+。USB设备接入PC,会把PC USB口的D-或D+拉高,从而通知有新设备接入。

注意:

 (1)USB是主从结构的,所有的传输都是由USB主机方发起的。例如,USB键盘按下立刻产生数据,但是它没有能力通知PC机来读数据,只能等待PC机来读。

 (2)USB在数据传输中,以端点为对象的,其有传输类型,传输方向。每个端点只有一个方向的功能(除了端点0控制传输,既能输出也能输入外),例如把数据从端点1读出,向端点2写入数据。

 (3)我们所说的输入(IN)、输出(OUT) 是基于USB主机的立场提出的。例如:鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"。

二、驱动程序
1. USB驱动程序框架
 
1) 将USB设备插入开发板,在串口终端会打印:
  usb 1-1: new full speed USB device using s3c2410-ohci and address 2
  usb 1-1: configuration #1 chosen from 1 choice
  scsi0 : SCSI emulation for USB Mass Storage devices
  scsi 0:0:0:0: Direct-Access     HTC      Android Phone    0100 PQ: 0 ANSI: 2
  sd 0:0:0:0: [sda] Attached SCSI removable disk
拔掉后会打印:
  usb 1-1: USB disconnect, address 2
 
在linux内核目录下搜索: grep "USB device using" * -nR返回信息:
  drivers/usb/core/hub.c:2186:              "%s %s speed %sUSB device using %s and address %d\n",
由此可知,位于drivers/usb/core/Hub.c中的hub_port_init()函数中:

  1. dev_info (&udev->dev,
  2. "%s %s speed %sUSB device using %s and address %d\n",
  3. (udev->config) ? "reset" : "new", speed, type,
  4. udev->bus->controller->driver->name, udev->devnum);

2) 接下来在source insight中查看源码调用:

hub_thread-->

  hub_events()-->

    hub_port_connect_change()-->

                                  调用 hub_port_init()

在hub_thread()函数中:可知hub_events() 的调用,需要等待中断khubd_wait被唤醒;

  1. static int hub_thread(void *__unused)
  2. {
  3. do {
  4. hub_events();
  5. wait_event_interruptible(khubd_wait,
  6. !list_empty(&hub_event_list) ||
  7. kthread_should_stop());
  8. try_to_freeze();
  9. } while (!kthread_should_stop() || !list_empty(&hub_event_list));
  10.  
  11. pr_debug("%s: khubd exiting\n", usbcore_name);
  12. return ;
  13. }

3)  搜索“中断khubd_wait”,可以得到它是在drivers/usb/core/Hub.c -->  kick_khubd()函数中:

  1. static void kick_khubd(struct usb_hub *hub)
  2. {
  3. unsigned long flags;
  4. /* Suppress autosuspend until khubd runs */
  5. to_usb_interface(hub->intfdev)->pm_usage_cnt = ;
  6.  
  7. spin_lock_irqsave(&hub_event_lock, flags);
  8. if (list_empty(&hub->event_list)) {
  9. list_add_tail(&hub->event_list, &hub_event_list);
  10. wake_up(&khubd_wait);
  11. }
  12. spin_unlock_irqrestore(&hub_event_lock, flags);
  13. }

4) 继续搜索kick_khubd(),可知该函数被hub_irq()调用,因此当USB设备插入后,D+ 或者D-会被拉高,从而使USB控制器产生usb_irq中断。

5) 在hub_port_connect_change()实现了设备注册、端口连接、创建USB设备等功能。

  1. static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
  2. {
  3.   udev = usb_alloc_dev(hdev, hdev->bus, port1);   //注册usb_device,放在usb总线上
  4.           device_initialize(&dev->dev);              // 初始化usb_device
              dev->dev.bus = &usb_bus_type;             // 设置usb_device的成员device->bus等于usb_bus总线
              dev->dev.type = &usb_device_type;          // 设置usb_device的成员device->type等于usb_device_type
              return dev;                                    // 返回一个usb_device结构体
      usb_set_device_state(udev, USB_STATE_POWERED); //设置注册的USB设备的状态标识
  5.   choose_address(udev); /*分配一个地址编号 */
  6. /* usb 1-1: new full speed USB device using s3c2410-ohci and address 3 **初始化端口,与USB设备建立连接*/
  7.   hub_port_init(hub, udev, port1, i)
              retval = hub_set_address(udev);      //(1)设置地址,告诉USB设备新的地址编号
              retval = usb_get_device_descriptor(udev, 8);   //(2)获得USB设备描述符前8个字节   status = usb_new_device(udev);            //创建USB设备,与USB驱动连接
              err = usb_get_configuration(udev);        // 把所有的描述符都读出来,并解析
  8.    -->usb_parse_configuration
  9.    -->device_add // 把device放入usb_bus_type的dev链表,
  10.     // 从usb_bus_type的driver链表里取出usb_driver,
  11.      // 把usb_interface和usb_driver的id_table比较
  12.      // 如果能匹配,调用usb_driver的probe
  13. }

小结

1)USB驱动程序源码的执行流程为:

2)usb_bus_type是一个全局变量, 和我们之前学的platform平台总线原理、结构体、设备与驱动节点、函数调用等相似, 属于USB总线, 是Linux中bus的一种. 每当创建一个USB设备,或者USB设备驱动时,USB总线都会调用match成员来匹配一次,使USB设备和USB设备驱动联系起来.

3)在分配一个地址编号时,USB接口最大能接127个设备,连续插拔两次USB键盘,也可以看出,如下图所示:

4)usb_get_device_descriptor()函数主要是获取目标设备描述符前8个字节,为什么先只开始读取8个字节?是因为开始时还不知道对方所支持的信包容量,这8个字节是每个设备都有的,后面再根据设备的数据,通过usb_get_device_descriptor()重读一次目标设备的设备描述结构.

5)那么USB驱动的id_table又该如何定义?

例如:参考/drivers/hid/usbhid/usbmouse.c (鼠标驱动)

  1. static struct usb_device_id usb_mouse_id_table [] = {
  2. { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
  3. USB_INTERFACE_PROTOCOL_MOUSE) },
      //(1)USB_INTERFACE_CLASS_HID 为设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03,HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03
    //(2)USB_INTERFACE_SUBCLASS_BOOT 为设置匹配USB的接口子类型为启动设备
    //(3)USB_INTERFACE_PROTOCOL_MOUSE 设置匹配USB的接口协议为USB鼠标的协议,等于2;当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议
    { } /* Terminating entry */
  4. };
  5.  
  6. MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
  7.  
  8. static struct usb_driver usb_mouse_driver = {
  9. .name = "usbmouse",
  10. .probe = usb_mouse_probe,
  11. .disconnect = usb_mouse_disconnect,
  12. .id_table = usb_mouse_id_table,
  13. };

源码分析完后,就可以自己写USB设备驱动程序了。


参考:https://www.cnblogs.com/lifexy/p/7631900.html

 

八、USB驱动分析的更多相关文章

  1. USB驱动分析

    INIT函数: 这是内核模块的初始化函数,其所作的工作只有注册定义好的USB驱动结构体. USB驱动结构体如下: Usb_driver中的probe函数是驱动和设备匹配成功后调用. Usb_drive ...

  2. usb键鼠标驱动分析

    一.鼠标 linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现 1.加载初始化过程 1.1模块入口 module_init(usb_mouse_init); ...

  3. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  4. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  5. linux下usb转串口驱动分析【转】

    转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...

  6. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html   ---------------------------------------- ...

  7. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  8. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  9. linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

    转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 预备知识 lin ...

随机推荐

  1. JSTL核心库

    1 out和set(重点) out <c:out value=”aaa”/> 输出aaa字符串常量 <c:out value=”${aaa}”/> 与${aaa}相同 < ...

  2. Java—集合框架 Collections.sort()、Comparable接口和Comparator接口

    Collentions工具类--java.util.Collections Collentions是Java集合框架中,用来操作集合对象的工具类,也是Java集合框架的成员,与List.Map和Set ...

  3. Java—集合框架Set

    Set接口及其实现类——HashSet Set是元素无序并且不可以重复的集合,被称作集. HashSet—哈希集,是Set的一个重要实现类. Set的使用   HashSet没有像List一样的set ...

  4. C# 轻松读取、改变文件的创建、修改、访问时间 z

    // 读取文件的创建.修改.访问时间FileInfo fi = new FileInfo("C://test.txt");Console.WriteLine(fi.Creation ...

  5. 使用SAPGUI画图

    国内80后上的编程课应该都学过Logo这门编程语言: Logo语言是一门专门设计用来进行编程教学的语言,于1967年由Wally Feurzeig, Seymour Papert和Cynthia So ...

  6. Django运行SQL语句

    1.Manager.raw(raw_query, params=None, translations=None) >>> for p in Person.objects.raw('S ...

  7. swift 协议(结合扩展)的特点

    协议的传统实现: 定义接口+实现协议    由抽象到具体: 协议的逆向实现(使用扩展): 由已存在的类型抽离部分功能作为协议,并让原体符合协议: 由具体到抽象:  向上抽离:  向上生成: 协议的缺省 ...

  8. 【LOJ6041】「雅礼集训 2017 Day7」事情的相似度(用LCT维护SAM的parent树)

    点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...

  9. ACM-ICPC(10/21)

    写一发后缀数组套路题,看起来简单,写起来要人命哦~~~ 总共13题. 分两天debug吧,有点累了~~~ suffix(后缀数组的应用) sa[i] :排名第 i 的后缀在哪(i 从 1 开始) ra ...

  10. Chrome,本地页面和插件

    今天测试一款Chrome插件,这款插件提供了一些本地页面做测试用,在解决一些技术问题之后,在插件的官网上可以测试成功了,但是在本地页面上测试时Chrome始终会拦截插件,即使在右上角的地址栏中允许该本 ...