Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
# SPDX-License-Identifier: GPL-2.0-only
config STM
tristate "System Trace Module devices"
select CONFIGFS_FS
help
A System Trace Module (STM) is a device exporting data in System
Trace Protocol (STP) format as defined by MIPI STP standards.
Examples of such devices are Intel(R) Trace Hub and Coresight STM.
Say Y here to enable System Trace Module device support.
if STM
config STM_PROTO_BASIC
tristate "Basic STM framing protocol driver"
default STM
help
This is a simple framing protocol for sending data over STM
devices. This was the protocol that the STM framework used
exclusively until the MIPI SyS-T support was added. Use this
driver for compatibility with your existing STM setup.
The receiving side only needs to be able to decode the MIPI
STP protocol in order to extract the data.
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 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
This is a simple dummy device that pretends to be an stm device
and discards your data. Use for stm class testing.
If you don't know what this is, say N.
config STM_SOURCE_CONSOLE
tristate "Kernel console over STM devices"
help
This is a kernel space trace source that sends kernel log
messages to trace hosts over STM devices.
If you want to send kernel console messages over STM devices,
say Y.
config STM_SOURCE_HEARTBEAT
tristate "Heartbeat over STM devices"
help
This is a kernel space trace source that sends periodic
heartbeat messages to trace hosts over STM devices. It is
also useful for testing stm class drivers and the stm class
framework itself.
If you want to send heartbeat messages over STM devices,
say Y.
config STM_SOURCE_FTRACE
tristate "Copy the output from kernel Ftrace to STM engine"
depends on TRACING
help
This option can be used to copy the output from kernel Ftrace
to STM engine. Enabling this option will introduce a slight
timing effect.
If you want to send kernel Ftrace messages over STM devices,
say Y.
endif
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
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
obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o
obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o
obj-$(CONFIG_STM_SOURCE_FTRACE) += stm_ftrace.o
stm_console-y := console.o
stm_heartbeat-y := heartbeat.o
stm_ftrace-y := ftrace.o
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Simple kernel console driver for STM devices
* Copyright (c) 2014, Intel Corporation.
*
* STM console will send kernel messages over STM devices to a trace host.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/slab.h>
#include <linux/stm.h>
static int stm_console_link(struct stm_source_data *data);
static void stm_console_unlink(struct stm_source_data *data);
static struct stm_console {
struct stm_source_data data;
struct console console;
} stm_console = {
.data = {
.name = "console",
.nr_chans = 1,
.type = STM_USER,
.link = stm_console_link,
.unlink = stm_console_unlink,
},
};
static void
stm_console_write(struct console *con, const char *buf, unsigned len)
{
struct stm_console *sc = container_of(con, struct stm_console, console);
stm_source_write(&sc->data, 0, buf, len);
}
static int stm_console_link(struct stm_source_data *data)
{
struct stm_console *sc = container_of(data, struct stm_console, data);
strcpy(sc->console.name, "stm_console");
sc->console.write = stm_console_write;
sc->console.flags = CON_ENABLED | CON_PRINTBUFFER;
register_console(&sc->console);
return 0;
}
static void stm_console_unlink(struct stm_source_data *data)
{
struct stm_console *sc = container_of(data, struct stm_console, data);
unregister_console(&sc->console);
}
static int stm_console_init(void)
{
return stm_source_register_device(NULL, &stm_console.data);
}
static void stm_console_exit(void)
{
stm_source_unregister_device(&stm_console.data);
}
module_init(stm_console_init);
module_exit(stm_console_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("stm_console driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0
/*
* A dummy STM device for stm/stm_source class testing.
* Copyright (c) 2014, Intel Corporation.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stm.h>
#include <uapi/linux/stm.h>
static ssize_t notrace
dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int packet, unsigned int flags,
unsigned int size, const unsigned char *payload)
{
#ifdef DEBUG
u64 pl = 0;
if (payload)
pl = *(u64 *)payload;
if (size < 8)
pl &= (1ull << (size * 8)) - 1;
trace_printk("[%u:%u] [pkt: %x/%x] (%llx)\n", master, channel,
packet, size, pl);
#endif
return size;
}
#define DUMMY_STM_MAX 32
static struct stm_data dummy_stm[DUMMY_STM_MAX];
static int nr_dummies = 4;
module_param(nr_dummies, int, 0400);
static unsigned int fail_mode;
module_param(fail_mode, int, 0600);
static unsigned int master_min;
module_param(master_min, int, 0400);
static unsigned int master_max = STP_MASTER_MAX;
module_param(master_max, int, 0400);
static unsigned int nr_channels = STP_CHANNEL_MAX;
module_param(nr_channels, int, 0400);
static int dummy_stm_link(struct stm_data *data, unsigned int master,
unsigned int channel)
{
if (fail_mode && (channel & fail_mode))
return -EINVAL;
return 0;
}
static int dummy_stm_init(void)
{
int i, ret = -ENOMEM;
if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
return -EINVAL;
if (master_min > master_max ||
master_max > STP_MASTER_MAX ||
nr_channels > STP_CHANNEL_MAX)
return -EINVAL;
for (i = 0; i < nr_dummies; i++) {
dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
if (!dummy_stm[i].name)
goto fail_unregister;
dummy_stm[i].sw_start = master_min;
dummy_stm[i].sw_end = master_max;
dummy_stm[i].sw_nchannels = nr_channels;
dummy_stm[i].packet = dummy_stm_packet;
dummy_stm[i].link = dummy_stm_link;
ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE);
if (ret)
goto fail_free;
}
return 0;
fail_unregister:
for (i--; i >= 0; i--) {
stm_unregister_device(&dummy_stm[i]);
fail_free:
kfree(dummy_stm[i].name);
}
return ret;
}
static void dummy_stm_exit(void)
{
int i;
for (i = 0; i < nr_dummies; i++) {
stm_unregister_device(&dummy_stm[i]);
kfree(dummy_stm[i].name);
}
}
module_init(dummy_stm_init);
module_exit(dummy_stm_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("dummy_stm device");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Simple kernel driver to link kernel Ftrace and an STM device
* Copyright (c) 2016, Linaro Ltd.
*
* STM Ftrace will be registered as a trace_export.
*/
#include <linux/module.h>
#include <linux/stm.h>
#include <linux/trace.h>
#define STM_FTRACE_NR_CHANNELS 1
#define STM_FTRACE_CHAN 0
static int stm_ftrace_link(struct stm_source_data *data);
static void stm_ftrace_unlink(struct stm_source_data *data);
static struct stm_ftrace {
struct stm_source_data data;
struct trace_export ftrace;
} stm_ftrace = {
.data = {
.name = "ftrace",
.nr_chans = STM_FTRACE_NR_CHANNELS,
.type = STM_FTRACE,
.link = stm_ftrace_link,
.unlink = stm_ftrace_unlink,
},
};
/**
* stm_ftrace_write() - write data to STM via 'stm_ftrace' source
* @buf: buffer containing the data packet
* @len: length of the data packet
*/
static void notrace
stm_ftrace_write(struct trace_export *export, const void *buf, unsigned int len)
{
struct stm_ftrace *stm = container_of(export, struct stm_ftrace, ftrace);
/* This is called from trace system with preemption disabled */
unsigned int cpu = smp_processor_id();
stm_source_write(&stm->data, STM_FTRACE_CHAN + cpu, buf, len);
}
static int stm_ftrace_link(struct stm_source_data *data)
{
struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
sf->ftrace.write = stm_ftrace_write;
sf->ftrace.flags = TRACE_EXPORT_FUNCTION | TRACE_EXPORT_EVENT
| TRACE_EXPORT_MARKER;
return register_ftrace_export(&sf->ftrace);
}
static void stm_ftrace_unlink(struct stm_source_data *data)
{
struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
unregister_ftrace_export(&sf->ftrace);
}
static int __init stm_ftrace_init(void)
{
int ret;
stm_ftrace.data.nr_chans = roundup_pow_of_two(num_possible_cpus());
ret = stm_source_register_device(NULL, &stm_ftrace.data);
if (ret)
pr_err("Failed to register stm_source - ftrace.\n");
return ret;
}
static void __exit stm_ftrace_exit(void)
{
stm_source_unregister_device(&stm_ftrace.data);
}
module_init(stm_ftrace_init);
module_exit(stm_ftrace_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("stm_ftrace driver");
MODULE_AUTHOR("Chunyan Zhang <zhang.chunyan@linaro.org>");
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Simple heartbeat STM source driver
* Copyright (c) 2016, Intel Corporation.
*
* Heartbeat STM source will send repetitive messages over STM devices to a
* trace host.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/slab.h>
#include <linux/stm.h>
#define STM_HEARTBEAT_MAX 32
static int nr_devs = 4;
static int interval_ms = 10;
module_param(nr_devs, int, 0400);
module_param(interval_ms, int, 0600);
static struct stm_heartbeat {
struct stm_source_data data;
struct hrtimer hrtimer;
unsigned int active;
} stm_heartbeat[STM_HEARTBEAT_MAX];
static const char str[] = "heartbeat stm source driver is here to serve you";
static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr)
{
struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat,
hrtimer);
stm_source_write(&heartbeat->data, 0, str, sizeof str);
if (heartbeat->active)
hrtimer_forward_now(hr, ms_to_ktime(interval_ms));
return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART;
}
static int stm_heartbeat_link(struct stm_source_data *data)
{
struct stm_heartbeat *heartbeat =
container_of(data, struct stm_heartbeat, data);
heartbeat->active = 1;
hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms),
HRTIMER_MODE_ABS);
return 0;
}
static void stm_heartbeat_unlink(struct stm_source_data *data)
{
struct stm_heartbeat *heartbeat =
container_of(data, struct stm_heartbeat, data);
heartbeat->active = 0;
hrtimer_cancel(&heartbeat->hrtimer);
}
static int stm_heartbeat_init(void)
{
int i, ret;
if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
return -EINVAL;
for (i = 0; i < nr_devs; i++) {
stm_heartbeat[i].data.name =
kasprintf(GFP_KERNEL, "heartbeat.%d", i);
if (!stm_heartbeat[i].data.name) {
ret = -ENOMEM;
goto fail_unregister;
}
stm_heartbeat[i].data.nr_chans = 1;
stm_heartbeat[i].data.type = STM_USER;
stm_heartbeat[i].data.link = stm_heartbeat_link;
stm_heartbeat[i].data.unlink = stm_heartbeat_unlink;
hrtimer_setup(&stm_heartbeat[i].hrtimer, stm_heartbeat_hrtimer_handler,
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ret = stm_source_register_device(NULL, &stm_heartbeat[i].data);
if (ret)
goto fail_free;
}
return 0;
fail_unregister:
for (i--; i >= 0; i--) {
stm_source_unregister_device(&stm_heartbeat[i].data);
fail_free:
kfree(stm_heartbeat[i].data.name);
}
return ret;
}
static void stm_heartbeat_exit(void)
{
int i;
for (i = 0; i < nr_devs; i++) {
stm_source_unregister_device(&stm_heartbeat[i].data);
kfree(stm_heartbeat[i].data.name);
}
}
module_init(stm_heartbeat_init);
module_exit(stm_heartbeat_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("stm_heartbeat driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Basic framing protocol for STM devices.
* Copyright (c) 2018, Intel Corporation.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/stm.h>
#include "stm.h"
static ssize_t basic_write(struct stm_data *data, struct stm_output *output,
unsigned int chan, const char *buf, size_t count,
struct stm_source_data *source)
{
unsigned int c = output->channel + chan;
unsigned int m = output->master;
const unsigned char nil = 0;
ssize_t sz;
sz = stm_data_write(data, m, c, true, buf, count);
if (sz > 0)
data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
return sz;
}
static const struct stm_protocol_driver basic_pdrv = {
.owner = THIS_MODULE,
.name = "p_basic",
.write = basic_write,
};
static int basic_stm_init(void)
{
return stm_register_protocol(&basic_pdrv);
}
static void basic_stm_exit(void)
{
stm_unregister_protocol(&basic_pdrv);
}
module_init(basic_stm_init);
module_exit(basic_stm_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Basic STM framing protocol driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
+461
View File
@@ -0,0 +1,461 @@
// 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_obj(*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>");
+570
View File
@@ -0,0 +1,570 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Trace Module (STM) master/channel allocation policy management
* Copyright (c) 2014, Intel Corporation.
*
* A master/channel allocation policy allows mapping string identifiers to
* master and channel ranges, where allocation can be done.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/configfs.h>
#include <linux/slab.h>
#include <linux/stm.h>
#include "stm.h"
/*
* STP Master/Channel allocation policy configfs layout.
*/
struct stp_policy {
struct config_group group;
struct stm_device *stm;
};
struct stp_policy_node {
struct config_group group;
struct stp_policy *policy;
unsigned int first_master;
unsigned int last_master;
unsigned int first_channel;
unsigned int last_channel;
/* this is the one that's exposed to the attributes */
unsigned char priv[];
};
void *stp_policy_node_priv(struct stp_policy_node *pn)
{
if (!pn)
return NULL;
return pn->priv;
}
static struct configfs_subsystem stp_policy_subsys;
void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
unsigned int *mstart, unsigned int *mend,
unsigned int *cstart, unsigned int *cend)
{
*mstart = policy_node->first_master;
*mend = policy_node->last_master;
*cstart = policy_node->first_channel;
*cend = policy_node->last_channel;
}
static inline struct stp_policy *to_stp_policy(struct config_item *item)
{
return item ?
container_of(to_config_group(item), struct stp_policy, group) :
NULL;
}
static inline struct stp_policy_node *
to_stp_policy_node(struct config_item *item)
{
return item ?
container_of(to_config_group(item), struct stp_policy_node,
group) :
NULL;
}
void *to_pdrv_policy_node(struct config_item *item)
{
struct stp_policy_node *node = to_stp_policy_node(item);
return stp_policy_node_priv(node);
}
EXPORT_SYMBOL_GPL(to_pdrv_policy_node);
static ssize_t
stp_policy_node_masters_show(struct config_item *item, char *page)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
ssize_t count;
count = sprintf(page, "%u %u\n", policy_node->first_master,
policy_node->last_master);
return count;
}
static ssize_t
stp_policy_node_masters_store(struct config_item *item, const char *page,
size_t count)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
unsigned int first, last;
struct stm_device *stm;
char *p = (char *)page;
ssize_t ret = -ENODEV;
if (sscanf(p, "%u %u", &first, &last) != 2)
return -EINVAL;
mutex_lock(&stp_policy_subsys.su_mutex);
stm = policy_node->policy->stm;
if (!stm)
goto unlock;
/* must be within [sw_start..sw_end], which is an inclusive range */
if (first > last || first < stm->data->sw_start ||
last > stm->data->sw_end) {
ret = -ERANGE;
goto unlock;
}
ret = count;
policy_node->first_master = first;
policy_node->last_master = last;
unlock:
mutex_unlock(&stp_policy_subsys.su_mutex);
return ret;
}
static ssize_t
stp_policy_node_channels_show(struct config_item *item, char *page)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
ssize_t count;
count = sprintf(page, "%u %u\n", policy_node->first_channel,
policy_node->last_channel);
return count;
}
static ssize_t
stp_policy_node_channels_store(struct config_item *item, const char *page,
size_t count)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
unsigned int first, last;
struct stm_device *stm;
char *p = (char *)page;
ssize_t ret = -ENODEV;
if (sscanf(p, "%u %u", &first, &last) != 2)
return -EINVAL;
mutex_lock(&stp_policy_subsys.su_mutex);
stm = policy_node->policy->stm;
if (!stm)
goto unlock;
if (first > INT_MAX || last > INT_MAX || first > last ||
last >= stm->data->sw_nchannels) {
ret = -ERANGE;
goto unlock;
}
ret = count;
policy_node->first_channel = first;
policy_node->last_channel = last;
unlock:
mutex_unlock(&stp_policy_subsys.su_mutex);
return ret;
}
static void stp_policy_node_release(struct config_item *item)
{
struct stp_policy_node *node = to_stp_policy_node(item);
kfree(node);
}
static struct configfs_item_operations stp_policy_node_item_ops = {
.release = stp_policy_node_release,
};
CONFIGFS_ATTR(stp_policy_node_, masters);
CONFIGFS_ATTR(stp_policy_node_, channels);
static struct configfs_attribute *stp_policy_node_attrs[] = {
&stp_policy_node_attr_masters,
&stp_policy_node_attr_channels,
NULL,
};
static const struct config_item_type stp_policy_type;
static const struct config_item_type stp_policy_node_type;
const struct config_item_type *
get_policy_node_type(struct configfs_attribute **attrs)
{
struct config_item_type *type;
struct configfs_attribute **merged;
type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type),
GFP_KERNEL);
if (!type)
return NULL;
merged = memcat_p(stp_policy_node_attrs, attrs);
if (!merged) {
kfree(type);
return NULL;
}
type->ct_attrs = merged;
return type;
}
static struct config_group *
stp_policy_node_make(struct config_group *group, const char *name)
{
const struct config_item_type *type = &stp_policy_node_type;
struct stp_policy_node *policy_node, *parent_node;
const struct stm_protocol_driver *pdrv;
struct stp_policy *policy;
if (group->cg_item.ci_type == &stp_policy_type) {
policy = container_of(group, struct stp_policy, group);
} else {
parent_node = container_of(group, struct stp_policy_node,
group);
policy = parent_node->policy;
}
if (!policy->stm)
return ERR_PTR(-ENODEV);
pdrv = policy->stm->pdrv;
policy_node =
kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]),
GFP_KERNEL);
if (!policy_node)
return ERR_PTR(-ENOMEM);
if (pdrv->policy_node_init)
pdrv->policy_node_init((void *)policy_node->priv);
if (policy->stm->pdrv_node_type)
type = policy->stm->pdrv_node_type;
config_group_init_type_name(&policy_node->group, name, type);
policy_node->policy = policy;
/* default values for the attributes */
policy_node->first_master = policy->stm->data->sw_start;
policy_node->last_master = policy->stm->data->sw_end;
policy_node->first_channel = 0;
policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
return &policy_node->group;
}
static void
stp_policy_node_drop(struct config_group *group, struct config_item *item)
{
config_item_put(item);
}
static struct configfs_group_operations stp_policy_node_group_ops = {
.make_group = stp_policy_node_make,
.drop_item = stp_policy_node_drop,
};
static const struct config_item_type stp_policy_node_type = {
.ct_item_ops = &stp_policy_node_item_ops,
.ct_group_ops = &stp_policy_node_group_ops,
.ct_attrs = stp_policy_node_attrs,
.ct_owner = THIS_MODULE,
};
/*
* Root group: policies.
*/
static ssize_t stp_policy_device_show(struct config_item *item,
char *page)
{
struct stp_policy *policy = to_stp_policy(item);
ssize_t count;
count = sprintf(page, "%s\n",
(policy && policy->stm) ?
policy->stm->data->name :
"<none>");
return count;
}
CONFIGFS_ATTR_RO(stp_policy_, device);
static ssize_t stp_policy_protocol_show(struct config_item *item,
char *page)
{
struct stp_policy *policy = to_stp_policy(item);
ssize_t count;
count = sprintf(page, "%s\n",
(policy && policy->stm) ?
policy->stm->pdrv->name :
"<none>");
return count;
}
CONFIGFS_ATTR_RO(stp_policy_, protocol);
static struct configfs_attribute *stp_policy_attrs[] = {
&stp_policy_attr_device,
&stp_policy_attr_protocol,
NULL,
};
void stp_policy_unbind(struct stp_policy *policy)
{
struct stm_device *stm = policy->stm;
/*
* stp_policy_release() will not call here if the policy is already
* unbound; other users should not either, as no link exists between
* this policy and anything else in that case
*/
if (WARN_ON_ONCE(!policy->stm))
return;
lockdep_assert_held(&stm->policy_mutex);
stm->policy = NULL;
policy->stm = NULL;
/*
* Drop the reference on the protocol driver and lose the link.
*/
stm_put_protocol(stm->pdrv);
stm->pdrv = NULL;
stm_put_device(stm);
}
static void stp_policy_release(struct config_item *item)
{
struct stp_policy *policy = to_stp_policy(item);
struct stm_device *stm = policy->stm;
/* a policy *can* be unbound and still exist in configfs tree */
if (!stm)
return;
mutex_lock(&stm->policy_mutex);
stp_policy_unbind(policy);
mutex_unlock(&stm->policy_mutex);
kfree(policy);
}
static struct configfs_item_operations stp_policy_item_ops = {
.release = stp_policy_release,
};
static struct configfs_group_operations stp_policy_group_ops = {
.make_group = stp_policy_node_make,
};
static const struct config_item_type stp_policy_type = {
.ct_item_ops = &stp_policy_item_ops,
.ct_group_ops = &stp_policy_group_ops,
.ct_attrs = stp_policy_attrs,
.ct_owner = THIS_MODULE,
};
static struct config_group *
stp_policy_make(struct config_group *group, const char *name)
{
const struct config_item_type *pdrv_node_type;
const struct stm_protocol_driver *pdrv;
char *devname, *proto, *p;
struct config_group *ret;
struct stm_device *stm;
int err;
devname = kasprintf(GFP_KERNEL, "%s", name);
if (!devname)
return ERR_PTR(-ENOMEM);
/*
* node must look like <device_name>.<policy_name>, where
* <device_name> is the name of an existing stm device; may
* contain dots;
* <policy_name> is an arbitrary string; may not contain dots
* <device_name>:<protocol_name>.<policy_name>
*/
p = strrchr(devname, '.');
if (!p) {
kfree(devname);
return ERR_PTR(-EINVAL);
}
*p = '\0';
/*
* look for ":<protocol_name>":
* + no protocol suffix: fall back to whatever is available;
* + unknown protocol: fail the whole thing
*/
proto = strrchr(devname, ':');
if (proto)
*proto++ = '\0';
stm = stm_find_device(devname);
if (!stm) {
kfree(devname);
return ERR_PTR(-ENODEV);
}
err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type);
kfree(devname);
if (err) {
stm_put_device(stm);
return ERR_PTR(-ENODEV);
}
mutex_lock(&stm->policy_mutex);
if (stm->policy) {
ret = ERR_PTR(-EBUSY);
goto unlock_policy;
}
stm->policy = kzalloc_obj(*stm->policy);
if (!stm->policy) {
ret = ERR_PTR(-ENOMEM);
goto unlock_policy;
}
config_group_init_type_name(&stm->policy->group, name,
&stp_policy_type);
stm->pdrv = pdrv;
stm->pdrv_node_type = pdrv_node_type;
stm->policy->stm = stm;
ret = &stm->policy->group;
unlock_policy:
mutex_unlock(&stm->policy_mutex);
if (IS_ERR(ret)) {
/*
* pdrv and stm->pdrv at this point can be quite different,
* and only one of them needs to be 'put'
*/
stm_put_protocol(pdrv);
stm_put_device(stm);
}
return ret;
}
static struct configfs_group_operations stp_policy_root_group_ops = {
.make_group = stp_policy_make,
};
static const struct config_item_type stp_policy_root_type = {
.ct_group_ops = &stp_policy_root_group_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem stp_policy_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "stp-policy",
.ci_type = &stp_policy_root_type,
},
},
};
/*
* Lock the policy mutex from the outside
*/
static struct stp_policy_node *
__stp_policy_node_lookup(struct stp_policy *policy, char *s)
{
struct stp_policy_node *policy_node, *ret = NULL;
struct list_head *head = &policy->group.cg_children;
struct config_item *item;
char *start, *end = s;
if (list_empty(head))
return NULL;
next:
for (;;) {
start = strsep(&end, "/");
if (!start)
break;
if (!*start)
continue;
list_for_each_entry(item, head, ci_entry) {
policy_node = to_stp_policy_node(item);
if (!strcmp(start,
policy_node->group.cg_item.ci_name)) {
ret = policy_node;
if (!end)
goto out;
head = &policy_node->group.cg_children;
goto next;
}
}
break;
}
out:
return ret;
}
struct stp_policy_node *
stp_policy_node_lookup(struct stm_device *stm, char *s)
{
struct stp_policy_node *policy_node = NULL;
mutex_lock(&stp_policy_subsys.su_mutex);
mutex_lock(&stm->policy_mutex);
if (stm->policy)
policy_node = __stp_policy_node_lookup(stm->policy, s);
mutex_unlock(&stm->policy_mutex);
if (policy_node)
config_item_get(&policy_node->group.cg_item);
else
mutex_unlock(&stp_policy_subsys.su_mutex);
return policy_node;
}
void stp_policy_node_put(struct stp_policy_node *policy_node)
{
lockdep_assert_held(&stp_policy_subsys.su_mutex);
mutex_unlock(&stp_policy_subsys.su_mutex);
config_item_put(&policy_node->group.cg_item);
}
int __init stp_configfs_init(void)
{
config_group_init(&stp_policy_subsys.su_group);
mutex_init(&stp_policy_subsys.su_mutex);
return configfs_register_subsystem(&stp_policy_subsys);
}
void __exit stp_configfs_exit(void)
{
configfs_unregister_subsystem(&stp_policy_subsys);
}
+117
View File
@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
#ifndef _STM_STM_H_
#define _STM_STM_H_
#include <linux/configfs.h>
struct stp_policy;
struct stp_policy_node;
struct stm_protocol_driver;
int stp_configfs_init(void);
void stp_configfs_exit(void);
void *stp_policy_node_priv(struct stp_policy_node *pn);
struct stp_master {
unsigned int nr_free;
unsigned long chan_map[];
};
struct stm_device {
struct device dev;
struct module *owner;
struct stp_policy *policy;
struct mutex policy_mutex;
int major;
unsigned int sw_nmasters;
struct stm_data *data;
struct mutex link_mutex;
spinlock_t link_lock;
struct list_head link_list;
/* framing protocol in use */
const struct stm_protocol_driver *pdrv;
const struct config_item_type *pdrv_node_type;
/* master allocation */
spinlock_t mc_lock;
struct stp_master *masters[];
};
#define to_stm_device(_d) \
container_of((_d), struct stm_device, dev)
struct stp_policy_node *
stp_policy_node_lookup(struct stm_device *stm, char *s);
void stp_policy_node_put(struct stp_policy_node *policy_node);
void stp_policy_unbind(struct stp_policy *policy);
void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
unsigned int *mstart, unsigned int *mend,
unsigned int *cstart, unsigned int *cend);
const struct config_item_type *
get_policy_node_type(struct configfs_attribute **attrs);
struct stm_output {
spinlock_t lock;
unsigned int master;
unsigned int channel;
unsigned int nr_chans;
void *pdrv_private;
};
struct stm_file {
struct stm_device *stm;
struct stm_output output;
};
struct stm_device *stm_find_device(const char *name);
void stm_put_device(struct stm_device *stm);
struct stm_source_device {
struct device dev;
struct stm_source_data *data;
spinlock_t link_lock;
struct stm_device __rcu *link;
struct list_head link_entry;
/* one output per stm_source device */
struct stm_output output;
};
#define to_stm_source_device(_d) \
container_of((_d), struct stm_source_device, dev)
void *to_pdrv_policy_node(struct config_item *item);
struct stm_protocol_driver {
struct module *owner;
const char *name;
ssize_t (*write)(struct stm_data *data,
struct stm_output *output, unsigned int chan,
const char *buf, size_t count, struct stm_source_data *source);
void (*policy_node_init)(void *arg);
int (*output_open)(void *priv, struct stm_output *output);
void (*output_close)(struct stm_output *output);
ssize_t priv_sz;
struct configfs_attribute **policy_attr;
};
int stm_register_protocol(const struct stm_protocol_driver *pdrv);
void stm_unregister_protocol(const struct stm_protocol_driver *pdrv);
int stm_lookup_protocol(const char *name,
const struct stm_protocol_driver **pdrv,
const struct config_item_type **type);
void stm_put_protocol(const struct stm_protocol_driver *pdrv);
ssize_t stm_data_write(struct stm_data *data, unsigned int m,
unsigned int c, bool ts_first, const void *buf,
size_t count);
#endif /* _STM_STM_H_ */