blob: 6a550329b9fe8ef3aa2994eba8269793afe0deb0 [file] [log] [blame] [edit]
/*
* Goodix Gesture Module
*
* Copyright (C) 2019 - 2020 Goodix, Inc.
*
* 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.
*/
#include "goodix_ts_core.h"
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/version.h>
/*
* [GOOG]
* Move GOODIX_GESTURE_* define to goodix_ts_core.h.
*/
static ssize_t gsx_double_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
uint32_t type = cd->gesture_type;
return sprintf(buf, "%s\n",
(type & GESTURE_DOUBLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_double_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
if (buf[0] == '1') {
ts_info("enable double tap");
cd->gesture_type |= GESTURE_DOUBLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable double tap");
cd->gesture_type &= ~GESTURE_DOUBLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_single_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
uint32_t type = cd->gesture_type;
return sprintf(buf, "%s\n",
(type & GESTURE_SINGLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_single_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
if (buf[0] == '1') {
ts_info("enable single tap");
cd->gesture_type |= GESTURE_SINGLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable single tap");
cd->gesture_type &= ~GESTURE_SINGLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_fod_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
uint32_t type = cd->gesture_type;
return sprintf(
buf, "%s\n", (type & GESTURE_FOD_PRESS) ? "enable" : "disable");
}
static ssize_t gsx_fod_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct device *device =
container_of(((struct kobject *)dev)->parent, struct device, kobj);
struct goodix_ts_core *cd = dev_get_drvdata(device);
if (buf[0] == '1') {
ts_info("enable fod");
cd->gesture_type |= GESTURE_FOD_PRESS;
} else if (buf[0] == '0') {
ts_info("disable fod");
cd->gesture_type &= ~GESTURE_FOD_PRESS;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static DEVICE_ATTR(double_type, 0664, gsx_double_type_show, gsx_double_type_store);
static DEVICE_ATTR(single_type, 0664, gsx_single_type_show, gsx_single_type_store);
static DEVICE_ATTR(fod_type, 0664, gsx_fod_type_show, gsx_fod_type_store);
static struct attribute *gesture_attrs[] = {
&dev_attr_double_type.attr,
&dev_attr_single_type.attr,
&dev_attr_fod_type.attr,
NULL,
};
const static struct attribute_group gesture_sysfs_group = {
.attrs = gesture_attrs,
};
/* [GOOG]
* Use cd->gesture_data to avoid data racing issue.
*/
int goodix_ts_report_gesture(struct goodix_ts_core *cd)
{
int coor_x, coor_y, coor_size, coor_press;
int major, minor, orientation;
unsigned char event_type = GOODIX_GESTURE_UNKNOWN;
mutex_lock(&cd->gesture_data_lock);
coor_x = le16_to_cpup((__le16 *)cd->gesture_data.data);
coor_y = le16_to_cpup((__le16 *)(cd->gesture_data.data + 2));
coor_size = le16_to_cpup((__le16 *)(cd->gesture_data.data + 4));
coor_press = cd->gesture_data.data[6];
major = le16_to_cpup((__le16 *)(cd->gesture_data.data + 7));
minor = le16_to_cpup((__le16 *)(cd->gesture_data.data + 9));
orientation = (s8)cd->gesture_data.data[11];
event_type = cd->gesture_data.event_type;
mutex_unlock(&cd->gesture_data_lock);
switch (event_type) {
case GOODIX_GESTURE_SINGLE_TAP:
if (cd->gesture_type & GESTURE_SINGLE_TAP) {
ts_info("get SINGLE-TAP gesture");
ts_debug(
"fodx:%d fody:%d size:%d press:%d maj:%d min:%d ori:%d",
coor_x, coor_y, coor_size, coor_press, major,
minor, orientation);
input_report_key(cd->input_dev, BTN_TOUCH, 1);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(
cd->input_dev, MT_TOOL_FINGER, 1);
input_report_abs(
cd->input_dev, ABS_MT_POSITION_X, coor_x);
input_report_abs(
cd->input_dev, ABS_MT_POSITION_Y, coor_y);
input_report_abs(
cd->input_dev, ABS_MT_PRESSURE, coor_press);
input_report_abs(
cd->input_dev, ABS_MT_TOUCH_MAJOR, major);
input_report_abs(
cd->input_dev, ABS_MT_TOUCH_MINOR, minor);
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, BTN_TOUCH, 0);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(
cd->input_dev, MT_TOOL_FINGER, 0);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable SINGLE-TAP");
}
break;
case GOODIX_GESTURE_DOUBLE_TAP:
if (cd->gesture_type & GESTURE_DOUBLE_TAP) {
ts_info("get DOUBLE-TAP gesture");
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable DOUBLE-TAP");
}
break;
case GOODIX_GESTURE_FOD_DOWN:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-DOWN gesture");
ts_debug(
"fodx:%d fody:%d size:%d press:%d maj:%d min:%d ori:%d",
coor_x, coor_y, coor_size, coor_press, major,
minor, orientation);
input_report_key(cd->input_dev, BTN_TOUCH, 1);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(
cd->input_dev, MT_TOOL_FINGER, 1);
input_report_abs(
cd->input_dev, ABS_MT_POSITION_X, coor_x);
input_report_abs(
cd->input_dev, ABS_MT_POSITION_Y, coor_y);
input_report_abs(
cd->input_dev, ABS_MT_PRESSURE, coor_press);
input_report_abs(
cd->input_dev, ABS_MT_TOUCH_MAJOR, major);
input_report_abs(
cd->input_dev, ABS_MT_TOUCH_MINOR, minor);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-DOWN");
}
break;
case GOODIX_GESTURE_FOD_UP:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-UP gesture");
input_report_key(cd->input_dev, BTN_TOUCH, 0);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(
cd->input_dev, MT_TOOL_FINGER, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-UP");
}
break;
default:
ts_err("not support gesture type[%02X]", event_type);
break;
}
return 0;
}
int gesture_module_init(struct goodix_ts_core *core_data)
{
int ret = 0;
struct kobject *parent = &core_data->pdev->dev.kobj;
/* gesture sysfs init */
core_data->gesture_kobj = kobject_create_and_add("gesture", parent);
if (!core_data->gesture_kobj) {
ts_err("failed create gesture sysfs node!");
ret = -ENOENT; /* [GOOG] */
goto err_out;
}
ret = sysfs_create_group(core_data->gesture_kobj, &gesture_sysfs_group);
if (ret) {
ts_err("failed create gesture sysfs files");
kobject_put(core_data->gesture_kobj);
goto err_out;
}
ts_info("gesture module init success");
return 0;
err_out:
ts_err("gesture module init failed!");
return ret;
}
void gesture_module_exit(struct goodix_ts_core *core_data)
{
ts_info("gesture module exit");
sysfs_remove_group(core_data->gesture_kobj, &gesture_sysfs_group);
kobject_put(core_data->gesture_kobj);
}