一、鼠标
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 |
| Ref:typical |
|
|
|
| ||
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)
}