usb鍵鼠驅動分析

一、鼠標

linux下的usb鼠標驅動在/drivers/hid/usbhid/usbmouse.c中實現

1.加載初始化過程

1.1模塊入口

  1. module_init(usb_mouse_init);  

1.2初始化函數

  1. static int __init usb_mouse_init(void)  //初始化  
  2. {  
  3.     int retval = usb_register(&usb_mouse_driver);   //註冊usb鼠標驅動  
  4.     if (retval == 0)  
  5.         printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");  
  6.     return retval;  
  7. }  

1.3初始化函數註冊了一個usb驅動usb_mouse_driver

  1. static struct usb_driver usb_mouse_driver = {   //usb鼠標驅動  
  2.     .name       = "usbmouse",           //驅動名  
  3.     .probe      = usb_mouse_probe,      //匹配方法  
  4.     .disconnect = usb_mouse_disconnect, //拔出方法  
  5.     .id_table   = usb_mouse_id_table,   //支持設備id表  
  6. };  

1.4當插入鼠標時會根據usb_mouse_id_table去匹配創建usb設備

  1. static struct usb_device_id usb_mouse_id_table [] = {  
  2.     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },  
  3.     { } /* Terminating entry */  
  4. };  

它的匹配方式是接口id匹配.接口類USB_INTERFACE_CLASS_HID

usb插入枚舉時候會獲取usb鼠標的接口類型,獲取其接口類信息,匹配成功的話會動態創建一個usb_device.

在分析probe和disconnect方法之前先介紹下驅動用來描述usb鼠標對象的結構體usb_mouse

  1. struct usb_mouse {  
  2.     char name[128];//usb鼠標設備名  
  3.     char phys[64];//路徑  
  4.     struct usb_device *usbdev;//usb設備  
  5.     struct input_dev *dev;//輸入設備  
  6.     struct urb *irq;//urb結構體  
  7.     signed char *data;  //數據傳輸緩衝區指針  
  8.     dma_addr_t data_dma;  
  9. };  

usb鼠標既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性

1.5 匹配成功了就會調用probe方法

  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_device  
  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.     if (interface->desc.bNumEndpoints != 1)  //鼠標的端點有且僅有1個控制端點  
  13.         return -ENODEV;  
  14.     endpoint = &interface->endpoint[0].desc; //獲取端點描述符  
  15.     if (!usb_endpoint_is_int_in(endpoint))  //判斷該端點是否中斷端點  
  16.         return -ENODEV;  
  17.     //上面判斷了usb鼠標的屬性,有且僅有1個控制端點(0號端點不算進來的)  
  18.       
  19.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //設置端點爲中斷輸入端點  
  20.     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //獲取包數據最大值  
  21.     mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);  //分配usb_mouse對象  
  22.     input_dev = input_allocate_device();    //初始化輸入設備  
  23.     if (!mouse || !input_dev)  
  24.         goto fail1;  
  25.     mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//分配初始化usb鼠標數據緩衝區內存(默認8位數據)  
  26.     if (!mouse->data)  
  27.         goto fail1;  
  28.     mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配初始化urb  
  29.     if (!mouse->irq)  
  30.         goto fail2;  
  31.     mouse->usbdev = dev; //設置usb鼠標設備的usb設備對象  
  32.     mouse->dev = input_dev;  //設備usb鼠標設備的input設備對象  
  33.       
  34.     if (dev->manufacturer)   //枚舉時候有獲取到有效的廠商名  
  35.         strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));  //複製廠商名到name  
  36.     if (dev->product) {      //枚舉時候有獲取到有效的產品名  
  37.         if (dev->manufacturer)   //如果也有廠商名  
  38.             strlcat(mouse->name, " "sizeof(mouse->name));   //則用空格將廠商名和產品名隔開  
  39.         strlcat(mouse->name, dev->product, sizeof(mouse->name));   //追加產品名到name  
  40.     }  
  41.     if (!strlen(mouse->name))    //如果廠商和產品名都沒有  
  42.         snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",  
  43.         le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));  
  44.         //則直接根據廠商id和產品id給name賦值  
  45.     usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //設置設備路徑名  
  46.     strlcat(mouse->phys, "/input0"sizeof(mouse->phys)); //追加/input0  
  47.     input_dev->name = mouse->name;    //輸入設備的名字設置成usb鼠標的名字  
  48.     input_dev->phys = mouse->phys;    //輸入設備的路徑設置成usb鼠標的路徑  
  49.     usb_to_input_id(dev, &input_dev->id);    //設置輸入設備的bustype,vendor,product,version  
  50.     input_dev->dev.parent = &intf->dev;       //usb接口設備爲輸入設備的父設備  
  51.       
  52.     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);   //輸入事件類型按鍵+相對位移  
  53.     input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);  
  54.     //按鍵類型 鼠標:左鍵,右鍵,中鍵  
  55.     input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);    //相對位移x方向+y方向  
  56.     input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);  
  57.     //按鍵類型 鼠標:旁鍵,外部鍵  
  58.     input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //相對位移 鼠標滾輪事件  
  59.       
  60.     input_set_drvdata(input_dev, mouse);    //usb鼠標驅動文件作爲輸入設備的設備文件的驅動數據  
  61.     input_dev->open = usb_mouse_open;    //設置輸入事件的打開方法  
  62.     input_dev->close = usb_mouse_close;  //設置輸入事件的關閉方法  
  63.   
  64.     usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);  
  65.     //填充中斷類型urb 指定了urb的回調函數是usb_mouse_irq  
  66.     mouse->irq->transfer_dma = mouse->data_dma;//dma數據緩衝區指向usb鼠標設備的data_dma成員  
  67.     mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//沒DMA映射  
  68.     error = input_register_device(mouse->dev);  
  69.     if (error)  
  70.         goto fail3;  
  71.     usb_set_intfdata(intf, mouse);  ////usb鼠標驅動文件作爲usb接口設備的設備文件的驅動數據  
  72.     return 0;  
  73. fail3:    
  74.     usb_free_urb(mouse->irq);  
  75. fail2:    
  76.     usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);  
  77. fail1:    
  78.     input_free_device(input_dev);  
  79.     kfree(mouse);  
  80.     return error;  
  81. }  

1.6 拔掉usb鼠標就會調用disconnect方法

  1. static void usb_mouse_disconnect(struct usb_interface *intf)  
  2. {  
  3.     struct usb_mouse *mouse = usb_get_intfdata (intf);  //根據usb接口設備的設備文件的驅動數據,獲取usb鼠標設備  
  4.   
  5.     usb_set_intfdata(intf, NULL);   //清空usb接口設備的設備文件的驅動數據  
  6.     if (mouse) {      
  7.         usb_kill_urb(mouse->irq);    //斷掉urb傳輸  
  8.         input_unregister_device(mouse->dev); //註銷輸入設備  
  9.         usb_free_urb(mouse->irq);    //釋放urb  
  10.         usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);    //清除傳輸數據緩衝區  
  11.         kfree(mouse);   //釋放usb鼠標設備  
  12.     }  
  13. }  

基本上disconnect只是probe的一個逆操作而已

經過probe過程,註冊了輸入設備則會在/dev/input/目錄下會產生對應的鼠標設備節點,應用程序可以打開該節點來控制usb鼠標設備

此時會調用usb_mouse_open方法

1.7打開鼠標

  1. static int usb_mouse_open(struct input_dev *dev)  
  2. {  
  3.     struct usb_mouse *mouse = input_get_drvdata(dev);   //通過輸入設備獲取usb鼠標設備  
  4.     mouse->irq->dev = mouse->usbdev;   //設置urb設備對應的usb設備  
  5.     if (usb_submit_urb(mouse->irq, GFP_KERNEL))  //提交urb  
  6.         return -EIO;  
  7.     return 0;  
  8. }  

通過urb提交之後,鼠標動作通過usb傳輸數據就會交由urb去處理了

1.8.urb數據傳輸

當操作鼠標的時候,會引起urb數據傳輸在數據傳輸之後會調用usb_mouse_irq

  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.     switch (urb->status) {   //判斷urb傳輸的狀態  
  8.     case 0:         /* success */   //傳輸成功跳出switch  
  9.         break;  
  10.     case -ECONNRESET:   /* unlink */  
  11.     case -ENOENT:  
  12.     case -ESHUTDOWN:  
  13.         return;  
  14.     /* -EPIPE:  should clear the halt */  
  15.     default:        /* error */  
  16.         goto resubmit;  
  17.     }  
  18.     input_report_key(dev, BTN_LEFT,   data[0] & 0x01);  //右鍵  
  19.     input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);  //左鍵  
  20.     input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);  //中鍵  
  21.     input_report_key(dev, BTN_SIDE,   data[0] & 0x08);  //邊鍵  
  22.     input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);  //外部鍵  
  23.     input_report_rel(dev, REL_X,     data[1]);          //相對x座標位移  
  24.     input_report_rel(dev, REL_Y,     data[2]);          //相對y座標位移  
  25.     input_report_rel(dev, REL_WHEEL, data[3]);          //相對滾輪位移  
  26.     input_sync(dev);                                    //同步事件  
  27. resubmit:  
  28.     status = usb_submit_urb (urb, GFP_ATOMIC);          //繼續提交urb  
  29.     if (status)  
  30.         err ("can't resubmit intr, %s-%s/input0, status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);  
  31. }  

usb接口傳來的數據會保存在usb鼠標data指針成員指向的緩衝區中

這裏可以看出usb鼠標傳輸的每次數據基本是4個字節

第0個字節的第1位表示右鍵,第2位表示左鍵,第3位表示中鍵,第4位表示邊鍵,第5爲表示外部鍵
而第1個字節表示相對x座標的位移,第2個字節表示相對y座標的位移,第3個字節表示相對滾輪的位移


當輸入設備上報完usb接口接收來的數據後,需要調用input_sync同步事件消息,並調用usb_submit_urb提交urb

使其繼續監視處理usb鼠標設備傳遞的新數據.

應用程序要獲取鼠標操作信息可以打開對應的輸入設備節點,並通過輸入設備的讀接口,獲取到usb鼠標通過usb接口傳遞並交由輸入設備上報過來的數據

漏掉的函數

1.應用程序關閉鼠標設備

  1. static void usb_mouse_close(struct input_dev *dev)  
  2. {  
  3.     struct usb_mouse *mouse = input_get_drvdata(dev);   //通過輸入設備獲取usb鼠標設備  
  4.     usb_kill_urb(mouse->irq);    //當關閉鼠標設備時候,需要斷掉urb傳輸  
  5. }  

2.模塊移除調用的函數

  1. module_exit(usb_mouse_exit);  
  1. static void __exit usb_mouse_exit(void)  
  2. {  
  3.     usb_deregister(&usb_mouse_driver);  //註銷掉usb鼠標設備  
  4. }  




 二、鍵盤

linux下的usb鍵盤驅動在/drivers/hid/usbhid/usbkbd.c中實現

1.加載初始化過程

1.1 模塊入口

  1. module_init(usb_kbd_init);  

1.2 初始化函數

  1. static int __init usb_kbd_init(void)  
  2. {  
  3.     int result = usb_register(&usb_kbd_driver); //註冊usb鍵盤  
  4.     if (result == 0)  
  5.         printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");  
  6.     return result;  
  7. }  

1.3 初始化函數註冊了一個usb驅動usb_kbd_driver

  1. static struct usb_driver usb_kbd_driver = { //usb鍵盤驅動  
  2.     .name =     "usbkbd",               //驅動名  
  3.     .probe =    usb_kbd_probe,          //匹配方法  
  4.     .disconnect =   usb_kbd_disconnect, //拔出方法  
  5.     .id_table = usb_kbd_id_table,       //支持設備id  
  6. };  

1.4 當插入鼠標時會根據usb_kbd_id_table去匹配創建usb設備

  1. static struct usb_device_id usb_kbd_id_table [] = {  
  2.     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD) },  
  3.     { }                     /* Terminating entry */  
  4. };  

它的匹配方式是接口id匹配.接口類USB_INTERFACE_CLASS_HID

usb插入枚舉時候會獲取usb鍵盤的接口類型,獲取其接口類信息,匹配成功的話會動態創建一個usb_device.

在分析probe和disconnect方法之前先介紹下驅動用來描述usb鍵盤對象的結構體usb_kbd

  1. struct usb_kbd {  
  2.     struct input_dev *dev;  //輸入設備  
  3.     struct usb_device *usbdev;  //usb設備  
  4.     unsigned char old[8];   //舊的鍵盤按鍵數據  
  5.     struct urb *irq, *led;  //鍵盤urb,led urb  
  6.     unsigned char newleds;  //新的led數據  
  7.     char name[128]; //usb鍵盤設備名字  
  8.     char phys[64];  //usb鍵盤設備路徑  
  9.     unsigned char *new//usb鍵盤按鍵 數據傳輸緩衝區指針  
  10.     struct usb_ctrlrequest *cr; //setup數據包控制請求描述符  
  11.     unsigned char *leds;    //usb鍵盤led 數據傳輸緩衝區指針  
  12.     dma_addr_t new_dma; //usb鍵盤按鍵DMA映射總線地址  
  13.     dma_addr_t leds_dma;    //usb鍵盤led DMA映射總線地址  
  14. };  

usb鍵盤既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性

1.5 匹配成功了就會調用probe方法

  1. static int usb_kbd_probe(struct usb_interface *iface,const struct usb_device_id *id)  
  2. {  
  3.     struct usb_device *dev = interface_to_usbdev(iface);    //根據usb接口獲取動態創建的usb_device  
  4.     struct usb_host_interface *interface;  
  5.     struct usb_endpoint_descriptor *endpoint;  
  6.     struct usb_kbd *kbd;  
  7.     struct input_dev *input_dev;  
  8.     int i, pipe, maxp;  
  9.     int error = -ENOMEM;  
  10.   
  11.     interface = iface->cur_altsetting;       //獲取usb_host_interface     
  12.     if (interface->desc.bNumEndpoints != 1)  //鍵盤的端點有且僅有1個控制端點  
  13.         return -ENODEV;  
  14.     endpoint = &interface->endpoint[0].desc; //獲取端點描述符  
  15.     if (!usb_endpoint_is_int_in(endpoint))  //判斷該端點是否中斷端點   
  16.         return -ENODEV;  
  17.     //上面判斷了usb鍵盤的屬性,有且僅有1個控制端點(0號端點不算進來的)   
  18.       
  19.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //設置端點爲中斷輸入端點  
  20.     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //獲取包數據最大值  
  21.     kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);  //分配usb_kbd對象  
  22.     input_dev = input_allocate_device();    //初始化輸入設備  
  23.     if (!kbd || !input_dev)  
  24.         goto fail1;  
  25.     if (usb_kbd_alloc_mem(dev, kbd))    //分配usb鍵盤需要的內存  
  26.         goto fail2;  
  27.     kbd->usbdev = dev;   //設置usb鍵盤設備的usb設備對象  
  28.     kbd->dev = input_dev;    //設備usb鍵盤設備的input設備對象  
  29.   
  30.     if (dev->manufacturer)   //枚舉時候有獲取到有效的廠商名  
  31.         strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));  //複製廠商名到name       
  32.     if (dev->product) {      //枚舉時候有獲取到有效的產品名  
  33.         if (dev->manufacturer)   //如果也有廠商名  
  34.             strlcat(kbd->name, " "sizeof(kbd->name));   //則用空格將廠商名和產品名隔開  
  35.         strlcat(kbd->name, dev->product, sizeof(kbd->name));   //追加產品名到name       
  36.     }  
  37.     if (!strlen(kbd->name))  //如果廠商和產品名都沒有  
  38.         snprintf(kbd->name, sizeof(kbd->name),"USB HIDBP Keyboard %04x:%04x",  
  39.              le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));  
  40.     //則直接根據廠商id和產品id給name賦值  
  41.     usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); //設置設備路徑名  
  42.     strlcat(kbd->phys, "/input0"sizeof(kbd->phys)); //追加/input0  
  43.     input_dev->name = kbd->name;  //輸入設備的名字設置成usb鍵盤的名字  
  44.     input_dev->phys = kbd->phys;  //輸入設備的路徑設置成usb鍵盤的路徑  
  45.     usb_to_input_id(dev, &input_dev->id);    //設置輸入設備的bustype,vendor,product,version    
  46.     input_dev->dev.parent = &iface->dev;  //usb接口設備爲輸入設備的父設備  
  47.     input_set_drvdata(input_dev, kbd);  //usb鍵盤驅動文件作爲輸入設備的設備文件的驅動數據  
  48.       
  49.     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP);    //輸入事件類型 按鍵+led+重複  
  50.     input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);  
  51.     //鍵盤led事件:小鍵盤,大小寫,滾動鎖定,組合鍵,KANA  
  52.     for (i = 0; i < 255; i++)  
  53.         set_bit(usb_kbd_keycode[i], input_dev->keybit);  
  54.     clear_bit(0, input_dev->keybit); //清除無效的0位  
  55.     //鍵盤按鍵事件:遍歷全局usb_kbd_keycode數組設置  
  56.     input_dev->event = usb_kbd_event;    //設置輸入事件的event方法  
  57.     input_dev->open = usb_kbd_open;      //設置輸入事件的open方法  
  58.     input_dev->close = usb_kbd_close;    //設置輸入事件的close方法  
  59.     usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),usb_kbd_irq, kbd, endpoint->bInterval);  
  60.     //填充中斷類型urb 指定了urb的回調函數是usb_kbd_irq  
  61.     kbd->irq->transfer_dma = kbd->new_dma;     //usb鍵盤按鍵設備DMA映射總線地址  
  62.     kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //沒DMA映射  
  63.     //設置usb setup傳輸數據包控制請求結構體  
  64.     kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;  
  65.     kbd->cr->bRequest = 0x09;//SET_IDLE?  
  66.     kbd->cr->wValue = cpu_to_le16(0x200);  
  67.     kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);  
  68.     kbd->cr->wLength = cpu_to_le16(1);  
  69.     usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1,usb_kbd_led, kbd);  
  70.     //設置爲控制輸出端點,填充控制類型urb,回調函數usb_kbd_led  
  71.     kbd->led->transfer_dma = kbd->leds_dma;    //usb鍵盤led設備DMA映射總線地址  
  72.     kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //沒DMA映射  
  73.     error = input_register_device(kbd->dev); //註冊輸入設備  
  74.     if (error)  
  75.         goto fail2;  
  76.     usb_set_intfdata(iface, kbd);   //usb鍵盤驅動文件作爲usb接口設備的設備文件的驅動數據    
  77.     device_set_wakeup_enable(&dev->dev, 1);  //使能系統喚醒  
  78.     return 0;  
  79. fail2:    
  80.     usb_kbd_free_mem(dev, kbd); //分配失敗則釋放相關內存  
  81. fail1:    
  82.     input_free_device(input_dev);   //釋放輸入設備  
  83.     kfree(kbd); //釋放usb_kbd  
  84.     return error;  
  85. }  

probe方法中調用的內存分配釋放函數

分配內存

  1. static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)  
  2. {  
  3.     if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))  //分配按鍵urb  
  4.         return -1;  
  5.     if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))  //分配led燈urb  
  6.         return -1;  
  7.     if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))  //分配初始化usb鍵盤數據緩衝區內存(默認8位數據)  
  8.         return -1;  
  9.     if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))    //分配setup包的控制請求描述符  
  10.         return -1;  
  11.     if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))    //分配初始化usb鍵盤led數據緩衝區內存  
  12.         return -1;  
  13.     return 0;  
  14. }  

釋放內存

  1. static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)  
  2. {  
  3.     usb_free_urb(kbd->irq);  //釋放鍵盤按鍵urb  
  4.     usb_free_urb(kbd->led);  //釋放鍵盤led urb  
  5.     usb_free_coherent(dev, 8, kbd->new, kbd->new_dma);    //釋放usb鍵盤數據緩衝區  
  6.     kfree(kbd->cr);  //釋放setup包的控制請求描述符  
  7.     usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma);  //釋放urb鍵盤led數據緩衝區內存  
  8. }  

配置用到的全局鍵值數組

  1. static const unsigned char usb_kbd_keycode[256] = { //鍵值  
  2.       0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,  
  3.      50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,  
  4.       4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,  
  5.      27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,  
  6.      65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,  
  7.     105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,  
  8.      72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,  
  9.     191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,  
  10.     115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,  
  11.     122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  12.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  13.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  14.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  15.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  16.      29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,  
  17.     150,158,159,128,136,177,178,176,142,152,173,140  
  18. };  


 

1.6 拔掉usb鼠標就會調用disconnect方法

  1. static void usb_kbd_disconnect(struct usb_interface *intf)  
  2. {  
  3.     struct usb_kbd *kbd = usb_get_intfdata (intf);  //根據usb接口設備的設備文件的驅動數據,獲取usb鍵盤設備    
  4.     usb_set_intfdata(intf, NULL);   //清空usb接口設備的設備文件的驅動數據  
  5.     if (kbd) {  
  6.         usb_kill_urb(kbd->irq);  //斷掉urb傳輸  
  7.         input_unregister_device(kbd->dev);   //註銷輸入設備  
  8.         usb_kbd_free_mem(interface_to_usbdev(intf), kbd);   //釋放usb鍵盤需要的內存  
  9.         kfree(kbd); //釋放usb鍵盤設備  
  10.     }  
  11. }  

基本上disconnect只是probe的一個逆操作而已

經過probe過程,註冊了輸入設備則會在/dev/input/目錄下會產生對應的鍵盤設備節點,應用程序可以打開該節點來控制usb鍵盤設備

此時會調用usb_kbd_open方法

1.7打開鍵盤

  1. static int usb_kbd_open(struct input_dev *dev)  
  2. {  
  3.     struct usb_kbd *kbd = input_get_drvdata(dev);   //通過輸入設備獲取usb鍵盤設備  
  4.     kbd->irq->dev = kbd->usbdev;   //usb鍵盤按鍵urb捆綁usb設備  
  5.     if (usb_submit_urb(kbd->irq, GFP_KERNEL))    //提交usb鍵盤按鍵urb  
  6.         return -EIO;  
  7.     return 0;  
  8. }  

關閉鍵盤調用usb_kbd_close

  1. static void usb_kbd_close(struct input_dev *dev)  
  2. {  
  3.     struct usb_kbd *kbd = input_get_drvdata(dev);   //通過輸入設備獲取usb鍵盤設備  
  4.     usb_kill_urb(kbd->irq);  //斷開usb鍵盤按鍵urb  
  5. }  

通過urb提交之後,鍵盤動作通過usb傳輸數據就會交由urb去處理了

1.8.urb數據傳輸

  1. static void usb_kbd_irq(struct urb *urb)  
  2. {  
  3.     struct usb_kbd *kbd = urb->context;  //獲取usb鍵盤設備  
  4.     int i;  
  5.   
  6.     switch (urb->status) {   //判斷urb傳輸的狀態  
  7.     case 0:         /* success */   //傳輸成功跳出switch  
  8.         break;  
  9.     case -ECONNRESET:   /* unlink */  
  10.     case -ENOENT:  
  11.     case -ESHUTDOWN:  
  12.         return;  
  13.     /* -EPIPE:  should clear the halt */  
  14.     default:        /* error */  
  15.         goto resubmit;  
  16.     }  
  17.     //L-ctrl,L-shift,L-alt,L-gui,R-ctrl,R-shift,R-alt,R-gui  
  18.     for (i = 0; i < 8; i++)      //(224~231)判斷新按下的是否組合鍵  
  19.         input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);   //組合鍵  
  20.           
  21.     //memscan(kbd->new + 2, kbd->old[i], 6)表示從kbd->new[2]掃描6個單位到kbd->new[7],  
  22.     //查找kbd->old[i]一樣的字符,返回掃描到的單位地址"==kbd->new+8"表示沒找到  
  23.     //鍵盤掃描碼0-No Event 1-Overrun Error 2-POST Fail 3-ErrorUndefined所以kbd->old[i] > 3  
  24.     //鍵值usb_kbd_keycode[i]和掃描碼new[i]/old[i]要區分好別亂了  
  25.     //鍵盤掃描碼和數據格式見函數下面圖片  
  26.     for (i = 2; i < 8; i++) {  
  27.         //新數據中沒有查找到舊數據一樣的碼值--表示新的按鍵按下,舊的按鍵釋放  
  28.         if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {  
  29.             if (usb_kbd_keycode[kbd->old[i]])    //鬆開的按鍵是正常的按鍵  
  30.                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);  //上報釋放按鍵事件  
  31.             else  
  32.                 dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->old[i]);  
  33.         }  
  34.         //舊數據中沒有查找到新數據一樣的碼值--表示新的按鍵按下,就的按鍵按下  
  35.         if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {  
  36.             if (usb_kbd_keycode[kbd->new[i]])    //按下的按鍵是正常的按鍵  
  37.                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);  //上報按下按鍵事件  
  38.             else  
  39.                 dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->new[i]);  
  40.         }  
  41.     }  
  42.     //數據的第2~7字節用於存放鍵碼,分別可以存放6個,也就是可以支持同時6個按鍵按下  
  43.     //如果一直按住鍵盤的某個按鍵,則usb接收到的數據會都是一樣的也就是kbd->old==kbd->new,則按下的時候會上報按下事件,一直按着的時候不會繼續上報按下或釋放按鍵  
  44.     //若有新的按鍵按下,則所有的kdb->old的值可以在kdb->new中找到,而kdb->new中代表新按鍵鍵碼的值在kdb->old中會找不到,所以觸發第二個if條件成立,上報按下按鍵事件  
  45.     //若之前的按鍵鬆開,則所有的kdb->new的值可以在kdb->old中找到,而kdb->old中代表舊按鍵鍵碼的值在kdb->new中會找不到,所以觸發第一個if條件成立,上報釋放按鍵事件  
  46.     input_sync(kbd->dev);    //同步事件  
  47.     memcpy(kbd->old, kbd->new, 8);    //新的鍵值存放在舊的鍵值  
  48. resubmit:  
  49.     i = usb_submit_urb (urb, GFP_ATOMIC);   //提交urb  
  50.     if (i)  
  51.         err_hid ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);  
  52. }  


Usage
index
(dec)

Usage
Index
(hex)



Usage

Ref:typical
AT-101
position


PC-AT


Mac-
intosh



UNIX



Boot

0

00

Reserved (no event indicated) 9

N/A

Ö

Ö

Ö

84/101/104

1

01

Keyboard ErrorRollOver9

N/A

Ö

Ö

Ö

84/101/104

2

02

Keyboard POSTFail9

N/A

Ö

Ö

Ö

84/101/104

3

03

Keyboard ErrorUndefined9

N/A

Ö

Ö

Ö

84/101/104

4

04

Keyboard a and A4

31

Ö

Ö

Ö

84/101/104

5

05

Keyboard b and B

50

Ö

Ö

Ö

84/101/104

6

06

Keyboard c and C4

48

Ö

Ö

Ö

84/101/104

7

07

Keyboard d and D

33

Ö

Ö

Ö

84/101/104

8

08

Keyboard e and E

19

Ö

Ö

Ö

84/101/104

9

09

Keyboard f and F

34

Ö

Ö

Ö

84/101/104

10

0A

Keyboard g and G

35

Ö

Ö

Ö

84/101/104

11

0B

Keyboard h and H

36

Ö

Ö

Ö

84/101/104

12

0C

Keyboard i and I

24

Ö

Ö

Ö

84/101/104

13

0D

Keyboard j and J

37

Ö

Ö

Ö

84/101/104

14

0E

Keyboard k and K

38

Ö

Ö

Ö

84/101/104

15

0F

Keyboard l and L

39

Ö

Ö

Ö

84/101/104

16

10

Keyboard m and M4

52

Ö

Ö

Ö

84/101/104

17

11

Keyboard n and N

51

Ö

Ö

Ö

84/101/104

18

12

Keyboard o and O4

25

Ö

Ö

Ö

84/101/104

19

13

Keyboard p and P4

26

Ö

Ö

Ö

84/101/104

20

14

Keyboard q and Q4

17

Ö

Ö

Ö

84/101/104

21

15

Keyboard r and R

20

Ö

Ö

Ö

84/101/104

22

16

Keyboard s and S4

32

Ö

Ö

Ö

84/101/104

23

17

Keyboard t and T

21

Ö

Ö

Ö

84/101/104

24

18

Keyboard u and U

23

Ö

Ö

Ö

84/101/104

25

19

Keyboard v and V

49

Ö

Ö

Ö

84/101/104

26

1A

Keyboard w and W4

18

Ö

Ö

Ö

84/101/104

27

1B

Keyboard x and X4

47

Ö

Ö

Ö

84/101/104

28

1C

Keyboard y and Y4

22

Ö

Ö

Ö

84/101/104

29

1D

Keyboard z and Z4

46

Ö

Ö

Ö

84/101/104

30

1E

Keyboard 1 and ! 4

2

Ö

Ö

Ö

84/101/104

31

1F

Keyboard 2 and @4

3

Ö

Ö

Ö

84/101/104

32

20

Keyboard 3 and #4

4

Ö

Ö

Ö

84/101/104

33

21

Keyboard 4 and $4

5

Ö

Ö

Ö

84/101/104

34

22

Keyboard 5 and %4

6

Ö

Ö

Ö

84/101/104

35

23

Keyboard 6 and ^4

7

Ö

Ö

Ö

84/101/104

36

24

Keyboard 7 and &4

8

Ö

Ö

Ö

84/101/104

37

25

Keyboard 8 and *4

9

Ö

Ö

Ö

84/101/104

38

26

Keyboard 9 and (4

10

Ö

Ö

Ö

84/101/104

39

27

Keyboard 0 and ) 4

11

Ö

Ö

Ö

84/101/104

40

28

Keyboard Return(ENTER) 5

43

Ö

Ö

Ö

84/101/104

41

29

Keyboard ESCAPE

110

Ö

Ö

Ö

84/101/104

42

2A

Keyboard DELETE
(Backspace) 13

15

Ö

Ö

Ö

84/101/104

43

2B

Keyboard Tab

16

Ö

Ö

Ö

84/101/104

44

2C

Keyboard Spacebar

61

Ö

Ö

Ö

84/101/104

45

2D

Keyboard - and (underscore) 4

12

Ö

Ö

Ö

84/101/104

46

2E

Keyboard = and+4

13

Ö

Ö

Ö

84/101/104

47

2F

Keyboard [ and {4

27

Ö

Ö

Ö

84/101/104

48

30

Keyboard ] and }4

28

Ö

Ö

Ö

84/101/104

49

31

Keyboard \ and |

29

Ö

Ö

Ö

84/101/104

50

32

Keyboard Non-US# and ~2

42

Ö

Ö

Ö

84/101/104

51

33

Keyboard 4

40

Ö

Ö

Ö

84/101/104

52

34

Keyboard ‘ and “4

41

Ö

Ö

Ö

84/101/104

53

35

Keyboard Grave Accent and 

Tilde4

1

Ö

Ö

Ö

84/101/104

54

36

Keyboard , and <4

53

Ö

Ö

Ö

84/101/104

55

37

Keyboard . and >4

54

Ö

Ö

Ö

84/101/104

56

38

Keyboard / and ? 4

55

Ö

Ö

Ö

84/101/104

57

39

Keyboard CapsLock11

30

Ö

Ö

Ö

84/101/104

58

3A

Keyboard F1

112

Ö

Ö

Ö

84/101/104

59

3B

Keyboard F2

113

Ö

Ö

Ö

84/101/104

60

3C

Keyboard F3

114

Ö

Ö

Ö

84/101/104

61

3D

Keyboard F4

115

Ö

Ö

Ö

84/101/104

62

3E

Keyboard F5

116

Ö

Ö

Ö

84/101/104

63

3F

Keyboard F6

117

Ö

Ö

Ö

84/101/104

64

40

Keyboard F7

118

Ö

Ö

Ö

84/101/104

65

41

Keyboard F8

119

Ö

Ö

Ö

84/101/104

66

42

Keyboard F9

120

Ö

Ö

Ö

84/101/104

67

43

Keyboard F10

121

Ö

Ö

Ö

84/101/104

68

44

Keyboard F11

122

Ö

Ö

Ö

101/104

69

45

Keyboard F12

123

Ö

Ö

Ö

101/104

70

46

Keyboard PrintScreen1

124

Ö

Ö

Ö

101/104

71

47

Keyboard ScrollLock11

125

Ö

Ö

Ö

84/101/104

72

48

Keyboard Pause1

126

Ö

Ö

Ö

101/104

73

49

Keyboard Insert1

75

Ö

Ö

Ö

101/104

74

4A

Keyboard Home1

80

Ö

Ö

Ö

101/104

75

4B

Keyboard PageUp1

85

Ö

Ö

Ö

101/104

76

4C

Keyboard Delete Forward1

76

Ö

Ö

Ö

101/104

77

4D

Keyboard End1

81

Ö

Ö

Ö

101/104

78

4E

Keyboard PageDown1

86

Ö

Ö

Ö

101/104

79

4F

Keyboard RightArrow1

89

Ö

Ö

Ö

101/104

80

50

Keyboard LeftArrow1

79

Ö

Ö

Ö

101/104

81

51

Keyboard DownArrow1

84

Ö

Ö

Ö

101/104

82

52

Keyboard UpArrow1

83

Ö

Ö

Ö

101/104

83

53

Keypad NumLock and Clear11

90

Ö

Ö

Ö

101/104

84

54

Keypad /1

95

Ö

Ö

Ö

101/104

85

55

Keypad *

100

Ö

Ö

Ö

84/101/104

86

56

Keypad -

105

Ö

Ö

Ö

84/101/104

87

57

Keypad +

106

Ö

Ö

Ö

84/101/104

88

58

Keypad ENTER5

108

Ö

Ö

Ö

101/104

89

59

Keypad 1 and End

93

Ö

Ö

Ö

84/101/104

90

5A

Keypad 2 and Down Arrow

98

Ö

Ö

Ö

84/101/104

91

5B

Keypad 3 and PageDn

103

Ö

Ö

Ö

84/101/104

92

5C

Keypad 4 and Left Arrow

92

Ö

Ö

Ö

84/101/104

93

5D

Keypad 5

97

Ö

Ö

Ö

84/101/104

94

5E

Keypad 6 and Righ tArrow

102

Ö

Ö

Ö

84/101/104

95

5F

Keypad 7 and Home

91

Ö

Ö

Ö

84/101/104

96

60

Keypad 8 and Up Arrow

96

Ö

Ö

Ö

84/101/104

97

61

Keypad 9 and PageUp

101

Ö

Ö

Ö

84/101/104

98

62

Keypad 0 and Insert

99

Ö

Ö

Ö

84/101/104

99

63

Keypad . and Delete

104

Ö

Ö

Ö

84/101/104

100

64

Keyboard Non-US\ and |3;6

45

Ö

Ö

Ö

84/101/104

101

65

Keyboard Application10

129

Ö

Ö

104

102

66

Keyboard Power9

Ö

Ö

103

67

Keypad =

Ö

104

68

Keyboard F13

Ö

105

69

Keyboard F14

Ö

106

6A

Keyboard F15

Ö

107

6B

Keyboard F16

108

6C

Keyboard F17

109

6D

Keyboard F18

110

6E

Keyboard F19

111

6F

Keyboard F20

112

70

Keyboard F21

113

71

Keyboard F22

114

72

Keyboard F23

115

73

Keyboard F24

116

74

Keyboard Execute

Ö

117

75

Keyboard Help

Ö

118

76

Keyboard Menu

Ö

119

77

Keyboard Select

Ö

120

78

Keyboard Stop

Ö

121

79

Keyboard Again

Ö

122

7A

Keyboard Undo

Ö

123

7B

Keyboard Cut

Ö

124

7C

Keyboard Copy

Ö

125

7D

Keyboard Paste

Ö

126

7E

Keyboard Find

Ö

127

7F

Keyboard Mute

Ö

128

80

Keyboard Volume Up

Ö

129

81

Keyboard Volume Down

Ö

130

82

Keyboard Locking Caps Lock12

Ö

131

83

Keyboard Locking Num Lock12

Ö

132

84

Keyboard Locking Scroll

Ö

Lock 12

133

85

Keypad Comma

134

86

Keypad Equal Sign

135

87

Keyboard Kanji115

136

88

Keyboard Kanji216

137

89

Keyboard Kanji317

138

8A

Keyboard Kanji418

139

8B

Keyboard Kanji519

140

8C

Keyboard Kanji620

141

8D

Keyboard Kanji721

142

8E

Keyboard Kanji822

143

8F

Keyboard Kanji922

144

90

Keyboard LANG18

145

91

Keyboard LANG28

146

92

Keyboard LANG38

147

93

Keyboard LANG48

148

94

Keyboard LANG58

149

95

Keyboard LANG68

150

96

Keyboard LANG78

151

97

Keyboard LANG88

152

98

Keyboard LANG98

153

99

Keyboard AlternateErase7

154

9A

Keyboard SysReq/Attenti1

155

9B

Keyboard Cancel

156

9C

Keyboard Clear

157

9D

Keyboard Prior

158

9E

Keyboard Return

159

9F

Keyboard Separator

160

A0

Keyboard Out

161

A1

Keyboard Oper

162

A2

Keyboard Clear/Again

163

A3

Keyboard CrSel/Props

164

A4

Keyboard ExSel

165-223

A5-DF

Reserved

224

E0

Keyboard LeftControl

58

Ö

Ö

Ö

84/101/104

225

E1

Keyboard LeftShift

44

Ö

Ö

Ö

84/101/104

226

E2

Keyboard LeftAlt

60

Ö

Ö

Ö

84/101/104

227

E3

Keyboard Left GUI10;23

127

Ö

Ö

Ö

104

228

E4

Keyboard RightControl

64

Ö

Ö

Ö

101/104

229

E5

Keyboard RightShift

57

Ö

Ö

Ö

84/101/104

230

E6

Keyboard RightAlt

62

Ö

Ö

Ö

101/104

231

E7

Keyboard Right GUI10;24

128

Ö

Ö

Ö

104

232-255

E8-FF

Reserved

1.9 usb鍵盤的led指示燈

當按下小鍵盤,大小寫,滾動鎖定,組合鍵,KANA控制按鍵的時候,usb鍵盤按鍵urb會處理usb數據並上報數據給輸入子系統處理

輸入子系統對鍵值爲小鍵盤,大小寫,滾動鎖定,組合鍵,KANA的事件做處理,處理後會調用輸入設備的event方法也就是usb_kbd_event

  1. static int usb_kbd_event(struct input_dev *dev, unsigned int type,unsigned int code, int value)  
  2. {  
  3.     struct usb_kbd *kbd = input_get_drvdata(dev);   //通過輸入設備獲取usb鍵盤設備  
  4.   
  5.     if (type != EV_LED)  
  6.         return -1;  
  7.     kbd->newleds = (!!test_bit(LED_KANA,dev->led) << 3)|(!!test_bit(LED_COMPOSE, dev->led) << 3)|  
  8.     (!!test_bit(LED_SCROLLL,dev->led) << 2)|(!!test_bit(LED_CAPSL,dev->led) << 1)|(!!test_bit(LED_NUML,dev->led));  
  9.     //判斷是否有 小鍵盤,大小寫,滾動鎖定,組合鍵,KANA事件  
  10.     if (kbd->led->status == -EINPROGRESS)  
  11.         return 0;  
  12.     if (*(kbd->leds) == kbd->newleds) //比較新舊指示燈狀態,跟目前狀態一致,則返回  
  13.         return 0;  
  14.     *(kbd->leds) = kbd->newleds;  //填充usb鍵盤led數據傳輸緩衝區  
  15.     kbd->led->dev = kbd->usbdev;   //捆綁usb設備  
  16.     if (usb_submit_urb(kbd->led, GFP_ATOMIC))    //跟目前狀態不一致,則提交usb鍵盤led urb 會通過控制輸出端口發送setup包設置led燈狀態  
  17.         err_hid("usb_submit_urb(leds) failed");  
  18.     return 0;  
  19. }  

usb鍵盤led燈urb的回調函數

  1. static void usb_kbd_led(struct urb *urb)  
  2. {  
  3.     struct usb_kbd *kbd = urb->context;  //通過urb獲取usb鍵盤設備  
  4.     if (urb->status)  
  5.         dev_warn(&urb->dev->dev, "led urb status %d received\n",urb->status);  
  6.     if (*(kbd->leds) == kbd->newleds) //比較新舊指示燈狀態,跟目前狀態一致,則返回  
  7.         return;  
  8.     *(kbd->leds) = kbd->newleds;  //填充usb鍵盤led數據傳輸緩衝區  
  9.     kbd->led->dev = kbd->usbdev;   //捆綁usb設備  
  10.     if (usb_submit_urb(kbd->led, GFP_ATOMIC))    //跟目前狀態不一致,提交usb鍵盤led urb 會通過控制輸出端口發送setup包設置led燈狀態  
  11.         err_hid("usb_submit_urb(leds) failed");  
  12. }  

urb會發送setup包,Set_Report請求包通過控制端點0,緊接着是個2字節的數據輸出包,第一個字節對應報告id,第二個字節是led數據信息(上圖)


2.0 後話 關於usb_kbd_event函數調用的流程

首先定義了一個鍵盤任務,任務會循環執行kbd_bh函數
這裏定義的時候是禁用了,在後面的執行的kbd_init函數中會使能,和調度keyboard_tasklet任務

  1. DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);  //創建keyboard_tasklet執行kbd_bh  

kbd_bh函數獲取通過getleds函數獲取led狀態標誌,然後最終會調用kbd_update_leds_helper函數

  1. static void kbd_bh(unsigned long dummy)  
  2. {  
  3.     unsigned char leds = getleds(); //獲取led狀態標誌  
  4.     if (leds != ledstate) {  
  5.         input_handler_for_each_handle(&kbd_handler, &leds,kbd_update_leds_helper);  //會調用kbd_update_leds_helper  
  6.         ledstate = leds;  
  7.     }  
  8. }  

getleds函數獲取kbd->ledflagstate這個值,處理並返回.

  1. static inline unsigned char getleds(void)  
  2. {  
  3.     struct kbd_struct *kbd = kbd_table + fg_console;  
  4.     unsigned char leds;  
  5.     int i;  
  6.   
  7.     if (kbd->ledmode == LED_SHOW_IOCTL)  
  8.         return ledioctl;  
  9.     leds = kbd->ledflagstate;    //獲取led標誌狀態  
  10.     if (kbd->ledmode == LED_SHOW_MEM) {  
  11.         for (i = 0; i < 3; i++)  
  12.             if (ledptrs[i].valid) {  
  13.                 if (*ledptrs[i].addr & ledptrs[i].mask)  
  14.                     leds |= (1 << i);  
  15.                 else  
  16.                     leds &= ~(1 << i);  
  17.             }  
  18.     }  
  19.     return leds;  
  20. }  

ldeflagstate的值可以由以下三個函數來設置

  1. static inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)  
  2. {  
  3.         kbd->ledflagstate |= 1 << flag;  
  4. }  
  5.   
  6. static inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)  
  7. {  
  8.     kbd->ledflagstate &= ~(1 << flag);  
  9. }  
  10.   
  11. static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)  
  12. {  
  13.     kbd->ledflagstate ^= 1 << flag;  
  14. }  

而這三個函數的調用情況如下,鍵盤按鍵處理事件

  1. fn_caps_on      >>> set_vc_kbd_led(kbd, VC_CAPSLOCK);  //大小寫led  
  2. k_shift         >>> clr_vc_kbd_led(kbd, VC_CAPSLOCK);  //大小寫led  
  3. fn_caps_toggle  >>> chg_vc_kbd_led(kbd, VC_CAPSLOCK);  //大小寫led  
  4.   
  5. fn_bare_num     >>> chg_vc_kbd_led(kbd, VC_NUMLOCK);   //小鍵盤led  
  6.   
  7. con_stop        >>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滾輪鎖定led  
  8. con_start       >>> clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滾輪鎖定led  

獲取led狀態標誌後,調用kbd_update_leds_helper函數,上報led事件

  1. static int kbd_update_leds_helper(struct input_handle *handle, void *data)  
  2. {  
  3.     unsigned char leds = *(unsigned char *)data;  
  4.     if (test_bit(EV_LED, handle->dev->evbit)) {  
  5.         input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));   //上報滾輪鎖定事件  
  6.         input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));   //上報數字小鍵盤事件  
  7.         input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));   //上報大小寫事件  
  8.         input_inject_event(handle, EV_SYN, SYN_REPORT, 0);                  //同步事件  
  9.     }  
  10.     return 0;  
  11. }  

調用input_inject_event上報led事件,最終調用input_handle_event函數

  1. void input_inject_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)  
  2. {  
  3.     struct input_dev *dev = handle->dev;  
  4.     struct input_handle *grab;  
  5.     unsigned long flags;  
  6.   
  7.     if (is_event_supported(type, dev->evbit, EV_MAX)) {  
  8.         spin_lock_irqsave(&dev->event_lock, flags);  
  9.         rcu_read_lock();  
  10.         grab = rcu_dereference(dev->grab);  
  11.         if (!grab || grab == handle)  
  12.             input_handle_event(dev, handle->handler,type, code, value);  //調用input_handle_event函數  
  13.         rcu_read_unlock();  
  14.         spin_unlock_irqrestore(&dev->event_lock, flags);  
  15.     }  
  16. }  
  17. EXPORT_SYMBOL(input_inject_event);  

input_handle_event函數處理各種事件分支,最終就會調用到input設備的event方法(usb_kbd_event)

  1. static void input_handle_event(struct input_dev *dev,struct input_handler *src_handler,unsigned int type, unsigned int code, int value)  
  2. {  
  3.     int disposition = INPUT_IGNORE_EVENT;  
  4.   
  5.     switch (type) {  
  6.   
  7.     case EV_SYN:    //同步事件  
  8.         switch (code) {  
  9.         case SYN_CONFIG:  
  10.             disposition = INPUT_PASS_TO_ALL;  
  11.             break;  
  12.         case SYN_REPORT:    //led同步事件分支  
  13.             if (!dev->sync) {  
  14.                 dev->sync = true;  
  15.                 disposition = INPUT_PASS_TO_HANDLERS;  
  16.             }  
  17.             break;  
  18.         case SYN_MT_REPORT:  
  19.             dev->sync = false;  
  20.             disposition = INPUT_PASS_TO_HANDLERS;  
  21.             break;  
  22.         }  
  23.         break;  
  24.     case EV_KEY:  
  25.         if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {  
  26.             if (value != 2) {  
  27.                 __change_bit(code, dev->key);  
  28.                 if (value)  
  29.                     input_start_autorepeat(dev, code);  
  30.                 else  
  31.                     input_stop_autorepeat(dev);  
  32.             }  
  33.             disposition = INPUT_PASS_TO_HANDLERS;  
  34.         }  
  35.         break;  
  36.     case EV_SW:  
  37.         if (is_event_supported(code, dev->swbit, SW_MAX) &&  
  38.             !!test_bit(code, dev->sw) != value) {  
  39.             __change_bit(code, dev->sw);  
  40.             disposition = INPUT_PASS_TO_HANDLERS;  
  41.         }  
  42.         break;  
  43.     case EV_ABS:  
  44.         if (is_event_supported(code, dev->absbit, ABS_MAX))  
  45.             disposition = input_handle_abs_event(dev, src_handler,code, &value);  
  46.         break;  
  47.     case EV_REL:  
  48.         if (is_event_supported(code, dev->relbit, REL_MAX) && value)  
  49.             disposition = INPUT_PASS_TO_HANDLERS;  
  50.         break;  
  51.     case EV_MSC:  
  52.         if (is_event_supported(code, dev->mscbit, MSC_MAX))  
  53.             disposition = INPUT_PASS_TO_ALL;  
  54.   
  55.         break;  
  56.   
  57.     case EV_LED:    //led處理  
  58.         if (is_event_supported(code, dev->ledbit, LED_MAX) &&  
  59.             !!test_bit(code, dev->led) != value) {  
  60.             __change_bit(code, dev->led);    //修改input設備的led標誌位  
  61.             disposition = INPUT_PASS_TO_ALL;  
  62.         }  
  63.         break;  
  64.     case EV_SND:  
  65.         if (is_event_supported(code, dev->sndbit, SND_MAX)) {  
  66.             if (!!test_bit(code, dev->snd) != !!value)  
  67.                 __change_bit(code, dev->snd);  
  68.             disposition = INPUT_PASS_TO_ALL;  
  69.         }  
  70.         break;  
  71.     case EV_REP:  
  72.         if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {  
  73.             dev->rep[code] = value;  
  74.             disposition = INPUT_PASS_TO_ALL;  
  75.         }  
  76.         break;  
  77.     case EV_FF:  
  78.         if (value >= 0)  
  79.             disposition = INPUT_PASS_TO_ALL;  
  80.         break;  
  81.     case EV_PWR:  
  82.         disposition = INPUT_PASS_TO_ALL;  
  83.         break;  
  84.     }  
  85.     if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)  
  86.         dev->sync = false;  
  87.     if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)  //led事件  
  88.         dev->event(dev, type, code, value);  //調用input設備的event方法(usb_kbd_event)  
  89.     if (disposition & INPUT_PASS_TO_HANDLERS)   //led同步事件  
  90.         input_pass_event(dev, src_handler, type, code, value);  //會調用input_handler的event方法(kbd_event)  
  91. }  
  92.   
  93.   
  94.   
  95.   
  96.   
  97.   
  98.   
  99.   
  100.   
  101.    

 

 

 

 

 

 

 

 

 

 

 

 



 

 

 

 

 

發佈了2 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章