usb键鼠驱动分析【钻】

本文转载自:http://blog.csdn.net/orz415678659/article/details/9197859

一、鼠标

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. interface = intf->cur_altsetting;    //获取usb_host_interface
  11. if (interface->desc.bNumEndpoints != 1)  //鼠标的端点有且仅有1个控制端点
  12. return -ENODEV;
  13. endpoint = &interface->endpoint[0].desc; //获取端点描述符
  14. if (!usb_endpoint_is_int_in(endpoint))  //判断该端点是否中断端点
  15. return -ENODEV;
  16. //上面判断了usb鼠标的属性,有且仅有1个控制端点(0号端点不算进来的)
  17. pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //设置端点为中断输入端点
  18. maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
  19. mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);  //分配usb_mouse对象
  20. input_dev = input_allocate_device();    //初始化输入设备
  21. if (!mouse || !input_dev)
  22. goto fail1;
  23. mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//分配初始化usb鼠标数据缓冲区内存(默认8位数据)
  24. if (!mouse->data)
  25. goto fail1;
  26. mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配初始化urb
  27. if (!mouse->irq)
  28. goto fail2;
  29. mouse->usbdev = dev; //设置usb鼠标设备的usb设备对象
  30. mouse->dev = input_dev;  //设备usb鼠标设备的input设备对象
  31. if (dev->manufacturer)   //枚举时候有获取到有效的厂商名
  32. strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));  //复制厂商名到name
  33. if (dev->product) {      //枚举时候有获取到有效的产品名
  34. if (dev->manufacturer)   //如果也有厂商名
  35. strlcat(mouse->name, " ", sizeof(mouse->name));   //则用空格将厂商名和产品名隔开
  36. strlcat(mouse->name, dev->product, sizeof(mouse->name));   //追加产品名到name
  37. }
  38. if (!strlen(mouse->name))    //如果厂商和产品名都没有
  39. snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",
  40. le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
  41. //则直接根据厂商id和产品id给name赋值
  42. usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //设置设备路径名
  43. strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0
  44. input_dev->name = mouse->name;    //输入设备的名字设置成usb鼠标的名字
  45. input_dev->phys = mouse->phys;    //输入设备的路径设置成usb鼠标的路径
  46. usb_to_input_id(dev, &input_dev->id);    //设置输入设备的bustype,vendor,product,version
  47. input_dev->dev.parent = &intf->dev;       //usb接口设备为输入设备的父设备
  48. input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);   //输入事件类型按键+相对位移
  49. input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
  50. //按键类型 鼠标:左键,右键,中键
  51. input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);    //相对位移x方向+y方向
  52. input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
  53. //按键类型 鼠标:旁键,外部键
  54. input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //相对位移 鼠标滚轮事件
  55. input_set_drvdata(input_dev, mouse);    //usb鼠标驱动文件作为输入设备的设备文件的驱动数据
  56. input_dev->open = usb_mouse_open;    //设置输入事件的打开方法
  57. input_dev->close = usb_mouse_close;  //设置输入事件的关闭方法
  58. usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);
  59. //填充中断类型urb 指定了urb的回调函数是usb_mouse_irq
  60. mouse->irq->transfer_dma = mouse->data_dma;//dma数据缓冲区指向usb鼠标设备的data_dma成员
  61. mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//没DMA映射
  62. error = input_register_device(mouse->dev);
  63. if (error)
  64. goto fail3;
  65. usb_set_intfdata(intf, mouse);  ////usb鼠标驱动文件作为usb接口设备的设备文件的驱动数据
  66. return 0;
  67. fail3:
  68. usb_free_urb(mouse->irq);
  69. fail2:
  70. usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
  71. fail1:
  72. input_free_device(input_dev);
  73. kfree(mouse);
  74. return error;
  75. }

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. usb_set_intfdata(intf, NULL);   //清空usb接口设备的设备文件的驱动数据
  5. if (mouse) {
  6. usb_kill_urb(mouse->irq);    //断掉urb传输
  7. input_unregister_device(mouse->dev); //注销输入设备
  8. usb_free_urb(mouse->irq);    //释放urb
  9. usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);    //清除传输数据缓冲区
  10. kfree(mouse);   //释放usb鼠标设备
  11. }
  12. }

基本上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接口接收来的数据后,需要调用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. interface = iface->cur_altsetting;       //获取usb_host_interface
  11. if (interface->desc.bNumEndpoints != 1)  //键盘的端点有且仅有1个控制端点
  12. return -ENODEV;
  13. endpoint = &interface->endpoint[0].desc; //获取端点描述符
  14. if (!usb_endpoint_is_int_in(endpoint))  //判断该端点是否中断端点
  15. return -ENODEV;
  16. //上面判断了usb键盘的属性,有且仅有1个控制端点(0号端点不算进来的)
  17. pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //设置端点为中断输入端点
  18. maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
  19. kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);  //分配usb_kbd对象
  20. input_dev = input_allocate_device();    //初始化输入设备
  21. if (!kbd || !input_dev)
  22. goto fail1;
  23. if (usb_kbd_alloc_mem(dev, kbd))    //分配usb键盘需要的内存
  24. goto fail2;
  25. kbd->usbdev = dev;   //设置usb键盘设备的usb设备对象
  26. kbd->dev = input_dev;    //设备usb键盘设备的input设备对象
  27. if (dev->manufacturer)   //枚举时候有获取到有效的厂商名
  28. strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));  //复制厂商名到name
  29. if (dev->product) {      //枚举时候有获取到有效的产品名
  30. if (dev->manufacturer)   //如果也有厂商名
  31. strlcat(kbd->name, " ", sizeof(kbd->name));   //则用空格将厂商名和产品名隔开
  32. strlcat(kbd->name, dev->product, sizeof(kbd->name));   //追加产品名到name
  33. }
  34. if (!strlen(kbd->name))  //如果厂商和产品名都没有
  35. snprintf(kbd->name, sizeof(kbd->name),"USB HIDBP Keyboard %04x:%04x",
  36. le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
  37. //则直接根据厂商id和产品id给name赋值
  38. usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); //设置设备路径名
  39. strlcat(kbd->phys, "/input0", sizeof(kbd->phys)); //追加/input0
  40. input_dev->name = kbd->name;  //输入设备的名字设置成usb键盘的名字
  41. input_dev->phys = kbd->phys;  //输入设备的路径设置成usb键盘的路径
  42. usb_to_input_id(dev, &input_dev->id);    //设置输入设备的bustype,vendor,product,version
  43. input_dev->dev.parent = &iface->dev;  //usb接口设备为输入设备的父设备
  44. input_set_drvdata(input_dev, kbd);  //usb键盘驱动文件作为输入设备的设备文件的驱动数据
  45. input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP);    //输入事件类型 按键+led+重复
  46. input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);
  47. //键盘led事件:小键盘,大小写,滚动锁定,组合键,KANA
  48. for (i = 0; i < 255; i++)
  49. set_bit(usb_kbd_keycode[i], input_dev->keybit);
  50. clear_bit(0, input_dev->keybit); //清除无效的0位
  51. //键盘按键事件:遍历全局usb_kbd_keycode数组设置
  52. input_dev->event = usb_kbd_event;    //设置输入事件的event方法
  53. input_dev->open = usb_kbd_open;      //设置输入事件的open方法
  54. input_dev->close = usb_kbd_close;    //设置输入事件的close方法
  55. usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),usb_kbd_irq, kbd, endpoint->bInterval);
  56. //填充中断类型urb 指定了urb的回调函数是usb_kbd_irq
  57. kbd->irq->transfer_dma = kbd->new_dma;     //usb键盘按键设备DMA映射总线地址
  58. kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //没DMA映射
  59. //设置usb setup传输数据包控制请求结构体
  60. kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
  61. kbd->cr->bRequest = 0x09;//SET_IDLE?
  62. kbd->cr->wValue = cpu_to_le16(0x200);
  63. kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
  64. kbd->cr->wLength = cpu_to_le16(1);
  65. usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1,usb_kbd_led, kbd);
  66. //设置为控制输出端点,填充控制类型urb,回调函数usb_kbd_led
  67. kbd->led->transfer_dma = kbd->leds_dma;    //usb键盘led设备DMA映射总线地址
  68. kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //没DMA映射
  69. error = input_register_device(kbd->dev); //注册输入设备
  70. if (error)
  71. goto fail2;
  72. usb_set_intfdata(iface, kbd);   //usb键盘驱动文件作为usb接口设备的设备文件的驱动数据
  73. device_set_wakeup_enable(&dev->dev, 1);  //使能系统唤醒
  74. return 0;
  75. fail2:
  76. usb_kbd_free_mem(dev, kbd); //分配失败则释放相关内存
  77. fail1:
  78. input_free_device(input_dev);   //释放输入设备
  79. kfree(kbd); //释放usb_kbd
  80. return error;
  81. }

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. };

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. switch (urb->status) {   //判断urb传输的状态
  6. case 0:         /* success */   //传输成功跳出switch
  7. break;
  8. case -ECONNRESET:   /* unlink */
  9. case -ENOENT:
  10. case -ESHUTDOWN:
  11. return;
  12. /* -EPIPE:  should clear the halt */
  13. default:        /* error */
  14. goto resubmit;
  15. }
  16. //L-ctrl,L-shift,L-alt,L-gui,R-ctrl,R-shift,R-alt,R-gui
  17. for (i = 0; i < 8; i++)      //(224~231)判断新按下的是否组合键
  18. input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);   //组合键
  19. //memscan(kbd->new + 2, kbd->old[i], 6)表示从kbd->new[2]扫描6个单位到kbd->new[7],
  20. //查找kbd->old[i]一样的字符,返回扫描到的单位地址"==kbd->new+8"表示没找到
  21. //键盘扫描码0-No Event 1-Overrun Error 2-POST Fail 3-ErrorUndefined所以kbd->old[i] > 3
  22. //键值usb_kbd_keycode[i]和扫描码new[i]/old[i]要区分好别乱了
  23. //键盘扫描码和数据格式见函数下面图片
  24. for (i = 2; i < 8; i++) {
  25. //新数据中没有查找到旧数据一样的码值--表示新的按键按下,旧的按键释放
  26. if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
  27. if (usb_kbd_keycode[kbd->old[i]])    //松开的按键是正常的按键
  28. input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);  //上报释放按键事件
  29. else
  30. dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->old[i]);
  31. }
  32. //旧数据中没有查找到新数据一样的码值--表示新的按键按下,就的按键按下
  33. if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
  34. if (usb_kbd_keycode[kbd->new[i]])    //按下的按键是正常的按键
  35. input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);  //上报按下按键事件
  36. else
  37. dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->new[i]);
  38. }
  39. }
  40. //数据的第2~7字节用于存放键码,分别可以存放6个,也就是可以支持同时6个按键按下
  41. //如果一直按住键盘的某个按键,则usb接收到的数据会都是一样的也就是kbd->old==kbd->new,则按下的时候会上报按下事件,一直按着的时候不会继续上报按下或释放按键
  42. //若有新的按键按下,则所有的kdb->old的值可以在kdb->new中找到,而kdb->new中代表新按键键码的值在kdb->old中会找不到,所以触发第二个if条件成立,上报按下按键事件
  43. //若之前的按键松开,则所有的kdb->new的值可以在kdb->old中找到,而kdb->old中代表旧按键键码的值在kdb->new中会找不到,所以触发第一个if条件成立,上报释放按键事件
  44. input_sync(kbd->dev);    //同步事件
  45. memcpy(kbd->old, kbd->new, 8);    //新的键值存放在旧的键值
  46. resubmit:
  47. i = usb_submit_urb (urb, GFP_ATOMIC);   //提交urb
  48. if (i)
  49. err_hid ("can‘t resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);
  50. }


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

[cpp] view plain copy

  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. if (type != EV_LED)
  5. return -1;
  6. kbd->newleds = (!!test_bit(LED_KANA,dev->led) << 3)|(!!test_bit(LED_COMPOSE, dev->led) << 3)|
  7. (!!test_bit(LED_SCROLLL,dev->led) << 2)|(!!test_bit(LED_CAPSL,dev->led) << 1)|(!!test_bit(LED_NUML,dev->led));
  8. //判断是否有 小键盘,大小写,滚动锁定,组合键,KANA事件
  9. if (kbd->led->status == -EINPROGRESS)
  10. return 0;
  11. if (*(kbd->leds) == kbd->newleds) //比较新旧指示灯状态,跟目前状态一致,则返回
  12. return 0;
  13. *(kbd->leds) = kbd->newleds;  //填充usb键盘led数据传输缓冲区
  14. kbd->led->dev = kbd->usbdev;   //捆绑usb设备
  15. if (usb_submit_urb(kbd->led, GFP_ATOMIC))    //跟目前状态不一致,则提交usb键盘led urb 会通过控制输出端口发送setup包设置led灯状态
  16. err_hid("usb_submit_urb(leds) failed");
  17. return 0;
  18. }

usb键盘led灯urb的回调函数

[cpp] view plain copy

  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任务

[cpp] view plain copy

  1. DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);  //创建keyboard_tasklet执行kbd_bh

kbd_bh函数获取通过getleds函数获取led状态标志,然后最终会调用kbd_update_leds_helper函数

[cpp] view plain copy

  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这个值,处理并返回.

[cpp] view plain copy

  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. if (kbd->ledmode == LED_SHOW_IOCTL)
  7. return ledioctl;
  8. leds = kbd->ledflagstate;    //获取led标志状态
  9. if (kbd->ledmode == LED_SHOW_MEM) {
  10. for (i = 0; i < 3; i++)
  11. if (ledptrs[i].valid) {
  12. if (*ledptrs[i].addr & ledptrs[i].mask)
  13. leds |= (1 << i);
  14. else
  15. leds &= ~(1 << i);
  16. }
  17. }
  18. return leds;
  19. }

ldeflagstate的值可以由以下三个函数来设置

[cpp] view plain copy

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

而这三个函数的调用情况如下,键盘按键处理事件

[cpp] view plain copy

  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. fn_bare_num     >>> chg_vc_kbd_led(kbd, VC_NUMLOCK);   //小键盘led
  5. con_stop        >>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led
  6. con_start       >>> clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led

获取led状态标志后,调用kbd_update_leds_helper函数,上报led事件

[cpp] view plain copy

  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函数

[cpp] view plain copy

  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. if (is_event_supported(type, dev->evbit, EV_MAX)) {
  7. spin_lock_irqsave(&dev->event_lock, flags);
  8. rcu_read_lock();
  9. grab = rcu_dereference(dev->grab);
  10. if (!grab || grab == handle)
  11. input_handle_event(dev, handle->handler,type, code, value);  //调用input_handle_event函数
  12. rcu_read_unlock();
  13. spin_unlock_irqrestore(&dev->event_lock, flags);
  14. }
  15. }
  16. EXPORT_SYMBOL(input_inject_event);

input_handle_event函数处理各种事件分支,最终就会调用到input设备的event方法(usb_kbd_event)

[cpp] view plain copy

    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. switch (type) {
    5. case EV_SYN:    //同步事件
    6. switch (code) {
    7. case SYN_CONFIG:
    8. disposition = INPUT_PASS_TO_ALL;
    9. break;
    10. case SYN_REPORT:    //led同步事件分支
    11. if (!dev->sync) {
    12. dev->sync = true;
    13. disposition = INPUT_PASS_TO_HANDLERS;
    14. }
    15. break;
    16. case SYN_MT_REPORT:
    17. dev->sync = false;
    18. disposition = INPUT_PASS_TO_HANDLERS;
    19. break;
    20. }
    21. break;
    22. case EV_KEY:
    23. if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {
    24. if (value != 2) {
    25. __change_bit(code, dev->key);
    26. if (value)
    27. input_start_autorepeat(dev, code);
    28. else
    29. input_stop_autorepeat(dev);
    30. }
    31. disposition = INPUT_PASS_TO_HANDLERS;
    32. }
    33. break;
    34. case EV_SW:
    35. if (is_event_supported(code, dev->swbit, SW_MAX) &&
    36. !!test_bit(code, dev->sw) != value) {
    37. __change_bit(code, dev->sw);
    38. disposition = INPUT_PASS_TO_HANDLERS;
    39. }
    40. break;
    41. case EV_ABS:
    42. if (is_event_supported(code, dev->absbit, ABS_MAX))
    43. disposition = input_handle_abs_event(dev, src_handler,code, &value);
    44. break;
    45. case EV_REL:
    46. if (is_event_supported(code, dev->relbit, REL_MAX) && value)
    47. disposition = INPUT_PASS_TO_HANDLERS;
    48. break;
    49. case EV_MSC:
    50. if (is_event_supported(code, dev->mscbit, MSC_MAX))
    51. disposition = INPUT_PASS_TO_ALL;
    52. break;
    53. case EV_LED:    //led处理
    54. if (is_event_supported(code, dev->ledbit, LED_MAX) &&
    55. !!test_bit(code, dev->led) != value) {
    56. __change_bit(code, dev->led);    //修改input设备的led标志位
    57. disposition = INPUT_PASS_TO_ALL;
    58. }
    59. break;
    60. case EV_SND:
    61. if (is_event_supported(code, dev->sndbit, SND_MAX)) {
    62. if (!!test_bit(code, dev->snd) != !!value)
    63. __change_bit(code, dev->snd);
    64. disposition = INPUT_PASS_TO_ALL;
    65. }
    66. break;
    67. case EV_REP:
    68. if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
    69. dev->rep[code] = value;
    70. disposition = INPUT_PASS_TO_ALL;
    71. }
    72. break;
    73. case EV_FF:
    74. if (value >= 0)
    75. disposition = INPUT_PASS_TO_ALL;
    76. break;
    77. case EV_PWR:
    78. disposition = INPUT_PASS_TO_ALL;
    79. break;
    80. }
    81. if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
    82. dev->sync = false;
    83. if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)  //led事件
    84. dev->event(dev, type, code, value);  //调用input设备的event方法(usb_kbd_event)
    85. if (disposition & INPUT_PASS_TO_HANDLERS)   //led同步事件
    86. input_pass_event(dev, src_handler, type, code, value);  //会调用input_handler的event方法(kbd_event)
    87. }
时间: 2024-10-22 02:47:14

usb键鼠驱动分析【钻】的相关文章

linux下usb转串口驱动分析【转】

转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,(如果把struct 比作类,kmalloc就是类的实例化,结构体里面的函数指针就是方法,还有重构,多态) 2. 在linux里面,设备是对象,驱动也是对象,并且这两个是分开的 现在我们来看驱动的总体架构 并不用太在意这个图,对用户来说usb_serial设备就是普通的串口设备 我们可以看驱动里面几个

Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析

转: http://blog.csdn.net/zqixiao_09/article/details/51146149 前面学习了SDIO接口的WiFi驱动,现在我们来学习一下USB接口的WiFi驱动,二者的区别在于接口不同.而USB接口的设备驱动,我们前面也有学习,比如USB摄像头驱动.USB鼠标驱动,同样都符合LinuxUSB驱动结构: USB设备驱动(字符设备.块设备.网络设备) | USB 核心 | USB主机控制器驱动 不同之处只是在于USB摄像头驱动是字符设备,而我们今天要学习的Wi

无线键鼠监听与劫持

引言:键盘是生活中最常见的硬件之一,无线键鼠由于其方便美观受到很多人的青睐,那么它的安全性如何呢?面对攻击我们又如何防护呢? 本文选自<硬件安全攻防大揭秘>. 1.无线键鼠的兴起 键盘连接到计算机有多种方式,有线键盘鼠标在生活中最常见,适用范围也很广泛,但有线连接不仅对操作距离有限制,而且给携带造成了不便.不仅如此,繁杂的线缆还很容易把桌面弄得凌乱不堪.无线键鼠非常好地解决了上述问题.无线键鼠又分为蓝牙类型和2.4GHz 类型,文中所指的无线鼠标一般指2.4GHz 类型.值得注意的是,虽然蓝牙

Linux USB 鼠标输入驱动详解

平台:mini2440 内核:linux 2.6.32.2 USB设备插入时,内核会读取设备信息,接着就把id_table里的信息与读取到的信息做比较,看是否匹配,如果匹配,就调用probe函数.USB设备拔出时会调用disconnect函数.URB在USB设备驱动程序中用来描述与USB设备通信时用到的基本载体和核心数据结构. URB(usb request block)处理流程: ①USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb并提交给USB core. ②USB cor

LInux LCD驱动分析

一.让LCD显示可爱的小企鹅还是先说说环境吧,处理器为S3C2410,linux的版本当然是2.6.20的.下面先说说怎样让LCD上显示出可爱的小企鹅.最直接的步骤如下(记住不要问为什么哈-_-,一步一步跟着走就行了):1.       添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:static struct s

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC

多台主机共享键鼠

synergy 是一个用于在多台主机间(Linux, Windows)共享键鼠的工具, 下载 从以下网站下载synergy http://synergy-project.org/ 这里用Linux和Windows举例: 64位Linux下下载: http://synergy-project.org/download/?file=synergy-1.5.0-r2278-Linux-x86_64.rpm 64位Windows下下载: http://synergy-project.org/downlo

第十八篇:融汇贯通--谈USB Video Class驱动

USB Video Class驱动是WINDOWS系统包含的一个针对于USB VIDEO 类的驱动程序. 好多工程师都做过USB VIDEO设备端的开发, 主要的工作内容为: 使用FIRMWARE,或者LIINUX GADGET驱动程序, 构建USB VIDEO设备的描述符, 通过这些描述符的TOPOLOGY关系, 让系统了解,设备所支持的某些控制, 支持的视频格式, 系统驱动通过对这些描述符的解释, 构建自己的KS, FILTER, NODE, PIN网络, 属性, 方法, 最终, 又由这些F