自己写Linux Usb鼠标驱动程序

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 
#include 
#include 
#include 
#include 
#include 

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");

你可能感兴趣的:(Linux驱动)