1 /* SPDX-License-Identifier: BSD-3-Clause
2 *
3 * Copyright(c) 2020 Intel Corporation. All rights reserved.
4 *
5 * Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
6 */
7
8 #include <ipc/trace.h>
9 #include <smex/ldc.h>
10 #include <sof/lib/uuid.h>
11 #include <sof/list.h>
12 #include <user/trace.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include "convert.h"
19 #include "filter.h"
20 #include "misc.h"
21
22 #define COMPONENTS_SEPARATOR ','
23 #define COMPONENT_NAME_SCAN_STRING_LENGTH 32
24
25 /** map between log level given by user and enum value */
26 static const struct {
27 const char name[16];
28 int32_t log_level;
29 } log_level_dict[] = {
30 {"verbose", LOG_LEVEL_VERBOSE},
31 {"debug", LOG_LEVEL_DEBUG},
32 {"info", LOG_LEVEL_INFO},
33 {"warning", LOG_LEVEL_WARNING},
34 {"error", LOG_LEVEL_ERROR},
35 {"critical", LOG_LEVEL_CRITICAL},
36 {"v", LOG_LEVEL_VERBOSE},
37 {"d", LOG_LEVEL_DEBUG},
38 {"i", LOG_LEVEL_INFO},
39 {"w", LOG_LEVEL_WARNING},
40 {"e", LOG_LEVEL_ERROR},
41 {"c", LOG_LEVEL_CRITICAL},
42 };
43
44 struct filter_element {
45 struct list_item list;
46 int32_t uuid_id; /**< type id, or -1 when not important */
47 int32_t comp_id; /**< component id or -1 when not important */
48 int32_t pipe_id; /**< pipeline id or -1 when not important */
49 int32_t log_level; /**< new log level value */
50 };
51
52 /**
53 * Search for uuid entry with given component name in given uids dictionary
54 * @param name of uuid entry
55 * @return pointer to sof_uuid_entry with given name
56 */
get_uuid_by_name(const char * name)57 static struct sof_uuid_entry *get_uuid_by_name(const char *name)
58 {
59 const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
60 uintptr_t beg = (uintptr_t)uids_dict + uids_dict->data_offset;
61 uintptr_t end = beg + uids_dict->data_length;
62 struct sof_uuid_entry *ptr = (struct sof_uuid_entry *)beg;
63
64 while ((uintptr_t)ptr < end) {
65 if (strcmp(name, ptr->name) == 0)
66 return ptr;
67 ++ptr;
68 }
69 return NULL;
70 }
71
72 /**
73 * Parse log level name (from log_level_dict) to enum value.
74 * Take care about possible whitespace at the begin.
75 * @param value_start pointer to the begin of range to search
76 * @return enum value for given log level, or -1 for invalid value
77 */
filter_parse_log_level(const char * value_start)78 static int filter_parse_log_level(const char *value_start)
79 {
80 int i;
81
82 for (i = 0; i < ARRAY_SIZE(log_level_dict); ++i) {
83 if (!strcmp(log_level_dict[i].name, value_start))
84 return log_level_dict[i].log_level;
85 }
86 return -1;
87 }
88
filter_parse_component_name(char * input_str,struct filter_element * out)89 static char *filter_parse_component_name(char *input_str, struct filter_element *out)
90 {
91 static char scan_format_string[COMPONENT_NAME_SCAN_STRING_LENGTH] = "";
92 char comp_name[UUID_NAME_MAX_LEN];
93 struct sof_uuid_entry *uuid_entry;
94 int ret;
95
96 /* if component name is not specified, stay with default out->uuid_id value */
97 if (input_str[0] == '*')
98 return &input_str[1];
99
100 /*
101 * Take care about buffer overflows when dealing with input from
102 * user, so scan no more than UUID_NAME_MAX_LEN bytes to
103 * `comp_name` variable. Only once initialise scan_format_string.
104 */
105 if (strlen(scan_format_string) == 0) {
106 ret = snprintf(scan_format_string, sizeof(scan_format_string),
107 "%%%d[^0-9* ]s", UUID_NAME_MAX_LEN);
108 if (ret <= 0)
109 return NULL;
110 }
111 ret = sscanf(input_str, scan_format_string, comp_name);
112 if (ret <= 0)
113 return NULL;
114
115 /* find component uuid key */
116 uuid_entry = get_uuid_by_name(comp_name);
117 if (!uuid_entry) {
118 log_err("unknown component name `%s`\n", comp_name);
119 return NULL;
120 }
121 out->uuid_id = get_uuid_key(uuid_entry);
122 return strstr(input_str, comp_name) + strlen(comp_name);
123 }
124
125 /**
126 * Parse component definition from input_str.
127 *
128 * Possible input_str formats:
129 * `name pipe_id.comp_id`
130 * `name pipe_id.*`
131 * `name *`
132 * `name`
133 * `* pipe_id.comp_id`
134 * `* pipe_id.*`
135 * `*`
136 * Whitespace is possible at the begin, end and after `name`.
137 * `name` must refer to values from given UUID dictionary,
138 * (so name comes from DECLARE_SOF_UUID macro usage)
139
140 * @param input_str formatted component definition
141 * @param out element where component definition should be saved
142 */
filter_parse_component(char * input_str,struct filter_element * out)143 static int filter_parse_component(char *input_str, struct filter_element *out)
144 {
145 char *instance_info;
146 int ret;
147
148 /* trim whitespaces, to easily check first and last char */
149 input_str = trim(input_str);
150
151 /* assign default values */
152 out->uuid_id = 0;
153 out->pipe_id = -1;
154 out->comp_id = -1;
155
156 /* parse component name and store pointer after component name, pointer to instance info */
157 instance_info = filter_parse_component_name(input_str, out);
158 if (!instance_info) {
159 log_err("component name parsing `%s`\n", input_str);
160 return -EINVAL;
161 }
162
163 /* if instance is not specified then stop parsing */
164 instance_info = ltrim(instance_info);
165 if (instance_info[0] == '\0' ||
166 (instance_info[0] == '*' && instance_info[1] == '\0')) {
167 return 0;
168 }
169
170 /* now parse last part: `number.x` where x is a number or `*` */
171 ret = sscanf(instance_info, "%d.%d", &out->pipe_id, &out->comp_id);
172 if (ret == 2)
173 return 0;
174 else if (ret != 1)
175 return -EINVAL;
176
177 /* pipeline id parsed but component id is not a number */
178 if (instance_info[strlen(instance_info) - 1] == '*')
179 return 0;
180 log_err("Use * to specify each component on particular pipeline\n");
181 return -EINVAL;
182 }
183
184 /**
185 * Convert argument from -F flag to sof_ipc_dma_trace_filter_elem struct values.
186 *
187 * Possible log_level - see filter_parse_log_level() documentation
188 * Possible component - list of components separated by `COMPONENTS_SEPARATOR`,
189 * for single component definition description look at
190 * filter_parse_component() documentation
191 *
192 * Examples:
193 * `debug="pipe1"` - set debug log level for components from pipeline1
194 * `d="pipe1, dai2.3"` - as above, but also for dai2.3
195 * `error="FIR*"` - for each FIR component set log level to error
196 *
197 * @param input_str log level settings in format `log_level=component`
198 * @param out_list output list with filter_element elements
199 */
filter_parse_entry(char * input_str,struct list_item * out_list)200 static int filter_parse_entry(char *input_str, struct list_item *out_list)
201 {
202 struct filter_element *filter;
203 char *comp_fmt_end;
204 int32_t log_level;
205 char *comp_fmt;
206 int ret;
207
208 /*
209 * split string on '=' char, left part describes log level,
210 * the right one is for component description.
211 */
212 comp_fmt = strchr(input_str, '=');
213 if (!comp_fmt) {
214 log_err("unable to find `=` in `%s`\n", input_str);
215 return -EINVAL;
216 }
217 *comp_fmt = 0;
218 ++comp_fmt;
219
220 /* find correct log level in given conf string - string before `=` */
221 log_level = filter_parse_log_level(input_str);
222 if (log_level < 0) {
223 log_err("unable to parse log level from `%s`\n", input_str);
224 return log_level;
225 }
226
227 /*
228 * now parse list of components name and optional instance identifier,
229 * split string on `COMPONENTS_SEPARATOR`
230 */
231 while (comp_fmt) {
232 filter = malloc(sizeof(struct filter_element));
233 if (!filter) {
234 log_err("unable to malloc memory\n");
235 return -ENOMEM;
236 }
237
238 comp_fmt_end = strchr(comp_fmt, COMPONENTS_SEPARATOR);
239 if (comp_fmt_end)
240 *comp_fmt_end = '\0';
241 ret = filter_parse_component(comp_fmt, filter);
242 if (ret < 0) {
243 log_err("unable to parse component from `%s`\n", comp_fmt);
244 free(filter);
245 return ret;
246 }
247 filter->log_level = log_level;
248
249 list_item_append(&filter->list, out_list);
250 comp_fmt = comp_fmt_end ? comp_fmt_end + 1 : 0;
251 }
252
253 return 0;
254 }
255
256 /**
257 * Parse `input_str` content and send it to FW via debugFS.
258 *
259 * `input_str` contain single filter definition element per line.
260 * Each line is parsed by `filter_parse_entry`, and saved in list.
261 * List of `sof_ipc_dma_trace_filter_elem` is writend to debugFS,
262 * and then send as IPC to FW (this action is implemented in driver).
263 * Each line in debugFS represents single IPC message.
264 */
filter_update_firmware(void)265 int filter_update_firmware(void)
266 {
267 char *input_str = global_config->filter_config;
268 struct filter_element *filter;
269 struct list_item filter_list;
270 struct list_item *list_elem;
271 struct list_item *list_temp;
272 char *line_end;
273 FILE *out_fd = NULL;
274 int ret = 0;
275
276 list_init(&filter_list);
277
278 /* parse `input_str` line by line */
279 line_end = strchr(input_str, '\n');
280 while (line_end) {
281 line_end[0] = '\0';
282 ret = filter_parse_entry(input_str, &filter_list);
283 if (ret < 0)
284 goto err;
285 input_str = line_end + 1;
286 line_end = strchr(input_str, '\n');
287 }
288
289 /* write output to debugFS */
290 out_fd = fopen(FILTER_KERNEL_PATH, "w");
291 if (!out_fd) {
292 log_err("Unable to open out file '%s'\n", FILTER_KERNEL_PATH);
293 ret = -errno;
294 goto err;
295 }
296
297 list_for_item(list_elem, &filter_list) {
298 filter = container_of(list_elem, struct filter_element, list);
299 fprintf(out_fd, "%d %X %d %d;", filter->log_level,
300 filter->uuid_id, filter->pipe_id, filter->comp_id);
301 }
302 fprintf(out_fd, "\n");
303
304 err:
305 if (out_fd)
306 fclose(out_fd);
307
308 /* free each component from parsed element list */
309 list_for_item_safe(list_elem, list_temp, &filter_list) {
310 filter = container_of(list_elem, struct filter_element, list);
311 free(filter);
312 }
313
314 return ret;
315 }
316