Linux驅動之USB鼠標驅動編寫

本篇博客分如下幾部分講解linux

一、介紹USB四大描述數組

二、介紹USB鼠標驅動程序功能及框架緩存

三、介紹程序用到的結構體app

四、介紹程序用到的函數框架

五、編寫程序函數

六、測試程序post

 

一、介紹USB四大描述符測試

USB設備驅動程序裏定義了許多與驅動程序密切相關的描述符。這裏介紹一下四種比較關鍵的描述符:設備描述符配置描述符接口描述符端點描述符。這幾個描述符都位於include\linux\usb\ch9.h中,先看一下每一個描述直接的關係,從圖中能夠看出每個查到USB主機上的USB設備都有一個設備描述符,設備描述符下面能夠接多個配置描述符,配置描述符下面又能夠接多個
ui


當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大類。this

/* 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));

 

 二、介紹USB鼠標驅動程序功能及框架

USB鼠標一共有三個按鍵:左鍵、右鍵、中鍵。在這裏把這三個分別做爲l、s、enter鍵。這就是這個USB設備驅動程序的功能。

要實現這個功能,仍是須要用到輸入子系統的框架,與觸摸屏驅動同樣,再回顧一下輸入系統的框架

 輸入子系統按框架能夠分爲設備驅動層、事件層、以及核心層。

整個調用過程以下:

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

應用層           事件層          設備層         核心層                   核心層            事件層          事件層

若是要本身添加一個輸入子系統的設備,只須要添加設備層的文件便可。

一、在裏面添加設備層input_dev結構並初始化

二、編寫中斷處理程序

 在USB驅動程序中,中斷處理程序就不是真正的CPU中斷的處理程序了。而是USB總線驅動程序接收完成一包數據後的回調函數。

編寫程序步驟:其中硬件相關的設置就是設置USB驅動設備傳輸的數據來源、數據存放地址、數據長度、怎麼樣處理數據

/* a、分配一個 input_dev結構體*/

/* b、設置 */
/* b.1 能產生哪類事件 */
/* b.2 能產生哪些事件 */

/* c、註冊 */

/* d、硬件相關的設置 */

 

三、介紹程序用到的結構體

 一、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
    
    ...
    ...
};

二、struct usb_device結構體,描述整個USB設備的結構體,通常不會用到裏面的變量,這裏不作詳細註釋。

三、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 */
};

四、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;//額外描述大小
};

五、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;
};

六、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));

七、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 */
};

八、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;//
};

九、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;
};

 

四、介紹程序用到的函數

一、輸入子系統相關的函數

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表示事件的值

 

二、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請求塊的指針

 

五、編寫程序

直接放出程序源碼,能夠看到這個程序的結構和輸入子系統的結構差很少。

一、首先加載這個模塊後,會調用usb_mouse_as_key_init函數,而後usb_mouse_as_key_driver 結構體被註冊到USB核心

二、當插上USB鼠標設備後,若是此設備和此驅動的接口類、接口子類、接口協議相同(位於usb_mouse_as_key_id_table ),那麼usb_mouse_as_key_probe函數被調用

三、usb_mouse_as_key_probe函數是核心函數,在裏面作許多事情,具體看代碼

/* a、分配一個 input_dev結構體*/

/* b、設置 */
/* b.1 能產生哪類事件 */
/* b.2 能產生哪些事件 */

/* c、註冊 */

/* d、硬件相關的設置 */

四、當按下USB按鍵後在第三步中設置的usb_mouse_as_key_irq函數被調用

五、數據包會在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");

 

六、測試程序

測試流程以下:

一、insmod 11th_usbmouse_as_key_drv.ko

二、ls /dev/event*

三、接上USB鼠標

四、ls /dev/event*後能夠看到新增了一個event1

五、cat dev/tty1 而後按鼠標按鍵,左鍵、右鍵、中鍵分別爲l、s、enter

六、測試成功

相關文章
相關標籤/搜索