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