定义鼠标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);
}
-
pre_val
是一个静态变量,用于保存上一次读取到的鼠标事件。 -
函数首先通过比较
pre_val
和usb_buf[0]
的位状态,检测左键、右键和中建是否发生了变化。 -
如果左键发生了变化,则使用
input_event()
函数向输入设备发送相应的事件(EV_KEY)和按键(KEY_L),如果该位为1,则表示按下;如果该位为0,则表示松开。然后通过input_sync()
同步输入事件。 -
同样的,如果右键或中键发生了变化,也会发送相应的事件给输入设备。
-
最后,将当前的
usb_buf[0]
赋值给pre_val
,以便在下一次中断时比较。 -
在函数最后,重新提交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;
}
-
通过传入的
usb_interface
和usb_device_id
参数获取USB设备对象dev
。 -
获取当前配置的接信息
interface
,并获得第一个端点的描述符endpoint
。 -
创建一个用于输入设备的
input_dev
结构体对象,并分配内存空间,保存在uk_dev
变量中。 -
设置该
input_dev
对象支持产生按键时间(EV_KEY)和重复时间(EV_REP)。 -
设置该
input_dev
对象支持产生左键(KEY_L)、右键(KEY_S)和回车键(KEY_ENTER)事件。 -
注册该
input_dev
对象,使其可以被系统识别和使用。 -
进行硬件相关操作前,需要确定数据传输的三个要素:源、目的和长度。其中,源是USB设备的某个端点,通过函数
usb_rcvintpipt()
获取相应管道号存储在pipe
变量中;长度通过端点描述符中的最大包长度字段wMaxPacketSize
获取存储在变量len
中。 -
使用函数
usb_alloc_coherent()
分配一块连续的内存作为目的缓冲区,并将物理地址保存在变量usb_buf_phys
中,虚拟地址保存在变量usb_buf
中。 -
分配一个USB Request Block(URB)作为数据传输的控制块,使用函数
usb_alloc_urb()
进行分配,并将URB相关信息填充:设备对象、管道号、缓冲区指针、缓冲区长度、中断处理函数usbmouse_as_key_irq
等。 -
设备URB的传输物理地址和标志位,确保不进行DMA映射。
-
使用函数
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);
}
-
usb_kill_urb(uk_urb)
: 停止和取消正在进行的URB(USB请求块)。 -
usb_free_urb(uk_urb)
: 释放之前分配的URB资源。 -
usb_free_coherent(dev, len, usb_buf, usb_buf_phys)
: 释放通过usb_alloc_coherent()
分配的一段连续内存区域。 -
input_unregister_device(uk_dev)
: 注销输入设备,将其从系统中移除。 -
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);
}