usb键鼠驱动分析
来源:互联网 发布:赛博朋克 知乎 编辑:程序博客网 时间:2024/05/15 12:55
一、鼠标
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_devicestruct 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_interfaceif (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);//分配初始化urbif (!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));//复制厂商名到nameif (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));//追加/input0input_dev->name = mouse->name;//输入设备的名字设置成usb鼠标的名字input_dev->phys = mouse->phys;//输入设备的路径设置成usb鼠标的路径usb_to_input_id(dev, &input_dev->id);//设置输入设备的bustype,vendor,product,versioninput_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_irqmouse->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);//释放urbusb_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))//提交urbreturn -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 *///传输成功跳出switchbreak;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);//继续提交urbif (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 urbunsigned 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_devicestruct 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));//追加/input0input_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事件:小键盘,大小写,滚动锁定,组合键,KANAfor (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_irqkbd->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_ledkbd->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_kbdreturn 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)))//分配按键urbreturn -1;if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))//分配led灯urbreturn -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);//释放键盘按键urbusb_free_urb(kbd->led);//释放键盘led urbusb_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键盘按键urbreturn -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 *///传输成功跳出switchbreak;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-guifor (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);//上报释放按键事件elsedev_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);//上报按下按键事件elsedev_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);//提交urbif (i)err_hid ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);}
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
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_helperledstate = 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);elseleds &= ~(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);//大小写ledk_shift>>> clr_vc_kbd_led(kbd, VC_CAPSLOCK);//大小写ledfn_caps_toggle >>> chg_vc_kbd_led(kbd, VC_CAPSLOCK);//大小写ledfn_bare_num>>> chg_vc_kbd_led(kbd, VC_NUMLOCK);//小键盘ledcon_stop>>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);//滚轮锁定ledcon_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);elseinput_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)}
- usb键鼠驱动分析
- usb键鼠驱动分析
- usb键鼠驱动分析
- usb键鼠驱动分析
- USB OTG驱动分析
- usb驱动分析
- usb鼠标驱动分析
- Linux USB驱动分析
- linux usb驱动分析
- usb gadget驱动分析
- USB hub驱动分析
- usb storage驱动分析
- OK6410 usb驱动分析
- USB摄像头驱动分析
- USB驱动结构分析
- Linux usb 驱动分析
- USB设备驱动分析
- Linux USB驱动分析
- FNDLOAD使用
- timestamp with time zone 和 timestamp with local time zone
- UML建模的要点总结
- Linux进程函数详解
- Android应用--简、美音乐播放器实现左右切屏功能
- usb键鼠驱动分析
- 高晓松脱口秀--晓说(第一季&第二季)mp3下载
- ASIHTTPRequest类库简介和使用说明
- Spring Security 2.0.x中文参考文档
- SharePoint 2010/2013 禁止复制打印 webpart
- 矩阵累积和的最大路径
- Handler 引起的内存泄露
- XML简单的增改删操作
- InputStream 和OutputStream