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)} 

 

 

 

 

 

 

 

 

 

 

 

 



 

 

 

 

 

原创粉丝点击