转自: https://blog.csdn.net/jkzrc/article/details/18549957

1. 添加I2C 设备

TP 一般采用的是I2C 作为数据和命令接口,所以TP 驱动也可以归类为I2C 驱动。TP驱动的主要逻辑不在这里,但是了解了Linux 的I2C 体系架构,就可以对整个驱动流程有了

更加清晰的认识,但这里不详细展开讨论I2C 的体系架构,只围绕怎么移植开发TP 驱动展开讨论。

在板级文件中,也就是瑞星微的代码文件board-rk30-sdk.c 中,实例化一个i2c_board_info结构体,该结构抽象描述一个具体的i2c 设备,然后将该实例添加到__i2c_board_list 全局链表中。举个实例:

- 阅读剩余部分 -

转自: https://blog.csdn.net/ctx_ubuntu/article/details/80053694

TP驱动芯片厂商主要有:Goodix(汇顶),FocalTech(敦泰科技),cypress(赛普拉斯),synaptics(新思)。 TP驱动的调试主要是将接口调通。 从TP厂商获取驱动和配置资料。

4.2.1. 集成或者使用系统自带的TP驱动

查找 kernel/drivers/input/touchscreen,看是否有对应的驱动。如果没有,可以从高通的网站下载驱动或者找厂商要驱动

由于TP通过I2C与处理器通信,所以只要调通I2C,TP调试工作基本就完成了。 通过kernel log 查看类似这些probe函数:goodix_ts_probe(), cyttsp5_probe(), synaptics_rmi4_probe() 的执行情况,看看问题出在哪。 I2C主要看地址是否正确。还要用示波器量一下I2C线上的波形。I2C是不用时拉高,传输数据时才会有拉低的信号。如果log里说I2C忙,那就有可能I2C没有拉高。

TP触摸屏,应该是驱动开发中比较简单并且适合新手入手的模块。不过虽然简单,但涉及到的内容还是比较多的,其中linux相关主要的机制:

  1. input 机制
  2. 中断、定时器
  3. I2C

- 阅读剩余部分 -

转自: https://blog.csdn.net/xubin341719/article/details/7833383

关键词:android 电容屏 tp 工作队列 中断 坐点计算 电容屏主要参数 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0

平台:S5PV310(samsung exynos 4210)

作者:xubin341719(欢迎转载,请注明作者)

android 电容屏(一):电容屏基本原理篇

android 电容屏(二):驱动调试之基本概念篇

android 电容屏(三):驱动调试之驱动程序分析篇

以goodix的gt8105为例

一、总体架构

- 阅读剩余部分 -

转自: https://blog.csdn.net/xubin341719/article/details/7833277

关键词:android 电容屏 tp 工作队列 中断 多点触摸协议 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 平台:S5PV310(samsung exynos 4210)

参考网站:http://edsionte.com/techblog/archives/1582 这部分参考别人的多一点

android 电容屏(一):电容屏基本原理篇

android 电容屏(二):驱动调试之基本概念篇

android 电容屏(三):驱动调试之驱动程序分析篇

    电容屏驱动调试先了解Linux电容屏驱动中几个常用的概念:
          中断下半部-工作队列;
          input机制;
          Linux与Android 多点触摸协议。

一、中断下半部-工作队列

1、中断

先看一下宋宝华先生的《linux设备驱动开发详解》里面对中断的描述吧。这本书个人感觉 写的比较好,从开始学驱动到现在,还能从中得到不少知识。

设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可能地短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。如下图描述了Linux内核的中断处理机制。为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux将中断处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求。现在,中断处理工作的重心就落在了底半部的头上,它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部往往被设计成不可中断。底半部则相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。尽管顶半部、底半部的结合能够改善系统的响应能力,但是,僵化地认为Linux设备驱动中的中断处理一定要分两个半部则是不对的。如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。

其实上面这一段大致说明一个问题,那就是:中断要尽可能耗时比较短,尽快恢复系统正常调试,所以把中断触发、中断执行分开,也就是所说的“上半部分(中断触发)、底半部(中断执行)”,其实就是我们后面说的中断上下文。下半部分一般有tasklet、工作队列实现,触摸屏中中断实现以工作队列形式实现的,所以我们今天重点讲工作队列。

- 阅读剩余部分 -

转自: https://blog.csdn.net/xubin341719/article/details/7820492

关键词:android 电容屏 tp ITO 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 平台:S5PV310(samsung exynos 4210)

android 电容屏(一):电容屏基本原理篇

android 电容屏(二):驱动调试之基本概念篇

android 电容屏(三):驱动调试之驱动程序分析篇

一、电容屏工作原理

触摸屏的工作原理概括来说就是上报坐标值,X轴、Y轴的值。前面我们分析了电阻触摸屏,它是通过ADC来检测计算X、Y轴坐标值,下面我们分析一下电容触摸屏的工作原理,看它是如何去检测计算X、Y坐标的值。

与电阻式触摸屏不同,电容式触摸屏不依靠手指按力创造、改变电压值来检测坐标的。电容屏通过任何持有电荷的物体包括人体皮肤工作。(人体所带的电荷)电容式触摸屏是由诸如合金或是銦錫氧化物(ITO)这样的材料构成,电荷存储在一根根比头发还要细的微型静电网中。当手指点击屏幕,会从接触点吸收小量电流,造成角落电极的压降,利用感应人体微弱电流的方式来达到触控的目的。(这是为什么当你带上手套触摸屏幕时,没有反应的原因),下图可以清晰的说明电容屏的工作原理。

- 阅读剩余部分 -

转自: https://blog.csdn.net/qq_27677015/article/details/53415848

一、描述

ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。为了改变这种局面,Linux社区的大牛们参考了PowerPC等体系架构中使用的Flattened Device Tree(FDT),也采用了Device Tree结构,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)。在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):

  • CPU的数量和类别
  • 内存基地址和大小
  • 总线和桥
  • 外设连接
  • 中断控制器和中断使用情况
  • GPIO控制器和GPIO使用情况
  • Clock控制器和Clock使用情况

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

通常由.dts文件以文本方式对系统设备树进行描述,经过Device Tree Compiler(dtc)将dts文件转换成二进制文件binary device tree blob(dtb),.dtb文件可由Linux内核解析,有了device tree就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。

- 阅读剩余部分 -

转自: https://cloud.tencent.com/developer/article/1855138

整理出了6种驱动开发时与设备注册、设备树相关的调试方法,彼此间没有优先级之分,每种方法不一定是最优解,但可以作为一种debug查找问题的手段,快速定位问题原因。例如在芯片验证时,不同时钟频率下系统启动情况摸底时,U-Boot fdt命令可以方便快捷的帮助我们完成这个实验。

- 阅读剩余部分 -

转自: https://blog.csdn.net/daoshuti/article/details/72983620

AndroidTP驱动之(三)input

0. 前情提要

上文中我们已经完成对设备树的解析工作,获取了tp的硬件信息。 我们知道Linux内核上报输入事件是通过input子系统,TP作为输入设备自然要通过input子系统来上报。 现在的kernel都支持多点触控了,多点触控的协议有两种协议A和协议B,协议A不需要硬件支持,协议B需要硬件支持。 这里我们不讨论协议A/B,有兴趣的同学自己Google。 下面我们就初始化input子系统。

1. probe函数

在probe函数的开始我们先定义了两个结构体指针。为后面的input初始化做准备。 这两个以结构体,一个是input_dev,另一个是自定义的结构体mytp_data。 input_dev自不别说,我们的TP通过这个结构体来上报数据。 mytp_data是我们TP驱动中自定义的结构体,方便我们来编写程序。 具体定义如下:

struct mytp_data
{
    struct i2c_client *client;
    struct input_dev *input_dev;
    struct mytp_platform_data *pdata;
};

如上所示,现在mytp_data结构体的成员很少只有三个,后期随着功能的增加,我们还会添加。 这三个结构体成员都是很重要的,i2c_client用来和TP通信,input_dev用来上报TP数据,mytp_platform_data存放了硬件平台的数据。 probe函数中input初始化代码具体如下:

struct input_dev *input_dev;
struct mytp_data *data;

data = devm_kzalloc(&client->dev, sizeof(struct mytp_data), GFP_KERNEL);
if (!data)
{
    PRINT_INFO("[MEMORY]Failed to allocate memory");
    return -ENOMEM;
}

input_dev = input_allocate_device();
if (!input_dev)
{
    PRINT_INFO("[INPUT]Failed to allocate input device");
    return -ENOMEM;
}

data->input_dev = input_dev;
data->client = client;
data->pdata = pdata;

mytp_input_dev_init(client, data, input_dev, pdata);

代码的逻辑很清晰,完成结构体变量的初始化后调用mytp_input_dev_init()函数。 这个函数就是用来初始化input的。具体见下一小节。

2. mytp_input_dev_init函数

函数一开始显示初始化input_dev结构体, 然后将驱动私有的mytp_data结构体指针变量data,通过input_set_drvdata和i2c_set_clientdata函数放入input_dev和client中。 之后通过__set_bit函数设置上报的数据类型,和键值。 通过宏MYTP_MT_PROTOCOL_B_EN选择多点上报的方式。 通过input_register_device注册input设备。

static int mytp_input_dev_init( struct i2c_client *client, struct mytp_data *data,  struct input_dev *input_dev, struct mytp_platform_data *pdata)
{
    int  err, len;

    /* Init and register Input device */
    input_dev->name = MYTP_DRIVER_NAME;
    input_dev->id.bustype = BUS_I2C;
    input_dev->dev.parent = &client->dev;

    input_set_drvdata(input_dev, data);
    i2c_set_clientdata(client, data);

    __set_bit(EV_KEY, input_dev->evbit);
    if (data->pdata->have_key)
    {
        PRINT_INFO("set key capabilities");
        for (len = 0; len < data->pdata->key_number; len++)
        {
            input_set_capability(input_dev, EV_KEY, data->pdata->keys[len]);
        }
    }
    __set_bit(EV_ABS, input_dev->evbit);
    __set_bit(BTN_TOUCH, input_dev->keybit);
    __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);

#if MYTP_MT_PROTOCOL_B_EN
    input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT);
#else
    input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0f, 0, 0);
#endif
    input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);

    err = input_register_device(input_dev);
    if (err)
    {
        PRINT_INFO("Input device registration failed");
        goto free_inputdev;
    }

    return 0;

free_inputdev:
    input_free_device(input_dev);
    return err;
}

3. remove函数

input_unregister_device(data->input_dev);

注销input设备。 另注:

前文中i2c设备在exit函数中注销。  
而设备树,只是从中获取硬件信息,不能注销。  
内存申请时,使用的是devm\_kzalloc函数,其会在驱动模块注销主动释放内存,所以不需要手动free。  

用到的头文件和宏

头文件

#include <linux/input.h>
#include <linux/input/mt.h>

#define MYTP_MT_PROTOCOL_B_EN   1

MYTP_DRIVER_NAME宏,前文已经定义过。 End

至此TP的input初始化完成。

转自: https://blog.csdn.net/daoshuti/article/details/72972350

AndroidTP驱动之(二)设备树解析

0. 前情提要

上文中我们已经初步搭好了TP驱动的框架。当然我们还缺了重要的部分input子系统,这个不急,我们下一篇文章再叙。 本文我们来完成TP driver中对设备树的解析。

1. probe函数

我们知道驱动匹配有四种方式:设备树匹配、设备名称匹配、设备ID匹配、ACPI方式匹配。 这里我们只用设备树匹配,这也是Android驱动中最常用的匹配方式。 我们知道当驱动和设备树匹配的时候,probe函数就会被执行。 这时一般首先进行设备树的解析,从设备树中获取设备的硬件信息。

    struct mytp_platform_data *pdata;
    int err;

    if (client->dev.of_node)
    {
        pdata = devm_kzalloc(&client->dev,
                sizeof(struct mytp_platform_data),
                GFP_KERNEL);
        if (!pdata)
        {
            PRINT_INFO("[MEMORY]Failed to allocate memory");
            return -ENOMEM;
        }
        err = mytp_parse_dt(&client->dev, pdata);
        if (err)
        {
            PRINT_INFO("[DTS]DT parsing failed");
        }
    }
    else
    {
        return -1;
    }

    if (!pdata)
    {
        PRINT_INFO("Invalid pdata");
        return -EINVAL;
    }

从代码中我们很清晰的看到,我们先是定义了一个结构体指针pdata。这是用来存放平台数据。 接着,我们为这结构体申请了内存,并做异常处理。 之后调用mytp_parse_dt函数对设备树进行解析。也做了相应的异常处理。

1.1 mytp_platform_data结构体

struct mytp_platform_data
{
    u32 irq_gpio;
    u32 irq_gpio_flags;
    u32 reset_gpio;
    u32 reset_gpio_flags;
    bool have_key;
    u32 key_number;
    u32 keys[4];
    u32 key_y_coord;
    u32 key_x_coords[4];
    u32 x_max;
    u32 y_max;
    u32 x_min;
    u32 y_min;
    u32 max_touch_number;
};

头四个结构体成员变量,分别是两个gpio所使用的,一个是irq中断,一个是TP的reset复位引脚。 之后的bool变量,是TP按键的标志,表示是否支持TP按键。 紧接着的四个key_打头的变量是提供给TP按键处理函数用的。 x_max、x_min、y_max、y_min限定了tp的触摸范围。 最后的一个变量限定了tp支持的最大多点触控数目。

1.2 mytp_parse_dt函数

这个函数就是用来解析设备树的。 其中的mytp_get_dt_coords函数是自己实现的,用来解析设备树中的数组的。具体实现见1.3小节。

static int mytp_parse_dt(struct device *dev, struct mytp_platform_data *pdata)
{
    int rc;
    struct device_node *np = dev->of_node;
    u32 temp_val;

    rc = mytp_get_dt_coords(dev, "focaltech,display-coords", pdata);
    if (rc)
        PRINT_INFO("Unable to get display-coords");

    /* key */
    pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
    if (pdata->have_key)
    {
        rc = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number);
        if (rc)
        {
            PRINT_INFO("Key number undefined!");
        }
        rc = of_property_read_u32_array(np, "focaltech,keys",
                pdata->keys, pdata->key_number);
        if (rc)
        {
            PRINT_INFO("Keys undefined!");
        }
        rc = of_property_read_u32(np, "focaltech,key-y-coord", &pdata->key_y_coord);
        if (rc)
        {
            PRINT_INFO("Key Y Coord undefined!");
        }
        rc = of_property_read_u32_array(np, "focaltech,key-x-coords",
                pdata->key_x_coords, pdata->key_number);
        if (rc)
        {
            PRINT_INFO("Key X Coords undefined!");
        }
        PRINT_INFO("%d: (%d, %d, %d), [%d, %d, %d][%d]",
                pdata->key_number, pdata->keys[0], pdata->keys[1], pdata->keys[2],
                pdata->key_x_coords[0], pdata->key_x_coords[1], pdata->key_x_coords[2],
                pdata->key_y_coord);
    }

    /* reset, irq gpio info */
    pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0, &pdata->reset_gpio_flags);
    if (pdata->reset_gpio < 0)
    {
        PRINT_INFO("Unable to get reset_gpio");
    }

    pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0, &pdata->irq_gpio_flags);
    if (pdata->irq_gpio < 0)
    {
        PRINT_INFO("Unable to get irq_gpio");
    }

    rc = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
    if (!rc)
    {
        pdata->max_touch_number = temp_val;
        PRINT_INFO("max_touch_number=%d", pdata->max_touch_number);
    }
    else
    {
        PRINT_INFO("Unable to get max-touch-number");
        pdata->max_touch_number = MYTP_MAX_POINTS;
    }

    return 0;
}

1.3 mytp_get_dt_coords函数

static int mytp_get_dt_coords(struct device *dev, char *name,
        struct mytp_platform_data *pdata)
{
    u32 coords[MYTP_COORDS_ARR_SIZE];
    struct property *prop;
    struct device_node *np = dev->of_node;
    int coords_size, rc;

    prop = of_find_property(np, name, NULL);
    if (!prop)
        return -EINVAL;
    if (!prop->value)
        return -ENODATA;

    coords_size = prop->length / sizeof(u32);
    if (coords_size != MYTP_COORDS_ARR_SIZE)
    {
        PRINT_INFO("invalid %s", name);
        return -EINVAL;
    }

    rc = of_property_read_u32_array(np, name, coords, coords_size);
    if (rc && (rc != -EINVAL))
    {
        PRINT_INFO("Unable to read %s", name);
        return rc;
    }

    if (!strcmp(name, "focaltech,display-coords"))
    {
        pdata->x_min = coords[0];
        pdata->y_min = coords[1];
        pdata->x_max = coords[2];
        pdata->y_max = coords[3];
    }
    else
    {
        PRINT_INFO("unsupported property %s", name);
        return -EINVAL;
    }

    return 0;
}

End

至此我们完成了对设备树的解析。