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的时候也会有指示类似如下:

  1. # usb 1-1: new full speed USB device using s3c2410-ohci and address 2
  2. usb 1-1: configuration #1 chosen from 1 choice
  3. 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中调用

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

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

"HUB"是一个特殊的"USB 设备"。

继续搜索可以有如下关系

  1. >hub_thread
  2. >hub_events
  3. >hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
  4. //分配一个 usb_device 空间
  5. >usb_alloc_dev(hdev, hdev->bus, port1);
  6. >kzalloc(sizeof(*dev), GFP_KERNEL)
  7. >device_initialize(&dev->dev)
  8. >dev->dev.bus = &usb_bus_type;
  9. >dev->dev.type = &usb_device_type
  10. >sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); //usb 的编号
  11. //分配地址,最大127
  12. >choose_address(udev)
  13. >if (devnum >= 128) devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
  14. //根据 usb高速低速全速设置一些数据
  15. >hub_port_init(hub, udev, port1, i)
  16. //设置地址
  17. >hub_set_address
  18. >usb_control_msg
  19. //先读取必备的描述符 8
  20. >usb_get_device_descriptor(udev, 8)
  21. >usb_control_msg
  22. //18个长度的描述符
  23. >usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)
  24. //打印usb信息
  25. >dev_info
  26. >usb_new_device(udev)
  27. //获取配置
  28. >usb_get_configuration
  29. //分配设备号
  30. >dev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)))
  31. //这个是通用的函数哦,并不是单独给usb用的
  32. >device_add
  33. //创建设备文件
  34. >device_create_file
  35. >bus_add_device
  36. >bus_attach_device
  37. >bus_attach_device
  38. >device_attach(dev)
  39. >bus_for_each_drv(dev->bus, NULL, dev, __device_attach)
  40. >__device_attach //(while ((drv = next_driver(&i)) && !error))
  41. >driver_probe_device(drv, dev)
  42. >if (drv->bus->match && !drv->bus->match(dev, drv))
  43. goto done;
  44. >really_probe(dev, drv);
  45. >dev->bus->probe
  46. >drv->probe //没有dev->bus->probe执行
  47. >list_for_each_entry
  48. //休眠
  49. >wait_event_interruptible
  50. >hub_irq
  51. //唤醒队列
  52. >wake_up(&khubd_wait)

alloc_dev

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

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

都是属于bus_type,类比如下:

  1. struct bus_type platform_bus_type = {
  2. .name = "platform",
  3. .dev_attrs = platform_dev_attrs,
  4. .match = platform_match,
  5. .uevent = platform_uevent,
  6. .suspend = platform_suspend,
  7. .suspend_late = platform_suspend_late,
  8. .resume_early = platform_resume_early,
  9. .resume = platform_resume,
  10. };
  11. struct bus_type usb_bus_type = {
  12. .name = "usb",
  13. .match = usb_device_match,
  14. .uevent = usb_uevent,
  15. .suspend = usb_suspend,
  16. .resume = usb_resume,
  17. };

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

choose_address

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

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

hub_port_init

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

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

usb_get_device_descriptor

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


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

usb_control_msg

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

usb_new_device

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

usb_get_configuration

  1. //支持最大8种配置
  2. if (ncfg > USB_MAXCONFIG) {
  3. dev_warn(ddev, "too many configurations: %d, "
  4. "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
  5. dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
  6. }
  7. //分配9字节存储,读取描述符中的各种配置
  8. buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
  9. usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
  10. //分配配置使用实际允许的大小
  11. length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
  12. bigbuffer = kmalloc(length, GFP_KERNEL);
  13. usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);
  14. //填充
  15. dev->rawdescriptors[cfgno] = bigbuffer;
  16. //解析
  17. usb_parse_configuration

device_add

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

bus_attach_device

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

  1. bus_attach_device
  2. >device_attach(dev)
  3. >bus_for_each_drv(dev->bus, NULL, dev, __device_attach)//函数指针在这里
  4. >__device_attach //(while ((drv = next_driver(&i)) && !error))
  5. >driver_probe_device(drv, dev)
  6. >if (drv->bus->match && !drv->bus->match(dev, drv))
  7. goto done;
  8. >really_probe(dev, drv);
  9. >dev->bus->probe
  10. >drv->probe //如果没有dev->bus->probe执行

match

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

  1. struct bus_type usb_bus_type = {
  2. .name = "usb",
  3. .match = usb_device_match,
  4. .uevent = usb_uevent,
  5. .suspend = usb_suspend,
  6. .resume = usb_resume,
  7. };
  8. static int usb_device_match(struct device *dev, struct device_driver *drv)
  9. {
  10. if (is_usb_device(dev)) { //判断是不是USB设备
  11. if (!is_usb_device_driver(drv))
  12. return 0;
  13. return 1;
  14. }
  15. else { //否则就是USB驱动或者USB设备的接口
  16. struct usb_interface *intf;
  17. struct usb_driver *usb_drv;
  18. const struct usb_device_id *id;
  19. if (is_usb_device_driver(drv)) //如果是USB驱动,就不需要匹配,直接return
  20. return 0;
  21. intf = to_usb_interface(dev); //获取USB设备的接口
  22. usb_drv = to_usb_driver(drv); //获取USB驱动
  23. id = usb_match_id(intf, usb_drv->id_table); //匹配USB驱动的成员id_table
  24. if (id)
  25. return 1;
  26. id = usb_match_dynamic_id(intf, usb_drv);
  27. if (id)
  28. return 1;
  29. }
  30. return 0;
  31. }

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

  1. struct usb_device_id {
  2. __u16 match_flags; //与usb设备匹配那种类型?比较类型的宏如下:
  3. //USB_DEVICE_ID_MATCH_INT_INFO : 用于匹配设备的接口描述符的3个成员
  4. //USB_DEVICE_ID_MATCH_DEV_INFO: 用于匹配设备描述符的3个成员
  5. //USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION: 用于匹配特定的USB设备的4个成员
  6. //USB_DEVICE_ID_MATCH_DEVICE:用于匹配特定的USB设备的2个成员(idVendor和idProduct)
  7. /* 以下4个用匹配描述特定的USB设备 */
  8. __u16 idVendor; //厂家ID
  9. __u16 idProduct; //产品ID
  10. __u16 bcdDevice_lo; //设备的低版本号
  11. __u16 bcdDevice_hi; //设备的高版本号
  12. /*以下3个就是用于比较设备描述符的*/
  13. __u8 bDeviceClass; //设备类
  14. __u8 bDeviceSubClass; //设备子类
  15. __u8 bDeviceProtocol; //设备协议
  16. /* 以下3个就是用于比较设备的接口描述符的 */
  17. __u8 bInterfaceClass; //接口类型
  18. __u8 bInterfaceSubClass; //接口子类型
  19. __u8 bInterfaceProtocol; //接口所遵循的协议
  20. /* not matched against */
  21. kernel_ulong_t driver_info;
  22. };

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

  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) },
  4. { } /* Terminating entry */
  5. };
  6. #define USB_INTERFACE_INFO(cl,sc,pr) \
  7. //设置id_table的.match_flags成员
  8. .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
  9. //设置id_table的3个成员,用于与匹配USB设备的3个成员
  10. .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
  11. //最终结果如下
  12. .bInterfaceClass =USB_INTERFACE_CLASS_HID;
  13. //设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03
  14. //HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03
  15. .bInterfaceSubClass =USB_INTERFACE_SUBCLASS_BOOT;
  16. //设置匹配USB的接口子类型为启动设备
  17. .bInterfaceProtocol=USB_INTERFACE_PROTOCOL_MOUSE;
  18. //设置匹配USB的接口协议为USB鼠标的协议,等于2
  19. //当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议

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

linux usb总线驱动(一)的更多相关文章

  1. EDK II之USB总线驱动的实现框架

    本文简单介绍一下UEFI中USB驱动的实现框架: 下图是USBD向上层驱动提供的接口: 1.从图中我们可以看出,USBDI的实现主要通过调用HCDI实现 和 访问USB_INTERFACE结构体(该结 ...

  2. Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析

    下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC.VPSS.VO最后通过HD ...

  3. Linux下的USB总线驱动(一)

    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1.      USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...

  4. Linux USB摄像头驱动【转】

    本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...

  5. Linux 设备总线驱动模型

    尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要.     Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...

  6. Linux USB 摄像头驱动

    在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 USB 接口的摄像头.这一章主要是介绍 USB 摄像头的设备驱动程序.在我们印象中,驱动程序都是一个萝卜一个坑, ...

  7. linux平台总线驱动设备模型之点亮LED

    这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备. 问:如何编写平台总线驱动设备这一套架构的设备驱动? ...

  8. 嵌入式Linux USB WIFI驱动的移植

    硬件平台:飞思卡尔MX258开发板 操作系统:Linux2.6.31 WIFI:    RT2860 USB WIFI模组 交叉编译环境:gcc version 4.1.2 调试步骤: 第一步:测试U ...

  9. Linux平台总线驱动设备模型

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...

随机推荐

  1. 基于MFC的学生成绩管理系统的设计与实现

    1.技术介绍MFC是微软基础类库的简称,是微软公司实现的一个C++类库,主要封装了大部分的WINDOWS API函数,并且包含一个应用程序框架,以减少应用程序开发人员工作量.VC++是微软公司开发的C ...

  2. 创建你的第一个Composer/Packagist包

    今天我们要介绍一下如何通过Composer和Packagist向PHP社区贡献代码包.首先,如果你是一个PHP开发者但是还不知道什么是Composer,请先参考了一下这篇文章http://docs.p ...

  3. (转载)Python之道1-环境搭建与pycharm的配置django安装及MySQL数据库配置

    近期做那个python的开发,今天就来简单的写一下开发路线的安装及配置, 开发路线 Python3.6.1+Pycharm5.0.6+Django1.11+MySQL5.7.18 1-安装Python ...

  4. Jenkins插件之显示构建时间

    1.进入jenkin插件管理器中,安装  Timestamper 插件 2.安装完成后,进入到构建任务里面,在 构建环境 中勾选  Add timestamps to the Console Outp ...

  5. Django REST framework基础:序列化

    表结构: class Article(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(m ...

  6. Redis其他常用操作

    详细Redis操作手册: http://doc.redisfans.com/ ============================================================= ...

  7. 【Python 21】52周存钱挑战1.0

    1.案例描述 按照52周存钱法,存钱人必须在一年52周内,每周递存10元.例如,第一周存10元,第二周存20元,第三周存30元,直到第52周存520元. 记录52周后能存多少钱?即10+20+30+. ...

  8. 苹果手机连接Wifi认证机制

    Wifi状态保持方法和nas设备 https://patents.google.com/patent/CN106793171A/zh 基于ios终端的离线wifi热点认证方法和认证系统 https:/ ...

  9. Vultr CentOS 7 安装 Docker

    前言 最近在梳理公司的架构,想用 VPS 先做一些测试,然后就开始踩坑了!我用 Vultr 新买了个 VPS. 安装的 CentOS 版本: [root@dbn-seattle ~]# cat /et ...

  10. go笔记-限速器(limiter)

    参考: https://blog.csdn.net/wdy_yx/article/details/73849713https://www.jianshu.com/p/1ecb513f7632 http ...