本篇博客分如下幾部分講解linux
三、介紹程序用到的結構體app
五、編寫程序函數
六、測試程序post
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鼠標一共有三個按鍵:左鍵、右鍵、中鍵。在這裏把這三個分別做爲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
六、測試成功