• usb键鼠标驱动分析


    一、鼠标

    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 "
    ");
    	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 "
    ");
    	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.
    ", 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.
    ", 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
    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
    ",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)
    }
    
    
    
    
    
    
    
    
    
     

     

     

     

     

     

     

     

     

     

     

     

     



     

     

     

     

     

  • 相关阅读:
    <<剪绳子>>题解
    P5743 小猴吃桃 题解
    注意C++中的int与long long 的乘法
    数组初始化方法总结
    一维差分和二维差分
    一维前缀和与二维前缀和
    例2-6 字母转换
    例1-11 评测机队列
    golang ---查找字串实例 IP address
    mongodb ---加减等操作
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3162977.html
Copyright © 2020-2023  润新知