700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 第16章 USB主机 设备与Gadget驱动之USB设备驱动(一)

第16章 USB主机 设备与Gadget驱动之USB设备驱动(一)

时间:2023-06-28 20:40:32

相关推荐

第16章 USB主机 设备与Gadget驱动之USB设备驱动(一)

16.3USB设备驱动

16.3.1USB设备驱动的整体结构

这里所说的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(/linux-usb/)是一个图形化的GTK工具,可以显示USB信息。

在sysfs文件系统中,同样包含USB相关信息的描述,但只限于接口级别。USB设备和USB接口在sysfs中均表示为单独的USB设备,其目录命名规则如下:

根集线器-集线器端口号(-集线器端口号-...):配置.接口

如下给出一个/sys/bus/usb目录下的树形结构实例,其中的多数文件都是锚定到/sys/devices及/sys/drivers中相应文件的软链接。

ubuntu@ubuntu:/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.11usb_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 *);

/* use a define to avoid include chaining to get THIS_MODULE & friends */

#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? */

__u16match_flags;//匹配标志

/* Used for product specific matches; range is inclusive */

__u16idVendor; //制造商ID

__u16idProduct;//产品ID

__u16bcdDevice_lo;

__u16bcdDevice_hi;

/* Used for device class matches */

__u8bDeviceClass;//设备类

__u8bDeviceSubClass;

__u8bDeviceProtocol;

/* Used for interface class matches */

__u8bInterfaceClass;//接口类

__u8bInterfaceSubClass;

__u8bInterfaceProtocol;

/* Used for vendor-specific interface matches */

__u8bInterfaceNumber;

/* 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_CLASS0x0010

#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020

#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040

#define USB_DEVICE_ID_MATCH_INT_CLASS0x0080

#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.12usb_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.3USB设备驱动结构

usb_driver本身只是有找到USB设备、管理USB设备连接和断开的作用,而作为USB设备的可以是字符设备、网络设备或块设备,因此必须实现相应设备类的驱动。

16.3.2USB请求块

1.urb结构体

USB请求块(USB Request Block,URB)是USB设备驱动中描述与USB设备通信所用的基本载体和核心数据结构,类似于网络设备驱动中的sk_buff结构体。

代码清单16.13URB结构体

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.4URB处理流程

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。