From b9989fed01a1283134d8b965d66da6408fdd14da Mon Sep 17 00:00:00 2001 From: Dudley Du Date: Tue, 1 Jul 2014 14:39:21 +0800 Subject: [PATCH 05/14] input: cyapa: add read firmware image and raw data interfaces in debugfs system Add read_fw and raw_data debugfs interfaces for easier issues location and collection when report by user. TEST=test on Chomebooks. Signed-off-by: Dudley Du --- drivers/input/mouse/cyapa.c | 223 +++++++++++++++++++++++++++++++++++++++++++ drivers/input/mouse/cyapa.h | 14 +++ 2 files changed, 237 insertions(+) diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index da11dbe..37047d5 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -39,6 +39,9 @@ const char unique_str[] = "CYTRA"; +/* global root node of the cyapa debugfs directory. */ +static struct dentry *cyapa_debugfs_root; + void cyapa_enable_irq(struct cyapa *cyapa) { @@ -205,6 +208,9 @@ const struct cyapa_dev_ops cyapa_default_ops = { NULL, NULL, + NULL, + NULL, + cyapa_default_irq_handler, NULL, NULL, @@ -554,6 +560,205 @@ done: } /* + ************************************************************** + * debugfs interface + ************************************************************** +*/ +static int cyapa_debugfs_open(struct inode *inode, struct file *file) +{ + struct cyapa *cyapa = inode->i_private; + int ret; + + if (!cyapa) + return -ENODEV; + + ret = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (ret) + return ret; + + if (!kobject_get(&cyapa->client->dev.kobj)) { + ret = -ENODEV; + goto out; + } + + file->private_data = cyapa; + + if (cyapa->read_fw_image) { + ret = 0; + goto out; + } + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + /* + * If firmware hasn't been read yet, read it all in one pass. + * Subsequent opens will reuse the data in this same buffer. + */ + if (cyapa->ops->cyapa_read_fw) + ret = cyapa->ops->cyapa_read_fw(cyapa); + else + ret = -EPERM; + cyapa_state_sync_exit(cyapa); + + /* redetect trackpad device states. */ + cyapa_detect_async(cyapa, 0); + +out: + mutex_unlock(&cyapa->debugfs_mutex); + return ret; +} + +static int cyapa_debugfs_release(struct inode *inode, struct file *file) +{ + struct cyapa *cyapa = file->private_data; + int ret; + + if (!cyapa) + return 0; + + ret = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (ret) + return ret; + file->private_data = NULL; + kobject_put(&cyapa->client->dev.kobj); + mutex_unlock(&cyapa->debugfs_mutex); + + return 0; +} + +/* Return some bytes from the buffered firmware image, starting from *ppos */ +static ssize_t cyapa_debugfs_read_fw(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct cyapa *cyapa = file->private_data; + + if (!cyapa->read_fw_image) + return -EINVAL; + + if (*ppos >= cyapa->read_fw_image_size) + return 0; + + if (count + *ppos > cyapa->read_fw_image_size) + count = cyapa->read_fw_image_size - *ppos; + + if (copy_to_user(buffer, &cyapa->read_fw_image[*ppos], count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static const struct file_operations cyapa_read_fw_fops = { + .open = cyapa_debugfs_open, + .release = cyapa_debugfs_release, + .read = cyapa_debugfs_read_fw +}; + +static int cyapa_debugfs_raw_data_open(struct inode *inode, struct file *file) +{ + struct cyapa *cyapa = inode->i_private; + int ret; + + if (!cyapa) + return -ENODEV; + + ret = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (ret) + return ret; + + if (!kobject_get(&cyapa->client->dev.kobj)) { + ret = -ENODEV; + goto out; + } + + file->private_data = cyapa; + + if (!cyapa_state_sync_enter(cyapa)) { + ret = -EBUSY; + goto out; + } + if (cyapa->ops->cyapa_read_raw_data) + ret = cyapa->ops->cyapa_read_raw_data(cyapa); + else + ret = -EPERM; + cyapa_state_sync_exit(cyapa); +out: + mutex_unlock(&cyapa->debugfs_mutex); + return ret; +} + +static int cyapa_debugfs_raw_data_release(struct inode *inode, + struct file *file) +{ + struct cyapa *cyapa = file->private_data; + int ret; + + if (!cyapa) + return 0; + + ret = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (ret) + return ret; + file->private_data = NULL; + kobject_put(&cyapa->client->dev.kobj); + mutex_unlock(&cyapa->debugfs_mutex); + + return 0; +} + +/* Always return the sensors' latest raw data from trackpad device. */ +static ssize_t cyapa_debugfs_read_raw_data(struct file *file, + char __user *buffer, + size_t count, loff_t *ppos) +{ + struct cyapa *cyapa = file->private_data; + + if (!cyapa->tp_raw_data) + return -EINVAL; + + if (*ppos >= cyapa->tp_raw_data_size) + return 0; + + if (count + *ppos > cyapa->tp_raw_data_size) + count = cyapa->tp_raw_data_size - *ppos; + + if (copy_to_user(buffer, &cyapa->tp_raw_data[*ppos], count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static const struct file_operations cyapa_read_raw_data_fops = { + .open = cyapa_debugfs_raw_data_open, + .release = cyapa_debugfs_raw_data_release, + .read = cyapa_debugfs_read_raw_data +}; + +static int cyapa_debugfs_init(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + + if (!cyapa_debugfs_root) + return -ENODEV; + + cyapa->dentry_dev = debugfs_create_dir(kobject_name(&dev->kobj), + cyapa_debugfs_root); + + if (!cyapa->dentry_dev) + return -ENODEV; + + mutex_init(&cyapa->debugfs_mutex); + + debugfs_create_file(CYAPA_DEBUGFS_READ_FW, S_IRUSR, cyapa->dentry_dev, + cyapa, &cyapa_read_fw_fops); + + debugfs_create_file(CYAPA_DEBUGFS_RAW_DATA, S_IRUSR, cyapa->dentry_dev, + cyapa, &cyapa_read_raw_data_fops); + return 0; +} + +/* * Sysfs Interface. */ @@ -948,6 +1153,13 @@ static int cyapa_probe(struct i2c_client *client, if (sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group)) dev_warn(dev, "error creating sysfs entries.\n"); + /* Create a global debugfs root for all cyapa devices */ + cyapa_debugfs_root = debugfs_create_dir("cyapa", NULL); + if (cyapa_debugfs_root == ERR_PTR(-ENODEV)) + cyapa_debugfs_root = NULL; + if (cyapa_debugfs_init(cyapa)) + dev_warn(dev, "error creating debugfs entries.\n"); + #ifdef CONFIG_PM_SLEEP if (device_can_wakeup(dev) && sysfs_merge_group(&client->dev.kobj, &cyapa_power_wakeup_group)) @@ -979,8 +1191,19 @@ static int cyapa_remove(struct i2c_client *client) #ifdef CONFIG_PM_RUNTIME sysfs_unmerge_group(&client->dev.kobj, &cyapa_power_runtime_group); #endif + + kfree(cyapa->read_fw_image); + cyapa->read_fw_image = NULL; + cyapa->read_fw_image_size = 0; + kfree(cyapa->tp_raw_data); + cyapa->tp_raw_data = NULL; + cyapa->tp_raw_data_size = 0; free_irq(cyapa->irq, cyapa); + debugfs_remove_recursive(cyapa->dentry_dev); + debugfs_remove_recursive(cyapa_debugfs_root); + mutex_destroy(&cyapa->debugfs_mutex); + input_unregister_device(cyapa->input); if (cyapa->ops->cyapa_set_power_mode) cyapa->ops->cyapa_set_power_mode(cyapa, PWR_MODE_OFF, 0); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index 3e72fca..319f19d 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -173,6 +173,9 @@ struct cyapa_dev_ops { ssize_t (*cyapa_calibrate_store)(struct device *, struct device_attribute *, const char *, size_t); + int (*cyapa_read_fw)(struct cyapa *); + int (*cyapa_read_raw_data)(struct cyapa *); + size_t (*cyapa_get_private_size)(void); int (*cyapa_private_init)(struct cyapa *cyapa, void *private_mem); @@ -244,6 +247,17 @@ struct cyapa { bool irq_enabled; bool prev_irq_enabled; + /* per-instance debugfs root */ + struct dentry *dentry_dev; + + /* Buffer to store firmware read using debugfs */ + struct mutex debugfs_mutex; + u8 *read_fw_image; + size_t read_fw_image_size; + /* Buffer to store sensors' raw data */ + u8 *tp_raw_data; + size_t tp_raw_data_size; + const struct cyapa_dev_ops *ops; }; -- 1.7.9.5