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、测试成功

  • 相关阅读:
    文件上传控件 jQueryFileUpload在 MVC3中的实现
    谈谈开心王国的用户注册页面
    一个个人网站如何融资一千万
    netTcpBinding三种方法实现调用函数
    MVC实现RadioButtonList
    网易盖楼回复的简易实现
    sqlserver复制表结构
    显示目录下所有文件列表
    Jira:用户权限设置
    C#:Socket通信
  • 原文地址:https://www.cnblogs.com/andyfly/p/9593199.html
Copyright © 2011-2022 走看看