仿照drivers/hid/usbhid/usbmouse.c系统自带的USB鼠标驱动实现的USB鼠标驱动。
一、usb/usbmouse_as_key.c
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>static int len;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
struct urb *uk_urb;
struct input_dev *uk_dev;static void usbmouse_as_key_irq(struct urb *urb)
{
#if 0int i;static int cnt = 0;printk("date cnt %d: ", ++cnt);for(i = 0; i < len; i++){printk("%02x ", usb_buf[i]);}printk("\n");//加载驱动之后可通过 hexdump dev/input/event0 查看鼠标数据
#endifstatic char per_val;/*鼠标数据格式:data[0]: bit0 - 左键,1 - 按下,0 - 松开bit1 - 右键,1 - 按下,0 - 松开bit2 = 中键,1 - 按下,0 - 松开data[6]: = 0x01 滚轮前进= 0xff 滚轮后进*/if((per_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((per_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((per_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);}per_val = usb_buf[0];/* 重新提交urb */usb_submit_urb(uk_urb, GFP_KERNEL);
}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_endpoint_descriptor *endpoint;struct usb_host_interface *interface;int pipe;printk("bcdUSB = %x\nVID = 0x%x\nPID = 0x%x\n", dev->descriptor.bcdUSB, dev->descriptor.idVendor, dev->descriptor.idProduct);printk("run %s, %d\n", __FUNCTION__, __LINE__);interface = intf->cur_altsetting;endpoint = &interface->endpoint[0].desc;/* a. 分配一个input_dev */uk_dev = input_allocate_device();uk_dev->name = "usbmouse";/* b. 设置 *//* b.1 能产生哪类事件 */set_bit(EV_KEY, uk_dev->evbit);set_bit(EV_REP, uk_dev->evbit); /* b.2 能产生哪些事件 */set_bit(KEY_L, uk_dev->keybit);set_bit(KEY_S, uk_dev->keybit);set_bit(KEY_ENTER, uk_dev->keybit);/* c. 注册 */input_register_device(uk_dev);/* d. 硬件相关操作 *//* 数据传输3要素: 源,目的,长度 *//* 源: USB设备的某个端点 */pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */len = endpoint->wMaxPacketSize;/* 目的: */usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);/* 使用"3要素" *//* 分配usb request block */uk_urb = usb_alloc_urb(0, GFP_KERNEL);/* 使用"3要素设置urb" */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;/* 使用URB */usb_submit_urb(uk_urb, GFP_KERNEL);return 0;
}static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{struct usb_device *dev = interface_to_usbdev(intf);printk("run %s, %d\n", __FUNCTION__, __LINE__); 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);
}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) },{ } /* Terminating entry */
};static struct usb_driver usbmouse_as_key_driver = {.name = "usbmouse",.probe = usbmouse_as_key_probe,.disconnect = usbmouse_as_key_disconnect,.id_table = usbmouse_as_key_id_table,
};static int usbmouse_as_key_init(void)
{usb_register(&usbmouse_as_key_driver);return 0;
}static void usbmouse_as_key_exit(void)
{usb_deregister(&usbmouse_as_key_driver);
}module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);
MODULE_LICENSE("GPL");
二、测试
1、说明
KEY_L = 38 = 0x0026
KEY_S = 31 = 0x001f
KEY_ENTER = 28 = 0x001c
因为鼠标中间坏了,就不验证了。
2、内核处理
内核需要把系统自带的USB鼠标驱动移除
insmod usbmouse_as_key.ko
bcdUSB = 200
VID = 0x46d
PID = 0xc52f
run usbmouse_as_key_probe, 71
input: usbmouse as /devices/virtual/input/input4
usbcore: registered new interface driver usbmouse
/ #
/ # hexdump dev/input/event0
0000000 0878 0000 c7fa 000b 0001 0026 0001 0000
0000010 0878 0000 c7fa 000b 0000 0000 0000 0000
0000020 0878 0000 8d09 000d 0001 0026 0000 0000
0000030 0878 0000 8d09 000d 0000 0000 0000 0000
0000040 0879 0000 3d1c 000c 0001 001f 0001 0000
0000050 0879 0000 3d1c 000c 0000 0000 0000 0000
三、问题排查
1、接上鼠标打印device descriptor read/64, error -62
经分析是USB D+、D-接反导致。
/ # usb 4-1: new full-speed USB device number 2 using ohci-platform
usb 4-1: device descriptor read/64, error -62
usb 4-1: device descriptor read/64, error -62
usb 4-1: new full-speed USB device number 3 using ohci-platform
usb 4-1: device descriptor read/64, error -62
usb 4-1: device descriptor read/64, error -62
usb 4-1: new full-speed USB device number 4 using ohci-platform
usb 4-1: device not accepting address 4, error -62
usb 4-1: new full-speed USB device number 5 using ohci-platform
usb 4-1: device not accepting address 5, error -62
usb usb4-port1: unable to enumerate USB device
2、打印USB disconnect, device number 2
采用有线鼠标测试的时候,出现过加载驱动后打印显示成功加载,但过了一会儿就显示断开了 USB disconnect,采用无线鼠标则没有此类情况。可能是单板USB口采用飞线引出,信号有影响导致。
/ # insmod usbmouse_as_key.ko
bcdUSB = 200
VID = 0x17ef
PID = 0x608d
run usbmouse_as_key_probe, 40
input: usbmouse as /devices/virtual/input/input0
usbcore: registered new interface driver usbmouse
/ # usb 4-1: USB disconnect, device number 2
run usbmouse_as_key_disconnect, 83
usb 4-1: new low-speed USB device number 3 using ohci-platform
bcdUSB = 200
VID = 0x17ef
PID = 0x608d
run usbmouse_as_key_probe, 40
input: usbmouse as /devices/virtual/input/input1