当前位置: 首页 > news >正文

《Linux驱动:USB设备驱动看这一篇就够了》

文章目录

  • 一,前言
  • 二,USB子系统
    • 2.1 USB驱动基础概念
      • 2.1.1 USB版本
      • 2.1.2 USB主从结构
      • 2.1.3 USB的传输类型
      • 2.1.4 USB设备描述符
        • 2.1.4.1 设备描述符
        • 2.1.4.2 配置描述符
        • 2.1.4.3 接口描述符
        • 2.1.4.4 端点描述符
        • 2.1.4.5 字符串描述符
        • 2.1.4.6 人机接口描述符
        • 2.1.4.6 USB描述符的类型值
      • 2.1.5 USB的数据传输对象
    • 2.2 USB子系统框架
  • 三,USB总线驱动程序
    • 3.1 USB Core
      • 3.1.1 注册 USB 总线
      • 3.1.2 注册USB接口驱动
      • 3.1.3 初始化USB Hub
        • 3.1.3.1 khubd_wait 的唤醒
      • 3.1.4 注册USB设备驱动
      • 3.1.5 usb_register 和 usb_register_device_driver
      • 3.1.6 总结
    • 3.2 USB主机控制器驱动(HCD)
      • 3.2.1 USB主机控制器-设备
      • 3.2.2 USB主机控制器-驱动
      • 3.2.3 USB主机控制器设备和驱动的匹配
      • 3.2.4 USB主机控制器驱动的probe函数
        • 3.2.4.1 usb_device_match
        • 3.2.4.2 USB设备驱动的probe函数
  • 四,USB设备驱动 -- USB鼠标
    • 4.1 注册一个USB接口驱动
    • 4.2 USB接口设备的创建
    • 4.3 USB接口驱动和USB接口设备的匹配
    • 4.4 创建数据传输管道
    • 4.5 分配urb
    • 4.6 urb数据结构初始化
    • 4.7 提交USB请求块
    • 4.8 总结
  • 五,实现一个USB设备驱动程序

一,前言

这一篇学习分析USB设备驱动程序,主要涉及到USB驱动基本概念,USB版本对比、USB主从结构、USB设备的传输类型、USB设备描述符关系、USB的数据传输对象;详细分析了USB总线驱动框架,USB Core、USB HCD、USB总线-设备-驱动模型;最后总结了USB驱动开发中的一般流程并模拟实现一个USB设备驱动程序。

二,USB子系统

2.1 USB驱动基础概念

2.1.1 USB版本

版本理论最大速率花名
USB 1.01.5Mbps低速(Low-Speed)
USB 1.112Mbps全速(Full-Speed)
USB 2.0480Mbps高速(High-Speed)
USB 3.05Gbps超高速(Super-Speed)
USB 3.110Gbps超高速+(Super-speed+)
USB 4.040Gbps

2.1.2 USB主从结构

USB分为USB 主机(USB Host)和USB device(USB设备)。比如USB鼠标插入到电脑,电脑就是USB Host,鼠标就是USB device。所有的USB传输,都是从USB主机这方发起的;USB设备没有"主动"通知USB主机的能力。

2.1.3 USB的传输类型

  • 控制传输:控制传输是双向传输,数据量通常比较小,主要指由USB总线驱动程序用来进行查询、配置以及给USB设备发送通用的命令。控制传输典型地用在主计算机和USB外设之间的端点0(Endpoint 0)之间的传输,但是指定供应商的控制传输可能用到其它的端点。比如:USB设备的识别过程。
  • 批量传输:主要应用在数据大量传输,同时又没有带宽和间隔时间要求的情况下,进行可靠传输。比如:U盘拷贝数据。
  • 中断传输:中断传输主要用于定时查询设备是否有中断数据要传输,设备的端点模式器的结构决定了它的查询频率,从1到255ms之间。这种传输方式典型的应用在少量的、分散的、不可预测数据的传输,比如,键盘和鼠标就属于这一类型。中断传输是单向的并且对于host来说只有输入的方式。
  • 实时传输:实时传输提供了确定的带宽和间隔时间,它被用于时间严格并具有较强容错性的流数据传输,或者用于要求恒定的数据传输率的即时应用中。比如:USB摄像头。

2.1.4 USB设备描述符

在这里插入图片描述

  • 一个USB设备描述符中可以有多个配置描述符,即USB设备可以有多种配置;一个配置描述符中可以有多个接口描述符,即USB设备可以支持多种功能(接口);一个接口描述符中可以有多个端点描述符。
  • 一设备至少要包含设备描述符、配置描述符和接口描述符,如果USB设备没有端点描述符,则它仅仅用默认管道与主机进行数据传输。
  • 接口,表示逻辑上的设备,比如USB声卡可以分为接口1-录音设备,接口2-播放设备。
  • 访问设备时,即访问某个接口,接口表示逻辑设备。
  • 传输数据时,即读写某个端口,端口是数据通道。
2.1.4.1 设备描述符
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {__u8  bLength; //该结构体大小__u8  bDescriptorType; //描述符类型 (此处应为0x01,即设备描述符)__le16 bcdUSB; //usb版本号 200 -> USB2.0__u8  bDeviceClass; //设备类 __u8  bDeviceSubClass; //设备类子类__u8  bDeviceProtocol; //设备协议,以上三点都是USB官方定义__u8  bMaxPacketSize0; //端点0最大包大小 (只能为8,16,32,64)__le16 idVendor; //厂家id__le16 idProduct; //产品id__le16 bcdDevice; //设备出厂编号__u8  iManufacturer; //描述厂商信息的字符串描述符的索引值__u8  iProduct; //描述产品信息的字串描述符的索引值__u8  iSerialNumber; //描述设备序列号信息的字串描述符的索引值 __u8  bNumConfigurations; //可能的配置描述符的数目
} __attribute__ ((packed));
2.1.4.2 配置描述符
struct usb_config_descriptor {__u8  bLength; //该结构体大小__u8  bDescriptorType;//描述符类型(本结构体中固定为0x02)  __le16 wTotalLength; //该配置下,信息的总长度(包括配置,接口,端点和设备类及厂商定义的描述符)__u8  bNumInterfaces; //接口的个数__u8  bConfigurationValue; //Set_Configuration命令所需要的参数值,用来选定此配置__u8  iConfiguration; //描述该配置的字符串描述的索引值 __u8  bmAttributes;//供电模式的选择  __u8  bMaxPower;//设备从总线提取的最大电流
} __attribute__ ((packed));
2.1.4.3 接口描述符

配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。

struct usb_interface_descriptor {__u8  bLength;		//该结构体大小__u8  bDescriptorType;//接口描述符的类型编号(0x04)__u8  bInterfaceNumber;  //该接口的编号  __u8  bAlternateSetting; //备用的接口描述符编号  __u8  bNumEndpoints; //该接口使用的端点数,不包括端点0  __u8  bInterfaceClass; //接口类__u8  bInterfaceSubClass; //子类__u8  bInterfaceProtocol; //协议__u8  iInterface;//描述此接口的字串描述表的索引值  
} __attribute__ ((packed));
2.1.4.4 端点描述符

端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。

除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {__u8  bLength;        //端点描述符字节数大小(7个字节)__u8  bDescriptorType;//端点描述符类型编号(0x05) __u8  bEndpointAddress; //此描述表所描述的端点的地址、方向 : // bit3~bit0:端点号,bit6~bit4:保留,// bit7:方向,如果是控制端点则忽略,0-输出端点(主机到设备)1-输入端点(设备到主机)__u8  bmAttributes; // 端点特性,bit1~bit0 表示传输类型,其他位保留// 00-控制传输  01-实时传输   10-批量传输 11-中断传输__le16 wMaxPacketSize;  //端点收、发的最大包大小__u8  bInterval; // 中断传输模式中主机查询端点的时间间隔。// 对于实时传输的端点此域必需为1,表示周期为1ms。对于中断传输的端点此域值的范围为1ms到255ms/* NOTE:  these two are _only_ in audio endpoints. *//* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */__u8  bRefresh;__u8  bSynchAddress;
} __attribute__ ((packed));
2.1.4.5 字符串描述符
struct usb_string_descriptor {__u8  bLength;  // 此描述表的字节数(bString域的数值N+2)__u8  bDescriptorType; // 字串描述表类型(此处应为0x03)__le16 wData[1];		/* UTF-16LE encoded */  
} __attribute__ ((packed));
2.1.4.6 人机接口描述符

USB 设备中有一大类就是 HID 设备,即 Human Interface Devices,人机接口设备。这类设备包括鼠标、键盘等,主要用于人与计算机进行交互。 它是 USB 协议最早支持的一种设备类。 HID 设备可以作为低速、全速、高速设备用。由于 HID 设备要求用户输入能得到及时响应,故其传输方式通常采用中断方式。
在 USB 协议中, HID 设备的定义放置在接口描述符中, USB 的设备描述符和配置描述符中不包含 HID 设备的信息。因此,对于某些特定的 HID 设备,可以定义多个接口,只有其中一个接口为 HID 设备类即可。

2.1.4.6 USB描述符的类型值
类型描述符类型值
标准描述符设备描述符0x01
配置描述符0x02
字符串描述符0x03
接口描述符0x04
端点描述符0x05
类描述符集线器类描述符0x29
人机接口类描述符0x21
厂商定义的描述符0xff

2.1.5 USB的数据传输对象

端点,一个USB设备可以有多个端点,和主机间的数据传输称为到设备端点的数据传输。比如说,对于一个U盘,可以细分为两个端点,把数据写到U盘的端点1、从U盘的端点2读取数据。

2.2 USB子系统框架

在这里插入图片描述

三,USB总线驱动程序

3.1 USB Core

初始化内核USB总线及提供USB相关API,为设备驱动和HCD的交互提供桥梁。

Linux启动阶段,通过subsys_initcall会完成USB Core的加载

subsys_initcall(usb_init);                

3.1.1 注册 USB 总线

USB是基于总线-驱动-设备模型的框架,其初始化阶段一个重点任务就是完成USB总线的创建。usb_bus_type提供了驱动和设备匹配的匹配函数,后面注册设备和驱动时会调用到。

retval = bus_register(&usb_bus_type);
if (retval) goto bus_register_failed;struct bus_type usb_bus_type = {.name =		"usb",.match =	usb_device_match,.uevent =	usb_uevent,.suspend =	usb_suspend,.resume =	usb_resume,
};

使用bus_register接口注册USB总线,会创建出两条链表用来分别存放向USB总线注册的设备和驱动。

klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);

3.1.2 注册USB接口驱动

一个设备可以有多个接口,每个接口对应着不同的功能。

// 在usb总线注册USB接口驱动,该驱动被放在usb总线的驱动链表中。
retval = usb_register(&usbfs_driver);
if (retval)goto driver_register_failed;struct usb_driver usbfs_driver = {.name =		"usbfs",.probe =	driver_probe,.disconnect =	driver_disconnect,
};

3.1.3 初始化USB Hub

初始化一个USB设备集线器,用来检测USB设备的连接和断开。

// linux-2.6.22.6/drivers/usb/core/usb.c
retval = usb_hub_init();
if (retval)goto hub_init_failed;// linux-2.6.22.6/drivers/usb/core/hub.c
int usb_hub_init(void)
{// 在usb总线注册一个hub驱动,该驱动被放在usb总线的驱动链表中。if (usb_register(&hub_driver) < 0) {printk(KERN_ERR "%s: can't register hub driver\n",usbcore_name);return -1;}// 创建一个线程,用来处理USB设备的断开、连接等事件khubd_task = kthread_run(hub_thread, NULL, "khubd");if (!IS_ERR(khubd_task))return 0;/* Fall through if kernel_thread failed */usb_deregister(&hub_driver);printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);return -1;
}static struct usb_driver hub_driver = {.name =		"hub",.probe =	hub_probe,.disconnect =	hub_disconnect,.suspend =	hub_suspend,.resume =	hub_resume,.pre_reset =	hub_pre_reset,.post_reset =	hub_post_reset,.ioctl =	hub_ioctl,.id_table =	hub_id_table,.supports_autosuspend =	1,
};static int hub_thread(void *__unused)
{do {/*hub_events()是一个死循环,其任务是解析hub_event_list,来一个一个处理发生在hub上的事件,比如插入,拔出。当hub_event_list事件被处理完后,break跳出while,通过wait_event_freezable使进程进入休眠态。一旦hub_event_list上有新事件需要处理,此处khubd_wait会在事件中断中被唤醒,重新执行到此处的hub_events()来遍历执行事件,完成处理。*/hub_events();wait_event_interruptible(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());try_to_freeze();} while (!kthread_should_stop() || !list_empty(&hub_event_list));pr_debug("%s: khubd exiting\n", usbcore_name);return 0;
}
3.1.3.1 khubd_wait 的唤醒
hub_probe ->    hub_configure ->usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,hub, endpoint->bInterval);// usb_fill_int_urb 接口创建了一个中断类型的 USB请求控制块hub_irq -> kick_khubd(hub) ->wake_up
http://www.xdnf.cn/news/809767.html

相关文章:

  • 网络爬虫爬取常见网站数据
  • 网络安全最新程序员副业接单做私活避坑指南
  • BAT机器学习面试1000题系列
  • Linux系统安装(超级详细教程)
  • 通过修改ajaxFileUpload.js实现多图片动态上传并实现预览
  • 终于有人把TCP协议与UDP协议给搞明白了
  • 2022考研计算机-数据库原理教程1-7章
  • Qt 的发展历史、现状与启示
  • 经纬度编码方法推荐-plus code简介
  • 搭建CA并签发数字证书
  • Cisco 路由过滤之 Route-map Distribute-list
  • 产生随机数的好方法random_shuffle()
  • 中国计量计算机组成原理,中国计量学院计算机组成原理课程设计
  • SecureCRT 常用命令
  • ASP.NET全部教程
  • 什么是UCML
  • 使用SchedulerFactoryBean集成Quarz Job与Spring
  • Web 2.0(维基百科)
  • Oracle之主键(Primary Key)用法详解
  • Android ActivityManagerService总结(一)AMS启动
  • 智慧档案室一体化建设方案
  • Linux makefile详解
  • .NET Framework 3.5 SP1 最终文件下载及离线安装
  • 一站式Shell编程攻略:从入门到精通
  • java中用中国网建提供的SMS短信平台发送短信
  • 计算机网络基础知识(非常详细)从零基础入门到精通,看完这一篇就够了
  • 亚马逊开店详细教程(3)- 分配存款方式
  • Tomcat的webapps文件夹
  • FormulaR1C1是EXCEL中单元格公式输入方法
  • 40个在线杀毒网站