usb键鼠驱动分析【钻】
本文转载自:http://blog.csdn.net/orz415678659/article/details/9197859
一、鼠标
Linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现
1.加载初始化过程
1.1模块入口
- module_init(usb_mouse_init);
1.2初始化函数
- static int __init usb_mouse_init(void) //初始化
- {
- int retval = usb_register(&usb_mouse_driver); //注册usb鼠标驱动
- if (retval == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
- return retval;
- }
1.3初始化函数注册了一个usb驱动usb_mouse_driver
- static struct usb_driver usb_mouse_driver = { //usb鼠标驱动
- .name = "usbmouse", //驱动名
- .probe = usb_mouse_probe, //匹配方法
- .disconnect = usb_mouse_disconnect, //拔出方法
- .id_table = usb_mouse_id_table, //支持设备id表
- };
1.4当插入鼠标时会根据usb_mouse_id_table去匹配创建usb设备
- static struct usb_device_id usb_mouse_id_table [] = {
- { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },
- { } /* Terminating entry */
- };
它的匹配方式是接口id匹配.接口类USB_INTERFACE_CLASS_HID
usb插入枚举时候会获取usb鼠标的接口类型,获取其接口类信息,匹配成功的话会动态创建一个usb_device.
在分析probe和disconnect方法之前先介绍下驱动用来描述usb鼠标对象的结构体usb_mouse
- struct usb_mouse {
- char name[128];//usb鼠标设备名
- char phys[64];//路径
- struct usb_device *usbdev;//usb设备
- struct input_dev *dev;//输入设备
- struct urb *irq;//urb结构体
- signed char *data; //数据传输缓冲区指针
- dma_addr_t data_dma;
- };
usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性
1.5 匹配成功了就会调用probe方法
- static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
- {
- struct usb_device *dev = interface_to_usbdev(intf); //根据usb接口获取动态创建的usb_device
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_mouse *mouse;
- struct input_dev *input_dev;
- int pipe, maxp;
- int error = -ENOMEM;
- interface = intf->cur_altsetting; //获取usb_host_interface
- if (interface->desc.bNumEndpoints != 1) //鼠标的端点有且仅有1个控制端点
- return -ENODEV;
- endpoint = &interface->endpoint[0].desc; //获取端点描述符
- if (!usb_endpoint_is_int_in(endpoint)) //判断该端点是否中断端点
- return -ENODEV;
- //上面判断了usb鼠标的属性,有且仅有1个控制端点(0号端点不算进来的)
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //设置端点为中断输入端点
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
- mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); //分配usb_mouse对象
- input_dev = input_allocate_device(); //初始化输入设备
- if (!mouse || !input_dev)
- goto fail1;
- mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//分配初始化usb鼠标数据缓冲区内存(默认8位数据)
- if (!mouse->data)
- goto fail1;
- mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配初始化urb
- if (!mouse->irq)
- goto fail2;
- mouse->usbdev = dev; //设置usb鼠标设备的usb设备对象
- mouse->dev = input_dev; //设备usb鼠标设备的input设备对象
- if (dev->manufacturer) //枚举时候有获取到有效的厂商名
- strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); //复制厂商名到name
- if (dev->product) { //枚举时候有获取到有效的产品名
- if (dev->manufacturer) //如果也有厂商名
- strlcat(mouse->name, " ", sizeof(mouse->name)); //则用空格将厂商名和产品名隔开
- strlcat(mouse->name, dev->product, sizeof(mouse->name)); //追加产品名到name
- }
- if (!strlen(mouse->name)) //如果厂商和产品名都没有
- snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",
- le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
- //则直接根据厂商id和产品id给name赋值
- usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //设置设备路径名
- strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0
- input_dev->name = mouse->name; //输入设备的名字设置成usb鼠标的名字
- input_dev->phys = mouse->phys; //输入设备的路径设置成usb鼠标的路径
- usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
- input_dev->dev.parent = &intf->dev; //usb接口设备为输入设备的父设备
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //输入事件类型按键+相对位移
- input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
- //按键类型 鼠标:左键,右键,中键
- input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); //相对位移x方向+y方向
- input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
- //按键类型 鼠标:旁键,外部键
- input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //相对位移 鼠标滚轮事件
- input_set_drvdata(input_dev, mouse); //usb鼠标驱动文件作为输入设备的设备文件的驱动数据
- input_dev->open = usb_mouse_open; //设置输入事件的打开方法
- input_dev->close = usb_mouse_close; //设置输入事件的关闭方法
- usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);
- //填充中断类型urb 指定了urb的回调函数是usb_mouse_irq
- mouse->irq->transfer_dma = mouse->data_dma;//dma数据缓冲区指向usb鼠标设备的data_dma成员
- mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//没DMA映射
- error = input_register_device(mouse->dev);
- if (error)
- goto fail3;
- usb_set_intfdata(intf, mouse); ////usb鼠标驱动文件作为usb接口设备的设备文件的驱动数据
- return 0;
- fail3:
- usb_free_urb(mouse->irq);
- fail2:
- usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
- fail1:
- input_free_device(input_dev);
- kfree(mouse);
- return error;
- }
1.6 拔掉usb鼠标就会调用disconnect方法
- static void usb_mouse_disconnect(struct usb_interface *intf)
- {
- struct usb_mouse *mouse = usb_get_intfdata (intf); //根据usb接口设备的设备文件的驱动数据,获取usb鼠标设备
- usb_set_intfdata(intf, NULL); //清空usb接口设备的设备文件的驱动数据
- if (mouse) {
- usb_kill_urb(mouse->irq); //断掉urb传输
- input_unregister_device(mouse->dev); //注销输入设备
- usb_free_urb(mouse->irq); //释放urb
- usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); //清除传输数据缓冲区
- kfree(mouse); //释放usb鼠标设备
- }
- }
基本上disconnect只是probe的一个逆操作而已
经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的鼠标设备节点,应用程序可以打开该节点来控制usb鼠标设备
此时会调用usb_mouse_open方法
1.7打开鼠标
- static int usb_mouse_open(struct input_dev *dev)
- {
- struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备
- mouse->irq->dev = mouse->usbdev; //设置urb设备对应的usb设备
- if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //提交urb
- return -EIO;
- return 0;
- }
通过urb提交之后,鼠标动作通过usb传输数据就会交由urb去处理了
1.8.urb数据传输
当操作鼠标的时候,会引起urb数据传输在数据传输之后会调用usb_mouse_irq
- static void usb_mouse_irq(struct urb *urb)
- {
- struct usb_mouse *mouse = urb->context; //获取usb鼠标设备
- signed char *data = mouse->data; //数据传输缓冲区指针
- struct input_dev *dev = mouse->dev; //输入设备
- int status;
- switch (urb->status) { //判断urb传输的状态
- case 0: /* success */ //传输成功跳出switch
- break;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- /* -EPIPE: should clear the halt */
- default: /* error */
- goto resubmit;
- }
- input_report_key(dev, BTN_LEFT, data[0] & 0x01); //右键
- input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //左键
- input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //中键
- input_report_key(dev, BTN_SIDE, data[0] & 0x08); //边键
- input_report_key(dev, BTN_EXTRA, data[0] & 0x10); //外部键
- input_report_rel(dev, REL_X, data[1]); //相对x坐标位移
- input_report_rel(dev, REL_Y, data[2]); //相对y坐标位移
- input_report_rel(dev, REL_WHEEL, data[3]); //相对滚轮位移
- input_sync(dev); //同步事件
- resubmit:
- status = usb_submit_urb (urb, GFP_ATOMIC); //继续提交urb
- if (status)
- err ("can't resubmit intr, %s-%s/input0, status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);
- }
usb接口传来的数据会保存在usb鼠标data指针成员指向的缓冲区中
这里可以看出usb鼠标传输的每次数据基本是4个字节
第0个字节的第1位表示右键,第2位表示左键,第3位表示中键,第4位表示边键,第5为表示外部键
而第1个字节表示相对x坐标的位移,第2个字节表示相对y坐标的位移,第3个字节表示相对滚轮的位移
当输入设备上报完usb接口接收来的数据后,需要调用input_sync同步事件消息,并调用usb_submit_urb提交urb
使其继续监视处理usb鼠标设备传递的新数据.
应用程序要获取鼠标操作信息可以打开对应的输入设备节点,并通过输入设备的读接口,获取到usb鼠标通过usb接口传递并交由输入设备上报过来的数据
漏掉的函数
1.应用程序关闭鼠标设备
- static void usb_mouse_close(struct input_dev *dev)
- {
- struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备
- usb_kill_urb(mouse->irq); //当关闭鼠标设备时候,需要断掉urb传输
- }
2.模块移除调用的函数
- module_exit(usb_mouse_exit);
- static void __exit usb_mouse_exit(void)
- {
- usb_deregister(&usb_mouse_driver); //注销掉usb鼠标设备
- }
二、键盘
linux下的usb键盘驱动在/drivers/hid/usbhid/usbkbd.c中实现
1.加载初始化过程
1.1 模块入口
- module_init(usb_kbd_init);
1.2 初始化函数
- static int __init usb_kbd_init(void)
- {
- int result = usb_register(&usb_kbd_driver); //注册usb键盘
- if (result == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
- return result;
- }
1.3 初始化函数注册了一个usb驱动usb_kbd_driver
- static struct usb_driver usb_kbd_driver = { //usb键盘驱动
- .name = "usbkbd", //驱动名
- .probe = usb_kbd_probe, //匹配方法
- .disconnect = usb_kbd_disconnect, //拔出方法
- .id_table = usb_kbd_id_table, //支持设备id
- };
1.4 当插入鼠标时会根据usb_kbd_id_table去匹配创建usb设备
- static struct usb_device_id usb_kbd_id_table [] = {
- { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD) },
- { } /* Terminating entry */
- };
它的匹配方式是接口id匹配.接口类USB_INTERFACE_CLASS_HID
usb插入枚举时候会获取usb键盘的接口类型,获取其接口类信息,匹配成功的话会动态创建一个usb_device.
在分析probe和disconnect方法之前先介绍下驱动用来描述usb键盘对象的结构体usb_kbd
- struct usb_kbd {
- struct input_dev *dev; //输入设备
- struct usb_device *usbdev; //usb设备
- unsigned char old[8]; //旧的键盘按键数据
- struct urb *irq, *led; //键盘urb,led urb
- unsigned char newleds; //新的led数据
- char name[128]; //usb键盘设备名字
- char phys[64]; //usb键盘设备路径
- unsigned char *new; //usb键盘按键 数据传输缓冲区指针
- struct usb_ctrlrequest *cr; //setup数据包控制请求描述符
- unsigned char *leds; //usb键盘led 数据传输缓冲区指针
- dma_addr_t new_dma; //usb键盘按键DMA映射总线地址
- dma_addr_t leds_dma; //usb键盘led DMA映射总线地址
- };
usb键盘既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性
1.5 匹配成功了就会调用probe方法
- static int usb_kbd_probe(struct usb_interface *iface,const struct usb_device_id *id)
- {
- struct usb_device *dev = interface_to_usbdev(iface); //根据usb接口获取动态创建的usb_device
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_kbd *kbd;
- struct input_dev *input_dev;
- int i, pipe, maxp;
- int error = -ENOMEM;
- interface = iface->cur_altsetting; //获取usb_host_interface
- if (interface->desc.bNumEndpoints != 1) //键盘的端点有且仅有1个控制端点
- return -ENODEV;
- endpoint = &interface->endpoint[0].desc; //获取端点描述符
- if (!usb_endpoint_is_int_in(endpoint)) //判断该端点是否中断端点
- return -ENODEV;
- //上面判断了usb键盘的属性,有且仅有1个控制端点(0号端点不算进来的)
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //设置端点为中断输入端点
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
- kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); //分配usb_kbd对象
- input_dev = input_allocate_device(); //初始化输入设备
- if (!kbd || !input_dev)
- goto fail1;
- if (usb_kbd_alloc_mem(dev, kbd)) //分配usb键盘需要的内存
- goto fail2;
- kbd->usbdev = dev; //设置usb键盘设备的usb设备对象
- kbd->dev = input_dev; //设备usb键盘设备的input设备对象
- if (dev->manufacturer) //枚举时候有获取到有效的厂商名
- strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); //复制厂商名到name
- if (dev->product) { //枚举时候有获取到有效的产品名
- if (dev->manufacturer) //如果也有厂商名
- strlcat(kbd->name, " ", sizeof(kbd->name)); //则用空格将厂商名和产品名隔开
- strlcat(kbd->name, dev->product, sizeof(kbd->name)); //追加产品名到name
- }
- if (!strlen(kbd->name)) //如果厂商和产品名都没有
- snprintf(kbd->name, sizeof(kbd->name),"USB HIDBP Keyboard %04x:%04x",
- le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
- //则直接根据厂商id和产品id给name赋值
- usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); //设置设备路径名
- strlcat(kbd->phys, "/input0", sizeof(kbd->phys)); //追加/input0
- input_dev->name = kbd->name; //输入设备的名字设置成usb键盘的名字
- input_dev->phys = kbd->phys; //输入设备的路径设置成usb键盘的路径
- usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
- input_dev->dev.parent = &iface->dev; //usb接口设备为输入设备的父设备
- input_set_drvdata(input_dev, kbd); //usb键盘驱动文件作为输入设备的设备文件的驱动数据
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP); //输入事件类型 按键+led+重复
- input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);
- //键盘led事件:小键盘,大小写,滚动锁定,组合键,KANA
- for (i = 0; i < 255; i++)
- set_bit(usb_kbd_keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit); //清除无效的0位
- //键盘按键事件:遍历全局usb_kbd_keycode数组设置
- input_dev->event = usb_kbd_event; //设置输入事件的event方法
- input_dev->open = usb_kbd_open; //设置输入事件的open方法
- input_dev->close = usb_kbd_close; //设置输入事件的close方法
- usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),usb_kbd_irq, kbd, endpoint->bInterval);
- //填充中断类型urb 指定了urb的回调函数是usb_kbd_irq
- kbd->irq->transfer_dma = kbd->new_dma; //usb键盘按键设备DMA映射总线地址
- kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //没DMA映射
- //设置usb setup传输数据包控制请求结构体
- kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kbd->cr->bRequest = 0x09;//SET_IDLE?
- kbd->cr->wValue = cpu_to_le16(0x200);
- kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
- kbd->cr->wLength = cpu_to_le16(1);
- usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1,usb_kbd_led, kbd);
- //设置为控制输出端点,填充控制类型urb,回调函数usb_kbd_led
- kbd->led->transfer_dma = kbd->leds_dma; //usb键盘led设备DMA映射总线地址
- kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //没DMA映射
- error = input_register_device(kbd->dev); //注册输入设备
- if (error)
- goto fail2;
- usb_set_intfdata(iface, kbd); //usb键盘驱动文件作为usb接口设备的设备文件的驱动数据
- device_set_wakeup_enable(&dev->dev, 1); //使能系统唤醒
- return 0;
- fail2:
- usb_kbd_free_mem(dev, kbd); //分配失败则释放相关内存
- fail1:
- input_free_device(input_dev); //释放输入设备
- kfree(kbd); //释放usb_kbd
- return error;
- }
probe方法中调用的内存分配释放函数
分配内存
- static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
- {
- if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) //分配按键urb
- return -1;
- if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) //分配led灯urb
- return -1;
- if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma))) //分配初始化usb键盘数据缓冲区内存(默认8位数据)
- return -1;
- if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) //分配setup包的控制请求描述符
- return -1;
- if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) //分配初始化usb键盘led数据缓冲区内存
- return -1;
- return 0;
- }
释放内存
- static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
- {
- usb_free_urb(kbd->irq); //释放键盘按键urb
- usb_free_urb(kbd->led); //释放键盘led urb
- usb_free_coherent(dev, 8, kbd->new, kbd->new_dma); //释放usb键盘数据缓冲区
- kfree(kbd->cr); //释放setup包的控制请求描述符
- usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma); //释放urb键盘led数据缓冲区内存
- }
配置用到的全局键值数组
- static const unsigned char usb_kbd_keycode[256] = { //键值
- 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
- 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
- 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
- 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
- 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
- 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
- 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
- 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
- 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
- 150,158,159,128,136,177,178,176,142,152,173,140
- };
1.6 拔掉usb鼠标就会调用disconnect方法
- static void usb_kbd_disconnect(struct usb_interface *intf)
- {
- struct usb_kbd *kbd = usb_get_intfdata (intf); //根据usb接口设备的设备文件的驱动数据,获取usb键盘设备
- usb_set_intfdata(intf, NULL); //清空usb接口设备的设备文件的驱动数据
- if (kbd) {
- usb_kill_urb(kbd->irq); //断掉urb传输
- input_unregister_device(kbd->dev); //注销输入设备
- usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //释放usb键盘需要的内存
- kfree(kbd); //释放usb键盘设备
- }
- }
基本上disconnect只是probe的一个逆操作而已
经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的键盘设备节点,应用程序可以打开该节点来控制usb键盘设备
此时会调用usb_kbd_open方法
1.7打开键盘
- static int usb_kbd_open(struct input_dev *dev)
- {
- struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备
- kbd->irq->dev = kbd->usbdev; //usb键盘按键urb捆绑usb设备
- if (usb_submit_urb(kbd->irq, GFP_KERNEL)) //提交usb键盘按键urb
- return -EIO;
- return 0;
- }
关闭键盘调用usb_kbd_close
- static void usb_kbd_close(struct input_dev *dev)
- {
- struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备
- usb_kill_urb(kbd->irq); //断开usb键盘按键urb
- }
通过urb提交之后,键盘动作通过usb传输数据就会交由urb去处理了
1.8.urb数据传输
- static void usb_kbd_irq(struct urb *urb)
- {
- struct usb_kbd *kbd = urb->context; //获取usb键盘设备
- int i;
- switch (urb->status) { //判断urb传输的状态
- case 0: /* success */ //传输成功跳出switch
- break;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- /* -EPIPE: should clear the halt */
- default: /* error */
- goto resubmit;
- }
- //L-ctrl,L-shift,L-alt,L-gui,R-ctrl,R-shift,R-alt,R-gui
- for (i = 0; i < 8; i++) //(224~231)判断新按下的是否组合键
- input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); //组合键
- //memscan(kbd->new + 2, kbd->old[i], 6)表示从kbd->new[2]扫描6个单位到kbd->new[7],
- //查找kbd->old[i]一样的字符,返回扫描到的单位地址"==kbd->new+8"表示没找到
- //键盘扫描码0-No Event 1-Overrun Error 2-POST Fail 3-ErrorUndefined所以kbd->old[i] > 3
- //键值usb_kbd_keycode[i]和扫描码new[i]/old[i]要区分好别乱了
- //键盘扫描码和数据格式见函数下面图片
- for (i = 2; i < 8; i++) {
- //新数据中没有查找到旧数据一样的码值--表示新的按键按下,旧的按键释放
- if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
- if (usb_kbd_keycode[kbd->old[i]]) //松开的按键是正常的按键
- input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上报释放按键事件
- else
- dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->old[i]);
- }
- //旧数据中没有查找到新数据一样的码值--表示新的按键按下,就的按键按下
- if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
- if (usb_kbd_keycode[kbd->new[i]]) //按下的按键是正常的按键
- input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); //上报按下按键事件
- else
- dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->new[i]);
- }
- }
- //数据的第2~7字节用于存放键码,分别可以存放6个,也就是可以支持同时6个按键按下
- //如果一直按住键盘的某个按键,则usb接收到的数据会都是一样的也就是kbd->old==kbd->new,则按下的时候会上报按下事件,一直按着的时候不会继续上报按下或释放按键
- //若有新的按键按下,则所有的kdb->old的值可以在kdb->new中找到,而kdb->new中代表新按键键码的值在kdb->old中会找不到,所以触发第二个if条件成立,上报按下按键事件
- //若之前的按键松开,则所有的kdb->new的值可以在kdb->old中找到,而kdb->old中代表旧按键键码的值在kdb->new中会找不到,所以触发第一个if条件成立,上报释放按键事件
- input_sync(kbd->dev); //同步事件
- memcpy(kbd->old, kbd->new, 8); //新的键值存放在旧的键值
- resubmit:
- i = usb_submit_urb (urb, GFP_ATOMIC); //提交urb
- if (i)
- err_hid ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);
- }
Usage |
Usage |
Usage |
Ref:typical |
PC-AT |
Mac- |
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 |
15 |
Ö |
Ö |
Ö |
84/101/104 |
||
43 |
2B |
Keyboard Tab |
16 |
Ö |
Ö |
Ö |
84/101/104 |
||
44 |
2C |
Keyboard Spacebar |
61 |
Ö |
Ö |
Ö |
84/101/104 |
||
45 |
2D |
Keyboard - and (underscore) 4 |
12 |
Ö |
Ö |
Ö |
84/101/104 |
||
46 |
2E |
Keyboard = and+4 |
13 |
Ö |
Ö |
Ö |
84/101/104 |
||
47 |
2F |
Keyboard [ and {4 |
27 |
Ö |
Ö |
Ö |
84/101/104 |
||
48 |
30 |
Keyboard ] and }4 |
28 |
Ö |
Ö |
Ö |
84/101/104 |
||
49 |
31 |
Keyboard \ and | |
29 |
Ö |
Ö |
Ö |
84/101/104 |
||
50 |
32 |
Keyboard Non-US# and ~2 |
42 |
Ö |
Ö |
Ö |
84/101/104 |
||
51 |
33 |
Keyboard 4 |
40 |
Ö |
Ö |
Ö |
84/101/104 |
||
52 |
34 |
Keyboard ‘ and “4 |
41 |
Ö |
Ö |
Ö |
84/101/104 |
||
53 |
35 |
Keyboard Grave Accent and Tilde4 |
1 |
Ö |
Ö |
Ö |
84/101/104 |
||
54 |
36 |
Keyboard , and <4 |
53 |
Ö |
Ö |
Ö |
84/101/104 |
||
55 |
37 |
Keyboard . and >4 |
54 |
Ö |
Ö |
Ö |
84/101/104 |
||
56 |
38 |
Keyboard / and ? 4 |
55 |
Ö |
Ö |
Ö |
84/101/104 |
||
57 |
39 |
Keyboard CapsLock11 |
30 |
Ö |
Ö |
Ö |
84/101/104 |
||
58 |
3A |
Keyboard F1 |
112 |
Ö |
Ö |
Ö |
84/101/104 |
||
59 |
3B |
Keyboard F2 |
113 |
Ö |
Ö |
Ö |
84/101/104 |
||
60 |
3C |
Keyboard F3 |
114 |
Ö |
Ö |
Ö |
84/101/104 |
||
61 |
3D |
Keyboard F4 |
115 |
Ö |
Ö |
Ö |
84/101/104 |
||
62 |
3E |
Keyboard F5 |
116 |
Ö |
Ö |
Ö |
84/101/104 |
||
63 |
3F |
Keyboard F6 |
117 |
Ö |
Ö |
Ö |
84/101/104 |
||
64 |
40 |
Keyboard F7 |
118 |
Ö |
Ö |
Ö |
84/101/104 |
||
65 |
41 |
Keyboard F8 |
119 |
Ö |
Ö |
Ö |
84/101/104 |
||
66 |
42 |
Keyboard F9 |
120 |
Ö |
Ö |
Ö |
84/101/104 |
||
67 |
43 |
Keyboard F10 |
121 |
Ö |
Ö |
Ö |
84/101/104 |
||
68 |
44 |
Keyboard F11 |
122 |
Ö |
Ö |
Ö |
101/104 |
||
69 |
45 |
Keyboard F12 |
123 |
Ö |
Ö |
Ö |
101/104 |
||
70 |
46 |
Keyboard PrintScreen1 |
124 |
Ö |
Ö |
Ö |
101/104 |
||
71 |
47 |
Keyboard ScrollLock11 |
125 |
Ö |
Ö |
Ö |
84/101/104 |
||
72 |
48 |
Keyboard Pause1 |
126 |
Ö |
Ö |
Ö |
101/104 |
||
73 |
49 |
Keyboard Insert1 |
75 |
Ö |
Ö |
Ö |
101/104 |
||
74 |
4A |
Keyboard Home1 |
80 |
Ö |
Ö |
Ö |
101/104 |
||
75 |
4B |
Keyboard PageUp1 |
85 |
Ö |
Ö |
Ö |
101/104 |
||
76 |
4C |
Keyboard Delete Forward1 |
76 |
Ö |
Ö |
Ö |
101/104 |
||
77 |
4D |
Keyboard End1 |
81 |
Ö |
Ö |
Ö |
101/104 |
||
78 |
4E |
Keyboard PageDown1 |
86 |
Ö |
Ö |
Ö |
101/104 |
||
79 |
4F |
Keyboard RightArrow1 |
89 |
Ö |
Ö |
Ö |
101/104 |
||
80 |
50 |
Keyboard LeftArrow1 |
79 |
Ö |
Ö |
Ö |
101/104 |
||
81 |
51 |
Keyboard DownArrow1 |
84 |
Ö |
Ö |
Ö |
101/104 |
||
82 |
52 |
Keyboard UpArrow1 |
83 |
Ö |
Ö |
Ö |
101/104 |
||
83 |
53 |
Keypad NumLock and Clear11 |
90 |
Ö |
Ö |
Ö |
101/104 |
||
84 |
54 |
Keypad /1 |
95 |
Ö |
Ö |
Ö |
101/104 |
||
85 |
55 |
Keypad * |
100 |
Ö |
Ö |
Ö |
84/101/104 |
||
86 |
56 |
Keypad - |
105 |
Ö |
Ö |
Ö |
84/101/104 |
||
87 |
57 |
Keypad + |
106 |
Ö |
Ö |
Ö |
84/101/104 |
||
88 |
58 |
Keypad ENTER5 |
108 |
Ö |
Ö |
Ö |
101/104 |
||
89 |
59 |
Keypad 1 and End |
93 |
Ö |
Ö |
Ö |
84/101/104 |
||
90 |
5A |
Keypad 2 and Down Arrow |
98 |
Ö |
Ö |
Ö |
84/101/104 |
||
91 |
5B |
Keypad 3 and PageDn |
103 |
Ö |
Ö |
Ö |
84/101/104 |
||
92 |
5C |
Keypad 4 and Left Arrow |
92 |
Ö |
Ö |
Ö |
84/101/104 |
||
93 |
5D |
Keypad 5 |
97 |
Ö |
Ö |
Ö |
84/101/104 |
||
94 |
5E |
Keypad 6 and Righ tArrow |
102 |
Ö |
Ö |
Ö |
84/101/104 |
||
95 |
5F |
Keypad 7 and Home |
91 |
Ö |
Ö |
Ö |
84/101/104 |
||
96 |
60 |
Keypad 8 and Up Arrow |
96 |
Ö |
Ö |
Ö |
84/101/104 |
||
97 |
61 |
Keypad 9 and PageUp |
101 |
Ö |
Ö |
Ö |
84/101/104 |
||
98 |
62 |
Keypad 0 and Insert |
99 |
Ö |
Ö |
Ö |
84/101/104 |
||
99 |
63 |
Keypad . and Delete |
104 |
Ö |
Ö |
Ö |
84/101/104 |
||
100 |
64 |
Keyboard Non-US\ and |3;6 |
45 |
Ö |
Ö |
Ö |
84/101/104 |
||
101 |
65 |
Keyboard Application10 |
129 |
Ö |
Ö |
104 |
|||
102 |
66 |
Keyboard Power9 |
Ö |
Ö |
|||||
103 |
67 |
Keypad = |
Ö |
||||||
104 |
68 |
Keyboard F13 |
Ö |
||||||
105 |
69 |
Keyboard F14 |
Ö |
||||||
106 |
6A |
Keyboard F15 |
Ö |
||||||
107 |
6B |
Keyboard F16 |
|||||||
108 |
6C |
Keyboard F17 |
|||||||
109 |
6D |
Keyboard F18 |
|||||||
110 |
6E |
Keyboard F19 |
|||||||
111 |
6F |
Keyboard F20 |
|||||||
112 |
70 |
Keyboard F21 |
|||||||
113 |
71 |
Keyboard F22 |
|||||||
114 |
72 |
Keyboard F23 |
|||||||
115 |
73 |
Keyboard F24 |
|||||||
116 |
74 |
Keyboard Execute |
Ö |
||||||
117 |
75 |
Keyboard Help |
Ö |
||||||
118 |
76 |
Keyboard Menu |
Ö |
||||||
119 |
77 |
Keyboard Select |
Ö |
||||||
120 |
78 |
Keyboard Stop |
Ö |
||||||
121 |
79 |
Keyboard Again |
Ö |
||||||
122 |
7A |
Keyboard Undo |
Ö |
||||||
123 |
7B |
Keyboard Cut |
Ö |
||||||
124 |
7C |
Keyboard Copy |
Ö |
||||||
125 |
7D |
Keyboard Paste |
Ö |
||||||
126 |
7E |
Keyboard Find |
Ö |
||||||
127 |
7F |
Keyboard Mute |
Ö |
||||||
128 |
80 |
Keyboard Volume Up |
Ö |
||||||
129 |
81 |
Keyboard Volume Down |
Ö |
||||||
130 |
82 |
Keyboard Locking Caps Lock12 |
Ö |
||||||
131 |
83 |
Keyboard Locking Num Lock12 |
Ö |
||||||
132 |
84 |
Keyboard Locking Scroll |
Ö |
||||||
Lock 12 |
|||||||||
133 |
85 |
Keypad Comma |
|||||||
134 |
86 |
Keypad Equal Sign |
|||||||
135 |
87 |
Keyboard Kanji115 |
|||||||
136 |
88 |
Keyboard Kanji216 |
|||||||
137 |
89 |
Keyboard Kanji317 |
|||||||
138 |
8A |
Keyboard Kanji418 |
|||||||
139 |
8B |
Keyboard Kanji519 |
|||||||
140 |
8C |
Keyboard Kanji620 |
|||||||
141 |
8D |
Keyboard Kanji721 |
|||||||
142 |
8E |
Keyboard Kanji822 |
|||||||
143 |
8F |
Keyboard Kanji922 |
|||||||
144 |
90 |
Keyboard LANG18 |
|||||||
145 |
91 |
Keyboard LANG28 |
|||||||
146 |
92 |
Keyboard LANG38 |
|||||||
147 |
93 |
Keyboard LANG48 |
|||||||
148 |
94 |
Keyboard LANG58 |
|||||||
149 |
95 |
Keyboard LANG68 |
|||||||
150 |
96 |
Keyboard LANG78 |
|||||||
151 |
97 |
Keyboard LANG88 |
|||||||
152 |
98 |
Keyboard LANG98 |
|||||||
153 |
99 |
Keyboard AlternateErase7 |
|||||||
154 |
9A |
Keyboard SysReq/Attenti1 |
|||||||
155 |
9B |
Keyboard Cancel |
|||||||
156 |
9C |
Keyboard Clear |
|||||||
157 |
9D |
Keyboard Prior |
|||||||
158 |
9E |
Keyboard Return |
|||||||
159 |
9F |
Keyboard Separator |
|||||||
160 |
A0 |
Keyboard Out |
|||||||
161 |
A1 |
Keyboard Oper |
|||||||
162 |
A2 |
Keyboard Clear/Again |
|||||||
163 |
A3 |
Keyboard CrSel/Props |
|||||||
164 |
A4 |
Keyboard ExSel |
|||||||
165-223 |
A5-DF |
Reserved |
|||||||
224 |
E0 |
Keyboard LeftControl |
58 |
Ö |
Ö |
Ö |
84/101/104 |
||
225 |
E1 |
Keyboard LeftShift |
44 |
Ö |
Ö |
Ö |
84/101/104 |
||
226 |
E2 |
Keyboard LeftAlt |
60 |
Ö |
Ö |
Ö |
84/101/104 |
||
227 |
E3 |
Keyboard Left GUI10;23 |
127 |
Ö |
Ö |
Ö |
104 |
||
228 |
E4 |
Keyboard RightControl |
64 |
Ö |
Ö |
Ö |
101/104 |
||
229 |
E5 |
Keyboard RightShift |
57 |
Ö |
Ö |
Ö |
84/101/104 |
||
230 |
E6 |
Keyboard RightAlt |
62 |
Ö |
Ö |
Ö |
101/104 |
||
231 |
E7 |
Keyboard Right GUI10;24 |
128 |
Ö |
Ö |
Ö |
104 |
||
232-255 |
E8-FF |
Reserved |
1.9 usb键盘的led指示灯
当按下小键盘,大小写,滚动锁定,组合键,KANA控制按键的时候,usb键盘按键urb会处理usb数据并上报数据给输入子系统处理
输入子系统对键值为小键盘,大小写,滚动锁定,组合键,KANA的事件做处理,处理后会调用输入设备的event方法也就是usb_kbd_event
- static int usb_kbd_event(struct input_dev *dev, unsigned int type,unsigned int code, int value)
- {
- struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备
- if (type != EV_LED)
- return -1;
- kbd->newleds = (!!test_bit(LED_KANA,dev->led) << 3)|(!!test_bit(LED_COMPOSE, dev->led) << 3)|
- (!!test_bit(LED_SCROLLL,dev->led) << 2)|(!!test_bit(LED_CAPSL,dev->led) << 1)|(!!test_bit(LED_NUML,dev->led));
- //判断是否有 小键盘,大小写,滚动锁定,组合键,KANA事件
- if (kbd->led->status == -EINPROGRESS)
- return 0;
- if (*(kbd->leds) == kbd->newleds) //比较新旧指示灯状态,跟目前状态一致,则返回
- return 0;
- *(kbd->leds) = kbd->newleds; //填充usb键盘led数据传输缓冲区
- kbd->led->dev = kbd->usbdev; //捆绑usb设备
- if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前状态不一致,则提交usb键盘led urb 会通过控制输出端口发送setup包设置led灯状态
- err_hid("usb_submit_urb(leds) failed");
- return 0;
- }
usb键盘led灯urb的回调函数
- static void usb_kbd_led(struct urb *urb)
- {
- struct usb_kbd *kbd = urb->context; //通过urb获取usb键盘设备
- if (urb->status)
- dev_warn(&urb->dev->dev, "led urb status %d received\n",urb->status);
- if (*(kbd->leds) == kbd->newleds) //比较新旧指示灯状态,跟目前状态一致,则返回
- return;
- *(kbd->leds) = kbd->newleds; //填充usb键盘led数据传输缓冲区
- kbd->led->dev = kbd->usbdev; //捆绑usb设备
- if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前状态不一致,提交usb键盘led urb 会通过控制输出端口发送setup包设置led灯状态
- err_hid("usb_submit_urb(leds) failed");
- }
urb会发送setup包,Set_Report请求包通过控制端点0,紧接着是个2字节的数据输出包,第一个字节对应报告id,第二个字节是led数据信息(上图)
2.0 后话 关于usb_kbd_event函数调用的流程
首先定义了一个键盘任务,任务会循环执行kbd_bh函数
这里定义的时候是禁用了,在后面的执行的kbd_init函数中会使能,和调度keyboard_tasklet任务
- DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); //创建keyboard_tasklet执行kbd_bh
kbd_bh函数获取通过getleds函数获取led状态标志,然后最终会调用kbd_update_leds_helper函数
- static void kbd_bh(unsigned long dummy)
- {
- unsigned char leds = getleds(); //获取led状态标志
- if (leds != ledstate) {
- input_handler_for_each_handle(&kbd_handler, &leds,kbd_update_leds_helper); //会调用kbd_update_leds_helper
- ledstate = leds;
- }
- }
getleds函数获取kbd->ledflagstate这个值,处理并返回.
- static inline unsigned char getleds(void)
- {
- struct kbd_struct *kbd = kbd_table + fg_console;
- unsigned char leds;
- int i;
- if (kbd->ledmode == LED_SHOW_IOCTL)
- return ledioctl;
- leds = kbd->ledflagstate; //获取led标志状态
- if (kbd->ledmode == LED_SHOW_MEM) {
- for (i = 0; i < 3; i++)
- if (ledptrs[i].valid) {
- if (*ledptrs[i].addr & ledptrs[i].mask)
- leds |= (1 << i);
- else
- leds &= ~(1 << i);
- }
- }
- return leds;
- }
ldeflagstate的值可以由以下三个函数来设置
- static inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
- {
- kbd->ledflagstate |= 1 << flag;
- }
- static inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
- {
- kbd->ledflagstate &= ~(1 << flag);
- }
- static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
- {
- kbd->ledflagstate ^= 1 << flag;
- }
而这三个函数的调用情况如下,键盘按键处理事件
- fn_caps_on >>> set_vc_kbd_led(kbd, VC_CAPSLOCK); //大小写led
- k_shift >>> clr_vc_kbd_led(kbd, VC_CAPSLOCK); //大小写led
- fn_caps_toggle >>> chg_vc_kbd_led(kbd, VC_CAPSLOCK); //大小写led
- fn_bare_num >>> chg_vc_kbd_led(kbd, VC_NUMLOCK); //小键盘led
- con_stop >>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led
- con_start >>> clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led
获取led状态标志后,调用kbd_update_leds_helper函数,上报led事件
- static int kbd_update_leds_helper(struct input_handle *handle, void *data)
- {
- unsigned char leds = *(unsigned char *)data;
- if (test_bit(EV_LED, handle->dev->evbit)) {
- input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); //上报滚轮锁定事件
- input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); //上报数字小键盘事件
- input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); //上报大小写事件
- input_inject_event(handle, EV_SYN, SYN_REPORT, 0); //同步事件
- }
- return 0;
- }
调用input_inject_event上报led事件,最终调用input_handle_event函数
- void input_inject_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
- {
- struct input_dev *dev = handle->dev;
- struct input_handle *grab;
- unsigned long flags;
- if (is_event_supported(type, dev->evbit, EV_MAX)) {
- spin_lock_irqsave(&dev->event_lock, flags);
- rcu_read_lock();
- grab = rcu_dereference(dev->grab);
- if (!grab || grab == handle)
- input_handle_event(dev, handle->handler,type, code, value); //调用input_handle_event函数
- rcu_read_unlock();
- spin_unlock_irqrestore(&dev->event_lock, flags);
- }
- }
- EXPORT_SYMBOL(input_inject_event);
input_handle_event函数处理各种事件分支,最终就会调用到input设备的event方法(usb_kbd_event)
- static void input_handle_event(struct input_dev *dev,struct input_handler *src_handler,unsigned int type, unsigned int code, int value)
- {
- int disposition = INPUT_IGNORE_EVENT;
- switch (type) {
- case EV_SYN: //同步事件
- switch (code) {
- case SYN_CONFIG:
- disposition = INPUT_PASS_TO_ALL;
- break;
- case SYN_REPORT: //led同步事件分支
- if (!dev->sync) {
- dev->sync = true;
- disposition = INPUT_PASS_TO_HANDLERS;
- }
- break;
- case SYN_MT_REPORT:
- dev->sync = false;
- disposition = INPUT_PASS_TO_HANDLERS;
- break;
- }
- break;
- case EV_KEY:
- if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {
- if (value != 2) {
- __change_bit(code, dev->key);
- if (value)
- input_start_autorepeat(dev, code);
- else
- input_stop_autorepeat(dev);
- }
- disposition = INPUT_PASS_TO_HANDLERS;
- }
- break;
- case EV_SW:
- if (is_event_supported(code, dev->swbit, SW_MAX) &&
- !!test_bit(code, dev->sw) != value) {
- __change_bit(code, dev->sw);
- disposition = INPUT_PASS_TO_HANDLERS;
- }
- break;
- case EV_ABS:
- if (is_event_supported(code, dev->absbit, ABS_MAX))
- disposition = input_handle_abs_event(dev, src_handler,code, &value);
- break;
- case EV_REL:
- if (is_event_supported(code, dev->relbit, REL_MAX) && value)
- disposition = INPUT_PASS_TO_HANDLERS;
- break;
- case EV_MSC:
- if (is_event_supported(code, dev->mscbit, MSC_MAX))
- disposition = INPUT_PASS_TO_ALL;
- break;
- case EV_LED: //led处理
- if (is_event_supported(code, dev->ledbit, LED_MAX) &&
- !!test_bit(code, dev->led) != value) {
- __change_bit(code, dev->led); //修改input设备的led标志位
- disposition = INPUT_PASS_TO_ALL;
- }
- break;
- case EV_SND:
- if (is_event_supported(code, dev->sndbit, SND_MAX)) {
- if (!!test_bit(code, dev->snd) != !!value)
- __change_bit(code, dev->snd);
- disposition = INPUT_PASS_TO_ALL;
- }
- break;
- case EV_REP:
- if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
- dev->rep[code] = value;
- disposition = INPUT_PASS_TO_ALL;
- }
- break;
- case EV_FF:
- if (value >= 0)
- disposition = INPUT_PASS_TO_ALL;
- break;
- case EV_PWR:
- disposition = INPUT_PASS_TO_ALL;
- break;
- }
- if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- dev->sync = false;
- if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) //led事件
- dev->event(dev, type, code, value); //调用input设备的event方法(usb_kbd_event)
- if (disposition & INPUT_PASS_TO_HANDLERS) //led同步事件
- input_pass_event(dev, src_handler, type, code, value); //会调用input_handler的event方法(kbd_event)
- }
usb键鼠驱动分析【钻】的更多相关文章
- usb键鼠标驱动分析
一.鼠标 linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现 1.加载初始化过程 1.1模块入口 module_init(usb_mouse_init); ...
- Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
- Linux 串口、usb转串口驱动分析(2-1) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...
- linux下usb转串口驱动分析【转】
转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- mini2440触摸屏驱动分析
mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...
- 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏 power_supply 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台: ...
- linux i2c驱动架构-dm368 i2c驱动分析
linux i2c驱动架构-dm368 i2c驱动分析 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinc ...
- linux驱动基础系列--Linux下Spi接口Wifi驱动分析
前言 本文纯粹的纸上谈兵,我并未在实际开发过程中遇到需要编写或调试这类驱动的时候,本文仅仅是根据源码分析后的记录!基于内核版本:2.6.35.6 .主要是想对spi接口的wifi驱动框架有一个整体的把 ...
随机推荐
- 3B课程笔记分享_StudyJams_2017
昨晚才发现 Study Jams China的官方论坛也支持MarkDown,所以直接发在了那上面.http://www.studyjamscn.com/thread-21807-1-1.html
- for 循环练习题
X3 * 6528 = 3X * 8256X为一个数字 填入一个数字 使等式成立 for (var x=1;x<=9&&x>0;x++) { if ((x*10+3)*65 ...
- JQuery特效之心形图片墙
效果如图: 代码如下: <!doctype html> <html lang="en"> <head> <meta charset=&qu ...
- QT设计UI:QT模式对话框打开文件
使用QT模式对话框,并使显示框 为背景色: 方法使用了QCheckBox *native; #include <QCheckBox> 初始化函数代码: //设置默认打开图像位置 nat ...
- python 爬取妹子
爬取妹子图片 网址:https://www.mzitu.com/jiepai/ 2019-06-13 环境WIN10 1903 python 3.7.3 个人习惯先在IDLE中进行调试 import ...
- Python统计字符串中出现次数最多的人名
人名最多数统计题目摘自https://python123.io 描述编程模板中给出了一个字符串,其中包含了含有重复的人名,请直接输出出现最多的人名. ...
- luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP
题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...
- Lua的string库函数、lua中string的模式匹配
--****************Lua的string库函数****************** --1.string.byte --string.byte (s [, i [, j]]) --取出 ...
- 对 p 开 n 次方 (数学推论)
#include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> us ...
- 亚马逊免费服务器搭建Discuz!论坛过程(一)
1:申请 目前亚马逊服务器免费12个月,需要一张信用卡即可免费注册领取. 地址:https://aws.amazon.com/cn/free/ 2: 创建实例 2.1进入控制台:https://ap- ...