触摸按键文件 /kernel/driver/input/touchscreen/ft6x0x_ts.c针对触摸屏按键驱动,我们来做一些简单的分析和了解。
static int ft6x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) // 探测函数{struct jztsc_platform_data *pdata = NULL;struct input_dev *input_dev;
int err = 0;uint8_t uc_reg_value, uc_reg_addr;
pdata = (struct jztsc_platform_data *)client->dev.platform_data; // 获取I2C客户端数据if (!pdata) {dev_info(&client->dev, "ERROR : %s --> platform data is NULL! will exit!\n",__func__);err = -EINVAL;goto exit_pdata_is_null;}
if (! i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { // 判定适配器功能err = -ENODEV;goto exit_check_functionality_failed;}
ft6x0x_ts = kzalloc(sizeof(struct ft6x0x_ts_data), GFP_KERNEL); // 申请触摸屏按键内存空间if (!ft6x0x_ts) {err = -ENOMEM;goto exit_alloc_data_failed;}
ft6x0x_ts->pdata = pdata; // 设置触摸屏按键数据
ft6x0x_gpio_init(ft6x0x_ts, client); // 初始化引脚功能
i2c_set_clientdata(client, ft6x0x_ts); // 设置 I2C 数据
ft6x0x_ts->power = regulator_get(&client->dev, " vtsc"); // 更改电源输入引脚if (IS_ERR(ft6x0x_ts->power)) {dev_warn(&client->dev, "get regulator vtsc failed, try board power interface.\n");if (pdata->power_init) {pdata-> power_init(&client->dev);} else {dev_warn(&client->dev, "board power control interface is NULL !\n");}}
atomic_set(&ft6x0x_ts->regulator_enabled, 0); //使能校准器
ft6x0x_ts_power_on(ft6x0x_ts); // 打开电源
/*make sure CTP already finish startup process */ft6x0x_ts_reset(ft6x0x_ts); //重新设置引脚状态msleep(100);
/*get some register information */uc_reg_addr = FT6X0X_REG_FW_VER;
err = ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1); // 接收客户端信息if(err < 0){dev_info(&client->dev, "ft6x0x_ts probe failed\n");goto exit_read_reg_failed;}
#if defined(CONFIG_FT6X0X_EXT_FUNC)err = fts_ctpm_auto_upgrade(client); // 自动更新客户端数据if (err < 0) {dev_info(&client->dev, "fts_ctpm_auto_upgrade return %d\n",err);}#endif
dev_dbg(&client->dev, "[FTS] Firmware version = 0x%x\n", uc_reg_value);
uc_reg_addr = FT6X0X_REG_POINT_RATE;err = ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1); // 接收客户端 数据if(err < 0){dev_info(&client->dev, "ft6x0x_ts probe failed\n");goto exit_read_reg_failed;}dev_dbg(&client->dev, "[FTS] report rate is %dHz.\n", uc_reg_value * 10);
uc_reg_addr = FT6X0X_REG_THGROUP;err = ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1); // 接收客户端 数据if(err < 0){dev_info(&client->dev, "ft6x0x_ts probe failed\n");goto exit_read_reg_failed;}dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", uc_reg_value * 4);
// ft6x0x_set_reg(ft6x0x_ts, FT6X0X_REG_THCAL, 4);
mutex_init(&ft6x0x_ts->lock);mutex_init(&ft6x0x_ts->rwlock);//初始化客户端设备名client->dev.init_name=client->name;
#ifndef FTTP_THREAD_MODEINIT_WORK(&ft6x0x_ts->work, ft6x0x_work_handler); // 初始化工作队列ft6x0x_ts->workqueue = create_singlethread_workqueue("ft6x0x_tsc"); // 创建工作队列if (!ft6x0x_ts->workqueue) {dev_info(&client->dev, "create_singlethread_workqueue failed!\n");goto exit_create_singlethread_workqueue;}#elseft6x0x_ts->thread = kthread_run( touch_event_handler, (void *)ft6x0x_ts, "ft6x0x_ts"); // 创建并启动线程if (IS_ERR(ft6x0x_ts->thread))goto exit_create_singlethread_workqueue;#endif
client->irq = gpio_to_irq(ft6x0x_ts->gpio.irq->num); // 将gpio_pin 映射为中断形式// 申请中断处理函数 ft6x0x_ts_interrupterr = request_irq(client->irq, ft6x0x_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "ft6x0x_ts", ft6x0x_ts);if (err < 0) {dev_err(&client->dev, "ft6x0x_probe: request irq failed\n");goto exit_irq_request_failed;}//初始化相关数据ft6x0x_ts->irq = client->irq;ft6x0x_ts->client = client;ft6x0x_ts->x_max = pdata->x_max;ft6x0x_ts->y_max = pdata->y_max;ft6x0x_ts->is_suspend = 0;
disable_irq(client->irq);#ifdef CONFIG_KEY_SPECIAL_POWER_KEYsetup_timer(&ft6x0x_ts->tp_blk_delay, tp_off_blk_timer, (unsigned long)ft6x0x_ts); // 设置背光灯处理函数#endifinput_dev = input_allocate_device(); //分配内存空间if (!input_dev) {err = -ENOMEM;dev_err(&client->dev, "failed to allocate input device\n");goto exit_input_dev_alloc_failed;}// 设置输入设备ft6x0x_ts->input_dev = input_dev;
// 设置触摸按键支持的事件类型// set_bit(KEY_MENU, input_dev->keybit);set_bit(KEY_BACK, input_dev->keybit);
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);set_bit(ABS_MT_POSITION_X, input_dev->absbit);set_bit(ABS_MT_POSITION_Y, input_dev->absbit);set_bit(ABS_MT_TRACKING_ID,input_dev->absbit);set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
// 初始化输入设备信息input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ft6x0x_ts->x_max, 0, 0);input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ft6x0x_ts->y_max, 0, 0);input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 250, 0, 0);input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
set_bit(EV_KEY, input_dev->evbit);set_bit(EV_ABS, input_dev->evbit);
input_dev->name = FTS_NAME;err = input_register_device(input_dev); // 注册输入子系统设备if (err) {dev_err(&client->dev, "ft6x0x_ts_probe: failed to register input device: %s\n", dev_name(&client->dev));goto exit_input_register_device_failed;}
#ifdef CONFIG_HAS_EARLYSUSPENDft6x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;ft6x0x_ts->early_suspend.suspend = ft6x0x_ts_suspend;ft6x0x_ts->early_suspend.resume = ft6x0x_ts_resume;register_early_suspend(&ft6x0x_ts->early_suspend);#endif
#if defined(CONFIG_FT6X0X_EXT_FUNC)ft6x0x_create_sysfs(client); // 创建文件系统#endif
enable_irq(client->irq); // 使能中断return 0;
exit_input_register_device_failed:input_free_device(input_dev); // 释放设备
exit_input_dev_alloc_failed:free_irq(client->irq, ft6x0x_ts); // 释放中断
exit_irq_request_failed:#ifndef FTTP_THREAD_MODEdestroy_workqueue(ft6x0x_ts->workqueue); // 销毁工作队列#elsekthread_stop(ft6x0x_ts->thread); // 结束线程运行#endifexit_read_reg_failed:exit_create_singlethread_workqueue:ft6x0x_ts_power_off(ft6x0x_ts); // 关闭电源供电if (!IS_ERR(ft6x0x_ts->power))regulator_put(ft6x0x_ts->power); // 释放校准器资源
gpio_free(ft6x0x_ts->gpio.irq->num); // 释放gpio中断端gpio_free(ft6x0x_ts->gpio.wake->num); //释放gpio 唤醒端i2c_set_clientdata(client, NULL); //将I2C客户端数据清空kfree(ft6x0x_ts); // 释放触摸按键内存空间
exit_alloc_data_failed:exit_check_functionality_failed:exit_pdata_is_null:return err;
}
探测函数主要做一些驱动的初始化工作,固件自动更新的处理、初始化工作队列、触摸按键事件的处理、中断的处理和背光的处理等工作,下面来分别介绍这些部分的功能。
下面我们了解一下它的核心部分,固件自动更新部分的代码;
int fts_ctpm_auto_upgrade(struct i2c_client *client){int i_ret;u8 uc_host_fm_ver = FT6x06_REG_FW_VER;u8 uc_tp_fm_ver;
struct ft6x0x_ts_data *ts = (struct ft6x0x_ts_data *) i2c_get_clientdata(client); // 获得I2C 客户端数据结构体指针if ((NULL == ts) || (0 == ts->pdata->fw_ver)) {return -EINVAL;}
ft6x06_read_reg(client, FT6x06_REG_FW_VER, &uc_tp_fm_ver); // 接收客户端数据uc_host_fm_ver = fts_ctpm_get_i_file_ver(); //获得固件参数文件列表
if (0x00 == uc_host_fm_ver) {return -ENOENT;}
/*the firmware in touch panel maybe corrupted *//*the firmware in host flash is new, need upgrade */if ( c_tp_fm_ver == FT6x06_REG_FW_VER) || uc_tp_fm_ver != uc_host_fm_ver ){msleep(100);dev_dbg(&client->dev, "[FTS] uc_tp_fm_ver = 0x%x, uc_host_fm_ver = 0x%x\n", uc_tp_fm_ver, uc_host_fm_ver);i_ret = fts_ctpm_fw_upgrade_with_i_file(client); // 更新固件参数文件列表if (i_ret == 0) {msleep(300);uc_host_fm_ver = fts_ctpm_get_i_file_ver(); //获得固件参数文件列表dev_dbg(&client->dev, "[FTS] upgrade to new version 0x%x\n", uc_host_fm_ver);} else {pr_err("[FTS] upgrade failed ret=%d.\n", i_ret);return -EIO;}}dev_info(&client->dev,"OLD VERSION = 0x%x\n", uc_tp_fm_ver);ft6x06_read_reg(client, FT6x06_REG_FW_VER, &uc_tp_fm_ver); // 接收客户端数据dev_info(&client->dev,"NEW VERSION = 0x%x\n", uc_tp_fm_ver);return 0;}
下面我们来介绍上面函数调用的几个重要的函数,来简单分析下它的功能。
首先看看 i2c_get_clientdata函数,其实它是调用 dev_get_drvdata函数,下面我们来看看它的实现代码,其实只是做了一个简单的动作。/** These exports can't be _GPL due to .h files using this within them, and it* might break something that was previously working...*/void * dev_get_drvdata(const struct device *dev){if (dev && dev->p)return dev->p->driver_data; // 该函数主要是获取驱动数据结构体指针return NULL;}再来看看另外一个函数 ft6x06_read_reg其实是调用了 ft6x0x_i2c_Read函数,看看它具体做了些什么工作。
int ft6x0x_i2c_Read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen){int ret;
struct ft6x0x_ts_data *ft6x0x_ts = i2c_get_clientdata(client); // 获得客户端数据结构体指针if (writelen > 0) {struct i2c_msg msgs[] = {{.addr = client->addr,.flags = 0, // 发送数据.len = writelen,.buf = writebuf,},{.addr = client->addr,.flags = I2C_M_RD, // 接收数据.len = readlen,.buf = readbuf,},};ret = i2c_transfer(client->adapter, msgs, 2); // 调用平台接口读写数据if (ret < 0){dev_err(&client->dev, "%s: -i2c read error.\n", __func__);ft6x0x_ts_release(ft6x0x_ts); // 释放按键值ft6x0x_ts_reset(ft6x0x_ts); // 重新启动}} else {struct i2c_msg msgs[] = {{.addr = client->addr,.flags = I2C_M_RD, // 接收数据.len = readlen,.buf = readbuf,},};ret = i2c_transfer(client->adapter, msgs, 1); // 调用平台接口接收数据if (ret < 0){dev_err(&client->dev, "%s:i2c read error.\n", __func__);ft6x0x_ts_reset(ft6x0x_ts);}}return ret;}
再看看另外一个函数,它的具体实现内容。
/* ----------------------------------------------------* the functional interface to the i2c busses.* ----------------------------------------------------*/
/*** i2c_transfer - execute a single or combined I2C message* @adap: Handle to I2C bus* @msgs: One or more messages to execute before STOP is issued to* terminate the operation; each message begins with a START.* @num: Number of messages to be executed.** Returns negative errno, else the number of messages executed.** Note that there is no requirement that each message be sent to* the same slave address, although that is the most common model.*/int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){unsigned long orig_jiffies;int ret, try;
/* REVISIT the fault reporting model here is weak:** - When we get an error after receiving N bytes from a slave,* there is no way to report "N".** - When we get a NAK after transmitting N bytes to a slave,* there is no way to report "N" ... or to let the master* continue executing the rest of this combined message, if* that's the appropriate response.** - When for example "num" is two and we successfully complete* the first message but get an error part way through the* second, it's unclear whether that should be reported as* one (discarding status on the second message) or errno* (discarding status on the first one).*/
if (adap->algo->master_xfer) {#ifdef DEBUGfor (ret = 0; ret < num; ret++) {dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)? 'R' : 'W', msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");}#endif
if (in_atomic() || irqs_disabled()) {ret = i2c_trylock_adapter(adap); //对I2C适配器尝试加锁if (!ret)/* I2C activity is ongoing. */return -EAGAIN;} else {i2c_lock_adapter(adap); //对I2C 适配器加锁}
/* Retry automatically on arbitration loss */orig_jiffies = jiffies;for (ret = 0, try = 0; try <= adap->retries; try++) {ret = adap->algo->master_xfer(adap, msgs, num); // 核心部分,提交数据给总线驱动层if (ret != -EAGAIN)break;if (time_after(jiffies, orig_jiffies + adap->timeout))break;}i2c_unlock_adapter(adap); //对I2C 适配器解锁
return ret;} else {dev_dbg(&adap->dev, "I2C level transfers not supported\n");return -EOPNOTSUPP;}}本函数主要是调用平台接口来读写I2C客户端的数据,它是读写函数的核心部分。
下面再来看看另外一个函数接口fts_ctpm_get_i_file_ver函数,看看它的功能和实现代码。
u8 fts_ctpm_get_i_file_ver(void) // 获得固件参数表{u16 ui_sz;ui_sz = sizeof( CTPM_FW);if (ui_sz > 2)return CTPM_FW[ui_sz - 2];
return 0x00; /*default value */}
上面函数的功能很简单就是获取CTPM_FW数组的值,下面我们来看看这个数组到底存放什么数据;static unsigned char CTPM_FW[] = { // 触摸按键类型的参数#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)#include "TWS20176_FT5206_V08_LCD_20140821_1S_app.dat" //根据包含的路径找的文件,打开发现其实就是触摸板的一些参数。#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_TWS)#include "TWS20074-FT6206-v11_20140620_app.dat"#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_YIYANG_10S)#include "IT_YY16-G1409+X1_V12_20140626_app.dat"#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_YIYANG_1S)#include "IT_YY16_1409OGS_V22_20140913_app.dat"#endif}; // 固件参数表
我们再来看看另外一个自动更新固件列表的函数,看看它的具体实现和功能。
/*upgrade with *.i file*/int fts_ctpm_fw_upgrade_with_i_file(struct i2c_client *client) // 升级固件参数表{u8 *pbt_buf = NULL;int i_ret;int fw_len = sizeof( CTPM_FW);
/*judge the fw that will be upgraded* if illegal, then stop upgrade and return.*/if (fw_len < 8 || fw_len > 32 * 1024) {dev_err(&client->dev, "%s:FW length error\n", __func__);return -EIO;}
if ((CTPM_FW[fw_len - 8] ^ CTPM_FW[fw_len - 6]) == 0xFF && (CTPM_FW[fw_len - 7] ^ CTPM_FW[fw_len - 5]) == 0xFF&& (CTPM_FW[fw_len - 3] ^ CTPM_FW[fw_len - 4]) == 0xFF) {/*FW upgrade */pbt_buf = CTPM_FW;/*call the upgrade function */i_ret = fts_ctpm_fw_upgrade(client, pbt_buf, sizeof( CTPM_FW));if (i_ret != 0)dev_err(&client->dev, "%s:upgrade failed. err.\n", __func__);#ifdef AUTO_CLBelsefts_ctpm_auto_clb(client); /*start auto CLB */ 自动校准客户端#endif} else {dev_err(&client->dev, "%s:FW format error\n", __func__);return -EBADFD;}
return i_ret;}
我们再看上面函数调用的fts_ctpm_fw_upgrade函数,看看它的代码实现。
//upgrade firmwareint fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf, u32 dw_lenth){u8 reg_val[2] = {0};u32 i = 0;u32 packet_number;u32 j;u32 temp;u32 lenght;u8 packet_buf[FTS_PACKET_LENGTH + 6];u8 auc_i2c_write_buf[10];u8 bt_ecc;int i_ret;
for (i = 0; i < FTS_UPGRADE_LOOP; i++) {/*********Step 1:Reset CTPM *****/#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)/*write 0xaa to register 0xfc */ft6x06_write_reg(client, 0xfc, FT_UPGRADE_AA);msleep(FT5X06_UPGRADE_AA_DELAY);
/*write 0x55 to register 0xfc */ft6x06_write_reg(client, 0xfc, FT_UPGRADE_55);msleep(FT5X06_UPGRADE_55_DELAY);#else/*write 0xaa to register 0xbc */ft6x06_write_reg(client, 0xbc, FT_UPGRADE_AA);msleep(FT6X06_UPGRADE_AA_DELAY);
/*write 0x55 to register 0xbc */ft6x06_write_reg(client, 0xbc, FT_UPGRADE_55);msleep(FT6X06_UPGRADE_55_DELAY);#endif/*********Step 2:Enter upgrade mode *****/auc_i2c_write_buf[0] = FT_UPGRADE_55;auc_i2c_write_buf[1] = FT_UPGRADE_AA;do {i++;i_ret = ft6x0x_i2c_Write(client, auc_i2c_write_buf, 2);msleep(5);} while (i_ret <= 0 && i < 5);
/*********Step 3:check READ-ID***********************/msleep(FT6X06_UPGRADE_READID_DELAY);auc_i2c_write_buf[0] = 0x90;auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] = 0x00;ft6x0x_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)if (reg_val[0] == FT5X06_UPGRADE_ID_1 && reg_val[1] == FT5X06_UPGRADE_ID_2)#elseif (reg_val[0] == FT6X06_UPGRADE_ID_1 && reg_val[1] == FT6X06_UPGRADE_ID_2)#endif{//dev_dbg(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",//reg_val[0], reg_val[1]);DBG("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", reg_val[0], reg_val[1]);break;} else {dev_err(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", reg_val[0], reg_val[1]);}}if (i > FTS_UPGRADE_LOOP)return -EIO;auc_i2c_write_buf[0] = 0xcd;
ft6x0x_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
/*Step 4:erase app and panel paramenter area*/ 擦除app 和 参数DBG("Step 4:erase app and panel paramenter area\n");auc_i2c_write_buf[0] = 0x61;ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1); /*erase app area */msleep(FT6X06_UPGRADE_EARSE_DELAY);/*erase panel parameter area */auc_i2c_write_buf[0] = 0x63;ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1);msleep(100);
/*********Step 5:write firmware(FW) to ctpm flash*********/bt_ecc = 0;DBG("Step 5:write firmware(FW) to ctpm flash\n");
dw_lenth = dw_lenth - 8;packet_number = (dw_lenth) / FTS_PACKET_LENGTH;packet_buf[0] = 0xbf;packet_buf[1] = 0x00;
for (j = 0; j < packet_number; j++) {temp = j * FTS_PACKET_LENGTH;packet_buf[2] = (u8) (temp >> 8);packet_buf[3] = (u8) temp;lenght = FTS_PACKET_LENGTH;packet_buf[4] = (u8) (lenght >> 8); // 获得8 -15 bit 数据packet_buf[5] = (u8) lenght; // 获得0 - 7 bit 数据
for (i = 0; i < FTS_PACKET_LENGTH; i++) {packet_buf[6 + i] = pbt_buf[j * FTS_PACKET_LENGTH + i];bt_ecc ^= packet_buf[6 + i];}
ft6x0x_i2c_Write(client, packet_buf, FTS_PACKET_LENGTH + 6);msleep(FTS_PACKET_LENGTH / 6 + 1);//DBG("write bytes:0x%04x\n", (j+1) * FTS_PACKET_LENGTH);//delay_qt_ms(FTS_PACKET_LENGTH / 6 + 1);}
if ((dw_lenth) % FTS_PACKET_LENGTH > 0) {temp = packet_number * FTS_PACKET_LENGTH;packet_buf[2] = (u8) (temp >> 8);packet_buf[3] = (u8) temp;temp = (dw_lenth) % FTS_PACKET_LENGTH;packet_buf[4] = (u8) (temp >> 8);packet_buf[5] = (u8) temp;
for (i = 0; i < temp; i++) {packet_buf[6 + i] = pbt_buf[packet_number * FTS_PACKET_LENGTH + i];bt_ecc ^= packet_buf[6 + i];}
ft6x0x_i2c_Write(client, packet_buf, temp + 6);msleep(20);}
/*send the last six byte */for (i = 0; i < 6; i++) {temp = 0x6ffa + i;packet_buf[2] = (u8) (temp >> 8);packet_buf[3] = (u8) temp;temp = 1;packet_buf[4] = (u8) (temp >> 8);packet_buf[5] = (u8) temp;packet_buf[6] = pbt_buf[dw_lenth + i];bt_ecc ^= packet_buf[6];ft6x0x_i2c_Write(client, packet_buf, 7);msleep(20);}
/*********Step 6: read out checksum***********************//*send the opration head */DBG("Step 6: read out checksum\n");auc_i2c_write_buf[0] = 0xcc;ft6x0x_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);if (reg_val[0] != bt_ecc) {dev_err(&client->dev, "[FTS]--ecc error! FW=%02x bt_ecc=%02x\n", reg_val[0], bt_ecc);return -EIO;}
/*********Step 7: reset the new FW***********************/DBG("Step 7: reset the new FW\n");auc_i2c_write_buf[0] = 0x07;ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1);msleep(300); /*make sure CTP startup normally */
return 0;}
下面我们对上面的调用的 ft6x0x_i2c_Write函数进行分析下,其实他也是调用ft6x0x_i2c_Write函数而已,具体的调用关系如下:int ft6x06_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue) // send data{unsigned char buf[2] = {0};buf[0] = regaddr;buf[1] = regvalue;return ft6x0x_i2c_Write(client, buf, sizeof(buf)); // 发送客户端数据}
来看看 ft6x0x_i2c_Write函数,代码如下:/*write data by i2c*/int ft6x0x_i2c_Write(struct i2c_client *client, char *writebuf, int writelen){int ret;struct ft6x0x_ts_data *ft6x0x_ts = i2c_get_clientdata (client); // 获得客户端驱动数据结构体指针地址struct i2c_msg msg[] = {{.addr = client->addr,.flags = 0, // 发送数据.len = writelen,.buf = writebuf,},};ret = i2c_transfer(client->adapter, msg, 1); //发送数据if (ret < 0){dev_err(&client->dev, "%s i2c write error.\n", __func__);ft6x0x_ts_release(ft6x0x_ts); //释放触摸按键数据ft6x0x_ts_reset(ft6x0x_ts); //重启}return ret;}
我们再来看看自动更新固件函数中的另外一个重要的函数,自动校正函数 fts_ctpm_auto_clb函数的实现。int fts_ctpm_auto_clb(struct i2c_client *client) // 自动校准{unsigned char uc_temp = 0x00;unsigned char i = 0;
/*start auto CLB */msleep(200);
ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE);/*make sure already enter factory mode */msleep(100);/*write command to start calibration */ft6x06_write_reg(client, 2, 0x4);msleep(300);for (i = 0; i < 100; i++) {ft6x06_read_reg(client, 0, &uc_temp);/*return to normal mode, calibration finish */if (0x0 == ((uc_temp & 0x70) >> 4))break;}
msleep(200);/*calibration OK */msleep(300);ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE); /*goto factory mode for store */msleep(100); /*make sure already enter factory mode */ft6x06_write_reg(client, 2, 0x5); /*store CLB result */msleep(300);ft6x06_write_reg(client, 0, FTS_WORKMODE_VALUE); /*return to normal mode */msleep(300);
/*store CLB result OK */return 0;}
自动更新固件模块的函数我们先就了解到这里,我们再来看看另外一个模块工作队列函数的实现工程。static void ft6x0x_work_handler(struct work_struct *work) // 上报参数{struct ft6x0x_ts_data *ft6x0x_ts = container_of(work, struct ft6x0x_ts_data,work);int ret = 0;ret = ft6x0x_read_Touchdata(ft6x0x_ts); // 读触摸按键信息if (ret == 0)ft6x0x_report_value(ft6x0x_ts); // 上报按键值enable_irq(ft6x0x_ts->client->irq); // 使能中断}
下面再来看看被调用的函数 ft6x0x_read_Touchdata函数的代码实现。/*Read touch point information when the interrupt is asserted.*/static int ft6x0x_read_Touchdata(struct ft6x0x_ts_data *data){struct ts_event *event = &data->event;int ret = -1, i = 0;
uint8_t buf[POINT_READ_BUF] = { 0 };uint8_t finger_regs[] = {0x03,0x09,0x0F,0x15,0x1B};
memset(event, 0, sizeof(struct ts_event)); // 初始化触摸事件结构体为0.event->touch_point = 0;buf[0] = 0;ret = ft6x0x_i2c_Read(data->client, buf, 1, buf, 3);if (ret < 0) {return ret;}event->touch_point = buf[2] & 0x0f;
if (0 == event->touch_point) {ft6x0x_ts_release(data);return 1;}
if (event->touch_point > sizeof(finger_regs)) {if (event->touch_point == 0x0F)return 1;else {dev_info(&data->client->dev, "ERROR : %s --> %d check finger_regs!\n", __func__, __LINE__);return 1;}}
//设置触摸事件的基本参数信息for (i = 0; i < event->touch_point; i ++) {buf[0] = finger_regs[i];ft6x0x_i2c_Read(data->client,buf,1,buf,6);event->au16_x[i] = (s16) (buf[0] & 0x0F) << 8 | (s16) buf[1];event->au16_y[i] = (s16) (buf[2] & 0x0F) << 8 | (s16) buf[3];event->au8_touch_event[i] = buf[0] >> 6;event->au8_finger_id[i] = (buf[2]) >> 4;event->weight[i] = buf[4];}if(event->au16_y[0] < 260){finger_up = false;}if ((1 == event->touch_point) && finger_up == true) {if(! ft6x0x_touchket_detec(event->au16_x[0], event->au16_y[0])) { //判断触摸按键事件是否触发ft6x0x_touchkey_report(data->input_dev); //设置按键事件触发值}}event->pressure = FT_PRESS;
return 0;}
上面函数的主要功能是设置触摸按键事件触发时所对应的按键值,以便于按键事件处理时使用。下面看看被调用的函数 ft6x0x_touchket_detec函数的代码实现,看看它的具体工作内容是什么.static int ft6x0x_touchket_detec(int x, int y) // 判断按键事件类型{//根据触摸按键的坐标值来区分按键事件的类型if(x >= tk_info.x_min_back && x <= tk_info.x_max_back && y >=tk_info.y_min_back && y <= tk_info.y_max_back){tp_key_back_down = 1;return 0;}if(x >=tk_info.x_min_home && x <= tk_info.x_max_home && y >=tk_info.y_min_home && y <= tk_info.y_max_home){tp_key_home_down = 1;return 0;}if(x >=tk_info.x_min_menu && x <= tk_info.x_max_menu && y >=tk_info.y_min_menu && y <= tk_info.y_max_menu){tp_key_menu_down = 1;return 0;}if(x >=tk_info.x_min_search && x <= tk_info.x_max_search && y >=tk_info.y_min_search && y <= tk_info.y_max_search){tp_key_search_down = 1;return 0;}return 1;}
下面看看另外一个函数 ft6x0x_touchkey_report函数,主要是设置按键事件被触发的按键值static void ft6x0x_touchkey_report(struct input_dev *dev) // 设置按键事件被触发的按键值{if(tp_key_search_down) {input_report_key(dev, KEY_SEARCH, 1); //设置被触发的按键值input_sync(dev); //同步事件} else if(tp_key_back_down) {input_report_key(dev, KEY_BACK, 1);input_sync(dev);} else if(tp_key_home_down) {input_report_key(dev, KEY_HOME, 1);input_sync(dev);} else if(tp_key_menu_down) {input_report_key(dev, KEY_MENU, 1);input_sync(dev);}}
我们再来看看另外一个函数 ft6x0x_report_value函数的代码,看看它的具体功能。/**report the point information*/static int ft6x0x_report_value(struct ft6x0x_ts_data *data) // 上报event 参数{struct ts_event *event = &data->event;int i = 0;for (i = 0; i < event->touch_point; i++) {if(event->au16_x[i] > data->x_max || event->au16_y[i] > data->y_max)continue;
#ifdef CONFIG_TSC_SWAP_XYtsc_swap_xy(&(event->au16_x[i]),&(event->au16_y[i])); // exchange x axis and y axis#endif
#ifdef CONFIG_TSC_SWAP_Xtsc_swap_x(&(event->au16_x[i]),data->x_max); // x_max - x_current#endif
#ifdef CONFIG_TSC_SWAP_Ytsc_swap_y(&(event->au16_y[i]),data->y_max); // y_max - y_current#endifpr_pos("x[%d]: %d,\ty[%d]: %d\n", i, event->au16_x[i], i, event->au16_y[i]);
// 上报参数值input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->au16_x[i]);input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]);input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->au8_finger_id[i]);input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR, event->weight[i]);input_report_abs(data->input_dev,ABS_MT_WIDTH_MAJOR, event->weight[i]);input_mt_sync(data->input_dev);}
input_sync(data->input_dev); //同步事件return 0;}对于上面的函数是将参数上报到事件层,我再来看看3个交换函数的代码。static inline void tsc_swap_xy(u16 * x,u16 * y){u16 tmp = 0;tmp = *x;*x = *y;*y = tmp;}
static inline void tsc_swap_x(u16 * x,u16 max_x){*x = max_x - *x;}
static inline void tsc_swap_y(u16 * y,u16 max_y){*y = max_y - *y;}
工作队列的初始化工作已经简单的看了一下,下面我们再来触摸屏按键事件的处理机制,看看它的实现原理和代码结构。static int touch_event_handler (void *unused){int ret = -1;struct ft6x0x_ts_data *ft6x0x_ts = (struct ft6x0x_ts_data *)unused;struct sched_param param = { .sched_priority = RTPM_PRIO_TPD};
sched_setscheduler(current, SCHED_RR, ¶m); // 设置进程调度策略和时实优先级set_freezable(); // 使当前线程挂起或者休眠do {set_current_state(TASK_INTERRUPTIBLE); // 设置当前状态值wait_event_freezable(waiter, ft6x0x_ts->tpd_flag != 0); //等待队列进入休眠的条件是第二个参数为假时。ft6x0x_ts->tpd_flag = 0;set_current_state(TASK_RUNNING);ret = ft6x0x_read_Touchdata(ft6x0x_ts); // 读取触摸按键值if (ret == 0)ft6x0x_report_value(ft6x0x_ts); // 上报按键值enable_irq(ft6x0x_ts->client->irq);} while (! kthread_should_stop()); // 完成工作后主动结束线程
return 0;}这个函数调用的函数的具体的实现和代码我就不在拿出来看了,如果有兴趣可以自己在内核中代码中查看,下面我们来看看另外一个模块中断处理机制。
/*The ft6x0x device will signal the host about TRIGGER_FALLING.*Processed when the interrupt is asserted.*/static irqreturn_t ft6x0x_ts_interrupt(int irq, void *dev_id){struct ft6x0x_ts_data *ft6x0x_ts = dev_id;disable_irq_nosync(ft6x0x_ts->irq); // 禁止中断并立即返回
if(ft6x0x_ts->is_suspend == 1)return IRQ_HANDLED;#ifdef CONFIG_KEY_SPECIAL_POWER_KEYif(bkl_flag != 1) {if( timer_pending(&ft6x0x_ts->tp_blk_delay))del_timer_sync(&ft6x0x_ts->tp_blk_delay); //取消定时器
bkl_flag = 2;mod_timer(&ft6x0x_ts->tp_blk_delay, get_jiffies_64() + msecs_to_jiffies(ft6x0x_ts->pdata->blight_off_timer));}#endif#ifdef FTTP_THREAD_MODEft6x0x_ts->tpd_flag = 1;wake_up_interruptible(&waiter); // 唤醒休眠等待的队列#elsequeue_work(ft6x0x_ts->workqueue, &ft6x0x_ts->work); // 调度执行工作队列#endifreturn IRQ_HANDLED;}
下面我们再来看看最后一个模块背光处理机制,看看他是如何实现的。其实背光函数tp_off_blk_timer函数实际上是在调用set_backlight_light函数,我们来看看它的具体实现。
int set_backlight_light(int state) //背光灯的控制{int value;value = gpio_get_value(GPIO_BL_PWR_EN); // 获得背光电源引脚的gpio值switch (state) {case BKL_ON:break;case BKL_TP_OFF:if(value)gpio_direction_output(GPIO_BL_PWR_EN, 0); // 设置背光电源关闭break;case BKL_KEY_OFF:if(value)gpio_direction_output(GPIO_BL_PWR_EN, 0); // 设置背光电源关闭break;case BKL_TURN:if(value)gpio_direction_output(GPIO_BL_PWR_EN, 0); // 设置背光电源关闭elsegpio_direction_output(GPIO_BL_PWR_EN, 1); // 设置背光电源打开break;default:break;}bkl_flag = 0;return gpio_get_value(GPIO_BL_PWR_EN); // 返回 背光电源引脚的当前gpio值}
s2121b_16t 触摸按键 (君正)
最新推荐文章于 2021-11-02 10:22:55 发布