usb键鼠驱动分析

本文详细介绍了Linux系统下USB鼠标和键盘驱动的工作原理。针对鼠标,重点分析了驱动加载初始化、匹配创建设备、probe和disconnect方法,以及urb数据传输过程。对于键盘,同样解析了初始化、probe方法中的内存管理、urb数据传输和LED指示灯处理。通过 urb,设备动作通过USB传输,输入设备接收到数据后同步事件消息。
摘要由CSDN通过智能技术生成

一、鼠标

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 &&
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值