第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中相应文件的软链接。

ubuntu@ubuntu2018:/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 *); 

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

        __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处理流程


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80581566
今日推荐