Linux USB子系统(1)-设备、驱动、框架

来源:互联网 发布:mac软件下载安装 编辑:程序博客网 时间:2024/04/30 19:35

Linux USB子系统框架


内核版本:linux-3.4.2

1. Linux USB驱动层次

USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,设备不能主动向主机发送任何消息。如图:

linux_usb_driver

1.1 主机侧

从主机侧去看,在Linux驱动中,处于USB驱动最底层的是USB主机控制器硬件,在其上的是USB主机控制器驱动,在主机控制器驱动上的为USB核心层,再上层为USB设备驱动层。因此,在主机侧的层级结构中,要实现的USB驱动包括:USB主机控制器驱动和USB设备驱动。
USB核心层向上为USB设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口,维护真个系统的USB设备信息,完成设备热插拔控制、总线数据传输控制等。
USB设备驱动负责驱动具体的设备,例如U盘,鼠标等设备。USB设备驱动既可以注册成某个类型的设备驱动,例如输入子系统,此时的主设备号依据具体的子系统而定;又可以作为一个独立的USB设备注册进系统,这时的主设备号是180(USB_MAJOR)。

USB主机控制器驱动的代码位于:driver/usb/host,根据具体的硬件实现一个该硬件对应的主机控制器驱动的文件。
USB核心层代码位于:driver/usb/core。
USB设备驱动代码依据具体的设备放在对应的目录下。

1.2 设备侧

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

2.设备、配置、接口、端点、端点0

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

2.1 设备

一个设备里包含了不同级别的配置,可以有一个或者多个配置。设备描述符描述了这个设备。
在linux中,结构体usb_device_descriptor对应于协议中的设备描述符。

struct usb_device_descriptor {    __u8  bLength;                           /* 描述符长度 */    __u8  bDescriptorType;                   /* 描述符类型 设备描述符类型值是1 */    __le16 bcdUSB;                           /* USB版本号 */    __u8  bDeviceClass;                      /* USB分配的设备类code */    __u8  bDeviceSubClass;                   /* USB分配的子类code */    __u8  bDeviceProtocol;                   /* USB分配的协议code */    __u8  bMaxPacketSize0;                   /* 端点0最大包大小 */    __le16 idVendor;                         /* 厂商编号 */    __le16 idProduct;                        /* 产品编号 */    __le16 bcdDevice;                        /* 设备出厂编号 */    __u8  iManufacturer;                     /* 描述厂商字符串的索引 */    __u8  iProduct;                          /* 描述产品字符串的索引 */    __u8  iSerialNumber;                     /* 描述设备序列号字符串的索引 */    __u8  bNumConfigurations;                /* 可能的配置数量 */} __attribute__ ((packed));

2.2 配置

一个USB设备具有一个或多个配置,不同的配置使设备表现出不同的功能组合。一个配置下有一个或多个接口。
在linux中,结构体usb_config_descriptor对应于协议中的配置描述符。

struct usb_config_descriptor {    __u8  bLength;                            /* 描述符长度 */    __u8  bDescriptorType;                    /* 描述符类型 配置描述符类型值是2 */    __le16 wTotalLength;                      /* 配置下面所有描述符的长度,包括这个配置描述符 */    __u8  bNumInterfaces;                     /* 配置所支持的接口数 */    __u8  bConfigurationValue;                /* 配置值 */    __u8  iConfiguration;                     /* 描述该配置的字符串的索引值 */    __u8  bmAttributes;                       /* 供电模式的选择 */    __u8  bMaxPower;                          /* 设备从总线提取的最大电流 */} __attribute__ ((packed));

2.3 接口

一个接口是由几个端点组成,代表了一个基本的功能,是USB设备驱动程序控制的对象。一个设备中的某个配置有多个接口表示多种功能,例如一个USB声卡有两个接口,一个用来播放声音,一个用来录音。每个接口都有一个或者多个设置,表示这个功能下的不同参数。
在linux中,结构体usb_interface_descriptor对应于协议中的接口描述符。

struct usb_interface_descriptor {    __u8  bLength;                            /* 描述符长度 */    __u8  bDescriptorType;                    /* 描述符类型 接口描述符类型值是4 */    __u8  bInterfaceNumber;                   /* 接口的编号,相同的编号代表相同的功能接口 */    __u8  bAlternateSetting;                  /* 接口的设置的编号,同一个接口可以有一个或者多个设置代表该接口下不同的参数 */    __u8  bNumEndpoints;                      /* 该接口下的端点数,不包括端点0 */    __u8  bInterfaceClass;                    /* 接口类型 */    __u8  bInterfaceSubClass;                 /* 接口子类型 */    __u8  bInterfaceProtocol;                 /* 接口遵循的协议 */    __u8  iInterface;                         /* 描述该接口的字符串索引值 */} __attribute__ ((packed));

2.4 端点(不包括端点0)

端点是USB通信的最基本形式,每一个USB设备接口在主机看来就是一个端点的集合。在USB系统中每一个端点都有唯一的地址,这是由设备地址和端点号给出的。一个端点只能在一个方向传输数据,从主机到设备称为输出端点,从设备到主机称为输入端点,端点可以看作是一个单项的管道。
在linux中,结构体usb_interface_descriptor对应于协议中的设备描述符。

struct usb_endpoint_descriptor {    __u8  bLength;                            /* 描述符长度 */    __u8  bDescriptorType;                    /* 描述符类型 端点描述符类型值是5 */    __u8  bEndpointAddress;                   /* 端点地址:0~3位是端点号,第7位是方向(0为输出,1为输入) */    __u8  bmAttributes;                       /* 端点属性:bit[0:1]的值为00表示控制,为01表示同步,为02表示批量,为03表示中断 */    __le16 wMaxPacketSize;                    /* 本端点接受或发送的最大信息包的大小 */    __u8  bInterval;                          /* 轮训数据传送端点的时间间隔,不同类型的端点代表了不同的含义 */    /* NOTE:  these two are _only_ in audio endpoints. */    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */    __u8  bRefresh;    __u8  bSynchAddress;} __attribute__ ((packed));

2.5 端点0

端点0是个是个特殊的端点,通常为控制端点,用于设备初始化参数等。只要设备连接到USB上并且上电,端点0就可以被访问。端点0的包大小在设备描述符中。

设备、配置、接口、端点的关系图如下:
这里写图片描述

3. SOC上的USB主机控制器和驱动

SOC上的USB主机控制器并没有像PC上的主机控制器是挂在PCI总线上,因此是作为一个platform设备存在,那么硬件部分就会是一个platform_device的结构体,里面包含了主机控制器的中断号、控制器地址等信息,这是板级相关的内容,一般会包含在arch/arm/下板级相关的文件中。而主机控制器的驱动,则会是一个platform设备的驱动,是一个platform_driver的结构体。所以当platform设备和platform驱动匹配到的时候,会执行platform中的probe函数,而一般在这个函数中应该去注册一个主机控制器的驱动。

每一个主机控制器都会集成一个root hub,而在USB核心层,每当添加一个主机控制器驱动的时候,也会向USB总线添加一个root hub的设备(这里所说的总线,是usb_bus_type,属于设备模型的虚拟的总线,并不是物理上的一个USB总线,关于USB物理上的总线后面说),这个设备是一个usb_device结构体的实例。

root hub设备是作为一个USB设备存在于USB系统中的,这个USB设备与驱动的匹配过程大概是这样的:
在usb子系统初始化的时候会注册一个hub的驱动(hub_driver)和一个USB设备驱动(usb_generic_driver),当root hub的对象注册到总线后首先会匹配到usb_generic_driver驱动,这时会调用usb_generic_driver中的probe函数,也就是generic_probe()函数,在generic_probe()函数中会配置root hub,然后再分配USB接口设备,将USB接口设备注册到总线,此时才会匹配到hub_driver驱动,然后调用hub_driver中的probe函数,也就是hub_probe()函数,在hub_probe()函数中会为hub的这个接口下的中断端点设置urb,并且会设置一个回调函数hub_irq()

关于这一段中platform的设备匹配和root hub和hub驱动的匹配过程大概如下图所示:
主机控制器驱动和roothub注册过程

4. USB子系统中的设备和驱动

从上图可以看出,注册到总线的设备有两种类型,一种是struct usb_device,另一种是struct usb_interface, 这两个结构体都内嵌了struct device结构体,那么两种设备是如何区分的。在usb_alloc_dev()函数分配一个usb_device结构体后,将成员device中的type指针指向了 usb_device_type变量;而在向总线添加usb_interface的地方将usb_interfacedevice成员的type指针指向了usb_if_device_type变量,而总线的match函数就是通过这个来分辨这个设备是USB接口设备还是USB设备。

这里我将一个物理存在的USB设备称为USB设备,而USB接口设备是指逻辑上一个USB接口的设备,前面说接口的时候说过“一个接口代表了一个基本的功能”,这里的USB接口设备就是这个基本的功能设备。

上图中的hub_driver类型是struct usb_driverusb_generic_driver类型是struct usb_device_driver,从匹配关系可以看出,USB设备匹配的是struct usb_device_driver类型的驱动,USB接口设备匹配的是struct usb_driver类型的驱动。这两个驱动中都内嵌了一个struct usbdrv_wrap结构体,从名字看是个wrap层,这个结构体:

struct usbdrv_wrap {    struct device_driver driver;    int for_devices;};

这里的for_devices成员就是用来分辨USB设备驱动和USB接口驱动的,当for_devices不是0的时候代表这是一个USB设备驱动,当for_devices是0的时候代表这是一个USB接口驱动。

所以在USB子系统中有两种设备:USB设备 和 USB接口设备
有两种驱动:USB设备驱动 和 USB接口驱动

通过总线的match函数,可以看出,USB设备只能和USB设备驱动匹配,而USB接口设备只能和USB接口驱动匹配。而USB设备驱动只有一个,那就是usb_generic_driver,USB接口驱动是对应于设备的功能开发的不同的驱动程序。

5. USB物理设备的插入及USB设备和USB接口设备的注册和驱动匹配

USB物理设备插入及USB设备和USB接口设备的注册和匹配的过程如图所示:

USB设备和USB接口设备的注册和匹配的过程

如图所示,xxx_driver就是实现的接口功能的驱动,可以是一个输入设备驱动,也可以是一个U盘驱动,也可以是一个普通的USB字符设备。

0 0