604 lines
14 KiB
C
604 lines
14 KiB
C
|
/* drivers/input/touchscreen/goodix_tool.c
|
||
|
*
|
||
|
* 2010 - 2012 Goodix Technology.
|
||
|
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be a reference
|
||
|
* to you, when you are integrating the GOODiX's CTP IC into your system,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details.
|
||
|
*
|
||
|
* Version:1.6
|
||
|
* V1.0:2012/05/01,create file.
|
||
|
* V1.2:2012/06/08,modify some warning.
|
||
|
* V1.4:2012/08/28,modified to support GT9XX
|
||
|
* V1.6:new proc name
|
||
|
*/
|
||
|
|
||
|
#include "gt9xx.h"
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/proc_fs.h>
|
||
|
#include <linux/debugfs.h>
|
||
|
|
||
|
#define DATA_LENGTH_UINT 512
|
||
|
#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head) - sizeof(u8 *))
|
||
|
static char procname[20] = {0};
|
||
|
|
||
|
struct st_cmd_head {
|
||
|
u8 wr; /* write read flag 0:R 1:W 2:PID 3: */
|
||
|
u8 flag; /* 0:no need flag/int 1: need flag 2:need int */
|
||
|
u8 flag_addr[2];/* flag address */
|
||
|
u8 flag_val; /* flag val */
|
||
|
u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */
|
||
|
u16 circle; /* polling cycle */
|
||
|
u8 times; /* plling times */
|
||
|
u8 retry; /* I2C retry times */
|
||
|
u16 delay; /* delay before read or after write */
|
||
|
u16 data_len; /* data length */
|
||
|
u8 addr_len; /* address length */
|
||
|
u8 addr[2]; /* address */
|
||
|
u8 res[3]; /* reserved */
|
||
|
u8 *data; /* data pointer */
|
||
|
} __packed;
|
||
|
|
||
|
static struct st_cmd_head cmd_head;
|
||
|
|
||
|
static struct i2c_client *gt_client;
|
||
|
|
||
|
static struct proc_dir_entry *goodix_proc_entry;
|
||
|
|
||
|
static struct mutex lock;
|
||
|
|
||
|
static s32 (*tool_i2c_read)(u8 *, u16);
|
||
|
static s32 (*tool_i2c_write)(u8 *, u16);
|
||
|
|
||
|
s32 data_length;
|
||
|
s8 ic_type[16] = {0};
|
||
|
|
||
|
static void tool_set_proc_name(char *procname)
|
||
|
{
|
||
|
char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
|
||
|
"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||
|
char date[20] = {0};
|
||
|
char month[4] = {0};
|
||
|
int i = 0, n_month = 1, n_day = 0, n_year = 0, ret;
|
||
|
|
||
|
snprintf(date, 20, "%s", __DATE__);
|
||
|
|
||
|
/* pr_debug("compile date: %s", date); */
|
||
|
|
||
|
ret = sscanf(date, "%s %d %d", month, &n_day, &n_year);
|
||
|
if (!ret)
|
||
|
return;
|
||
|
for (i = 0; i < 12; ++i) {
|
||
|
if (!memcmp(months[i], month, 3)) {
|
||
|
n_month = i+1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day);
|
||
|
/* pr_debug("procname = %s", procname); */
|
||
|
}
|
||
|
|
||
|
static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
|
||
|
{
|
||
|
s32 ret = -1;
|
||
|
u8 i = 0;
|
||
|
struct i2c_msg msgs[2] = {
|
||
|
{
|
||
|
.flags = !I2C_M_RD,
|
||
|
.addr = gt_client->addr,
|
||
|
.len = cmd_head.addr_len,
|
||
|
.buf = &buf[0],
|
||
|
},
|
||
|
{
|
||
|
.flags = I2C_M_RD,
|
||
|
.addr = gt_client->addr,
|
||
|
.len = len,
|
||
|
.buf = &buf[GTP_ADDR_LENGTH],
|
||
|
},
|
||
|
};
|
||
|
|
||
|
for (i = 0; i < cmd_head.retry; i++) {
|
||
|
ret = i2c_transfer(gt_client->adapter, msgs, 2);
|
||
|
if (ret > 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i == cmd_head.retry) {
|
||
|
dev_err(>_client->dev, "I2C read retry limit over.\n");
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
|
||
|
{
|
||
|
s32 ret = -1;
|
||
|
u8 i = 0;
|
||
|
struct i2c_msg msg = {
|
||
|
.flags = !I2C_M_RD,
|
||
|
.addr = gt_client->addr,
|
||
|
.len = len,
|
||
|
.buf = buf,
|
||
|
};
|
||
|
|
||
|
for (i = 0; i < cmd_head.retry; i++) {
|
||
|
ret = i2c_transfer(gt_client->adapter, &msg, 1);
|
||
|
if (ret > 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i == cmd_head.retry) {
|
||
|
dev_err(>_client->dev, "I2C write retry limit over.\n");
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static s32 tool_i2c_read_with_extra(u8 *buf, u16 len)
|
||
|
{
|
||
|
s32 ret = -1;
|
||
|
u8 pre[2] = {0x0f, 0xff};
|
||
|
u8 end[2] = {0x80, 0x00};
|
||
|
|
||
|
tool_i2c_write_no_extra(pre, 2);
|
||
|
ret = tool_i2c_read_no_extra(buf, len);
|
||
|
tool_i2c_write_no_extra(end, 2);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static s32 tool_i2c_write_with_extra(u8 *buf, u16 len)
|
||
|
{
|
||
|
s32 ret = -1;
|
||
|
u8 pre[2] = {0x0f, 0xff};
|
||
|
u8 end[2] = {0x80, 0x00};
|
||
|
|
||
|
tool_i2c_write_no_extra(pre, 2);
|
||
|
ret = tool_i2c_write_no_extra(buf, len);
|
||
|
tool_i2c_write_no_extra(end, 2);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void register_i2c_func(void)
|
||
|
{
|
||
|
if (strcmp(ic_type, "GT8110") && strcmp(ic_type, "GT8105")
|
||
|
&& strcmp(ic_type, "GT801") && strcmp(ic_type, "GT800")
|
||
|
&& strcmp(ic_type, "GT801PLUS") && strcmp(ic_type, "GT811")
|
||
|
&& strcmp(ic_type, "GTxxx")) {
|
||
|
tool_i2c_read = tool_i2c_read_with_extra;
|
||
|
tool_i2c_write = tool_i2c_write_with_extra;
|
||
|
pr_debug("I2C function: with pre and end cmd!");
|
||
|
} else {
|
||
|
tool_i2c_read = tool_i2c_read_no_extra;
|
||
|
tool_i2c_write = tool_i2c_write_no_extra;
|
||
|
pr_info("I2C function: without pre and end cmd!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void unregister_i2c_func(void)
|
||
|
{
|
||
|
tool_i2c_read = NULL;
|
||
|
tool_i2c_write = NULL;
|
||
|
pr_info("I2C function: unregister i2c transfer function!");
|
||
|
}
|
||
|
|
||
|
void uninit_wr_node(void)
|
||
|
{
|
||
|
cmd_head.data = NULL;
|
||
|
unregister_i2c_func();
|
||
|
proc_remove(goodix_proc_entry);
|
||
|
}
|
||
|
|
||
|
static u8 relation(u8 src, u8 dst, u8 rlt)
|
||
|
{
|
||
|
u8 ret = 0;
|
||
|
|
||
|
switch (rlt) {
|
||
|
|
||
|
case 0:
|
||
|
ret = (src != dst) ? true : false;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
ret = (src == dst) ? true : false;
|
||
|
pr_debug("equal:src:0x%02x dst:0x%02x ret:%d.",
|
||
|
src, dst, (s32)ret);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
ret = (src > dst) ? true : false;
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
ret = (src < dst) ? true : false;
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
ret = (src & dst) ? true : false;
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
ret = (!(src | dst)) ? true : false;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ret = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*******************************************************
|
||
|
Function:
|
||
|
Comfirm function.
|
||
|
Input:
|
||
|
None.
|
||
|
Output:
|
||
|
Return write length.
|
||
|
********************************************************/
|
||
|
static u8 comfirm(void)
|
||
|
{
|
||
|
s32 i = 0;
|
||
|
u8 buf[32];
|
||
|
|
||
|
memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
|
||
|
|
||
|
for (i = 0; i < cmd_head.times; i++) {
|
||
|
if (tool_i2c_read(buf, 1) <= 0) {
|
||
|
dev_err(>_client->dev, "Read flag data failed!");
|
||
|
return FAIL;
|
||
|
}
|
||
|
if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val,
|
||
|
cmd_head.flag_relation)) {
|
||
|
pr_debug("value at flag addr:0x%02x.",
|
||
|
buf[GTP_ADDR_LENGTH]);
|
||
|
pr_debug("flag value:0x%02x.", cmd_head.flag_val);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
msleep(cmd_head.circle);
|
||
|
}
|
||
|
|
||
|
if (i >= cmd_head.times) {
|
||
|
dev_err(>_client->dev, "Didn't get the flag to continue!");
|
||
|
return FAIL;
|
||
|
}
|
||
|
|
||
|
return SUCCESS;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
|
||
|
static s32 fill_update_info(char __user *user_buf,
|
||
|
size_t count, loff_t *ppos)
|
||
|
{
|
||
|
u8 buf[4];
|
||
|
|
||
|
buf[0] = show_len >> 8;
|
||
|
buf[1] = show_len & 0xff;
|
||
|
buf[2] = total_len >> 8;
|
||
|
buf[3] = total_len & 0xff;
|
||
|
return simple_read_from_buffer(user_buf, count, ppos,
|
||
|
buf, sizeof(buf));
|
||
|
}
|
||
|
#else
|
||
|
static s32 fill_update_info(char __user *user_buf,
|
||
|
size_t count, loff_t *ppos)
|
||
|
{
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/********************************************************
|
||
|
Function:
|
||
|
Goodix tool write function.
|
||
|
nput:
|
||
|
standard proc write function param.
|
||
|
Output:
|
||
|
Return write length.
|
||
|
********************************************************/
|
||
|
static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
|
||
|
size_t count, loff_t *ppos)
|
||
|
{
|
||
|
s32 ret = 0;
|
||
|
|
||
|
mutex_lock(&lock);
|
||
|
ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH);
|
||
|
if (ret) {
|
||
|
dev_err(>_client->dev, "copy_from_user failed.");
|
||
|
ret = -EACCES;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
dev_dbg(>_client->dev, "wr:0x%02x, flag:0x%02x, flag addr:0x%02x%02x, flag val:0x%02x, flag rel:0x%02x, circle:%d, times:%d, retry:%d, delay:%d, data len:%d, addr len:%d, addr:0x%02x%02x, write len: %d.",
|
||
|
cmd_head.wr, cmd_head.flag, cmd_head.flag_addr[0],
|
||
|
cmd_head.flag_addr[1], cmd_head.flag_val,
|
||
|
cmd_head.flag_relation, (s32)cmd_head.circle,
|
||
|
(s32)cmd_head.times, (s32)cmd_head.retry, (s32)cmd_head.delay,
|
||
|
(s32)cmd_head.data_len, (s32)cmd_head.addr_len,
|
||
|
cmd_head.addr[0], cmd_head.addr[1], (s32)count);
|
||
|
|
||
|
if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) {
|
||
|
dev_err(>_client->dev, "data len %d > data buff %d, rejected!\n",
|
||
|
cmd_head.data_len, (data_length - GTP_ADDR_LENGTH));
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
if (cmd_head.addr_len > GTP_ADDR_LENGTH) {
|
||
|
dev_err(>_client->dev, "addr len %d > data buff %d, rejected!\n",
|
||
|
cmd_head.addr_len, GTP_ADDR_LENGTH);
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (cmd_head.wr == GTP_RW_WRITE) {
|
||
|
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
|
||
|
&userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
|
||
|
if (ret) {
|
||
|
dev_err(>_client->dev, "copy_from_user failed.");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
|
||
|
cmd_head.addr, cmd_head.addr_len);
|
||
|
|
||
|
if (cmd_head.flag == GTP_NEED_FLAG) {
|
||
|
if (comfirm() == FAIL) {
|
||
|
dev_err(>_client->dev, "Comfirm fail!");
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
} else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
|
||
|
/* Need interrupt! */
|
||
|
}
|
||
|
if (tool_i2c_write(
|
||
|
&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
|
||
|
cmd_head.data_len + cmd_head.addr_len) <= 0) {
|
||
|
dev_err(>_client->dev, "Write data failed!");
|
||
|
ret = -EIO;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (cmd_head.delay)
|
||
|
msleep(cmd_head.delay);
|
||
|
|
||
|
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_WRITE_IC_TYPE) { /* Write ic type */
|
||
|
ret = copy_from_user(&cmd_head.data[0],
|
||
|
&userbuf[CMD_HEAD_LENGTH],
|
||
|
cmd_head.data_len);
|
||
|
if (ret) {
|
||
|
dev_err(>_client->dev, "copy_from_user failed.");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (cmd_head.data_len > sizeof(ic_type)) {
|
||
|
dev_err(>_client->dev,
|
||
|
"data len %d > data buff %d, rejected!\n",
|
||
|
cmd_head.data_len, sizeof(ic_type));
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
memcpy(ic_type, cmd_head.data, cmd_head.data_len);
|
||
|
|
||
|
register_i2c_func();
|
||
|
|
||
|
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_NO_WRITE) {
|
||
|
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_DISABLE_IRQ) { /* disable irq! */
|
||
|
gtp_irq_disable(i2c_get_clientdata(gt_client));
|
||
|
|
||
|
#if GTP_ESD_PROTECT
|
||
|
gtp_esd_switch(gt_client, SWITCH_OFF);
|
||
|
#endif
|
||
|
ret = CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_ENABLE_IRQ) { /* enable irq! */
|
||
|
gtp_irq_enable(i2c_get_clientdata(gt_client));
|
||
|
|
||
|
#if GTP_ESD_PROTECT
|
||
|
gtp_esd_switch(gt_client, SWITCH_ON);
|
||
|
#endif
|
||
|
ret = CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_CHECK_RAWDIFF_MODE) {
|
||
|
struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
|
||
|
|
||
|
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
|
||
|
&userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
|
||
|
if (ret) {
|
||
|
pr_debug("copy_from_user failed.");
|
||
|
goto exit;
|
||
|
}
|
||
|
if (cmd_head.data[GTP_ADDR_LENGTH]) {
|
||
|
pr_debug("gtp enter rawdiff.");
|
||
|
ts->gtp_rawdiff_mode = true;
|
||
|
} else {
|
||
|
ts->gtp_rawdiff_mode = false;
|
||
|
pr_debug("gtp leave rawdiff.");
|
||
|
}
|
||
|
ret = CMD_HEAD_LENGTH;
|
||
|
goto exit;
|
||
|
} else if (cmd_head.wr == GTP_RW_ENTER_UPDATE_MODE) {
|
||
|
/* Enter update mode! */
|
||
|
if (gup_enter_update_mode(gt_client) == FAIL) {
|
||
|
ret = -EBUSY;
|
||
|
goto exit;
|
||
|
}
|
||
|
} else if (cmd_head.wr == GTP_RW_LEAVE_UPDATE_MODE) {
|
||
|
/* Leave update mode! */
|
||
|
gup_leave_update_mode(gt_client);
|
||
|
} else if (cmd_head.wr == GTP_RW_UPDATE_FW) {
|
||
|
/* Update firmware! */
|
||
|
show_len = 0;
|
||
|
total_len = 0;
|
||
|
if (cmd_head.data_len + 1 > data_length) {
|
||
|
dev_err(>_client->dev, "data len %d > data buff %d, rejected!\n",
|
||
|
cmd_head.data_len + 1, data_length);
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
memset(cmd_head.data, 0, cmd_head.data_len + 1);
|
||
|
memcpy(cmd_head.data, &userbuf[CMD_HEAD_LENGTH],
|
||
|
cmd_head.data_len);
|
||
|
|
||
|
if (gup_update_proc((void *)cmd_head.data) == FAIL) {
|
||
|
ret = -EBUSY;
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
ret = CMD_HEAD_LENGTH;
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*******************************************************
|
||
|
Function:
|
||
|
Goodix tool read function.
|
||
|
Input:
|
||
|
standard seq file read function param.
|
||
|
Output:
|
||
|
Return read length.
|
||
|
********************************************************/
|
||
|
static s32 goodix_tool_read(struct file *file, char __user *user_buf,
|
||
|
size_t count, loff_t *ppos)
|
||
|
{
|
||
|
u16 data_len = 0;
|
||
|
s32 ret;
|
||
|
u8 buf[32];
|
||
|
|
||
|
mutex_lock(&lock);
|
||
|
if (cmd_head.wr & 0x1) {
|
||
|
dev_err(>_client->dev, "command head wrong\n");
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
switch (cmd_head.wr) {
|
||
|
case GTP_RW_READ:
|
||
|
if (cmd_head.flag == GTP_NEED_FLAG) {
|
||
|
if (comfirm() == FAIL) {
|
||
|
dev_err(>_client->dev, "Comfirm fail!");
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
} else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
|
||
|
/* Need interrupt! */
|
||
|
}
|
||
|
|
||
|
memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
|
||
|
|
||
|
pr_debug("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0],
|
||
|
cmd_head.data[1]);
|
||
|
pr_debug("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0],
|
||
|
cmd_head.addr[1]);
|
||
|
|
||
|
if (cmd_head.delay)
|
||
|
msleep(cmd_head.delay);
|
||
|
|
||
|
data_len = cmd_head.data_len;
|
||
|
if (data_len <= 0 || (data_len > data_length)) {
|
||
|
dev_err(>_client->dev, "Invalid data length %d\n",
|
||
|
data_len);
|
||
|
ret = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
if (data_len > count)
|
||
|
data_len = count;
|
||
|
|
||
|
if (tool_i2c_read(cmd_head.data, data_len) <= 0) {
|
||
|
dev_err(>_client->dev, "Read data failed!\n");
|
||
|
ret = -EIO;
|
||
|
goto exit;
|
||
|
}
|
||
|
ret = simple_read_from_buffer(user_buf, count, ppos,
|
||
|
&cmd_head.data[GTP_ADDR_LENGTH], data_len);
|
||
|
break;
|
||
|
case GTP_RW_FILL_INFO:
|
||
|
ret = fill_update_info(user_buf, count, ppos);
|
||
|
break;
|
||
|
case GTP_RW_READ_VERSION:
|
||
|
/* Read driver version */
|
||
|
data_len = scnprintf(buf, sizeof(buf), "%s\n",
|
||
|
GTP_DRIVER_VERSION);
|
||
|
ret = simple_read_from_buffer(user_buf, count, ppos,
|
||
|
buf, data_len);
|
||
|
break;
|
||
|
default:
|
||
|
ret = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct file_operations goodix_proc_fops = {
|
||
|
.write = goodix_tool_write,
|
||
|
.read = goodix_tool_read,
|
||
|
.open = simple_open,
|
||
|
.owner = THIS_MODULE,
|
||
|
};
|
||
|
|
||
|
s32 init_wr_node(struct i2c_client *client)
|
||
|
{
|
||
|
u8 i;
|
||
|
|
||
|
gt_client = client;
|
||
|
memset(&cmd_head, 0, sizeof(cmd_head));
|
||
|
cmd_head.data = NULL;
|
||
|
|
||
|
i = GTP_I2C_RETRY_5;
|
||
|
while ((!cmd_head.data) && i) {
|
||
|
cmd_head.data = devm_kzalloc(&client->dev,
|
||
|
i * DATA_LENGTH_UINT, GFP_KERNEL);
|
||
|
if (cmd_head.data)
|
||
|
break;
|
||
|
i--;
|
||
|
}
|
||
|
if (i) {
|
||
|
data_length = i * DATA_LENGTH_UINT;
|
||
|
dev_dbg(&client->dev, "Applied memory size:%d.", data_length);
|
||
|
} else {
|
||
|
dev_err(&client->dev, "Apply for memory failed.");
|
||
|
return FAIL;
|
||
|
}
|
||
|
|
||
|
cmd_head.addr_len = 2;
|
||
|
cmd_head.retry = GTP_I2C_RETRY_5;
|
||
|
|
||
|
register_i2c_func();
|
||
|
|
||
|
mutex_init(&lock);
|
||
|
tool_set_proc_name(procname);
|
||
|
goodix_proc_entry = proc_create(procname,
|
||
|
S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
|
||
|
goodix_proc_entry,
|
||
|
&goodix_proc_fops);
|
||
|
if (goodix_proc_entry == NULL) {
|
||
|
dev_err(&client->dev, "Couldn't create proc entry!");
|
||
|
return FAIL;
|
||
|
}
|
||
|
|
||
|
return SUCCESS;
|
||
|
}
|