第16章 USB主机、设备与Gadget驱动之USB设备驱动(一)
16.3 USB设备驱动
16.3.1 USB设备驱动的整体结构
这里所说的USB设备驱动指的是从主机角度来看,如何访问被插入的USB设备,不是指USB设备内部本身运行的固件程序。Linux系统实现几类通用的USB设备驱动(也称客户驱动),划分为如下几个设备类。
音频设备类、通信设备类、HID(人机接口)设备类、显示设备类、海量存储设备类、电源设备类、打印设备类、集线器设备类。
一般的通用Linux设备(如U盘、USB鼠标、USB键盘等)都不需要再编写驱动,需要编写的是特定厂商、特定芯片的驱动,可以参考已经在内核中提供的驱动模板。
Linux内核为各类USB设备分配相应的设备号,如ACM USB调制解调器的主设备号为166(默认设备名/dev/ttyACMn)、USB打印机的主设备号为180,次设备号为0~15(默认设备名/dev/lpn)、USB串口的主设备号为188(默认设备名/dev/ttyUSBn)等。
在debugfs下,/sys/kernel/debug/usb/devices包含USB的设备信息,在Ubuntu上插入一个U盘后,在
可看到类似信息。
sudo cat /sys/kernel/debug/usb/devices
T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 8
B: Alloc= 2/900 us ( 0%), #Int= 1, #Iso= 0
D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0001 Rev= 4.00
S: Manufacturer=Linux 4.0.0-rc1 ohci_hcd
S: Product=OHCI PCI host controller
S: SerialNumber=0000:00:06.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms…
T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0930 ProdID=6545 Rev= 1.00
S: Manufacturer=Kingston
S: Product=DataTraveler 3.0
S: SerialNumber=60A44C3FAE22EEA0797900F7
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=498mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
通过分析上述记录信息,可得到系统中USB的完整信息。USBView(http://www.kroah.com/linux-usb/)是一个图形化的GTK工具,可以显示USB信息。
在sysfs文件系统中,同样包含USB相关信息的描述,但只限于接口级别。USB设备和USB接口在sysfs中均表示为单独的USB设备,其目录命名规则如下:
根集线器-集线器端口号(-集线器端口号-...):配置.接口
如下给出一个/sys/bus/usb目录下的树形结构实例,其中的多数文件都是锚定到/sys/devices及/sys/drivers中相应文件的软链接。
[email protected]:/sys/bus/usb$ tree
.
├── devices
│ ├── 1-0:1.0 -> ../../../devices/pci0000:00/0000:00:1a.0/usb1/1-0:1.0
│ ├── 1-1 -> ../../../devices/pci0000:00/0000:00:1a.0/usb1/1-1
│ ├── 1-1:1.0 -> ../../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1:1.0
│ ├── 2-0:1.0 -> ../../../devices/pci0000:00/0000:00:1d.0/usb2/2-0:1.0
│ ├── 2-1 -> ../../../devices/pci0000:00/0000:00:1d.0/usb2/2-1
│ ├── 2-1:1.0 -> ../../../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1:1.0
│ ├── 3-0:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-0:1.0
│ ├── 3-10 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-10
│ ├── 3-10:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0
│ ├── 3-10:1.1 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.1
│ ├── 3-9 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-9
│ ├── 3-9:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9:1.0
│ ├── 4-0:1.0 -> ../../../devices/pci0000:00/0000:00:14.0/usb4/4-0:1.0
│ ├── usb1 -> ../../../devices/pci0000:00/0000:00:1a.0/usb1
│ ├── usb2 -> ../../../devices/pci0000:00/0000:00:1d.0/usb2
│ ├── usb3 -> ../../../devices/pci0000:00/0000:00:14.0/usb3
│ └── usb4 -> ../../../devices/pci0000:00/0000:00:14.0/usb4
├── drivers
│ ├── hub
│ │ ├── 1-0:1.0 -> ../../../../devices/pci0000:00/0000:00:1a.0/usb1/1-0:1.0
│ │ ├── 1-1:1.0 -> ../../../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1:1.0
│ │ ├── 2-0:1.0 -> ../../../../devices/pci0000:00/0000:00:1d.0/usb2/2-0:1.0
│ │ ├── 2-1:1.0 -> ../../../../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1:1.0
│ │ ├── 3-0:1.0 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-0:1.0
│ │ ├── 4-0:1.0 -> ../../../../devices/pci0000:00/0000:00:14.0/usb4/4-0:1.0
│ │ ├── bind
│ │ ├── module -> ../../../../module/usbcore
│ │ ├── new_id
│ │ ├── remove_id
│ │ ├── uevent
│ │ └── unbind
│ ├── usb
│ │ ├── 1-1 -> ../../../../devices/pci0000:00/0000:00:1a.0/usb1/1-1
│ │ ├── 2-1 -> ../../../../devices/pci0000:00/0000:00:1d.0/usb2/2-1
│ │ ├── 3-10 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-10
│ │ ├── 3-9 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-9
│ │ ├── bind
│ │ ├── uevent
│ │ ├── unbind
│ │ ├── usb1 -> ../../../../devices/pci0000:00/0000:00:1a.0/usb1
│ │ ├── usb2 -> ../../../../devices/pci0000:00/0000:00:1d.0/usb2
│ │ ├── usb3 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3
│ │ └── usb4 -> ../../../../devices/pci0000:00/0000:00:14.0/usb4
│ ├── usbfs
│ │ ├── bind
│ │ ├── module -> ../../../../module/usbcore
│ │ ├── new_id
│ │ ├── remove_id
│ │ ├── uevent
│ │ └── unbind
│ └── usbhid
│ ├── 3-10:1.0 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0
│ ├── 3-10:1.1 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.1
│ ├── 3-9:1.0 -> ../../../../devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9:1.0
│ ├── bind
│ ├── module -> ../../../../module/usbhid
│ ├── new_id
│ ├── remove_id
│ ├── uevent
│ └── unbind
├── drivers_autoprobe
├── drivers_probe
└── uevent
如tty_driver、i2c_driver等,在Linux内核中,使用usb_driver结构体描述一个USB设备(外设)驱动,usb_driver结构体的定义如代码清单16.11所示。
代码清单16.11 usb_driver结构体
include/linux/usb.h
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 (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int disable_hub_initiated_lpm:1;
unsigned int soft_unbind:1;
};
在编写新的USB设备驱动时,主要应该完成的工作是probe()和disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出时调用,用于初始化和释放软硬件资源。
对usb_driver的注册和注销通过下面函数完成:
include/linux/usb.h
/*
* use these in module_init()/module_exit()
* and don't forget MODULE_DEVICE_TABLE(usb, ...)
*/
extern int usb_register_driver(struct usb_driver *, struct module *, const char *);
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
extern void usb_deregister(struct usb_driver *);、
usb_driver结构体中的id_table成员描述这个USB驱动所支持的USB设备列表,指向一个usb_device_id结构体类型的数组,usb_device_id结构体的定义如下:
linux/mod_devicetable.h
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;//匹配标志
/* Used for product specific matches; range is inclusive */__u16 idVendor; //制造商ID
__u16 idProduct;//产品ID
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;//设备类
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;//接口类
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* Used for vendor-specific interface matches */
__u8 bInterfaceNumber;
/* not matched against */
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
其中匹配标志:
/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
借助下面一组宏来生成usb_device_id结构体的实例:
linux/usb.h
1、USB_DEVICE(vendor, product)
该宏根据制造商ID和产品ID生成一个usb_device_id结构体的实例,在数组中增加该元素意味着该驱动可支持与制造商ID、产品ID匹配的设备。
2、USB_DEVICE_VER(vendor, product, lo, hi)
该宏根据制造商ID、产品ID、产品版本的最小值和最大值生成一个usb_device_id结构体的实例,在数组中增加该元素将意味着该驱动可支持与制造商ID、产品ID匹配和lo~hi范围内版本的设备。
3、USB_DEVICE_INFO(class, subclass, protocol)
该宏用于创建一个匹配设备指定类型的usb_device_id结构体实例。
4、USB_INTERFACE_INFO(class, subclass, protocol)
该宏用于创建一个匹配接口指定类型的usb_device_id结构体实例。
代码清单16.12所示为两个用于描述某USB驱动支持的USB设备的usb_device_id结构体数组实例。
代码清单16.12 usb_device_id结构体数组实例
/* 本驱动支持的USB设备列表 */
/* 实例1 */
#define USB_SKEL_VENDOR_ID 0x04b4
#define USB_SKEL_PRODUCT_ID 0x0002
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE (usb, id_table);
/* 实例2 */
static struct usb_device_id id_table [] = {
{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
当USB核心检测到某个设备的属性和某个驱动程序的usb_device_id结构体携带的信息一致时,这个驱动程序的probe()函数就被执行(如果这个USB驱动是个模块的话,相关的.ko还应被Linux自动加载)。拔掉设备或者卸掉驱动模块后,USB核心就执行disconnect()函数来响应这个动作。
usb_driver结构体中的函数是USB设备驱动中与USB相关的部分,而USB只是一个总线,USB设备驱动的主体工作仍然是USB设备本身所属类型(哪一类设备)的驱动,如字符设备、tty设备、块设备、输入设备等。USB设备(外设)驱动包含其作为总线上挂接设备的驱动和本身所属设备类型(哪一类设备)的驱动两部分。
与platform_driver、i2c_driver类似,usb_driver起到了“牵线”的作用,即在probe()里注册相应的字
符、tty等设备,在disconnect()注销相应的字符、tty等设备,而原先对设备的注册和注销一般直接发生在模块加载和卸载函数中。
USB本身所属设备驱动的结构与其挂不挂在USB总线上没什么关系,但是据此在访问方式上却有很大的变化,例如,对于USB接口的字符设备而言,尽管仍然是write()、read()、ioctl()这些函数,但是在这些函数中,贯穿始终的是称为URB的USB请求块。
如图16.3所示,在这棵树里,把树根比作主机控制器,树叶比作具体的USB设备,树干和树枝就是USB总线。树叶本身与树枝通过usb_driver连接(设备、驱动依附于总线),树叶本身的驱动(读写、控制)需要通过树叶设备本身所属类设备驱动来完成。树根(主机控制器)和树叶(设备)之间的“通信”依靠在树干和树枝(总线)里“流淌”的URB(USB请求块)来完成。
图16.3 USB设备驱动结构
usb_driver本身只是有找到USB设备、管理USB设备连接和断开的作用,而作为USB设备的可以是字符设备、网络设备或块设备,因此必须实现相应设备类的驱动。
16.3.2 USB请求块
1.urb结构体
USB请求块(USB Request Block,URB)是USB设备驱动中描述与USB设备通信所用的基本载体和核心数据结构,类似于网络设备驱动中的sk_buff结构体。
代码清单16.13 URB结构体
linux/usb.h
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's
* current owner */
struct list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
void *priv_data; /* (in) additional private data */
};
2.URB处理流程
USB设备中的每个端点都处理一个URB队列,在队列被清空之前,一个URB的生命周期如下:
1)被一个USB设备驱动创建
创建URB结构体的函数为:
linux/usb.h
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets:URB应当包含的等时数据包的数目,若为0表示不创建等时数据包。
mem_flags:分配内存的标志,如果分配成功,该函数返回一个URB结构体指针,否则返回0。
drivers/usb/core/urb.c
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
printk(KERN_ERR "alloc_urb: kmalloc failed\n");
return NULL;
}
usb_init_urb(urb);
return urb;
}
EXPORT_SYMBOL_GPL(usb_alloc_urb);
备注:
URB结构体在驱动中不宜静态创建,因为这可能破坏USB核心给URB使用的引用计数方法。
usb_alloc_urb()的“反函数”为:void usb_free_urb(struct urb *urb);
该函数用于释放由usb_alloc_urb()分配的URB结构体。
2)初始化,被安排给一个特定USB设备的特定端点
1、对于中断URB,使用usb_fill_int_urb()函数来初始化URB,如下所示:
linux/usb.h
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval)
{
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) {
/* make sure interval is within allowed range */
interval = clamp(interval, 1, 16);
urb->interval = 1 << (interval - 1);
} else {
urb->interval = interval;
}
urb->start_frame = -1;
}
函数参数描述如下:
URB:要被初始化的URB的指针
dev:这个URB要被发送到的USB设备
pipe:这个URB要被发送到的USB设备的特定端点,使用usb_sndintpipe()或usb_rcvintpipe()宏创建
transfer_buffer:指向发送数据或接收数据的缓冲区的指针,和URB一样,它也不能是静态缓冲区,必须使用kmalloc()来分配
buffer_length:transfer_buffer指针所指向缓冲区的大小
complete:指向当这个URB完成时被调用的完成处理函数
context:完成处理函数的“上下文”
interval:这个URB应当被调度的间隔
2、对于批量URB,使用usb_fill_bulk_urb()函数来初始化,如下所示:
linux/usb.h
/**
* usb_fill_bulk_urb - macro to help initialize a bulk urb
* @urb: pointer to the urb to initialize.
* @dev: pointer to the struct usb_device for this urb.
* @pipe: the endpoint pipe
* @transfer_buffer: pointer to the transfer buffer
* @buffer_length: length of the transfer buffer
* @complete_fn: pointer to the usb_complete_t function
* @context: what to set the urb context to.
*
* Initializes a bulk urb with the proper information needed to submit it
* to a device.
*/
static inline void usb_fill_bulk_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
{
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
}
函数参数描述如下:
URB:要被初始化的URB的指针
dev:这个URB要被发送到的USB设备
pipe:这个URB要被发送到的USB设备的特定端点,使用usb_sndbulkpipe()或者usb_rcvbulkpipe()宏创建
transfer_buffer:指向发送数据或接收数据的缓冲区的指针,和URB一样,它也不能是静态缓冲区,必须使用kmalloc()来分配
buffer_length:transfer_buffer指针所指向缓冲区的大小
complete:指向当这个URB完成时被调用的完成处理函数
context:完成处理函数的“上下文”
3、对于控制URB,使用usb_fill_control_urb()函数来初始化,如下所示:
linux/usb.h
/**
* usb_fill_control_urb - initializes a control urb
* @urb: pointer to the urb to initialize.
* @dev: pointer to the struct usb_device for this urb.
* @pipe: the endpoint pipe
* @setup_packet: pointer to the setup_packet buffer
* @transfer_buffer: pointer to the transfer buffer
* @buffer_length: length of the transfer buffer
* @complete_fn: pointer to the usb_complete_t function
* @context: what to set the urb context to.
*
* Initializes a control urb with the proper information needed to submit
* it to a device.
*/
static inline void usb_fill_control_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
{
urb->dev = dev;
urb->pipe = pipe;
urb->setup_packet = setup_packet;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
}
函数参数描述如下:
URB:要被初始化的URB的指针
dev:这个URB要被发送到的USB设备
pipe:这个URB要被发送到的USB设备的特定端点,使用usb_sndctrlpipe()或usb_rcvictrlpipe()宏创建
transfer_buffer:指向发送数据或接收数据的缓冲区的指针,和URB一样,它也不能是静态缓冲区,必须使用kmalloc()来分配
buffer_length:transfer_buffer指针所指向缓冲区的大小
complete:指向当这个URB完成时被调用的完成处理函数
context:完成处理函数的“上下文”
setup_packet:指向即将被发送到端点的设置数据包。
等时URB没有像中断、控制和批量URB的初始化函数,只能手动对等时URB初始化,而后才能提交给USB核心。代码清单16.14给出了初始化等时URB的例子,代码路径:drivers/media/usb/uvc/uvc_video.c
代码清单16.14 初始化等时URB
/*
* Initialize isochronous URBs and allocate transfer buffers. The packet size
* is given by the endpoint.
*/
static int uvc_init_video_isoc(struct uvc_streaming *stream,
struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
struct urb *urb;
unsigned int npackets, i, j;
u16 psize;
u32 size;
psize = uvc_endpoint_max_bpi(stream->dev->udev, ep);
size = stream->ctrl.dwMaxVideoFrameSize;
npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;
size = npackets * psize;
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
uvc_uninit_video(stream, 1);
return -ENOMEM;
}
urb->dev = stream->dev->udev;
urb->context = stream;
urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = stream->urb_dma[i];
#else
urb->transfer_flags = URB_ISO_ASAP;
#endif
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = stream->urb_buffer[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
for (j = 0; j < npackets; ++j) {
urb->iso_frame_desc[j].offset = j * psize;
urb->iso_frame_desc[j].length = psize;
}
stream->urb[i] = urb;
}
return 0;
}
3)被USB设备驱动提交给USB核心
完成第1)、2)步的创建和初始化URB后,URB就可以提交给USB核心了,通过usb_submit_urb()函数来完成,如下所示:
linux/usb.h
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
URB:指向URB的指针
mem_flags:告知USB核心如何分配内存缓冲区
备注:
1、在提交URB到USB核心后,直到完成函数被调用之前,不要访问URB中的任何成员。
2、usb_submit_urb()在原子上下文和进程上下文中都可以被调用,mem_flags参数需根据调用环境进行相应的设置,如下所示:
GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及URB完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将current->state(进程的状态)修改为非TASK_RUNNING时,应使用此标志。
GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志。
GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_KERNEL(在内核进程中分配内存空间)。
如果usb_submit_urb()调用成功,即URB(USB请求块)的控制权被移交给USB核心,该函数返回0;否则,返回错误号。
4)提交由USB核心指定的USB主机控制器驱动
5)被USB主机控制器处理,进行一次到USB设备的传送
第4)~5)步由USB核心和主机控制器完成,不受USB设备驱动的控制。
6)当URB完成,USB主机控制器驱动通知USB设备驱动
在如下3种情况下,URB将结束,URB完成回调函数将被调用(完成回调是通过usb_fill_xxx_urb的参数传入的)。在完成回调中,通常要进行urb->status的判断。
URB被成功发送给设备,并且设备返回正确的确认。如果urb->status为0,意味着对于一个输出URB,数据被成功发送;对于一个输入URB,请求的数据被成功收到。
如果发送数据到设备或从设备接收数据时发生了错误,urb->status将记录错误值。
URB被从USB核心“去除连接”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消或URB虽已提交而USB设备被拔出的情况下。
linux/usb.h
int usb_unlink_urb(struct urb *urb);
void usb_kill_urb(struct urb *urb);
上面两个函数用于取消已提交的URB,其参数为要被取消的URB指针。
备注:usb_unlink_urb是异步的,搞定后对应的完成回调会被调用;usb_kill_urb会彻底终止URB的生命周期并等待这一行为,usb_kill_urb通常在设备的disconnect()函数中被调用。
当URB生命结束时(处理完成或被解除链接),在URB的完成回调中通过URB结构体的status成员可以获知其原因。
图16.4给出一个URB的完整处理流程,虚线框的usb_unlink_urb()和usb_kill_urb()不一定发生,它们只是在URB正在被USB核心和主机控制器处理时又被驱动程序取消的情况下才发生。
图16.4 URB处理流程