USB驱动之常规usb鼠标驱动

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USB驱动之常规usb鼠标驱动相关的知识,希望对你有一定的参考价值。

        常规usb鼠标驱动使用的是linux中常用的drivers/hid/usbhid/usbmouse.c驱动。

static const 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 */
;

MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);

static struct usb_driver usb_mouse_driver = 
	.name		= "usbmouse",
	.probe		= usb_mouse_probe,
	.disconnect	= usb_mouse_disconnect,
	.id_table	= usb_mouse_id_table,
;

module_usb_driver(usb_mouse_driver);

        probe主要是初始化usb设备和input设备,终极目标是为了完成urb的提交和input设备的注册

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

	struct usb_device *dev = interface_to_usbdev(intf);//由接口获取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;

	if (interface->desc.bNumEndpoints != 1)//鼠标端点只有1个
		return -ENODEV;

	endpoint = &interface->endpoint[0].desc;//获得端点描述符
	if (!usb_endpoint_is_int_in(endpoint))//检查该端点是否是中断输入端点
		return -ENODEV;

	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//建立中断输入端点
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));//返回端点能传输的最大的数据包,鼠标的返回的最大数据包为4个字节

	mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);//分配mouse结构体
	input_dev = input_allocate_device();//分配input设备空间
	if (!mouse || !input_dev)
		goto fail1;

	mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
	if (!mouse->data)
		goto fail1;

	mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
	if (!mouse->irq)
		goto fail2;

	mouse->usbdev = dev;
	mouse->dev = input_dev;

	if (dev->manufacturer) //拷贝厂商ID
		strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

	if (dev->product)  //拷贝产品ID
		if (dev->manufacturer)
			strlcat(mouse->name, " ", sizeof(mouse->name));
		strlcat(mouse->name, dev->product, sizeof(mouse->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));

	usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
	strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

	input_dev->name = mouse->name;//将鼠标名赋给内嵌input结构体
	input_dev->phys = mouse->phys;//将鼠标设备节点名赋给内嵌input结构体
	usb_to_input_id(dev, &input_dev->id);//将usb_driver的支持项拷贝给input
	input_dev->dev.parent = &intf->dev;

    //evbit表明支持按键事件(EV_KEY)和相对坐标事件(EV_REL)
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
   //keybit表明按键值包括左键、右键和中键
	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
		BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
    //relbit表明相对坐标事件值包括X坐标和Y坐标
	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
    //keybit表明除了左键、右键和中键,还支持其他按键
	input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
		BIT_MASK(BTN_EXTRA);
    //relbit表明除了X坐标和Y坐标,还支持中键滚轮的滚动值
	input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

	input_set_drvdata(input_dev, mouse);//将mouse设置为input的私有数据

	input_dev->open = usb_mouse_open; //input设备的open
	input_dev->close = usb_mouse_close;

    //提交urb和中断服务程序
	usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
			 (maxp > 8 ? 8 : maxp),
			 usb_mouse_irq, mouse, endpoint->bInterval);
	mouse->irq->transfer_dma = mouse->data_dma;
	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/使用transfer_dma

	error = input_register_device(mouse->dev);//注册input设备
	if (error)
		goto fail3;

	usb_set_intfdata(intf, mouse);
	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;

        根据USB数据处理流程,当处理完毕后,USB核心会通知USB设备驱动程序,这里我们是响应中断服务程序,这就相当于该URB的回调函数。我们在提交urb时候定义了中断服务程序usb_mouse_irq

static void usb_mouse_irq(struct urb *urb)

	struct usb_mouse *mouse = urb->context;
	signed char *data = mouse->data;
	struct input_dev *dev = mouse->dev;
	int status;

	switch (urb->status) 
	case 0:			/* success */
		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]);
	input_report_rel(dev, REL_Y,     data[2]);
	input_report_rel(dev, REL_WHEEL, data[3]);

	input_sync(dev);
resubmit:
	status = usb_submit_urb (urb, GFP_ATOMIC);//提交urb,等待下次响应
	if (status)
		dev_err(&mouse->usbdev->dev,
			"can't resubmit intr, %s-%s/input0, status %d\\n",
			mouse->usbdev->bus->bus_name,
			mouse->usbdev->devpath, status);

以上是关于USB驱动之常规usb鼠标驱动的主要内容,如果未能解决你的问题,请参考以下文章

USB-SC-09(假冒PL2303HXA芯片)WIN7-64位驱动之终极大法

USB-SC-09(假冒PL2303HXA芯片)WIN7-64位驱动之终极大法

linux驱动之USB驱动程序

Linux USB 驱动开发—— USB 鼠标驱动注解及测试

和菜鸟一起学linux总线驱动之初识USB鼠标抓包数据

Linux USB 驱动开发实例—— USB 鼠标驱动注解及测试