zoukankan      html  css  js  c++  java
  • Linux驱动之USB鼠标驱动编写

    本篇博客分以下几部分讲解

    1、介绍USB四大描述

    2、介绍USB鼠标驱动程序功能及框架

    3、介绍程序用到的结构体

    4、介绍程序用到的函数

    5、编写程序

    6、测试程序

    1、介绍USB四大描述符

    USB设备驱动程序里定义了许多与驱动程序密切相关的描述符。这里介绍一下四种比较关键的描述符:设备描述符配置描述符接口描述符端点描述符。这几个描述符都位于includelinuxusbch9.h中,先看一下每个描述直接的关系,从图中可以看出每一个查到USB主机上的USB设备都有一个设备描述符,设备描述符下面可以接多个配置描述符,配置描述符下面又可以接多个


    当USB设备接到USB控制器上后,USB控制器第一次读取到的数据包,总共8字节
    /*当USB设备接到USB控制器上后,USB控制器第一次读取到的数据包,总共8字节*/
    struct usb_ctrlrequest {
        __u8 bRequestType;
        __u8 bRequest;
        __le16 wValue;
        __le16 wIndex;
        __le16 wLength;
    } __attribute__ ((packed));

    设备描述符是在设备连接时,主机第一个读取的描述符,包含了主机需要从设备读取的基本信息。设备描述符有14个字段,如下所示。依照功能来分,设备描述符的字段包含了描述符本身、设备、配置以及类别4大类。

    /* USB_DT_DEVICE: Device descriptor */
    struct usb_device_descriptor {
        __u8  bLength;          //描述符长度
        __u8  bDescriptorType;  //描述符类型
    
        __le16 bcdUSB;          //USB规范版本号码,BCD码表示
        __u8  bDeviceClass;     //USB设备类别
        __u8  bDeviceSubClass;  //USB设备子类别
        __u8  bDeviceProtocol;  //USB设备协议码
        __u8  bMaxPacketSize0;  //端点0的最大信息包大小(端点0用于控制传输,既能输出也能输入)
        __le16 idVendor;        //厂商ID
        __le16 idProduct;    //产品ID
        __le16 bcdDevice;       //设备版本编号,BCD码表示
        __u8  iManufacturer;    //制造者的字符串描述符的索引值
        __u8  iProduct;         //产品的字符串描述符的索引值
        __u8  iSerialNumber;    //序号的字符串描述符的索引值
        __u8  bNumConfigurations;//可能配置的数目
    } __attribute__ ((packed));

    在读取设备描述符后,主机可以读取该设备的配置、接口以及端点描述符。每一个设备都至少有一个配置描述符,用来描述该设备的特性与能力。通常一个设置配置就已经足够,不过多用途或模式的设备可以支持多个设置配置,在同一时间只能有一个作用。 每一个设置配置都需要一个描述符,此描述符包含设备中的电源使用以及支持的接口数目。每一个配置描述符都有附属的描述符,包含一个或多个接口描述符,以及选择性的端点描述符。

    struct usb_config_descriptor {
        __u8  bLength;             //描述符长度
        __u8  bDescriptorType;     //描述符类型02
    
        __le16 wTotalLength;       //此配置传回的所有数据大小(字节)
        __u8  bNumInterfaces;      //此配置支持的接口数目
        __u8  bConfigurationValue; //Set_configuration与get_configuration要求的标识符
        __u8  iConfiguration;      //此配置的字符串描述符的索引值
        __u8  bmAttributes;        //自身电源/总线电源以及远程唤醒设置
        __u8  bMaxPower;           //需要总线电源,标识法为(最大mA/2)
    } __attribute__ ((packed));

    接口表示被设备的特性或功能所使用的端点、配置的接口描述符,包含该接口所支持的端点信息。每一个设置配置必须支持一个接口,对许多设备来说,一个接口就已经足够,不过一个设置配置,可以同时又多个作用中的接口。每一个接口有它自己的接口描述符,此接口所支持的所有端点又各有一个附属描述符。如果一个设备拥有同时多个作用中接口的设置配置,它就是一个复合设备,主机会为每一个接口,加载一个驱动程序。

    /* USB_DT_INTERFACE: Interface descriptor */
    struct usb_interface_descriptor {
        __u8  bLength;                     //描述符长度
        __u8  bDescriptorType;   //描述符类型04
    
        __u8  bInterfaceNumber;  //识别此接口的数量
        __u8  bAlternateSetting; //用来选择一个替代设置的数值
        __u8  bNumEndpoints;     //除了端点0外,支持的端点数量
        __u8  bInterfaceClass;   //接口类别码
        __u8  bInterfaceSubClass;//接口子类别码
        __u8  bInterfaceProtocol;//接口协议码
        __u8  iInterface;                 //此接口的字符串描述符的索引值
    } __attribute__ ((packed));

    每一个指定在接口描述符内的端点,都有一个端点描述符。端点0没有端点描述符,因为每一个端点都必须支持断点0。设备描述符包含最大信息包大小的信息,而端点描述符则是定义端点的其他信息。

    /* USB_DT_ENDPOINT: Endpoint descriptor */
    struct usb_endpoint_descriptor {
        __u8  bLength;                //描述符长度
        __u8  bDescriptorType;//描述符类型05
    
        __u8  bEndpointAddress;//端点数目与方向
        __u8  bmAttributes;         //支持的传输类型
        __le16 wMaxPacketSize; //支持的最大信息包大小
        __u8  bInterval;             //最大延迟/轮询时距/NAK速率
    
        /* NOTE:  these two are _only_ in audio endpoints. */
        /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
        __u8  bRefresh;
        __u8  bSynchAddress;
    } __attribute__ ((packed));

     2、介绍USB鼠标驱动程序功能及框架

    USB鼠标一共有三个按键:左键、右键、中键。在这里把这三个分别作为l、s、enter键。这就是这个USB设备驱动程序的功能。

    要实现这个功能,还是需要用到输入子系统的框架,与触摸屏驱动一样,再回顾一下输入系统的框架

     输入子系统按框架可以分为设备驱动层、事件层、以及核心层。

    整个调用过程如下:

    app_read->evdev_read->kbtab_irq->input_report_key->input_event->evdev_event->evdev_read

    应用层           事件层          设备层         核心层                   核心层            事件层          事件层

    如果要自己添加一个输入子系统的设备,只需要添加设备层的文件即可。

    1、在里面添加设备层input_dev结构并初始化

    2、编写中断处理程序

     在USB驱动程序中,中断处理程序就不是真正的CPU中断的处理程序了。而是USB总线驱动程序接收完成一包数据后的回调函数。

    编写程序步骤:其中硬件相关的设置就是设置USB驱动设备传输的数据来源、数据存放地址、数据长度、怎么样处理数据

    /* a、分配一个 input_dev结构体*/
    
    /* b、设置 */
    /* b.1 能产生哪类事件 */
    /* b.2 能产生哪些事件 */
    
    /* c、注册 */
    
    /* d、硬件相关的设置 */

    3、介绍程序用到的结构体

     1、struct input_dev结构体

    struct input_dev {
    
        void *private;
    
        const char *name;//设备名字
        const char *phys;//文件路径,比如 input/buttons
        const char *uniq;
        struct input_id id;
    
        unsigned long evbit[NBITS(EV_MAX)];//表示支持哪类事件,常用于以下几种事件(可以多选)
        //EV_SYN      同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
        //EV_KEY       键盘事件
        //EV_REL       (relative)相对坐标事件,比如鼠标
        //EV_ABS       (absolute)绝对坐标事件,比如摇杆、触摸屏感应
        
        unsigned long keybit[NBITS(KEY_MAX)];//存放支持的键盘按键值
        //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)、BTN_TOUCH(触摸屏的按键)
        
        
        unsigned long relbit[NBITS(REL_MAX)];//存放支持的相对坐标值
        unsigned long absbit[NBITS(ABS_MAX)];//存放支持的绝对坐标值
        unsigned long mscbit[NBITS(MSC_MAX)];
        unsigned long ledbit[NBITS(LED_MAX)];
        unsigned long sndbit[NBITS(SND_MAX)];
        unsigned long ffbit[NBITS(FF_MAX)];
        unsigned long swbit[NBITS(SW_MAX)];
    
        ...
        ...
    
        int absmax[ABS_MAX + 1];//绝对坐标的最大值
        int absmin[ABS_MAX + 1];//绝对坐标的最小值
        int absfuzz[ABS_MAX + 1];//绝对坐标的干扰值,默认为0,
        int absflat[ABS_MAX + 1];//绝对坐标的平焊位置,默认为0
        
        ...
        ...
    };

    2、struct usb_device结构体,描述整个USB设备的结构体,一般不会用到里面的变量,这里不做详细注释。

    3、struct usb_interface结构体,这个结构体是由USB核心传递给USB驱动程序的,用它来描述USB接口。USB驱动程序负责后续的控制。

    struct usb_interface {
        /* array of alternate settings for this interface,
         * stored in no particular order */
        struct usb_host_interface *altsetting;//一个接口结构体数组,包含了所有可能用于该接口的可选设置
    
        struct usb_host_interface *cur_altsetting;    /* the currently active alternate setting *///表示该接口的当前活动设置
        unsigned num_altsetting;    /* number of alternate settings *///可选设置数量
    
        int minor;            /* minor number this interface is bound to */      //USB核心分配的次设备号
        enum usb_interface_condition condition;        /* state of binding */   
        unsigned is_active:1;        /* the interface is not suspended */
        unsigned needs_remote_wakeup:1;    /* driver requires remote wakeup */
    
        struct device dev;        /* interface specific device info */
        struct device *usb_dev;        /* pointer to the usb class's device, if any */
        int pm_usage_cnt;        /* usage counter for autosuspend */
    };

    4、struct usb_host_interface结构体,主要用来描述USB接口

    struct usb_host_interface {
        struct usb_interface_descriptor    desc;//接口描述符
    
        /* array of desc.bNumEndpoint endpoints associated with this
         * interface setting.  these will be in no particular order.
         */
        struct usb_host_endpoint *endpoint;//端点描述符指针
    
        char *string;        /* iInterface string, if present *///接口描述符的名字字符指针
        unsigned char *extra;   /* Extra descriptors *///额外的描述字符指针
        int extralen;//额外描述大小
    };

    5、usb_host_endpoint ,主要用来描述USB端点

    struct usb_host_endpoint {
        struct usb_endpoint_descriptor    desc;//端点描述符
        struct list_head        urb_list;//端点描述符列表指针,可以根据这个结构体找到所有的处于同一指针链表的usb_host_endpoint结构
        void                *hcpriv;
        struct ep_device         *ep_dev;    /* For sysfs info */
    
        unsigned char *extra;   /* Extra descriptors */
        int extralen;
    };

    6、struct usb_endpoint_descriptor结构体,端点描述符

    /* USB_DT_ENDPOINT: Endpoint descriptor */
    struct usb_endpoint_descriptor {
        __u8  bLength;                //描述符长度
        __u8  bDescriptorType;//描述符类型05
    
        __u8  bEndpointAddress;//端点数目与方向
        __u8  bmAttributes;         //支持的传输类型
        __le16 wMaxPacketSize; //支持的最大信息包大小
        __u8  bInterval;             //最大延迟/轮询时距/NAK速率
    
        /* NOTE:  these two are _only_ in audio endpoints. */
        /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
        __u8  bRefresh;
        __u8  bSynchAddress;
    } __attribute__ ((packed));

    7、struct urb结构体,是一个USB请求块,作用是和所有的USB设备通讯。

    struct urb
    {
        /* private: usb core and host controller only fields in the urb */
        struct kref kref;        /* reference count of the URB */
        spinlock_t lock;        /* lock for the URB */
        void *hcpriv;            /* private data for host controller */
        atomic_t use_count;        /* concurrent submissions counter */
        u8 reject;            /* submissions will fail */
    
        /* 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 usb_device *dev;     /* (in) pointer to associated device *///urb所发送的目标usb_devices指针
        unsigned int pipe;        /* (in) pipe information */                //端点信息,可以设置为控制、批量、中断、等时等端点输入或输出
        int status;            /* (return) non-ISO status */                    //当urb结束后或者正在被USB核心处理时,该变量被设置为当前的状态
        unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/   //USB数据传输标志,可以通过这个标志判断数据传输情况
        void *transfer_buffer;        /* (in) associated data buffer */      //以DMA方式传输数据到USB设备的缓存区指针,必须用kmalloc来创建
        dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer *///以DMA方式传输数据到USB设备的地址
        int transfer_buffer_length;    /* (in) data buffer length */        //以DMA方式传输数据到USB设备的缓冲区的长度
        int actual_length;        /* (return) actual transfer length */             //当urb结束后,实际接收到的或者发送的数据长度
        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) */  //urb被轮询的时间间隔,仅对中断或者等时urb有效
        int error_count;        /* (return) number of ISO errors */          //用于等时urb结束后,报告的类型错误的数量
        void *context;            /* (in) context for completion */  //指向一个可用被USB驱动程序设置的数据块
        usb_complete_t complete;    /* (in) completion routine *///指向一个结束处理例程的指针,当urb被完全传输或者错误时调用这个函数
        struct usb_iso_packet_descriptor iso_frame_desc[0];    //
                        /* (in) ISO ONLY */
    };

    8、struct usb_device_id结构体,提供不同类型的该驱动程序支持的USB设备,USB核心使用该列表来判断对于一个设备改使用哪一个驱动程序

    struct usb_device_id {
        /* which fields to match against? */
        __u16        match_flags;//在设备插上后需要匹配下面的哪几个参数来匹配驱动
    
        /* Used for product specific matches; range is inclusive */
        __u16        idVendor;//USB制造商ID
        __u16        idProduct;//USB产品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;//接口协议
    
        /* not matched against */
        kernel_ulong_t    driver_info;//
    };

    9、struct usb_driver结构体,USB驱动程序必须创建的主要结构体,它向USB核心代码描述USB驱动程序。

    struct usb_driver {
        const char *name;//执行驱动程序名字的指针
    
        int (*probe) (struct usb_interface *intf,
                  const struct usb_device_id *id);//指向USB驱动程序中的探测函数的指针
    
        void (*disconnect) (struct usb_interface *intf);//指向USB驱动程序中的断开函数的指针
    
        int (*ioctl) (struct usb_interface *intf, unsigned int code,
                void *buf);//执行USB驱动程序中的ioctl指针
    
        int (*suspend) (struct usb_interface *intf, pm_message_t message);//指向USB驱动程序中的挂起函数指针
        int (*resume) (struct usb_interface *intf);//指向USB驱动程序中的恢复函数指针
    
        void (*pre_reset) (struct usb_interface *intf);//
        void (*post_reset) (struct usb_interface *intf);
    
        const struct usb_device_id *id_table;//指向struct usb_device_id表的指针
    
        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
    };

    4、介绍程序用到的函数

    1、输入子系统相关的函数

    struct input_dev *input_allocate_device(void);//分配一个struct input_dev结构体,返回的是struct input_dev *
    inline void set_bit(int nr, volatile unsigned long *addr);//这是一个内联函数,在调用的时候展开,功能为设置*addr的nr位为1
    int input_register_device(struct input_dev *dev);//注册输入子系统设备驱动,输入参数为struct input_dev *
    void input_unregister_device(struct input_dev *dev);//反注册输入子系统的设备驱动,输入参数为struct input_dev *
    void input_free_device(s3c_ts_input);//释放分配的input_dev结构,,输入参数为struct input_dev *
    
    static inline void input_sync(struct input_dev *dev);//上传同步事件,表示这次事件数据已经传送完成了
    void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);//上传输入事件
    //struct input_dev *dev表示哪个输入子系统设备的事件上传
    //unsigned int type,表示上传的事件类型
    //unsigned int code,表示事件类型中的哪类事件
    //value表示事件的值

    2、USB核心相关的函数

    static inline int usb_register(struct usb_driver *driver);//注册一个usb驱动结构体driver到USB核心
    void usb_deregister(struct usb_driver *driver);//从USB核心释放一个usb驱动结构体driver
    usb_rcvintpipe(dev,endpoint);//这是一个宏。设置usb设备dev的端点endpoint为中断IN端点
    void *usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);//分配一个地址作为USB接收到的数据,返回分配地址的首地址
    //struct usb_device *dev是USB设备结构体
    //size_t size分配的内存的大小
    //gfp_t mem_flags是分配的标志
    //dma_addr_t *dma是分配完成后返回的物理地址
    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);//分配一个urb结构体
    //int iso_packets是等时数据包
    //gfp_t mem_flags内存分配标志
    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);//初始化即将被发送到USB设备的中断端点的urb
    //struct urb *urb是指向需要初始化的urb的指针
    //struct usb_device *dev是该urb所发送的目标USB设备
    //unsigned int pipe是该urb所发送的目标USB设备的特点端点。该值由usb_sndintpipe或usb_rcvintpipe函数创建
    //void *transfer_buffer用于保存外发数据或接收数据的缓冲区的指针
    //int buffer_length是transfer_buffer指针所指向缓存区的大小
    //usb_complete_t complete_fn指向当该urb结束之后调用的结束处理例程的指针
    //void *context指向一个小数据块,该块被添加到urb结构体中以便进行结束处理例程后面的查找
    //int interval该urb应该被调度的间隔
    int usb_submit_urb(struct urb *urb, gfp_t mem_flags);//提交urb
    //struct urb *urb表示需要提交的urb控制块
    //gfp_t mem_flags内存分配标志
    void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);//释放分配的usb缓存数据
    //struct usb_device *dev表示目标USB设备
    //size_t size释放的缓冲区的大小
    //void *addr指向释放的缓冲区的指针
    //dma_addr_t dma表示释放的缓冲区的物理地址
    int usb_unlink_urb(struct urb *urb);//释放urb请求块
    //struct urb *urb表示指向释放的urb请求块的指针

    5、编写程序

    直接放出程序源码,可以看到这个程序的结构和输入子系统的结构差不多。

    1、首先加载这个模块后,会调用usb_mouse_as_key_init函数,然后usb_mouse_as_key_driver 结构体被注册到USB核心

    2、当插上USB鼠标设备后,如果此设备和此驱动的接口类、接口子类、接口协议相同(位于usb_mouse_as_key_id_table ),那么usb_mouse_as_key_probe函数被调用

    3、usb_mouse_as_key_probe函数是核心函数,在里面做许多事情,具体看代码

    /* a、分配一个 input_dev结构体*/
    
    /* b、设置 */
    /* b.1 能产生哪类事件 */
    /* b.2 能产生哪些事件 */
    
    /* c、注册 */
    
    /* d、硬件相关的设置 */

    4、当按下USB按键后在第三步中设置的usb_mouse_as_key_irq函数被调用

    5、数据包会在usb_mouse_as_key_irq回调函数被处理。至于数据的含义需要自己根据USB设备来判断然后定义,我用的鼠标按键的值位于usb_buf[1]中,所以可以根据这个值判断是哪一个按键被按下或松开,然后上传事件到输入子系统。

    详细的解释可以参考如下代码:

    #include <linux/kernel.h>
    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/usb/input.h>
    #include <linux/hid.h>
    
    static struct input_dev *uk_dev; //定义一个输入子系统设备结构体
    static dma_addr_t usb_buf_phys; //物理地址
    static char *usb_buf;                  //从USB主控制器接收到的数据存放的导致
    static int len;                             //从USB主控制器接收到的数据的长度
    static struct urb *uk_urb;           //定义一个USB请求块
    
    
    
    /* 一包数据接收完成后处理函数*/
     static void usb_mouse_as_key_irq(struct urb *urb)
     {
    //    int i;
    //    static int cnt = 0;
    
    //    printk("data cnt %d: ", ++cnt);
    //    for (i = 0; i < len; i++)
    //    {
    //        printk("%02x ", usb_buf[i]);
    //    }
    //    printk("
    ");
    
        /* 
          * USB鼠标数据含义
         *  data[0]: bit0-左键, 1-按下, 0-松开
         *          bit1-右键, 1-按下, 0-松开
         *          bit2-中键, 1-按下, 0-松开 
         */
         
        static unsigned char pre_val;//前一个按键的按键值,每当按键值变化才上传
    
        if((pre_val & (1<<0)) != (usb_buf[1] & (1<<0)))//左键发生变化
        {
             input_event(uk_dev,EV_KEY, KEY_L, (usb_buf[1]?1:0));
             input_sync(uk_dev);                                                    //上传同步事件
        }
    
        if((pre_val & (1<<1)) != (usb_buf[1] & (1<<1)))//右键发生变化
        {
             input_event(uk_dev,EV_KEY, KEY_S, (usb_buf[1]?1:0));
             input_sync(uk_dev);                                                    //上传同步事件
        }
    
        if((pre_val & (1<<2)) != (usb_buf[1] & (1<<2)))//中键发生变化
        {
             input_event(uk_dev,EV_KEY, KEY_ENTER, (usb_buf[1]?1:0));
             input_sync(uk_dev);                                                    //上传同步事件
        }
        pre_val = usb_buf[1];
        
         /* 重新提交urb */
        usb_submit_urb(uk_urb, GFP_KERNEL);//提交URB,将URB的控制还给USB核心处理程序
     }
     
    static int usb_mouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
        struct usb_device *dev  = interface_to_usbdev(intf);//根据usb接口,取得usb设备
        struct usb_host_interface *interface;                        //定义一个USB主机控制器接口描述符
        struct usb_endpoint_descriptor *endpoint;                 //定义一个端点描述符
        int pipe;
        
        interface = intf->cur_altsetting;                               //获得usb控制器的接口描述符
    
        endpoint = &interface->endpoint[0].desc;//取得usb 控制器的第一个端点描述符
        
        
        printk("found usbmouse!
    ");
    
        printk("bcdUSB = %x
    ",dev->descriptor.bcdUSB);  //从USB设备描述符中获取USB版本
        printk("vidUSB = %x
    ",dev->descriptor.idVendor); //从USB设备描述符中获取厂商ID
        printk("pidUSB = %x
    ",dev->descriptor.idProduct);//从USB设备描述符中获取产品ID
    
        printk("bdcUSB = %x
    ",intf->cur_altsetting->desc.bInterfaceClass);//从USB设备获取设备类
        printk("bdsUSB = %x
    ",intf->cur_altsetting->desc.bInterfaceSubClass);//从USB设备获取设备从类
        printk("bdpUSB = %x
    ",intf->cur_altsetting->desc.bInterfaceProtocol);//从USB设备获取设备协议
    
        /* a、分配一个 input_dev结构体*/
        uk_dev = input_allocate_device();//分配一个input_dev结构体
    
        /* b、设置 */
            /* b.1 能产生哪类事件 */
        set_bit(EV_KEY, uk_dev->evbit);//产生按键事件
        set_bit(EV_REP, uk_dev->evbit);//产生重复事件
            /* b.2 能产生哪些事件 */
        set_bit(KEY_L, uk_dev->keybit);//产生按键事件的L事件
        set_bit(KEY_S, uk_dev->keybit);//产生按键事件的S事件
        set_bit(KEY_ENTER, uk_dev->keybit);//产生按键事件的ENTER时间
        /* c、注册 */
        input_register_device(uk_dev);//注册一个输入设备
        
        /* d、硬件相关的设置 */
        /* 数据传输三要素: 源、目的、长度*/
        /* 源:USB设备某个端点 */
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//设置端点为中断IN端点
        
        /* 长度 */
        len = endpoint->wMaxPacketSize;//长度为最大包长度
        
        /* 目的 */
        usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//分配一个地址作为USB接收到的数据
        
        /* 使用三要素*/
        uk_urb= usb_alloc_urb(0, GFP_KERNEL);              //分配一个USB请求块
        
        /* 使用三要素,设置urb */
        usb_fill_int_urb(uk_urb, dev, pipe, usb_buf,
                    len,usb_mouse_as_key_irq, NULL, endpoint->bInterval);//初始化即将被发送到USB设备的中断端点的URB
        
        uk_urb->transfer_dma = usb_buf_phys;            //usb控制器完成数据接收后将数据存放的物理地址
        uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //当URB包含一个即将传输的DMA缓冲区时应该设置URB_NO_TRANSFER_DMA_MAP
        
        /* 使用URB */
        ret = usb_submit_urb(uk_urb, GFP_KERNEL);//提交urb
        if(ret)
            return -1;
        return 0;
    }
    
    static void usb_mouse_as_key_disconnect(struct usb_interface *intf)
    {
        struct usb_device *dev = interface_to_usbdev(intf);
        
        input_free_device(uk_dev);//释放一个input_dev结构体
        input_unregister_device(uk_dev);//反注册一个输入设备
        usb_buffer_free(dev, len, usb_buf, usb_buf_phys);//释放分配的usb缓存数据
        usb_unlink_urb(uk_urb);//不使用urb控制块
        
        printk("disconnetc usbmouse
    ");
    }
    
    static struct usb_device_id usb_mouse_as_key_id_table [] = {
        { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
            USB_INTERFACE_PROTOCOL_MOUSE) },
        { }    /* Terminating entry *///终止入口项*/
    };
    
    
    
    
    static struct usb_driver usb_mouse_as_key_driver = {
        .name        = "usbmouse_askey",
        .probe        = usb_mouse_as_key_probe,
        .disconnect    = usb_mouse_as_key_disconnect,
        .id_table    = usb_mouse_as_key_id_table,
    };
    
    static int __init usb_mouse_as_key_init(void)
    {
        int retval = usb_register(&usb_mouse_as_key_driver);//注册一个usb驱动
        return retval;
    }
    
    static void __exit usb_mouse_as_key_exit(void)
    {
        usb_deregister(&usb_mouse_as_key_driver);
    }
    
    module_init(usb_mouse_as_key_init);
    module_exit(usb_mouse_as_key_exit);
    
    MODULE_LICENSE("GPL");

    6、测试程序

    测试流程如下:

    1、insmod 11th_usbmouse_as_key_drv.ko

    2、ls /dev/event*

    3、接上USB鼠标

    4、ls /dev/event*后可以看到新增了一个event1

    5、cat dev/tty1 然后按鼠标按键,左键、右键、中键分别为l、s、enter

    6、测试成功

  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/andyfly/p/9593199.html
Copyright © 2011-2022 走看看