USB子系统相关内容参考《精通Linux设备驱动程序》第11章。
USB鼠标驱动程序可以参考内核中的鼠标驱动,路径为linux-3.0.86\drivers\hid\usbhid\usbmouse.c
USB鼠标驱动编写步骤为:
1、创建usb_driver结构体变量,设置id_table为usb鼠标设备,并注册usb驱动。
static 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 */
};
usb_register(&my_usb_mouse_driver);
在probe函数中:
2、创建并分配input_device输入设备。
my_mouse_dev = input_allocate_device();
3、设置input_device,支持按键和相对位移输入。
set_bit(EV_KEY, my_mouse_dev->evbit);
set_bit(EV_REL, my_mouse_dev->evbit);
set_bit(BTN_LEFT, my_mouse_dev->keybit);
set_bit(BTN_RIGHT, my_mouse_dev->keybit);
set_bit(BTN_MIDDLE, my_mouse_dev->keybit);
set_bit(REL_X, my_mouse_dev->relbit);
set_bit(REL_Y, my_mouse_dev->relbit);
3、注册input_device输入设备.
input_register_device(my_mouse_dev);
4、创建urb结构体变量,为urb分配内存。
data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &data_dma);
5、为urb中数据缓冲区分配内存。
urb = usb_alloc_urb(0, GFP_KERNEL);
6、设置pipe,表示终端节点地址,是接收还是发送,是什么模式,比如int中断传输模式。
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
7、填充urb,report_mouse为函数指针,表示urb出错或者接收数据完成,要执行的这个函数。bInterval表示中断传输的轮询间隔时间。
usb_fill_int_urb(urb, dev, pipe, data, len,
report_mouse, dev, endpoint->bInterval);
8、提交urb。
usb_submit_urb(urb, GFP_KERNEL);
8、在report_mouse()函数中上报数据并再次提交urb。
// 左键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x01);
// 右键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x02);
// 中键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x04);
// 左右移动
input_report_rel(my_mouse_dev, REL_X, data[1]);
// 前后移动
input_report_rel(my_mouse_dev, REL_Y, data[2]);
input_sync(my_mouse_dev);
usb_submit_urb (urb, GFP_ATOMIC);
完整源码如下:
/* my usbmouse */
#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 struct input_dev *my_mouse_dev;
static unsigned char *data;
static dma_addr_t data_dma;
static struct urb *urb;
static 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 */
};
void report_mouse(struct urb * urb)
{
#if 0
//printk("report _mouse data\n");
int cnt = 0;
int i = 0;
printk("report data cnt:%d\n",++cnt);
for(i=0; i < urb->actual_length; i++)
{
printk(" 0x%x", data[i]);
}
printk("\n");
#endif
// 上报数据
// 左键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x01);
// 右键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x02);
// 中键
input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x04);
// 左右移动
input_report_rel(my_mouse_dev, REL_X, data[1]);
// 前后移动
input_report_rel(my_mouse_dev, REL_Y, data[2]);
// 同步信号
input_sync(my_mouse_dev);
// 再次提交urb,不可休眠
usb_submit_urb (urb, GFP_ATOMIC);
}
static int my_usb_mouse_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret;
int pipe, len;
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_device_descriptor des = dev->descriptor;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
ret = 0;
printk("bcdDevice:%x \nidVender:%x \nidProduce:%x \niManufacturer:%d\n",des.bcdDevice,des.idVendor, \
des.idProduct,des.iManufacturer);
interface = intf->cur_altsetting;
// 终端描述符
endpoint = &interface->endpoint[0].desc;
// 1、 input 设备分配
my_mouse_dev = input_allocate_device();
if(NULL == my_mouse_dev){
printk("allocate input dev error.\n");
}
my_mouse_dev->name = "myusbmouse";
set_bit(EV_KEY, my_mouse_dev->evbit);
set_bit(EV_REL, my_mouse_dev->evbit);
set_bit(BTN_LEFT, my_mouse_dev->keybit);
set_bit(BTN_RIGHT, my_mouse_dev->keybit);
set_bit(BTN_MIDDLE, my_mouse_dev->keybit);
set_bit(REL_X, my_mouse_dev->relbit);
set_bit(REL_Y, my_mouse_dev->relbit);
// 1.2、注册输入设备ret
ret = input_register_device(my_mouse_dev);
if(ret )
goto fail1;
// 2、 urb操作
// 2.1、为urb中数据缓冲区分配空间
data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &data_dma);
if(NULL == data)
goto fail2;
// 2.2、设置管道,端点为数据流向地址,rcv为接收,int表示中断
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
len = endpoint->wMaxPacketSize;
printk("rcv data len is %d\n", len);
// 2.3 为urb分配内存
urb = usb_alloc_urb(0, GFP_KERNEL);
if(NULL == urb )
goto fail3;
// 2.4、填充urb结构体
usb_fill_int_urb(urb, dev, pipe, data, len,
report_mouse, dev, endpoint->bInterval);
// dma方式传输数据
urb->transfer_dma = data_dma;
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
// 2.5、提交urb
usb_submit_urb(urb, GFP_KERNEL);
return 0;
fail3:
usb_free_urb(urb);
fail2:
usb_free_coherent(dev, 8, data, data_dma);
fail1:
input_free_device(my_mouse_dev);
input_unregister_device(my_mouse_dev);
return ret;
}
static void my_usb_mouse_disconnect(struct usb_interface *intf)
{
usb_kill_urb(urb);
usb_free_coherent(interface_to_usbdev(intf), 8, data, data_dma);
usb_free_urb(urb);
input_unregister_device(my_mouse_dev);
input_free_device(my_mouse_dev);
usb_set_intfdata(intf, NULL);
}
static struct usb_driver my_usb_mouse_driver = {
.name = "my_usbmouse",
.probe = my_usb_mouse_probe,
.disconnect = my_usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
static int __init my_usb_mouse_init(void)
{
// 注册USB设备
int retval = usb_register(&my_usb_mouse_driver);
if (retval < 0)
printk(KERN_INFO "register usb deivce error \n");
return retval;
}
static void __exit my_usb_mouse_exit(void)
{
usb_deregister(&my_usb_mouse_driver);
}
module_init(my_usb_mouse_init);
module_exit(my_usb_mouse_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhangdalei");