Linux/Android——usb触摸屏驱动 - usbtouchscreen

最近需要往TV上装一个触摸屏设备,现在比较常见的就是使用usb接口的触摸框,适用于各种平台,这里大体记录一下在android上kernel中的usbtouchscreen驱动.

撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/41827495

驱动编译:

目前的kernel中都是自带了usbtouchscreen驱动的,我的版本3.1.10

源码位于:/kernel/drivers/input/touchscreen/usbtouchscreen.c

从这个路径可以看出所属驱动分支,我这边平台本身是没放开的,并没有编译进kernel,谁会想到触摸电视呢~

可以在make menuconfig之后,通过Device Drivers——>Input device support——>Touchscreens——>USB Touchscreen Driver 然后选取需要的touchscreen类型

通过查看相关目录下的的Kconfig Makefile,可参考:Kernel 编译配置机制

注册usb驱动:

熟悉linux驱动的都知道模块入口:module_init(usbtouch_init) ,这里看下这个init:

static int __init usbtouch_init(void)
{
    return usb_register(&usbtouch_driver);  //调用了usb 核心的注册函数,传入的是一个usb_driver结构体指针
}

usb_register实现在/kernel/include/linux/usb.h中:

static inline int usb_register(struct usb_driver *driver)
{
    return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);//这里再往后就是usb核心驱动的事,注册这个module驱动到usb总线上
}

这里必须是要先注册的总线,当一个USB设备被插入的时候,USB设备驱动,也就是usb_generic_driver会跟USB设备交互,得到其所有的各种描述符,并为每个接口都定义成为一个device,之后再加载到usb_bus上,让其去匹配其对应的接口驱动程序,有兴趣可以去看下/kernel/drivers/base/bus.c中的bus_for_each_drv函数。

这里注册到总线的接口驱动就是 usbtouch_driver

usbtouch_driver

这个usb_driver类型的变量usbtouch_driver 就是整个usbtouchscreen的灵魂核心,可以在上面说到的usb.h中查看usb_driver结构原型,

这里usbtouch_driver使用了部分接口:

static struct usb_driver usbtouch_driver = {
    .name        = "usbtouchscreen", //driver name
    .probe        = usbtouch_probe,  //probe接口,用于总线上匹配检测到这个驱动对应的设备之后,/kernel/drivers/usb/core/driver.c中的usb_probe_interface调用到我们这个驱动的接口
    .disconnect    = usbtouch_disconnect,  //与probe相反,断开的时候调用
    .suspend    = usbtouch_suspend, //usb 设备挂起
    .resume        = usbtouch_resume,  // 和上面挂起相反,唤醒
    .reset_resume    = usbtouch_reset_resume,  // 重置唤醒
    .id_table    = usbtouch_devices, //支持的设备ID表
    .supports_autosuspend = 1,
};

id_table:

首先可以关注一下 id_table 这个变量,代表支持的设备id列表,数据类型为:

struct usb_device_id {
	/* which fields to match against? */
	__u16		match_flags;

	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;
	__u16		idProduct;
	__u16		bcdDevice_lo;
	__u16		bcdDevice_hi;

	/* Used for device class matches */
	__u8		bDeviceClass;
	__u8		bDeviceSubClass;
	__u8		bDeviceProtocol;

	/* Used for interface class matches */
	__u8		bInterfaceClass;
	__u8		bInterfaceSubClass;
	__u8		bInterfaceProtocol;

	/* not matched against */
	kernel_ulong_t	driver_info;
};

这些设备信息会被上面说到的usb bus 来匹配对应的驱动,只有这里的信息跟usb设备驱动那边收集到的设备信息匹配上,才会调用进这个驱动.

目前已有的id_table:

static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
    /* ignore the HID capable devices, handled by usbhid */
    {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
    {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},

...

#endif

...

};

其中可以看到 两个字节的十六进制数字,第一个代表idVendor 厂商ID,idProduct 产品ID ,这两个一般作为设备的标识.

driver_info:

像上面的usbtouch_devices的数组中driver_info 设置为枚举值:

/* device types */
enum {
	DEVTYPE_IGNORE = -1,
	DEVTYPE_EGALAX,
	DEVTYPE_PANJIT,
	DEVTYPE_3M,
	DEVTYPE_ITM,
	DEVTYPE_ETURBO,
	DEVTYPE_GUNZE,
	DEVTYPE_DMC_TSC10,
	DEVTYPE_IRTOUCH,
	DEVTYPE_IDEALTEK,
	DEVTYPE_GENERAL_TOUCH,
	DEVTYPE_GOTOP,
	DEVTYPE_JASTEC,
	DEVTYPE_E2I,
	DEVTYPE_ZYTRONIC,
	DEVTYPE_TC45USB,
	DEVTYPE_NEXIO,
};

那么这些driver 的真正的info保存在哪里呢? 在注册的时候,现在只是注册上去一个枚举数字而已,

真正有设备识别到的时候这些个枚举值就起到作用了! 在下面的 usbtouch_probe 会介绍!

usbtouch_probe

在前面有稍微提到,usbtouchscreen驱动是怎么被映射到的,这个过程暂时不做深入,作为这个驱动中的第一个接入点就是usbtouch_probe.

static int usbtouch_probe(struct usb_interface *intf,
              const struct usb_device_id *id)
{
    struct usbtouch_usb *usbtouch;  //usbtouch 设备
    struct input_dev *input_dev;  //输入设备
    struct usb_endpoint_descriptor *endpoint;  //usb 的端点
    struct usb_device *udev = interface_to_usbdev(intf);  //从usb接口获取对应的设备
    struct usbtouch_device_info *type;   //这个就是上面说的真正的 driver info了

    endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);  //获取端点
    if (!endpoint)
        return -ENXIO;
    usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
    input_dev = input_allocate_device();  //分配内存,申请input 设备结构

...

    type = &usbtouch_dev_info[id->driver_info];   // 这里就用到了 上面说到的枚举值了, 真正的info 是放在这个数组里面的!

...

    usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);  //分配了一个urb 用于 获得触摸屏设备返回的触摸事件的数据,urb的概念可参考usb driver
    if (!usbtouch->irq) {
        dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
        goto out_free_buffers;
    }

...

//往下都是一些分配内存,input注册,初始化操作了

    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); //这里是就是input设备触摸坐标的初始化赋值了,为ABS 绝对坐标
    input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
    input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);

...

    if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
        usb_fill_int_urb(usbtouch->irq, udev,
             usb_rcvintpipe(udev, endpoint->bEndpointAddress),
             usbtouch->data, type->rept_size,
             usbtouch_irq, usbtouch, endpoint->bInterval);
    else
        usb_fill_bulk_urb(usbtouch->irq, udev,
             usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
             usbtouch->data, type->rept_size,
             usbtouch_irq, usbtouch);  //初始化urb的回调函数为 usbtouch_irq

    usbtouch->irq->dev = udev;
    usbtouch->irq->transfer_dma = usbtouch->data_dma;
    usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

...

}

usbtouch_device_info:

这个就是上面driver_info 以及usbtouch_probe 中抽取的驱动模块的info数组,不同的usbtouchscreen 注册的时候就是注册了一个枚举值,这个值就是
usbtouch_dev_info 数组的第几元素.

struct usbtouch_device_info {
    int min_xc, max_xc;
    int min_yc, max_yc;
    int min_press, max_press;
    int rept_size;

    /*
     * Always service the USB devices irq not just when the input device is
     * open. This is useful when devices have a watchdog which prevents us
     * from periodically polling the device. Leave this unset unless your
     * touchscreen device requires it, as it does consume more of the USB
     * bandwidth.
     */
    bool irq_always;

    void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);  //这个函数指针是用来接收处理中断的。

    /*
     * used to get the packet len. possible return values:
     * > 0: packet len
     * = 0: skip one byte
     * < 0: -return value more bytes needed
     */
    int  (*get_pkt_len) (unsigned char *pkt, int len);

    int  (*read_data)   (struct usbtouch_usb *usbtouch, unsigned char *pkt);
    int  (*alloc)       (struct usbtouch_usb *usbtouch);
    int  (*init)        (struct usbtouch_usb *usbtouch);
    void (*exit)        (struct usbtouch_usb *usbtouch);
};

usbtouch_dev_info 数组:

static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
    [DEVTYPE_EGALAX] = {
        .min_xc        = 0x0,
        .max_xc        = 0x07ff,
        .min_yc        = 0x0,
        .max_yc        = 0x07ff,
        .rept_size    = 16,
        .process_pkt    = usbtouch_process_multi,//用于中断回调函数,用于处理中断,得到input的event,上传数据
        .get_pkt_len    = egalax_get_pkt_len,
        .read_data    = egalax_read_data, //用于中断回调函数,用于读取数据
    },
#endif

...

#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
    [DEVTYPE_IRTOUCH] = {
        .min_xc        = 0x0,
        .max_xc        = 0x0fff,
        .min_yc        = 0x0,
        .max_yc        = 0x0fff,
        .rept_size    = 8,
        .read_data    = irtouch_read_data,
    },
#endif 

...

};

可以看到这个数组的成员都是以前面说到的注册枚举值来区分的!这些x,y 参数以及回调函数,都在上面说到的 usbtouch_probe 中被抽离出来使用.

usbtouch_irq:

这个函数作为中断响应函数,在上面的 usbtouch_probe中初始化,看下函数主要实现:

static void usbtouch_irq(struct urb *urb)
{

...

    usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);  

//这个type的类型就是 usbtouch_device_info,此时的process_pkt指针自然指向的是上面对应的函数,如果此时是触发的设备type为 DEVTYPE_EGALAX,那么这里调用的 usbtouch_process_multi

//如果此时是DEVTYPE_IRTOUCH 那么就是执行 usbtouch_process_pkt函数,因为usbtouch_probe中:

//    if (!type->process_pkt)
//        type->process_pkt = usbtouch_process_pkt;

...

}

接下来的都会调用到usbtouch_process_pkt中,通过type->read_data,和上面一样的指针读取,然后调用input_report_key发送,input_sync用于同步.

关于usbtouchscreen的驱动部分就分析到这里。

时间: 2024-10-20 19:46:23

Linux/Android——usb触摸屏驱动 - usbtouchscreen的相关文章

Linux/Android——usb触摸屏驱动 - usbtouchscreen (一)【转】

本文转载自:http://blog.csdn.net/jscese/article/details/41827495 最近需要往TV上装一个触摸屏设备,现在比较常见的就是使用usb接口的触摸框,适用于各种平台,这里大体记录一下在android上kernel中的usbtouchscreen驱动. 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/41827495 驱动编译: 目前的kernel中都是自带了usbtouchscreen驱

linux下USB设备驱动

本文以USB鼠标讲解USB设备驱动. 驱动程序中调用usb_register(struct usb_driver *)函数时,先判断USB总线驱动程序是否支持usb_driver里定义的id_table,若支持,调用usb_driver的probe函数. 所以首先要定义并初始化usb_driver结构体: static struct usb_driver usbmouse_as_key_driver = { .name = "usbmouse_as_key", .probe = usb

Linux下 USB设备驱动分析(原创)

之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB枚举过程:https://blog.csdn.net/go_str/article/details/80802452 USB其它概念: (1)usb是主从结构,usb的传输都是主机发起: (2)usb右四种传输类型:控制.批量.中断.实时传输. (3)usb数据是通过端点进行通讯的,0端点既能输入也

linux下usb转串口驱动分析【转】

转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,(如果把struct 比作类,kmalloc就是类的实例化,结构体里面的函数指针就是方法,还有重构,多态) 2. 在linux里面,设备是对象,驱动也是对象,并且这两个是分开的 现在我们来看驱动的总体架构 并不用太在意这个图,对用户来说usb_serial设备就是普通的串口设备 我们可以看驱动里面几个

Linux/Android——输入子系统input_event传递

在前文Linux/Android--usb触摸屏驱动 - usbtouchscreen中记录了如何在kernel中添加input device 类型为touchscreen的驱动, 这在整个输入体系中是最下层的设备驱动部分,往上一层就是linux内核的管理驱动input系统,kernel中的源码位置:/kernel/drivers/input/input.c 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/42099381 到目前

Linux/Android——输入子系统input_event传递 (二)【转】

本文转载自:http://blog.csdn.net/jscese/article/details/42099381 在前文Linux/Android——usb触摸屏驱动 - usbtouchscreen (一)中记录了如何在kernel中添加input device 类型为touchscreen的驱动, 这在整个输入体系中是最下层的设备驱动部分,往上一层就是linux内核的管理驱动input系统,kernel中的源码位置:/kernel/drivers/input/input.c 撰写不易,转

【视频】嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发

嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发 专题简介:自1971年,美国人SamHurst发明了世界上第一个触摸传感器以来,触摸屏技术不断革新,给了程序设计师和UI工程师无限的想象空间,它极大改善了终端用户对各种设备的操作方便程度,现在我们的日常生活如手机.平板等,已经很大程度上依赖于和习惯于使用和操作触摸屏.做为工程师,我们很有必要掌握触摸屏的工作原理和软件驱动方法,如果您对一窥如何在嵌入式中操控和使用触摸屏这一司空见惯却又神奇的技术感兴趣,敬请关注! 1.LINUX驱动

Linux下安装Android的adb驱动-解决不能识别的问题

Linux下安装Android的adb驱动-解决不能识别的问题 20141011更新: 老方法对我当时使用的一款设备一直都没有出现问题,最后遇到小米手机还有Android4.4版本的系统都会每次出现error: insufficient permissions for device问题,老方法的最后也说了这个问题的解决方法是什么.大意就是adb服务得以root用户身份启动,比较靠谱的方法是添加或者修改/etc/udev/rules.d/51-android.rules文件,内容如下: SUBSY

android下调试3G之USB串口驱动集成配置

一.修改Android内核的编译配置 vi ~/myandroid/kernel_imx/.config文件中,确保下面的的配置项已经被选定. (假如源码在~/myandroid目录下) 1. USB电源管理特性的相关配置项: CONFIG_USB_SUSPEND=y 2. USB串口驱动相关的配置项: CONFIG_USB_SERIAL=y CONFIG_USB_SERIAL_OPTION=y CONFIG_USB_SERIAL_WWAN=y 3. PPP拨号的相关配置项: CONFIG_PP