USB设备驱动代码分析(鼠标)

定义鼠标ID表

static struct usb_device_id usbmouse_as_key_id_table [] = {

    {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE)}, {}
};

这段代码是用于定义一个 USB 鼠标设备的 ID 表。它使用了 Linux 内核中的 usb_device_id 结构,其中包含了 USB 设备的类、子类和协议信息。

这个ID表定义了USB接口,其类别为HID(Human Interface Device),子类别为BOOT(表示鼠标设备),协议为MOUSE(表示鼠标协议)。这个表示在Linux内核的USB驱动中使用,可以匹配对应类型的USB设备,并进行相应的操作。在最后添加一个空结构表示结束符。

中断处理函数

static void usbmouse_as_key_irq(struct urb* urb) {

    static unsigned char pre_val;

    if ((pre_val & (1 << 0)) != (usb_buf[0] & (1 << 0))) {

        input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1 << 0)) ? 1 : 0);
        input_sync(uk_dev);
    }

    if ((pre_val & (1 << 1)) != (usb_buf[0] & (1 << 1))) {

        input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1 << 1)) ? 1 : 0);
        input_sync(uk_dev);
    }

    if ((pre_val & (1 << 2)) != (usb_buf[0] & (1 << 2))) {

        input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1 << 2)) ? 1 : 0);
        input_sync(uk_dev);
    }

    pre_val = usb_buf[0];

    usb_submit_urb(uk_urb, GFP_KERNEL);
}
  1. pre_val是一个静态变量,用于保存上一次读取到的鼠标事件。

  2. 函数首先通过比较pre_valusb_buf[0]的位状态,检测左键、右键和中建是否发生了变化。

  3. 如果左键发生了变化,则使用input_event()函数向输入设备发送相应的事件(EV_KEY)和按键(KEY_L),如果该位为1,则表示按下;如果该位为0,则表示松开。然后通过input_sync()同步输入事件。

  4. 同样的,如果右键或中键发生了变化,也会发送相应的事件给输入设备。

  5. 最后,将当前的usb_buf[0]赋值给pre_val,以便在下一次中断时比较。

  6. 在函数最后,重新提交urb(USB Request Block)

检测设备

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

    struct usb_device* dev = interface_to_usbdev(intf);
    struct usb_host_interface* interface;
    struct usb_endpoint_descriptor* endpoint;
    int pipe;

    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc;

    uk_dev = input_allocate_device();

    set_bit(EV_KEY, uk_dev->evbit);
    set_bit(EV_REP, uk_dev->evbit);

    set_bit(KEY_L, uk_dev->keybit);
    set_bit(KEY_S, uk_dev->keybit);
    set_bit(KEY_ENTER, uk_dev->keybit);

    input_register_device(uk_dev);

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    len = endpoint->wMaxPacketSize;

    usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);

    uk_urb = usb_alloc_urb(0, GFP_KERNEL);

    usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
    uk_urb->transfer_dma = usb_buf_phys;
    uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    usb_submit_urb(uk_urb, GFP_KERNEL);

    return 0;
}
  1. 通过传入的usb_interfaceusb_device_id参数获取USB设备对象dev

  2. 获取当前配置的接信息interface,并获得第一个端点的描述符endpoint

  3. 创建一个用于输入设备的input_dev结构体对象,并分配内存空间,保存在uk_dev变量中。

  4. 设置该input_dev对象支持产生按键时间(EV_KEY)和重复时间(EV_REP)。

  5. 设置该input_dev对象支持产生左键(KEY_L)、右键(KEY_S)和回车键(KEY_ENTER)事件。

  6. 注册该input_dev对象,使其可以被系统识别和使用。

  7. 进行硬件相关操作前,需要确定数据传输的三个要素:源、目的和长度。其中,源是USB设备的某个端点,通过函数usb_rcvintpipt()获取相应管道号存储在pipe变量中;长度通过端点描述符中的最大包长度字段wMaxPacketSize获取存储在变量len中。

  8. 使用函数usb_alloc_coherent()分配一块连续的内存作为目的缓冲区,并将物理地址保存在变量usb_buf_phys中,虚拟地址保存在变量usb_buf中。

  9. 分配一个USB Request Block(URB)作为数据传输的控制块,使用函数usb_alloc_urb()进行分配,并将URB相关信息填充:设备对象、管道号、缓冲区指针、缓冲区长度、中断处理函数usbmouse_as_key_irq等。

  10. 设备URB的传输物理地址和标志位,确保不进行DMA映射。

  11. 使用函数usb_submit_urb()提交URB,驱动数据传输过程。

断开释放

static void usbmouse_as_key_disconnect(struct usb_interface *intf) {
	struct usb_device *dev = interface_to_usbdev(intf);

	usb_kill_urb(uk_urb);
	usb_free_urb(uk_urb);

	usb_free_coherent(dev, len, usb_buf, usb_buf_phys);
	input_unregister_device(uk_dev);
	input_free_device(uk_dev);
}
  1. usb_kill_urb(uk_urb): 停止和取消正在进行的URB(USB请求块)。

  2. usb_free_urb(uk_urb): 释放之前分配的URB资源。

  3. usb_free_coherent(dev, len, usb_buf, usb_buf_phys): 释放通过usb_alloc_coherent()分配的一段连续内存区域。

  4. input_unregister_device(uk_dev): 注销输入设备,将其从系统中移除。

  5. input_free_device(uk_dev): 释放输入设备占用的资源。

插入和退出内核

static int usbmouse_as_key_init(void) {
	/* 2. 注册 */
	usb_register(&usbmouse_as_key_driver);
	return 0;
}

static void usbmouse_as_key_exit(void) {
	usb_deregister(&usbmouse_as_key_driver);	
}
  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值