diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-09-09 11:53:11 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-09-09 11:53:11 -0700 |
| commit | 272c3ee99c568e5e6da08e24ebcccabdbed860ce (patch) | |
| tree | a30a4885f14eebafdd86f73f8d992cf7959ac744 | |
| parent | f479b5032cab7da452d59464d14e188f06ec3e19 (diff) | |
| parent | 93d6b9b0b4754ffbe1e849dafc4424b058aa517b (diff) | |
Merge "input: ft5x06: create secure touch sysfs files"
| -rw-r--r-- | drivers/input/touchscreen/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/input/touchscreen/ft5x06_ts.c | 249 |
2 files changed, 257 insertions, 2 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index bc5126a08ecb..b02abfc58aea 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1195,4 +1195,14 @@ config TOUCHSCREEN_FT5X06 To compile this driver as a module, choose M here: the module will be called ft5x06_ts. +config FT_SECURE_TOUCH + bool "Secure Touch support for Focaltech Touchscreen" + depends on TOUCHSCREEN_FT5X06 + help + Say Y here + -Focaltech touch driver is connected + -To enable secure touch for Focaltech touch driver + + If unsure, say N. + endif diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 17bce3d18d2f..2db0256b22a7 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -47,6 +47,12 @@ #define FT_SUSPEND_LEVEL 1 #endif +#if defined(CONFIG_FT_SECURE_TOUCH) +#include <linux/completion.h> +#include <linux/atomic.h> +#include <linux/pm_runtime.h> +#endif + #define FT_DRIVER_VERSION 0x02 #define FT_META_REGS 3 @@ -202,6 +208,8 @@ #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" #define PINCTRL_STATE_RELEASE "pmx_ts_release" +static irqreturn_t ft5x06_ts_interrupt(int irq, void *data); + enum { FT_BLOADER_VERSION_LZ4 = 0, FT_BLOADER_VERSION_Z7 = 1, @@ -240,6 +248,7 @@ struct ft5x06_ts_data { struct ft5x06_gesture_platform_data *gesture_pdata; struct regulator *vdd; struct regulator *vcc_i2c; + struct mutex ft_clk_io_ctrl_mutex; char fw_name[FT_FW_NAME_MAX_LEN]; bool loading_fw; u8 family_id; @@ -262,11 +271,200 @@ struct ft5x06_ts_data { struct pinctrl_state *pinctrl_state_active; struct pinctrl_state *pinctrl_state_suspend; struct pinctrl_state *pinctrl_state_release; +#if defined(CONFIG_FT_SECURE_TOUCH) + atomic_t st_enabled; + atomic_t st_pending_irqs; + struct completion st_powerdown; + struct completion st_irq_processed; +#endif }; static int ft5x06_ts_start(struct device *dev); static int ft5x06_ts_stop(struct device *dev); +#if defined(CONFIG_FT_SECURE_TOUCH) +static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data) +{ + init_completion(&data->st_powerdown); + init_completion(&data->st_irq_processed); +} + +static void ft5x06_secure_touch_notify(struct ft5x06_ts_data *data) +{ + sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch"); +} + +static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data) +{ + if (atomic_read(&data->st_enabled)) { + if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) { + reinit_completion(&data->st_irq_processed); + ft5x06_secure_touch_notify(data); + wait_for_completion_interruptible( + &data->st_irq_processed); + } + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* + * 'blocking' variable will have value 'true' when we want to prevent the driver + * from accessing the xPU/SMMU protected HW resources while the session is + * active. + */ +static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking) +{ + if (atomic_read(&data->st_enabled)) { + atomic_set(&data->st_pending_irqs, -1); + ft5x06_secure_touch_notify(data); + if (blocking) + wait_for_completion_interruptible( + &data->st_powerdown); + } +} + +static int ft5x06_bus_get(struct ft5x06_ts_data *data) +{ + int retval; + + mutex_lock(&data->ft_clk_io_ctrl_mutex); + retval = pm_runtime_get_sync(data->client->adapter->dev.parent); + mutex_unlock(&data->ft_clk_io_ctrl_mutex); + return retval; +} + +static void ft5x06_bus_put(struct ft5x06_ts_data *data) +{ + mutex_lock(&data->ft_clk_io_ctrl_mutex); + pm_runtime_put_sync(data->client->adapter->dev.parent); + mutex_unlock(&data->ft_clk_io_ctrl_mutex); +} + +static ssize_t ft5x06_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled)); +} + +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process and + * the interrupt handler. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t ft5x06_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + err = count; + switch (value) { + case 0: + if (atomic_read(&data->st_enabled) == 0) + break; + ft5x06_bus_put(data); + atomic_set(&data->st_enabled, 0); + ft5x06_secure_touch_notify(data); + complete(&data->st_irq_processed); + ft5x06_ts_interrupt(data->client->irq, data); + complete(&data->st_powerdown); + break; + + case 1: + if (atomic_read(&data->st_enabled)) { + err = -EBUSY; + break; + } + synchronize_irq(data->client->irq); + if (ft5x06_bus_get(data) < 0) { + dev_err(data->client->dev.parent, + "focalTech_bus_get failed\n"); + err = -EIO; + break; + } + reinit_completion(&data->st_powerdown); + reinit_completion(&data->st_irq_processed); + atomic_set(&data->st_enabled, 1); + atomic_set(&data->st_pending_irqs, 0); + break; + + default: + dev_err(data->client->dev.parent, + "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +/* + * This function returns whether there are pending interrupts, or + * other error conditions that need to be signaled to the userspace library, + * according tot he following logic: + * - st_enabled is 0 if secure touch is not enabled, returning -EBADF + * - st_pending_irqs is -1 to signal that secure touch is in being stopped, + * returning -EINVAL + * - st_pending_irqs is 1 to signal that there is a pending irq, returning + * the value "1" to the sysfs read operation + * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt + * has been processed, so the interrupt handler can be allowed to continue. + */ +static ssize_t ft5x06_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int val = 0; + + if (atomic_read(&data->st_enabled) == 0) + return -EBADF; + if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1) + val = 1; + else + complete(&data->st_irq_processed); + return scnprintf(buf, PAGE_SIZE, "%u", val); +} +#else +static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data) +{ +} +static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data) +{ + return IRQ_NONE; +} +static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking) +{ +} +#endif + +static struct device_attribute attrs[] = { +#if defined(CONFIG_FT_SECURE_TOUCH) + __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + ft5x06_secure_touch_enable_show, + ft5x06_secure_touch_enable_store), + __ATTR(secure_touch, S_IRUGO, + ft5x06_secure_touch_show, NULL), +#endif +}; + static inline bool ft5x06_gesture_support_enabled(void) { return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_GESTURE); @@ -593,6 +791,9 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } + if (ft5x06_filter_interrupt(data) == IRQ_HANDLED) + return IRQ_HANDLED; + ip_dev = data->input_dev; buf = data->tch_data; @@ -620,6 +821,10 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) } for (i = 0; i < data->pdata->num_max_touches; i++) { + /* + * Getting the finger ID of the touch event incase of + * multiple touch events + */ id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; if (id >= FT_MAX_ID) break; @@ -1057,6 +1262,8 @@ static int ft5x06_ts_suspend(struct device *dev) return 0; } + ft5x06_secure_touch_stop(data, true); + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support && device_may_wakeup(dev) && data->gesture_pdata->gesture_enable_to_set) { @@ -1083,6 +1290,8 @@ static int ft5x06_ts_resume(struct device *dev) return 0; } + ft5x06_secure_touch_stop(data, true); + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support && device_may_wakeup(dev) && !(data->gesture_pdata->in_pocket) && @@ -1185,6 +1394,13 @@ static void ft5x06_ts_early_suspend(struct early_suspend *handler) struct ft5x06_ts_data, early_suspend); + /* + * During early suspend/late resume, the driver doesn't access xPU/SMMU + * protected HW resources. So, there is no compelling need to block, + * but notifying the userspace that a power event has occurred is + * enough. Hence 'blocking' variable can be set to false. + */ + ft5x06_secure_touch_stop(data, false); ft5x06_ts_suspend(&data->client->dev); } @@ -1194,6 +1410,7 @@ static void ft5x06_ts_late_resume(struct early_suspend *handler) struct ft5x06_ts_data, early_suspend); + ft5x06_secure_touch_stop(data, false); ft5x06_ts_resume(&data->client->dev); } #endif @@ -1984,7 +2201,8 @@ static int ft5x06_ts_probe(struct i2c_client *client, struct dentry *temp; u8 reg_value; u8 reg_addr; - int err, len; + int err, len, retval, attr_count; + if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, sizeof(struct ft5x06_ts_platform_data), GFP_KERNEL); @@ -2284,6 +2502,23 @@ static int ft5x06_ts_probe(struct i2c_client *client, } } + /*Initialize secure touch */ + ft5x06_secure_touch_init(data); + ft5x06_secure_touch_stop(data, true); + mutex_init(&(data->ft_clk_io_ctrl_mutex)); + + /* Creation of secure touch sysfs files */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto free_secure_touch_sysfs; + } + } + ft5x06_update_fw_ver(data); ft5x06_update_fw_vendor_id(data); @@ -2311,6 +2546,11 @@ static int ft5x06_ts_probe(struct i2c_client *client, #endif return 0; +free_secure_touch_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } free_debug_dir: debugfs_remove_recursive(data->dir); free_force_update_fw_sys: @@ -2371,7 +2611,7 @@ unreg_inputdev: static int ft5x06_ts_remove(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); - int retval; + int retval, attr_count; if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) { device_init_wakeup(&client->dev, 0); @@ -2414,6 +2654,11 @@ static int ft5x06_ts_remove(struct i2c_client *client) } } + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + if (data->pdata->power_on) data->pdata->power_on(false); else |
