1、usbmouse的定義:usb滑鼠既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性struct usb_mouse {char name[128];///USB滑鼠設備名稱char phys[64];///路徑struct usb_device ...
1、usbmouse的定義:usb滑鼠既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性
struct usb_mouse { char name[128];///USB滑鼠設備名稱 char phys[64];///路徑 struct usb_device *usbdev;///USB設備 struct input_dev *dev;///Input 設備 struct urb *irq; ///urb結構體 signed char *data;///數據傳輸緩衝區指針 dma_addr_t data_dma;/// };
2、usbmouse的載入:
module_usb_driver(usb_mouse_driver);///系統啟動時註冊usb_mouse_driver
3、usbmouse的初始化:
1 static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) 2 { 3 struct usb_device *dev = interface_to_usbdev(intf);///通過 USB 介面來獲得 usb 設備 4 struct usb_host_interface *interface; 5 struct usb_endpoint_descriptor *endpoint; 6 struct usb_mouse *mouse; 7 struct input_dev *input_dev; 8 int pipe, maxp; 9 int error = -ENOMEM; 10 11 interface = intf->cur_altsetting;///獲取 usb_host_interface 12 13 if (interface->desc.bNumEndpoints != 1)/// usb滑鼠端點有且只有一個控制端點,否則返回 ENODEV 14 return -ENODEV; 15 16 endpoint = &interface->endpoint[0].desc; ///usb滑鼠只有一個端點,獲取端點描述符 17 if (!usb_endpoint_is_int_in(endpoint))///檢查端點是不是中斷類型的輸入端點 18 return -ENODEV; 19 20 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);////獲取 pipe(),根據端點地址bEndpointAddress,中斷方式,IN端點就可以得到一個pipe,然後主機就知道跟誰去通信,該如何通信 21 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));///獲取主機和設備一次通訊的最大位元組數 22 23 mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);///分配一個usb_mouse 24 input_dev = input_allocate_device();///初始化 input 設備 25 if (!mouse || !input_dev) 26 goto fail1; 27 28 mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);///為usbmouse的data 分配8個位元組的空間 29 if (!mouse->data) 30 goto fail1; 31 32 mouse->irq = usb_alloc_urb(0, GFP_KERNEL);///申請分配 urb ,賦值給 usb_mouse 的 urb 33 if (!mouse->irq) 34 goto fail2; 35 36 mouse->usbdev = dev; //設置usb滑鼠設備的usb設備對象 37 mouse->dev = input_dev;//設備usb滑鼠設備的input設備對象 38 39 if (dev->manufacturer)///枚舉時候有獲取到有效的廠商名 40 strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));///複製廠商名到 name 41 42 if (dev->product) { //枚舉時候有獲取到有效的產品名 43 if (dev->manufacturer) //如果也有廠商名 44 strlcat(mouse->name, " ", sizeof(mouse->name)); //則用空格將廠商名和產品名隔開 45 strlcat(mouse->name, dev->product, sizeof(mouse->name));//追加產品名到name 46 } 47 48 if (!strlen(mouse->name)) //如果廠商和產品名都沒有 49 snprintf(mouse->name, sizeof(mouse->name), //則直接根據廠商id和產品id給name賦值 50 "USB HIDBP Mouse %04x:%04x", 51 le16_to_cpu(dev->descriptor.idVendor), 52 le16_to_cpu(dev->descriptor.idProduct)); 53 54 usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //設置設備路徑名 55 strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0 56 57 input_dev->name = mouse->name; //輸入設備的名字設置成usb滑鼠的名字 58 input_dev->phys = mouse->phys; //輸入設備的路徑設置成usb滑鼠的路徑 59 usb_to_input_id(dev, &input_dev->id); //設置輸入設備的bustype,vendor,product,version 60 input_dev->dev.parent = &intf->dev; //usb介面設備為輸入設備的父設備 61 62 ////evbit 關於設備支持事件類型的 bitmap 63 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); ///BIT_MASK 找到參數值所在的 bit位,輸入事件按鍵類型 + 相對位移 64 input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | ///滑鼠支持左鍵、右鍵、中鍵三個按鍵 65 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); 66 input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); ///REL_X REL_Y 表示滑鼠的位置信息 x \ Y 67 input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | ///在已有按鍵的基礎上加上一個邊鍵和一個而外的鍵 68 BIT_MASK(BTN_EXTRA); 69 input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);///給相對事件加上滾輪的事件 70 71 input_set_drvdata(input_dev, mouse);///usb滑鼠驅動文件作為輸入設備的設備文件的驅動數據 " input_dev -> dev->driver_data = mouse " 72 73 input_dev->open = usb_mouse_open; //設置輸入事件的打開方法 74 input_dev->close = usb_mouse_close; //設置輸入事件的關閉方法 75 76 usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, ///初始化 urb (中斷傳輸方式),並指定 urb 的回調函數是 usb_mouse_irq 77 (maxp > 8 ? 8 : maxp), 78 usb_mouse_irq, mouse, endpoint->bInterval);//// usb_mouse_irq --回調函數,上下文信息 -- mouse 79 mouse->irq->transfer_dma = mouse->data_dma;//dma數據緩衝區指向usb滑鼠設備的data_dma成員 80 mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;///沒有 DMA 映射 81 82 error = input_register_device(mouse->dev);///註冊設備驅動 mouse->dev 83 if (error) 84 goto fail3; 85 86 usb_set_intfdata(intf, mouse);///usb 滑鼠驅動文件作為 usb 介面設備的設備文件的驅動數據 ;intf->dev->driver_data = mouse ; 87 return 0; 88 89 fail3: 90 usb_free_urb(mouse->irq); 91 fail2: 92 usb_free_coherent(dev, 8, mouse->data, mouse->data_dma); 93 fail1: 94 input_free_device(input_dev); 95 kfree(mouse); 96 return error; 97 }
經過probe過程,註冊了輸入設備則會在/dev/input/目錄下會產生對應的滑鼠設備節點,應用程式可以打開該節點來控制usb滑鼠設備
關鍵函數調用順序:
.open = evdev_open,///上層應用程式通過系統調用open 打開設備
static int evdev_open(struct inode *inode, struct file *file);
evdev_open_device(evdev);
input_open_device(&evdev->handle);
dev->open(dev);////---->調用usbmouse_open()
input_dev->open = usb_mouse_open; //設置輸入事件的打開方法
usb_submit_urb(mouse->irq, GFP_KERNEL)
usb_mouse_irq(struct urb *urb)
input_report_key\input_report_rel\input_sync ///提交滑鼠數據給input 子系統
usb_submit_urb (urb, GFP_ATOMIC);///usb設備提交urb,主機再次輪詢usb設備
static int usb_mouse_open(struct input_dev *dev) { struct usb_mouse *mouse = input_get_drvdata(dev); //通過輸入設備獲取usb滑鼠設備 mouse->irq->dev = mouse->usbdev; //設置urb設備對應的usb設備 if (usb_submit_urb(mouse->irq, GFP_KERNEL))///提交 urb ,只有打開設備的時候,才會把 urb 發送出去 return -EIO; return 0; }
1 static void usb_mouse_irq(struct urb *urb) 2 { 3 struct usb_mouse *mouse = urb->context; ///獲取 usb 滑鼠設備 4 signed char *data = mouse->data; ///數據傳輸緩衝區指針 5 struct input_dev *dev = mouse->dev;//輸入設備 6 int status; 7 8 switch (urb->status) {///判斷 urb 傳輸的狀態 9 case 0: /* success */ ///傳輸成功跳出 switch 10 break; 11 case -ECONNRESET: /* unlink */ 12 case -ENOENT: 13 case -ESHUTDOWN: 14 return; 15 /* -EPIPE: should clear the halt */ 16 default: /* error */ 17 goto resubmit; 18 } 19 20 input_report_key(dev, BTN_LEFT, data[0] & 0x01);////提交按鍵信息,data[0] 的第 0 位為 1,表示左鍵按下 21 input_report_key(dev, BTN_RIGHT, data[0] & 0x02);////提交按鍵信息,data[0] 的第 1 位為 1,表示右鍵按下 22 input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);////提交按鍵信息,data[0] 的第 2 位為 1,表示中鍵按下 23 input_report_key(dev, BTN_SIDE, data[0] & 0x08);////提交按鍵信息,data[0] 的第 3 位為 1,表示邊鍵按下 24 input_report_key(dev, BTN_EXTRA, data[0] & 0x10);////提交按鍵信息,data[0] 的第 4 位為 1,表示而外鍵按下 25 26 input_report_rel(dev, REL_X, data[1]);///提交滑鼠相對坐標值,data[1] 為 X 坐標 27 input_report_rel(dev, REL_Y, data[2]);///提交滑鼠相對坐標值,data[2] 為 Y 坐標 28 input_report_rel(dev, REL_WHEEL, data[3]);///提交滑鼠滾輪相對值,data[3] 為 滾輪相對值 29 30 input_sync(dev);///同步信息,表示上面的信息作為完整一幀傳遞給上層系統 31 resubmit: 32 status = usb_submit_urb (urb, GFP_ATOMIC);///usb設備提交urb,主機再次輪詢usb設備 33 if (status) 34 dev_err(&mouse->usbdev->dev, 35 "can't resubmit intr, %s-%s/input0, status %d\n", 36 mouse->usbdev->bus->bus_name, 37 mouse->usbdev->devpath, status); 38 }
(註:以上圖片來自麥子學院 金鑫老師的課程,在此對其辛勤付出和無私分享表示真摯的感謝!)