Signed-off-by: Du, Dudley --- diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index c2b67d3..3b80de6 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -190,6 +190,17 @@ void cyapa_default_irq_handler(struct cyapa *cyapa) const struct cyapa_dev_ops cyapa_default_ops = { NULL, NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + NULL, + NULL, + + NULL, + NULL, NULL, NULL, @@ -470,6 +481,78 @@ static void cyapa_detect(struct cyapa *cyapa) } } +static int cyapa_firmware(struct cyapa *cyapa, const char *fw_name) +{ + struct device *dev = &cyapa->client->dev; + int ret; + const struct firmware *fw; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + dev_err(dev, "Could not load firmware from %s, %d\n", + fw_name, ret); + return ret; + } + + if (cyapa->ops->cyapa_check_fw) { + ret = cyapa->ops->cyapa_check_fw(cyapa, fw); + if (ret) { + dev_err(dev, "Invalid CYAPA firmware image: %s\n", + fw_name); + goto done; + } + } else { + dev_err(dev, "Unknown status, operation forbidden, gen=%d\n", + cyapa->gen); + ret = -EPERM; + goto done; + } + + /* + * Resume the potentially suspended device because doing FW + * update on a device not in the FULL mode has a chance to + * fail. + */ + pm_runtime_get_sync(dev); + + if (cyapa->ops->cyapa_bl_enter) { + ret = cyapa->ops->cyapa_bl_enter(cyapa); + if (ret) + goto err_detect; + } + + if (cyapa->ops->cyapa_bl_activate) { + ret = cyapa->ops->cyapa_bl_activate(cyapa); + if (ret) + goto err_detect; + } + + if (cyapa->ops->cyapa_bl_initiate) { + ret = cyapa->ops->cyapa_bl_initiate(cyapa, fw); + if (ret) + goto err_detect; + } + + if (cyapa->ops->cyapa_update_fw) { + ret = cyapa->ops->cyapa_update_fw(cyapa, fw); + if (ret) + goto err_detect; + } + + if (cyapa->ops->cyapa_bl_verify_app_integrity) { + ret = cyapa->ops->cyapa_bl_verify_app_integrity(cyapa); + if (ret) + goto err_detect; + } + +err_detect: + pm_runtime_put_noidle(dev); + +done: + release_firmware(fw); + return ret; +} + /* * Sysfs Interface. */ @@ -656,6 +739,127 @@ static void cyapa_start_runtime(struct cyapa *cyapa) static void cyapa_start_runtime(struct cyapa *cyapa) {} #endif /* CONFIG_PM_RUNTIME */ +static ssize_t cyapa_show_fm_ver(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct cyapa *cyapa = dev_get_drvdata(dev); + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + + ret = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver, + cyapa->fw_min_ver); + cyapa_state_sync_exit(cyapa); + return ret; +} + +static ssize_t cyapa_show_product_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct cyapa *cyapa = dev_get_drvdata(dev); + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + + ret = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id); + cyapa_state_sync_exit(cyapa); + return ret; +} + +static ssize_t cyapa_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + const char *fw_name; + int ret; + + /* Do not allow paths that step out of /lib/firmware */ + if (strstr(buf, "../") != NULL) + return -EINVAL; + + fw_name = !strncmp(buf, "1", count) || + !strncmp(buf, "1\n", count) ? CYAPA_FW_NAME : buf; + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + + ret = cyapa_firmware(cyapa, fw_name); + if (ret) + dev_err(dev, "firmware update failed, %d\n", ret); + else + dev_dbg(dev, "firmware update succeeded\n"); + + cyapa_state_sync_exit(cyapa); + + /* redetect trackpad device states. */ + cyapa_detect_async(cyapa, 0); + + return ret ? ret : count; +} + +static ssize_t cyapa_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int ret; + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + + if (!cyapa->ops->cyapa_calibrate_store) { + cyapa_state_sync_exit(cyapa); + dev_err(dev, "Calibrate operation not permitted.\n"); + return -EPERM; + } + ret = cyapa->ops->cyapa_calibrate_store(dev, attr, buf, count); + + cyapa_state_sync_exit(cyapa); + return ret < 0 ? ret : count; +} + +static ssize_t cyapa_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + ssize_t ret; + + if (!cyapa_state_sync_enter(cyapa)) + return -EBUSY; + + if (!cyapa->ops->cyapa_show_baseline) { + cyapa_state_sync_exit(cyapa); + dev_err(dev, "Calibrate operation not permitted.\n"); + return -EPERM; + } + ret = cyapa->ops->cyapa_show_baseline(dev, attr, buf); + + cyapa_state_sync_exit(cyapa); + return ret; +} + +static DEVICE_ATTR(firmware_version, S_IRUGO, cyapa_show_fm_ver, NULL); +static DEVICE_ATTR(product_id, S_IRUGO, cyapa_show_product_id, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, cyapa_update_fw_store); +static DEVICE_ATTR(baseline, S_IRUGO, cyapa_show_baseline, NULL); +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, cyapa_calibrate_store); + +static struct attribute *cyapa_sysfs_entries[] = { + &dev_attr_firmware_version.attr, + &dev_attr_product_id.attr, + &dev_attr_update_fw.attr, + &dev_attr_baseline.attr, + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group cyapa_sysfs_group = { + .attrs = cyapa_sysfs_entries, +}; + void cyapa_detect_async(void *data, async_cookie_t cookie) { struct cyapa *cyapa = (struct cyapa *)data; @@ -740,6 +944,9 @@ static int cyapa_probe(struct i2c_client *client, } cyapa_disable_irq(cyapa); + if (sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group)) + dev_warn(dev, "error creating sysfs entries.\n"); + #ifdef CONFIG_PM_SLEEP if (device_can_wakeup(dev) && sysfs_merge_group(&client->dev.kobj, &cyapa_power_wakeup_group)) @@ -762,6 +969,7 @@ static int cyapa_remove(struct i2c_client *client) struct cyapa *cyapa = i2c_get_clientdata(client); pm_runtime_disable(&client->dev); + sysfs_remove_group(&client->dev.kobj, &cyapa_sysfs_group); #ifdef CONFIG_PM_SLEEP sysfs_unmerge_group(&client->dev.kobj, &cyapa_power_wakeup_group); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index 91fb84a..3e72fca 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -160,6 +160,19 @@ struct cyapa; typedef bool (*cb_sort)(struct cyapa *, u8 *, int); struct cyapa_dev_ops { + int (*cyapa_check_fw)(struct cyapa *, const struct firmware *); + int (*cyapa_bl_enter)(struct cyapa *); + int (*cyapa_bl_activate)(struct cyapa *); + int (*cyapa_bl_initiate)(struct cyapa *, const struct firmware *); + int (*cyapa_update_fw)(struct cyapa *, const struct firmware *); + int (*cyapa_bl_verify_app_integrity)(struct cyapa *); + int (*cyapa_bl_deactivate)(struct cyapa *); + + ssize_t (*cyapa_show_baseline)(struct device *, + struct device_attribute *, char *); + ssize_t (*cyapa_calibrate_store)(struct device *, + struct device_attribute *, const char *, size_t); + size_t (*cyapa_get_private_size)(void); int (*cyapa_private_init)(struct cyapa *cyapa, void *private_mem);