From: Duncan Laurie <[email protected]>

Add a sysfs attribute that allows sending raw commands to the EC.
This is useful for development and debug but should not be enabled
in a production environment.

> echo 00 f0 38 00 03 00 > /sys/bus/platform/devices/GOOG000C\:00/raw
> cat /sys/bus/platform/devices/GOOG000C\:00/raw
00 37 33 38 65 64 00...

Signed-off-by: Duncan Laurie <[email protected]>
Signed-off-by: Nick Crews <[email protected]>
---

 drivers/platform/chrome/Kconfig            |  10 ++
 drivers/platform/chrome/wilco_ec.h         |   6 +
 drivers/platform/chrome/wilco_ec_mailbox.c |   6 -
 drivers/platform/chrome/wilco_ec_sysfs.c   | 126 +++++++++++++++++++++
 4 files changed, 142 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 4168d5e6bedc..05c6d9a00395 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -61,6 +61,16 @@ config WILCO_EC
          To compile this driver as a module, choose M here: the
          module will be called wilco_ec.
 
+config WILCO_EC_SYSFS_RAW
+       bool "Enable raw access to EC via sysfs"
+       depends on WILCO_EC
+       default n
+       help
+         If you say Y here, you get support for sending raw commands to
+         the Wilco EC via sysfs.  These commands do not do any byte
+         manipulation and allow for testing arbitrary commands.  This
+         interface is intended for debug only and is disabled by default.
+
 config CROS_EC_CTL
         tristate
 
diff --git a/drivers/platform/chrome/wilco_ec.h 
b/drivers/platform/chrome/wilco_ec.h
index 699f4cf744dc..0b3dec4e2830 100644
--- a/drivers/platform/chrome/wilco_ec.h
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -20,6 +20,12 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 
+/* Normal commands have a maximum 32 bytes of data */
+#define EC_MAILBOX_DATA_SIZE           32
+
+/* Extended commands have 256 bytes of response data */
+#define EC_MAILBOX_DATA_SIZE_EXTENDED  256
+
 #define WILCO_EC_FLAG_NO_RESPONSE      BIT(0) /* EC does not respond */
 #define WILCO_EC_FLAG_EXTENDED_DATA    BIT(1) /* EC returns 256 data bytes */
 #define WILCO_EC_FLAG_RAW_REQUEST      BIT(2) /* Do not trim request data */
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c 
b/drivers/platform/chrome/wilco_ec_mailbox.c
index 414ea0a8ad03..1cb34b7280fd 100644
--- a/drivers/platform/chrome/wilco_ec_mailbox.c
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -44,12 +44,6 @@
 /* Version of EC protocol */
 #define EC_MAILBOX_PROTO_VERSION       3
 
-/* Normal commands have a maximum 32 bytes of data */
-#define EC_MAILBOX_DATA_SIZE           32
-
-/* Extended commands have 256 bytes of response data */
-#define EC_MAILBOX_DATA_SIZE_EXTENDED  256
-
 /* Number of header bytes to be counted as data bytes */
 #define EC_MAILBOX_DATA_EXTRA          2
 
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c 
b/drivers/platform/chrome/wilco_ec_sysfs.c
index f9ae6cef6169..eeebd4ba4a39 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -23,6 +23,126 @@
 #define EC_INFO_SIZE                    9
 #define EC_COMMAND_STEALTH_MODE                0xfc
 
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+
+/* Raw data buffer, large enough to hold extended responses */
+static size_t raw_response_size;
+static u8 raw_response_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
+
+/*
+ * raw: write a raw command and return the result
+ *
+ * Bytes 0-1 indicate the message type:
+ *  00 F0 = Execute Legacy Command
+ *  00 F2 = Read/Write NVRAM Property
+ * Byte 2 provides the command code
+ * Bytes 3+ consist of the data passed in the request
+ *
+ * example: read the EC info type 1:
+ *  # echo 00 f0 38 00 01 00 > raw
+ *  # cat raw
+ *  00 38 31 34 34 66 00 00 00 00 00 00 00 00 00 00 00...
+ */
+
+static ssize_t raw_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       ssize_t count = 0;
+
+       if (raw_response_size) {
+               int i;
+
+               for (i = 0; i < raw_response_size; ++i)
+                       count += scnprintf(buf + count, PAGE_SIZE - count,
+                                          "%02x ", raw_response_data[i]);
+
+               count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+               /* Only return response the first time it is read */
+               raw_response_size = 0;
+       }
+
+       return count;
+}
+
+static ssize_t raw_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct wilco_ec_device *ec = dev_get_drvdata(dev);
+       struct wilco_ec_message msg;
+       u8 raw_request_data[EC_MAILBOX_DATA_SIZE];
+       int in_offset = 0;
+       int out_offset = 0;
+       int ret;
+
+       while (in_offset < count) {
+               char word_buf[EC_MAILBOX_DATA_SIZE];
+               u8 byte;
+               int start_offset = in_offset;
+               int end_offset;
+
+               /* Find the start of the byte */
+               while (buf[start_offset] && isspace(buf[start_offset]))
+                       start_offset++;
+               if (!buf[start_offset])
+                       break;
+
+               /* Find the start of the next byte, if any */
+               end_offset = start_offset;
+               while (buf[end_offset] && !isspace(buf[end_offset]))
+                       end_offset++;
+               if (start_offset > count || end_offset > count)
+                       break;
+               if (start_offset > EC_MAILBOX_DATA_SIZE ||
+                   end_offset > EC_MAILBOX_DATA_SIZE)
+                       break;
+
+               /* Copy to a new nul-terminated string */
+               memcpy(word_buf, buf + start_offset, end_offset - start_offset);
+               word_buf[end_offset - start_offset] = '\0';
+
+               /* Convert from hex string */
+               ret = kstrtou8(word_buf, 16, &byte);
+               if (ret)
+                       break;
+
+               /* Fill this byte into the request buffer */
+               raw_request_data[out_offset++] = byte;
+               if (out_offset >= EC_MAILBOX_DATA_SIZE)
+                       break;
+
+               in_offset = end_offset;
+       }
+       if (out_offset == 0)
+               return -EINVAL;
+
+       /* Clear response data buffer */
+       memset(raw_response_data, 0, EC_MAILBOX_DATA_SIZE_EXTENDED);
+
+       msg.type = raw_request_data[0] << 8 | raw_request_data[1];
+       msg.flags = WILCO_EC_FLAG_RAW;
+       msg.command = raw_request_data[2];
+       msg.request_data = raw_request_data + 3;
+       msg.request_size = out_offset - 3;
+       msg.response_data = raw_response_data;
+       msg.response_size = EC_MAILBOX_DATA_SIZE;
+
+       /* Telemetry commands use extended response data */
+       if (msg.type == WILCO_EC_MSG_TELEMETRY) {
+               msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
+               msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+       }
+
+       ret = wilco_ec_mailbox(ec, &msg);
+       if (ret < 0)
+               return ret;
+       raw_response_size = ret;
+       return count;
+}
+
+#endif /* CONFIG_WILCO_EC_SYSFS_RAW */
+
 struct ec_info {
        u8 index;
        const char *label;
@@ -102,10 +222,16 @@ static ssize_t stealth_mode_store(struct device *dev,
 
 static DEVICE_ATTR_RO(version);
 static DEVICE_ATTR_WO(stealth_mode);
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+static DEVICE_ATTR_RW(raw);
+#endif
 
 static struct attribute *wilco_ec_attrs[] = {
        &dev_attr_version.attr,
        &dev_attr_stealth_mode.attr,
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+       &dev_attr_raw.attr,
+#endif
        NULL
 };
 ATTRIBUTE_GROUPS(wilco_ec);
-- 
2.20.0.405.gbc1bbc6f85-goog

Reply via email to