kernel-brax3-ubuntu-touch/drivers/input/touchscreen/focaltech_ft8725_spi/focaltech_hp.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

888 lines
25 KiB
C
Executable file

/*
*
* FocalTech TouchScreen driver.
*
* Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* 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.
*
*/
/*****************************************************************************
*
* File Name: focaltech_hp.c
*
* Author: Focaltech Driver Team
*
* Created: 2023-12-12
*
* Abstract: Host Processing
*
* Version: V1.0
*
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_FHP_NAME "fhp_ft"
#define FTS_FHPI_DEV_NAME "fhp_input"
#define DEFAULT_IOCTL_SPI_BUFSIZE 256
#define MAX_QUEUE_BUFSIZE (32768)
#define FHP_CMD_GET_FRAME 0x01
struct fhp_spi_sync_data {
char *tx;
char *rx;
unsigned int size;
};
struct fhp_input_report_data {
char *buffer;
unsigned int size;
};
#define FHP_IOCTL_DEVICE 0xC5
#define FHP_IOCTL_RESET _IOW(FHP_IOCTL_DEVICE, 0x01, long)
#define FHP_IOCTL_SPI_SYNC _IOWR(FHP_IOCTL_DEVICE, 0x02, struct fhp_spi_sync_data)
#define FHP_IOCTL_SET_IRQ _IOW(FHP_IOCTL_DEVICE, 0x03, long)
#define FHP_IOCTL_SET_RAW_SIZE _IOW(FHP_IOCTL_DEVICE, 0x04, long)
#define FHP_IOCTL_SET_SPI_SPEED _IOW(FHP_IOCTL_DEVICE, 0x05, long)
#define FHP_IOCTL_GET_CHIP_INIT_DONE _IOR(FHP_IOCTL_DEVICE, 0x06, long)
#define FHP_IOCTL_GET_FRAME _IOR(FHP_IOCTL_DEVICE, 0x07, void *)
#define FHP_IOCTL_CLEAR_FRAME _IOW(FHP_IOCTL_DEVICE, 0x08, long)
#define FHP_IOCTL_SET_TIMEOUT _IOW(FHP_IOCTL_DEVICE, 0x09, long)
#define FHP_IOCTL_REPORT _IOW(FHP_IOCTL_DEVICE, 0x0A, struct fhp_input_report_data)
#define FHP_INPUT_IOCTL_DEVICE 0xC6
#define FHP_INPUT_IOCTL_REPORT _IOW(FHP_INPUT_IOCTL_DEVICE, 0x01, struct fhp_input_report_data)
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
typedef enum {
FHP_FRAME_WAITQ_DEFAULT,
FHP_FRAME_WAITQ_WAIT,
FHP_FRAME_WAITQ_WAIT_POLL,
FHP_FRAME_WAITQ_WAKEUP,
} FHP_FRAME_WAITQ_FLAG;
struct fhp_frame {
u64 tv;
unsigned char value[0];
};
struct fhp_queue {
int head;
int tail;
int count;
int max_count;
int elem_size;
u8 *buffer;
struct mutex mutexq;
};
struct fhp_core_data {
struct fts_ts_data *ts_data;
struct miscdevice fhp_dev;
struct miscdevice fhpi_dev;
wait_queue_head_t frame_waitq;
struct fhp_queue q;
int fhp_dev_open_cnt;
int timeout; /*0:no wait, <0 wait infinitely, >0 wait timeout*/
int frame_waitq_flag;
int touch_size_bak;
};
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
static struct fhp_core_data *fts_fhp_data;
static long fhp_input_report(struct fhp_core_data *fhp_data, unsigned long arg);
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
static u64 fhp_get_timestamp(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
ktime_t tv;
tv = ktime_get_real() / 1000;
return (u64)tv;
#else
struct timeval tv;
do_gettimeofday(&tv);
return (u64)(((u64)tv.tv_sec * 1000000) + tv.tv_usec);
#endif
}
static int fhp_spi_sync(struct fhp_core_data *fhp_data, u8 *tx_buf, u8 *rx_buf, u32 len)
{
int ret = 0;
u8 *tbuf = NULL;
u8 *rbuf = NULL;
struct spi_message msg;
struct spi_transfer xfer = { 0 };
if (!fhp_data || !fhp_data->ts_data || !fhp_data->ts_data->spi) {
FTS_ERROR("fhp/ts data/spi is null");
return -EINVAL;
}
if (!fhp_data->ts_data->bus_tx_buf || !fhp_data->ts_data->bus_rx_buf || !tx_buf || !rx_buf || !len) {
FTS_ERROR("tx_buf/rx_buf is null/len(%d) invalid", len);
return -EINVAL;
}
mutex_lock(&fhp_data->ts_data->bus_lock);
if (len > FTS_MAX_BUS_BUF) {
tbuf = kzalloc(len, GFP_KERNEL);
if (NULL == tbuf) {
FTS_ERROR("txbuf malloc fail");
ret = -ENOMEM;
goto err_spi_sync;
}
rbuf = kzalloc(len, GFP_KERNEL);
if (NULL == rbuf) {
FTS_ERROR("rxbuf malloc fail");
ret = -ENOMEM;
goto err_spi_sync;
}
} else {
tbuf = fhp_data->ts_data->bus_tx_buf;
rbuf = fhp_data->ts_data->bus_rx_buf;
memset(tbuf, 0x0, FTS_MAX_BUS_BUF);
memset(rbuf, 0x0, FTS_MAX_BUS_BUF);
}
memcpy(tbuf, tx_buf, len);
xfer.tx_buf = tbuf;
xfer.rx_buf = rbuf;
xfer.len = len;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(fhp_data->ts_data->spi, &msg);
memcpy(rx_buf, rbuf, len);
if (ret) {
FTS_ERROR("spi_sync fail,addr:%x,status:%x,ret:%d", tbuf[0], rbuf[3], ret);
goto err_spi_sync;
}
ret = 0;
err_spi_sync:
if (len > FTS_MAX_BUS_BUF) {
if (tbuf) {
kfree(tbuf);
tbuf = NULL;
}
if (rbuf) {
kfree(rbuf);
rbuf = NULL;
}
}
mutex_unlock(&fhp_data->ts_data->bus_lock);
return ret;
}
static int fhpq_open(struct fhp_queue *q)
{
if (!q) {
FTS_ERROR("q is null");
return -EINVAL;
}
FTS_FUNC_ENTER();
mutex_lock(&(q->mutexq));
q->head = q->tail = q->count = 0;
q->buffer = kzalloc(MAX_QUEUE_BUFSIZE, GFP_KERNEL);
if (!q->buffer) {
FTS_ERROR("malloc queue buffer failed");
mutex_unlock(&(q->mutexq));
return -ENOMEM;
}
mutex_unlock(&(q->mutexq));
FTS_FUNC_EXIT();
return 0;
}
static int fhpq_set_element_size(struct fhp_queue *q, int elem_size)
{
int max_size = FTS_MAX_TOUCH_BUF - sizeof(struct fhp_frame);
if (!q || !elem_size || (elem_size > max_size)) {
FTS_ERROR("q is null/elem_size(%d) is invalid", elem_size);
return -EINVAL;
}
FTS_FUNC_ENTER();
mutex_lock(&(q->mutexq));
q->elem_size = elem_size;
q->max_count = MAX_QUEUE_BUFSIZE / elem_size;
FTS_INFO("set queue elem_size=%d,max_count=%d", q->elem_size, q->max_count);
mutex_unlock(&(q->mutexq));
FTS_FUNC_EXIT();
return 0;
}
static bool fhpq_full(struct fhp_queue *q)
{
return q->count == q->max_count;
}
static bool fhpq_empty(struct fhp_queue *q)
{
return q->count == 0;
}
static void fhpq_clear(struct fhp_queue *q)
{
mutex_lock(&(q->mutexq));
q->head = q->tail = q->count = 0;
mutex_unlock(&(q->mutexq));
}
static int fhpq_enqueue(struct fhp_queue *q, u8 *val, u64 timestamp)
{
struct fhp_frame *tail_elem = NULL;
if (!q || !val || !q->buffer || !q->elem_size || !q->max_count) {
FTS_ERROR("q/val/buffer is null/size is 0");
return -EINVAL;
}
mutex_lock(&(q->mutexq));
tail_elem = (struct fhp_frame *)&q->buffer[q->tail * q->elem_size];
tail_elem->tv = timestamp;
memcpy(tail_elem->value, val, q->elem_size);
q->tail = (q->tail + 1) % q->max_count;
if (fhpq_full(q)) {
q->head = (q->head + 1) % q->max_count;
} else {
q->count++;
}
mutex_unlock(&(q->mutexq));
return 0;
}
static int fhpq_dequeue_userspace(struct fhp_queue *q, void __user *buff)
{
if (!q || !buff || !q->buffer) {
FTS_ERROR("q/buff/buffer is null");
return -EINVAL;
}
mutex_lock(&(q->mutexq));
if (fhpq_empty(q)) {
mutex_unlock(&(q->mutexq));
return -ENODATA;
}
if (copy_to_user(buff, &q->buffer[q->head * q->elem_size], q->elem_size)) {
FTS_ERROR("copy frame data to user failed");
mutex_unlock(&(q->mutexq));
return -EFAULT;
}
q->head = (q->head + 1) % q->max_count;
q->count--;
mutex_unlock(&(q->mutexq));
return 0;
}
static int fhpq_release(struct fhp_queue *q)
{
if (q) {
mutex_lock(&(q->mutexq));
q->head = q->tail = q->count = 0;
q->max_count = q->elem_size = 0;
if (q->buffer) {
kfree(q->buffer);
q->buffer = NULL;
}
mutex_unlock(&(q->mutexq));
}
return 0;
}
static int fhp_ioctl_reset(struct fhp_core_data *fhp_data, unsigned long high)
{
if (!fhp_data || !fhp_data->ts_data) {
FTS_ERROR("fhp/ts data is null");
return -EINVAL;
}
return fts_set_reset(fhp_data->ts_data, (int)high);
}
static int fhp_ioctl_set_irq(struct fhp_core_data *fhp_data, unsigned long enable)
{
if (!fhp_data || !fhp_data->ts_data) {
FTS_ERROR("fhp/ts data is null");
return -EINVAL;
}
FTS_INFO("set irq to %s", enable ? "enable" : "disable");
if (enable)
fts_irq_enable();
else
fts_irq_disable();
return 0;
}
static int fhp_ioctl_spi_sync(struct fhp_core_data *fhp_data, unsigned long arg)
{
int ret = 0;
struct fhp_spi_sync_data spi_data;
u8 txbuf_temp[DEFAULT_IOCTL_SPI_BUFSIZE] = { 0 };
u8 rxbuf_temp[DEFAULT_IOCTL_SPI_BUFSIZE] = { 0 };
u8 *txbuf = txbuf_temp;
u8 *rxbuf = rxbuf_temp;
if (!fhp_data || !fhp_data->ts_data) {
FTS_ERROR("fhp/ts data is null");
return -EINVAL;
}
if (copy_from_user(&spi_data, (void *)arg, sizeof(struct fhp_spi_sync_data))) {
FTS_ERROR("copy spi_sync data from userspace fail");
return -EFAULT;
}
if (!spi_data.tx || !spi_data.rx || !spi_data.size) {
FTS_ERROR("tx/rx/size(%d) from userspace are invalid", spi_data.size);
return -EINVAL;
}
if (spi_data.size > DEFAULT_IOCTL_SPI_BUFSIZE) {
txbuf = kmalloc(spi_data.size, GFP_KERNEL);
rxbuf = kmalloc(spi_data.size, GFP_KERNEL);
if (!txbuf || !rxbuf) {
FTS_ERROR("kzalloc memory(size:%d) for spi tx/rx buffer fail", spi_data.size);
ret = -ENOMEM;
goto spi_sync_err;
}
}
memset(txbuf, 0, spi_data.size);
memset(rxbuf, 0, spi_data.size);
if (copy_from_user(txbuf, spi_data.tx, spi_data.size)) {
FTS_ERROR("copy spi tx data from userspace fail");
ret = -EFAULT;
goto spi_sync_err;
}
ret = fhp_spi_sync(fhp_data, txbuf, rxbuf, spi_data.size);
if (ret) {
FTS_ERROR("spi sync fail");
ret = -EIO;
goto spi_sync_err;
}
if (copy_to_user(spi_data.rx, rxbuf, spi_data.size)) {
FTS_ERROR("copy spi rx data to userspace fail");
ret = -EFAULT;
goto spi_sync_err;
}
return 0;
spi_sync_err:
if (spi_data.size > DEFAULT_IOCTL_SPI_BUFSIZE) {
kfree(txbuf);
kfree(rxbuf);
}
return ret;
}
static int fhp_ioctl_set_frame_size(struct fhp_core_data *fhp_data, unsigned long size)
{
int ret = 0;
int chip_frame_size = (int)size;
int frame_size = 0;
int max_size = FTS_MAX_TOUCH_BUF - sizeof(struct fhp_frame);
if (!fhp_data || !fhp_data->ts_data || (chip_frame_size <= 0) || (chip_frame_size > max_size)) {
FTS_ERROR("fhp/ts data is null/size(%d) is invalid", chip_frame_size);
return -EINVAL;
}
FTS_INFO("set frame size to %d", chip_frame_size);
frame_size = chip_frame_size + sizeof(struct fhp_frame);
ret = fhpq_set_element_size(&fhp_data->q, frame_size);
if (ret) {
FTS_ERROR("set queue element size failed,ret=%d", ret);
return ret;
}
fhp_data->touch_size_bak = fhp_data->ts_data->touch_size;
fhp_data->ts_data->touch_size = chip_frame_size;
return 0;
}
static int fhp_ioctl_set_spi_speed(struct fhp_core_data *fhp_data, unsigned long speed)
{
int ret = 0;
if (!fhp_data || !fhp_data->ts_data || !fhp_data->ts_data->spi) {
FTS_ERROR("fhp/ts data/spi is null");
return -EINVAL;
}
if (fhp_data->ts_data->spi->max_speed_hz == speed) {
FTS_INFO("spi speed == max_speed_hz");
return 0;
}
FTS_INFO("set spi speed to %ld", speed);
mutex_lock(&fhp_data->ts_data->bus_lock);
fhp_data->ts_data->spi->max_speed_hz = speed;
ret = spi_setup(fhp_data->ts_data->spi);
if (ret < 0) {
FTS_ERROR("spi setup fail,ret:%d", ret);
}
mutex_unlock(&fhp_data->ts_data->bus_lock);
return ret;
}
static int fhp_ioctl_get_chip_init_done(struct fhp_core_data *fhp_data, unsigned long arg)
{
int ret = 0;
u8 status = 0;
if (!fhp_data || !fhp_data->ts_data) {
FTS_ERROR("fhp/ts data is null");
return -EINVAL;
}
/* 1:init done, 0:not done, others:reserved */
status = fhp_data->ts_data->fw_loading ? 0 : 1;
FTS_INFO("chip init flag:%d", status);
if (copy_to_user((void *)arg, (u8 *)&status, sizeof(u8))) {
FTS_ERROR("copy chip_init_done flag data to userspace fail");
ret = -EFAULT;
return ret;
}
return 0;
}
static int fhp_ioctl_get_frame(struct fhp_core_data *fhp_data, unsigned long arg)
{
int ret = 0;
if (!fhp_data || !fhp_data->ts_data || !fhp_data->ts_data->touch_buf) {
FTS_ERROR("fhp/ts data is null");
return -EINVAL;
}
if (fhpq_empty(&fhp_data->q) && (fhp_data->frame_waitq_flag != FHP_FRAME_WAITQ_WAIT_POLL)) {
fhp_data->frame_waitq_flag = FHP_FRAME_WAITQ_WAIT;
if (fhp_data->timeout == 0) {
FTS_INFO("No frame data, and not wait");
return -ENODATA;
} else if (fhp_data->timeout < 0) {
wait_event_interruptible(fhp_data->frame_waitq,
fhp_data->frame_waitq_flag == FHP_FRAME_WAITQ_WAKEUP);
} else if (fhp_data->timeout > 0) {
ret = wait_event_interruptible_timeout(fhp_data->frame_waitq,
fhp_data->frame_waitq_flag == FHP_FRAME_WAITQ_WAKEUP,
msecs_to_jiffies(fhp_data->timeout));
if (ret == 0) return -ETIMEDOUT;
}
}
ret = fhpq_dequeue_userspace(&fhp_data->q, (void *)arg);
if (ret < 0) {
FTS_ERROR("dequeue frame failed");
return ret;
}
return 0;
}
static int fhp_ioctl_clear_frame(struct fhp_core_data *fhp_data, unsigned long arg)
{
if (fhp_data) {
FTS_INFO("clear queue frame");
fhpq_clear(&fhp_data->q);
}
return 0;
}
static int fhp_ioctl_set_timeout(struct fhp_core_data *fhp_data, unsigned long arg)
{
int timeout = (int)arg;
if (!fhp_data) {
FTS_ERROR("fhp data is null");
return -EINVAL;
}
if (fhp_data->timeout != timeout) {
FTS_INFO("set timeout:%d->%d", fhp_data->timeout, timeout);
fhp_data->timeout = timeout;
}
return 0;
}
static int fhp_open(struct inode *inode, struct file *file)
{
int ret = 0;
struct fhp_core_data *fhp_data = container_of(file->private_data, struct fhp_core_data, fhp_dev);
FTS_FUNC_ENTER();
if (!fhp_data && !fhp_data->ts_data) {
FTS_ERROR("fhp_data/ts_data is null");
return -ENODATA;
}
if (fhp_data->fhp_dev_open_cnt >= 1) {
FTS_ERROR("open fail(cnt:%d)", fhp_data->fhp_dev_open_cnt);
return -EBUSY;
}
fts_irq_disable();
ret = fhpq_open(&fhp_data->q);
if (ret) {
FTS_ERROR("queue open failed");
return ret;
}
fhp_data->frame_waitq_flag = FHP_FRAME_WAITQ_DEFAULT;
fhp_data->timeout = 0;
fhp_data->fhp_dev_open_cnt++;
fhp_data->ts_data->fhp_mode = ENABLE;
FTS_INFO("open successfully(cnt:%d)", fhp_data->fhp_dev_open_cnt);
FTS_FUNC_EXIT();
return 0;
}
static int fhp_close(struct inode *inode, struct file *file)
{
struct fhp_core_data *fhp_data = container_of(file->private_data, struct fhp_core_data, fhp_dev);
FTS_FUNC_ENTER();
if (!fhp_data && !fhp_data->ts_data) {
FTS_ERROR("fhp_data/ts_data is null");
return -ENODATA;
}
fhpq_release(&fhp_data->q);
fhp_data->fhp_dev_open_cnt--;
fhp_data->ts_data->fhp_mode = DISABLE;
fhp_data->ts_data->touch_size = fhp_data->touch_size_bak;
fts_irq_enable();
FTS_FUNC_EXIT();
return 0;
}
static long fhp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct fhp_core_data *fhp_data = container_of(filp->private_data, struct fhp_core_data, fhp_dev);
if (!fhp_data) {
FTS_ERROR("fhp_data is null");
return -ENODATA;
}
//FTS_DEBUG("ioctl cmd:%x,arg:%lx", cmd, arg);
switch (cmd) {
case FHP_IOCTL_GET_FRAME:
ret = fhp_ioctl_get_frame(fhp_data, arg);
break;
case FHP_IOCTL_SPI_SYNC:
ret = fhp_ioctl_spi_sync(fhp_data, arg);
break;
case FHP_IOCTL_RESET:
ret = fhp_ioctl_reset(fhp_data, arg);
break;
case FHP_IOCTL_SET_IRQ:
ret = fhp_ioctl_set_irq(fhp_data, arg);
break;
case FHP_IOCTL_SET_RAW_SIZE:
fhp_ioctl_set_frame_size(fhp_data, arg);
break;
case FHP_IOCTL_SET_SPI_SPEED:
ret = fhp_ioctl_set_spi_speed(fhp_data, arg);
break;
case FHP_IOCTL_GET_CHIP_INIT_DONE:
ret = fhp_ioctl_get_chip_init_done(fhp_data, arg);
break;
case FHP_IOCTL_CLEAR_FRAME:
ret = fhp_ioctl_clear_frame(fhp_data, arg);
break;
case FHP_IOCTL_SET_TIMEOUT:
ret = fhp_ioctl_set_timeout(fhp_data, arg);
break;
case FHP_IOCTL_REPORT:
ret = fhp_input_report(fhp_data, arg);
break;
default:
FTS_ERROR("unkown ioctl cmd(0x%x)", (int)cmd);
return -EINVAL;
}
return ret;
}
static unsigned int fhp_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct fhp_core_data *fhp_data = container_of(filp->private_data, struct fhp_core_data, fhp_dev);
if (!fhp_data) {
FTS_ERROR("fhp_data is null");
return -ENODATA;
}
poll_wait(filp, &fhp_data->frame_waitq, wait);
if (fhp_data->frame_waitq_flag == FHP_FRAME_WAITQ_WAKEUP)
mask |= POLLIN | POLLRDNORM;
fhp_data->frame_waitq_flag = FHP_FRAME_WAITQ_WAIT_POLL;
return mask;
}
static struct file_operations fhp_fops = {
.open = fhp_open,
.release = fhp_close,
.unlocked_ioctl = fhp_ioctl,
.poll = fhp_poll,
};
static int fhp_miscdev_init(struct fhp_core_data *fhp_data)
{
int ret = 0;
fhp_data->fhp_dev.minor = MISC_DYNAMIC_MINOR;
fhp_data->fhp_dev.name = FTS_FHP_NAME;
fhp_data->fhp_dev.fops = &fhp_fops;
ret = misc_register(&fhp_data->fhp_dev);
if (ret) {
FTS_ERROR("misc_register(fhp_dev) fail");
return ret;
}
FTS_INFO("misc_register(fhp_dev) success");
return 0;
}
static long fhp_input_report(struct fhp_core_data *fhp_data, unsigned long arg)
{
int ret = 0;
u8 report_buf[FTS_TOUCH_DATA_LEN_V2] = { 0 };
struct fhp_input_report_data report;
if (!fhp_data || !fhp_data->ts_data) {
FTS_ERROR("fhp_data/ts_data is null");
return -EINVAL;
}
if (copy_from_user(&report, (void *)arg, sizeof(struct fhp_input_report_data))) {
FTS_ERROR("copy input touch report data from userspace fail");
return -EFAULT;
}
if ((report.size <= 0) || (report.size > FTS_TOUCH_DATA_LEN_V2)) {
FTS_ERROR("report size(%d) is invalid", report.size);
return -EINVAL;
}
memset(report_buf, 0xFF, FTS_TOUCH_DATA_LEN_V2);
if (copy_from_user(report_buf, report.buffer , report.size)) {
FTS_ERROR("copy input touch report buffer from userspace fail");
return -EFAULT;
}
if ((report_buf[1] == 0xFF) && (report_buf[2] == 0xFF) && (report_buf[3] == 0xFF) && (report_buf[4] == 0xFF)) {
fts_release_all_finger();
fts_tp_state_recovery(fhp_data->ts_data);
return 0;
}
ret = fts_input_report_buffer(fhp_data->ts_data, report_buf);
if (ret < 0) {
FTS_ERROR("report buffer failed");
return ret;
}
return 0;
}
static int fhp_input_open(struct inode *inode, struct file *file)
{
return 0;
}
static int fhp_input_close(struct inode *inode, struct file *file)
{
return 0;
}
static long fhp_input_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct fhp_core_data *fhp_data = container_of(filp->private_data, struct fhp_core_data, fhpi_dev);
if (!fhp_data) {
FTS_ERROR("fhp_data is null");
return -ENODATA;
}
switch (cmd) {
case FHP_INPUT_IOCTL_REPORT:
ret = fhp_input_report(fhp_data, arg);
break;
default:
FTS_ERROR("unkown ioctl cmd(0x%x)", (int)cmd);
return -EINVAL;
}
return ret;
}
/*
* On certain platforms, it is necessary to initialize the open function in order to initialize the private_data of the current device.
* Otherwise, there may be a situation where using container_of to retrieve the device fails.
*/
static struct file_operations fhp_input_fops = {
.open = fhp_input_open,
.release = fhp_input_close,
.unlocked_ioctl = fhp_input_ioctl,
};
static int fhp_input_miscdev_init(struct fhp_core_data *fhp_data)
{
int ret = 0;
fhp_data->fhpi_dev.minor = MISC_DYNAMIC_MINOR;
fhp_data->fhpi_dev.name = FTS_FHPI_DEV_NAME;
fhp_data->fhpi_dev.fops = &fhp_input_fops;
ret = misc_register(&fhp_data->fhpi_dev);
if (ret) {
FTS_ERROR("misc_register(input misc device) fail");
return ret;
}
FTS_INFO("misc_register(input misc device) success");
return 0;
}
static int fhp_get_frame(u8 *touch_buf, u32 touch_size)
{
struct fhp_core_data *fhp_data = fts_fhp_data;
if (!touch_buf || !touch_size) {
FTS_ERROR("touch_buf is null/touch_size is 0");
return -EINVAL;
}
if (fhp_data->q.elem_size != (sizeof(struct fhp_frame) + touch_size)) {
FTS_ERROR("touch size(%u,%d,%lu) is invalid", touch_size, fhp_data->q.elem_size, sizeof(struct fhp_frame));
return -EINVAL;
}
fhpq_enqueue(&fhp_data->q, touch_buf, fhp_get_timestamp());
if ((fhp_data->frame_waitq_flag == FHP_FRAME_WAITQ_WAIT)
|| (fhp_data->frame_waitq_flag == FHP_FRAME_WAITQ_WAIT_POLL)) {
fhp_data->frame_waitq_flag = FHP_FRAME_WAITQ_WAKEUP;
wake_up_interruptible(&fhp_data->frame_waitq);
}
return 0;
}
int fts_fhp_irq_handler(struct fts_ts_data *ts_data)
{
int ret = 0;
u8 cmd = FHP_CMD_GET_FRAME;
if (!ts_data || !ts_data->touch_buf) {
FTS_ERROR("ts_data/touch_buf is null");
return -EINVAL;
}
if (!ts_data->touch_size || (ts_data->touch_size > FTS_MAX_TOUCH_BUF)) {
FTS_ERROR("touch_size(%d) is invalid", ts_data->touch_size);
return -EINVAL;
}
if (ts_data->suspended && ts_data->gesture_support) {
fts_gesture_readdata(ts_data, NULL);
return 0;
}
ret = fts_read(&cmd, 1, ts_data->touch_buf, ts_data->touch_size);
if (ret < 0) {
FTS_ERROR("read frame data failed");
return ret;
}
ret = fhp_get_frame(ts_data->touch_buf, ts_data->touch_size);
if (ret < 0) {
FTS_ERROR("get frame failed");
return ret;
}
return 0;
}
int fts_fhp_init(struct fts_ts_data *ts_data)
{
int ret = 0;
struct fhp_core_data *fhp_data = NULL;
FTS_FUNC_ENTER();
fhp_data = kzalloc(sizeof(struct fhp_core_data), GFP_KERNEL);
if (!fhp_data) {
FTS_ERROR("allocate memory for fhp_data failed");
return -ENOMEM;
}
fts_fhp_data = fhp_data;
fhp_data->ts_data = ts_data;
fhp_data->fhp_dev_open_cnt = 0;
fhp_data->frame_waitq_flag = FHP_FRAME_WAITQ_DEFAULT;
init_waitqueue_head(&fhp_data->frame_waitq);
mutex_init(&fhp_data->q.mutexq);
/*register /dev/fhp_ft*/
ret = fhp_miscdev_init(fhp_data);
if (ret) {
FTS_ERROR("fhp_dev miscdev register fail");
goto err_miscdev_init;
}
ret = fhp_input_miscdev_init(fhp_data);
if (ret) {
FTS_ERROR("input misc device register fail");
goto err_input_miscdev_init;
}
FTS_FUNC_EXIT();
return 0;
err_input_miscdev_init:
misc_deregister(&fhp_data->fhp_dev);
err_miscdev_init:
kfree(fhp_data);
fhp_data = NULL;
return ret;
}
void fts_fhp_exit(struct fts_ts_data *ts_data)
{
struct fhp_core_data *fhp_data = fts_fhp_data;
FTS_FUNC_ENTER();
if (fhp_data) {
misc_deregister(&fhp_data->fhpi_dev);
misc_deregister(&fhp_data->fhp_dev);
kfree(fhp_data);
fhp_data = NULL;
}
FTS_FUNC_EXIT();
}