25.2  USB驱动程序框架
Linux内核提供了完整的USB驱动程序框架。USB总线采用树形结构,在一条总线上只能有唯一的主机设备。Linux内核从主机和设备两个角度观察USB总线结构。本节介绍Linux内核USB驱动程序框架。
25.2.1  Linux内核USB驱动框架
图25-2是Linux内核从主机和设备两个角度观察USB总线结构的示意图。
从图25-2中可以看出,Linux内核USB驱动是按照主机驱动和设备驱动两套体系实现的,下面介绍两套体系的结构和特点。
1.基本结构
图25-2的左侧是主机驱动结构。主机驱动的最底层是USB主机控制器,提供了OHCI/EHCI/UHCI这3种类型的总线控制功能。在USB控制器的上一层是主机控制器的驱动,分别对应OHCI/EHCI/UHCI这3种类型的总线接口。USB核心部分连接了USB控制器驱动和设备驱动,是两者之间的转换接口。USB设备驱动层提供了各种设备的驱动程序。
USB主机部分的设计结构完全是从USB总线特点出发的。在USB总线上可以连接各种不同类型的设备,
包括字符设备、块设备和网络设备。所有类型的USB设备都是用相同的电气接口,使用的传输协议也基本相同。向用户提供某种特定类型的USB设备时,需要处理USB总线协议。内核完成所有的USB总线协议处理,并且向用户提供编程接口。
图25-2  Linux内核USB总线结构
图25-2右侧是设备驱动结构。与USB主机类似,USB设备提供了相同的层次结构与之对应。但是在USB设备一侧使用名为Gadget API的结构作为核心。Gadget API是Linux内核实现的对应USB设备的核心结构。Gadget API屏蔽了USB设备控制器的细节,控制具体的USB设备实现。
2.设备
每个USB设备提供了不同级别的配置信息。一个USB设备可以包含一个或多个配置,不同的配置使设备表现出不同的特点。其中,设备的配置是通过接口组成的。Linux内核定义了USB设备描述结构如下:
struct usb_device_descriptor {
__u8  bLength;              // 设备描述符长度
__u8  bDescriptorType;      // 设备类型
__le16 bcdUSB;              // USB版本号(使用BCD编码)
__u8  bDeviceClass;        // USB设备类型
__u8  bDeviceSubClass;      // USB设备子类型
__u8  bDeviceProtocol;      // USB设备协议号
__u8  bMaxPacketSize0;      // 传输数据的最大包长
__le16 idVendor;            // 厂商编号
__le16 idProduct;          // 产品编号
__le16 bcdDevice;          // 设备出厂号
__u8  iManufacturer;    // 厂商字符串索引
__u8  iProduct;            // 产品字符串索引
__u8  iSerialNumber;    // 产品序列号索引
__u8  bNumConfigurations;  // 最大的配置数量
} __attribute__ ((packed));
从usb_device_descriptor结构定义看出,一个设备描述符定义了与USB设备有关的所有信息。
3.接口
在USB体系中,接口是由多个端点组成的。一个接口代表一个基本的功能,是USB设备驱动程序控制的对象。一个USB设备最少有一个接口,功能复杂的USB设备可以有多个接口。接口描述定义如下:
struct usb_interface_descriptor {
__u8  bLength;              // 描述符长度
__u8  bDescriptorType;      // 描述符类型
__u8  bInterfaceNumber;    // 接口编号
__u8  bAlternateSetting; // 备用接口编号
__u8  bNumEndpoints;    // 端点数量
__u8  bInterfaceClass;      // 接口类型
__u8  bInterfaceSubClass;  // 接口子类型
__u8  bInterfaceProtocol;  // 接口使用的协议
__u8  iInterface;          // 接口索引字符串数值
} __attribute__ ((packed));
4.端点
端点是USB总线通信的基本形式,每个USB设备接口可以认为是端点的集合。主机只能通过端点与设备通信。USB体系结构规定每个端点都有一个唯一的地址,由设备地址和端点号决定端点地址。端点还包括了与主机通信用到的属性,如传输方式、总线访问频率、带宽和端点号等。端点的通信是单向的,通过端点传输的数据只能是从主机到设备或者从设备到主机。端点定义描述如下:
struct usb_endpoint_descriptor {
__u8  bLength;              // 描述符长度
__u8  bDescriptorType;      // 描述符类型
__u8  bEndpointAddress;    // 端点地址
__u8  bmAttributes;        // 端点属性
__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));
5.配置
配置是一个接口的集合。Linux内核配置的定义如下:
struct usb_config_descriptor {
__u8  bLength;              // 描述符长度
__u8  bDescriptorType;      // 描述符类型
__le16 wTotalLength;        // 配置返回数据长度
__u8  bNumInterfaces;      // 最大接口数
__u8  bConfigurationValue;  // 配置参数值
linux内核设计与实现 pdf
__u8  iConfiguration;      // 配置描述字符串索引
__u8  bmAttributes;        // 供电模式
__u8  bMaxPower;        // 接口的最大电流
} __attribute__ ((packed));
配置描述符结构定义了配置的基本属性和接口数量等信息。
25.2.2  主机驱动结构
USB主机控制器有3种类型:
● OHCI:英文全称是Open Host Controller Interface,是用于SiS和Ali芯片组的USB控制器。
● UHCI:英文全称是Universal Host Controller Interface,用于Intel和AMD芯片组的USB控制器。UHCI类型的控制器比OHCI控制器硬件结构要简单,但是需要额外的驱动支持,因此从理论上说速度要慢。
● EHCI:是USB 2.0规范提出的一种控制器标准,可以兼容UHCI和OHCI。
1.USB主机控制器驱动
Linux内核使用usb_hcd结构描述USB主机控制器驱动。usb_hcd结构描述了USB主机控制器的硬件信
息、状态和操作函数,定义如下:
struct usb_hcd {    /
* usb_bus.hcpriv points to this
*
/
/
*
*
housekeeping                      // 控制器基本信息*
/
struct usb_bus      self;      /
* hcd is-a bus
*
/
const char
*product_desc;  /
*
product/vendor string
*
/          // 厂商名称字符串
char            irq_descr[24];  /
* driver + bus #
*
/                  // 驱动和总线类型
struct timer_list  rh_timer;  /
* drives root-hub polling
*
/          // 根hub轮询时间间隔
struct urb
*status_urb; /
*
the current status urb
*
/
            // 当前urb状态
/
*
*
hardware info/state              // 硬件信息和状态*
/
const struct hc_driver
*driver; /
*
hw-specific hooks
*
/
            // 控制器驱动使用的回调函数
/
* Flags that need to be manipulated atomically
*
/
unsigned long      flags;
#define HCD_FLAG_HW_ACCESSIBLE  0x00000001
#define HCD_FLAG_SAW_IRQ    0x00000002
unsigned        rh_registered:1;/
*
is root hub registered?
*
/                                                            // 是否注册根hub
/
*
The next flag is a stopgap, to be removed when all the HCDs
* support the new root-hub polling mechanism.
*
/
unsigned        uses_new_polling:1;            // 是否允许轮询根hub状态
unsigned        poll_rh:1;  /
* poll for rh status?
*
/
unsigned        poll_pending:1; /
* status has changed?
*
/              // 状态是否改变
int        irq;        /
* irq allocated
*
/      // 控制器的中断请求号
void __iomem
*regs;      /
*
device memory/io
*
/                // 控制器使用的内存和I/O
u64        rsrc_start; /
* memory/io resource start
*
/                // 控制器使用的内存和I/O起始地址
u64        rsrc_len;  /
* memory/io resource length
*
/            // 控制器使用的内存和I/O资源长度
unsigned        power_budget;  /
* in mA, 0 = no limit
*
/
#define HCD_BUFFER_POOLS    4
struct dma_pool
*
pool [HCD_BUFFER_POOLS];
int        state;
#  define  __ACTIVE        0x01
#  define  __SUSPEND      0x04
#  define  __TRANSIENT    0x80
#  define  HC_STATE_HALT      0
#  define  HC_STATE_RUNNING    (__ACTIVE)
#  define  HC_STATE_QUIESCING  (__SUSPEND|__TRANSIENT|__ACTIVE) #  define  HC_STATE_RESUMING  (__SUSPEND|__TRANSIENT)
#  define  HC_STATE_SUSPENDED  (__SUSPEND)
#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
/
*
more shared queuing code would be good; it should support
*
smarter scheduling, handle transaction translators, etc;
*
input size of periodic table to an interrupt scheduler.
*
(ohci 32, uhci 1024, ehci 256/512/1024).
*
/
/
*
The HC driver's private data is stored at the end of
*
this structure.
*
/
unsigned long hcd_priv[0]
__attribute__ ((aligned (sizeof(unsigned long))));
};
2.OHCI控制器驱动
usb_hcd结构可以理解为一个通用的USB控制器描述结构。OHCI主机控制器是usb_hcd结构的具体实现,内核使用ohci_hcd 结构描述OHCI主机控制器,定义如下:
struct ohci_hcd {
spinlock_t      lock;
/
*
*
I/O memory used to communicate with the HC (dma-consistent)        // 用于HC通信的I/O内存地址
*
/
struct ohci_regs __iomem
*
regs;
/
*
*
main memory used to communicate with the HC (dma-consistent).      // 用于HC通行的主内存地址
*
hcd adds to schedule for a live hc any time, but removals finish
*
only at the start of the next frame.
*
/
struct ohci_hcca
*
hcca;
dma_addr_t      hcca_dma;
struct ed
*ed_rm_list;    /
*
to be removed
*
/                  // 将被移除列表
struct ed
*ed_bulktail;      /
*
last in bulk list
*
/            // 列表最后一项
struct ed
*ed_controltail; /
*
last in ctrl list
*
/              // 控制列表最后一项
struct ed
*periodic [NUM_INTS];  /
*
shadow int_table
*
/
/
*
*
OTG controllers and transceivers need software interaction;
*
other external transceivers should be software-transparent
*
/
struct otg_transceiver
*
transceiver;
/
*
*
memory management for queue data structures                              // 内存管理队列使用的数据结构*
/
struct dma_pool
*
td_cache;
struct dma_pool
*
ed_cache;
struct td
*
td_hash [TD_HASH_SIZE];
struct list_head    pending;
/
*
*
driver state
*
/
int        num_ports;
int        load [NUM_INTS];
u32            hc_control; /
* copy of hc control reg
*
/// HC控制寄存器复制
unsigned long      next_statechange;  /
* suspend/resume
*
/
                      // 挂起/恢复
u32        fminterval;    /
* saved register
*
/ // 保存的寄存器
struct notifier_block  reboot_notifier;
unsigned long      flags;      /
* for HC bugs
*
/
/ *以下是各厂家芯片ID定义
*
/
#define OHCI_QUIRK_AMD756  0x01            /
* erratum #4
*
/
#define OHCI_QUIRK_SUPERIO  0x02            /
* natsemi
*
/
#define OHCI_QUIRK_INITRESET    0x04        /
* SiS, OPTi, ...
*
/
#define OHCI_BIG_ENDIAN    0x08            /
* big endian HC
*
/
#define OHCI_QUIRK_ZFMICRO  0x10            /
* Compaq ZFMicro chipset
*
/
/
/ 芯片的初始化逻辑里也同样会有怪异的Bug
};
OHCI主机控制器是嵌入式系统最常用的一种USB主机控制器。
25.2.3  设备驱动结构
USB协议规定了许多种USB设备类型。Linux内核实现了音频设备、通信设备、人机接口、存储设备、电源设备、打印设备等几种USB设备类。
1.基本概念
Linux内核实现的USB设备类驱动都是针对通用的设备类型设计的。如存储设备类,只要USB存储设备是按照标准的USB存储设备规范实现的,就可以直接被内核USB存储设备驱动程序驱动。如果一个USB设备是非标准的,则需要编写对应设备的驱动程序。
Linux内核为不同的USB设备分配了设备号。在内核中还提供了一个usbfs文件系统,通过usbfs文件系统,用户可以方便地使用USB设备。为了使用usbfs,使用root权限在控制台输入“mount –t usbfs none /proc/bus/usb”,可以加载USB文件系统到内核。在插入一个USB设备的时候,内核会试图加载对应的驱动程序。
2.设备驱动结构
内核使用usb_driver结构体描述USB设备驱动,定义如下:
struct usb_driver {
const char
*
name;
int (
*probe) (struct usb_interface
*
intf,
const struct usb_device_id
*
id);          // 探测函数
void (
*disconnect) (struct usb_interface
*
intf);    // 断开连接函数
int (
*ioctl) (struct usb_interface
*
intf, unsigned int code,  void
*
buf);            // I/O控制函数
int (
*suspend) (struct usb_interface
*
intf, pm_message_t message);                    // 挂起函数
int (
*resume) (struct usb_interface
*
intf);          // 恢复函数
void (
*pre_reset) (struct usb_interface
*
intf);
void (
*post_reset) (struct usb_interface
*
intf);
const struct usb_device_id
*
id_table;
struct usb_dynids dynids;
struct device_driver driver;
unsigned int no_dynamic_id:1;
};
实现一个USB设备的驱动主要是实现probe()和disconnect()函数接口。probe()函数在插入USB设备的时候被调用,disconnect()函数在拔出USB设备的时候被调用。在25.2.4节中详细讲解USB设备驱动程序框架。
3.USB请求块
USB请求块(USB request block,urb)的功能类似于网络设备中的sk_buff,用于描述USB设备与主机通信的基本数据结构。urb结构在内核中定义如下:
struct urb
{
/
* private: usb core and host controller only fields in the urb
*
/  // 私有数据,仅供USB核心和主机控制器使用
struct kref kref;      /
* reference count of the URB
*
/                  // urb引用计数

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。