一、鼠標
linux下的usb鼠標驅動在/drivers/hid/usbhid/usbmouse.c中實現
1.加載初始化過程
1.1模塊入口
module_init(usb_mouse_init);
1.2初始化函數
static int __init usb_mouse_init(void) //初始化
{
int retval = usb_register(&usb_mouse_driver); //注冊usb鼠標驅動
if (retval == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
return retval;
}
1.3初始化函數注冊了一個usb驅動usb_mouse_driver
static struct usb_driver usb_mouse_driver = { //usb鼠標驅動
.name = "usbmouse", //驅動名
.probe = usb_mouse_probe, //匹配方法
.disconnect = usb_mouse_disconnect, //拔出方法
.id_table = usb_mouse_id_table, //支持設備id表
};
1.4當插入鼠標時會根據usb_mouse_id_table去匹配創建usb設備
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
它的匹配方式是接口id匹配.接口類USB_INTERFACE_CLASS_HID
usb插入枚舉時候會獲取usb鼠標的接口類型,獲取其接口類信息,匹配成功的話會動態創建一個usb_device.
在分析probe和disconnect方法之前先介紹下驅動用來描述usb鼠標對象的結構體usb_mouse
struct usb_mouse {
char name[128];//usb鼠標設備名
char phys[64];//路徑
struct usb_device *usbdev;//usb設備
struct input_dev *dev;//輸入設備
struct urb *irq;//urb結構體
signed char *data; //數據傳輸緩沖區指針
dma_addr_t data_dma;
};
usb鼠標既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性
1.5 匹配成功了就會調用probe方法
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //根據usb接口獲取動態創建的usb_device
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM;
interface = intf->cur_altsetting; //獲取usb_host_interface
if (interface->desc.bNumEndpoints != 1) //鼠標的端點有且僅有1個控制端點
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //獲取端點描述符
if (!usb_endpoint_is_int_in(endpoint)) //判斷該端點是否中斷端點
return -ENODEV;
//上面判斷了usb鼠標的屬性,有且僅有1個控制端點(0號端點不算進來的)
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //設置端點為中斷輸入端點
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //獲取包數據最大值
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); //分配usb_mouse對象
input_dev = input_allocate_device(); //初始化輸入設備
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//分配初始化usb鼠標數據緩沖區內存(默認8位數據)
if (!mouse->data)
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配初始化urb
if (!mouse->irq)
goto fail2;
mouse->usbdev = dev; //設置usb鼠標設備的usb設備對象
mouse->dev = input_dev; //設備usb鼠標設備的input設備對象
if (dev->manufacturer) //枚舉時候有獲取到有效的廠商名
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); //復制廠商名到name
if (dev->product) { //枚舉時候有獲取到有效的產品名
if (dev->manufacturer) //如果也有廠商名
strlcat(mouse->name, " ", sizeof(mouse->name)); //則用空格將廠商名和產品名隔開
strlcat(mouse->name, dev->product, sizeof(mouse->name)); //追加產品名到name
}
if (!strlen(mouse->name)) //如果廠商和產品名都沒有
snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
//則直接根據廠商id和產品id給name賦值
usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //設置設備路徑名
strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0
input_dev->name = mouse->name; //輸入設備的名字設置成usb鼠標的名字
input_dev->phys = mouse->phys; //輸入設備的路徑設置成usb鼠標的路徑
usb_to_input_id(dev, &input_dev->id); //設置輸入設備的bustype,vendor,product,version
input_dev->dev.parent = &intf->dev; //usb接口設備為輸入設備的父設備
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //輸入事件類型按鍵+相對位移
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
//按鍵類型 鼠標:左鍵,右鍵,中鍵
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); //相對位移x方向+y方向
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
//按鍵類型 鼠標:旁鍵,外部鍵
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //相對位移 鼠標滾輪事件
input_set_drvdata(input_dev, mouse); //usb鼠標驅動文件作為輸入設備的設備文件的驅動數據
input_dev->open = usb_mouse_open; //設置輸入事件的打開方法
input_dev->close = usb_mouse_close; //設置輸入事件的關閉方法
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);
//填充中斷類型urb 指定了urb的回調函數是usb_mouse_irq
mouse->irq->transfer_dma = mouse->data_dma;//dma數據緩沖區指向usb鼠標設備的data_dma成員
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//沒DMA映射
error = input_register_device(mouse->dev);
if (error)
goto fail3;
usb_set_intfdata(intf, mouse); ////usb鼠標驅動文件作為usb接口設備的設備文件的驅動數據
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
1.6 拔掉usb鼠標就會調用disconnect方法
static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf); //根據usb接口設備的設備文件的驅動數據,獲取usb鼠標設備
usb_set_intfdata(intf, NULL); //清空usb接口設備的設備文件的驅動數據
if (mouse) {
usb_kill_urb(mouse->irq); //斷掉urb傳輸
input_unregister_device(mouse->dev); //注銷輸入設備
usb_free_urb(mouse->irq); //釋放urb
usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); //清除傳輸數據緩沖區
kfree(mouse); //釋放usb鼠標設備
}
}
基本上disconnect只是probe的一個逆操作而已
經過probe過程,注冊了輸入設備則會在/dev/input/目錄下會產生對應的鼠標設備節點,應用程序可以打開該節點來控制usb鼠標設備
此時會調用usb_mouse_open方法
1.7打開鼠標
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
return -EIO;
return 0;
}
通過urb提交之后,鼠標動作通過usb傳輸數據就會交由urb去處理了
1.8.urb數據傳輸
當操作鼠標的時候,會引起urb數據傳輸在數據傳輸之后會調用usb_mouse_irq
static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context; //獲取usb鼠標設備
signed char *data = mouse->data; //數據傳輸緩沖區指針
struct input_dev *dev = mouse->dev; //輸入設備
int status;
switch (urb->status) { //判斷urb傳輸的狀態
case 0: /* success */ //傳輸成功跳出switch
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01); //右鍵
input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //左鍵
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //中鍵
input_report_key(dev, BTN_SIDE, data[0] & 0x08); //邊鍵
input_report_key(dev, BTN_EXTRA, data[0] & 0x10); //外部鍵
input_report_rel(dev, REL_X, data[1]); //相對x坐標位移
input_report_rel(dev, REL_Y, data[2]); //相對y坐標位移
input_report_rel(dev, REL_WHEEL, data[3]); //相對滾輪位移
input_sync(dev); //同步事件
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC); //繼續提交urb
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);
}
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.應用程序關閉鼠標設備
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); //通過輸入設備獲取usb鼠標設備
usb_kill_urb(mouse->irq); //當關閉鼠標設備時候,需要斷掉urb傳輸
}
2.模塊移除調用的函數
module_exit(usb_mouse_exit);
static void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver); //注銷掉usb鼠標設備
}
二、鍵盤
linux下的usb鍵盤驅動在/drivers/hid/usbhid/usbkbd.c中實現
1.加載初始化過程
1.1 模塊入口
module_init(usb_kbd_init);
1.2 初始化函數
static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver); //注冊usb鍵盤
if (result == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
return result;
}
1.3 初始化函數注冊了一個usb驅動usb_kbd_driver
static struct usb_driver usb_kbd_driver = { //usb鍵盤驅動
.name = "usbkbd", //驅動名
.probe = usb_kbd_probe, //匹配方法
.disconnect = usb_kbd_disconnect, //拔出方法
.id_table = usb_kbd_id_table, //支持設備id
};
1.4 當插入鼠標時會根據usb_kbd_id_table去匹配創建usb設備
static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ } /* Terminating entry */
};
它的匹配方式是接口id匹配.接口類USB_INTERFACE_CLASS_HID
usb插入枚舉時候會獲取usb鍵盤的接口類型,獲取其接口類信息,匹配成功的話會動態創建一個usb_device.
在分析probe和disconnect方法之前先介紹下驅動用來描述usb鍵盤對象的結構體usb_kbd
struct usb_kbd {
struct input_dev *dev; //輸入設備
struct usb_device *usbdev; //usb設備
unsigned char old[8]; //舊的鍵盤按鍵數據
struct urb *irq, *led; //鍵盤urb,led urb
unsigned char newleds; //新的led數據
char name[128]; //usb鍵盤設備名字
char phys[64]; //usb鍵盤設備路徑
unsigned char *new; //usb鍵盤按鍵 數據傳輸緩沖區指針
struct usb_ctrlrequest *cr; //setup數據包控制請求描述符
unsigned char *leds; //usb鍵盤led 數據傳輸緩沖區指針
dma_addr_t new_dma; //usb鍵盤按鍵DMA映射總線地址
dma_addr_t leds_dma; //usb鍵盤led DMA映射總線地址
};
usb鍵盤既包含usb設備(usb_device)的屬性也包含input輸入設備(input_dev)的屬性
1.5 匹配成功了就會調用probe方法
static int usb_kbd_probe(struct usb_interface *iface,const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface); //根據usb接口獲取動態創建的usb_device
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
int error = -ENOMEM;
interface = iface->cur_altsetting; //獲取usb_host_interface
if (interface->desc.bNumEndpoints != 1) //鍵盤的端點有且僅有1個控制端點
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //獲取端點描述符
if (!usb_endpoint_is_int_in(endpoint)) //判斷該端點是否中斷端點
return -ENODEV;
//上面判斷了usb鍵盤的屬性,有且僅有1個控制端點(0號端點不算進來的)
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //設置端點為中斷輸入端點
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //獲取包數據最大值
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); //分配usb_kbd對象
input_dev = input_allocate_device(); //初始化輸入設備
if (!kbd || !input_dev)
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd)) //分配usb鍵盤需要的內存
goto fail2;
kbd->usbdev = dev; //設置usb鍵盤設備的usb設備對象
kbd->dev = input_dev; //設備usb鍵盤設備的input設備對象
if (dev->manufacturer) //枚舉時候有獲取到有效的廠商名
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); //復制廠商名到name
if (dev->product) { //枚舉時候有獲取到有效的產品名
if (dev->manufacturer) //如果也有廠商名
strlcat(kbd->name, " ", sizeof(kbd->name)); //則用空格將廠商名和產品名隔開
strlcat(kbd->name, dev->product, sizeof(kbd->name)); //追加產品名到name
}
if (!strlen(kbd->name)) //如果廠商和產品名都沒有
snprintf(kbd->name, sizeof(kbd->name),"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
//則直接根據廠商id和產品id給name賦值
usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); //設置設備路徑名
strlcat(kbd->phys, "/input0", sizeof(kbd->phys)); //追加/input0
input_dev->name = kbd->name; //輸入設備的名字設置成usb鍵盤的名字
input_dev->phys = kbd->phys; //輸入設備的路徑設置成usb鍵盤的路徑
usb_to_input_id(dev, &input_dev->id); //設置輸入設備的bustype,vendor,product,version
input_dev->dev.parent = &iface->dev; //usb接口設備為輸入設備的父設備
input_set_drvdata(input_dev, kbd); //usb鍵盤驅動文件作為輸入設備的設備文件的驅動數據
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP); //輸入事件類型 按鍵+led+重復
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);
//鍵盤led事件:小鍵盤,大小寫,滾動鎖定,組合鍵,KANA
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit); //清除無效的0位
//鍵盤按鍵事件:遍歷全局usb_kbd_keycode數組設置
input_dev->event = usb_kbd_event; //設置輸入事件的event方法
input_dev->open = usb_kbd_open; //設置輸入事件的open方法
input_dev->close = usb_kbd_close; //設置輸入事件的close方法
usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),usb_kbd_irq, kbd, endpoint->bInterval);
//填充中斷類型urb 指定了urb的回調函數是usb_kbd_irq
kbd->irq->transfer_dma = kbd->new_dma; //usb鍵盤按鍵設備DMA映射總線地址
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //沒DMA映射
//設置usb setup傳輸數據包控制請求結構體
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;//SET_IDLE?
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1,usb_kbd_led, kbd);
//設置為控制輸出端點,填充控制類型urb,回調函數usb_kbd_led
kbd->led->transfer_dma = kbd->leds_dma; //usb鍵盤led設備DMA映射總線地址
kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //沒DMA映射
error = input_register_device(kbd->dev); //注冊輸入設備
if (error)
goto fail2;
usb_set_intfdata(iface, kbd); //usb鍵盤驅動文件作為usb接口設備的設備文件的驅動數據
device_set_wakeup_enable(&dev->dev, 1); //使能系統喚醒
return 0;
fail2:
usb_kbd_free_mem(dev, kbd); //分配失敗則釋放相關內存
fail1:
input_free_device(input_dev); //釋放輸入設備
kfree(kbd); //釋放usb_kbd
return error;
}
probe方法中調用的內存分配釋放函數
分配內存
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) //分配按鍵urb
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) //分配led燈urb
return -1;
if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma))) //分配初始化usb鍵盤數據緩沖區內存(默認8位數據)
return -1;
if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) //分配setup包的控制請求描述符
return -1;
if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) //分配初始化usb鍵盤led數據緩沖區內存
return -1;
return 0;
}
釋放內存
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
usb_free_urb(kbd->irq); //釋放鍵盤按鍵urb
usb_free_urb(kbd->led); //釋放鍵盤led urb
usb_free_coherent(dev, 8, kbd->new, kbd->new_dma); //釋放usb鍵盤數據緩沖區
kfree(kbd->cr); //釋放setup包的控制請求描述符
usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma); //釋放urb鍵盤led數據緩沖區內存
}
配置用到的全局鍵值數組
static const unsigned char usb_kbd_keycode[256] = { //鍵值
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};

1.6 拔掉usb鼠標就會調用disconnect方法
static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf); //根據usb接口設備的設備文件的驅動數據,獲取usb鍵盤設備
usb_set_intfdata(intf, NULL); //清空usb接口設備的設備文件的驅動數據
if (kbd) {
usb_kill_urb(kbd->irq); //斷掉urb傳輸
input_unregister_device(kbd->dev); //注銷輸入設備
usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //釋放usb鍵盤需要的內存
kfree(kbd); //釋放usb鍵盤設備
}
}
基本上disconnect只是probe的一個逆操作而已
經過probe過程,注冊了輸入設備則會在/dev/input/目錄下會產生對應的鍵盤設備節點,應用程序可以打開該節點來控制usb鍵盤設備
此時會調用usb_kbd_open方法
1.7打開鍵盤
static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通過輸入設備獲取usb鍵盤設備
kbd->irq->dev = kbd->usbdev; //usb鍵盤按鍵urb捆綁usb設備
if (usb_submit_urb(kbd->irq, GFP_KERNEL)) //提交usb鍵盤按鍵urb
return -EIO;
return 0;
}
關閉鍵盤調用usb_kbd_close
static void usb_kbd_close(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通過輸入設備獲取usb鍵盤設備
usb_kill_urb(kbd->irq); //斷開usb鍵盤按鍵urb
}
通過urb提交之后,鍵盤動作通過usb傳輸數據就會交由urb去處理了
1.8.urb數據傳輸
static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context; //獲取usb鍵盤設備
int i;
switch (urb->status) { //判斷urb傳輸的狀態
case 0: /* success */ //傳輸成功跳出switch
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
//L-ctrl,L-shift,L-alt,L-gui,R-ctrl,R-shift,R-alt,R-gui
for (i = 0; i < 8; i++) //(224~231)判斷新按下的是否組合鍵
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); //組合鍵
//memscan(kbd->new + 2, kbd->old[i], 6)表示從kbd->new[2]掃描6個單位到kbd->new[7],
//查找kbd->old[i]一樣的字符,返回掃描到的單位地址"==kbd->new+8"表示沒找到
//鍵盤掃描碼0-No Event 1-Overrun Error 2-POST Fail 3-ErrorUndefined所以kbd->old[i] > 3
//鍵值usb_kbd_keycode[i]和掃描碼new[i]/old[i]要區分好別亂了
//鍵盤掃描碼和數據格式見函數下面圖片
for (i = 2; i < 8; i++) {
//新數據中沒有查找到舊數據一樣的碼值--表示新的按鍵按下,舊的按鍵釋放
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]]) //松開的按鍵是正常的按鍵
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上報釋放按鍵事件
else
dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->old[i]);
}
//舊數據中沒有查找到新數據一樣的碼值--表示新的按鍵按下,就的按鍵按下
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]]) //按下的按鍵是正常的按鍵
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); //上報按下按鍵事件
else
dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->new[i]);
}
}
//數據的第2~7字節用於存放鍵碼,分別可以存放6個,也就是可以支持同時6個按鍵按下
//如果一直按住鍵盤的某個按鍵,則usb接收到的數據會都是一樣的也就是kbd->old==kbd->new,則按下的時候會上報按下事件,一直按着的時候不會繼續上報按下或釋放按鍵
//若有新的按鍵按下,則所有的kdb->old的值可以在kdb->new中找到,而kdb->new中代表新按鍵鍵碼的值在kdb->old中會找不到,所以觸發第二個if條件成立,上報按下按鍵事件
//若之前的按鍵松開,則所有的kdb->new的值可以在kdb->old中找到,而kdb->old中代表舊按鍵鍵碼的值在kdb->new中會找不到,所以觸發第一個if條件成立,上報釋放按鍵事件
input_sync(kbd->dev); //同步事件
memcpy(kbd->old, kbd->new, 8); //新的鍵值存放在舊的鍵值
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC); //提交urb
if (i)
err_hid ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);
}

| Usage |
Usage |
|
Ref:typical |
|
|
|
|
||
| 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 |
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
static int usb_kbd_event(struct input_dev *dev, unsigned int type,unsigned int code, int value)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通過輸入設備獲取usb鍵盤設備
if (type != EV_LED)
return -1;
kbd->newleds = (!!test_bit(LED_KANA,dev->led) << 3)|(!!test_bit(LED_COMPOSE, dev->led) << 3)|
(!!test_bit(LED_SCROLLL,dev->led) << 2)|(!!test_bit(LED_CAPSL,dev->led) << 1)|(!!test_bit(LED_NUML,dev->led));
//判斷是否有 小鍵盤,大小寫,滾動鎖定,組合鍵,KANA事件
if (kbd->led->status == -EINPROGRESS)
return 0;
if (*(kbd->leds) == kbd->newleds) //比較新舊指示燈狀態,跟目前狀態一致,則返回
return 0;
*(kbd->leds) = kbd->newleds; //填充usb鍵盤led數據傳輸緩沖區
kbd->led->dev = kbd->usbdev; //捆綁usb設備
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前狀態不一致,則提交usb鍵盤led urb 會通過控制輸出端口發送setup包設置led燈狀態
err_hid("usb_submit_urb(leds) failed");
return 0;
}
usb鍵盤led燈urb的回調函數
static void usb_kbd_led(struct urb *urb)
{
struct usb_kbd *kbd = urb->context; //通過urb獲取usb鍵盤設備
if (urb->status)
dev_warn(&urb->dev->dev, "led urb status %d received\n",urb->status);
if (*(kbd->leds) == kbd->newleds) //比較新舊指示燈狀態,跟目前狀態一致,則返回
return;
*(kbd->leds) = kbd->newleds; //填充usb鍵盤led數據傳輸緩沖區
kbd->led->dev = kbd->usbdev; //捆綁usb設備
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前狀態不一致,提交usb鍵盤led urb 會通過控制輸出端口發送setup包設置led燈狀態
err_hid("usb_submit_urb(leds) failed");
}
urb會發送setup包,Set_Report請求包通過控制端點0,緊接着是個2字節的數據輸出包,第一個字節對應報告id,第二個字節是led數據信息(上圖)
2.0 后話 關於usb_kbd_event函數調用的流程
首先定義了一個鍵盤任務,任務會循環執行kbd_bh函數
這里定義的時候是禁用了,在后面的執行的kbd_init函數中會使能,和調度keyboard_tasklet任務
DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); //創建keyboard_tasklet執行kbd_bh
kbd_bh函數獲取通過getleds函數獲取led狀態標志,然后最終會調用kbd_update_leds_helper函數
static void kbd_bh(unsigned long dummy)
{
unsigned char leds = getleds(); //獲取led狀態標志
if (leds != ledstate) {
input_handler_for_each_handle(&kbd_handler, &leds,kbd_update_leds_helper); //會調用kbd_update_leds_helper
ledstate = leds;
}
}
getleds函數獲取kbd->ledflagstate這個值,處理並返回.
static inline unsigned char getleds(void)
{
struct kbd_struct *kbd = kbd_table + fg_console;
unsigned char leds;
int i;
if (kbd->ledmode == LED_SHOW_IOCTL)
return ledioctl;
leds = kbd->ledflagstate; //獲取led標志狀態
if (kbd->ledmode == LED_SHOW_MEM) {
for (i = 0; i < 3; i++)
if (ledptrs[i].valid) {
if (*ledptrs[i].addr & ledptrs[i].mask)
leds |= (1 << i);
else
leds &= ~(1 << i);
}
}
return leds;
}
ldeflagstate的值可以由以下三個函數來設置
static inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate |= 1 << flag;
}
static inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate &= ~(1 << flag);
}
static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate ^= 1 << flag;
}
而這三個函數的調用情況如下,鍵盤按鍵處理事件
fn_caps_on >>> set_vc_kbd_led(kbd, VC_CAPSLOCK); //大小寫led k_shift >>> clr_vc_kbd_led(kbd, VC_CAPSLOCK); //大小寫led fn_caps_toggle >>> chg_vc_kbd_led(kbd, VC_CAPSLOCK); //大小寫led fn_bare_num >>> chg_vc_kbd_led(kbd, VC_NUMLOCK); //小鍵盤led con_stop >>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滾輪鎖定led con_start >>> clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滾輪鎖定led
獲取led狀態標志后,調用kbd_update_leds_helper函數,上報led事件
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned char leds = *(unsigned char *)data;
if (test_bit(EV_LED, handle->dev->evbit)) {
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); //上報滾輪鎖定事件
input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); //上報數字小鍵盤事件
input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); //上報大小寫事件
input_inject_event(handle, EV_SYN, SYN_REPORT, 0); //同步事件
}
return 0;
}
調用input_inject_event上報led事件,最終調用input_handle_event函數
void input_inject_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
struct input_dev *dev = handle->dev;
struct input_handle *grab;
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
rcu_read_lock();
grab = rcu_dereference(dev->grab);
if (!grab || grab == handle)
input_handle_event(dev, handle->handler,type, code, value); //調用input_handle_event函數
rcu_read_unlock();
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_inject_event);
input_handle_event函數處理各種事件分支,最終就會調用到input設備的event方法(usb_kbd_event)
static void input_handle_event(struct input_dev *dev,struct input_handler *src_handler,unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN: //同步事件
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT: //led同步事件分支
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, src_handler,code, &value);
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED: //led處理
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led); //修改input設備的led標志位
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) //led事件
dev->event(dev, type, code, value); //調用input設備的event方法(usb_kbd_event)
if (disposition & INPUT_PASS_TO_HANDLERS) //led同步事件
input_pass_event(dev, src_handler, type, code, value); //會調用input_handler的event方法(kbd_event)
}
