s5pv210-Linux驱动之USB鼠标

来源:互联网 发布:nginx php 500错误 编辑:程序博客网 时间:2024/04/30 00:00

一、开发环境

硬件平台:我用的是TQ210核心板,板载S5PV210芯片,USB扩展接有FE1.1S芯片,是一个4端口的HUB
软件平台:开发板移植的是Linux3.10.46内核,UBOOT移植的是2014.12版本

二、资源简介

    前几篇已经移植好了USB的主机控制器驱动,只要编写鼠标对应的驱动,注册到USB总线就可以了。

三、移植步骤

1、分配一个usb_driver结构体,如下

static struct usb_driver tq_usb_mouse_driver = {    .name       = "tq_usbmouse",    .probe      = tq_usb_mouse_probe,    .disconnect = tq_usb_mouse_disconnect,    .id_table   = usb_mouse_id_table,};

2、usb_mouse_id_table是用来和插入开发板的设备匹配用的,定义如下:

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 */};MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
其中,USB_INTERFACE_INFO在include/linux/usb.h中定义,如下:

#define USB_INTERFACE_INFO(cl, sc, pr) \.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \.bInterfaceClass = (cl), \.bInterfaceSubClass = (sc), \.bInterfaceProtocol = (pr)
其中,USB_DEVICE_ID_MATCH_INT_INFO定义如下:

#define USB_DEVICE_ID_MATCH_INT_INFO \(USB_DEVICE_ID_MATCH_INT_CLASS | \USB_DEVICE_ID_MATCH_INT_SUBCLASS | \USB_DEVICE_ID_MATCH_INT_PROTOCOL)
表示,需要完全匹这3项才可以

3、编写tq_usb_mouse_probe函数,当驱动和设备匹配成功以后,就会调用此函数:

static int tq_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 设备分配    tq_mouse_dev = input_allocate_device();    if(NULL == tq_mouse_dev){        printk("allocate input dev error.\n");    }    tq_mouse_dev->name = "tqusbmouse";    set_bit(EV_KEY, tq_mouse_dev->evbit);    set_bit(EV_REL, tq_mouse_dev->evbit);    set_bit(BTN_LEFT, tq_mouse_dev->keybit);    set_bit(BTN_RIGHT, tq_mouse_dev->keybit);    set_bit(BTN_MIDDLE, tq_mouse_dev->keybit);    set_bit(REL_X, tq_mouse_dev->relbit);    set_bit(REL_Y, tq_mouse_dev->relbit);    // 注册输入设备    ret = input_register_device(tq_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(tq_mouse_dev);    input_unregister_device(tq_mouse_dev);    return ret;}

些函数主要做了两件事:

注册一个input输入子系统,并设置为鼠标功能;

注册一个urb,并设置其需拟地址、DMA地址、传输长度等

当USB总线处理完成这个urb请求块时,会调用usb_fill_int_urb函数提供的回调函数report_mouse,定义如下:

void report_mouse(struct urb * urb){    // 上报数据    // 左键    input_report_key(tq_mouse_dev, BTN_LEFT, data[0] & 0x01);    // 右键    input_report_key(tq_mouse_dev, BTN_LEFT, data[0] & 0x02);    // 中键    input_report_key(tq_mouse_dev, BTN_LEFT, data[0] & 0x04);    // 左右移动    input_report_rel(tq_mouse_dev, REL_X, data[1]);    // 前后移动    input_report_rel(tq_mouse_dev, REL_Y, data[2]);    // 同步信号    input_sync(tq_mouse_dev);    // 再次提交urb,不可休眠    usb_submit_urb (urb, GFP_ATOMIC);}
此函数会将鼠标的按键、位移信息上报系统输入子系统,然后再次提交urb请求块

5、编写tq_usb_mouse_disconnect函数,当拔出设备时就会调用此函数:

static void tq_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(tq_mouse_dev);    input_free_device(tq_mouse_dev);    usb_set_intfdata(intf, NULL);}
些函数就是释放一些内存等操作

四、编进内核

    驱动可以作为模块加载,也可以和内核一起编译,步骤如下:

1、切换到目录drivers\usb中,把鼠标驱动文件拷贝到此目录

2、修改Kconfig,在config USB_COMMON后面添加

config USB_COMMON        tristate        default y        depends on USB || USB_GADGETconfig TQ_USB_MOUSE        bool "TQ210 usb mouse support"        depends on USB || USB_GADGET        ---help---          Say Y here if you wish to control a tq210 usb mouse.
3、修改Makefile,在最后一行添加

obj-$(CONFIG_TQ_USB_MOUSE)      += tqUsbMouse.o
4、执行make menuconfig,将驱动文件编译进内核

[*]   TQ210 usb mouse support
5、重新编译内核,烧进开发板。
注:这种编进内核的方法,只是我试验的一种简单方法,不是很好,大家可以把驱动文件放在别的地方编译。

五、总结

    USB鼠标驱动最终是通过输入子系统,向内核上报鼠标数据的,但是数据的来源是USB总线的urb请求块。注意匹配条件usb_mouse_id_table一定要编写正确,不然总线不会匹配到相应的设备,而且确保系统内没有其他驱动会匹配到设备,因为当USB设备插入系统时,USB总线采用查询的方式,当找到第一个匹配的驱动时,就不继续查找了。





原创粉丝点击