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、測試成功

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM