Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * Information interface for ALSA driver
4 : : * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
5 : : */
6 : :
7 : : #include <linux/init.h>
8 : : #include <linux/time.h>
9 : : #include <linux/mm.h>
10 : : #include <linux/slab.h>
11 : : #include <linux/string.h>
12 : : #include <linux/module.h>
13 : : #include <sound/core.h>
14 : : #include <sound/minors.h>
15 : : #include <sound/info.h>
16 : : #include <linux/utsname.h>
17 : : #include <linux/proc_fs.h>
18 : : #include <linux/mutex.h>
19 : : #include <stdarg.h>
20 : :
21 : 0 : int snd_info_check_reserved_words(const char *str)
22 : : {
23 : 0 : static const char * const reserved[] =
24 : : {
25 : : "version",
26 : : "meminfo",
27 : : "memdebug",
28 : : "detect",
29 : : "devices",
30 : : "oss",
31 : : "cards",
32 : : "timers",
33 : : "synth",
34 : : "pcm",
35 : : "seq",
36 : : NULL
37 : : };
38 : 0 : const char * const *xstr = reserved;
39 : :
40 [ # # ]: 0 : while (*xstr) {
41 [ # # ]: 0 : if (!strcmp(*xstr, str))
42 : : return 0;
43 : 0 : xstr++;
44 : : }
45 [ # # ]: 0 : if (!strncmp(str, "card", 4))
46 : 0 : return 0;
47 : : return 1;
48 : : }
49 : :
50 : : static DEFINE_MUTEX(info_mutex);
51 : :
52 : : struct snd_info_private_data {
53 : : struct snd_info_buffer *rbuffer;
54 : : struct snd_info_buffer *wbuffer;
55 : : struct snd_info_entry *entry;
56 : : void *file_private_data;
57 : : };
58 : :
59 : : static int snd_info_version_init(void);
60 : : static void snd_info_disconnect(struct snd_info_entry *entry);
61 : :
62 : : /*
63 : :
64 : : */
65 : :
66 : : static struct snd_info_entry *snd_proc_root;
67 : : struct snd_info_entry *snd_seq_root;
68 : : EXPORT_SYMBOL(snd_seq_root);
69 : :
70 : : #ifdef CONFIG_SND_OSSEMUL
71 : : struct snd_info_entry *snd_oss_root;
72 : : #endif
73 : :
74 : 0 : static int alloc_info_private(struct snd_info_entry *entry,
75 : : struct snd_info_private_data **ret)
76 : : {
77 : 0 : struct snd_info_private_data *data;
78 : :
79 [ # # # # ]: 0 : if (!entry || !entry->p)
80 : : return -ENODEV;
81 [ # # ]: 0 : if (!try_module_get(entry->module))
82 : : return -EFAULT;
83 : 0 : data = kzalloc(sizeof(*data), GFP_KERNEL);
84 [ # # ]: 0 : if (!data) {
85 : 0 : module_put(entry->module);
86 : 0 : return -ENOMEM;
87 : : }
88 : 0 : data->entry = entry;
89 : 0 : *ret = data;
90 : 0 : return 0;
91 : : }
92 : :
93 : 0 : static bool valid_pos(loff_t pos, size_t count)
94 : : {
95 : 0 : if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
96 : : return false;
97 [ # # # # : 0 : if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
# # ]
98 : : return false;
99 : : return true;
100 : : }
101 : :
102 : : /*
103 : : * file ops for binary proc files
104 : : */
105 : 0 : static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
106 : : {
107 : 0 : struct snd_info_private_data *data;
108 : 0 : struct snd_info_entry *entry;
109 : 0 : loff_t ret = -EINVAL, size;
110 : :
111 : 0 : data = file->private_data;
112 : 0 : entry = data->entry;
113 : 0 : mutex_lock(&entry->access);
114 [ # # ]: 0 : if (entry->c.ops->llseek) {
115 : 0 : offset = entry->c.ops->llseek(entry,
116 : : data->file_private_data,
117 : : file, offset, orig);
118 : 0 : goto out;
119 : : }
120 : :
121 : 0 : size = entry->size;
122 [ # # # # ]: 0 : switch (orig) {
123 : : case SEEK_SET:
124 : : break;
125 : 0 : case SEEK_CUR:
126 : 0 : offset += file->f_pos;
127 : 0 : break;
128 : 0 : case SEEK_END:
129 [ # # ]: 0 : if (!size)
130 : 0 : goto out;
131 : 0 : offset += size;
132 : 0 : break;
133 : 0 : default:
134 : 0 : goto out;
135 : : }
136 [ # # ]: 0 : if (offset < 0)
137 : 0 : goto out;
138 [ # # ]: 0 : if (size && offset > size)
139 : 0 : offset = size;
140 : 0 : file->f_pos = offset;
141 : 0 : ret = offset;
142 : 0 : out:
143 : 0 : mutex_unlock(&entry->access);
144 : 0 : return ret;
145 : : }
146 : :
147 : 0 : static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
148 : : size_t count, loff_t * offset)
149 : : {
150 : 0 : struct snd_info_private_data *data = file->private_data;
151 : 0 : struct snd_info_entry *entry = data->entry;
152 : 0 : size_t size;
153 : 0 : loff_t pos;
154 : :
155 : 0 : pos = *offset;
156 [ # # ]: 0 : if (!valid_pos(pos, count))
157 : : return -EIO;
158 [ # # ]: 0 : if (pos >= entry->size)
159 : : return 0;
160 : 0 : size = entry->size - pos;
161 : 0 : size = min(count, size);
162 : 0 : size = entry->c.ops->read(entry, data->file_private_data,
163 : : file, buffer, size, pos);
164 [ # # ]: 0 : if ((ssize_t) size > 0)
165 : 0 : *offset = pos + size;
166 : : return size;
167 : : }
168 : :
169 : 0 : static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
170 : : size_t count, loff_t * offset)
171 : : {
172 : 0 : struct snd_info_private_data *data = file->private_data;
173 : 0 : struct snd_info_entry *entry = data->entry;
174 : 0 : ssize_t size = 0;
175 : 0 : loff_t pos;
176 : :
177 : 0 : pos = *offset;
178 [ # # ]: 0 : if (!valid_pos(pos, count))
179 : : return -EIO;
180 [ # # ]: 0 : if (count > 0) {
181 : 0 : size_t maxsize = entry->size - pos;
182 : 0 : count = min(count, maxsize);
183 : 0 : size = entry->c.ops->write(entry, data->file_private_data,
184 : : file, buffer, count, pos);
185 : : }
186 [ # # ]: 0 : if (size > 0)
187 : 0 : *offset = pos + size;
188 : : return size;
189 : : }
190 : :
191 : 0 : static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
192 : : {
193 : 0 : struct snd_info_private_data *data = file->private_data;
194 : 0 : struct snd_info_entry *entry = data->entry;
195 : 0 : __poll_t mask = 0;
196 : :
197 [ # # ]: 0 : if (entry->c.ops->poll)
198 : 0 : return entry->c.ops->poll(entry,
199 : : data->file_private_data,
200 : : file, wait);
201 [ # # ]: 0 : if (entry->c.ops->read)
202 : 0 : mask |= EPOLLIN | EPOLLRDNORM;
203 [ # # ]: 0 : if (entry->c.ops->write)
204 : 0 : mask |= EPOLLOUT | EPOLLWRNORM;
205 : : return mask;
206 : : }
207 : :
208 : 0 : static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
209 : : unsigned long arg)
210 : : {
211 : 0 : struct snd_info_private_data *data = file->private_data;
212 : 0 : struct snd_info_entry *entry = data->entry;
213 : :
214 [ # # ]: 0 : if (!entry->c.ops->ioctl)
215 : : return -ENOTTY;
216 : 0 : return entry->c.ops->ioctl(entry, data->file_private_data,
217 : : file, cmd, arg);
218 : : }
219 : :
220 : 0 : static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
221 : : {
222 [ # # ]: 0 : struct inode *inode = file_inode(file);
223 : 0 : struct snd_info_private_data *data;
224 : 0 : struct snd_info_entry *entry;
225 : :
226 : 0 : data = file->private_data;
227 [ # # ]: 0 : if (data == NULL)
228 : : return 0;
229 : 0 : entry = data->entry;
230 [ # # ]: 0 : if (!entry->c.ops->mmap)
231 : : return -ENXIO;
232 : 0 : return entry->c.ops->mmap(entry, data->file_private_data,
233 : : inode, file, vma);
234 : : }
235 : :
236 : 0 : static int snd_info_entry_open(struct inode *inode, struct file *file)
237 : : {
238 : 0 : struct snd_info_entry *entry = PDE_DATA(inode);
239 : 0 : struct snd_info_private_data *data;
240 : 0 : int mode, err;
241 : :
242 : 0 : mutex_lock(&info_mutex);
243 : 0 : err = alloc_info_private(entry, &data);
244 [ # # ]: 0 : if (err < 0)
245 : 0 : goto unlock;
246 : :
247 : 0 : mode = file->f_flags & O_ACCMODE;
248 [ # # # # ]: 0 : if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
249 [ # # # # ]: 0 : ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
250 : 0 : err = -ENODEV;
251 : 0 : goto error;
252 : : }
253 : :
254 [ # # ]: 0 : if (entry->c.ops->open) {
255 : 0 : err = entry->c.ops->open(entry, mode, &data->file_private_data);
256 [ # # ]: 0 : if (err < 0)
257 : 0 : goto error;
258 : : }
259 : :
260 : 0 : file->private_data = data;
261 : 0 : mutex_unlock(&info_mutex);
262 : 0 : return 0;
263 : :
264 : 0 : error:
265 : 0 : kfree(data);
266 : 0 : module_put(entry->module);
267 : 0 : unlock:
268 : 0 : mutex_unlock(&info_mutex);
269 : 0 : return err;
270 : : }
271 : :
272 : 0 : static int snd_info_entry_release(struct inode *inode, struct file *file)
273 : : {
274 : 0 : struct snd_info_private_data *data = file->private_data;
275 : 0 : struct snd_info_entry *entry = data->entry;
276 : :
277 [ # # ]: 0 : if (entry->c.ops->release)
278 : 0 : entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
279 : : data->file_private_data);
280 : 0 : module_put(entry->module);
281 : 0 : kfree(data);
282 : 0 : return 0;
283 : : }
284 : :
285 : : static const struct proc_ops snd_info_entry_operations =
286 : : {
287 : : .proc_lseek = snd_info_entry_llseek,
288 : : .proc_read = snd_info_entry_read,
289 : : .proc_write = snd_info_entry_write,
290 : : .proc_poll = snd_info_entry_poll,
291 : : .proc_ioctl = snd_info_entry_ioctl,
292 : : .proc_mmap = snd_info_entry_mmap,
293 : : .proc_open = snd_info_entry_open,
294 : : .proc_release = snd_info_entry_release,
295 : : };
296 : :
297 : : /*
298 : : * file ops for text proc files
299 : : */
300 : 0 : static ssize_t snd_info_text_entry_write(struct file *file,
301 : : const char __user *buffer,
302 : : size_t count, loff_t *offset)
303 : : {
304 : 0 : struct seq_file *m = file->private_data;
305 : 0 : struct snd_info_private_data *data = m->private;
306 : 0 : struct snd_info_entry *entry = data->entry;
307 : 0 : struct snd_info_buffer *buf;
308 : 0 : loff_t pos;
309 : 0 : size_t next;
310 : 0 : int err = 0;
311 : :
312 [ # # ]: 0 : if (!entry->c.text.write)
313 : : return -EIO;
314 : 0 : pos = *offset;
315 [ # # ]: 0 : if (!valid_pos(pos, count))
316 : : return -EIO;
317 : 0 : next = pos + count;
318 : : /* don't handle too large text inputs */
319 [ # # ]: 0 : if (next > 16 * 1024)
320 : : return -EIO;
321 : 0 : mutex_lock(&entry->access);
322 : 0 : buf = data->wbuffer;
323 [ # # ]: 0 : if (!buf) {
324 : 0 : data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
325 [ # # ]: 0 : if (!buf) {
326 : 0 : err = -ENOMEM;
327 : 0 : goto error;
328 : : }
329 : : }
330 [ # # ]: 0 : if (next > buf->len) {
331 : 0 : char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
332 [ # # ]: 0 : if (!nbuf) {
333 : 0 : err = -ENOMEM;
334 : 0 : goto error;
335 : : }
336 : 0 : kvfree(buf->buffer);
337 : 0 : buf->buffer = nbuf;
338 : 0 : buf->len = PAGE_ALIGN(next);
339 : : }
340 [ # # # # ]: 0 : if (copy_from_user(buf->buffer + pos, buffer, count)) {
341 : 0 : err = -EFAULT;
342 : 0 : goto error;
343 : : }
344 : 0 : buf->size = next;
345 : 0 : error:
346 : 0 : mutex_unlock(&entry->access);
347 [ # # ]: 0 : if (err < 0)
348 : 0 : return err;
349 : 0 : *offset = next;
350 : 0 : return count;
351 : : }
352 : :
353 : 0 : static int snd_info_seq_show(struct seq_file *seq, void *p)
354 : : {
355 : 0 : struct snd_info_private_data *data = seq->private;
356 : 0 : struct snd_info_entry *entry = data->entry;
357 : :
358 [ # # ]: 0 : if (!entry->c.text.read) {
359 : : return -EIO;
360 : : } else {
361 : 0 : data->rbuffer->buffer = (char *)seq; /* XXX hack! */
362 : 0 : entry->c.text.read(entry, data->rbuffer);
363 : : }
364 : 0 : return 0;
365 : : }
366 : :
367 : 0 : static int snd_info_text_entry_open(struct inode *inode, struct file *file)
368 : : {
369 : 0 : struct snd_info_entry *entry = PDE_DATA(inode);
370 : 0 : struct snd_info_private_data *data;
371 : 0 : int err;
372 : :
373 : 0 : mutex_lock(&info_mutex);
374 : 0 : err = alloc_info_private(entry, &data);
375 [ # # ]: 0 : if (err < 0)
376 : 0 : goto unlock;
377 : :
378 : 0 : data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
379 [ # # ]: 0 : if (!data->rbuffer) {
380 : 0 : err = -ENOMEM;
381 : 0 : goto error;
382 : : }
383 [ # # ]: 0 : if (entry->size)
384 : 0 : err = single_open_size(file, snd_info_seq_show, data,
385 : : entry->size);
386 : : else
387 : 0 : err = single_open(file, snd_info_seq_show, data);
388 [ # # ]: 0 : if (err < 0)
389 : 0 : goto error;
390 : 0 : mutex_unlock(&info_mutex);
391 : 0 : return 0;
392 : :
393 : 0 : error:
394 : 0 : kfree(data->rbuffer);
395 : 0 : kfree(data);
396 : 0 : module_put(entry->module);
397 : 0 : unlock:
398 : 0 : mutex_unlock(&info_mutex);
399 : 0 : return err;
400 : : }
401 : :
402 : 0 : static int snd_info_text_entry_release(struct inode *inode, struct file *file)
403 : : {
404 : 0 : struct seq_file *m = file->private_data;
405 : 0 : struct snd_info_private_data *data = m->private;
406 : 0 : struct snd_info_entry *entry = data->entry;
407 : :
408 [ # # # # ]: 0 : if (data->wbuffer && entry->c.text.write)
409 : 0 : entry->c.text.write(entry, data->wbuffer);
410 : :
411 : 0 : single_release(inode, file);
412 : 0 : kfree(data->rbuffer);
413 [ # # ]: 0 : if (data->wbuffer) {
414 : 0 : kvfree(data->wbuffer->buffer);
415 : 0 : kfree(data->wbuffer);
416 : : }
417 : :
418 : 0 : module_put(entry->module);
419 : 0 : kfree(data);
420 : 0 : return 0;
421 : : }
422 : :
423 : : static const struct proc_ops snd_info_text_entry_ops =
424 : : {
425 : : .proc_open = snd_info_text_entry_open,
426 : : .proc_release = snd_info_text_entry_release,
427 : : .proc_write = snd_info_text_entry_write,
428 : : .proc_lseek = seq_lseek,
429 : : .proc_read = seq_read,
430 : : };
431 : :
432 : 21 : static struct snd_info_entry *create_subdir(struct module *mod,
433 : : const char *name)
434 : : {
435 : 21 : struct snd_info_entry *entry;
436 : :
437 : 21 : entry = snd_info_create_module_entry(mod, name, NULL);
438 [ + - ]: 21 : if (!entry)
439 : : return NULL;
440 : 21 : entry->mode = S_IFDIR | 0555;
441 [ - + ]: 21 : if (snd_info_register(entry) < 0) {
442 : 0 : snd_info_free_entry(entry);
443 : 0 : return NULL;
444 : : }
445 : : return entry;
446 : : }
447 : :
448 : : static struct snd_info_entry *
449 : : snd_info_create_entry(const char *name, struct snd_info_entry *parent,
450 : : struct module *module);
451 : :
452 : 21 : int __init snd_info_init(void)
453 : : {
454 : 21 : snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
455 [ + - ]: 21 : if (!snd_proc_root)
456 : : return -ENOMEM;
457 : 21 : snd_proc_root->mode = S_IFDIR | 0555;
458 : 21 : snd_proc_root->p = proc_mkdir("asound", NULL);
459 [ - + ]: 21 : if (!snd_proc_root->p)
460 : 0 : goto error;
461 : : #ifdef CONFIG_SND_OSSEMUL
462 : : snd_oss_root = create_subdir(THIS_MODULE, "oss");
463 : : if (!snd_oss_root)
464 : : goto error;
465 : : #endif
466 : : #if IS_ENABLED(CONFIG_SND_SEQUENCER)
467 : 21 : snd_seq_root = create_subdir(THIS_MODULE, "seq");
468 [ - + ]: 21 : if (!snd_seq_root)
469 : 0 : goto error;
470 : : #endif
471 [ + - ]: 42 : if (snd_info_version_init() < 0 ||
472 : 21 : snd_minor_info_init() < 0 ||
473 [ + - - + ]: 42 : snd_minor_info_oss_init() < 0 ||
474 : 21 : snd_card_info_init() < 0 ||
475 : : snd_info_minor_register() < 0)
476 : 0 : goto error;
477 : : return 0;
478 : :
479 : 0 : error:
480 : 0 : snd_info_free_entry(snd_proc_root);
481 : 0 : return -ENOMEM;
482 : : }
483 : :
484 : 0 : int __exit snd_info_done(void)
485 : : {
486 : 0 : snd_info_free_entry(snd_proc_root);
487 : 0 : return 0;
488 : : }
489 : :
490 : 0 : static void snd_card_id_read(struct snd_info_entry *entry,
491 : : struct snd_info_buffer *buffer)
492 : : {
493 : 0 : struct snd_card *card = entry->private_data;
494 : :
495 : 0 : snd_iprintf(buffer, "%s\n", card->id);
496 : 0 : }
497 : :
498 : : /*
499 : : * create a card proc file
500 : : * called from init.c
501 : : */
502 : 0 : int snd_info_card_create(struct snd_card *card)
503 : : {
504 : 0 : char str[8];
505 : 0 : struct snd_info_entry *entry;
506 : :
507 [ # # ]: 0 : if (snd_BUG_ON(!card))
508 : : return -ENXIO;
509 : :
510 : 0 : sprintf(str, "card%i", card->number);
511 : 0 : entry = create_subdir(card->module, str);
512 [ # # ]: 0 : if (!entry)
513 : : return -ENOMEM;
514 : 0 : card->proc_root = entry;
515 : :
516 : 0 : return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
517 : : }
518 : :
519 : : /*
520 : : * register the card proc file
521 : : * called from init.c
522 : : * can be called multiple times for reinitialization
523 : : */
524 : 0 : int snd_info_card_register(struct snd_card *card)
525 : : {
526 : 0 : struct proc_dir_entry *p;
527 : 0 : int err;
528 : :
529 [ # # ]: 0 : if (snd_BUG_ON(!card))
530 : : return -ENXIO;
531 : :
532 : 0 : err = snd_info_register(card->proc_root);
533 [ # # ]: 0 : if (err < 0)
534 : : return err;
535 : :
536 [ # # ]: 0 : if (!strcmp(card->id, card->proc_root->name))
537 : : return 0;
538 : :
539 [ # # ]: 0 : if (card->proc_root_link)
540 : : return 0;
541 : 0 : p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
542 [ # # ]: 0 : if (!p)
543 : : return -ENOMEM;
544 : 0 : card->proc_root_link = p;
545 : 0 : return 0;
546 : : }
547 : :
548 : : /*
549 : : * called on card->id change
550 : : */
551 : 0 : void snd_info_card_id_change(struct snd_card *card)
552 : : {
553 : 0 : mutex_lock(&info_mutex);
554 [ # # ]: 0 : if (card->proc_root_link) {
555 : 0 : proc_remove(card->proc_root_link);
556 : 0 : card->proc_root_link = NULL;
557 : : }
558 [ # # ]: 0 : if (strcmp(card->id, card->proc_root->name))
559 : 0 : card->proc_root_link = proc_symlink(card->id,
560 : 0 : snd_proc_root->p,
561 : : card->proc_root->name);
562 : 0 : mutex_unlock(&info_mutex);
563 : 0 : }
564 : :
565 : : /*
566 : : * de-register the card proc file
567 : : * called from init.c
568 : : */
569 : 0 : void snd_info_card_disconnect(struct snd_card *card)
570 : : {
571 [ # # ]: 0 : if (!card)
572 : : return;
573 : 0 : mutex_lock(&info_mutex);
574 : 0 : proc_remove(card->proc_root_link);
575 : 0 : card->proc_root_link = NULL;
576 [ # # ]: 0 : if (card->proc_root)
577 : 0 : snd_info_disconnect(card->proc_root);
578 : 0 : mutex_unlock(&info_mutex);
579 : : }
580 : :
581 : : /*
582 : : * release the card proc file resources
583 : : * called from init.c
584 : : */
585 : 0 : int snd_info_card_free(struct snd_card *card)
586 : : {
587 [ # # ]: 0 : if (!card)
588 : : return 0;
589 : 0 : snd_info_free_entry(card->proc_root);
590 : 0 : card->proc_root = NULL;
591 : 0 : return 0;
592 : : }
593 : :
594 : :
595 : : /**
596 : : * snd_info_get_line - read one line from the procfs buffer
597 : : * @buffer: the procfs buffer
598 : : * @line: the buffer to store
599 : : * @len: the max. buffer size
600 : : *
601 : : * Reads one line from the buffer and stores the string.
602 : : *
603 : : * Return: Zero if successful, or 1 if error or EOF.
604 : : */
605 : 0 : int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
606 : : {
607 : 0 : int c = -1;
608 : :
609 [ # # # # : 0 : if (snd_BUG_ON(!buffer || !buffer->buffer))
# # ]
610 : : return 1;
611 [ # # # # : 0 : if (len <= 0 || buffer->stop || buffer->error)
# # ]
612 : : return 1;
613 [ # # ]: 0 : while (!buffer->stop) {
614 : 0 : c = buffer->buffer[buffer->curr++];
615 [ # # ]: 0 : if (buffer->curr >= buffer->size)
616 : 0 : buffer->stop = 1;
617 [ # # ]: 0 : if (c == '\n')
618 : : break;
619 [ # # ]: 0 : if (len > 1) {
620 : 0 : len--;
621 : 0 : *line++ = c;
622 : : }
623 : : }
624 : 0 : *line = '\0';
625 : 0 : return 0;
626 : : }
627 : : EXPORT_SYMBOL(snd_info_get_line);
628 : :
629 : : /**
630 : : * snd_info_get_str - parse a string token
631 : : * @dest: the buffer to store the string token
632 : : * @src: the original string
633 : : * @len: the max. length of token - 1
634 : : *
635 : : * Parses the original string and copy a token to the given
636 : : * string buffer.
637 : : *
638 : : * Return: The updated pointer of the original string so that
639 : : * it can be used for the next call.
640 : : */
641 : 0 : const char *snd_info_get_str(char *dest, const char *src, int len)
642 : : {
643 : 0 : int c;
644 : :
645 [ # # ]: 0 : while (*src == ' ' || *src == '\t')
646 : 0 : src++;
647 [ # # ]: 0 : if (*src == '"' || *src == '\'') {
648 : 0 : c = *src++;
649 [ # # # # : 0 : while (--len > 0 && *src && *src != c) {
# # ]
650 : 0 : *dest++ = *src++;
651 : : }
652 [ # # ]: 0 : if (*src == c)
653 : 0 : src++;
654 : : } else {
655 [ # # # # : 0 : while (--len > 0 && *src && *src != ' ' && *src != '\t') {
# # # # ]
656 : 0 : *dest++ = *src++;
657 : : }
658 : : }
659 : 0 : *dest = 0;
660 [ # # ]: 0 : while (*src == ' ' || *src == '\t')
661 : 0 : src++;
662 : 0 : return src;
663 : : }
664 : : EXPORT_SYMBOL(snd_info_get_str);
665 : :
666 : : /*
667 : : * snd_info_create_entry - create an info entry
668 : : * @name: the proc file name
669 : : * @parent: the parent directory
670 : : *
671 : : * Creates an info entry with the given file name and initializes as
672 : : * the default state.
673 : : *
674 : : * Usually called from other functions such as
675 : : * snd_info_create_card_entry().
676 : : *
677 : : * Return: The pointer of the new instance, or %NULL on failure.
678 : : */
679 : : static struct snd_info_entry *
680 : 252 : snd_info_create_entry(const char *name, struct snd_info_entry *parent,
681 : : struct module *module)
682 : : {
683 : 252 : struct snd_info_entry *entry;
684 : 252 : entry = kzalloc(sizeof(*entry), GFP_KERNEL);
685 [ + - ]: 252 : if (entry == NULL)
686 : : return NULL;
687 : 252 : entry->name = kstrdup(name, GFP_KERNEL);
688 [ - + ]: 252 : if (entry->name == NULL) {
689 : 0 : kfree(entry);
690 : 0 : return NULL;
691 : : }
692 : 252 : entry->mode = S_IFREG | 0444;
693 : 252 : entry->content = SNDRV_INFO_CONTENT_TEXT;
694 : 252 : mutex_init(&entry->access);
695 [ + + ]: 252 : INIT_LIST_HEAD(&entry->children);
696 : 252 : INIT_LIST_HEAD(&entry->list);
697 : 252 : entry->parent = parent;
698 : 252 : entry->module = module;
699 [ + + ]: 252 : if (parent) {
700 : 231 : mutex_lock(&parent->access);
701 : 231 : list_add_tail(&entry->list, &parent->children);
702 : 231 : mutex_unlock(&parent->access);
703 : : }
704 : : return entry;
705 : : }
706 : :
707 : : /**
708 : : * snd_info_create_module_entry - create an info entry for the given module
709 : : * @module: the module pointer
710 : : * @name: the file name
711 : : * @parent: the parent directory
712 : : *
713 : : * Creates a new info entry and assigns it to the given module.
714 : : *
715 : : * Return: The pointer of the new instance, or %NULL on failure.
716 : : */
717 : 231 : struct snd_info_entry *snd_info_create_module_entry(struct module * module,
718 : : const char *name,
719 : : struct snd_info_entry *parent)
720 : : {
721 [ + + ]: 189 : if (!parent)
722 : 105 : parent = snd_proc_root;
723 : 210 : return snd_info_create_entry(name, parent, module);
724 : : }
725 : : EXPORT_SYMBOL(snd_info_create_module_entry);
726 : :
727 : : /**
728 : : * snd_info_create_card_entry - create an info entry for the given card
729 : : * @card: the card instance
730 : : * @name: the file name
731 : : * @parent: the parent directory
732 : : *
733 : : * Creates a new info entry and assigns it to the given card.
734 : : *
735 : : * Return: The pointer of the new instance, or %NULL on failure.
736 : : */
737 : 0 : struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
738 : : const char *name,
739 : : struct snd_info_entry * parent)
740 : : {
741 [ # # ]: 0 : if (!parent)
742 : 0 : parent = card->proc_root;
743 : 0 : return snd_info_create_entry(name, parent, card->module);
744 : : }
745 : : EXPORT_SYMBOL(snd_info_create_card_entry);
746 : :
747 : 0 : static void snd_info_disconnect(struct snd_info_entry *entry)
748 : : {
749 : 0 : struct snd_info_entry *p;
750 : :
751 [ # # ]: 0 : if (!entry->p)
752 : : return;
753 [ # # ]: 0 : list_for_each_entry(p, &entry->children, list)
754 : 0 : snd_info_disconnect(p);
755 : 0 : proc_remove(entry->p);
756 : 0 : entry->p = NULL;
757 : : }
758 : :
759 : : /**
760 : : * snd_info_free_entry - release the info entry
761 : : * @entry: the info entry
762 : : *
763 : : * Releases the info entry.
764 : : */
765 : 0 : void snd_info_free_entry(struct snd_info_entry * entry)
766 : : {
767 : 0 : struct snd_info_entry *p, *n;
768 : :
769 [ # # ]: 0 : if (!entry)
770 : : return;
771 [ # # ]: 0 : if (entry->p) {
772 : 0 : mutex_lock(&info_mutex);
773 : 0 : snd_info_disconnect(entry);
774 : 0 : mutex_unlock(&info_mutex);
775 : : }
776 : :
777 : : /* free all children at first */
778 [ # # ]: 0 : list_for_each_entry_safe(p, n, &entry->children, list)
779 : 0 : snd_info_free_entry(p);
780 : :
781 : 0 : p = entry->parent;
782 [ # # ]: 0 : if (p) {
783 : 0 : mutex_lock(&p->access);
784 : 0 : list_del(&entry->list);
785 : 0 : mutex_unlock(&p->access);
786 : : }
787 : 0 : kfree(entry->name);
788 [ # # ]: 0 : if (entry->private_free)
789 : 0 : entry->private_free(entry);
790 : 0 : kfree(entry);
791 : : }
792 : : EXPORT_SYMBOL(snd_info_free_entry);
793 : :
794 : 231 : static int __snd_info_register(struct snd_info_entry *entry)
795 : : {
796 : 231 : struct proc_dir_entry *root, *p = NULL;
797 : :
798 [ + - ]: 231 : if (snd_BUG_ON(!entry))
799 : : return -ENXIO;
800 [ - + ]: 231 : root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
801 : 231 : mutex_lock(&info_mutex);
802 [ + - - + ]: 231 : if (entry->p || !root)
803 : 0 : goto unlock;
804 [ + + ]: 231 : if (S_ISDIR(entry->mode)) {
805 : 21 : p = proc_mkdir_mode(entry->name, entry->mode, root);
806 [ - + ]: 21 : if (!p) {
807 : 0 : mutex_unlock(&info_mutex);
808 : 0 : return -ENOMEM;
809 : : }
810 : : } else {
811 : 210 : const struct proc_ops *ops;
812 [ + - ]: 210 : if (entry->content == SNDRV_INFO_CONTENT_DATA)
813 : : ops = &snd_info_entry_operations;
814 : : else
815 : 210 : ops = &snd_info_text_entry_ops;
816 : 210 : p = proc_create_data(entry->name, entry->mode, root,
817 : : ops, entry);
818 [ - + ]: 210 : if (!p) {
819 : 0 : mutex_unlock(&info_mutex);
820 : 0 : return -ENOMEM;
821 : : }
822 : 210 : proc_set_size(p, entry->size);
823 : : }
824 : 231 : entry->p = p;
825 : 231 : unlock:
826 : 231 : mutex_unlock(&info_mutex);
827 : 231 : return 0;
828 : : }
829 : :
830 : : /**
831 : : * snd_info_register - register the info entry
832 : : * @entry: the info entry
833 : : *
834 : : * Registers the proc info entry.
835 : : * The all children entries are registered recursively.
836 : : *
837 : : * Return: Zero if successful, or a negative error code on failure.
838 : : */
839 : 231 : int snd_info_register(struct snd_info_entry *entry)
840 : : {
841 : 231 : struct snd_info_entry *p;
842 : 231 : int err;
843 : :
844 [ + - ]: 231 : if (!entry->p) {
845 : 231 : err = __snd_info_register(entry);
846 [ + - ]: 231 : if (err < 0)
847 : : return err;
848 : : }
849 : :
850 [ - + ]: 231 : list_for_each_entry(p, &entry->children, list) {
851 : 0 : err = snd_info_register(p);
852 [ # # ]: 0 : if (err < 0)
853 : 0 : return err;
854 : : }
855 : :
856 : : return 0;
857 : : }
858 : : EXPORT_SYMBOL(snd_info_register);
859 : :
860 : : /**
861 : : * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
862 : : * @card: the card instance
863 : : * @name: the file name
864 : : * @private_data: the arbitrary private data
865 : : * @read: the read callback
866 : : * @write: the write callback, NULL for read-only
867 : : *
868 : : * This proc file entry will be registered via snd_card_register() call, and
869 : : * it will be removed automatically at the card removal, too.
870 : : */
871 : 0 : int snd_card_rw_proc_new(struct snd_card *card, const char *name,
872 : : void *private_data,
873 : : void (*read)(struct snd_info_entry *,
874 : : struct snd_info_buffer *),
875 : : void (*write)(struct snd_info_entry *entry,
876 : : struct snd_info_buffer *buffer))
877 : : {
878 : 0 : struct snd_info_entry *entry;
879 : :
880 : 0 : entry = snd_info_create_card_entry(card, name, card->proc_root);
881 [ # # # # ]: 0 : if (!entry)
882 : : return -ENOMEM;
883 [ # # ]: 0 : snd_info_set_text_ops(entry, private_data, read);
884 [ # # ]: 0 : if (write) {
885 : 0 : entry->mode |= 0200;
886 : 0 : entry->c.text.write = write;
887 : : }
888 : : return 0;
889 : : }
890 : : EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
891 : :
892 : : /*
893 : :
894 : : */
895 : :
896 : 0 : static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
897 : : {
898 : 0 : snd_iprintf(buffer,
899 : : "Advanced Linux Sound Architecture Driver Version k%s.\n",
900 : : init_utsname()->release);
901 : 0 : }
902 : :
903 : 21 : static int __init snd_info_version_init(void)
904 : : {
905 : 21 : struct snd_info_entry *entry;
906 : :
907 : 21 : entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
908 [ + - ]: 21 : if (entry == NULL)
909 : : return -ENOMEM;
910 : 21 : entry->c.text.read = snd_info_version_read;
911 : 21 : return snd_info_register(entry); /* freed in error path */
912 : : }
|