• 八、USB驱动分析


    学习目标:分析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驱动程序框架
    11 
    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 ",
    由此可知,位于drivers/usb/core/Hub.c中的hub_port_init()函数中:
     
    1 dev_info (&udev->dev,
    2           "%s %s speed %sUSB device using %s and address %d
    ",
    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
    ", usbcore_name);
    12     return 0;
    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 = 1;
     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总线上
              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结构体
    5   usb_set_device_state(udev, USB_STATE_POWERED); //设置注册的USB设备的状态标识 7   choose_address(udev); /*分配一个地址编号 */ 9 /* usb 1-1: new full speed USB device using s3c2410-ohci and address 3 **初始化端口,与USB设备建立连接*/ 10   hub_port_init(hub, udev, port1, i)
              retval = hub_set_address(udev);      //
    (1)设置地址,告诉USB设备新的地址编号
              retval = usb_get_device_descriptor(udev, 8);   //(2)获得USB设备描述符前8个字节
    12   status = usb_new_device(udev);            //创建USB设备,与USB驱动连接
              err = usb_get_configuration(udev);
            // 把所有的描述符都读出来,并解析 14    -->usb_parse_configuration 15    -->device_add // 把device放入usb_bus_type的dev链表, 16     // 从usb_bus_type的driver链表里取出usb_driver, 17      // 把usb_interface和usb_driver的id_table比较 18      // 如果能匹配,调用usb_driver的probe 19 }

    小结

    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键盘的协议
    4 { } /* Terminating entry */ 5 }; 6 7 MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); 8 9 static struct usb_driver usb_mouse_driver = { 10 .name = "usbmouse", 11 .probe = usb_mouse_probe, 12 .disconnect = usb_mouse_disconnect, 13 .id_table = usb_mouse_id_table, 14 };

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


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

     

  • 相关阅读:
    Connection with Web.config
    sp_user_no(參數數的oracle_sp)及fn_test(有返回值的oracle參數)
    xml學習心得
    OOP 术语:Attributes(特性)与 Properties(属性)的区别(转载)
    asp.net 4.0 新特性(转载)
    详解C#中Attribute特性应用 (转载)
    保存web.config文件(转载)
    OOP 术语:Arguments(参量)和 Parameters(参数)的区别(转载)
    HTTP调试工具:Fiddler,httpwatch 介绍(转)
    C# 4.0新特性dynamic有何用处?(转载)
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10184917.html
Copyright © 2020-2023  润新知