常规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_devicestruct usb_host_interface *interface;struct usb_endpoint_descriptor *endpoint;//端点描述符struct usb_mouse *mouse;//本驱动私有结构体struct input_dev *input_dev;//输入结构体int pipe, maxp;int error = -ENOMEM;interface = intf->cur_altsetting;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) //拷贝厂商IDstrlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));if (dev->product) { //拷贝产品IDif (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的支持项拷贝给inputinput_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设备的openinput_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_dmaerror = 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);
}