Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Alexander Shishkin | 1620 | 87.57% | 3 | 50.00% |
Mikhail Lappo | 205 | 11.08% | 2 | 33.33% |
Andy Shevchenko | 25 | 1.35% | 1 | 16.67% |
Total | 1850 | 6 |
// 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, MIPI_SYST_TYPE_SBD, }; 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, }; /** * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID * * Structured Binary Data messages can send information of arbitrary length, * together with ID's that describe BLOB's content and layout. */ enum sys_t_message_sbd_subtype { MIPI_SYST_SBD_ID32 = 0, MIPI_SYST_SBD_ID64 = 1, }; #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) #define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \ MIPI_SYST_SEVERITY(MAX)) /* * SyS-T and ftrace headers are compatible to an extent that ftrace event ID * and args can be treated as SyS-T SBD message with 64-bit ID and arguments * BLOB right behind the header without modification. Bits [16:63] coming * together with message ID from ftrace aren't used by SBD and must be zeroed. * * 0 15 16 23 24 31 32 39 40 63 * ftrace: <event_id> <flags> <preempt> <-pid-> <----> <args> * SBD: <------- msg_id ------------------------------> <BLOB> */ #define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \ MIPI_SYST_SEVERITY(INFO) | \ MIPI_SYST_OPT_GUID) struct sys_t_policy_node { uuid_t uuid; bool do_len; unsigned long ts_interval; unsigned long clocksync_interval; }; struct sys_t_output { struct sys_t_policy_node node; unsigned long ts_jiffies; unsigned long clocksync_jiffies; }; static void sys_t_policy_node_init(void *priv) { struct sys_t_policy_node *pn = priv; uuid_gen(&pn->uuid); } 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 ssize_t sys_t_policy_clocksync_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->clocksync_interval)); } static ssize_t sys_t_policy_clocksync_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->clocksync_interval = msecs_to_jiffies(ms); return count; } return ret; } CONFIGFS_ATTR(sys_t_policy_, clocksync_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, &sys_t_policy_attr_clocksync_interval, NULL, }; static inline bool sys_t_need_ts(struct sys_t_output *op) { if (op->node.ts_interval && time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) { op->ts_jiffies = jiffies; return true; } return false; } static bool sys_t_need_clock_sync(struct sys_t_output *op) { if (op->node.clocksync_interval && time_after(jiffies, op->clocksync_jiffies + op->node.clocksync_interval)) { op->clocksync_jiffies = jiffies; return true; } return false; } static ssize_t sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c) { u32 header = CLOCK_SYNC_HEADER; const unsigned char nil = 0; u64 payload[2]; /* Clock value and frequency */ ssize_t sz; sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, 4, (u8 *)&header); if (sz <= 0) return sz; payload[0] = ktime_get_real_ns(); payload[1] = NSEC_PER_SEC; sz = stm_data_write(data, m, c, false, &payload, sizeof(payload)); if (sz <= 0) return sz; data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); return sizeof(header) + sizeof(payload); } static inline u32 sys_t_header(struct stm_source_data *source) { if (source && source->type == STM_FTRACE) return SBD_HEADER; return DATA_HEADER; } static ssize_t sys_t_write_data(struct stm_data *data, struct stm_source_data *source, unsigned int master, unsigned int channel, bool ts_first, const void *buf, size_t count) { ssize_t sz; const unsigned char nil = 0; /* * Ftrace is zero-copy compatible with SyS-T SBD, but requires * special handling of first 64 bits. Trim and send them separately * to avoid damage on original ftrace buffer. */ if (source && source->type == STM_FTRACE) { u64 compat_ftrace_header; ssize_t header_sz; ssize_t buf_sz; if (count < sizeof(compat_ftrace_header)) return -EINVAL; /* SBD only makes use of low 16 bits (event ID) from ftrace event */ compat_ftrace_header = *(u64 *)buf & 0xffff; header_sz = stm_data_write(data, master, channel, false, &compat_ftrace_header, sizeof(compat_ftrace_header)); if (header_sz != sizeof(compat_ftrace_header)) return header_sz; buf_sz = stm_data_write(data, master, channel, false, buf + header_sz, count - header_sz); if (buf_sz != count - header_sz) return buf_sz; sz = header_sz + buf_sz; } else { sz = stm_data_write(data, master, channel, false, buf, count); } if (sz <= 0) return sz; data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); return sz; } static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, unsigned int chan, const char *buf, size_t count, struct stm_source_data *source) { struct sys_t_output *op = output->pdrv_private; unsigned int c = output->channel + chan; unsigned int m = output->master; u32 header = sys_t_header(source); u8 uuid[UUID_SIZE]; ssize_t sz; /* We require an existing policy node to proceed */ if (!op) return -EINVAL; if (sys_t_need_clock_sync(op)) { sz = sys_t_clock_sync(data, m, c); if (sz <= 0) return sz; } 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 */ export_uuid(uuid, &op->node.uuid); sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid)); 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 */ return sys_t_write_data(data, source, m, c, false, buf, count); } 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>");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1