在
USB 设备的逻辑组织中,包含设备、配置、接口和端点
4 个层次
每个USB
设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。
在
USB 协议中,接口由多个端点组成,代表一个基本的功能,是
USB 设备驱动程序控制的对象,一个功能复杂的USB
设备可以具有多个接口。每个配置中可以有多个接口,而设备接口是端点的汇集(
collection)。例如,
USB
扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。
端点是
USB 通信的最基本形式,每一个
USB 设备接口在主机看来就是一个端点的集合,主机只能通过端点与设备进行通信,以使用设备的功能。在USB
系统中每一个端点都有惟一的地址,这是由设备地址和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。一个USB
端点只能在一个方向承载数据,或者从主机到设备(称为输出端点),或者从设备到主机(称为输入端点),因此端点可看作一个单向的管道。端点0
通常为控制端点,用于设备初始化参数等。只要设备连接到
USB 上并且上电端点
0 就可以被访问。端点1、2
等一般用作数据端点,存放主机与设备间往来的数据。
设备通常有一个或多个配置;
配置通常有一个或多个接口;
接口通常有一个或多个设置;
接口有零或多个端点。
设备结构体:
代表了一个插入的USB设备,在内核使用数据结构
struct usb_device来描述整个USB设备。(include/linux/usb.h)
struct usb_device {
int devnum; //设备号,是在USB总线的地址
char devpath [16]; //用于消息的设备ID字符串
enum usb_device_state state; //设备状态:已配置、未连接等等
enum usb_device_speed speed; //设备速度:高速、全速、低速或错误
struct usb_tt *tt; //处理传输者信息;用于低速、全速设备和高速HUB
int ttport; //位于tt HUB的设备口
unsigned int toggle[2]; //每个端点的占一位,表明端点的方向([0] = IN, [1] = OUT)
struct usb_device *parent; //上一级HUB指针
struct usb_bus *bus; //总线指针
struct usb_host_endpoint ep0; //端点0数据
struct device dev; //一般的设备接口数据结构
struct usb_device_descriptor descriptor; //USB设备描述符
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; //每个配置的raw描述符
unsigned short bus_mA; //可使用的总线电流
u8 portnum;//父端口号
u8 level; //USB HUB的层数
unsigned can_submit:1; //URB可被提交标志
unsigned discon_suspended:1; //暂停时断开标志
unsigned persist_enabled:1; //USB_PERSIST使能标志
unsigned have_langid:1; //string_langid存在标志
unsigned authorized:1;
unsigned authenticated:1;
unsigned wusb:1; //无线USB标志
int string_langid; //字符串语言ID
/* static strings from the device */ //设备的静态字符串
char *product; //产品名
char *manufacturer; //厂商名
char *serial; //产品串号
struct list_head filelist; //此设备打开的usbfs文件
#ifdef CONFIG_USB_DEVICE_CLASS
struct device *usb_classdev; //用户空间访问的为usbfs设备创建的USB类设备
#endif
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; //设备的usbfs入口
#endif
int maxchild; //(若为HUB)接口数
struct usb_device *children[USB_MAXCHILDREN];//连接在这个HUB上的子设备
int pm_usage_cnt; //自动挂起的使用计数
u32 quirks;
atomic_t urbnum; //这个设备所提交的URB计数
unsigned long active_duration; //激活后使用计时
#ifdef CONFIG_PM //电源管理相关
struct delayed_work autosuspend; //自动挂起的延时
struct work_struct autoresume; //(中断的)自动唤醒需求
struct mutex pm_mutex; //PM的互斥锁
unsigned long last_busy; //最后使用的时间
int autosuspend_delay;
unsigned long connect_time; //第一次连接的时间
unsigned auto_pm:1; //自动挂起/唤醒
unsigned do_remote_wakeup:1; //远程唤醒
unsigned reset_resume:1; //使用复位替代唤醒
unsigned autosuspend_disabled:1; //挂起关闭
unsigned autoresume_disabled:1; //唤醒关闭
unsigned skip_sys_resume:1; //跳过下个系统唤醒
#endif
struct wusb_dev *wusb_dev; //(如果为无线USB)连接到WUSB特定的数据结构
};
设备描述符:关于设备的通用信息,如供应商ID、产品ID
和修订
ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在Linux
内核中,
USB 设备用usb_device
结构体来描述,
USB 设备描述符定义为
usb_device_descriptor结构体:
struct usb_device_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__le16 bcdUSB;//USB版本号
__u8 bDeviceClass;//USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型
//0x00不是在设备描述符中定义的,如HID
__u8 bDeviceSubClass;//usb分配的子类代码,同上,值由USB规定和分配的
__u8 bDeviceProtocol;//USB分配的设备协议代码,同上
__u8 bMaxPacketSize0;//端点0的最大包的大小
__le16 idVendor;//厂商编号
__le16 idProduct;//产品编号
__le16 bcdDevice;//设备出厂编号
__u8 iManufacturer;//描述厂商字符串的索引
__u8 iProduct;//描述产品字符串的索引
__u8 iSerialNumber;//描述设备序列号字符串的索引
__u8 bNumConfigurations;//可能的配置数量
} __attribute__ ((packed));
一个USB设备可以有多个配置,并可在它们之间转换以改变设备的状态。比如一个设备可以通过下载固件(firmware)的方式改变设备的使用状态(我感觉类似FPGA或CPLD),那么USB设备就要切换配置,来完成这个工作。一个时刻只能有一个配置可以被激活。Linux使用结构 struct usb_host_config 来描述USB配置。我们编写的USB设备驱动通常不需要读写这些结构的任何值。可在内核源码的文件include/linux/usb.h中找到对它们的描述。
struct usb_host_config {
struct usb_config_descriptor desc; //配置描述符
char *string; /* 配置的字符串指针(如果存在) */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; //配置的接口联合描述符链表
struct usb_interface *interface[USB_MAXINTERFACES]; //接口描述符链表
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra; /* 额外的描述符 */
int extralen;
};
配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求。USB
配置在内核中使用
usb_host_config结构体描述,
USB
配置描述符定义为结构体 usb_config_descriptor
struct usb_config_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__le16 wTotalLength;//配置所返回的所有数量的大小
__u8 bNumInterfaces;//此配置所支持的接口数量
__u8 bConfigurationValue;//Set_Configuration命令需要的参数值
__u8 iConfiguration;//描述该配置的字符串的索引值
__u8 bmAttributes;//供电模式的选择
__u8 bMaxPower;//设备从总线提取的最大电流
} __attribute__ ((packed));
USB端点被绑为接口,USB接口只处理一种USB逻辑连接。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要一个以上的驱动程序。这可以在“晕到死
差屁”系统中看出,有时插入一个USB设备后,系统会识别出多个设备,并安装相应多个的驱动。
USB 接口可以有其他的设置,它是对接口参数的不同选择. 接口的初始化的状态是第一个设置,编号为0。 其他的设置可以以不同方式控制独立的端点。
USB接口在内核中使用 struct usb_interface 来描述。USB 核心将其传递给USB驱动,并由USB驱动负责后续的控制。
struct usb_interface {
struct usb_host_interface *altsetting; /* 包含所有可用于该接口的可选设置的接口结构数组。每个 struct usb_host_interface 包含一套端点配置(即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序。*/
struct usb_host_interface *cur_altsetting; /* 指向altsetting内部的指针,表示当前激活的接口配置*/
unsigned num_altsetting; /* 可选设置的数量*/
/* If there is an interface association descriptor then it will list the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* 如果绑定到这个接口的 USB 驱动使用 USB 主设备号, 这个变量包含由 USB 核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效。*/
/*以下的数据在我们写的驱动中基本不用考虑,系统会自动设置*/
enum usb_interface_condition condition; /* state of binding */
unsigned is_active:1; /* the interface is not suspended */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned ep_devs_created:1; /* endpoint "devices" exist */
unsigned unregistering:1; /* unregistration is in progress */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */
unsigned reset_running:1;
struct device dev; /* 接口特定的设备信息 */
struct device *usb_dev;
int pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};
struct usb_host_interface {
struct usb_interface_descriptor desc; //接口描述符
struct usb_host_endpoint *endpoint; /* 这个接口的所有端点结构体的联合数组*/
char *string; /* 接口描述字符串 */
unsigned char *extra; /* 额外的描述符 */
int extralen;
};
接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。USB 接口在 内 核 中 使 用 usb_interface结 构 体 描 述 , USB 接 口 描 述 符 定 义 为 结 构 体usb_interface_descriptor
struct usb_interface_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__u8 bInterfaceNumber;//接口的编号
__u8 bAlternateSetting;//备用的接口描述符编号
__u8 bNumEndpoints;//该接口使用端点数,不包括端点0
__u8 bInterfaceClass;//接口类型
__u8 bInterfaceSubClass;//接口子类型
__u8 bInterfaceProtocol;//接口所遵循的协议
__u8 iInterface;//描述该接口的字符串索引值
} __attribute__ ((packed));
端点
USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。
一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:
控制CONTROL
控制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.
中断INTERRUPT
每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.
批量BULK
批量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。
等时ISOCHRONOUS
等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。
控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。
端点在内核中使用结构 struct usb_host_endpoint 来描述
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc; //端点描述符
struct list_head urb_list; //此端点的URB对列,由USB核心维护
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
};
struct
usb_host_endpoint 所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; /*这个特定端点的 USB 地址,这个8位数据包含端点的方向,结合位掩码 USB_DIR_OUT 和 USB_DIR_IN 使用, 确定这个端点的数据方向。*/
__u8 bmAttributes; //这是端点的类型,位掩码如下
__le16 wMaxPacketSize; /*端点可以一次处理的最大字节数。驱动可以发送比这个值大的数据量到端点, 但是当真正传送到设备时,数据会被分为 wMaxPakcetSize 大小的块。对于高速设备, 通过使用高位部分几个额外位,可用来支持端点的高带宽模式。*/
__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));
字符串描述符:在其他描述符中会为某些字段提供字符串索引,它们可被用来检索描述性字符串,可以以多种语言形式提供。字符串描述符是可选的,有的设备有,有的设备没有,字符串描述符对应于usb_string_descriptor结构体
struct usb_string_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));
例如,笔者在 PC 上插入一个 SanDisk U 盘后,通过 lsusb 命令得到这个 U 盘相关的描述符,从中可以显示这个U 盘包含了一个设备描述符、一个配置描述符、一个接口描述符以及批量输入和批量输出两个端点描述符。呈现出来的信息内容直接对应于usb_device_descriptor、usb_config_
descriptor、usb_interface_descriptor、usb_endpoint_descriptor、usb_string_descriptor结构体
app:
-------------------------------------------
USB设备驱动程序 // 知道数据含义
内核 --------------------------------------
USB总线驱动程序 // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数据含义)
-------------------------------------------
USB主机控制器
UHCI OHCI EHCI
硬件 -----------
USB设备
OHCI: microsoft 低速/全速
EHCI: 高速(480Mbps)
1. 识别USB设备
1.1 分配地址
1.2 并告诉USB设备(set address)
1.3 发出命令获取描述符
描述符的信息可以在include\linux\usb\Ch9.h看到
3. 提供USB读写函数
/*
* drivers\hid\usbhid\usbmouse.c
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
static void usbmouse_as_key_irq(struct urb *urb)
{
int i;
static int cnt = 0;
printk("data cnt %d: ", ++cnt);
for (i = 0; i < len; i++)
{
//printk("%02x ", usb_buf[i]);
if(usb_buf[i] == 01)
printk("left\n");
else
printk("no left\n");
}
printk("\n");
//data cnt 30: 01 ff 02 00
/* 重新提交urb */
usb_submit_urb(uk_urb, GFP_KERNEL);
}
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //根据usb_interface指针intf获取usb_device的地址
struct usb_host_interface *interface; //这里可能要补充以下一些关于usb_interface_descriptor的知识,但因为内核源码对该结构体的注释不多,所以只能靠个人猜测。在一个usb_host_interface结构里面有一个usb_interface_descriptor叫做desc的成员,他应该是用于描述该interface的一些属性,其中bNumEndpoints是一个8位(b for byte)的数字,他代表了该接口的端点数。probe然后遍历所有的端点,检查他们的类型跟方向,注册到usb_skel中。
struct usb_endpoint_descriptor *endpoint;
int pipe;
interface = intf->cur_altsetting; //当前活跃的设置
endpoint = &interface->endpoint[0].desc; //本接口对应的端点
/* a. 分配一个input_dev */
uk_dev = input_allocate_device();
/* b. 设置 */
/* b.1 能产生哪类事件 */
set_bit(EV_KEY, uk_dev->evbit);
set_bit(EV_REP, uk_dev->evbit);
/* b.2 能产生哪些事件 */
set_bit(KEY_L, uk_dev->keybit);
set_bit(KEY_S, uk_dev->keybit);
set_bit(KEY_ENTER, uk_dev->keybit);
/* c. 注册 */
input_register_device(uk_dev);
/* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
//设置端点信息,其实pipe是一个int类型的数据。urb所发送的特定目标struct usb_device的端点信息
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //bEndpointAddress 端点地址
/* 信息包长度: */
len = endpoint->wMaxPacketSize;
/* 目的: */
usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
/* 使用"3要素" */
/* 分配usb request block(urb) */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
//对于中断urb,使用usb_fill_int_urb初始化urb。
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL);
return 0;
}
static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
//printk("disconnect usbmouse!\n");
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb);
usb_free_coherent(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}
/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "s5p210usbmouse",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usb_mouse_id_table,
};
static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}
static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}
module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);
MODULE_LICENSE("GPL");
使用 usb_driver结构体描述一个 USB 设备驱动,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); /*恢复函数 */
int (*reset_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;/*usb_device_id表指针 */
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
};
/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "s5p210usbmouse",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usb_mouse_id_table,
};
<span style="font-family: SimSun; font-size: 10pt;">对<span style="font-family: TimesNewRomanPSMT; font-size: 10pt;">usb<span style="font-family: SimSun; font-size: 10pt;">_<span style="font-family: TimesNewRomanPSMT; font-size: 10pt;">driver <span style="font-family: SimSun; font-size: 10pt;">的注册和注销</span><br style="orphans: 2; text-align: -webkit-auto; widows: 2;" /></span></span></span></span>
static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}
static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
该宏用于创建一个匹配接口指定类型的 usb_device_id结构体实例。
该宏用于创建一个匹配设备指定类型的 usb_device_id结构体实例。
该宏根据制造商 ID 和产品 ID 生成一个 usb_device_id结构体的实例,在数组中增加该元素将意味着该驱动可支持匹配制造商ID、产品ID 的设备。
该宏根据制造商 ID、产品 ID、产品版本的最小值和最大值生成一个 usb_device_id结构体的实例,在数组中增加该元素将意味着该驱动可支持匹配制造商ID、产品ID 和 lo~hi范围内版本的设备。
USB 请求块( USB request block,urb)是USB 设备驱动中用来描述与 USB 设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的 sk_buff结构体。
struct urb
2 {
3 /* 私有的:只能由usb核心和主机控制器访问的字段 */
4 struct kref kref; /*urb引用计数 */
5 spinlock_t lock; /* urb锁 */
6 void *hcpriv; /* 主机控制器私有数据 */
7 int bandwidth; /* int/iso请求的带宽 */
8 atomic_t use_count; /* 并发传输计数 */
9 u8 reject; /* 传输将失败*/
10
11 /* 公共的: 可以被驱动使用的字段 */
12 struct list_head urb_list; /* 链表头*/
13 struct usb_device *dev; /* 关联的usb设备 */
14 unsigned int pipe; /* 管道信息 */
15 int status; /* urb的当前状态 */
16 unsigned int transfer_flags; /* urb_short_not_ok | ...*/
17 void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
18 dma_addr_t transfer_dma; /*用来以dma方式向设备传输数据的缓冲区 */
19 int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */
20
21 int actual_length; /* urb结束后,发送或接收数据的实际长度 */
22 unsigned char *setup_packet; /* 指向控制urb的设置数据包的指针*/
23 dma_addr_t setup_dma; /*控制urb的设置数据包的dma缓冲区*/
24 int start_frame; /*等时传输中用于设置或返回初始帧*/
25 int number_of_packets; /*等时传输中等时缓冲区数据 */
26 int interval; /* urb被轮询到的时间间隔(对中断和等时urb有效) */
27 int error_count; /* 等时传输错误数量 */
28 void *context; /* completion函数上下文 */
29 usb_complete_t complete; /* 当urb被完全传输或发生错误时,被调用 */
30 struct usb_iso_packet_descriptor iso_frame_desc[0];
31 /*单个urb一次可定义多个等时传输时,描述各个等时传输 */
32 };
urb
处理流程
USB
设备中的每个端点都处理一个 urb
队列,在队列被清空之前,一个 urb
的典型生命周期如下:
创建 urb 结构体的函数为:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
mem_flags参数是分配内存的标志,和 kmalloc()函数的分配标志参数含义相同。如果分配成功,该函数返回一个urb 结构体指针,否则返回 0。
urb 结构体在驱动中不能静态创建,因为这可能破坏 USB 核心给 urb 使用的引用计数方法。
/* 使用"3要素" */
/* 分配usb request block(urb) */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
//对于中断urb,使用usb_fill_int_urb初始化urb。
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL);
对于中断 urb,使用 usb_fill_int_urb()函数来初始化urb:
transfer_buffer指针所指向缓冲区的大小; complete 指针指向当这个 urb 完成时被调用的完成处理函数;context 是完成处理函数的“上下文”; interval是这个 urb 应当被调度的间隔。
urb 参数是指向 urb 的指针, mem_flags参数与传递给 kmalloc()函数参数的意义相同,它用于告知USB 核心如何在此时分配内存缓冲区。
在提交 urb 到 USB 核心后,直到完成函数被调用之前,不要访问 urb 中的任何成员。
usb_submit_urb()在原子上下文和进程上下文中都可以被调用,mem_flags变量需根据调用环境进行相应的设置,如下所示。
GFP_NOIO:在存储设备的块I/O 和错误处理路径中,应使用此标志;
GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和 GFP_NOIO,就使用GFP_KERNEL。
如果 usb_submit_urb()调用成功,即urb 的控制权被移交给 USB 核心,该函数返回 0;否则,返回错误号。
( 5)被USB 主机控制器处理,进行一次到 USB设备的传送。
第( 4) ~(5)步由USB 核心和主机控制器完成,不受 USB设备驱动的控制。
( 6)当urb 完成, USB主机控制器驱动通知 USB 设备驱动。
urb被成功发送给设备,并且设备返回正确的确认。如果 urb→status为 0,意味着对于一个输出urb,数据被成功发送;对于一个输入urb,请求的数据被成功收到。
如果发送数据到设备或从设备接收数据时发生了错误,urb→status将记录错误值。
urb被从 USB 核心“去除连接”,这发生在驱动通过 usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb 虽已提交,而 USB 设备被拔出的情况下