Linux USB驱动-鼠标驱动

1.概述

通用串行总线(USB)用于连接主机和外围设备。USB总线采用拓扑结构,USB主机和USB设备的连接构成了一颗树,树的结点为USB节点或USB集线器(HUB),USB集线器(HUB)用于扩展设备接口,一个集线器(HUB)可接多个USB设备或多个集线器。主机侧的USB节点为根节点,所有子节点都连接在根节点集线器(ROOT HUB)上,根节点由USB主机控制器(USB Host Controller)控制,设备侧的节点为子节点,由USB设备控制器(USB Device Controller)控制。在USB总线中,只能有一个USB主机控制器(根节点),可以有多个USB设备控制器(子节点)。USB主机负责协调主机和设备之间的通讯,USB设备不能主动向主机发送任何数据。

usb拓扑结构

2.USB规范

USB1.1协议包括OHCI(Open Host Controller Interface Specification)和UHCI(Universal Host Controller Interface Specification)规范。UHCI对硬件的要求较低,但驱动程序开发复杂,CPU处理负担较高,OHCI则使用硬件实现了较多的功能,对软件的要求降低,减轻了CPU的处理负担。USB2.0增加了EHCI(Enhanced Host Controller Interface),为USB 2.0主机高速数据传输控制器的软硬件设计提供了统一的接口标准,大大简化了USB 2.0的主机设计,提高了软件的可移植性。EHCI本身并不支持全速和低速设备,为了兼容USB 1.1,USB 2.0的主机控制器由EHCI和CHC(Companion Host Controller)两部分组成,CHC由OHCI和UHCI组成。xHCI是由Intel公司开发的可扩展主机控制器接口,主要面向USB3.0,同时也支持USB 2.0及以下设备。

3.USB驱动基础

USB设备包括设备、配置、接口和端点这四个层次,如下图所示。设备中包含若干个配置,配置中包含若干个接口,接口中包含若干个端点。

usb层次结构
USB设备逻辑层次结构.

3.1.端点

端点是USB最基本的通信形式,只能往一个方向传输数据,从主机到设备(输出设备)或者从设备到主机(称为输入端点),端点可以看作是单向的管道。每个端点都有唯一的地址和对应的属性,地址由设备地址和端点号给出,属性包括传输方向、总线访问频率、带宽、端点号和数据包的最大容量等。端点0通常为控制端点,用于设备的初始化。USB端点有四种不同的类型,分别具有不同的数据传递方式。
(1)控制
控制端点用于配置设备、获取设备信息、发送命令到设备、获取设备的状态。每个USB设备都有端点0的控制端点,当USB设备插入到USB主机拓扑网络中时,USB主机就通过端点0与USB设备通信,对USB设备进行配置,便于后续的数据传输。USB协议保证控制端点有足够的带宽。控制端点的数据传输方式为控制传输,控制传输可靠,时间有保证,但传输的数据量不大。如USB设备的识别过程就采用的时控制传输。
(2)中断
当USB主机请求USB设备传输数据时,中断端点以一个固定的速率传送少量的数据。中断端点的数据传输方式为中断传输,数据传输可靠,实时性高,这里的中断并不是USB设备产生中断,而是USB主机每隔一个固定的时间主动查询USB设备是否有数据要传输,以轮询的方式提高实时性。如USB鼠标采用的是中断传输。
(3)批量
批量端点用于传输大量数据,这些端点一次可以保存更多的数据。USB协议不保证这些数据传输可以在特定的时间内完成,但保证数据的准确性。如果总线上的空间不足以发送整个批量包,则将数据拆分为多个包传输。批量传输数据可靠,但实时性较低。如USB硬盘、打印机等设备就采用的是批量传输方式。
(4)等时
等时端点也可以传输大量数据,但数据的可靠性无法保证。采用等时传输的USB设备更加注重保持一个恒定的数据传输速度,对数据的可靠性要求不高。如USB摄像头就使用的是等时传输方式。
Linux内核使用struct usb_endpoint_descriptor结构体描述端点。

    [include/uapi/linux/usb/ch9.h]
    struct usb_endpoint_descriptor {
        __u8  bLength;  // 端点描述符长度
        __u8  bDescriptorType;  // 端点描述符类型 
        // 端点地址,低四位是端点号,最高位表示数据传输方向,0为输出,1为输入
        __u8  bEndpointAddress;  
        __u8  bmAttributes;  // 端点类型,为0表示控制,1表示等时,2表示批量,3表示中断
        __le16 wMaxPacketSize;  // 本端点接收或发送数据包的最大字节数
        // 中断传输轮询周期,批量传输忽略,等时传输为1,中断传输范围为1-255
        __u8  bInterval;  
        __u8  bRefresh;
        __u8  bSynchAddress;
    // __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,
    // 按照实际占用字节数进行对齐,是GCC特有的语法
    } __attribute__ ((packed));

Linux内核使用struct usb_host_endpoint描述主机侧的端点。

    [include/linux/usb.h]
    struct usb_host_endpoint {
        struct usb_endpoint_descriptor desc;  // 端点描述符
        struct usb_ss_ep_comp_descriptor ss_ep_comp;
        struct list_head		urb_list;  // USB请求块链表节点,由USB核心层管理
        void				*hcpriv;  // 主机控制器使用,通常用于硬件DMA队列头
        struct ep_device		*ep_dev;	/* For sysfs info */
        unsigned char *extra;   /* Extra descriptors */
        int extralen;  // extra的字节数
        int enabled;  // URBs may be submitted to this endpoint
        int streams;  // number of USB-3 streams allocated on the endpoint
    };
3.2.接口

USB接口代表了一个USB设备的基本功能,USB接口由多个USB端口组成。USB接口只处理一种USB逻辑连接,例如鼠标、键盘或者音频流。一些USB设备具有多个接口,例如USB扬声器可以包括两个接口:USB按键和USB音频流。
Linux内核使用struct usb_interface_descriptor描述端点。

    [include/uapi/linux/usb/ch9.h]
    struct usb_interface_descriptor {
        __u8  bLength;  // 描述符长度
        __u8  bDescriptorType;  // 描述符类型
        __u8  bInterfaceNumber;  // 接口的编号
        __u8  bAlternateSetting;  // 备用的接口描述符编号
        __u8  bNumEndpoints;  // 接口使用的端点数量,不包括端点0
        __u8  bInterfaceClass;  // 接口类型
        __u8  bInterfaceSubClass;  // 接口子类型
        __u8  bInterfaceProtocol;  // 接口所遵循的协议
        __u8  iInterface;  // 描述该接口的字符串索引值
    } __attribute__ ((packed));

Linux内核使用struct usb_host_interface描述主机侧的接口。

    [include/linux/usb.h]
    struct usb_host_interface {
        struct usb_interface_descriptor	desc;  // 接口描述符
        int extralen;  // extra的字节数
        unsigned char *extra;   /* Extra descriptors */
        struct usb_host_endpoint *endpoint;  // 主机侧的端点
        char *string;		/* 接口字符串 */
    };
3.3.配置

USB配置由一个或多个USB接口组成,每个配置具有一个或多个基本功能。Linux使用struct usb_config_descriptor描述配置。

    [include/uapi/linux/usb/ch9.h]
    struct usb_config_descriptor {
        __u8  bLength;  // 描述符长度
        __u8  bDescriptorType;  // 描述符类型编号
        __le16 wTotalLength;  // 配置返回的数据长度
        __u8  bNumInterfaces;  // 配置所支持的接口数量
        __u8  bConfigurationValue;  // Set_Configuration命令所需的参数值
        __u8  iConfiguration;  // 描述该配置字符串的索引值
        __u8  bmAttributes;  // 供电模式选择
        __u8  bMaxPower;  // 设备从总线提取的最大电流
    } __attribute__ ((packed));

Linux内核使用struct usb_host_config描述主机侧的配置。

    [include/linux/usb.h]
    #define USB_MAXINTERFACES	32
    #define USB_MAXIADS		(USB_MAXINTERFACES/2)
    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;   /* Extra descriptors */
        int extralen;  // extra字节数
    };
3.4.设备

USB设备有一个或多个配置组成。USB设备可以在这些配置之间切换,以改变设备的状态。Linux使用struct usb_device_descriptor描述设备。

    [include/uapi/linux/usb/ch9.h]
    struct usb_device_descriptor {
        __u8  bLength;  // 描述符长度
        __u8  bDescriptorType;  // 描述符类型编号
        __le16 bcdUSB;  // USB版本号
        __u8  bDeviceClass;  // USB分配的设备类code
        __u8  bDeviceSubClass;  // USB分配的子类code
        __u8  bDeviceProtocol;  // USB分配的协议code
        __u8  bMaxPacketSize0;  // 端点0最大包大小
        __le16 idVendor;  // 厂商编号
        __le16 idProduct;  // 产品编号
        __le16 bcdDevice;  // 设备出场编号
        __u8  iManufacturer;  // 描述厂商字符串的索引
        __u8  iProduct;  // 描述产品字符串的索引
        __u8  iSerialNumber;  // 描述设备序列号字符串的索引
        __u8  bNumConfigurations;  // 可能的配置数量
    } __attribute__ ((packed));

4.Linux USB驱动程序

在Linux系统中,USB驱动可以从两个角度观察,一个角度是主机侧,另一个角度是设备侧。主机测,处于USB驱动底层的是USB主机控制器硬件,在其上运行的是USB主机控制器驱动,再往上是USB核心层,最上层是USB设备驱动层(插入主机上的U盘、鼠标、键盘等设备驱动)。因此在主机侧,需要实现USB主机控制器驱动和USB设备驱动,前者用于控制USB主机控制器和插入USB总线的USB设备之间的通信,后者描述主机应该怎么和插入的USB设备通信。USB核心完成驱动管理和协议处理的主要工作,向上为主机侧USB设备驱动提供统一的编程接口,向下为USB主机控制器驱动提供统一的编程接口。设备侧驱动程序分为UDC驱动程序、Gadget Function API和Gadget Function驱动程序。UDC驱动程序直接访问硬件,控制USB设备控制器与USB主机控制器通信,向上提供与硬件交互的接口。Gadget Function API封装了UDC驱动程序向上提供的接口。Gadget Function驱动程序实现具体的USB设备功能。Linux内核支持的USB设备类包括USB打印设备、通信类设备、HID设备类、存储设备类、语音设备类等。驱动工程师一般需要实现USB设备驱动,USB主机控制器驱动通常由芯片厂家实现。

usb_driver_framework

4.1.USB设备驱动

Linux USB核心层使用struct usb_driver结构体表示一个USB设备驱动,编写USB设备驱动时,主要实现probedisconnect函数,分别用于初始化和释放软硬件资源,设备和驱动匹配成功后,probe函数被调用,设备断开时disconnect函数被调用。使用宏usb_register注册USB设备驱动程序,使用函数usb_deregister注销USB设备驱动程序,还可以使用宏module_usb_driver注册USB驱动程序,其同时完成注册、注销、module_initmodule_exit功能。在注册USB驱动程序时,还需要定义MODULE_DEVICE_TABLE宏,将设备驱动匹配表导出到用户空间,常用于设备热插拔时进行设备识别。USB协议支持设备的热拔插。
这里需要注意的时USB只是一种总线,而连接到总线上的USB设备可以是字符设备、tty设备、块设备、输入设备等。usb_driver结构体处理了设备和USB总线相关的工作,至于设备的功能,需要根据具体的设备类型来编写具体的设备驱动。因此USB设备驱动包含了其作为总线上挂接设备的驱动和所属设备类型的驱动。这和platform_driveri2c_driver等类似,usb_driver起到桥梁的作用,即在xxx_driver结构体的probe函数中注册具体的设备,如注册字符、tty等设备,在disconnect函数中注销字符、tty设备。

    [include/linux/usb.h]
    struct usb_driver {
        const char *name;  // 驱动名称
        // probe函数
        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;  // 描述了USB驱动支持的USB设备列表
        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设备驱动程序,driver为struct usb_driver结构体指针
    #define usb_register(driver) usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
    #define module_usb_driver(__usb_driver) module_driver(__usb_driver, 
                    usb_register, usb_deregister)
    // 注销USB设备驱动程序
    void usb_deregister(struct usb_driver *driver)
    [include/linux/device.h]
    // 简介的注册宏定义,同时完成初始化和卸载功能
    #define module_driver(__driver, __register, __unregister, ...) \
    static int __init __driver##_init(void) \
    { \
        return __register(&(__driver) , ##__VA_ARGS__); \
    } \
    module_init(__driver##_init); \
    static void __exit __driver##_exit(void) \
    { \
        __unregister(&(__driver) , ##__VA_ARGS__); \
    } \
    module_exit(__driver##_exit);

id_table描述了USB驱动支持的USB设备列表,其指向了struct usb_device_id类型的数组。struct usb_device_id包含了USB设备的制造商ID、产品ID、产品版本、设备类、接口类等信息及其要匹配标志成员match_flags(表明要与那些成员匹配)。match_flags的取值从下面的宏定义中选取。

    [include/linux/mod_devicetable.h]
    #define USB_DEVICE_ID_MATCH_VENDOR		0x0001  // 按供应商ID匹配
    #define USB_DEVICE_ID_MATCH_PRODUCT		0x0002  // 按产品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 // 按接口协议匹配
    #define USB_DEVICE_ID_MATCH_INT_NUMBER		0x0400
    struct usb_device_id {
        __u16		match_flags;  // 匹配方式
        __u16		idVendor;  // 供应商ID,由USB协会分配
        __u16		idProduct;  // 产品ID,供应商自己分配
        __u16		bcdDevice_lo;  // 产品版本号的最小值
        __u16		bcdDevice_hi;  // 产品版本号的最大值
        __u8		bDeviceClass;  // 设备类型
        __u8		bDeviceSubClass;  // 设备子类型
        __u8		bDeviceProtocol;  // 设备协议
        __u8		bInterfaceClass;  // 接口类
        __u8		bInterfaceSubClass;  // 接口子类
        __u8		bInterfaceProtocol;  // 接口协议

        __u8		bInterfaceNumber;
        /* not matched against */
        kernel_ulong_t	driver_info
            __attribute__((aligned(sizeof(kernel_ulong_t))));
    };

也可使用下面的宏定义来初始化usb_device_idUSB_DEVICE宏根据制造商ID和产品ID生成一个usb_device_id结构体实列,在数组中增加该元素将意味着该驱动可支持与制造商ID、产品ID相匹配的设备。USB_DEVICE_VER宏根据制造商ID、产品ID、产品版本号范围(在最大值与最小值之间)生成一个usb_device_id结构体的实列,在数组中增加该元素将意味着该驱动可支持与制造商ID、产品ID、lo~hi产品版本号范围内的产品匹配。USB_DEVICE_INFO宏用于创建一个匹配指定设备类型的usb_device_id结构体实列。USB_INTERFACE_INFO宏创建一个匹配指定接口类型的usb_device_id结构体实列。创建完usb_device_id结构体实列后,还需要使用MODULE_DEVICE_TABLE宏将usb_device_id导出到用户空间。

    [include/linux/usb.h]
    // 匹配方式的组合,用于设置struct usb_device_id的match_flags标志
    #define USB_DEVICE_ID_MATCH_DEVICE \
            (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
    #define USB_DEVICE_ID_MATCH_DEV_RANGE \
            (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
    #define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION \
            (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
    #define USB_DEVICE_ID_MATCH_DEV_INFO \
            (USB_DEVICE_ID_MATCH_DEV_CLASS | \
            USB_DEVICE_ID_MATCH_DEV_SUBCLASS | \
            USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
    #define USB_DEVICE_ID_MATCH_INT_INFO \
            (USB_DEVICE_ID_MATCH_INT_CLASS | \
            USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
            USB_DEVICE_ID_MATCH_INT_PROTOCOL)

    #define USB_DEVICE(vend, prod) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
        .idVendor = (vend), \  // 制造商ID
        .idProduct = (prod)    // 产品ID
    #define USB_DEVICE_VER(vend, prod, lo, hi) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \
        .idVendor = (vend), \     // 制造商ID
        .idProduct = (prod), \    // 产品ID
        .bcdDevice_lo = (lo), \   // 产品版本号范围的最小值
        .bcdDevice_hi = (hi)      // 产品版本号范围的最大值
    #define USB_DEVICE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \
        .bDeviceClass = (cl), \     // 设备类型 
        .bDeviceSubClass = (sc), \  // 设备子类
        .bDeviceProtocol = (pr)     // 设备协议
    #define USB_INTERFACE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
        .bInterfaceClass = (cl), \     // 接口类型
        .bInterfaceSubClass = (sc), \  // 接口子类
        .bInterfaceProtocol = (pr)     // 接口协议
    ......

下面是定义USB设备属性的例子,当USB核心检测到某个插入设备的属性和某个驱动程序的属性一致时,这个驱动程序的probe()函数就被调用,当拔掉设备或者卸载驱动后,USB核心层就执行disconnect()函数,将设备和驱动断开。

    // 先定义一个usb_device_id数组,使用USB_DEVICE初始化内部的元素,然后使用
    // MODULE_DEVICE_TABLE将usb_device_id导出到用户空间,便于热插拔时进行设备识别
    static struct usb_device_id id_table[] = {
        {USB_DEVICE(VEND_TD, PRODUCT_ID)},
        { },  // 最后一个元素必须是空
    }
    MODULE_DEVICE_TABLE(usb, id_table);

4.2.USB设备

USB核心层使用struct usb_device结构来表示一个USB设备。当USB控制器检测到设备连接时,就会分配一个struct usb_device结构体,然后注册到总线设备列表中,然后匹配对应的驱动。

    [include/linux/usb.h]
    struct usb_device {
        int		devnum;  // 该设备在总线上的序号
        char		devpath[16];  // USB拓扑路径
        u32		route;
        enum usb_device_state	state;  // 状态
        enum usb_device_speed	speed;  // 速度
        struct usb_tt	*tt;  // 事务转换(如高速接口兼容低速设备)
        int		ttport;;
        struct usb_device *parent;
        struct usb_bus *bus;
        struct usb_host_endpoint ep0;  // 端点0
        struct device dev;
        struct usb_device_descriptor descriptor;  // 设备描述符
        struct usb_host_bos *bos;
        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;  // GET_DESCRIPTOR命令返回的描述符原始字符串
        unsigned short bus_mA;  // 总线电流
        u8 portnum;  // HUB端口号
        u8 level;  // USB设备树层级
        ......
    };

4.3.USB主机控制器驱动

在Linux内核中,使用struct usb_hcd描述USB主机控制区驱动,包含了主机控制器的‘家务’信息、硬件资源、状态描述和用于控制主机控制器的hc_driver等。struct usb_hcdhc_driver成员非常重要,包含了操作主机控制器的所有方法。

    [include/linux/usb/hcd.h]
    struct usb_hcd {
        struct usb_bus		self;		/* hcd is-a bus */
        struct kref		kref;		/* reference counter */
        const char		*product_desc;	/* 厂商字符串 */
        int			speed;		/* 主机控制器根HUB的速度
        char			irq_descr[24];	/* driver + bus # */
        struct timer_list	rh_timer;	/* drives root-hub polling */
        struct urb		*status_urb;	/* the current status urb */
        // USB主机控制器操作函数集合
        const struct hc_driver	*driver;	/* hw-specific hooks */
        // OTG和某些USB控制器需要和PHY交互
        struct usb_phy		*usb_phy;
        struct phy		*phy;
        unsigned long		flags;
        unsigned int		irq;		/* irq allocated */
        void __iomem		*regs;		/* device memory/io */
        resource_size_t		rsrc_start;	/* memory/io resource start */
        resource_size_t		rsrc_len;	/* memory/io resource length */
        unsigned		power_budget;	/* in mA, 0 = no limit */
        struct giveback_urb_bh  high_prio_bh;
        struct giveback_urb_bh  low_prio_bh;
        struct mutex		*bandwidth_mutex;
        struct usb_hcd		*shared_hcd;
        struct usb_hcd		*primary_hcd;
        #define HCD_BUFFER_POOLS	4
        struct dma_pool		*pool[HCD_BUFFER_POOLS];
        int			state;
        unsigned long hcd_priv[0]  // 私有数据
                __attribute__ ((aligned(sizeof(s64))));
    };

urb_enqueue函数非常关键,上层通过usb_submit_urb提交一个USB请求后,该函数内部通过调用usb_hcd_submit_urb函数,最终调用到urb_enqueue

    [include/linux/usb/hcd.h]
    struct hc_driver {
        const char	*description;	/* "ehci-hcd" etc */
        const char	*product_desc;	/* product/vendor string */
        size_t		hcd_priv_size;	/* size of private data */
        /* irq handler */
        irqreturn_t	(*irq) (struct usb_hcd *hcd);
        int	flags;
        /* called to init HCD and root hub */
        int	(*reset) (struct usb_hcd *hcd);
        int	(*start) (struct usb_hcd *hcd);
        int	(*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
        int	(*pci_resume)(struct usb_hcd *hcd, bool hibernated);
        void	(*stop) (struct usb_hcd *hcd);
        /* shutdown HCD */
        void(*shutdown) (struct usb_hcd *hcd);
        /* return current frame number */
        int	(*get_frame_number) (struct usb_hcd *hcd);
        /* manage i/o requests, device state */
        int	(*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
        int	(*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status);
        int	(*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,gfp_t mem_flags);
        void(*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);
        void(*endpoint_disable)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);
        void(*endpoint_reset)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);
        int	(*hub_status_data) (struct usb_hcd *hcd, char *buf);
        int	(*hub_control) (struct usb_hcd *hcd,u16 typeReq, u16 wValue, u16 wIndex,
                    char *buf, u16 wLength);
        int	(*bus_suspend)(struct usb_hcd *);
        int	(*bus_resume)(struct usb_hcd *);
        ......
    };

在Linux内核中,使用usb_create_hcd函数创建主机控制器,使用usb_add_hcdusb_remove_hcd函数注册、注销主机控制器。

    [include/linux/usb/hcd.h]
    // 创建主机控制器struct usb_hcd结构体并初始化。driver为struct hc_driver结构体指针,dev为主机
    // 控制器的设备结构体,保存在hcd->self.controller中。bus_name为总线名称,保存在hcd->self.bus_name。
    struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
            struct device *dev, const char *bus_name)
    // 初始化并注册主机控制器struct usb_hcd结构体。hcd为struct usb_hcd结构体指针。irqnum为中断号。
    // irqflags为中断标志
    int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags)
    // 注销主机控制器struct usb_hcd结构体。hcd为struct usb_hcd结构体指针。
    void usb_remove_hcd(struct usb_hcd *hcd)

4.4.USB请求块URB

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

4.4.1.URB数据结构

struct urb结构体由两部分组成,第一部分是私有的,只能被usb核心层或主机控制器使用,第二部分是公有的,可被usb设备驱动程序使用。

    [include/linux/usb.h]
    struct urb {
        // 以下私有:usb核心层或主机控制器用
        struct kref kref;		/* URB引用计数 */
        void *hcpriv;			/* 主机控制器私有数据 */
        atomic_t use_count;		/* 并发提交个数 */
        atomic_t reject;		/* 提交失败的数量 */
        int unlinked;			/* unlink error code */
        // 以下公用:可被驱动使用的成员
        struct list_head urb_list;	    /* urb组成的链表 */
        struct list_head anchor_list;	/* the URB may be anchored */
        struct usb_anchor *anchor;
        struct usb_device *dev;		    /* 关联的设备 */
        struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */

        pipe用来查找端点队列,队列的特性定义在端点描述符中。pipe的各位定义如下:   
        bit7-0:bit7数据传输方向,0 = Host-to-Device [Out]1 = Device-to-Host [In]
        bit8-14:USB设备地址(编号),bit positions known to uhci-hcd
        bit15-18:端点地址(编号),... bit positions known to uhci-hcd,可找到对应的端点
        bit30-31:pipe的类型,00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
        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 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];  // 等时数据包
    };

使用usb_sndctrlpipeusb_rcvctrlpipe宏获取控制传输的发送和接收pipe,使用usb_sndisocpipeusb_rcvisocpipe宏获取等时传输的发送和接收pipe,使用usb_sndbulkpipeusb_rcvbulkpipe宏获取批量传输的发送和接收pipe,使用usb_sndintpipeusb_rcvintpipe宏获取中断传输的发送和接收pipe。

    #define USB_DIR_OUT			0		/* 数据传输方向 to device */
    #define USB_DIR_IN			0x80		/* 数据传输方向 to host */
    #define PIPE_ISOCHRONOUS		0  // pipe类型
    #define PIPE_INTERRUPT			1
    #define PIPE_CONTROL			2
    #define PIPE_BULK			3
    static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
    {
        return (dev->devnum << 8) | (endpoint << 15);
    }
    #define usb_sndctrlpipe(dev, endpoint)	\
        ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
    #define usb_rcvctrlpipe(dev, endpoint)	\
        ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
    #define usb_sndisocpipe(dev, endpoint)	\
        ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))
    #define usb_rcvisocpipe(dev, endpoint)	\
        ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
    #define usb_sndbulkpipe(dev, endpoint)	\
        ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
    #define usb_rcvbulkpipe(dev, endpoint)	\
        ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
    #define usb_sndintpipe(dev, endpoint)	\
        ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))
    #define usb_rcvintpipe(dev, endpoint)	\
        ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
4.4.2.urb的操作函数

(1)分配和释放URB
使用usb_alloc_urb分配一个urb结构体,使用usb_free_urb释放一个urb结构体。参数iso_packets表示分配等时数据包的数量,若为0则不分配,mem_flags为分配内存的标志,调用kmalloc时使用。urb不宜静态创建,因为这可能破环USB核心层使用的URB引用计数。

    [include/linux/usb.h]
    // 成功返回urb的指针,否则返回NULL
    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
    void usb_free_urb(struct urb *urb)

(2)初始化URB
urb结构体在使用之前要和USB设备端点对应起来,需要根据数据传递方式进行初始化。
中断传输方式(端点)使用usb_fill_int_urb初始化urb结构体,参数pipe比较重要,根据pipe可以知道对应的USB设备、端点、数据传输方向及数据传输类型。对应中断传输,可使用usb_sndintpipeusb_rcvintpipe宏来获取pipe。

    [include/linux/usb.h]
    // urb-urb结构体的指针
    // dev-urb关联的usb设备结构体指针
    // pipe-端点的管道,
    // transfer_buffer-传输缓冲区指针,和urb一样,不能是静态缓冲区,不能使用kmalloc分配,
    //     必须使用专门的函数usb_alloc_coherent分配
    // buffer_length-传输缓冲区transfer_buffer的长度
    // complete_fn-传输完成的回调函数
    // context-complete_fn函数的上下文
    // interval-urb被调度的间隔,不宜太大也不宜太小,必须在规定的范围内
    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)
    [include/linux/usb.h]
    // 批量传输方式(端点)使用usb_fill_bulk_urb初始化urb结构体
    // pipe-可使用usb_sndbulkpipe和usb_rcvbulkpipe宏来获取。批量传输没有interval参数
    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)
    [include/linux/usb.h]
    // 控制传输方式(端点)使用usb_fill_control_urb初始化urb结构体
    // pipe-可使用usb_sndctrlpipe和usb_rcvctrlpipe宏来获取
    // setup_packet-控制传输和批量传输相比,多了一个setup_packet参数,setup_packet指向设置数据包的缓冲区。
    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结构体。需要手动初始化并设置URB_ISO_ASSP标志。
(3)异步提交URB
urb结构体分配和初始化完成后,就可以使用usb_submit_urb函数将请求异步的提交到USB核心层了,提交成功返回0,否则返回小于0的错误代码,请求完成后回调函数complete将被调用。urb指向要提交的urb结构体,mem_flagskmalloc函数分配内存时的标志。在将URB提交到USB核心层后,直到完成函数被调用之前,不要操作urb结构体中的任何成员。usb_submit_urb在原子上下文和进程上下文中都可以调用,mem_flags参数需要根据调用环境进行设置。如下所示:
GFP_ATOMIC:在原子上下文环境中使用,如中断处理函数、中断下半本部分、tasklet、定时器处理函数、USR完成函数complete、持有自旋锁或读写锁时将current->stat设置为非TASK_RUNNING
GFP_NOIO:在存储设备的块I/O和错误处理路径中使用。
GFP_KERNEL:没有使用GFP_ATOMICGFP_NOIO的理由,就使用此标志。
GFP_NOFS:没有使用过。

    [include/linux/usb.h]
    // 异步提交请求,complete回调函数被调用表明请求已完成
    int usb_submit_urb(struct urb *urb, gfp_t mem_flags)

(4)同步提交URB(信息)
下面三个接口可以同步提交URB(信息),常用于提交一些简单的信息,所以省略了创建和初始化urb结构体的步骤,在请求处理完后或者超时时间到才返回,提交完成返回0,否则返回小于0的错误码。这三个接口都不能用在硬件中断、软件中断及持有自旋锁的上下文环境(!in_interrupt ())中。usb_control_msg用于提交控制传输的请求,usb_interrupt_msg用于传输中断类型的请求,usb_bulk_msg用于传输批量类型的请求。

    [include/linux/usb.h]
    // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,request:USB信息请求值,
    // requesttype:USB信息请求类型,value:USB消息值,index:USB消息索引值,data:发送或接收缓冲区指针,
    // size:发送或接收缓冲区的大小,timeout:等待消息完成的时间,单位为毫秒,0为一直等待
    int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
                __u8 requesttype, __u16 value, __u16 index, void *data, 
                __u16 size, int timeout)
    // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,data:发送或接收缓冲区指针,
    // len:缓冲区中发送的字节数,actual_length:保存实际传输的字节数,
    // timeout:等待消息完成的时间,单位为毫秒,0为一直等待
    int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
		      void *data, int len, int *actual_length, int timeout)
    // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,data:发送或接收缓冲区指针,
    // len:缓冲区中发送的字节数,actual_length:保存实际传输的字节数,
    // timeout:等待消息完成的时间,单位为毫秒,0为一直等待
    int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
            void *data, int len, int *actual_length, int timeout)

(5)取消URB
usb_kill_urbusb_kill_urb可以取消一个urb,前者是异步取消,不等待取消成功就返回,若取消的urb正在执行,则执行完成后取消,后者是同步的,直到取消成功才返回。

    [include/linux/usb.h]
    void usb_unlink_urb(struct urb *urb)
    void usb_kill_urb(struct urb *urb)

(6)URB传输数据缓冲区的分配和释放
初始化USB请求块URB的参数transfer_buffer不能静态定义和使用kmalloc等,必须使用usb_alloc_coherent函数分配。因为USB数据内部传输用到了DMA,为了保证数据一致性,则必须要使用和DMA相关的内存分配的函数,usb_alloc_coherent函数内部调用了dma_alloc_coherent。此函数分配的内存是uncache的。使用usb_free_coherent释放分配的内存。

    [include/linux/usb.h]
    // dev:USB设备结构体
    // size:缓冲区长度
    // mem_flags:内存分配标志
    // dma:分配的内存对应的物理地址
    void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,
			 dma_addr_t *dma)
    void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
		       dma_addr_t dma)

5.实验

首先取消内核中的usb mouse驱动支持,路径如下,取消USB HID transport layer的驱动支持。

Device Drivers > 
    HID support > 
        USB HID support > 
            <> USB HID transport layer

使用insmod usb_mouse.ko,插入USB鼠标,系统会打印鼠标信息,同时在dev目录下生成/dev/input/event4设备节点。使用hexdump /dev/input/event4命令,此时移动鼠标,会看到输出的数据。

6.USB鼠标驱动源码

	/*===========================usb_mouse.h================================*/
	#ifndef USB_MOUSE_H
	#define USB_MOUSE_H
	#include <linux/usb.h>
	#include <uapi/linux/usb/ch9.h>
	#include <linux/usb/input.h>
	#include <linux/input.h>
	struct usb_mouse
	{
	    struct usb_host_interface *intf;
	    struct usb_device* dev;
	    struct usb_endpoint_descriptor* endpoint;
	    struct urb* irq_urb;
	    struct input_dev* input;
	    int maxlen;
	    unsigned int pipe;
	    char* data;  // 虚拟地址
	    dma_addr_t data_dma;  // 物理地址
	    char name[128];
	    char phys[64];
	};
#endif // USB_MOUSE_H
	/*===========================usb_mouse.c================================*/
	#include "usb_mouse.h"
	#include <linux/kernel.h>
	#include <linux/slab.h>
	#include <linux/module.h>
	#include <linux/init.h>
	#include <linux/hid.h>
	#include <linux/device.h>
	
	// 参考usbmouse.c文件,路径:usbmouse.c	drivers/hid/usbhid/usbmouse.c	
	
	struct usb_mouse* usb_mouse_dev = NULL;
	
	static int usb_mouse_open(struct input_dev *dev)
	{
	    int ret = 0;
		struct usb_mouse* usb_mouse = input_get_drvdata(dev);
		usb_mouse->irq_urb->dev = usb_mouse->dev;
		if ((ret = usb_submit_urb(usb_mouse->irq_urb, GFP_KERNEL)) < 0) {
	        dev_err(&usb_mouse->dev->dev, "usb_submit_urb failed, errno %d\n", ret);
			return -EIO;
	    }
		return 0;
	}
	
	static void usb_mouse_close(struct input_dev *dev)
	{
		struct usb_mouse* usb_mouse = input_get_drvdata(dev);
		usb_kill_urb(usb_mouse->irq_urb);
	}
	
	
	static void usb_mouse_irq(struct urb *urb)
	{
		struct usb_mouse* usb_mouse = urb->context;
		char* data = usb_mouse->data;
		struct input_dev* dev = usb_mouse->input;
		int status, i;
	    for (i = 0; i < usb_mouse->maxlen; i++) {
	        printk("%02x ", data[i]);
	    }
	    printk("\n");
	    // 有标准的完成函数,不需要判断usb的状态
	    /*
		switch (urb->status) {  // 获取urb提交结果
		case 0:			// success 
			break;
		case -ECONNRESET:	// unlink 
		case -ENOENT:
		case -ESHUTDOWN:
			return;
		// -EPIPE:  should clear the halt 
		default:		// error 
			goto resubmit;
		}
	    */
		input_report_key(dev, BTN_LEFT,   data[0] & 0x01);
		input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);
		input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
		input_report_key(dev, BTN_SIDE,   data[0] & 0x08);
		input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);
	
		input_report_rel(dev, REL_X,     data[1]);
		input_report_rel(dev, REL_Y,     data[2]);
		input_report_rel(dev, REL_WHEEL, data[3]);
	
		input_sync(dev);
	//resubmit:
		status = usb_submit_urb(urb, GFP_ATOMIC);
		if (status)
			dev_err(&usb_mouse->dev->dev,
				"can't resubmit intr, %s-%s/input0, status %d\n",
				usb_mouse->dev->bus->bus_name,
				usb_mouse->dev->devpath, status);
	}
	
	static int usb_mouse_probe(struct usb_interface* intf, const struct usb_device_id* id)
	{
		int error = -ENOMEM;
	    struct usb_device* usbdev;
	    usbdev = interface_to_usbdev(intf);
	    usb_mouse_dev = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL);
	    if (NULL == usb_mouse_dev) {
			dev_err(&usbdev->dev, "usb_mouse_dev kmalloc failed\n");
	        return error; 
	    }
	    memset(usb_mouse_dev, 0, sizeof(struct usb_mouse));
	    usb_mouse_dev->dev = usbdev;
	    dev_info(&usbdev->dev, "bcdUSB = 0x%x\n", usb_mouse_dev->dev->descriptor.bcdUSB);  // USB版本号
	    dev_info(&usbdev->dev, "idVendor = 0x%x\n", usb_mouse_dev->dev->descriptor.idVendor);  // 厂商编号
	    dev_info(&usbdev->dev, "idProduct = 0x%x\n", usb_mouse_dev->dev->descriptor.idProduct);  // 设备出场编号
	
	    usb_mouse_dev->intf = intf->cur_altsetting;
	    // 检查此端点数量是否为1(除端点0)
		if (usb_mouse_dev->intf->desc.bNumEndpoints != 1) {
			error = -ENODEV;
			dev_err(&usbdev->dev, "bNumEndpoints error, bNumEndpoints %u\n", 
	                usb_mouse_dev->intf->desc.bNumEndpoints);
	        goto free_usb_mouse;
	    }
	    // 获取主机侧与鼠标通信的端点
		usb_mouse_dev->endpoint = &usb_mouse_dev->intf->endpoint[0].desc;
	    // 判断是否是中断输入端点
		if (!usb_endpoint_is_int_in(usb_mouse_dev->endpoint)) {
			error = -ENODEV;
	    	dev_err(&usbdev->dev, "endpoint isn't int in\n");
	        goto free_usb_mouse;
	    }
	    // 获取pipe,根据pipe可以知道对应的USB设备、端点、数据传输方向及数据传输类型
	    usb_mouse_dev->pipe = usb_rcvintpipe(usb_mouse_dev->dev, 
	            usb_mouse_dev->endpoint->bEndpointAddress);
	    // 获取本端点接收或发送数据包的最大字节数
	    usb_mouse_dev->maxlen = usb_maxpacket(usb_mouse_dev->dev, usb_mouse_dev->pipe, 
	            usb_pipeout(usb_mouse_dev->pipe));
	    dev_info(&usbdev->dev, "wMaxPacketSize 0x%x\n", usb_mouse_dev->maxlen);
	    // 分配usb数据传输的缓冲区,不能使用kmalloc分配
	    usb_mouse_dev->data = usb_alloc_coherent(usb_mouse_dev->dev, 
	            usb_mouse_dev->maxlen, GFP_ATOMIC, &usb_mouse_dev->data_dma);
	    if (NULL == usb_mouse_dev->data) {    
	        dev_err(&usbdev->dev, "usb mouse alloc usb coherent buffer error\n");  
	        goto free_usb_mouse;
	    }
	    // 分配一个usb请求块
	    usb_mouse_dev->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
	    if (NULL == usb_mouse_dev->irq_urb) {
	        dev_err(&usbdev->dev, "usb mouse alloc usb urb error\n");  
	        goto free_usb_coherent;
	    }
	    // 填充usb请求块
	    usb_fill_int_urb(usb_mouse_dev->irq_urb, usb_mouse_dev->dev, 
	            usb_mouse_dev->pipe, usb_mouse_dev->data, usb_mouse_dev->maxlen,
	            // usb_mouse_dev保存到struct urb的context变量中
	            usb_mouse_irq, usb_mouse_dev, usb_mouse_dev->endpoint->bInterval);
	    dev_info(&usbdev->dev, "bInterval 0x%x\n", usb_mouse_dev->endpoint->bInterval);
	    usb_mouse_dev->irq_urb->transfer_dma = usb_mouse_dev->data_dma;
		usb_mouse_dev->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	
	
	    usb_set_intfdata(intf, usb_mouse_dev);
	    usb_mouse_dev->input = input_allocate_device();
	    if (NULL == usb_mouse_dev->input) {
	        dev_err(&usbdev->dev, "usb mouse allocate input device error\n");
	        goto free_usb_urb;
	    }
		if (usb_mouse_dev->dev->manufacturer)
			strlcpy(usb_mouse_dev->name, usb_mouse_dev->dev->manufacturer, 
	                sizeof(usb_mouse_dev->name));
		if (usb_mouse_dev->dev->product) {
			if (usb_mouse_dev->dev->manufacturer)
				strlcat(usb_mouse_dev->name, " ", sizeof(usb_mouse_dev->name));
			strlcat(usb_mouse_dev->name, usb_mouse_dev->dev->product, 
	                sizeof(usb_mouse_dev->name));
		}
		if (!strlen(usb_mouse_dev->name))
			snprintf(usb_mouse_dev->name, sizeof(usb_mouse_dev->name),
				 "USB HIDBP Mouse %04x:%04x",
				 le16_to_cpu(usb_mouse_dev->dev->descriptor.idVendor),
				 le16_to_cpu(usb_mouse_dev->dev->descriptor.idProduct));
		usb_make_path(usb_mouse_dev->dev, usb_mouse_dev->phys, sizeof(usb_mouse_dev->phys));
		strlcat(usb_mouse_dev->phys, "/input0", sizeof(usb_mouse_dev->phys));
		usb_mouse_dev->input->name = usb_mouse_dev->name;
		usb_mouse_dev->input->phys = usb_mouse_dev->phys;
	    dev_info(&usbdev->dev, "name = %s\n", usb_mouse_dev->name);
	    dev_info(&usbdev->dev, "phys = %s\n", usb_mouse_dev->phys);
	
		usb_to_input_id(usb_mouse_dev->dev, &usb_mouse_dev->input->id);
		usb_mouse_dev->input->dev.parent = &intf->dev;
		usb_mouse_dev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
		usb_mouse_dev->input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
			BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
		usb_mouse_dev->input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
		usb_mouse_dev->input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
			BIT_MASK(BTN_EXTRA);
		usb_mouse_dev->input->relbit[0] |= BIT_MASK(REL_WHEEL);
	    input_set_drvdata(usb_mouse_dev->input, usb_mouse_dev);
	    usb_mouse_dev->input->open = usb_mouse_open;
		usb_mouse_dev->input->close = usb_mouse_close;
	    error = input_register_device(usb_mouse_dev->input);
	    if (error < 0) {
	        dev_err(&usbdev->dev, "usb mouse register input device error, errno %d\n", error);
	        goto free_input_device;
	    }
	#if 0  // 测试用
	    // 提交urb
	    if (error = usb_submit_urb(usb_mouse_dev->irq_urb, GFP_ATOMIC)) {
	        dev_err(&usbdev->dev, "usb_submit_urb failed, errno %d\n", error);
			return error;        
	    }
	#endif
	
	    return 0;
	free_input_device:
	    input_free_device(usb_mouse_dev->input);
	free_usb_urb:
	    usb_free_urb(usb_mouse_dev->irq_urb);
	free_usb_coherent:
	    usb_free_coherent(usb_mouse_dev->dev, usb_mouse_dev->maxlen, 
	                usb_mouse_dev->data, usb_mouse_dev->data_dma);
	free_usb_mouse:
	    kfree(usb_mouse_dev);
	    usb_mouse_dev = NULL;
	    return error;
	}
	
	static void usb_mouse_disconnect(struct usb_interface *intf)
	{
	    dev_info(&usb_mouse_dev->dev->dev, "usb_mouse_disconnect\n");
	    usb_kill_urb(usb_mouse_dev->irq_urb);
	    input_unregister_device(usb_mouse_dev->input);
	    input_free_device(usb_mouse_dev->input);
	    usb_free_urb(usb_mouse_dev->irq_urb);
	    usb_free_coherent(usb_mouse_dev->dev, usb_mouse_dev->maxlen, 
	                usb_mouse_dev->data, usb_mouse_dev->data_dma);
	    kfree(usb_mouse_dev);
	    usb_mouse_dev = NULL;
	}
	
	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 struct usb_driver usb_mouse_driver = {
		.name		= "usb_mouse",
		.probe		= usb_mouse_probe,
		.disconnect	= usb_mouse_disconnect,
		.id_table	= usb_mouse_id_table,
	};
	
	#if 0
	static int __init usb_mouse_init(void)
	{
	    int ret = usb_register(&usb_mouse_driver);
	    if (ret < 0)
	        pr_err("usb mouse register failed, errno: %d\n", ret);
	    return ret;
	}
	static void __exit usb_mouse_exit(void)
	{
	    usb_deregister(&usb_mouse_driver);
	}
	module_init(usb_mouse_init);
	module_exit(usb_mouse_exit);
	#else
	module_driver(usb_mouse_driver, usb_register, usb_deregister);
	#endif // 1
	
	MODULE_LICENSE("GPL");
	MODULE_AUTHOR("liyang.plus@foxmail.com");
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值