usb键鼠驱动分析

一、鼠标

linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现

1.加载初始化过程

1.1模块入口

[cpp]  view plain copy
  1. module_init(usb_mouse_init);  

1.2初始化函数

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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设备

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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方法

[cpp]  view plain copy
  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方法

[cpp]  view plain copy
  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打开鼠标

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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键鼠驱动分析_第1张图片
当输入设备上报完usb接口接收来的数据后,需要调用input_sync同步事件消息,并调用usb_submit_urb提交urb

使其继续监视处理usb鼠标设备传递的新数据.

应用程序要获取鼠标操作信息可以打开对应的输入设备节点,并通过输入设备的读接口,获取到usb鼠标通过usb接口传递并交由输入设备上报过来的数据

漏掉的函数

1.应用程序关闭鼠标设备

[cpp]  view plain copy
  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.模块移除调用的函数

[cpp]  view plain copy
  1. module_exit(usb_mouse_exit);  
[cpp]  view plain copy
  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 模块入口

[cpp]  view plain copy
  1. module_init(usb_kbd_init);  

1.2 初始化函数

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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设备

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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方法

[cpp]  view plain copy
  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方法中调用的内存分配释放函数

分配内存

[cpp]  view plain copy
  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. }  

释放内存

[cpp]  view plain copy
  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. }  

配置用到的全局键值数组

[cpp]  view plain copy
  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. };  


usb键鼠驱动分析_第2张图片

 

1.6 拔掉usb鼠标就会调用disconnect方法

[cpp]  view plain copy
  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打开键盘

[cpp]  view plain copy
  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

[cpp]  view plain copy
  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数据传输

[cpp]  view plain copy
  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. }  

usb键鼠驱动分析_第3张图片

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

你可能感兴趣的:(usb键鼠驱动分析)