stm class: Add MIPI SyS-T protocol support
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>
Fri, 5 Oct 2018 12:42:58 +0000 (15:42 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 11 Oct 2018 10:12:54 +0000 (12:12 +0200)
This adds support for MIPI SyS-T protocol as specified in an open
standard [1]. In addition to marking message boundaries, it also
supports tagging messages with the source UUID, to provide better
distinction between trace sources, including payload length and
timestamp in the message's metadata.

This driver adds attributes to STP policy nodes to control/configure
these metadata features.

[1] https://www.mipi.org/specifications/sys-t

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Tested-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/hwtracing/stm/Kconfig
drivers/hwtracing/stm/Makefile
drivers/hwtracing/stm/p_sys-t.c [new file with mode: 0644]

index 262e789..752dd66 100644 (file)
@@ -26,6 +26,20 @@ config STM_PROTO_BASIC
          If you want to be able to use the basic protocol or want the
          backwards compatibility for your existing setup, say Y.
 
+config STM_PROTO_SYS_T
+       tristate "MIPI SyS-T STM framing protocol driver"
+       default CONFIG_STM
+       help
+         This is an implementation of MIPI SyS-T protocol to be used
+         over the STP transport. In addition to the data payload, it
+         also carries additional metadata for time correlation, better
+         means of trace source identification, etc.
+
+         The receiving side must be able to decode this protocol in
+         addition to the MIPI STP, in order to extract the data.
+
+         If you don't know what this is, say N.
+
 config STM_DUMMY
        tristate "Dummy STM driver"
        help
index 1571de6..1692fcd 100644 (file)
@@ -4,8 +4,10 @@ obj-$(CONFIG_STM)      += stm_core.o
 stm_core-y             := core.o policy.o
 
 obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o
+obj-$(CONFIG_STM_PROTO_SYS_T) += stm_p_sys-t.o
 
 stm_p_basic-y          := p_basic.o
+stm_p_sys-t-y          := p_sys-t.o
 
 obj-$(CONFIG_STM_DUMMY)        += dummy_stm.o
 
diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c
new file mode 100644 (file)
index 0000000..ffeb057
--- /dev/null
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPI SyS-T framing protocol for STM devices.
+ * Copyright (c) 2018, Intel Corporation.
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+#include <linux/stm.h>
+#include "stm.h"
+
+enum sys_t_message_type {
+       MIPI_SYST_TYPE_BUILD    = 0,
+       MIPI_SYST_TYPE_SHORT32,
+       MIPI_SYST_TYPE_STRING,
+       MIPI_SYST_TYPE_CATALOG,
+       MIPI_SYST_TYPE_RAW      = 6,
+       MIPI_SYST_TYPE_SHORT64,
+       MIPI_SYST_TYPE_CLOCK,
+};
+
+enum sys_t_message_severity {
+       MIPI_SYST_SEVERITY_MAX  = 0,
+       MIPI_SYST_SEVERITY_FATAL,
+       MIPI_SYST_SEVERITY_ERROR,
+       MIPI_SYST_SEVERITY_WARNING,
+       MIPI_SYST_SEVERITY_INFO,
+       MIPI_SYST_SEVERITY_USER1,
+       MIPI_SYST_SEVERITY_USER2,
+       MIPI_SYST_SEVERITY_DEBUG,
+};
+
+enum sys_t_message_build_subtype {
+       MIPI_SYST_BUILD_ID_COMPACT32 = 0,
+       MIPI_SYST_BUILD_ID_COMPACT64,
+       MIPI_SYST_BUILD_ID_LONG,
+};
+
+enum sys_t_message_clock_subtype {
+       MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1,
+};
+
+enum sys_t_message_string_subtype {
+       MIPI_SYST_STRING_GENERIC        = 1,
+       MIPI_SYST_STRING_FUNCTIONENTER,
+       MIPI_SYST_STRING_FUNCTIONEXIT,
+       MIPI_SYST_STRING_INVALIDPARAM   = 5,
+       MIPI_SYST_STRING_ASSERT         = 7,
+       MIPI_SYST_STRING_PRINTF_32      = 11,
+       MIPI_SYST_STRING_PRINTF_64      = 12,
+};
+
+#define MIPI_SYST_TYPE(t)              ((u32)(MIPI_SYST_TYPE_ ## t))
+#define MIPI_SYST_SEVERITY(s)          ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
+#define MIPI_SYST_OPT_LOC              BIT(8)
+#define MIPI_SYST_OPT_LEN              BIT(9)
+#define MIPI_SYST_OPT_CHK              BIT(10)
+#define MIPI_SYST_OPT_TS               BIT(11)
+#define MIPI_SYST_UNIT(u)              ((u32)(u) << 12)
+#define MIPI_SYST_ORIGIN(o)            ((u32)(o) << 16)
+#define MIPI_SYST_OPT_GUID             BIT(23)
+#define MIPI_SYST_SUBTYPE(s)           ((u32)(MIPI_SYST_ ## s) << 24)
+#define MIPI_SYST_UNITLARGE(u)         (MIPI_SYST_UNIT(u & 0xf) | \
+                                        MIPI_SYST_ORIGIN(u >> 4))
+#define MIPI_SYST_TYPES(t, s)          (MIPI_SYST_TYPE(t) | \
+                                        MIPI_SYST_SUBTYPE(t ## _ ## s))
+
+#define DATA_HEADER    (MIPI_SYST_TYPES(STRING, GENERIC)       | \
+                        MIPI_SYST_SEVERITY(INFO)               | \
+                        MIPI_SYST_OPT_GUID)
+
+struct sys_t_policy_node {
+       uuid_t          uuid;
+       bool            do_len;
+       unsigned long   ts_interval;
+};
+
+struct sys_t_output {
+       struct sys_t_policy_node        node;
+       unsigned long   ts_jiffies;
+};
+
+static void sys_t_policy_node_init(void *priv)
+{
+       struct sys_t_policy_node *pn = priv;
+
+       generate_random_uuid(pn->uuid.b);
+}
+
+static int sys_t_output_open(void *priv, struct stm_output *output)
+{
+       struct sys_t_policy_node *pn = priv;
+       struct sys_t_output *opriv;
+
+       opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
+       if (!opriv)
+               return -ENOMEM;
+
+       memcpy(&opriv->node, pn, sizeof(opriv->node));
+       output->pdrv_private = opriv;
+
+       return 0;
+}
+
+static void sys_t_output_close(struct stm_output *output)
+{
+       kfree(output->pdrv_private);
+}
+
+static ssize_t sys_t_policy_uuid_show(struct config_item *item,
+                                     char *page)
+{
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+
+       return sprintf(page, "%pU\n", &pn->uuid);
+}
+
+static ssize_t
+sys_t_policy_uuid_store(struct config_item *item, const char *page,
+                       size_t count)
+{
+       struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+       int ret;
+
+       mutex_lock(mutexp);
+       ret = uuid_parse(page, &pn->uuid);
+       mutex_unlock(mutexp);
+
+       return ret < 0 ? ret : count;
+}
+
+CONFIGFS_ATTR(sys_t_policy_, uuid);
+
+static ssize_t sys_t_policy_do_len_show(struct config_item *item,
+                                     char *page)
+{
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+
+       return sprintf(page, "%d\n", pn->do_len);
+}
+
+static ssize_t
+sys_t_policy_do_len_store(struct config_item *item, const char *page,
+                       size_t count)
+{
+       struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+       int ret;
+
+       mutex_lock(mutexp);
+       ret = kstrtobool(page, &pn->do_len);
+       mutex_unlock(mutexp);
+
+       return ret ? ret : count;
+}
+
+CONFIGFS_ATTR(sys_t_policy_, do_len);
+
+static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
+                                            char *page)
+{
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+
+       return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
+}
+
+static ssize_t
+sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
+                              size_t count)
+{
+       struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
+       struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
+       unsigned int ms;
+       int ret;
+
+       mutex_lock(mutexp);
+       ret = kstrtouint(page, 10, &ms);
+       mutex_unlock(mutexp);
+
+       if (!ret) {
+               pn->ts_interval = msecs_to_jiffies(ms);
+               return count;
+       }
+
+       return ret;
+}
+
+CONFIGFS_ATTR(sys_t_policy_, ts_interval);
+
+static struct configfs_attribute *sys_t_policy_attrs[] = {
+       &sys_t_policy_attr_uuid,
+       &sys_t_policy_attr_do_len,
+       &sys_t_policy_attr_ts_interval,
+       NULL,
+};
+
+static inline bool sys_t_need_ts(struct sys_t_output *op)
+{
+       if (op->node.ts_interval &&
+           time_after(op->ts_jiffies + op->node.ts_interval, jiffies)) {
+               op->ts_jiffies = jiffies;
+
+               return true;
+       }
+
+       return false;
+}
+
+static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
+                          unsigned int chan, const char *buf, size_t count)
+{
+       struct sys_t_output *op = output->pdrv_private;
+       unsigned int c = output->channel + chan;
+       unsigned int m = output->master;
+       const unsigned char nil = 0;
+       u32 header = DATA_HEADER;
+       ssize_t sz;
+
+       /* We require an existing policy node to proceed */
+       if (!op)
+               return -EINVAL;
+
+       if (op->node.do_len)
+               header |= MIPI_SYST_OPT_LEN;
+       if (sys_t_need_ts(op))
+               header |= MIPI_SYST_OPT_TS;
+
+       /*
+        * STP framing rules for SyS-T frames:
+        *   * the first packet of the SyS-T frame is timestamped;
+        *   * the last packet is a FLAG.
+        */
+       /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
+       /* HEADER */
+       sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
+                         4, (u8 *)&header);
+       if (sz <= 0)
+               return sz;
+
+       /* GUID */
+       sz = stm_data_write(data, m, c, false, op->node.uuid.b, UUID_SIZE);
+       if (sz <= 0)
+               return sz;
+
+       /* [LENGTH] */
+       if (op->node.do_len) {
+               u16 length = count;
+
+               sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
+                                 (u8 *)&length);
+               if (sz <= 0)
+                       return sz;
+       }
+
+       /* [TIMESTAMP] */
+       if (header & MIPI_SYST_OPT_TS) {
+               u64 ts = ktime_get_real_ns();
+
+               sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
+               if (sz <= 0)
+                       return sz;
+       }
+
+       /* DATA */
+       sz = stm_data_write(data, m, c, false, buf, count);
+       if (sz > 0)
+               data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
+
+       return sz;
+}
+
+static const struct stm_protocol_driver sys_t_pdrv = {
+       .owner                  = THIS_MODULE,
+       .name                   = "p_sys-t",
+       .priv_sz                = sizeof(struct sys_t_policy_node),
+       .write                  = sys_t_write,
+       .policy_attr            = sys_t_policy_attrs,
+       .policy_node_init       = sys_t_policy_node_init,
+       .output_open            = sys_t_output_open,
+       .output_close           = sys_t_output_close,
+};
+
+static int sys_t_stm_init(void)
+{
+       return stm_register_protocol(&sys_t_pdrv);
+}
+
+static void sys_t_stm_exit(void)
+{
+       stm_unregister_protocol(&sys_t_pdrv);
+}
+
+module_init(sys_t_stm_init);
+module_exit(sys_t_stm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");