s2121b_16t 触摸按键 (君正)

触摸按键文件 /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_MODE
 INIT_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;
 }
#else
 ft6x0x_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_interrupt
 err =  request_irq(client->irq,  ft6x0x_ts_interruptIRQF_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_KEY
 setup_timer(&ft6x0x_ts->tp_blk_delay,  tp_off_blk_timer(unsigned long)ft6x0x_ts);   // 设置背光灯处理函数
#endif
 input_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_EARLYSUSPEND
 ft6x0x_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_MODE
 destroy_workqueue(ft6x0x_ts->workqueue);     // 销毁工作队列
#else
 kthread_stop(ft6x0x_ts->thread);        // 结束线程运行
#endif
exit_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 DEBUG
  for (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_CLB
  else
    fts_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 firmware
int  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) 
#else
  if (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_XY
   tsc_swap_xy(&(event->au16_x[i]),&(event->au16_y[i]));   // exchange x axis and y axis
#endif

#ifdef CONFIG_TSC_SWAP_X
   tsc_swap_x(&(event->au16_x[i]),data->x_max);    // x_max - x_current
#endif

#ifdef CONFIG_TSC_SWAP_Y
   tsc_swap_y(&(event->au16_y[i]),data->y_max);    // y_max - y_current
#endif
  pr_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, &param);     // 设置进程调度策略和时实优先级
  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_KEY
 if(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_MODE
  ft6x0x_ts->tpd_flag = 1;
   wake_up_interruptible(&waiter);     // 唤醒休眠等待的队列
#else
  queue_work(ft6x0x_ts->workqueue, &ft6x0x_ts->work);      // 调度执行工作队列
#endif
 return 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);    // 设置背光电源关闭
  else
    gpio_direction_output(GPIO_BL_PWR_EN, 1);   // 设置背光电源打开
  break;
 default:
  break;
 }
 bkl_flag = 0;
 return  gpio_get_value(GPIO_BL_PWR_EN);    // 返回 背光电源引脚的当前gpio值
}



猜你喜欢

转载自blog.csdn.net/dylan_524217139/article/details/48340289