您的位置:首页 > 运维架构 > Linux

s5pv210-Linux驱动之USB鼠标

2017-10-12 20:24 471 查看

一、开发环境

硬件平台:我用的是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_GADGET

config 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.o4、执行make menuconfig,将驱动文件编译进内核
[*] TQ210 usb mouse support5、重新编译内核,烧进开发板。

注:这种编进内核的方法,只是我试验的一种简单方法,不是很好,大家可以把驱动文件放在别的地方编译。

五、总结

    USB鼠标驱动最终是通过输入子系统,向内核上报鼠标数据的,但是数据的来源是USB总线的urb请求块。注意匹配条件usb_mouse_id_table一定要编写正确,不然总线不会匹配到相应的设备,而且确保系统内没有其他驱动会匹配到设备,因为当USB设备插入系统时,USB总线采用查询的方式,当找到第一个匹配的驱动时,就不继续查找了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  s5pv210 usb驱动 鼠标