Skip to content

Commit 394695f

Browse files
crojewsk-intelbroonie
authored andcommitted
ASoC: SOF: Provide probe debugfs support
Define debugfs subdirectory delegated for IPC communication with DSP. Input format: uint,uint,(...) which are later translated into DWORDS sequence and further into instances of struct of interest given the IPC type. For Extractor probes, following have been enabled: - PROBE_POINT_ADD (echo <..> probe_points) - PROBE_POINT_REMOVE (echo <..> probe_points_remove) - PROBE_POINT_INFO (cat probe_points) Signed-off-by: Cezary Rojewski <[email protected]> Acked-by: Pierre-Louis Bossart <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 4c414da commit 394695f

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed

sound/soc/sof/debug.c

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,221 @@
1717
#include "sof-priv.h"
1818
#include "ops.h"
1919

20+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
21+
#include "probe.h"
22+
23+
/**
24+
* strsplit_u32 - Split string into sequence of u32 tokens
25+
* @buf: String to split into tokens.
26+
* @delim: String containing delimiter characters.
27+
* @tkns: Returned u32 sequence pointer.
28+
* @num_tkns: Returned number of tokens obtained.
29+
*/
30+
static int
31+
strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns)
32+
{
33+
char *s;
34+
u32 *data, *tmp;
35+
size_t count = 0;
36+
size_t cap = 32;
37+
int ret = 0;
38+
39+
*tkns = NULL;
40+
*num_tkns = 0;
41+
data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
42+
if (!data)
43+
return -ENOMEM;
44+
45+
while ((s = strsep(buf, delim)) != NULL) {
46+
ret = kstrtouint(s, 0, data + count);
47+
if (ret)
48+
goto exit;
49+
if (++count >= cap) {
50+
cap *= 2;
51+
tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
52+
if (!tmp) {
53+
ret = -ENOMEM;
54+
goto exit;
55+
}
56+
data = tmp;
57+
}
58+
}
59+
60+
if (!count)
61+
goto exit;
62+
*tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
63+
if (*tkns == NULL) {
64+
ret = -ENOMEM;
65+
goto exit;
66+
}
67+
*num_tkns = count;
68+
69+
exit:
70+
kfree(data);
71+
return ret;
72+
}
73+
74+
static int tokenize_input(const char __user *from, size_t count,
75+
loff_t *ppos, u32 **tkns, size_t *num_tkns)
76+
{
77+
char *buf;
78+
int ret;
79+
80+
buf = kmalloc(count + 1, GFP_KERNEL);
81+
if (!buf)
82+
return -ENOMEM;
83+
84+
ret = simple_write_to_buffer(buf, count, ppos, from, count);
85+
if (ret != count) {
86+
ret = ret >= 0 ? -EIO : ret;
87+
goto exit;
88+
}
89+
90+
buf[count] = '\0';
91+
ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns);
92+
exit:
93+
kfree(buf);
94+
return ret;
95+
}
96+
97+
static ssize_t probe_points_read(struct file *file,
98+
char __user *to, size_t count, loff_t *ppos)
99+
{
100+
struct snd_sof_dfsentry *dfse = file->private_data;
101+
struct snd_sof_dev *sdev = dfse->sdev;
102+
struct sof_probe_point_desc *desc;
103+
size_t num_desc, len = 0;
104+
char *buf;
105+
int i, ret;
106+
107+
if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
108+
dev_warn(sdev->dev, "no extractor stream running\n");
109+
return -ENOENT;
110+
}
111+
112+
buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
113+
if (!buf)
114+
return -ENOMEM;
115+
116+
ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc);
117+
if (ret < 0)
118+
goto exit;
119+
120+
for (i = 0; i < num_desc; i++) {
121+
ret = snprintf(buf + len, PAGE_SIZE - len,
122+
"Id: %#010x Purpose: %d Node id: %#x\n",
123+
desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
124+
if (ret < 0)
125+
goto free_desc;
126+
len += ret;
127+
}
128+
129+
ret = simple_read_from_buffer(to, count, ppos, buf, len);
130+
free_desc:
131+
kfree(desc);
132+
exit:
133+
kfree(buf);
134+
return ret;
135+
}
136+
137+
static ssize_t probe_points_write(struct file *file,
138+
const char __user *from, size_t count, loff_t *ppos)
139+
{
140+
struct snd_sof_dfsentry *dfse = file->private_data;
141+
struct snd_sof_dev *sdev = dfse->sdev;
142+
struct sof_probe_point_desc *desc;
143+
size_t num_tkns, bytes;
144+
u32 *tkns;
145+
int ret;
146+
147+
if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
148+
dev_warn(sdev->dev, "no extractor stream running\n");
149+
return -ENOENT;
150+
}
151+
152+
ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
153+
if (ret < 0)
154+
return ret;
155+
bytes = sizeof(*tkns) * num_tkns;
156+
if (!num_tkns || (bytes % sizeof(*desc))) {
157+
ret = -EINVAL;
158+
goto exit;
159+
}
160+
161+
desc = (struct sof_probe_point_desc *)tkns;
162+
ret = sof_ipc_probe_points_add(sdev,
163+
desc, bytes / sizeof(*desc));
164+
if (!ret)
165+
ret = count;
166+
exit:
167+
kfree(tkns);
168+
return ret;
169+
}
170+
171+
static const struct file_operations probe_points_fops = {
172+
.open = simple_open,
173+
.read = probe_points_read,
174+
.write = probe_points_write,
175+
.llseek = default_llseek,
176+
};
177+
178+
static ssize_t probe_points_remove_write(struct file *file,
179+
const char __user *from, size_t count, loff_t *ppos)
180+
{
181+
struct snd_sof_dfsentry *dfse = file->private_data;
182+
struct snd_sof_dev *sdev = dfse->sdev;
183+
size_t num_tkns;
184+
u32 *tkns;
185+
int ret;
186+
187+
if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
188+
dev_warn(sdev->dev, "no extractor stream running\n");
189+
return -ENOENT;
190+
}
191+
192+
ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
193+
if (ret < 0)
194+
return ret;
195+
if (!num_tkns) {
196+
ret = -EINVAL;
197+
goto exit;
198+
}
199+
200+
ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns);
201+
if (!ret)
202+
ret = count;
203+
exit:
204+
kfree(tkns);
205+
return ret;
206+
}
207+
208+
static const struct file_operations probe_points_remove_fops = {
209+
.open = simple_open,
210+
.write = probe_points_remove_write,
211+
.llseek = default_llseek,
212+
};
213+
214+
static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev,
215+
const char *name, mode_t mode,
216+
const struct file_operations *fops)
217+
{
218+
struct snd_sof_dfsentry *dfse;
219+
220+
dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
221+
if (!dfse)
222+
return -ENOMEM;
223+
224+
dfse->type = SOF_DFSENTRY_TYPE_BUF;
225+
dfse->sdev = sdev;
226+
227+
debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops);
228+
/* add to dfsentry list */
229+
list_add(&dfse->list, &sdev->dfsentry_list);
230+
231+
return 0;
232+
}
233+
#endif
234+
20235
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
21236
#define MAX_IPC_FLOOD_DURATION_MS 1000
22237
#define MAX_IPC_FLOOD_COUNT 10000
@@ -436,6 +651,17 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
436651
return err;
437652
}
438653

654+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
655+
err = snd_sof_debugfs_probe_item(sdev, "probe_points",
656+
0644, &probe_points_fops);
657+
if (err < 0)
658+
return err;
659+
err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove",
660+
0200, &probe_points_remove_fops);
661+
if (err < 0)
662+
return err;
663+
#endif
664+
439665
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
440666
/* create read-write ipc_flood_count debugfs entry */
441667
err = snd_sof_debugfs_buf_item(sdev, NULL, 0,

0 commit comments

Comments
 (0)