1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MIPI SyS-T framing protocol for STM devices. 4 * Copyright (c) 2018, Intel Corporation. 5 */ 6 7 #include <linux/configfs.h> 8 #include <linux/module.h> 9 #include <linux/device.h> 10 #include <linux/slab.h> 11 #include <linux/uuid.h> 12 #include <linux/stm.h> 13 #include "stm.h" 14 15 enum sys_t_message_type { 16 MIPI_SYST_TYPE_BUILD = 0, 17 MIPI_SYST_TYPE_SHORT32, 18 MIPI_SYST_TYPE_STRING, 19 MIPI_SYST_TYPE_CATALOG, 20 MIPI_SYST_TYPE_RAW = 6, 21 MIPI_SYST_TYPE_SHORT64, 22 MIPI_SYST_TYPE_CLOCK, 23 MIPI_SYST_TYPE_SBD, 24 }; 25 26 enum sys_t_message_severity { 27 MIPI_SYST_SEVERITY_MAX = 0, 28 MIPI_SYST_SEVERITY_FATAL, 29 MIPI_SYST_SEVERITY_ERROR, 30 MIPI_SYST_SEVERITY_WARNING, 31 MIPI_SYST_SEVERITY_INFO, 32 MIPI_SYST_SEVERITY_USER1, 33 MIPI_SYST_SEVERITY_USER2, 34 MIPI_SYST_SEVERITY_DEBUG, 35 }; 36 37 enum sys_t_message_build_subtype { 38 MIPI_SYST_BUILD_ID_COMPACT32 = 0, 39 MIPI_SYST_BUILD_ID_COMPACT64, 40 MIPI_SYST_BUILD_ID_LONG, 41 }; 42 43 enum sys_t_message_clock_subtype { 44 MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1, 45 }; 46 47 enum sys_t_message_string_subtype { 48 MIPI_SYST_STRING_GENERIC = 1, 49 MIPI_SYST_STRING_FUNCTIONENTER, 50 MIPI_SYST_STRING_FUNCTIONEXIT, 51 MIPI_SYST_STRING_INVALIDPARAM = 5, 52 MIPI_SYST_STRING_ASSERT = 7, 53 MIPI_SYST_STRING_PRINTF_32 = 11, 54 MIPI_SYST_STRING_PRINTF_64 = 12, 55 }; 56 57 /** 58 * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes 59 * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID 60 * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID 61 * 62 * Structured Binary Data messages can send information of arbitrary length, 63 * together with ID's that describe BLOB's content and layout. 64 */ 65 enum sys_t_message_sbd_subtype { 66 MIPI_SYST_SBD_ID32 = 0, 67 MIPI_SYST_SBD_ID64 = 1, 68 }; 69 70 #define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t)) 71 #define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4) 72 #define MIPI_SYST_OPT_LOC BIT(8) 73 #define MIPI_SYST_OPT_LEN BIT(9) 74 #define MIPI_SYST_OPT_CHK BIT(10) 75 #define MIPI_SYST_OPT_TS BIT(11) 76 #define MIPI_SYST_UNIT(u) ((u32)(u) << 12) 77 #define MIPI_SYST_ORIGIN(o) ((u32)(o) << 16) 78 #define MIPI_SYST_OPT_GUID BIT(23) 79 #define MIPI_SYST_SUBTYPE(s) ((u32)(MIPI_SYST_ ## s) << 24) 80 #define MIPI_SYST_UNITLARGE(u) (MIPI_SYST_UNIT(u & 0xf) | \ 81 MIPI_SYST_ORIGIN(u >> 4)) 82 #define MIPI_SYST_TYPES(t, s) (MIPI_SYST_TYPE(t) | \ 83 MIPI_SYST_SUBTYPE(t ## _ ## s)) 84 85 #define DATA_HEADER (MIPI_SYST_TYPES(STRING, GENERIC) | \ 86 MIPI_SYST_SEVERITY(INFO) | \ 87 MIPI_SYST_OPT_GUID) 88 89 #define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \ 90 MIPI_SYST_SEVERITY(MAX)) 91 92 /* 93 * SyS-T and ftrace headers are compatible to an extent that ftrace event ID 94 * and args can be treated as SyS-T SBD message with 64-bit ID and arguments 95 * BLOB right behind the header without modification. Bits [16:63] coming 96 * together with message ID from ftrace aren't used by SBD and must be zeroed. 97 * 98 * 0 15 16 23 24 31 32 39 40 63 99 * ftrace: <event_id> <flags> <preempt> <-pid-> <----> <args> 100 * SBD: <------- msg_id ------------------------------> <BLOB> 101 */ 102 #define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \ 103 MIPI_SYST_SEVERITY(INFO) | \ 104 MIPI_SYST_OPT_GUID) 105 106 struct sys_t_policy_node { 107 uuid_t uuid; 108 bool do_len; 109 unsigned long ts_interval; 110 unsigned long clocksync_interval; 111 }; 112 113 struct sys_t_output { 114 struct sys_t_policy_node node; 115 unsigned long ts_jiffies; 116 unsigned long clocksync_jiffies; 117 }; 118 119 static void sys_t_policy_node_init(void *priv) 120 { 121 struct sys_t_policy_node *pn = priv; 122 123 uuid_gen(&pn->uuid); 124 } 125 126 static int sys_t_output_open(void *priv, struct stm_output *output) 127 { 128 struct sys_t_policy_node *pn = priv; 129 struct sys_t_output *opriv; 130 131 opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC); 132 if (!opriv) 133 return -ENOMEM; 134 135 memcpy(&opriv->node, pn, sizeof(opriv->node)); 136 output->pdrv_private = opriv; 137 138 return 0; 139 } 140 141 static void sys_t_output_close(struct stm_output *output) 142 { 143 kfree(output->pdrv_private); 144 } 145 146 static ssize_t sys_t_policy_uuid_show(struct config_item *item, 147 char *page) 148 { 149 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 150 151 return sprintf(page, "%pU\n", &pn->uuid); 152 } 153 154 static ssize_t 155 sys_t_policy_uuid_store(struct config_item *item, const char *page, 156 size_t count) 157 { 158 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 159 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 160 int ret; 161 162 mutex_lock(mutexp); 163 ret = uuid_parse(page, &pn->uuid); 164 mutex_unlock(mutexp); 165 166 return ret < 0 ? ret : count; 167 } 168 169 CONFIGFS_ATTR(sys_t_policy_, uuid); 170 171 static ssize_t sys_t_policy_do_len_show(struct config_item *item, 172 char *page) 173 { 174 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 175 176 return sprintf(page, "%d\n", pn->do_len); 177 } 178 179 static ssize_t 180 sys_t_policy_do_len_store(struct config_item *item, const char *page, 181 size_t count) 182 { 183 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 184 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 185 int ret; 186 187 mutex_lock(mutexp); 188 ret = kstrtobool(page, &pn->do_len); 189 mutex_unlock(mutexp); 190 191 return ret ? ret : count; 192 } 193 194 CONFIGFS_ATTR(sys_t_policy_, do_len); 195 196 static ssize_t sys_t_policy_ts_interval_show(struct config_item *item, 197 char *page) 198 { 199 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 200 201 return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval)); 202 } 203 204 static ssize_t 205 sys_t_policy_ts_interval_store(struct config_item *item, const char *page, 206 size_t count) 207 { 208 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 209 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 210 unsigned int ms; 211 int ret; 212 213 mutex_lock(mutexp); 214 ret = kstrtouint(page, 10, &ms); 215 mutex_unlock(mutexp); 216 217 if (!ret) { 218 pn->ts_interval = msecs_to_jiffies(ms); 219 return count; 220 } 221 222 return ret; 223 } 224 225 CONFIGFS_ATTR(sys_t_policy_, ts_interval); 226 227 static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item, 228 char *page) 229 { 230 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 231 232 return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval)); 233 } 234 235 static ssize_t 236 sys_t_policy_clocksync_interval_store(struct config_item *item, 237 const char *page, size_t count) 238 { 239 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 240 struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 241 unsigned int ms; 242 int ret; 243 244 mutex_lock(mutexp); 245 ret = kstrtouint(page, 10, &ms); 246 mutex_unlock(mutexp); 247 248 if (!ret) { 249 pn->clocksync_interval = msecs_to_jiffies(ms); 250 return count; 251 } 252 253 return ret; 254 } 255 256 CONFIGFS_ATTR(sys_t_policy_, clocksync_interval); 257 258 static struct configfs_attribute *sys_t_policy_attrs[] = { 259 &sys_t_policy_attr_uuid, 260 &sys_t_policy_attr_do_len, 261 &sys_t_policy_attr_ts_interval, 262 &sys_t_policy_attr_clocksync_interval, 263 NULL, 264 }; 265 266 static inline bool sys_t_need_ts(struct sys_t_output *op) 267 { 268 if (op->node.ts_interval && 269 time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) { 270 op->ts_jiffies = jiffies; 271 272 return true; 273 } 274 275 return false; 276 } 277 278 static bool sys_t_need_clock_sync(struct sys_t_output *op) 279 { 280 if (op->node.clocksync_interval && 281 time_after(jiffies, 282 op->clocksync_jiffies + op->node.clocksync_interval)) { 283 op->clocksync_jiffies = jiffies; 284 285 return true; 286 } 287 288 return false; 289 } 290 291 static ssize_t 292 sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c) 293 { 294 u32 header = CLOCK_SYNC_HEADER; 295 const unsigned char nil = 0; 296 u64 payload[2]; /* Clock value and frequency */ 297 ssize_t sz; 298 299 sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, 300 4, (u8 *)&header); 301 if (sz <= 0) 302 return sz; 303 304 payload[0] = ktime_get_real_ns(); 305 payload[1] = NSEC_PER_SEC; 306 sz = stm_data_write(data, m, c, false, &payload, sizeof(payload)); 307 if (sz <= 0) 308 return sz; 309 310 data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); 311 312 return sizeof(header) + sizeof(payload); 313 } 314 315 static inline u32 sys_t_header(struct stm_source_data *source) 316 { 317 if (source && source->type == STM_FTRACE) 318 return SBD_HEADER; 319 return DATA_HEADER; 320 } 321 322 static ssize_t sys_t_write_data(struct stm_data *data, 323 struct stm_source_data *source, 324 unsigned int master, unsigned int channel, 325 bool ts_first, const void *buf, size_t count) 326 { 327 ssize_t sz; 328 const unsigned char nil = 0; 329 330 /* 331 * Ftrace is zero-copy compatible with SyS-T SBD, but requires 332 * special handling of first 64 bits. Trim and send them separately 333 * to avoid damage on original ftrace buffer. 334 */ 335 if (source && source->type == STM_FTRACE) { 336 u64 compat_ftrace_header; 337 ssize_t header_sz; 338 ssize_t buf_sz; 339 340 if (count < sizeof(compat_ftrace_header)) 341 return -EINVAL; 342 343 /* SBD only makes use of low 16 bits (event ID) from ftrace event */ 344 compat_ftrace_header = *(u64 *)buf & 0xffff; 345 header_sz = stm_data_write(data, master, channel, false, 346 &compat_ftrace_header, 347 sizeof(compat_ftrace_header)); 348 if (header_sz != sizeof(compat_ftrace_header)) 349 return header_sz; 350 351 buf_sz = stm_data_write(data, master, channel, false, 352 buf + header_sz, count - header_sz); 353 if (buf_sz != count - header_sz) 354 return buf_sz; 355 sz = header_sz + buf_sz; 356 } else { 357 sz = stm_data_write(data, master, channel, false, buf, count); 358 } 359 360 if (sz <= 0) 361 return sz; 362 363 data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); 364 365 return sz; 366 } 367 368 static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, 369 unsigned int chan, const char *buf, size_t count, 370 struct stm_source_data *source) 371 { 372 struct sys_t_output *op = output->pdrv_private; 373 unsigned int c = output->channel + chan; 374 unsigned int m = output->master; 375 u32 header = sys_t_header(source); 376 u8 uuid[UUID_SIZE]; 377 ssize_t sz; 378 379 /* We require an existing policy node to proceed */ 380 if (!op) 381 return -EINVAL; 382 383 if (sys_t_need_clock_sync(op)) { 384 sz = sys_t_clock_sync(data, m, c); 385 if (sz <= 0) 386 return sz; 387 } 388 389 if (op->node.do_len) 390 header |= MIPI_SYST_OPT_LEN; 391 if (sys_t_need_ts(op)) 392 header |= MIPI_SYST_OPT_TS; 393 394 /* 395 * STP framing rules for SyS-T frames: 396 * * the first packet of the SyS-T frame is timestamped; 397 * * the last packet is a FLAG. 398 */ 399 /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */ 400 /* HEADER */ 401 sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, 402 4, (u8 *)&header); 403 if (sz <= 0) 404 return sz; 405 406 /* GUID */ 407 export_uuid(uuid, &op->node.uuid); 408 sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid)); 409 if (sz <= 0) 410 return sz; 411 412 /* [LENGTH] */ 413 if (op->node.do_len) { 414 u16 length = count; 415 416 sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2, 417 (u8 *)&length); 418 if (sz <= 0) 419 return sz; 420 } 421 422 /* [TIMESTAMP] */ 423 if (header & MIPI_SYST_OPT_TS) { 424 u64 ts = ktime_get_real_ns(); 425 426 sz = stm_data_write(data, m, c, false, &ts, sizeof(ts)); 427 if (sz <= 0) 428 return sz; 429 } 430 431 /* DATA */ 432 return sys_t_write_data(data, source, m, c, false, buf, count); 433 } 434 435 static const struct stm_protocol_driver sys_t_pdrv = { 436 .owner = THIS_MODULE, 437 .name = "p_sys-t", 438 .priv_sz = sizeof(struct sys_t_policy_node), 439 .write = sys_t_write, 440 .policy_attr = sys_t_policy_attrs, 441 .policy_node_init = sys_t_policy_node_init, 442 .output_open = sys_t_output_open, 443 .output_close = sys_t_output_close, 444 }; 445 446 static int sys_t_stm_init(void) 447 { 448 return stm_register_protocol(&sys_t_pdrv); 449 } 450 451 static void sys_t_stm_exit(void) 452 { 453 stm_unregister_protocol(&sys_t_pdrv); 454 } 455 456 module_init(sys_t_stm_init); 457 module_exit(sys_t_stm_exit); 458 459 MODULE_LICENSE("GPL v2"); 460 MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver"); 461 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 462