Linux驱动之USB鼠标驱动编写

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

1、介绍USB四大描述

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

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

4、介绍程序用到的函数

5、编写程序

6、测试程序

 

1、介绍USB四大描述符

USB设备驱动程序里定义了许多与驱动程序密切相关的描述符。这里介绍一下四种比较关键的描述符:设备描述符、配置描述符、接口描述符、端点描述符。这几个描述符都位于include\linux\usb\ch9.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("\n");

    /* 
      * 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!\n");

    printk("bcdUSB = %x\n",dev->descriptor.bcdUSB);  //从USB设备描述符中获取USB版本
    printk("vidUSB = %x\n",dev->descriptor.idVendor); //从USB设备描述符中获取厂商ID
    printk("pidUSB = %x\n",dev->descriptor.idProduct);//从USB设备描述符中获取产品ID

    printk("bdcUSB = %x\n",intf->cur_altsetting->desc.bInterfaceClass);//从USB设备获取设备类
    printk("bdsUSB = %x\n",intf->cur_altsetting->desc.bInterfaceSubClass);//从USB设备获取设备从类
    printk("bdpUSB = %x\n",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\n");
}

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

 

转载于:https://www.cnblogs.com/andyfly/p/9593199.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值