1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
3 // This file is provided under a dual BSD/GPLv2 license. When using or
4 // redistributing this file, you may do so under either license.
6 // Copyright(c) 2018 Intel Corporation. All rights reserved.
8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
11 #include <linux/debugfs.h>
12 #include <linux/sched/signal.h>
16 #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
17 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
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
)
23 if (*counter
>= capacity
)
26 elem_list
[*counter
].key
= key
;
27 elem_list
[*counter
].value
= value
;
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
)
37 int len
= strlen(line
);
46 /* ignore empty content */
47 ret
= sscanf(line
, " %n", &read
);
48 if (!ret
&& read
== len
)
51 ret
= sscanf(line
, " %d %x %d %d %n", &log_level
, &uuid_id
, &pipe_id
, &comp_id
, &read
);
52 if (ret
!= TRACE_FILTER_ELEMENTS_PER_ENTRY
|| read
!= len
) {
53 dev_err(sdev
->dev
, "error: invalid trace filter entry '%s'\n", line
);
58 ret
= trace_filter_append_elem(sdev
, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID
,
59 uuid_id
, elem
, capacity
, &cnt
);
64 ret
= trace_filter_append_elem(sdev
, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE
,
65 pipe_id
, elem
, capacity
, &cnt
);
70 ret
= trace_filter_append_elem(sdev
, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP
,
71 comp_id
, elem
, capacity
, &cnt
);
76 ret
= trace_filter_append_elem(sdev
, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL
|
77 SOF_IPC_TRACE_FILTER_ELEM_FIN
,
78 log_level
, elem
, capacity
, &cnt
);
82 /* update counter only when parsing whole entry passed */
88 static int trace_filter_parse(struct snd_sof_dev
*sdev
, char *string
,
90 struct sof_ipc_trace_filter_elem
**out
)
92 static const char entry_delimiter
[] = ";";
99 * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
100 * IPC elements, depending on content. Calculate IPC elements capacity
101 * for the input string where each element is set.
104 capacity
+= TRACE_FILTER_ELEMENTS_PER_ENTRY
;
105 entry
= strchr(entry
+ 1, entry_delimiter
[0]);
107 *out
= kmalloc(capacity
* sizeof(**out
), GFP_KERNEL
);
111 /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
112 while ((entry
= strsep(&string
, entry_delimiter
))) {
113 entry_len
= trace_filter_parse_entry(sdev
, entry
, *out
, capacity
, &cnt
);
115 dev_err(sdev
->dev
, "error: %s failed for '%s', %d\n", __func__
, entry
,
126 static int sof_ipc_trace_update_filter(struct snd_sof_dev
*sdev
, int num_elems
,
127 struct sof_ipc_trace_filter_elem
*elems
)
129 struct sof_ipc_trace_filter
*msg
;
130 struct sof_ipc_reply reply
;
134 size
= struct_size(msg
, elems
, num_elems
);
135 if (size
> SOF_IPC_MSG_MAX_SIZE
)
138 msg
= kmalloc(size
, GFP_KERNEL
);
142 msg
->hdr
.size
= size
;
143 msg
->hdr
.cmd
= SOF_IPC_GLB_TRACE_MSG
| SOF_IPC_TRACE_FILTER_UPDATE
;
144 msg
->elem_cnt
= num_elems
;
145 memcpy(&msg
->elems
[0], elems
, num_elems
* sizeof(*elems
));
147 ret
= pm_runtime_get_sync(sdev
->dev
);
148 if (ret
< 0 && ret
!= -EACCES
) {
149 pm_runtime_put_noidle(sdev
->dev
);
150 dev_err(sdev
->dev
, "error: enabling device failed: %d\n", ret
);
153 ret
= sof_ipc_tx_message(sdev
->ipc
, msg
->hdr
.cmd
, msg
, msg
->hdr
.size
,
154 &reply
, sizeof(reply
));
155 pm_runtime_mark_last_busy(sdev
->dev
);
156 pm_runtime_put_autosuspend(sdev
->dev
);
160 return ret
? ret
: reply
.error
;
163 static ssize_t
sof_dfsentry_trace_filter_write(struct file
*file
, const char __user
*from
,
164 size_t count
, loff_t
*ppos
)
166 struct snd_sof_dfsentry
*dfse
= file
->private_data
;
167 struct sof_ipc_trace_filter_elem
*elems
= NULL
;
168 struct snd_sof_dev
*sdev
= dfse
->sdev
;
174 if (count
> TRACE_FILTER_MAX_CONFIG_STRING_LENGTH
) {
175 dev_err(sdev
->dev
, "%s too long input, %zu > %d\n", __func__
, count
,
176 TRACE_FILTER_MAX_CONFIG_STRING_LENGTH
);
180 string
= kmalloc(count
+ 1, GFP_KERNEL
);
184 /* assert null termination */
186 ret
= simple_write_to_buffer(string
, count
, &pos
, from
, count
);
190 ret
= trace_filter_parse(sdev
, string
, &num_elems
, &elems
);
192 dev_err(sdev
->dev
, "error: fail in trace_filter_parse, %d\n", ret
);
197 ret
= sof_ipc_trace_update_filter(sdev
, num_elems
, elems
);
199 dev_err(sdev
->dev
, "error: fail in sof_ipc_trace_update_filter %d\n", ret
);
210 static const struct file_operations sof_dfs_trace_filter_fops
= {
212 .write
= sof_dfsentry_trace_filter_write
,
213 .llseek
= default_llseek
,
216 static int trace_debugfs_filter_create(struct snd_sof_dev
*sdev
)
218 struct snd_sof_dfsentry
*dfse
;
220 dfse
= devm_kzalloc(sdev
->dev
, sizeof(*dfse
), GFP_KERNEL
);
225 dfse
->type
= SOF_DFSENTRY_TYPE_BUF
;
227 debugfs_create_file("filter", 0200, sdev
->debugfs_root
, dfse
,
228 &sof_dfs_trace_filter_fops
);
229 /* add to dfsentry list */
230 list_add(&dfse
->list
, &sdev
->dfsentry_list
);
235 static size_t sof_trace_avail(struct snd_sof_dev
*sdev
,
236 loff_t pos
, size_t buffer_size
)
238 loff_t host_offset
= READ_ONCE(sdev
->host_offset
);
241 * If host offset is less than local pos, it means write pointer of
242 * host DMA buffer has been wrapped. We should output the trace data
243 * at the end of host DMA buffer at first.
245 if (host_offset
< pos
)
246 return buffer_size
- pos
;
248 /* If there is available trace data now, it is unnecessary to wait. */
249 if (host_offset
> pos
)
250 return host_offset
- pos
;
255 static size_t sof_wait_trace_avail(struct snd_sof_dev
*sdev
,
256 loff_t pos
, size_t buffer_size
)
258 wait_queue_entry_t wait
;
259 size_t ret
= sof_trace_avail(sdev
, pos
, buffer_size
);
261 /* data immediately available */
265 if (!sdev
->dtrace_is_enabled
&& sdev
->dtrace_draining
) {
267 * tracing has ended and all traces have been
268 * read by client, return EOF
270 sdev
->dtrace_draining
= false;
274 /* wait for available trace data from FW */
275 init_waitqueue_entry(&wait
, current
);
276 set_current_state(TASK_INTERRUPTIBLE
);
277 add_wait_queue(&sdev
->trace_sleep
, &wait
);
279 if (!signal_pending(current
)) {
280 /* set timeout to max value, no error code */
281 schedule_timeout(MAX_SCHEDULE_TIMEOUT
);
283 remove_wait_queue(&sdev
->trace_sleep
, &wait
);
285 return sof_trace_avail(sdev
, pos
, buffer_size
);
288 static ssize_t
sof_dfsentry_trace_read(struct file
*file
, char __user
*buffer
,
289 size_t count
, loff_t
*ppos
)
291 struct snd_sof_dfsentry
*dfse
= file
->private_data
;
292 struct snd_sof_dev
*sdev
= dfse
->sdev
;
295 size_t avail
, buffer_size
= dfse
->size
;
298 /* make sure we know about any failures on the DSP side */
299 sdev
->dtrace_error
= false;
301 /* check pos and count */
307 /* check for buffer wrap and count overflow */
309 lpos
= do_div(lpos_64
, buffer_size
);
311 if (count
> buffer_size
- lpos
) /* min() not used to avoid sparse warnings */
312 count
= buffer_size
- lpos
;
314 /* get available count based on current host offset */
315 avail
= sof_wait_trace_avail(sdev
, lpos
, buffer_size
);
316 if (sdev
->dtrace_error
) {
317 dev_err(sdev
->dev
, "error: trace IO error\n");
321 /* make sure count is <= avail */
322 count
= avail
> count
? count
: avail
;
324 /* copy available trace data to debugfs */
325 rem
= copy_to_user(buffer
, ((u8
*)(dfse
->buf
) + lpos
), count
);
331 /* move debugfs reading position */
335 static int sof_dfsentry_trace_release(struct inode
*inode
, struct file
*file
)
337 struct snd_sof_dfsentry
*dfse
= inode
->i_private
;
338 struct snd_sof_dev
*sdev
= dfse
->sdev
;
340 /* avoid duplicate traces at next open */
341 if (!sdev
->dtrace_is_enabled
)
342 sdev
->host_offset
= 0;
347 static const struct file_operations sof_dfs_trace_fops
= {
349 .read
= sof_dfsentry_trace_read
,
350 .llseek
= default_llseek
,
351 .release
= sof_dfsentry_trace_release
,
354 static int trace_debugfs_create(struct snd_sof_dev
*sdev
)
356 struct snd_sof_dfsentry
*dfse
;
362 ret
= trace_debugfs_filter_create(sdev
);
364 dev_err(sdev
->dev
, "error: fail in %s, %d", __func__
, ret
);
366 dfse
= devm_kzalloc(sdev
->dev
, sizeof(*dfse
), GFP_KERNEL
);
370 dfse
->type
= SOF_DFSENTRY_TYPE_BUF
;
371 dfse
->buf
= sdev
->dmatb
.area
;
372 dfse
->size
= sdev
->dmatb
.bytes
;
375 debugfs_create_file("trace", 0444, sdev
->debugfs_root
, dfse
,
376 &sof_dfs_trace_fops
);
381 int snd_sof_init_trace_ipc(struct snd_sof_dev
*sdev
)
383 struct sof_ipc_fw_ready
*ready
= &sdev
->fw_ready
;
384 struct sof_ipc_fw_version
*v
= &ready
->version
;
385 struct sof_ipc_dma_trace_params_ext params
;
386 struct sof_ipc_reply ipc_reply
;
389 if (!sdev
->dtrace_is_supported
)
392 if (sdev
->dtrace_is_enabled
|| !sdev
->dma_trace_pages
)
395 /* set IPC parameters */
396 params
.hdr
.cmd
= SOF_IPC_GLB_TRACE_MSG
;
397 /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
398 if (v
->abi_version
>= SOF_ABI_VER(3, 7, 0)) {
399 params
.hdr
.size
= sizeof(struct sof_ipc_dma_trace_params_ext
);
400 params
.hdr
.cmd
|= SOF_IPC_TRACE_DMA_PARAMS_EXT
;
401 params
.timestamp_ns
= ktime_get(); /* in nanosecond */
403 params
.hdr
.size
= sizeof(struct sof_ipc_dma_trace_params
);
404 params
.hdr
.cmd
|= SOF_IPC_TRACE_DMA_PARAMS
;
406 params
.buffer
.phy_addr
= sdev
->dmatp
.addr
;
407 params
.buffer
.size
= sdev
->dmatb
.bytes
;
408 params
.buffer
.pages
= sdev
->dma_trace_pages
;
409 params
.stream_tag
= 0;
411 sdev
->host_offset
= 0;
412 sdev
->dtrace_draining
= false;
414 ret
= snd_sof_dma_trace_init(sdev
, ¶ms
.stream_tag
);
417 "error: fail in snd_sof_dma_trace_init %d\n", ret
);
420 dev_dbg(sdev
->dev
, "stream_tag: %d\n", params
.stream_tag
);
422 /* send IPC to the DSP */
423 ret
= sof_ipc_tx_message(sdev
->ipc
,
424 params
.hdr
.cmd
, ¶ms
, sizeof(params
),
425 &ipc_reply
, sizeof(ipc_reply
));
428 "error: can't set params for DMA for trace %d\n", ret
);
432 ret
= snd_sof_dma_trace_trigger(sdev
, SNDRV_PCM_TRIGGER_START
);
435 "error: snd_sof_dma_trace_trigger: start: %d\n", ret
);
439 sdev
->dtrace_is_enabled
= true;
444 snd_sof_dma_trace_release(sdev
);
448 int snd_sof_init_trace(struct snd_sof_dev
*sdev
)
452 if (!sdev
->dtrace_is_supported
)
455 /* set false before start initialization */
456 sdev
->dtrace_is_enabled
= false;
458 /* allocate trace page table buffer */
459 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV
, sdev
->dev
,
460 PAGE_SIZE
, &sdev
->dmatp
);
463 "error: can't alloc page table for trace %d\n", ret
);
467 /* allocate trace data buffer */
468 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG
, sdev
->dev
,
469 DMA_BUF_SIZE_FOR_TRACE
, &sdev
->dmatb
);
472 "error: can't alloc buffer for trace %d\n", ret
);
476 /* create compressed page table for audio firmware */
477 ret
= snd_sof_create_page_table(sdev
->dev
, &sdev
->dmatb
,
478 sdev
->dmatp
.area
, sdev
->dmatb
.bytes
);
482 sdev
->dma_trace_pages
= ret
;
483 dev_dbg(sdev
->dev
, "dma_trace_pages: %d\n", sdev
->dma_trace_pages
);
485 if (sdev
->first_boot
) {
486 ret
= trace_debugfs_create(sdev
);
491 init_waitqueue_head(&sdev
->trace_sleep
);
493 ret
= snd_sof_init_trace_ipc(sdev
);
499 sdev
->dma_trace_pages
= 0;
500 snd_dma_free_pages(&sdev
->dmatb
);
502 snd_dma_free_pages(&sdev
->dmatp
);
505 EXPORT_SYMBOL(snd_sof_init_trace
);
507 int snd_sof_trace_update_pos(struct snd_sof_dev
*sdev
,
508 struct sof_ipc_dma_trace_posn
*posn
)
510 if (!sdev
->dtrace_is_supported
)
513 if (sdev
->dtrace_is_enabled
&& sdev
->host_offset
!= posn
->host_offset
) {
514 sdev
->host_offset
= posn
->host_offset
;
515 wake_up(&sdev
->trace_sleep
);
518 if (posn
->overflow
!= 0)
520 "error: DSP trace buffer overflow %u bytes. Total messages %d\n",
521 posn
->overflow
, posn
->messages
);
526 /* an error has occurred within the DSP that prevents further trace */
527 void snd_sof_trace_notify_for_error(struct snd_sof_dev
*sdev
)
529 if (!sdev
->dtrace_is_supported
)
532 if (sdev
->dtrace_is_enabled
) {
533 dev_err(sdev
->dev
, "error: waking up any trace sleepers\n");
534 sdev
->dtrace_error
= true;
535 wake_up(&sdev
->trace_sleep
);
538 EXPORT_SYMBOL(snd_sof_trace_notify_for_error
);
540 void snd_sof_release_trace(struct snd_sof_dev
*sdev
)
544 if (!sdev
->dtrace_is_supported
|| !sdev
->dtrace_is_enabled
)
547 ret
= snd_sof_dma_trace_trigger(sdev
, SNDRV_PCM_TRIGGER_STOP
);
550 "error: snd_sof_dma_trace_trigger: stop: %d\n", ret
);
552 ret
= snd_sof_dma_trace_release(sdev
);
555 "error: fail in snd_sof_dma_trace_release %d\n", ret
);
557 sdev
->dtrace_is_enabled
= false;
558 sdev
->dtrace_draining
= true;
559 wake_up(&sdev
->trace_sleep
);
561 EXPORT_SYMBOL(snd_sof_release_trace
);
563 void snd_sof_free_trace(struct snd_sof_dev
*sdev
)
565 if (!sdev
->dtrace_is_supported
)
568 snd_sof_release_trace(sdev
);
570 if (sdev
->dma_trace_pages
) {
571 snd_dma_free_pages(&sdev
->dmatb
);
572 snd_dma_free_pages(&sdev
->dmatp
);
573 sdev
->dma_trace_pages
= 0;
576 EXPORT_SYMBOL(snd_sof_free_trace
);