input: touchscreen: Import Synaptics ClearPad driver
* Sony package version: 52.1.A.0.532 Change-Id: Ie821d2ef530412b1c8aa40ed16e3b3091b5890c0 Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
parent
8a4a1b721e
commit
24b7b040df
8 changed files with 10804 additions and 0 deletions
|
|
@ -164,6 +164,26 @@ config TOUCHSCREEN_CHIPONE_ICN8318
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called chipone_icn8318.
|
||||
|
||||
config TOUCHSCREEN_CLEARPAD
|
||||
tristate "Synaptics ClearPad"
|
||||
default n
|
||||
help
|
||||
Say Y here to enable Synaptics ClearPad touchscreen.
|
||||
|
||||
config TOUCHSCREEN_CLEARPAD_I2C
|
||||
tristate "Synaptics ClearPad I2C"
|
||||
depends on I2C && TOUCHSCREEN_CLEARPAD
|
||||
default n
|
||||
help
|
||||
Say Y here to enable Synaptics ClearPad I2C touchscreen.
|
||||
|
||||
config TOUCHSCREEN_CLEARPAD_RMI_DEV
|
||||
tristate "Synaptics ClearPad RMI Dev"
|
||||
depends on GPIO_SYSFS && TOUCHSCREEN_CLEARPAD && TOUCHSCREEN_CLEARPAD_I2C
|
||||
default n
|
||||
help
|
||||
Say Y here to enable Synaptics ClearPad RMI dev.
|
||||
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV) += clearpad_rmi_dev.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD) += clearpad_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_I2C) += clearpad_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
|
||||
|
|
|
|||
9475
drivers/input/touchscreen/clearpad_core.c
Normal file
9475
drivers/input/touchscreen/clearpad_core.c
Normal file
File diff suppressed because it is too large
Load diff
335
drivers/input/touchscreen/clearpad_i2c.c
Normal file
335
drivers/input/touchscreen/clearpad_i2c.c
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/* msm/clearpad/clearpad_i2c/clearpad_i2c.c
|
||||
*
|
||||
* Copyright (c) 2011 Synaptics Incorporated
|
||||
* Copyright (c) 2011 Unixphere
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* Author: Yusuke Yoshimura <Yusuke.Yoshimura@sonyericsson.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/async.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/clearpad.h>
|
||||
|
||||
#define CLEARPAD_PAGE_SELECT_REGISTER 0xff
|
||||
#define CLEARPAD_REG(addr) ((addr) & 0xff)
|
||||
#define CLEARPAD_PAGE(addr) (((addr) >> 8) & 0xff)
|
||||
|
||||
struct clearpad_i2c_t {
|
||||
struct platform_device *pdev;
|
||||
unsigned int page;
|
||||
struct mutex page_mutex;
|
||||
};
|
||||
|
||||
static int clearpad_i2c_set_page(struct device *dev, u8 page)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
|
||||
char txbuf[2] = {CLEARPAD_PAGE_SELECT_REGISTER, page};
|
||||
int rc = 0;
|
||||
|
||||
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
|
||||
if (rc != sizeof(txbuf)) {
|
||||
dev_err(dev,
|
||||
"%s: set page failed: %d.", __func__, rc);
|
||||
rc = (rc < 0) ? rc : -EIO;
|
||||
goto exit;
|
||||
}
|
||||
this->page = page;
|
||||
rc = 0;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clearpad_i2c_read(struct device *dev, u16 addr, u8 *buf, u8 len)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
|
||||
s32 rc = 0;
|
||||
u8 page = CLEARPAD_PAGE(addr);
|
||||
u8 reg = CLEARPAD_REG(addr);
|
||||
int rsize = I2C_SMBUS_BLOCK_MAX;
|
||||
int off;
|
||||
|
||||
mutex_lock(&this->page_mutex);
|
||||
|
||||
if (page != this->page) {
|
||||
rc = clearpad_i2c_set_page(dev, page);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (off = 0; off < len; off += rsize) {
|
||||
if (len < off + I2C_SMBUS_BLOCK_MAX)
|
||||
rsize = len - off;
|
||||
rc = i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
|
||||
reg + off, rsize, &buf[off]);
|
||||
if (rc != rsize) {
|
||||
dev_err(dev, "%s: rc = %d\n", __func__, rc);
|
||||
if (rc > 0) {
|
||||
off += rc;
|
||||
break;
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
rc = off;
|
||||
exit:
|
||||
mutex_unlock(&this->page_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clearpad_i2c_write(struct device *dev, u16 addr,
|
||||
const u8 *buf, u8 len)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
|
||||
int rc = 0;
|
||||
u8 reg = CLEARPAD_REG(addr);
|
||||
u8 i;
|
||||
|
||||
mutex_lock(&this->page_mutex);
|
||||
|
||||
if (CLEARPAD_PAGE(addr) != this->page) {
|
||||
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
rc = i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
reg + i, buf[i]);
|
||||
if (rc)
|
||||
goto exit;
|
||||
}
|
||||
rc = i;
|
||||
exit:
|
||||
mutex_unlock(&this->page_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clearpad_i2c_read_block(struct device *dev, u16 addr, u8 *buf,
|
||||
int len)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
|
||||
u8 txbuf[1] = {addr & 0xff};
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&this->page_mutex);
|
||||
|
||||
if (CLEARPAD_PAGE(addr) != this->page) {
|
||||
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
|
||||
if (rc != sizeof(txbuf)) {
|
||||
rc = (rc < 0) ? rc : -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = i2c_master_recv(to_i2c_client(dev), buf, len);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: rc = %d\n", __func__, rc);
|
||||
exit:
|
||||
mutex_unlock(&this->page_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clearpad_i2c_write_block(struct device *dev, u16 addr, const u8 *buf,
|
||||
int len)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(dev);
|
||||
u8 txbuf[len + 1];
|
||||
int rc = 0;
|
||||
|
||||
txbuf[0] = addr & 0xff;
|
||||
memcpy(txbuf + 1, buf, len);
|
||||
|
||||
mutex_lock(&this->page_mutex);
|
||||
|
||||
if (CLEARPAD_PAGE(addr) != this->page) {
|
||||
rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr));
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf));
|
||||
if (rc < 0)
|
||||
dev_err(dev, "%s: rc = %d\n", __func__, rc);
|
||||
else
|
||||
rc -= 1;
|
||||
exit:
|
||||
mutex_unlock(&this->page_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct clearpad_bus_data_t clearpad_i2c_bus_data = {
|
||||
.bustype = BUS_I2C,
|
||||
.set_page = clearpad_i2c_set_page,
|
||||
.read = clearpad_i2c_read,
|
||||
.write = clearpad_i2c_write,
|
||||
.read_block = clearpad_i2c_read_block,
|
||||
.write_block = clearpad_i2c_write_block,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int clearpad_parse_dt(struct device *dev,
|
||||
struct clearpad_platform_data_t *pdata)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
pdata->irq_gpio = of_get_named_gpio_flags(np, "synaptics,irq_gpio",
|
||||
0, &pdata->irq_gpio_flags);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int clearpad_parse_dt(struct device *dev,
|
||||
struct clearpad_platform_data *pdata)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int clearpad_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct clearpad_data_t clearpad_data = {
|
||||
.pdata = NULL,
|
||||
.bdata = &clearpad_i2c_bus_data,
|
||||
.probe_retry = 0,
|
||||
.rmi_dev = NULL,
|
||||
};
|
||||
struct clearpad_i2c_t *this;
|
||||
int rc;
|
||||
|
||||
if (client->dev.of_node) {
|
||||
clearpad_data.pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct clearpad_platform_data_t),
|
||||
GFP_KERNEL);
|
||||
if (!clearpad_data.pdata) {
|
||||
dev_err(&client->dev, "failed to allocate memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
rc = clearpad_parse_dt(&client->dev, clearpad_data.pdata);
|
||||
if (rc) {
|
||||
dev_err(&client->dev, "failed to parse device tree\n");
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
clearpad_data.pdata = client->dev.platform_data;
|
||||
}
|
||||
|
||||
this = kzalloc(sizeof(struct clearpad_i2c_t), GFP_KERNEL);
|
||||
if (!this) {
|
||||
rc = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&client->dev, this);
|
||||
|
||||
mutex_init(&this->page_mutex);
|
||||
|
||||
this->pdev = platform_device_alloc(CLEARPAD_NAME, -1);
|
||||
if (!this->pdev) {
|
||||
rc = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
clearpad_data.bdata->dev = &client->dev;
|
||||
clearpad_data.bdata->of_node = client->dev.of_node;
|
||||
this->pdev->dev.parent = &client->dev;
|
||||
rc = platform_device_add_data(this->pdev,
|
||||
&clearpad_data, sizeof(clearpad_data));
|
||||
if (rc)
|
||||
goto err_device_put;
|
||||
|
||||
rc = platform_device_add(this->pdev);
|
||||
if (rc)
|
||||
goto err_device_put;
|
||||
|
||||
dev_info(&client->dev, "%s: success\n", __func__);
|
||||
goto exit;
|
||||
|
||||
err_device_put:
|
||||
platform_device_put(this->pdev);
|
||||
err_free:
|
||||
dev_set_drvdata(&client->dev, NULL);
|
||||
kfree(this);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int clearpad_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct clearpad_i2c_t *this = dev_get_drvdata(&client->dev);
|
||||
platform_device_unregister(this->pdev);
|
||||
dev_set_drvdata(&client->dev, NULL);
|
||||
kfree(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id clearpad_id[] = {
|
||||
{ CLEARPADI2C_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, clearpad_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id clearpad_match_table[] = {
|
||||
{ .compatible = "synaptics,clearpad", },
|
||||
{ },
|
||||
};
|
||||
#else
|
||||
#define clearpad_match_table NULL
|
||||
#endif
|
||||
|
||||
static struct i2c_driver clearpad_i2c_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = CLEARPADI2C_NAME,
|
||||
.of_match_table = clearpad_match_table,
|
||||
},
|
||||
.id_table = clearpad_id,
|
||||
.probe = clearpad_i2c_probe,
|
||||
.remove = clearpad_i2c_remove,
|
||||
};
|
||||
|
||||
#ifndef MODULE
|
||||
void clearpad_i2c_init_async(void *unused, async_cookie_t cookie)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = i2c_add_driver(&clearpad_i2c_driver);
|
||||
if (rc != 0)
|
||||
pr_err("Clearpad I2C registration failed rc = %d\n", rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init clearpad_i2c_init(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
return i2c_add_driver(&clearpad_i2c_driver);
|
||||
#else
|
||||
async_schedule(clearpad_i2c_init_async, NULL);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __exit clearpad_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&clearpad_i2c_driver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION(CLEARPADI2C_NAME "ClearPad I2C Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
module_init(clearpad_i2c_init);
|
||||
module_exit(clearpad_i2c_exit);
|
||||
512
drivers/input/touchscreen/clearpad_rmi_dev.c
Normal file
512
drivers/input/touchscreen/clearpad_rmi_dev.c
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright (c) 2011 Synaptics Incorporated
|
||||
* Copyright (C) 2017 Sony Mobile Communications 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 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/clearpad.h>
|
||||
|
||||
#define CHAR_DEVICE_NAME "rmi"
|
||||
#define DEVICE_CLASS_NAME "rmidev"
|
||||
|
||||
#define RMI_CHAR_DEV_TMPBUF_SZ (1024 * 8)
|
||||
#define RMI_REG_ADDR_PAGE_SELECT 0xFF
|
||||
#define REG_ADDR_LIMIT 0xFFFF
|
||||
|
||||
struct rmidev_data {
|
||||
/* mutex for file operation*/
|
||||
struct mutex file_mutex;
|
||||
/* main char dev structure */
|
||||
struct cdev main_dev;
|
||||
|
||||
/* pointer to the corresponding RMI4 device. We use this to do */
|
||||
/* read, write, etc. */
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct clearpad_platform_data_t *pdata;
|
||||
struct clearpad_bus_data_t *bdata;
|
||||
/* reference count */
|
||||
int ref_count;
|
||||
|
||||
struct class *device_class;
|
||||
|
||||
unsigned char tmpbuf[RMI_CHAR_DEV_TMPBUF_SZ];
|
||||
};
|
||||
|
||||
/*store dynamically allocated major number of char device*/
|
||||
static int rmidev_major_num;
|
||||
|
||||
|
||||
static struct class *rmidev_device_class;
|
||||
|
||||
static int rmi_read_block(struct rmidev_data *data, u16 addr, u8 *buf, int len)
|
||||
{
|
||||
return data->bdata->read_block(data->bdata->dev, addr, buf, len);
|
||||
}
|
||||
|
||||
static int rmi_write_block(struct rmidev_data *data, u16 addr, const u8 *buf,
|
||||
int len)
|
||||
{
|
||||
return data->bdata->write_block(data->bdata->dev, addr, buf, len);
|
||||
}
|
||||
|
||||
/* file operations for RMI char device */
|
||||
|
||||
/*
|
||||
* rmidev_llseek: - use to setup register address
|
||||
*
|
||||
* @filp: file structure for seek
|
||||
* @off: offset
|
||||
* if whence == SEEK_SET,
|
||||
* high 16 bits: page address
|
||||
* low 16 bits: register address
|
||||
*
|
||||
* if whence == SEEK_CUR,
|
||||
* offset from current position
|
||||
*
|
||||
* if whence == SEEK_END,
|
||||
* offset from END(0xFFFF)
|
||||
*
|
||||
* @whence: SEEK_SET , SEEK_CUR or SEEK_END
|
||||
*/
|
||||
static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
|
||||
{
|
||||
loff_t newpos;
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
|
||||
if (IS_ERR(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
newpos = -EBADF;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
newpos = off;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
newpos = filp->f_pos + off;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
newpos = REG_ADDR_LIMIT + off;
|
||||
break;
|
||||
|
||||
default: /* can't happen */
|
||||
newpos = -EINVAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
|
||||
dev_err(&data->pdev->dev, "newpos 0x%04x is invalid.\n",
|
||||
(unsigned int)newpos);
|
||||
newpos = -EINVAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
filp->f_pos = newpos;
|
||||
|
||||
clean_up:
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
exit:
|
||||
return newpos;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_read: - use to read data from RMI stream
|
||||
*
|
||||
* @filp: file structure for read
|
||||
* @buf: user-level buffer pointer
|
||||
*
|
||||
* @count: number of byte read
|
||||
* @f_pos: offset (starting register address)
|
||||
*
|
||||
* @return number of bytes read into user buffer (buf) if succeeds
|
||||
* negative number if error occurs.
|
||||
*/
|
||||
static ssize_t rmidev_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (*f_pos > REG_ADDR_LIMIT) {
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* limit offset to REG_ADDR_LIMIT-1 */
|
||||
if (count > (REG_ADDR_LIMIT - *f_pos))
|
||||
count = REG_ADDR_LIMIT - *f_pos;
|
||||
if (count > RMI_CHAR_DEV_TMPBUF_SZ)
|
||||
count = RMI_CHAR_DEV_TMPBUF_SZ;
|
||||
|
||||
if (count == 0)
|
||||
goto exit;
|
||||
|
||||
if (IS_ERR(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
retval = -EBADF;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
retval = rmi_read_block(data, *f_pos, data->tmpbuf, count);
|
||||
|
||||
if (retval < 0)
|
||||
goto clean_up;
|
||||
else
|
||||
*f_pos += retval;
|
||||
|
||||
if (copy_to_user(buf, data->tmpbuf, count))
|
||||
retval = -EFAULT;
|
||||
|
||||
clean_up:
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_write: - use to write data into RMI stream
|
||||
*
|
||||
* @filep : file structure for write
|
||||
* @buf: user-level buffer pointer contains data to be written
|
||||
* @count: number of byte be be written
|
||||
* @f_pos: offset (starting register address)
|
||||
*
|
||||
* @return number of bytes written from user buffer (buf) if succeeds
|
||||
* negative number if error occurs.
|
||||
*/
|
||||
static ssize_t rmidev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct rmidev_data *data = filp->private_data;
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (*f_pos > REG_ADDR_LIMIT) {
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* limit offset to REG_ADDR_LIMIT-1 */
|
||||
if (count > (REG_ADDR_LIMIT - *f_pos))
|
||||
count = REG_ADDR_LIMIT - *f_pos;
|
||||
if (count > RMI_CHAR_DEV_TMPBUF_SZ)
|
||||
count = RMI_CHAR_DEV_TMPBUF_SZ;
|
||||
|
||||
if (count == 0)
|
||||
goto exit;
|
||||
|
||||
if (IS_ERR(data)) {
|
||||
pr_err("%s: pointer of char device is invalid", __func__);
|
||||
retval = -EBADF;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (copy_from_user(data->tmpbuf, buf, count)) {
|
||||
retval = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
retval = rmi_write_block(data, *f_pos, data->tmpbuf, (int)count);
|
||||
|
||||
if (retval >= 0)
|
||||
*f_pos += count;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_open: - get a new handle for from RMI stream
|
||||
* @inp : inode struture
|
||||
* @filp: file structure for read/write
|
||||
*
|
||||
* @return 0 if succeeds
|
||||
*/
|
||||
static int rmidev_open(struct inode *inp, struct file *filp)
|
||||
{
|
||||
struct rmidev_data *data = container_of(inp->i_cdev,
|
||||
struct rmidev_data, main_dev);
|
||||
int retval = 0;
|
||||
|
||||
filp->private_data = data;
|
||||
|
||||
if (!data->pdata) {
|
||||
retval = -EACCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
if (data->ref_count < 1)
|
||||
data->ref_count++;
|
||||
else
|
||||
retval = -EACCES;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmidev_release: - release an existing handle
|
||||
* @inp: inode structure
|
||||
* @filp: file structure for read/write
|
||||
*
|
||||
* @return 0 if succeeds
|
||||
*/
|
||||
static int rmidev_release(struct inode *inp, struct file *filp)
|
||||
{
|
||||
struct rmidev_data *data = container_of(inp->i_cdev,
|
||||
struct rmidev_data, main_dev);
|
||||
int retval = 0;
|
||||
|
||||
if (!data->pdev) {
|
||||
retval = -EACCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&(data->file_mutex));
|
||||
|
||||
data->ref_count--;
|
||||
if (data->ref_count < 0)
|
||||
data->ref_count = 0;
|
||||
|
||||
mutex_unlock(&(data->file_mutex));
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations rmidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = rmidev_llseek,
|
||||
.read = rmidev_read,
|
||||
.write = rmidev_write,
|
||||
.open = rmidev_open,
|
||||
.release = rmidev_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* rmi_char_dev_clean_up - release memory or unregister driver
|
||||
* @rmi_char_dev: rmi_char_dev structure
|
||||
*
|
||||
*/
|
||||
static void rmidev_device_cleanup(struct rmidev_data *data)
|
||||
{
|
||||
dev_t devno;
|
||||
|
||||
/* Get rid of our char dev entries */
|
||||
if (data) {
|
||||
devno = data->main_dev.dev;
|
||||
|
||||
if (data->device_class)
|
||||
device_destroy(data->device_class, devno);
|
||||
|
||||
cdev_del(&data->main_dev);
|
||||
|
||||
/* cleanup_module is never called if registering failed */
|
||||
unregister_chrdev_region(devno, 1);
|
||||
pr_debug("%s: rmidev device is removed\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rmi_char_devnode - return device permission
|
||||
*
|
||||
* @dev: char device structure
|
||||
* @mode: file permission
|
||||
*
|
||||
*/
|
||||
static char *rmi_char_devnode(struct device *dev, umode_t *mode)
|
||||
{
|
||||
char *pret = NULL;
|
||||
|
||||
if (mode) {
|
||||
/**mode = 0600*/
|
||||
*mode = S_IRUSR|S_IWUSR;
|
||||
pret = kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
|
||||
}
|
||||
return pret;
|
||||
}
|
||||
|
||||
static int rmidev_init_device(struct rmidev_data *data)
|
||||
{
|
||||
dev_t dev_no;
|
||||
int retval;
|
||||
struct device *device_ptr;
|
||||
|
||||
if (rmidev_major_num) {
|
||||
dev_no = MKDEV(rmidev_major_num, 0);
|
||||
retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
|
||||
} else {
|
||||
retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
|
||||
/* let kernel allocate a major for us */
|
||||
rmidev_major_num = MAJOR(dev_no);
|
||||
dev_info(&data->pdev->dev, "Major number of rmidev: %d\n",
|
||||
rmidev_major_num);
|
||||
}
|
||||
if (retval < 0) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Failed to register or allocate char dev, code %d.\n",
|
||||
retval);
|
||||
goto exit;
|
||||
} else {
|
||||
dev_info(&data->pdev->dev, "Allocated rmidev %d %d.\n",
|
||||
MAJOR(dev_no), MINOR(dev_no));
|
||||
}
|
||||
|
||||
mutex_init(&data->file_mutex);
|
||||
|
||||
cdev_init(&data->main_dev, &rmidev_fops);
|
||||
|
||||
retval = cdev_add(&data->main_dev, dev_no, 1);
|
||||
if (retval) {
|
||||
dev_err(&data->pdev->dev, "Error %d adding rmi_char_dev.\n",
|
||||
retval);
|
||||
rmidev_device_cleanup(data);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_set_name(&data->pdev->dev, "rmidev%d", MINOR(dev_no));
|
||||
data->device_class = rmidev_device_class;
|
||||
device_ptr = device_create(
|
||||
data->device_class,
|
||||
NULL, dev_no, NULL,
|
||||
CHAR_DEVICE_NAME"%d",
|
||||
MINOR(dev_no));
|
||||
|
||||
if (IS_ERR(device_ptr)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Failed to create rmi device.\n");
|
||||
rmidev_device_cleanup(data);
|
||||
retval = -ENODEV;
|
||||
}
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rmi_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
struct clearpad_data_t *cdata = pdev->dev.platform_data;
|
||||
struct rmidev_data *data;
|
||||
int retval = 0;
|
||||
|
||||
data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, data);
|
||||
data->pdev = pdev;
|
||||
data->pdata = cdata->pdata;
|
||||
if (!data->pdata) {
|
||||
dev_err(&data->pdev->dev, "no platform data\n");
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
data->bdata = cdata->bdata;
|
||||
if (!data->bdata) {
|
||||
dev_err(&data->pdev->dev, "no bus data\n");
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
retval = rmidev_init_device(data);
|
||||
if (!retval)
|
||||
goto exit;
|
||||
|
||||
err_free:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
kfree(data);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rmi_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rmidev_data *data = dev_get_drvdata(&pdev->dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rmidev_driver = {
|
||||
.driver = {
|
||||
.name = CLEARPAD_RMI_DEV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = rmi_dev_probe,
|
||||
.remove = rmi_dev_remove,
|
||||
};
|
||||
|
||||
static int __init rmidev_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
pr_debug("%s: rmi_dev initialization.\n", __func__);
|
||||
|
||||
/* create device node */
|
||||
rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
|
||||
|
||||
if (IS_ERR(rmidev_device_class)) {
|
||||
pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
|
||||
CHAR_DEVICE_NAME);
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
/* setup permission */
|
||||
rmidev_device_class->devnode = rmi_char_devnode;
|
||||
|
||||
retval = platform_driver_register(&rmidev_driver);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit rmidev_exit(void)
|
||||
{
|
||||
pr_debug("%s: exiting.\n", __func__);
|
||||
platform_driver_unregister(&rmidev_driver);
|
||||
class_unregister(rmidev_device_class);
|
||||
class_destroy(rmidev_device_class);
|
||||
}
|
||||
|
||||
module_init(rmidev_init);
|
||||
module_exit(rmidev_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI4 Char Device");
|
||||
MODULE_LICENSE("GPL");
|
||||
119
include/linux/clearpad.h
Normal file
119
include/linux/clearpad.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* msm/clearpad/include/clearpad.h
|
||||
*
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
|
||||
* Yusuke Yoshimura <Yusuke.Yoshimura@sonymobile.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_CLEARPAD_H
|
||||
#define __LINUX_CLEARPAD_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define CLEARPAD_NAME "clearpad"
|
||||
#define CLEARPADI2C_NAME "clearpad-i2c"
|
||||
#define CLEARPAD_RMI_DEV_NAME "clearpad-rmi-dev"
|
||||
|
||||
#define SYN_PCA_BLOCK_SIZE 16
|
||||
#define SYN_PCA_BLOCK_NUMBER_MAX 31
|
||||
|
||||
//#define CLEARPAD_WAKEUP_GESTURE
|
||||
|
||||
enum clearpad_funcarea_kind_e {
|
||||
SYN_FUNCAREA_INSENSIBLE,
|
||||
SYN_FUNCAREA_POINTER,
|
||||
SYN_FUNCAREA_BUTTON,
|
||||
SYN_FUNCAREA_END,
|
||||
};
|
||||
|
||||
enum clearpad_flip_config_e {
|
||||
SYN_FLIP_NONE,
|
||||
SYN_FLIP_X,
|
||||
SYN_FLIP_Y,
|
||||
SYN_FLIP_XY,
|
||||
};
|
||||
|
||||
enum clearpad_infomation_attribute_kind_e {
|
||||
PCA_DATA = 0x00,
|
||||
PCA_IC = 0x01,
|
||||
PCA_CHIP = 0x03,
|
||||
PCA_MODULE = 0x05,
|
||||
};
|
||||
|
||||
enum clearpad_infomation_kind_e {
|
||||
PCA_NO_USE = 0x00,
|
||||
PCA_FW_INFO = 0x02,
|
||||
};
|
||||
|
||||
struct clearpad_area_t {
|
||||
int x1;
|
||||
int y1;
|
||||
int x2;
|
||||
int y2;
|
||||
};
|
||||
|
||||
struct clearpad_funcarea_t {
|
||||
struct clearpad_area_t original; /* actual area */
|
||||
struct clearpad_area_t extension; /* extended area to track events */
|
||||
enum clearpad_funcarea_kind_e func;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct clearpad_pointer_data_t {
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
};
|
||||
|
||||
struct clearpad_button_data_t {
|
||||
int code;
|
||||
bool down;
|
||||
bool down_report;
|
||||
};
|
||||
|
||||
struct clearpad_platform_data_t {
|
||||
int irq_gpio;
|
||||
u32 irq_gpio_flags;
|
||||
char *symlink_name;
|
||||
bool watchdog_enable;
|
||||
int watchdog_poll_t_ms;
|
||||
};
|
||||
|
||||
struct clearpad_bus_data_t {
|
||||
__u16 bustype;
|
||||
struct device *dev;
|
||||
struct device_node *of_node;
|
||||
int (*set_page)(struct device *dev, u8 page);
|
||||
int (*read)(struct device *dev, u16 addr, u8 *buf, u8 len);
|
||||
int (*write)(struct device *dev, u16 addr, const u8 *buf, u8 len);
|
||||
int (*read_block)(struct device *dev, u16 addr, u8 *buf, int len);
|
||||
int (*write_block)(struct device *dev, u16 addr, const u8 *buf,
|
||||
int len);
|
||||
};
|
||||
|
||||
struct clearpad_data_t {
|
||||
struct clearpad_platform_data_t *pdata;
|
||||
struct clearpad_bus_data_t *bdata;
|
||||
int probe_retry;
|
||||
struct platform_device *rmi_dev;
|
||||
};
|
||||
|
||||
struct clearpad_ioctl_pca_info {
|
||||
u16 block_pos;
|
||||
u8 data[SYN_PCA_BLOCK_SIZE];
|
||||
};
|
||||
|
||||
#define SYN_PCA_IOCTL_MAGIC 0xCC
|
||||
#define SYN_PCA_IOCTL_GET \
|
||||
_IOWR(SYN_PCA_IOCTL_MAGIC, 1, struct clearpad_ioctl_pca_info *)
|
||||
#define SYN_PCA_IOCTL_SET \
|
||||
_IOW(SYN_PCA_IOCTL_MAGIC, 2, struct clearpad_ioctl_pca_info *)
|
||||
|
||||
#endif
|
||||
170
include/linux/evdt_helper.h
Normal file
170
include/linux/evdt_helper.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/* msm/clearpad/include/evdt_helper.h
|
||||
*
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* Author: Kenji Suzuki <Kenji.X.Suzuki@sonymobile.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_INPUT_EVDT_HELPER_H__
|
||||
#define __LINUX_INPUT_EVDT_HELPER_H__
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
enum {
|
||||
EVDT_MSLEEP = 0,
|
||||
EVDT_KEY,
|
||||
EVDT_LOG,
|
||||
EVDT_END = 99,
|
||||
};
|
||||
|
||||
static inline int evdt_get_msleep(struct device_node *node, u32 *ms)
|
||||
{
|
||||
return of_property_read_u32(node, "ms", ms);
|
||||
}
|
||||
|
||||
static inline int evdt_get_key(struct device_node *node, u32 *code, u32 *down)
|
||||
{
|
||||
int rc;
|
||||
rc = of_property_read_u32(node, "code", code);
|
||||
if (!rc)
|
||||
rc = of_property_read_u32(node, "down", down);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int evdt_get_log(struct device_node *node, const char **message)
|
||||
{
|
||||
return of_property_read_string(node, "message", message);
|
||||
}
|
||||
|
||||
static inline struct device_node *evdt_initialize(struct device *dev,
|
||||
struct input_dev *input, const char *node)
|
||||
{
|
||||
struct device_node *wg_node = NULL;
|
||||
struct device_node *block = NULL;
|
||||
struct device_node *record = NULL;
|
||||
int index = 0;
|
||||
u32 type;
|
||||
u32 ms;
|
||||
u32 code;
|
||||
u32 down;
|
||||
const char *message;
|
||||
int rc;
|
||||
|
||||
if (!dev->of_node || !input)
|
||||
return NULL;
|
||||
|
||||
wg_node = of_find_node_by_name(dev->of_node, node);
|
||||
if (!wg_node) {
|
||||
pr_err("%s: No %s node found\n", __func__, node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(wg_node, block) {
|
||||
index = 0;
|
||||
for_each_child_of_node(block, record) {
|
||||
rc = of_property_read_u32(record, "type", &type);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
switch (type) {
|
||||
case EVDT_MSLEEP:
|
||||
rc = evdt_get_msleep(record, &ms);
|
||||
if (rc)
|
||||
goto error;
|
||||
pr_info("%s: MSLEEP [%u]\n", __func__, ms);
|
||||
break;
|
||||
case EVDT_KEY:
|
||||
rc = evdt_get_key(record, &code, &down);
|
||||
if (rc)
|
||||
goto error;
|
||||
input_set_capability(input, EV_KEY, code);
|
||||
pr_info("%s: KEY [%u, %s]\n", __func__, code,
|
||||
down ? "DOWN" : "UP");
|
||||
break;
|
||||
case EVDT_LOG:
|
||||
rc = evdt_get_log(record, &message);
|
||||
if (rc)
|
||||
goto error;
|
||||
pr_info("%s: LOG [%s]\n", __func__, message);
|
||||
break;
|
||||
case EVDT_END:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return wg_node;
|
||||
|
||||
error:
|
||||
pr_err("%s: invalid value at index %d for %s rc=%d\n",
|
||||
__func__, index, block->name, rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int evdt_execute(struct device_node *block,
|
||||
struct input_dev *input, u16 gesture_code)
|
||||
{
|
||||
struct device_node *gesture = NULL;
|
||||
struct device_node *record;
|
||||
u32 type;
|
||||
u32 ms;
|
||||
u32 code;
|
||||
u32 down;
|
||||
const char *message;
|
||||
int rc;
|
||||
|
||||
if (!block || !input)
|
||||
return 0;
|
||||
|
||||
for_each_child_of_node(block, gesture) {
|
||||
rc = of_property_read_u32(gesture, "gesture_code", &type);
|
||||
if (rc) {
|
||||
pr_err("%s: Error reading gesture_code for %s rc=%d\n",
|
||||
__func__, gesture->name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (gesture_code != type)
|
||||
continue;
|
||||
|
||||
for_each_child_of_node(gesture, record) {
|
||||
of_property_read_u32(record, "type", &type);
|
||||
|
||||
switch (type) {
|
||||
case EVDT_MSLEEP:
|
||||
evdt_get_msleep(record, &ms);
|
||||
pr_debug("%s: MSLEEP(%d)\n", __func__, ms);
|
||||
msleep(ms);
|
||||
break;
|
||||
case EVDT_KEY:
|
||||
evdt_get_key(record, &code, &down);
|
||||
pr_debug("%s: KEY(%d) %s\n", __func__, code,
|
||||
down ? "DOWN" : "UP");
|
||||
input_report_key(input, code, down);
|
||||
input_sync(input);
|
||||
break;
|
||||
case EVDT_LOG:
|
||||
evdt_get_log(record, &message);
|
||||
pr_info("%s: %s\n", __func__, message);
|
||||
break;
|
||||
case EVDT_END:
|
||||
default:
|
||||
goto loop_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
pr_warn("%s: Gesture code 0x%X not defined\n", __func__, gesture_code);
|
||||
|
||||
loop_end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __LINUX_INPUT_EVDT_HELPER_H__ */
|
||||
170
include/linux/input/evdt_helper.h
Normal file
170
include/linux/input/evdt_helper.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/* msm/clearpad/include/evdt_helper.h
|
||||
*
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* Author: Kenji Suzuki <Kenji.X.Suzuki@sonymobile.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_INPUT_EVDT_HELPER_H__
|
||||
#define __LINUX_INPUT_EVDT_HELPER_H__
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
enum {
|
||||
EVDT_MSLEEP = 0,
|
||||
EVDT_KEY,
|
||||
EVDT_LOG,
|
||||
EVDT_END = 99,
|
||||
};
|
||||
|
||||
static inline int evdt_get_msleep(struct device_node *node, u32 *ms)
|
||||
{
|
||||
return of_property_read_u32(node, "ms", ms);
|
||||
}
|
||||
|
||||
static inline int evdt_get_key(struct device_node *node, u32 *code, u32 *down)
|
||||
{
|
||||
int rc;
|
||||
rc = of_property_read_u32(node, "code", code);
|
||||
if (!rc)
|
||||
rc = of_property_read_u32(node, "down", down);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int evdt_get_log(struct device_node *node, const char **message)
|
||||
{
|
||||
return of_property_read_string(node, "message", message);
|
||||
}
|
||||
|
||||
static inline struct device_node *evdt_initialize(struct device *dev,
|
||||
struct input_dev *input, const char *node)
|
||||
{
|
||||
struct device_node *wg_node = NULL;
|
||||
struct device_node *block = NULL;
|
||||
struct device_node *record = NULL;
|
||||
int index = 0;
|
||||
u32 type;
|
||||
u32 ms;
|
||||
u32 code;
|
||||
u32 down;
|
||||
const char *message;
|
||||
int rc;
|
||||
|
||||
if (!dev->of_node || !input)
|
||||
return NULL;
|
||||
|
||||
wg_node = of_find_node_by_name(dev->of_node, node);
|
||||
if (!wg_node) {
|
||||
pr_err("%s: No %s node found\n", __func__, node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(wg_node, block) {
|
||||
index = 0;
|
||||
for_each_child_of_node(block, record) {
|
||||
rc = of_property_read_u32(record, "type", &type);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
switch (type) {
|
||||
case EVDT_MSLEEP:
|
||||
rc = evdt_get_msleep(record, &ms);
|
||||
if (rc)
|
||||
goto error;
|
||||
pr_info("%s: MSLEEP [%u]\n", __func__, ms);
|
||||
break;
|
||||
case EVDT_KEY:
|
||||
rc = evdt_get_key(record, &code, &down);
|
||||
if (rc)
|
||||
goto error;
|
||||
input_set_capability(input, EV_KEY, code);
|
||||
pr_info("%s: KEY [%u, %s]\n", __func__, code,
|
||||
down ? "DOWN" : "UP");
|
||||
break;
|
||||
case EVDT_LOG:
|
||||
rc = evdt_get_log(record, &message);
|
||||
if (rc)
|
||||
goto error;
|
||||
pr_info("%s: LOG [%s]\n", __func__, message);
|
||||
break;
|
||||
case EVDT_END:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return wg_node;
|
||||
|
||||
error:
|
||||
pr_err("%s: invalid value at index %d for %s rc=%d\n",
|
||||
__func__, index, block->name, rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int evdt_execute(struct device_node *block,
|
||||
struct input_dev *input, u16 gesture_code)
|
||||
{
|
||||
struct device_node *gesture = NULL;
|
||||
struct device_node *record;
|
||||
u32 type;
|
||||
u32 ms;
|
||||
u32 code;
|
||||
u32 down;
|
||||
const char *message;
|
||||
int rc;
|
||||
|
||||
if (!block || !input)
|
||||
return 0;
|
||||
|
||||
for_each_child_of_node(block, gesture) {
|
||||
rc = of_property_read_u32(gesture, "gesture_code", &type);
|
||||
if (rc) {
|
||||
pr_err("%s: Error reading gesture_code for %s rc=%d\n",
|
||||
__func__, gesture->name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (gesture_code != type)
|
||||
continue;
|
||||
|
||||
for_each_child_of_node(gesture, record) {
|
||||
of_property_read_u32(record, "type", &type);
|
||||
|
||||
switch (type) {
|
||||
case EVDT_MSLEEP:
|
||||
evdt_get_msleep(record, &ms);
|
||||
pr_debug("%s: MSLEEP(%d)\n", __func__, ms);
|
||||
msleep(ms);
|
||||
break;
|
||||
case EVDT_KEY:
|
||||
evdt_get_key(record, &code, &down);
|
||||
pr_debug("%s: KEY(%d) %s\n", __func__, code,
|
||||
down ? "DOWN" : "UP");
|
||||
input_report_key(input, code, down);
|
||||
input_sync(input);
|
||||
break;
|
||||
case EVDT_LOG:
|
||||
evdt_get_log(record, &message);
|
||||
pr_info("%s: %s\n", __func__, message);
|
||||
break;
|
||||
case EVDT_END:
|
||||
default:
|
||||
goto loop_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
pr_warn("%s: Gesture code 0x%X not defined\n", __func__, gesture_code);
|
||||
|
||||
loop_end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __LINUX_INPUT_EVDT_HELPER_H__ */
|
||||
Loading…
Reference in a new issue