USB 驱动(监测鼠标左键的动作)

(基于 Linux 3.4.2 内核)

可分为以下几个步骤来完成这个驱动:

1. 分配设置一个 usb_driver 结构体
2. 注册这个 usb_driver
(如果设备的 id_table 与驱动匹配的话会调用驱动程序的 probe 函数)
3. 在 probe 函数中分配 urb
4. 配置 urb
5. 调用 usb_submit_urb 启用 urb
6. 在 urb 中断函数内处理状态
7. 重新提交 urb

usb_driver 的配置与注册

/* 驱动的 id_table */
static struct usb_device_id usb_mouse_id_table [] = {
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE) },
    { }
};

/* 分配设置 usb_driver */
static struct usb_driver mouse_monitor = {
    .name       = "MouseMonitor",
    .probe      = mouse_monitor_probe,
    .disconnect = mouse_monitor_disconnect,
    .id_table       = usb_mouse_id_table,
};

/* 注册 usb_driver */
static int mouse_monitor_init(void)
{
    usb_register(&mouse_monitor);

    return 0;
}

probe 函数

static int mouse_monitor_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_host_interface *interface;
    struct usb_endpoint_descriptor *endpoint;
    static struct usb_device *dev;
    dma_addr_t usb_buf_phy;
    int pipe;
    int buffer_length;

    /* 得到 usb_device */
    dev = interface_to_usbdev(intf);

    /* 得到当前的接口描述符与端点描述符 */
    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc;

    /* 获取到设备数据长度 */
    buffer_length = __le16_to_cpu(endpoint->wMaxPacketSize);

    len = buffer_length;

    /* 获取到通信的管道 */
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    /* 分配一段连贯的内存 */
    usb_buf = usb_alloc_coherent(dev, buffer_length, GFP_ATOMIC, &usb_buf_phy);

    /* 分配 urb */
    MouseUrb = usb_alloc_urb(0, GFP_KERNEL);

    /* 配置 urb */
    usb_fill_int_urb(MouseUrb, dev, pipe, usb_buf,  (buffer_length > 8 ? 8 : buffer_length), usb_complete, NULL, endpoint->bInterval);
    MouseUrb->transfer_dma = usb_buf_phy;
    MouseUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    /* 提交调用 urb */
    usb_submit_urb(MouseUrb,  GFP_KERNEL);

    return 0;
}

urb 传输完成函数

static void usb_complete(struct urb *urb)
{
    static unsigned char presta;

#if 0
    int i;
    
    for (i = 0; i < len; i++)
        printk("%02x ", usb_buf[i]);

    printk("\n");
#endif

    if(presta != (usb_buf[1] & 0x01)){
        if(presta)
            printk("BTN_LEFT is released. \n");
        else
            printk("BTN_LEFT is pressed. \n");
    }

    /* 保存状态 */
    presta = usb_buf[1] & 0x01;

    /* 重新提交 urb */
    usb_submit_urb(MouseUrb,  GFP_KERNEL);
}

usb_complete 函数中注释掉的程序为测试使用,通过输出的数据找到鼠标左键对应的 usb_buf 与 bit 位。

测试驱动

make menuconfig 去掉原来的 USB 鼠标驱动
-> Device Drivers 
  -> HID Devices
  <> USB Human Interface Device (full HID) support

编译当前驱动,传入开发板并安装。

按下松开鼠标左键,现象如下: