|
13 | 13 | #include "sof-priv.h" |
14 | 14 | #include "ops.h" |
15 | 15 |
|
| 16 | +#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 |
| 17 | +#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 |
| 18 | + |
| 19 | +static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, |
| 20 | + struct sof_ipc_trace_filter_elem *elem_list, |
| 21 | + int capacity, int *counter) |
| 22 | +{ |
| 23 | + if (*counter >= capacity) |
| 24 | + return -ENOMEM; |
| 25 | + |
| 26 | + elem_list[*counter].key = key; |
| 27 | + elem_list[*counter].value = value; |
| 28 | + ++*counter; |
| 29 | + |
| 30 | + return 0; |
| 31 | +} |
| 32 | + |
| 33 | +static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, |
| 34 | + struct sof_ipc_trace_filter_elem *elem, |
| 35 | + int capacity, int *counter) |
| 36 | +{ |
| 37 | + int len = strlen(line); |
| 38 | + int cnt = *counter; |
| 39 | + uint32_t uuid_id; |
| 40 | + int log_level; |
| 41 | + int pipe_id; |
| 42 | + int comp_id; |
| 43 | + int read; |
| 44 | + int ret; |
| 45 | + |
| 46 | + /* ignore empty content */ |
| 47 | + if (!line[0]) |
| 48 | + return 0; |
| 49 | + |
| 50 | + ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); |
| 51 | + if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { |
| 52 | + dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line); |
| 53 | + return -EINVAL; |
| 54 | + } |
| 55 | + |
| 56 | + if (uuid_id > 0) { |
| 57 | + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, |
| 58 | + uuid_id, elem, capacity, &cnt); |
| 59 | + if (ret) |
| 60 | + return ret; |
| 61 | + } |
| 62 | + if (pipe_id >= 0) { |
| 63 | + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, |
| 64 | + pipe_id, elem, capacity, &cnt); |
| 65 | + if (ret) |
| 66 | + return ret; |
| 67 | + } |
| 68 | + if (comp_id >= 0) { |
| 69 | + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, |
| 70 | + comp_id, elem, capacity, &cnt); |
| 71 | + if (ret) |
| 72 | + return ret; |
| 73 | + } |
| 74 | + |
| 75 | + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | |
| 76 | + SOF_IPC_TRACE_FILTER_ELEM_FIN, |
| 77 | + log_level, elem, capacity, &cnt); |
| 78 | + if (ret) |
| 79 | + return ret; |
| 80 | + |
| 81 | + /* update counter only when parsing whole entry passed */ |
| 82 | + *counter = cnt; |
| 83 | + |
| 84 | + return len; |
| 85 | +} |
| 86 | + |
| 87 | +static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, |
| 88 | + int *out_elem_cnt, |
| 89 | + struct sof_ipc_trace_filter_elem **out) |
| 90 | +{ |
| 91 | + static const char entry_delimiter[] = ";"; |
| 92 | + char *entry = string; |
| 93 | + int capacity = 0; |
| 94 | + int entry_len; |
| 95 | + int cnt = 0; |
| 96 | + |
| 97 | + /* |
| 98 | + * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY |
| 99 | + * IPC elements, depending on content. Calculate IPC elements capacity |
| 100 | + * for the input string where each element is set. |
| 101 | + */ |
| 102 | + while (entry) { |
| 103 | + capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; |
| 104 | + entry = strchr(entry + 1, entry_delimiter[0]); |
| 105 | + } |
| 106 | + *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); |
| 107 | + if (!*out) |
| 108 | + return -ENOMEM; |
| 109 | + |
| 110 | + /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ |
| 111 | + while ((entry = strsep(&string, entry_delimiter))) { |
| 112 | + entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); |
| 113 | + if (entry_len < 0) { |
| 114 | + dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry, |
| 115 | + entry_len); |
| 116 | + return -EINVAL; |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + *out_elem_cnt = cnt; |
| 121 | + |
| 122 | + return 0; |
| 123 | +} |
| 124 | + |
| 125 | +static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, |
| 126 | + struct sof_ipc_trace_filter_elem *elems) |
| 127 | +{ |
| 128 | + struct sof_ipc_trace_filter *msg; |
| 129 | + struct sof_ipc_reply reply; |
| 130 | + size_t size; |
| 131 | + int ret; |
| 132 | + |
| 133 | + size = struct_size(msg, elems, num_elems); |
| 134 | + if (size > SOF_IPC_MSG_MAX_SIZE) |
| 135 | + return -EINVAL; |
| 136 | + |
| 137 | + msg = kmalloc(size, GFP_KERNEL); |
| 138 | + if (!msg) |
| 139 | + return -ENOMEM; |
| 140 | + |
| 141 | + msg->hdr.size = size; |
| 142 | + msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; |
| 143 | + msg->elem_cnt = num_elems; |
| 144 | + memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); |
| 145 | + |
| 146 | + ret = pm_runtime_get_sync(sdev->dev); |
| 147 | + if (ret < 0 && ret != -EACCES) { |
| 148 | + pm_runtime_put_noidle(sdev->dev); |
| 149 | + dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); |
| 150 | + goto error; |
| 151 | + } |
| 152 | + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, |
| 153 | + &reply, sizeof(reply)); |
| 154 | + pm_runtime_mark_last_busy(sdev->dev); |
| 155 | + pm_runtime_put_autosuspend(sdev->dev); |
| 156 | + |
| 157 | +error: |
| 158 | + kfree(msg); |
| 159 | + return ret ? ret : reply.error; |
| 160 | +} |
| 161 | + |
| 162 | +static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, |
| 163 | + size_t count, loff_t *ppos) |
| 164 | +{ |
| 165 | + struct snd_sof_dfsentry *dfse = file->private_data; |
| 166 | + struct sof_ipc_trace_filter_elem *elems = NULL; |
| 167 | + struct snd_sof_dev *sdev = dfse->sdev; |
| 168 | + loff_t pos = 0; |
| 169 | + int num_elems; |
| 170 | + char *string; |
| 171 | + int ret; |
| 172 | + |
| 173 | + if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { |
| 174 | + dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, |
| 175 | + TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); |
| 176 | + return -EINVAL; |
| 177 | + } |
| 178 | + |
| 179 | + string = kmalloc(count + 1, GFP_KERNEL); |
| 180 | + if (!string) |
| 181 | + return -ENOMEM; |
| 182 | + |
| 183 | + /* assert null termination */ |
| 184 | + string[count] = 0; |
| 185 | + ret = simple_write_to_buffer(string, count, &pos, from, count); |
| 186 | + if (ret < 0) |
| 187 | + goto error; |
| 188 | + |
| 189 | + ret = trace_filter_parse(sdev, string, &num_elems, &elems); |
| 190 | + if (ret < 0) { |
| 191 | + dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret); |
| 192 | + goto error; |
| 193 | + } |
| 194 | + |
| 195 | + if (num_elems) { |
| 196 | + ret = sof_ipc_trace_update_filter(sdev, num_elems, elems); |
| 197 | + if (ret < 0) { |
| 198 | + dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret); |
| 199 | + goto error; |
| 200 | + } |
| 201 | + } |
| 202 | + ret = count; |
| 203 | +error: |
| 204 | + kfree(string); |
| 205 | + kfree(elems); |
| 206 | + return ret; |
| 207 | +} |
| 208 | + |
| 209 | +static const struct file_operations sof_dfs_trace_filter_fops = { |
| 210 | + .open = simple_open, |
| 211 | + .write = sof_dfsentry_trace_filter_write, |
| 212 | + .llseek = default_llseek, |
| 213 | +}; |
| 214 | + |
| 215 | +static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) |
| 216 | +{ |
| 217 | + struct snd_sof_dfsentry *dfse; |
| 218 | + |
| 219 | + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); |
| 220 | + if (!dfse) |
| 221 | + return -ENOMEM; |
| 222 | + |
| 223 | + dfse->sdev = sdev; |
| 224 | + dfse->type = SOF_DFSENTRY_TYPE_BUF; |
| 225 | + |
| 226 | + debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, |
| 227 | + &sof_dfs_trace_filter_fops); |
| 228 | + /* add to dfsentry list */ |
| 229 | + list_add(&dfse->list, &sdev->dfsentry_list); |
| 230 | + |
| 231 | + return 0; |
| 232 | +} |
| 233 | + |
16 | 234 | static size_t sof_trace_avail(struct snd_sof_dev *sdev, |
17 | 235 | loff_t pos, size_t buffer_size) |
18 | 236 | { |
@@ -135,10 +353,15 @@ static const struct file_operations sof_dfs_trace_fops = { |
135 | 353 | static int trace_debugfs_create(struct snd_sof_dev *sdev) |
136 | 354 | { |
137 | 355 | struct snd_sof_dfsentry *dfse; |
| 356 | + int ret; |
138 | 357 |
|
139 | 358 | if (!sdev) |
140 | 359 | return -EINVAL; |
141 | 360 |
|
| 361 | + ret = trace_debugfs_filter_create(sdev); |
| 362 | + if (ret < 0) |
| 363 | + dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret); |
| 364 | + |
142 | 365 | dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); |
143 | 366 | if (!dfse) |
144 | 367 | return -ENOMEM; |
|
0 commit comments