Linux-USB驱动笔记(四)--USB整体框架

26 篇文章 31 订阅

1、前言

USB基础知识在前三篇笔记中。

Linux-USB驱动笔记一

Linux-USB驱动笔记二

Linux-USB驱动笔记三

2、 USB协议解析

USB 协议中有很多的基础概念,我们来总结一下吧。

之前在基础知识中,我们介绍过设备,配置,接口,端点等概念,它在Linux中有4个对应的结构体来表示。同时也有对应的结构体来描述USB设备。

内核版本:4.20.12

  • USB整体框架如下
    USB驱动分为主机侧和设备侧,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC)。USB核心层向上下提供编程接口,维护整个系统的USB信息,完成热插拔控制,数据传输控制。

在这里插入图片描述

  • 主机侧

从上图看,我们需要实现两个驱动,USB主机控制器驱动和USB设备驱动。

USB主机控制器驱动:控制插入的USB设备

USB设备驱动:控制具体USB设备和主机如何通信

  • 设备侧

设备侧也需要实现两部分驱动,UDC驱动和Gadget Function驱动。

UDC驱动:控制USB设备和主机的通信

Gadget Function驱动:控制USB设备功能的实现

2.1、USB 描述符

顾名思义,USB 描述符就是用来描述 USB 信息的,描述符就是一串按照一定规则构建的字符串,USB 设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如下表所示:

描述符类型名字
Device Descriptor设备描述符1
Configuration Descriptor配置描述符2
String Descriptor字符串描述符3
Interface Descriptor接口字符串4
Endpoint Descriptor端点描述符5

USB设备代码如下:

//表示一个USB设备
struct usb_device {
  int    devnum; //设备号,USB总线上的地址
  char    devpath[16]; //消息中使用的设备ID字符串,例如/port/...
  u32    route; //树拓扑(16进制字符串),用于xHCI
  enum usb_device_state  state; //设备状态
  enum usb_device_speed  speed; //设备速率 high/full/low
  unsigned int    rx_lanes; //正在使用的RX线路数
  unsigned int    tx_lanes; //正在使用的TX线路数
 
    //为了让高速USB设备兼容低速设备
    //通过使用TT(Transaction Translator)--高速USB中的转换电路
  struct usb_tt  *tt; //事务转换信息,用于低速/全速设备,高速HUB
  int    ttport; //tt hub上的设备口

  unsigned int toggle[2]; //每个端点使用一个bit表示输入输出状态
                           //([0] = 输入, [1] = 输出)
  struct usb_device *parent; //父hub,如果是roothub,就是NULL
  struct usb_bus *bus;       //设备所在的总线
  struct usb_host_endpoint ep0; //端点0数据 (默认用来控制)

  struct device dev; //设备

  struct usb_device_descriptor descriptor; //usb设备描述符
  struct usb_host_bos *bos; //USB设备BOS描述符集
  struct usb_host_config *config; //所有的设备配置

  struct usb_host_config *actconfig; //当前激活的配置
  struct usb_host_endpoint *ep_in[16]; //输入端点数组
  struct usb_host_endpoint *ep_out[16];//输出端点数组

  char **rawdescriptors; //每个配置的源描述符

  unsigned short bus_mA; //总线提供的电流
  u8 portnum;  //父port号
  u8 level;    //USB HUB上一级的数量

  unsigned can_submit:1;    //URB可以被提交
  unsigned persist_enabled:1;
  unsigned have_langid:1;
  unsigned authorized:1;
  unsigned authenticated:1;
  unsigned wusb:1;
  unsigned lpm_capable:1;
  unsigned usb2_hw_lpm_capable:1;
  unsigned usb2_hw_lpm_besl_capable:1;
  unsigned usb2_hw_lpm_enabled:1;
  unsigned usb2_hw_lpm_allowed:1;
  unsigned usb3_lpm_u1_enabled:1;
  unsigned usb3_lpm_u2_enabled:1;
  int string_langid;

  /* static strings from the device */
  char *product; //产品描述字符串
  char *manufacturer; //厂家描述字符串
  char *serial; //序列号字符串

  struct list_head filelist; //该设备打开的usb文件系统文件

  int maxchild;  //hub可以连接的最大port数

  u32 quirks;
  atomic_t urbnum; //整个设备URB提交总数

  unsigned long active_duration; //激活的总时间

#ifdef CONFIG_PM   //电源管理
  unsigned long connect_time;

  unsigned do_remote_wakeup:1;
  unsigned reset_resume:1;
  unsigned port_is_suspended:1;
#endif
  struct wusb_dev *wusb_dev;
  int slot_id;
  enum usb_device_removable removable;
  struct usb2_lpm_parameters l1_params;
  struct usb3_lpm_parameters u1_params;
  struct usb3_lpm_parameters u2_params;
  unsigned lpm_disable_count;

  u16 hub_delay;
};

为了更清晰的了解设备,配置,接口,端点等结构体信息,我在Ubuntu上插入一个U盘,然后使用lsusb -v去查看该usb设备的设备描述符等信息。
在这里插入图片描述

2.1.1、设备描述符

设备描述符用于描述 USB 设备的一般信息,USB 设备只有一个设备描述符。设备描述符里面记录了设备的 USB 版本号、设备类型、VID(厂商 ID)、PID(产品 ID)、设备序列号等。设备描述符结构如下表所示:

偏移大小(B)值类型描述
0bLength1数字此设备描述符长度,18 个字节
1bDescriptorType1常量描述符类型,为 0X01
2bcdUSB2BCD 码USB 版本号
4bDeviceClass1设备类
5bDeviceSubClass1子类设备子类
6bDevicePortocol1协议设备协议
7bMaxPacketSize01数字端点 0 的最大包长度
8idVendor2ID厂商 ID
10idProduct2ID产品 ID
12bcdDevice2BCD 码设备版本号
14iManufacturer1索引厂商信息字符串描述符索引值
15iProduct1索引产品信息字符串描述符索引值
16iSerialNumber1索引产品序列号字符串描述符索引值
17bNumConfigurations1索引可能的配置描述符数目

具体代码如下:

//\include\uapi\linux\usb\ch9.h
struct usb_device_descriptor {
  __u8  bLength;         //描述符长度
  __u8  bDescriptorType; //描述符类型

  __le16 bcdUSB;         //USB总线版本号
  __u8  bDeviceClass;    //由USB-IF分配的设备类代码
  __u8  bDeviceSubClass; //由USB-IF分配的USB子类代码
  __u8  bDeviceProtocol; //由USB-IF分配协议代码
  __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.2、配置描述符

设备描述符的 bNumConfigurations 域定义了一个 USB 设备的配置描述符数量,一个 USB设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、供电信息等,配置描述符结构如下表所示:

偏移大小(B)值类型描述
0bLength1数字此配置描述符长度,9 个字节
1bDescriptorType1常量配置描述符类型,为 0X02
2wTotalLength2数字整个配置信息总长度(包括配置、接口、端点、设备类和厂家定义的描述
4bNumInterfaces1数字此配置所支持的接口数
5bConfigurationValue1数字该配置的值,一个设备支持多种配置,通过配置值来区分不同的配置。
6bConfiguration1数字描述此配置的字符串描述索引
7bmAttributes1数字该设备的属性:D7:保留D6:自给电源D5:远程唤醒D4:0:保留
8bMaxPower1数字此配置下所需的总线电流(单位 2mA)

具体代码如下:


struct usb_config_descriptor {
  __u8  bLength;          //描述符长度
  __u8  bDescriptorType;  //描述符类型

  __le16 wTotalLength;    //配置所返回的所有数据的大小
  __u8  bNumInterfaces;   //配置支持的接口数量
  __u8  bConfigurationValue; //当使用SetConfiguration和GetConfiguration请求时所指定的配置索引值
  __u8  iConfiguration;   //描述配置的字符串描述符索引
  __u8  bmAttributes;     //供电模式选择
  __u8  bMaxPower;        //最大功耗
} __attribute__ ((packed));

2.1.3、接口描述符

配置描述符中指定了该配置下的接口数量,配置可以提供一个或多个接口,接口描述符用于描述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口描述符结构如下表所示:

偏移大小(B)值类型描述
0bLength1数字此接口描述符长度,9 个字节
1bDescriptorType1常量描述符类型,为 0X04
2bInterfaceNumber1数字当前接口编号,从 0 开始
3bAlternateSetting1数字当前接口备用编号
4bNumEndpoints1数字当前接口的端点数量
5bInterfaceClass1当前接口所属的类
6bInterfaceSubClass1子类当前接口所属的子类
7bInterfaceProtocol1协议当前接口所使用的协议
8iInterface1索引当前接口字符串的索引值

具体代码如下:

struct usb_interface_descriptor {
  __u8  bLength;          //描述符长度
  __u8  bDescriptorType;  //描述符类型

  __u8  bInterfaceNumber; //接口编号
  __u8  bAlternateSetting;//备用的接口描述符编号
  __u8  bNumEndpoints;    //端点数量,不包括端点0
  __u8  bInterfaceClass;  //接口类型
  __u8  bInterfaceSubClass;//接口子类型
  __u8  bInterfaceProtocol;//接口协议
  __u8  iInterface;        //描述该接口的字符串描述符索引
} __attribute__ ((packed));

2.1.4、端点描述符

接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端点 0 是双向端口,其他的端口都是单向的。端点描述符描述了树传输类型、方向、数据包大小、端点号等信息,端点描述符结构如下表所示:

偏移大小(B)值类型描述
0bLength1数字此端点描述符长度,7 个字节
1bDescriptorType1常量描述符类型,为 0X05
2bEndpointAddress1数字端点地址和方向:bit3:0:端点号 bit6:4:保留,为零。bit7:方向,0 输出端点(主机到设备),1 输入端点(设备到主机)
3bmAttributes1数字端点属性,bit1:0 表示传输类型:00:控制传输01:同步传输10:批量传输11:中断传输其他位保留
4wMaxPacketSize2数字端点能发送或接收的最大数据包长度
6bInterval1子类端点数据传输中周期时间间隙值,此域对于批量传输和控制传输无效,同步传输的话此域必须为 1ms,中断传输此域可以设置 1ms~255ms。

具体代码如下:


struct usb_endpoint_descriptor {
  __u8  bLength;          //描述符长度
  __u8  bDescriptorType;  //描述符类型

  __u8  bEndpointAddress; //端点地址, bit7是方向(0为输出,1为输入),bit0~3为端点号
  __u8  bmAttributes;     //端点属性
  __le16 wMaxPacketSize;  //端点所支持最大数据包的长度
  __u8  bInterval;        //端点数据传输的访问时间间隔

  /* 下面两个只用在Audio端点 */
  /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
  __u8  bRefresh;         //刷新
  __u8  bSynchAddress;    //同步地址
} __attribute__ ((packed));

2.1.5、字符串描述符

字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都必须为 0,字符串描述符结构如下表所示:

偏移大小(B)值类型描述
0bLength1N+2此字符串描述符长度
1bDescriptorType1常量字符串描述符类型,为 0X03
2wLANGID[0]2数字语言标识码 0
2数字
NwLANGID[x]2数字语言标识码 x

wLANGID[0]~wLANGID[x] 指 明 了 设 备 支 持 的 语 言,具 体 含 义 要 查 阅 文 档《 USB_LANGIDs.pdf 》

主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字符串索引值和语言。设备返回 Unicode 编码的字符串描述符,结构如表所示:

偏移大小(B)值类型描述
0bLength1N+2此字符串描述符长度
1bDescriptorType1常量字符串描述符类型,为 0X03
2bStringN数字Unicode 编码的字符串

具体代码如下:

struct usb_string_descriptor {
  __u8  bLength;          //描述符长度
  __u8  bDescriptorType;  //描述符类型

  __le16 wData[1];    /* UTF-16LE encoded */
} __attribute__ ((packed));
  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值