From patchwork Wed Nov 19 08:37:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dudley Du X-Patchwork-Id: 5335281 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 5E3F2C11AC for ; Wed, 19 Nov 2014 08:41:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 51E8B200E6 for ; Wed, 19 Nov 2014 08:41:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3EF32200F2 for ; Wed, 19 Nov 2014 08:41:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754826AbaKSIlh (ORCPT ); Wed, 19 Nov 2014 03:41:37 -0500 Received: from mail-pa0-f51.google.com ([209.85.220.51]:64146 "EHLO mail-pa0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754704AbaKSIlf (ORCPT ); Wed, 19 Nov 2014 03:41:35 -0500 Received: by mail-pa0-f51.google.com with SMTP id ey11so128925pad.38 for ; Wed, 19 Nov 2014 00:41:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YEtekZOSf0BVyE22VJKxVwBITKiC6nQTsGbnpwMW5vw=; b=GR1KqLS1s/5gHPXsj7RYAUiZIUBktbKqS8PB/0LnM4ovy8GlXq8oGoPCgjTyHL3+PS Ck1c2tzvOaDSsx+eXfalRiNhOPNmxO9osB/eleSid6KyKTzUOtV/ezblhPePrEkwjMI+ kfrw4vd2s3FuUt5TiLfj+/Ts0MnuHq3LlStKM5RsJeBP+6yWYuvtk1F0H34Lw2DlsxHd 5gC+k68AelQpHfI2DWF1t36bNHf7Rt7VPEBj95txwTECu+D4cG2FlB+GDMDv6exQ8poG FOMQnIhZFtoaeRDkl9obidpmmO9UJH5ts8Mau7fD3ViW2uPWPJeFIxd7q0pPzWE/WrJE lAXA== X-Received: by 10.66.227.103 with SMTP id rz7mr42968277pac.45.1416386494779; Wed, 19 Nov 2014 00:41:34 -0800 (PST) Received: from localhost ([140.207.206.26]) by mx.google.com with ESMTPSA id fb7sm1210783pab.10.2014.11.19.00.41.31 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Wed, 19 Nov 2014 00:41:33 -0800 (PST) From: Dudley Du To: dmitry.torokhov@gmail.com, rydberg@euromail.se Cc: Dudley Du , bleung@google.com, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 14/19] input: cyapa: add read firmware image debugfs interface support Date: Wed, 19 Nov 2014 16:37:46 +0800 Message-Id: <1416386271-28167-15-git-send-email-dudley.dulixin@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1416386271-28167-1-git-send-email-dudley.dulixin@gmail.com> References: <1416386271-28167-1-git-send-email-dudley.dulixin@gmail.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add read firmware image from trackpad device interface supported in cyapa driver through debugfs read_fw interface. Through this interface user can read out, check and backup the firmware image of the trackpad device before any firmware update, or can use the backed image to do firmware image recovery. TEST=test on Chromebooks. Signed-off-by: Dudley Du --- drivers/input/mouse/cyapa.c | 179 +++++++++++++++++++++++++++++++++++++++++++- drivers/input/mouse/cyapa.h | 10 +++ 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 305dffe..2e394c0 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -14,6 +14,7 @@ * more details. */ +#include #include #include #include @@ -32,10 +33,14 @@ #define CYAPA_ADAPTER_FUNC_SMBUS 2 #define CYAPA_ADAPTER_FUNC_BOTH 3 +#define CYAPA_DEBUGFS_READ_FW "read_fw" #define CYAPA_FW_NAME "cyapa.bin" const char unique_str[] = "CYTRA"; +/* Global root node of the cyapa debugfs directory. */ +static struct dentry *cyapa_debugfs_root; + /* Returns 0 on success, else negative errno on failure. */ ssize_t cyapa_i2c_read(struct cyapa *cyapa, u8 reg, size_t len, u8 *values) @@ -497,6 +502,138 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) } /* + ************************************************************** + * debugfs interface + ************************************************************** +*/ +static int cyapa_debugfs_open(struct inode *inode, struct file *file) +{ + struct cyapa *cyapa = inode->i_private; + int error; + + if (!cyapa) + return -ENODEV; + + error = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (error) + return error; + + if (!get_device(&cyapa->client->dev)) { + error = -ENODEV; + goto out; + } + + file->private_data = cyapa; + + if (cyapa->fw_image && cyapa->fw_image_size) { + error = 0; + goto out; + } + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + goto out; + + /* + * 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->read_fw) { + cyapa_enable_irq_for_cmd(cyapa); + error = cyapa->ops->read_fw(cyapa); + cyapa_disable_irq_for_cmd(cyapa); + + /* + * Redetect trackpad device states because read_fw will + * reset trackpad device into bootloader mode. + */ + cyapa_detect(cyapa); + } else { + error = -EPERM; + } + + mutex_unlock(&cyapa->state_sync_lock); +out: + mutex_unlock(&cyapa->debugfs_mutex); + return error; +} + +static int cyapa_debugfs_release(struct inode *inode, struct file *file) +{ + struct cyapa *cyapa = file->private_data; + int error; + + if (!cyapa) + return 0; + + error = mutex_lock_interruptible(&cyapa->debugfs_mutex); + if (error) + return error; + file->private_data = NULL; + put_device(&cyapa->client->dev); + 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->fw_image) + return -EINVAL; + + if (*ppos >= cyapa->fw_image_size) + return 0; + + if (count + *ppos > cyapa->fw_image_size) + count = cyapa->fw_image_size - *ppos; + + if (copy_to_user(buffer, &cyapa->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_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); + + return 0; +} + +static void cyapa_remove_debugfs(void *data) +{ + struct cyapa *cyapa = data; + + debugfs_remove_recursive(cyapa->dentry_dev); + mutex_destroy(&cyapa->debugfs_mutex); +} + +/* * Sysfs Interface. */ @@ -1076,6 +1213,20 @@ static int cyapa_probe(struct i2c_client *client, return error; } + error = cyapa_debugfs_init(cyapa); + if (error) { + dev_err(dev, "failed to create debugfs entries: %d\n", error); + return error; + } + + error = devm_add_action(dev, cyapa_remove_debugfs, cyapa); + if (error) { + cyapa_remove_debugfs(cyapa); + dev_err(dev, "failed to add debugfs cleanup action :%d\n", + error); + return error; + } + #ifdef CONFIG_PM_SLEEP if (device_can_wakeup(dev)) { error = sysfs_merge_group(&client->dev.kobj, @@ -1256,7 +1407,33 @@ static struct i2c_driver cyapa_driver = { .id_table = cyapa_id_table, }; -module_i2c_driver(cyapa_driver); +static int __init cyapa_init(void) +{ + int error; + + /* 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; + + error = i2c_add_driver(&cyapa_driver); + if (error) { + pr_err("cyapa driver register FAILED.\n"); + return error; + } + + return 0; +} + +static void __exit cyapa_exit(void) +{ + debugfs_remove_recursive(cyapa_debugfs_root); + + i2c_del_driver(&cyapa_driver); +} + +module_init(cyapa_init); +module_exit(cyapa_exit); MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); MODULE_AUTHOR("Dudley Du "); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index 0dc76d8..ceed843 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -186,6 +186,8 @@ struct cyapa_dev_ops { ssize_t (*calibrate_store)(struct device *, struct device_attribute *, const char *, size_t); + int (*read_fw)(struct cyapa *); + int (*initialize)(struct cyapa *cyapa); int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len); @@ -298,6 +300,14 @@ struct cyapa { */ struct mutex state_sync_lock; + /* Per-instance debugfs root */ + struct dentry *dentry_dev; + + /* Buffer to store firmware read using debugfs */ + struct mutex debugfs_mutex; + u8 *fw_image; + size_t fw_image_size; + const struct cyapa_dev_ops *ops; union cyapa_cmd_states cmd_states;