CHROMIUM: input: elan_i2c - Append PID to fw names
Previously, the elan_i2c touchpad driver in the 3.8 and 3.10 kernel had
two ways of triggering it.
1. You "cat" a 1 into the update_fw sysfs entry. When you do this it
simply loaded the default /lib/firmware/elan_i2c.bin and uses that
as the FW to update with.
2. You "cat" a filename into updat_fw. When you do this, it used
/lib/firmware/${WHAT_YOU_CATTED} instead.
The more modern versions of this driver (kernel >= 3.14) do not offer
option #2 anymore. Instead, regardless of what you cat in they will
first try to load /lib/firmware/elan_i2c_${PRODUCT_ID}.bin, and then if
that is not found, will fail back to /lib/firmware/elan_i2c.bin in an
effort to maintain backwards capability.
The logic behind adding the product_id onto the end of the filename is
to allow a single device/image to multi-source the touchpads while
maintaining different FWs for each one.
eg: if we have two Elan touchpads in one laptop with product_id's 1.0
and 2.0, then we just have to store a elan_i2c_1.0.bin and an
elan_i2c_2.0.bin in /lib/firmware and the updater will always use the
correct one.
We never had this functionality in 3.8 or 3.10, however. This CL adds
the product_id appending code to this version of the driver (failing
back to elan_i2c.bin for backwards compatibility) to allow devices
using this older version to support multiple Elan touchpads with
separate firmwares.
BUG=chrome-os-partner:50932
TEST=emerged onto a Peppy, and tested manually on it
CQ-DEPEND=CL:*253672
Change-Id: Iddac01749eff24c55ad35c9e4a648eefb455aad4
Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/338905
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
(cherry picked from commit 0525007b39985f82ba6cf352ee9a48671aabe8da)
Reviewed-on: https://chromium-review.googlesource.com/344642
diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
index 6985c85..f3316b1 100644
--- a/drivers/input/mouse/elan_i2c.c
+++ b/drivers/input/mouse/elan_i2c.c
@@ -128,7 +128,11 @@
#define ETP_I2C_IAP_REG_H 0x06
/* IAP F/W updater */
-#define ETP_FW_NAME "elan_i2c.bin"
+#define ETP_FW_BASENAME "elan_i2c"
+#define ETP_FW_EXTENSION "bin"
+#define ETP_FALLBACK_FW_NAME (ETP_FW_BASENAME "." ETP_FW_EXTENSION)
+#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
+#define ETP_MAX_FW_NAME_LEN 22
#define ETP_IAP_VERSION_ADDR 0x0082
#define ETP_IAP_START_ADDR 0x0083
#define ETP_FW_IAP_PAGE_ERR (1<<5)
@@ -600,6 +604,7 @@
u8 buffer[ETP_INF_LENGTH];
dev_dbg(dev, "Start firmware update....\n");
+ dev_err(dev, "Updating with fw_name '%s'\n", fw_name);
ret = request_firmware(&fw, fw_name, dev);
if (ret) {
@@ -1285,7 +1290,8 @@
{
struct elan_tp_data *data = dev_get_drvdata(dev);
data->product_id = elan_get_product_id(data);
- return sprintf(buf, "%d.0\n", data->product_id);
+ return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n",
+ data->product_id);
}
static ssize_t elan_sysfs_read_driver_ver(struct device *dev,
@@ -1329,27 +1335,47 @@
{
struct elan_tp_data *data = dev_get_drvdata(dev);
int ret, i;
- const char *fw_name;
- char tmp[count];
+ char fw_name[count];
+
/* Do not allow paths that step out of /lib/firmware */
if (strstr(buf, "../") != NULL)
return -EINVAL;
if (!strncmp(buf, "1", count) || !strncmp(buf, "1\n", count)) {
- fw_name = ETP_FW_NAME;
+ /* Look for a firmware with the product id appended. */
+ char *full_fw_name = kasprintf(GFP_KERNEL,
+ "%s_" ETP_PRODUCT_ID_FORMAT_STRING ".%s",
+ ETP_FW_BASENAME, data->product_id,
+ ETP_FW_EXTENSION);
+ if (!full_fw_name) {
+ dev_err(dev, "failed fw filename memory allocation.");
+ return -ENOMEM;
+ }
+ ret = elan_firmware(data, full_fw_name);
+
+ /* If that failed, fallback for backward compatibility. */
+ if (ret)
+ ret = elan_firmware(data, ETP_FALLBACK_FW_NAME);
+
+ if (ret) {
+ dev_err(dev, "cannot load firmware '%s' or '%s': %d\n",
+ full_fw_name, ETP_FALLBACK_FW_NAME, ret);
+ kfree(full_fw_name);
+ return ret;
+ }
+ kfree(full_fw_name);
} else {
/* check input file name buffer include '\n' or not */
for (i = 0; i < count; i++) {
if (buf[i] != '\n')
- tmp[i] = buf[i];
+ fw_name[i] = buf[i];
else
- tmp[i] = '\0';
+ fw_name[i] = '\0';
}
- fw_name = tmp;
+ ret = elan_firmware(data, fw_name);
}
- ret = elan_firmware(data, fw_name);
if (ret)
dev_err(dev, "firmware update failed.\n");
else