第16章 USB主机、设备与Gadget驱动之Linux USB驱动层次

时间:2024-05-20 16:07:55
本章知识点

        在Linux系统中,提供主机侧和设备侧视角的USB驱动框架,从主机侧看到的USB主机控制器和设备驱动,以及从设备侧看到的设备控制器和Gadget驱动。

1、Linux系统中USB驱动的整体视图,Linux中从主机侧和设备侧看到的USB驱动层次。

2、从主机侧的角度来看,需要编写的USB驱动程序包括主机控制器驱动和设备驱动两类,USB主机控制器驱动程序控制插入其中的USB设备,USB设备驱动程序控制该设备如何作为从设备与主机通信。

(1)分析USB主机控制器驱动的结构并给出Chipidea USB主机驱动实例。

(2)分析USB设备驱动的结构及其设备请求块处理过程,并给出USB键盘驱动实例。

3、从设备侧的角度来看,包含编写USB设备控制器(UDC)驱动和Gadget Function驱动两类,分析UDC和Gadget驱动,并给出Chipidea USB UDC和Loopback Function实例。

4、介绍USB OTG(On The Go)驱动。

16.1 Linux USB驱动层次

16.1.1 主机侧与设备侧USB驱动

    USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备之间的通信,设备不能主动向主机发送任何消息。如图16.1,在Linux系统中,USB驱动可以从两个角度去观察,一个角度是主机侧,一个角度是设备侧。

第16章 USB主机、设备与Gadget驱动之Linux USB驱动层次

图16.1 Linux USB驱动总体结构

分析:

    从主机侧去看,在Linux驱动中,处于USB驱动最底层的是USB主机控制器硬件,在其上运行的是USB主机控制器驱动,在主机控制器上的为USB核心层,再上层为USB设备驱动层插入主机上的U盘、鼠标、USB转串口等设备驱动)。在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动USB主机控制器驱动控制插入其中的USB设备,USB设备驱动控制USB设备如何与主机通信。Linux内核中的USB核心负责USB驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。

    从设备侧去看,Linux内核中USB设备侧驱动程序分为4个层次:USB设备控制器硬件、UDC(USB设备控制器)驱动程序、Gadget Function API和Gadget(小巧机械装置) Function驱动程序。UDC(USB设备控制器)驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。Gadget Function API是UDC驱动程序回调函数的简单包装。Gadget Function驱动程序具体控制USB设备功能的实现,使设备表现出“网络连接”、“打印机”或“USBMass Storage”等特性,Gadget Function驱动程序使用Gadget Function API控制UDC实现上述功能。Gadget Function API把下层的UDC驱动程序和上层的Gadget Function驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。

16.1.2 设备、配置、接口、端点

    在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。

    每个USB设备都提供不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。

    在USB协议中,接口由多个端点组成,代表一个基本的功能,是USB设备驱动程序控制的对象,一个功能复杂的USB设备可以具有多个接口。每个配置中可以有多个接口,而设备接口是端点的汇集。例如,USB扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。

    端点是USB通信的最基本形式,每一个USB设备接口在主机看来是一个端点的集合。主机只能通过端点与设备进行通信,以使用设备的功能。在USB系统中每一个端点都有唯一的地址,这是由设备地址和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。一个USB端点只能在一个方向上承载数据,从主机到设备(称为输出端点)或者从设备到主机(称为输入端点),因此端点可看作是一个单向的管道。端点0通常为控制端点,用于设备初始化参数等。只要设备连接到USB上并且上电,端点0就可以被访问。端点1、2等一般用作数据端点,存放主机与设备之间往来的数据。

    总之,USB设备非常复杂,由许多不同的逻辑单元组成,如图16.2所示,这些单元之间的关系如下:

第16章 USB主机、设备与Gadget驱动之Linux USB驱动层次

图16.2 USB设备、配置、接口和端点

1、设备通常有一个或多个配置;

2、配置通常有一个或多个接口;

3、接口通常有一个或多个设置;

4、接口有零个或多个端点。

这种层次化配置信息在设备中通过一组标准的描述符来描述,如下所示。

设备描述符:关于设备的通用信息,如供应商ID、产品ID和修订ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在Linux内核中,USB设备用usb_device结构体来描述,USB设备描述符定义为usb_device_descriptor结构体,如代码清单16.1所示。

代码清单16.1 usb_device_descriptor结构体

uapi/linux/usb/ch9.h
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
        __u8  bLength; /* 描述符长度 */
        __u8  bDescriptorType;/* 描述符类型编号 */

        __le16 bcdUSB;/* USB版本号 */
        __u8  bDeviceClass;/* USB分配的设备类code */
        __u8  bDeviceSubClass;/* USB分配的子类code */
        __u8  bDeviceProtocol;/* USB分配的协议code */
        __u8  bMaxPacketSize0;/* endpoint0最大包大小 */
        __le16 idVendor;/* 厂商编号 */
        __le16 idProduct; /* 产品编号 */
        __le16 bcdDevice;/* 设备出厂编号 */
        __u8  iManufacturer;/* 描述厂商字符串的索引 */
        __u8  iProduct;/* 描述产品字符串的索引 */
        __u8  iSerialNumber;/* 描述设备***字符串的索引 */
        __u8  bNumConfigurations;/* 可能的配置数量 */
} __attribute__ ((packed));

配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求USB配置在内核中使用usb_host_config结构体描述,而USB配置描述符定义为结构体usb_config_descriptor,如代码清单16.2所示。

uapi/linux/usb/ch9.h

代码清单16.2 usb_config_descriptor结构体

struct usb_config_descriptor {
        __u8  bLength;/* 描述符长度 */
        __u8  bDescriptorType;/* 描述符类型编号 */
        __le16 wTotalLength;/* 配置所返回的所有数据的大小 */
        __u8  bNumInterfaces;/* 配置所支持的接口数 */
        __u8  bConfigurationValue;/* Set_Configuration命令需要的参数值 */
        __u8  iConfiguration;/* 描述该配置的字符串的索引值 */
        __u8  bmAttributes;/* 供电模式的选择 */
        __u8  bMaxPower;/* 设备从总线提取的最大电流 */

} __attribute__ ((packed));

接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。USB接口在内核中使
用usb_interface结构体描述,而USB接口描述符定义为结构体usb_interface_descriptor,如代码清单16.3所

示。

代码清单16.3 usb_interface_descriptor结构体

uapi/linux/usb/ch9.h

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));

端点描述符:端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在Linux内核中,USB端点使用usb_host_endpoint结构体来描述,而USB端点描述符定义为usb_endpoint_descriptor结构体,如代码清单16.4所示。

代码清单16.4 usb_endpoint_descriptor结构体

uapi/linux/usb/ch9.h

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
        __u8  bLength;/* 描述符长度 */
        __u8  bDescriptorType;/* 描述符类型 */

        __u8  bEndpointAddress;/* 端点地址:0~3位是端点号,第7位是方向(0为输出,1为输入) */
        __u8  bmAttributes;/* 端点属性:bit[0:1] 的值为00表示控制,为01表示同步,为02表示批量,为03表示中断 */
        __le16 wMaxPacketSize;/* 本端点接收或发送的最大信息包的大小 */
        __u8  bInterval;     /* 轮询数据传送端点的时间间隔 */
                                    /* 对于批量传送的端点以及控制传送的端点,此域忽略 */
                                   /* 对于同步传送的端点,此域必须为1 */
                                   /* 对于中断传送的端点,此域值的范围为1~255 */

        /* NOTE:  these two are _only_ in audio endpoints. */
        /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
        __u8  bRefresh;
        __u8  bSynchAddress;

} __attribute__ ((packed));

字符串描述符:在其他描述符中会为某些字段提供字符串索引,可被用来检索描述性字符串,可以以多种语言形式提供。字符串描述符是可选的,有的设备有,有的设备没有,字符串描述符对应于usb_string_descriptor结构体,如代码清单16.5所示。

代码清单16.5 usb_string_descriptor结构体

uapi/linux/usb/ch9.h

/* USB_DT_STRING: String descriptor */
struct usb_string_descriptor {
        __u8  bLength;/* 描述符长度 */
        __u8  bDescriptorType;/* 描述符类型 */

        __le16 wData[1];                /* UTF-16LE encoded */

} __attribute__ ((packed));

例如,在Linux上插入一个SanDisk U盘,通过lsusb命令得到与这个U盘相关的描述符,从中可以显示这个U盘包含了一个设备描述符、一个配置描述符、一个接口描述符、批量输入和批量输出两个端点描述符。呈现出来的信息内容直接对应于usb_device_descriptor、usb_config_descriptor、usb_interface_descriptor、usb_endpoint_descriptor、usb_string_descriptor结构体,如下所示:

Bus 001 Device 004: ID 0781:5151 SanDisk Corp.
Device Descriptor:
 bLength              18
 bDescriptorType        1
 bcdUSB             2.00
 bDeviceClass           0 Interface
 bDeviceSubClass         0
 bDeviceProtocol         0
 bMaxPacketSize0        64
 idVendor          0x0781 SanDisk Corp.
 idProduct         0x5151
 bcdDevice           0.10
 iManufacturer          1 SanDisk Corporation
 iProduct              2 Cruzer Micro
 iSerial               3 20060877500A1BE1FDE1

 bNumConfigurations      1


 Configuration Descriptor:
  bLength                 9
  bDescriptorType        2
  wTotalLength          32
  bNumInterfaces         1
  bConfigurationValue     1
  iConfiguration         0
  bmAttributes        0x80

  MaxPower            200mA


  Interface Descriptor:
   bLength               9
   bDescriptorType        4
   bInterfaceNumber       0
   bAlternateSetting      0
   bNumEndpoints         2
   bInterfaceClass        8 Mass Storage
   bInterfaceSubClass     6 SCSI
   bInterfaceProtocol    80 Bulk (Zip)

   iInterface           0


   Endpoint Descriptor:
    bLength              7
    bDescriptorType       5
    bEndpointAddress    0x81  EP 1 IN
    bmAttributes          2
     Transfer Type         Bulk
     Synch Type           none
    wMaxPacketSize      512

    bInterval            0


   Endpoint Descriptor:
    bLength              7
    bDescriptorType       5
    bEndpointAddress    0x01  EP 1 OUT
    bmAttributes          2
     Transfer Type           Bulk
     Synch Type             none
    wMaxPacketSize       512
    bInterval             1
 Language IDs: (length=4)
   0409 English(US)