linux驅動開發:ft5x06的touch screen的IIC驅動程序編寫

觸摸屏屬於一個標準的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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章