本篇博客分以下幾部分講解
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));
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、硬件相關的設置 */
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; };
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請求塊的指針
直接放出程序源碼,可以看到這個程序的結構和輸入子系統的結構差不多。
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");
測試流程如下:
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、測試成功