热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linuxusb总线驱动(一)

目录linuxusb总线驱动框架USB介绍传输类型控制器接口2440接口基本流程alloc_dev

目录

  • linux usb总线驱动框架
    • USB 介绍
    • 传输类型
    • 控制器接口
    • 2440接口
    • 基本流程
      • alloc_dev
      • choose_address
      • hub_port_init
      • usb_get_device_descriptor
      • usb_control_msg
      • usb_new_device
      • usb_get_configuration
      • device_add
      • bus_attach_device
      • match

title: linux usb总线驱动
tags: linux
date: 2018/12/11/ 17:14:30
toc: true
---

linux usb总线驱动框架

USB 介绍

  • 当插入一个未知的usb设备,电脑也会有相应的提示?
    1. 插入有反应,是因为电脑的usb作为主机设备,有一个下拉电阻,从机上拉电压通知
    2. 识别到一些信息,是因为有着标准的 usb 总线协议,有一些简单的标准的交互方式获取信息。
  • usb 设备的基本构成
    1. 控制usb设备,其实也就是控制usb的寄存器来通过usb总线协议来与设备读写来实现具体的功能。
    2. usb 总线协议一般内核都会自带,我们只需要写总线协议下面的逻辑控制就好了
  • usb也是主从结构,读写只能由主机发起
  • 新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信
  • 所谓热拔插,其实是因为在主机上的D+和D-都接了15k下拉电阻,平时为低电平。从机有上拉电阻
    • 全速设备(12M/s)和高速设备(480M/s):D+ 上拉1.5k
    • 低俗设备:D-上拉1.5k

传输类型

类型 可靠性 时间性 举例
控制传输 可靠 有保证 usb设备识别
批量传输 可靠 没有保证 U盘
中断传输 可靠 实时 鼠标
实时同步传输 不可靠 实时 摄像头

USB中传输的对象为端点,端点0是每个设备都有的,作为控制传输,可读可写。其他端点只有一个方向。

控制器接口

USB规范都是从寄存器级别规定好的,不过各个厂商可能有自己的几个专用的寄存器 参考

OHCI(Open Host Controller Interface):微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂 其实这个不止是usb上用,其他接口也有用的

UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单

EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),

xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等

2440接口

2440是兼容OHCI Rev 1.0的在数据手册上有标准,同时我们插入usb的时候也会有指示类似如下:

# usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
usb 1-1: USB disconnect, address 2

基本流程

  1. 硬件插入,在D+或者D-上有电平变化,通知主机
  2. 主机通过端点地址0与usb设备交互,分配地址给usb从设备
  3. 通过端点0获取从设备的信息
  4. 安装对应的设备驱动,提供读写设备的函数

当插入usb时候,提示如下,搜索相关字符grep "USB device using" * -nR,可以在drivers\usb\core\hub.c中找到hub_port_init中调用

# usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
usb 1-1: USB disconnect, address 2

每一个 USB 主机控制器,都自带了一个 USB HUB, HUB 上再接“ USB 设备”。可以认为
"HUB"是一个特殊的"USB 设备"。

mark

继续搜索可以有如下关系

>hub_thread                     
    >hub_events                     
        >hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
            //分配一个 usb_device 空间
            >usb_alloc_dev(hdev, hdev->bus, port1);
                >kzalloc(sizeof(*dev), GFP_KERNEL)
                >device_initialize(&dev->dev)
                >dev->dev.bus = &usb_bus_type;
                >dev->dev.type = &usb_device_type
                >sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); //usb 的编号
            //分配地址,最大127
            >choose_address(udev)
                >if (devnum >= 128) devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
            //根据 usb高速低速全速设置一些数据
            >hub_port_init(hub, udev, port1, i)
                //设置地址
                >hub_set_address
                    >usb_control_msg
                //先读取必备的描述符 8
                >usb_get_device_descriptor(udev, 8)
                    >usb_control_msg
                //18个长度的描述符
                >usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)
                //打印usb信息
                >dev_info
            >usb_new_device(udev)
                //获取配置
                >usb_get_configuration
                //分配设备号
                >dev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)))
                //这个是通用的函数哦,并不是单独给usb用的
                >device_add
                    //创建设备文件
                    >device_create_file
                    >bus_add_device
                    >bus_attach_device
                        >bus_attach_device
                            >device_attach(dev)
                                >bus_for_each_drv(dev->bus, NULL, dev, __device_attach)
                                    >__device_attach //(while ((drv = next_driver(&i)) && !error))
                                        >driver_probe_device(drv, dev)
                                            >if (drv->bus->match && !drv->bus->match(dev, drv))
                                                goto done;
                                            >really_probe(dev, drv);
                                                >dev->bus->probe
                                                >drv->probe //没有dev->bus->probe执行
                    
                    
                    >list_for_each_entry
    //休眠                
    >wait_event_interruptible
    
>hub_irq
    //唤醒队列
    >wake_up(&khubd_wait)
    

mark

mark

alloc_dev

usb_alloc_dev中初始化了总线设备类型,这与之前初始化platform的总线类型是类似的

dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;

都是属于bus_type,类比如下:

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .suspend    = platform_suspend,
    .suspend_late   = platform_suspend_late,
    .resume_early   = platform_resume_early,
    .resume     = platform_resume,
};


struct bus_type usb_bus_type = {
    .name =     "usb",
    .match =    usb_device_match,
    .uevent =   usb_uevent,
    .suspend =  usb_suspend,
    .resume =   usb_resume,
};

这里的match也是同样的作用:设备插入时,总线驱动调用match来匹配

choose_address

分配usb的地址,支持最多127个设备端点

    devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
            bus->devnum_next);
    if (devnum >= 128)
        devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
    udev->devnum = devnum;

hub_port_init

这里进行usb插入后的信息打印,设置地址,获取描述符等

hub_set_address(udev);
usb_get_device_descriptor(udev, 8);  
    > memcpy(&dev->descriptor, desc, size);//存储的描述符结构
...
usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);

usb_get_device_descriptor

hub_port_init首先获取8字节的描述符,因为所有设备都支持这8字节的描述符,也支持8字节这个最小长度。具体描述符先结构如下:


struct usb_device_descriptor {
 __u8  bLength;                          //本描述符的size
 __u8  bDescriptorType;                //描述符的类型,这里是设备描述符DEVICE
 __u16 bcdUSB;                           //指明usb的版本,比如usb2.0
 __u8  bDeviceClass;                    //类
 __u8  bDeviceSubClass;                 //子类
 __u8  bDeviceProtocol;                  //指定协议
 __u8  bMaxPacketSize0;                 //端点0对应的最大包大小
 __u16 idVendor;                         //厂家ID
 __u16 idProduct;                        //产品ID
 __u16 bcdDevice;                        //设备的发布号
 __u8  iManufacturer;                    //字符串描述符中厂家ID的索引
 __u8  iProduct;                         //字符串描述符中产品ID的索引
 __u8  iSerialNumber;                   //字符串描述符中设备序列号的索引
 __u8  bNumConfigurations;               //可能的配置的数目
} __attribute__ ((packed));

usb_control_msg

hub_set_addressusb_get_device_descriptor中最终也是调用了这个usb_control_msg来与usb设备交互的。

usb_new_device

int usb_new_device(struct usb_device *udev)
{
   ... ...
   err = usb_get_configuration(udev); //(1)获取配置描述块
  ... ...
  err = device_add(&udev->dev);      // (2)把device放入bus的dev链表中,并寻找对应的设备驱动
}

usb_get_configuration

//支持最大8种配置
if (ncfg > USB_MAXCONFIG) {
    dev_warn(ddev, "too many configurations: %d, "
             "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
    dev->descriptor.bNumCOnfigurations= ncfg = USB_MAXCONFIG;
}

//分配9字节存储,读取描述符中的各种配置
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);

//分配配置使用实际允许的大小
length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
bigbuffer = kmalloc(length, GFP_KERNEL);
usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);

//填充
dev->rawdescriptors[cfgno] = bigbuffer;
//解析
usb_parse_configuration

device_add

dev = get_device(dev);
parent = get_device(dev->parent);
error = setup_parent(dev, parent);
//创建类下的文件
error = device_create_file(dev, &dev->uevent_attr);
。。
//把这个设备添加到dev->bus的device表中
bus_add_device
///来匹配对应的驱动程序
bus_attach_device
//链表遍历
list_for_each_entry(class_intf, &dev->class->interfaces, node)

bus_attach_device

device_add中会调用bus_attach_device来运行匹配函数后运行probe

bus_attach_device
    >device_attach(dev)
        >bus_for_each_drv(dev->bus, NULL, dev, __device_attach)//函数指针在这里
            >__device_attach   //(while ((drv = next_driver(&i)) && !error))
                >driver_probe_device(drv, dev)
                    >if (drv->bus->match && !drv->bus->match(dev, drv))
                        goto done;
                    >really_probe(dev, drv);
                        >dev->bus->probe
                        >drv->probe //如果没有dev->bus->probe执行

match

这个时候再来看匹配总线的函数

struct bus_type usb_bus_type = {
    .name =     "usb",
    .match =    usb_device_match,
    .uevent =   usb_uevent,
    .suspend =  usb_suspend,
    .resume =   usb_resume,
};


static int usb_device_match(struct device *dev, struct device_driver *drv)
{

       if (is_usb_device(dev)) {                       //判断是不是USB设备
              if (!is_usb_device_driver(drv))
                     return 0;
              return 1;
         }
else {                                                //否则就是USB驱动或者USB设备的接口

              struct usb_interface *intf;
              struct usb_driver *usb_drv;
              const struct usb_device_id *id;           

              if (is_usb_device_driver(drv))   //如果是USB驱动,就不需要匹配,直接return
                     return 0; 

              intf = to_usb_interface(dev);               //获取USB设备的接口
              usb_drv = to_usb_driver(drv);                    //获取USB驱动

              id = usb_match_id(intf, usb_drv->id_table);  //匹配USB驱动的成员id_table
              if (id)
                     return 1;

              id = usb_match_dynamic_id(intf, usb_drv);
              if (id)
                     return 1;
       }
       return 0;
}

查看下具体的id_table,具体的类型在include\linux\usb.h

struct usb_device_id {
        
       __u16             match_flags;   //与usb设备匹配那种类型?比较类型的宏如下:
 //USB_DEVICE_ID_MATCH_INT_INFO : 用于匹配设备的接口描述符的3个成员
 //USB_DEVICE_ID_MATCH_DEV_INFO: 用于匹配设备描述符的3个成员
 //USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION: 用于匹配特定的USB设备的4个成员
 //USB_DEVICE_ID_MATCH_DEVICE:用于匹配特定的USB设备的2个成员(idVendor和idProduct)

 
       /* 以下4个用匹配描述特定的USB设备 */
       __u16             idVendor;              //厂家ID
       __u16             idProduct;             //产品ID
       __u16             bcdDevice_lo;        //设备的低版本号
       __u16             bcdDevice_hi;        //设备的高版本号

       /*以下3个就是用于比较设备描述符的*/
       __u8        bDeviceClass;                    //设备类
       __u8        bDeviceSubClass;                 //设备子类
       __u8        bDeviceProtocol;                 //设备协议

       /* 以下3个就是用于比较设备的接口描述符的 */
       __u8        bInterfaceClass;                   //接口类型
       __u8        bInterfaceSubClass;             //接口子类型
       __u8        bInterfaceProtocol;           //接口所遵循的协议

       /* not matched against */
       kernel_ulong_t       driver_info;
};

参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动)

static struct usb_device_id usb_mouse_id_table [] = {
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE) },
    { } /* Terminating entry */
};

#define USB_INTERFACE_INFO(cl,sc,pr) \
//设置id_table的.match_flags成员
    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
//设置id_table的3个成员,用于与匹配USB设备的3个成员
    .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

        
//最终结果如下        
.bInterfaceClass =USB_INTERFACE_CLASS_HID;  
   //设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03
   //HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03

.bInterfaceSubClass =USB_INTERFACE_SUBCLASS_BOOT;  
   //设置匹配USB的接口子类型为启动设备

.bInterfaceProtocol=USB_INTERFACE_PROTOCOL_MOUSE;
  //设置匹配USB的接口协议为USB鼠标的协议,等于2
  //当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议        

查看下win7系统鼠标的设备信息,VID:表示厂家(vendor)ID,PID:表示产品(Product) ID

mark


推荐阅读
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • Android 图像色彩处理技术详解
    本文详细探讨了 Android 平台上的图像色彩处理技术,重点介绍了如何通过模仿美图秀秀的交互方式,利用 SeekBar 实现对图片颜色的精细调整。文章展示了具体的布局设计和代码实现,帮助开发者更好地理解和应用图像处理技术。 ... [详细]
  • 在进行网络编程时,准确获取本地主机的IP地址是一项基本但重要的任务。Winsock作为20世纪90年代初由Microsoft与多家公司共同制定的Windows平台网络编程接口,为开发者提供了一套高效且易用的工具。通过Winsock,开发者可以轻松实现网络通信功能,并准确获取本地主机的IP地址,从而确保应用程序在网络环境中的稳定运行。此外,了解Winsock的工作原理及其API函数的使用方法,有助于提高开发效率和代码质量。 ... [详细]
  • 本文介绍了如何通过掌握 IScroll 技巧来实现流畅的上拉加载和下拉刷新功能。首先,需要按正确的顺序引入相关文件:1. Zepto;2. iScroll.js;3. scroll-probe.js。此外,还提供了完整的代码示例,可在 GitHub 仓库中查看。通过这些步骤,开发者可以轻松实现高效、流畅的滚动效果,提升用户体验。 ... [详细]
  • 本文深入探讨了 MXOTDLL.dll 在 C# 环境中的应用与优化策略。针对近期公司从某生物技术供应商采购的指纹识别设备,该设备提供的 DLL 文件是用 C 语言编写的。为了更好地集成到现有的 C# 系统中,我们对原生的 C 语言 DLL 进行了封装,并利用 C# 的互操作性功能实现了高效调用。此外,文章还详细分析了在实际应用中可能遇到的性能瓶颈,并提出了一系列优化措施,以确保系统的稳定性和高效运行。 ... [详细]
  • 在Spring与Ibatis集成的环境中,通过Spring AOP配置事务管理至服务层。当在一个服务方法中引入自定义多线程时,发现事务管理功能失效。若不使用多线程,事务管理则能正常工作。本文深入分析了这一现象背后的潜在风险,并探讨了可能的解决方案,以确保事务一致性和线程安全。 ... [详细]
  • 网络通信基础:互联网协议(IP)详解
    互联网协议(IP)作为TCP/IP协议栈的核心组成部分,主要负责提供一种无连接且不可靠的数据包传输服务。这意味着IP并不确保数据包能够成功抵达目标地址,而是尽力而为地进行传输。此外,IP协议在数据传输过程中不维护任何连接状态,每个数据包独立处理,确保了网络的高效性和灵活性。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • 本文深入探讨了 Python Watchdog 库的使用方法和应用场景。通过详细的代码示例,展示了如何利用 Watchdog 监控文件系统的变化,包括文件的创建、修改和删除等操作。文章不仅介绍了 Watchdog 的基本功能,还探讨了其在实际项目中的高级应用,如日志监控和自动化任务触发。读者将能够全面了解 Watchdog 的工作原理及其在不同场景下的应用技巧。 ... [详细]
  • MongoDB Aggregates.group() 方法详解与编程实例 ... [详细]
  • Java 8 引入了 Stream API,这一新特性极大地增强了集合数据的处理能力。通过 Stream API,开发者可以更加高效、简洁地进行集合数据的遍历、过滤和转换操作。本文将详细解析 Stream API 的核心概念和常见用法,帮助读者更好地理解和应用这一强大的工具。 ... [详细]
  • DHCP三层交换机设置方式全局模式和接口模式设置方式和命令resetsave回车输入yreboot输入n输入y重启后就恢复默认设置了默认用户名密码adminAdmin@huawei ... [详细]
  • IIS配置大全:从基础到高级的全面指南
    IIS配置详解:从基础到高级的全面指南IIS前端配置与web.config文件紧密相关,相互影响。本文详细介绍了如何设置允许通过的HTTP请求方法,包括HEAD、POST、GET、TRACE和OPTIONS。提供了两种主要的配置方法,并探讨了它们在实际应用中的优缺点。此外,还深入讲解了其他高级配置选项,帮助读者全面提升IIS服务器的性能和安全性。 ... [详细]
  • 本文提供了 RabbitMQ 3.7 的快速上手指南,详细介绍了环境搭建、生产者和消费者的配置与使用。通过官方教程的指引,读者可以轻松完成初步测试和实践,快速掌握 RabbitMQ 的核心功能和基本操作。 ... [详细]
author-avatar
min-章_998
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有