linux驱动开发:ft5x06的touch screen的IIC驱动程序编写

版权声明:学习记录,积少成多 https://blog.csdn.net/changliang7731/article/details/53147903

触摸屏属于一个标准的input dev.所以我们按照输入子系统的流程来实现驱动开发。
实际板子与CTPM的通讯连接基于IIC总线,所以我们需要把驱动挂载到IIC总线下面去,也就是注册IIC驱动到iic_core.c中去。
实例化一个IIC设备有多种方式,仿照上一次的24cxx IIC设备的创建,我们来实现ft5x06IIC设备的创建。
因实际板子上TS IC使用的是ft5206,所以先实例化设备:

好像win10下面的自带浏览器有点问题,代码段怪怪的,蛋疼。

这里有一个头文件:

#ifndef __FT5X06_H__
#define __FT5X06_H__

#define FT5X0X_REG_FIRMID       0xa6




struct ft5x06_platform_data {
    uint32_t gpio_irq;          // IRQ port
    uint32_t irq_cfg;

    uint32_t gpio_wakeup;       // Wakeup support
    uint32_t wakeup_cfg;

    uint32_t gpio_reset;        // Reset support
    uint32_t reset_cfg;

    int screen_max_x;
    int screen_max_y;
    int pressure_max;
};
#endif
static struct ft5x06_platform_data ft5x06_pdata =
{
    .gpio_irq       = S5PV210_GPH1(6),
    .irq_cfg        = S3C_GPIO_SFN(0xf),
    .screen_max_x   = 800,
    .screen_max_y   = 480,
    .pressure_max   = 200,
};
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
    /* To Be Updated */
    { I2C_BOARD_INFO("ft5x06",(0x70 >> 1)),
      .platform_data = &ft5x06_pdata,
    },
};

i2c_register_board_info(2, smdkv210_i2c_devs2,
            ARRAY_SIZE(smdkv210_i2c_devs2));

首先,设备如何初始化是和驱动紧密相关的,所以在初始化一个设备时,一定需要读它对应的驱动是如何编写的,需要传递哪些参数。我们知道TS的IIC是挂载iic2上的,而上一个24cxx是挂在iic0上的,所以有i2c_register_board_info()里的第一个参数传入0还是2的区别。

驱动的编写。
上一章我们讲了多点触摸的协议。知道了根据触摸屏硬件是否支持,我们可以使用A类或者B类协议来实现多点触摸数据的上报。
这里的驱动使用A类协议上报数据.

中断:
中断服务程序分为两个部分:顶半部和底半部,通常我们在顶半部处理比较紧急的代码,底半部处理相对不紧急的代码。如果你的中断处理函数很大很长,都放在顶半部里是很不明智的。

底半部的实现机制:
1)tasklist:本质工作在进程上下文
2)工作者队列:把推后的work交给内核的一个线程去调度,允许重新调度与睡眠。
3)软中断
我们的中断处理程序中有用到工作者队列,所以这边就提一下。

驱动主要上报ABS事件。point(x,y),描述一个点最小的描述单元是x,y坐标。所以我们至少需要上报这两个参数。

触摸会产生中断,中断会使能工作者队列。工作者队列会读数据,上报数据。整个流程得以实现。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>
#include <linux/input/ft5x06.h>
#include <linux/input/mt.h>
#include <plat/gpio-cfg.h>

#define FT5X06_TP_MAX           5
#define TOUCH_MAX_X             0x700
#define TOUCH_MAX_Y             0x400


#define FT5X06_NAME             "ft5x06"    

static const struct i2c_device_id ft5x06_id[] =
{
    {"ft5206",0},
    {}
};

static int swap_xy = 0;
static int scal_xy = 0;

struct ft5x06_event 
{
    int touch_point;

    u16 x[FT5X06_TP_MAX];
    u16 y[FT5X06_TP_MAX];

    u16 pressure;
};

struct ft5x06_dev {
    struct input_dev    *i_dev;//for input dev use as inout dev 
    struct i2c_client   * ft_client;
    struct ft5x06_platform_data *pdata;
    struct ft5x06_event event;

    //irq bh use
    struct work_struct work;
    struct workqueue_struct *queue;
};

static struct ft5x06_dev *ft5x06_struct = NULL;

static int ft5x06_read_reg(u8 addr, u8 *pdata) 
{
    u8 buf[4] = {0};
    int ret;
    struct i2c_msg msgs[2];


    buf[0] = addr;
    memset(msgs,0,sizeof(msgs));

    msgs[0].addr    = ft5x06_struct->ft_client->addr;
    msgs[0].flags   = 0;//W cmd
    msgs[0].buf     = buf;//subadr
    msgs[0].len     = 1;    

    msgs[1].addr    = ft5x06_struct->ft_client->addr;
    msgs[1].flags   = 1;//R cmd
    msgs[1].buf     = buf;
    msgs[1].len     = 1;

    ret = i2c_transfer(ft5x06_struct->ft_client->adapter, msgs, 2);
    if(ret < 0)
    {
        printk("1.Ft5x06 iic transfer data fail.\n");
    }
    *pdata = buf[0];

    return ret;
}
static int ft5x06_i2c_rxdata(char *rxdata, int length)
{
    int ret;
    struct i2c_msg msgs[2];

    memset(msgs,0,sizeof(msgs));

    msgs[0].addr    = ft5x06_struct->ft_client->addr;
    msgs[0].flags   = 0;//W cmd
    msgs[0].buf     = rxdata;//subadr
    msgs[0].len     = 1;    

    msgs[1].addr    = ft5x06_struct->ft_client->addr;
    msgs[1].flags   = 1;//R cmd
    msgs[1].buf     = rxdata;
    msgs[1].len     = length;

    ret = i2c_transfer(ft5x06_struct->ft_client->adapter, msgs, 2);
    if(ret < 0)
    {
        printk("2.Ft5x06 iic transfer data fail.\n");
    }

    return ret;
}

static void ft5x06_release(struct ft5x06_dev *pdev)
{
    input_mt_sync(pdev->i_dev);
    input_sync(pdev->i_dev);
}

static int ft5x06_read_data(struct ft5x06_dev *pdev)
{
    struct ft5x06_event *event = &pdev->event;
    u8 buf[32] = {0};
    int ret;

    ret = ft5x06_i2c_rxdata(buf, 31);

    if(ret < 0)
    {
        printk("%s: read touch data failed, %d\n", __func__, ret);
        return ret;
    }

    memset(event, 0, sizeof(struct ft5x06_event));

    event->touch_point = buf[2] & 0x07;

    if(!event->touch_point)
    {
        ft5x06_release(pdev);
        return 1;
    }
    switch(event->touch_point)
    {
        case 5:
            event->x[4] = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
            event->y[4] = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
        case 4:
            event->x[3] = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
            event->y[3] = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
        case 3:
            event->x[2] = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
            event->y[2] = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
        case 2:
            event->x[1] = (s16)(buf[0x09] & 0x0F)<<8 | (s16)buf[0x0a];
            event->y[1] = (s16)(buf[0x0b] & 0x0F)<<8 | (s16)buf[0x0c];
        case 1:
            event->x[0] = (s16)(buf[0x03] & 0x0F)<<8 | (s16)buf[0x04];
            event->y[0] = (s16)(buf[0x05] & 0x0F)<<8 | (s16)buf[0x06];
            break;
        default:
            printk("%s: invalid touch data, %d\n", __func__, event->touch_point);
            return -1;
    }
    event->pressure = 200;

    return 0;

}

static void ft5x06_report(struct ft5x06_dev *pdev) 
{
    struct ft5x06_event *event = &pdev->event;
    int x, y;
    int i;

    for (i = 0; i < event->touch_point; i++)
    {
        if(swap_xy)
        {
            x = event->y[i];
            y = event->x[i];
        }
        else
        {
            x = event->x[i];
            y = event->y[i];
        }

        if(scal_xy)
        {
            x = (x * pdev->pdata->screen_max_x) / TOUCH_MAX_X;
            y = (y * pdev->pdata->screen_max_y) / TOUCH_MAX_Y;
        }

        input_report_abs(pdev->i_dev, ABS_MT_POSITION_X, x);
        input_report_abs(pdev->i_dev, ABS_MT_POSITION_Y, y);

        input_report_abs(pdev->i_dev, ABS_MT_PRESSURE, event->pressure);
        input_report_abs(pdev->i_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
        input_report_abs(pdev->i_dev, ABS_MT_TRACKING_ID, i);

        input_mt_sync(pdev->i_dev);
    }

    input_sync(pdev->i_dev);
}

static int ft5x06_read_fw_ver(unsigned char *val)
{
    int ret;
    *val = 0xff;
    ret = ft5x06_read_reg(FT5X0X_REG_FIRMID, val);

    printk("6.read fw version %2x.\n",*val);
    return ret;
}

static void ft5x06_set_irq_work(struct work_struct *work)
{
    struct ft5x06_dev *pdev = container_of(work, struct ft5x06_dev, work);

    if(!ft5x06_read_data(pdev))
    {   
        ft5x06_report(pdev);
    }

    enable_irq(pdev->ft_client->irq);
}

static irqreturn_t ft5x06_interrupt(int irq, void *dev_id) 
{
    struct ft5x06_dev *pdev = ft5x06_struct;

    disable_irq_nosync(pdev->ft_client->irq);

    if(!work_pending(&pdev->work))
    {
        queue_work(pdev->queue, &pdev->work);
    }

    return IRQ_HANDLED;
}


/*****int (*probe)(struct i2c_client *, const struct i2c_device_id *)*****/

static int ft5x06_probe(struct i2c_client *uc_i2c_client, const struct i2c_device_id * uc_i2c_id_table)
{
    int err=0;
    unsigned char val;
    //check iic
    if (!i2c_check_functionality(uc_i2c_client->adapter, I2C_FUNC_I2C)) 
    {
        err = -ENODEV;
        goto FAIL_CHECK_FUNC;
    }

    //check platdata
    if(!uc_i2c_client->dev.platform_data)
    {   
        err = -ENODATA;
        goto FAIL_NO_PLATFORM_DATA;
    }

    //allooc buf  
    ft5x06_struct = kzalloc(sizeof(struct ft5x06_dev),GFP_KERNEL);
    if(!ft5x06_struct)
    {
        err=-ENOMEM;
        goto FAIL_KZALLOC;
    }
    //initial start
    ft5x06_struct->ft_client =uc_i2c_client;
    ft5x06_struct->pdata = uc_i2c_client->dev.platform_data;

    if(ft5x06_struct->pdata->gpio_irq)
    {
        ft5x06_struct->ft_client->irq =gpio_to_irq(ft5x06_struct->pdata->gpio_irq);
    }
    else
    {
        err = -ENODATA;
        printk("3.the platformdata no irq data\n");
        goto FAIL_NO_IRQ_DATA;
    }

    if(ft5x06_struct->pdata->irq_cfg)
    {
        s3c_gpio_cfgpin(ft5x06_struct->pdata->gpio_irq, ft5x06_struct->pdata->irq_cfg);
        s3c_gpio_setpull(ft5x06_struct->pdata->gpio_irq, S3C_GPIO_PULL_NONE);
    }

    INIT_WORK(&ft5x06_struct->work,ft5x06_set_irq_work);

    i2c_set_clientdata(ft5x06_struct->ft_client,ft5x06_struct->pdata);

    ft5x06_struct->queue = create_singlethread_workqueue(dev_name(&ft5x06_struct->ft_client->dev));

    if(!ft5x06_struct->queue)
    {
        err = -ESRCH;
        goto FAIL_CREAT_SINGLE_QUEUE;
    }
    //initial the input dev
    ft5x06_struct->i_dev = input_allocate_device();
    if(!ft5x06_struct->i_dev)
    {
        err = -ENOMEM;
        printk("4.alloc input dev fail.\n");
        goto FAIL_ALLOC_INPUT_DEV;
    }

    //initial the input data
    set_bit(EV_SYN, ft5x06_struct->i_dev->evbit);
    set_bit(EV_ABS, ft5x06_struct->i_dev->evbit);
    //set_bit(EV_KEY, ft5x06_struct->i_dev->evbit);
    //Multi Touch
    set_bit(ABS_MT_TRACKING_ID, ft5x06_struct->i_dev->absbit);
    set_bit(ABS_MT_TOUCH_MAJOR, ft5x06_struct->i_dev->absbit);
    set_bit(ABS_MT_WIDTH_MAJOR, ft5x06_struct->i_dev->absbit);
    set_bit(ABS_MT_POSITION_X , ft5x06_struct->i_dev->absbit);
    set_bit(ABS_MT_POSITION_Y , ft5x06_struct->i_dev->absbit);

    input_set_abs_params(ft5x06_struct->i_dev, ABS_MT_POSITION_X, 0, ft5x06_struct->pdata->screen_max_x, 0, 0);
    input_set_abs_params(ft5x06_struct->i_dev, ABS_MT_POSITION_Y, 0, ft5x06_struct->pdata->screen_max_y, 0, 0);
    input_set_abs_params(ft5x06_struct->i_dev, ABS_MT_TOUCH_MAJOR, 0,ft5x06_struct->pdata->pressure_max, 0, 0);
    input_set_abs_params(ft5x06_struct->i_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
    input_set_abs_params(ft5x06_struct->i_dev, ABS_MT_TRACKING_ID, 0, FT5X06_TP_MAX, 0, 0);

    ft5x06_struct->i_dev->name = FT5X06_NAME;
    ft5x06_struct->i_dev->id.bustype =BUS_I2C;
    ft5x06_struct->i_dev->id.vendor  =0x12FA;
    ft5x06_struct->i_dev->id.product =0x2143;
    ft5x06_struct->i_dev->id.version =0x0100;

    err = input_register_device(ft5x06_struct->i_dev);
    if(err) 
    {
        printk("5.register input dev fail.\n");
        goto FAIL_INPUT_REGISTER_DEV;
    }

    msleep(3);

    err = ft5x06_read_fw_ver(&val);
    if(err < 0)
    {
        goto FAIL_READ_FW_VER;
    }

    err = request_irq(ft5x06_struct->ft_client->irq, ft5x06_interrupt,IRQ_TYPE_EDGE_FALLING, "ft5x06",NULL);

    if(err < 0)
    {
        goto FAIL_REQUEST_IRQ;
    }


    printk("FT5x06 driver probe success!\n");

    return 0;
FAIL_REQUEST_IRQ:
    disable_irq(ft5x06_struct->ft_client->irq);
    free_irq(ft5x06_struct->ft_client->irq,NULL);
FAIL_READ_FW_VER:
FAIL_INPUT_REGISTER_DEV:
    input_unregister_device(ft5x06_struct->i_dev);  
FAIL_ALLOC_INPUT_DEV:
    input_free_device(ft5x06_struct->i_dev);
FAIL_CREAT_SINGLE_QUEUE:
    cancel_work_sync(&ft5x06_struct->work);
    destroy_workqueue(ft5x06_struct->queue);
    i2c_set_clientdata(ft5x06_struct->ft_client,NULL);
FAIL_NO_IRQ_DATA:
FAIL_KZALLOC:
    kfree(ft5x06_struct);
FAIL_NO_PLATFORM_DATA:
FAIL_CHECK_FUNC:

    return err;

}
/*****int (*remove)(struct i2c_client *)*****/
static int ft5x06_remove(struct i2c_client *uc_i2c_client)
{
    disable_irq(ft5x06_struct->ft_client->irq);
    free_irq(ft5x06_struct->ft_client->irq,NULL);

    input_unregister_device(ft5x06_struct->i_dev);  

    input_free_device(ft5x06_struct->i_dev);

    cancel_work_sync(&ft5x06_struct->work);
    destroy_workqueue(ft5x06_struct->queue);

    i2c_set_clientdata(ft5x06_struct->ft_client,NULL);

    kfree(ft5x06_struct);

    return 0;
}
static struct i2c_driver ft5x06_drv =
{
    .driver =
    {
        .name = FT5X06_NAME,    
        .owner= THIS_MODULE,
    },
    .probe  = ft5x06_probe,
    .remove = ft5x06_remove,
    //match use
    .id_table = ft5x06_id,
};

int __init  ft5x06_init(void)
{
    i2c_add_driver(&ft5x06_drv);    
    return 0;
}

void __exit ft5x06_exit(void)
{
    i2c_del_driver(&ft5x06_drv);
}






module_init(ft5x06_init);
module_exit(ft5x06_exit);
MODULE_LICENSE("GPL");

测试程序其实可以用24cxx的测试程序。其实没差。event的数据格式之前有分析过,这边也是一样的。

测试程序:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<linux/input.h>


struct input__event
{
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

int fd;

int main()
{
    struct input__event ev_key;
    fd = open("/dev/input/event0",O_RDWR);

    printf("fd =%d,\n",fd);
    while(1)
    {
        read(fd,&ev_key,sizeof(ev_key));
        if(ev_key.type == EV_ABS) 
        {
            printf("time:%ld s,%ld us,type: %d,code:%d,vale:%d.\n",ev_key.time.tv_sec,ev_key.time.tv_usec,ev_key.type,ev_key.code,ev_key.value);
        }
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/changliang7731/article/details/53147903