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 : 3 : int snd_info_check_reserved_words(const char *str)
22 : : {
23 : : static char *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 : : char **xstr = reserved;
39 : :
40 : 3 : while (*xstr) {
41 : 3 : if (!strcmp(*xstr, str))
42 : : return 0;
43 : 3 : xstr++;
44 : : }
45 : 3 : if (!strncmp(str, "card", 4))
46 : : return 0;
47 : 3 : 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 : : 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 : : 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 : : struct snd_info_private_data *data;
108 : : struct snd_info_entry *entry;
109 : : 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 : : case SEEK_CUR:
126 : 0 : offset += file->f_pos;
127 : 0 : break;
128 : : case SEEK_END:
129 : 0 : if (!size)
130 : : goto out;
131 : 0 : offset += size;
132 : 0 : break;
133 : : default:
134 : : goto out;
135 : : }
136 : 0 : if (offset < 0)
137 : : goto out;
138 : 0 : if (size && offset > size)
139 : : offset = size;
140 : 0 : file->f_pos = offset;
141 : : ret = offset;
142 : : 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 : : size_t size;
153 : : 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 : 0 : 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 : : ssize_t size = 0;
175 : : 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 : 0 : 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 : : __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 : : mask |= EPOLLIN | EPOLLRDNORM;
203 : 0 : if (entry->c.ops->write)
204 : 0 : mask |= EPOLLOUT | EPOLLWRNORM;
205 : 0 : 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 : : struct inode *inode = file_inode(file);
223 : : struct snd_info_private_data *data;
224 : : 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 : : struct snd_info_private_data *data;
240 : : int mode, err;
241 : :
242 : 0 : mutex_lock(&info_mutex);
243 : 0 : err = alloc_info_private(entry, &data);
244 : 0 : if (err < 0)
245 : : 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 : : err = -ENODEV;
251 : : 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 : : goto error;
258 : : }
259 : :
260 : 0 : file->private_data = data;
261 : 0 : mutex_unlock(&info_mutex);
262 : 0 : return 0;
263 : :
264 : : error:
265 : 0 : kfree(data);
266 : 0 : module_put(entry->module);
267 : : 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 file_operations snd_info_entry_operations =
286 : : {
287 : : .owner = THIS_MODULE,
288 : : .llseek = snd_info_entry_llseek,
289 : : .read = snd_info_entry_read,
290 : : .write = snd_info_entry_write,
291 : : .poll = snd_info_entry_poll,
292 : : .unlocked_ioctl = snd_info_entry_ioctl,
293 : : .mmap = snd_info_entry_mmap,
294 : : .open = snd_info_entry_open,
295 : : .release = snd_info_entry_release,
296 : : };
297 : :
298 : : /*
299 : : * file ops for text proc files
300 : : */
301 : 0 : static ssize_t snd_info_text_entry_write(struct file *file,
302 : : const char __user *buffer,
303 : : size_t count, loff_t *offset)
304 : : {
305 : 0 : struct seq_file *m = file->private_data;
306 : 0 : struct snd_info_private_data *data = m->private;
307 : 0 : struct snd_info_entry *entry = data->entry;
308 : : struct snd_info_buffer *buf;
309 : : loff_t pos;
310 : : size_t next;
311 : : int err = 0;
312 : :
313 : 0 : if (!entry->c.text.write)
314 : : return -EIO;
315 : 0 : pos = *offset;
316 : 0 : if (!valid_pos(pos, count))
317 : : return -EIO;
318 : 0 : next = pos + count;
319 : : /* don't handle too large text inputs */
320 : 0 : if (next > 16 * 1024)
321 : : return -EIO;
322 : 0 : mutex_lock(&entry->access);
323 : 0 : buf = data->wbuffer;
324 : 0 : if (!buf) {
325 : 0 : data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
326 : 0 : if (!buf) {
327 : : err = -ENOMEM;
328 : : goto error;
329 : : }
330 : : }
331 : 0 : if (next > buf->len) {
332 : 0 : char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
333 : 0 : if (!nbuf) {
334 : : err = -ENOMEM;
335 : : goto error;
336 : : }
337 : 0 : kvfree(buf->buffer);
338 : 0 : buf->buffer = nbuf;
339 : 0 : buf->len = PAGE_ALIGN(next);
340 : : }
341 : 0 : if (copy_from_user(buf->buffer + pos, buffer, count)) {
342 : : err = -EFAULT;
343 : : goto error;
344 : : }
345 : 0 : buf->size = next;
346 : : error:
347 : 0 : mutex_unlock(&entry->access);
348 : 0 : if (err < 0)
349 : : return err;
350 : 0 : *offset = next;
351 : 0 : return count;
352 : : }
353 : :
354 : 0 : static int snd_info_seq_show(struct seq_file *seq, void *p)
355 : : {
356 : 0 : struct snd_info_private_data *data = seq->private;
357 : 0 : struct snd_info_entry *entry = data->entry;
358 : :
359 : 0 : if (!entry->c.text.read) {
360 : : return -EIO;
361 : : } else {
362 : 0 : data->rbuffer->buffer = (char *)seq; /* XXX hack! */
363 : 0 : entry->c.text.read(entry, data->rbuffer);
364 : : }
365 : 0 : return 0;
366 : : }
367 : :
368 : 0 : static int snd_info_text_entry_open(struct inode *inode, struct file *file)
369 : : {
370 : 0 : struct snd_info_entry *entry = PDE_DATA(inode);
371 : : struct snd_info_private_data *data;
372 : : int err;
373 : :
374 : 0 : mutex_lock(&info_mutex);
375 : 0 : err = alloc_info_private(entry, &data);
376 : 0 : if (err < 0)
377 : : goto unlock;
378 : :
379 : 0 : data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
380 : 0 : if (!data->rbuffer) {
381 : : err = -ENOMEM;
382 : : goto error;
383 : : }
384 : 0 : if (entry->size)
385 : 0 : err = single_open_size(file, snd_info_seq_show, data,
386 : : entry->size);
387 : : else
388 : 0 : err = single_open(file, snd_info_seq_show, data);
389 : 0 : if (err < 0)
390 : : goto error;
391 : 0 : mutex_unlock(&info_mutex);
392 : 0 : return 0;
393 : :
394 : : error:
395 : 0 : kfree(data->rbuffer);
396 : 0 : kfree(data);
397 : 0 : module_put(entry->module);
398 : : unlock:
399 : 0 : mutex_unlock(&info_mutex);
400 : 0 : return err;
401 : : }
402 : :
403 : 0 : static int snd_info_text_entry_release(struct inode *inode, struct file *file)
404 : : {
405 : 0 : struct seq_file *m = file->private_data;
406 : 0 : struct snd_info_private_data *data = m->private;
407 : 0 : struct snd_info_entry *entry = data->entry;
408 : :
409 : 0 : if (data->wbuffer && entry->c.text.write)
410 : 0 : entry->c.text.write(entry, data->wbuffer);
411 : :
412 : 0 : single_release(inode, file);
413 : 0 : kfree(data->rbuffer);
414 : 0 : if (data->wbuffer) {
415 : 0 : kvfree(data->wbuffer->buffer);
416 : 0 : kfree(data->wbuffer);
417 : : }
418 : :
419 : 0 : module_put(entry->module);
420 : 0 : kfree(data);
421 : 0 : return 0;
422 : : }
423 : :
424 : : static const struct file_operations snd_info_text_entry_ops =
425 : : {
426 : : .owner = THIS_MODULE,
427 : : .open = snd_info_text_entry_open,
428 : : .release = snd_info_text_entry_release,
429 : : .write = snd_info_text_entry_write,
430 : : .llseek = seq_lseek,
431 : : .read = seq_read,
432 : : };
433 : :
434 : 3 : static struct snd_info_entry *create_subdir(struct module *mod,
435 : : const char *name)
436 : : {
437 : : struct snd_info_entry *entry;
438 : :
439 : : entry = snd_info_create_module_entry(mod, name, NULL);
440 : 3 : if (!entry)
441 : : return NULL;
442 : 3 : entry->mode = S_IFDIR | 0555;
443 : 3 : if (snd_info_register(entry) < 0) {
444 : 0 : snd_info_free_entry(entry);
445 : 0 : return NULL;
446 : : }
447 : : return entry;
448 : : }
449 : :
450 : : static struct snd_info_entry *
451 : : snd_info_create_entry(const char *name, struct snd_info_entry *parent,
452 : : struct module *module);
453 : :
454 : 3 : int __init snd_info_init(void)
455 : : {
456 : 3 : snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
457 : 3 : if (!snd_proc_root)
458 : : return -ENOMEM;
459 : 3 : snd_proc_root->mode = S_IFDIR | 0555;
460 : 3 : snd_proc_root->p = proc_mkdir("asound", NULL);
461 : 3 : if (!snd_proc_root->p)
462 : : goto error;
463 : : #ifdef CONFIG_SND_OSSEMUL
464 : : snd_oss_root = create_subdir(THIS_MODULE, "oss");
465 : : if (!snd_oss_root)
466 : : goto error;
467 : : #endif
468 : : #if IS_ENABLED(CONFIG_SND_SEQUENCER)
469 : 3 : snd_seq_root = create_subdir(THIS_MODULE, "seq");
470 : 3 : if (!snd_seq_root)
471 : : goto error;
472 : : #endif
473 : 3 : if (snd_info_version_init() < 0 ||
474 : 3 : snd_minor_info_init() < 0 ||
475 : 3 : snd_minor_info_oss_init() < 0 ||
476 : 3 : snd_card_info_init() < 0 ||
477 : : snd_info_minor_register() < 0)
478 : : goto error;
479 : : return 0;
480 : :
481 : : error:
482 : 0 : snd_info_free_entry(snd_proc_root);
483 : 0 : return -ENOMEM;
484 : : }
485 : :
486 : 0 : int __exit snd_info_done(void)
487 : : {
488 : 0 : snd_info_free_entry(snd_proc_root);
489 : 0 : return 0;
490 : : }
491 : :
492 : 0 : static void snd_card_id_read(struct snd_info_entry *entry,
493 : : struct snd_info_buffer *buffer)
494 : : {
495 : 0 : struct snd_card *card = entry->private_data;
496 : :
497 : 0 : snd_iprintf(buffer, "%s\n", card->id);
498 : 0 : }
499 : :
500 : : /*
501 : : * create a card proc file
502 : : * called from init.c
503 : : */
504 : 3 : int snd_info_card_create(struct snd_card *card)
505 : : {
506 : : char str[8];
507 : : struct snd_info_entry *entry;
508 : :
509 : 3 : if (snd_BUG_ON(!card))
510 : : return -ENXIO;
511 : :
512 : 3 : sprintf(str, "card%i", card->number);
513 : 3 : entry = create_subdir(card->module, str);
514 : 3 : if (!entry)
515 : : return -ENOMEM;
516 : 3 : card->proc_root = entry;
517 : :
518 : 3 : return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
519 : : }
520 : :
521 : : /*
522 : : * register the card proc file
523 : : * called from init.c
524 : : * can be called multiple times for reinitialization
525 : : */
526 : 3 : int snd_info_card_register(struct snd_card *card)
527 : : {
528 : : struct proc_dir_entry *p;
529 : : int err;
530 : :
531 : 3 : if (snd_BUG_ON(!card))
532 : : return -ENXIO;
533 : :
534 : 3 : err = snd_info_register(card->proc_root);
535 : 3 : if (err < 0)
536 : : return err;
537 : :
538 : 3 : if (!strcmp(card->id, card->proc_root->name))
539 : : return 0;
540 : :
541 : 3 : if (card->proc_root_link)
542 : : return 0;
543 : 3 : p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
544 : 3 : if (!p)
545 : : return -ENOMEM;
546 : 3 : card->proc_root_link = p;
547 : 3 : return 0;
548 : : }
549 : :
550 : : /*
551 : : * called on card->id change
552 : : */
553 : 0 : void snd_info_card_id_change(struct snd_card *card)
554 : : {
555 : 0 : mutex_lock(&info_mutex);
556 : 0 : if (card->proc_root_link) {
557 : 0 : proc_remove(card->proc_root_link);
558 : 0 : card->proc_root_link = NULL;
559 : : }
560 : 0 : if (strcmp(card->id, card->proc_root->name))
561 : 0 : card->proc_root_link = proc_symlink(card->id,
562 : 0 : snd_proc_root->p,
563 : : card->proc_root->name);
564 : 0 : mutex_unlock(&info_mutex);
565 : 0 : }
566 : :
567 : : /*
568 : : * de-register the card proc file
569 : : * called from init.c
570 : : */
571 : 0 : void snd_info_card_disconnect(struct snd_card *card)
572 : : {
573 : 0 : if (!card)
574 : 0 : return;
575 : 0 : mutex_lock(&info_mutex);
576 : 0 : proc_remove(card->proc_root_link);
577 : 0 : card->proc_root_link = NULL;
578 : 0 : if (card->proc_root)
579 : 0 : snd_info_disconnect(card->proc_root);
580 : 0 : mutex_unlock(&info_mutex);
581 : : }
582 : :
583 : : /*
584 : : * release the card proc file resources
585 : : * called from init.c
586 : : */
587 : 0 : int snd_info_card_free(struct snd_card *card)
588 : : {
589 : 0 : if (!card)
590 : : return 0;
591 : 0 : snd_info_free_entry(card->proc_root);
592 : 0 : card->proc_root = NULL;
593 : 0 : return 0;
594 : : }
595 : :
596 : :
597 : : /**
598 : : * snd_info_get_line - read one line from the procfs buffer
599 : : * @buffer: the procfs buffer
600 : : * @line: the buffer to store
601 : : * @len: the max. buffer size
602 : : *
603 : : * Reads one line from the buffer and stores the string.
604 : : *
605 : : * Return: Zero if successful, or 1 if error or EOF.
606 : : */
607 : 0 : int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
608 : : {
609 : : int c = -1;
610 : :
611 : 0 : if (snd_BUG_ON(!buffer))
612 : : return 1;
613 : 0 : if (!buffer->buffer)
614 : : return 1;
615 : 0 : if (len <= 0 || buffer->stop || buffer->error)
616 : : return 1;
617 : 0 : while (!buffer->stop) {
618 : 0 : c = buffer->buffer[buffer->curr++];
619 : 0 : if (buffer->curr >= buffer->size)
620 : 0 : buffer->stop = 1;
621 : 0 : if (c == '\n')
622 : : break;
623 : 0 : if (len > 1) {
624 : 0 : len--;
625 : 0 : *line++ = c;
626 : : }
627 : : }
628 : 0 : *line = '\0';
629 : 0 : return 0;
630 : : }
631 : : EXPORT_SYMBOL(snd_info_get_line);
632 : :
633 : : /**
634 : : * snd_info_get_str - parse a string token
635 : : * @dest: the buffer to store the string token
636 : : * @src: the original string
637 : : * @len: the max. length of token - 1
638 : : *
639 : : * Parses the original string and copy a token to the given
640 : : * string buffer.
641 : : *
642 : : * Return: The updated pointer of the original string so that
643 : : * it can be used for the next call.
644 : : */
645 : 0 : const char *snd_info_get_str(char *dest, const char *src, int len)
646 : : {
647 : : int c;
648 : :
649 : 0 : while (*src == ' ' || *src == '\t')
650 : 0 : src++;
651 : 0 : if (*src == '"' || *src == '\'') {
652 : 0 : c = *src++;
653 : 0 : while (--len > 0 && *src && *src != c) {
654 : 0 : *dest++ = *src++;
655 : : }
656 : 0 : if (*src == c)
657 : 0 : src++;
658 : : } else {
659 : 0 : while (--len > 0 && *src && *src != ' ' && *src != '\t') {
660 : 0 : *dest++ = *src++;
661 : : }
662 : : }
663 : 0 : *dest = 0;
664 : 0 : while (*src == ' ' || *src == '\t')
665 : 0 : src++;
666 : 0 : return src;
667 : : }
668 : : EXPORT_SYMBOL(snd_info_get_str);
669 : :
670 : : /*
671 : : * snd_info_create_entry - create an info entry
672 : : * @name: the proc file name
673 : : * @parent: the parent directory
674 : : *
675 : : * Creates an info entry with the given file name and initializes as
676 : : * the default state.
677 : : *
678 : : * Usually called from other functions such as
679 : : * snd_info_create_card_entry().
680 : : *
681 : : * Return: The pointer of the new instance, or %NULL on failure.
682 : : */
683 : : static struct snd_info_entry *
684 : 3 : snd_info_create_entry(const char *name, struct snd_info_entry *parent,
685 : : struct module *module)
686 : : {
687 : : struct snd_info_entry *entry;
688 : 3 : entry = kzalloc(sizeof(*entry), GFP_KERNEL);
689 : 3 : if (entry == NULL)
690 : : return NULL;
691 : 3 : entry->name = kstrdup(name, GFP_KERNEL);
692 : 3 : if (entry->name == NULL) {
693 : 0 : kfree(entry);
694 : 0 : return NULL;
695 : : }
696 : 3 : entry->mode = S_IFREG | 0444;
697 : 3 : entry->content = SNDRV_INFO_CONTENT_TEXT;
698 : 3 : mutex_init(&entry->access);
699 : 3 : INIT_LIST_HEAD(&entry->children);
700 : 3 : INIT_LIST_HEAD(&entry->list);
701 : 3 : entry->parent = parent;
702 : 3 : entry->module = module;
703 : 3 : if (parent) {
704 : 3 : mutex_lock(&parent->access);
705 : 3 : list_add_tail(&entry->list, &parent->children);
706 : 3 : mutex_unlock(&parent->access);
707 : : }
708 : 3 : return entry;
709 : : }
710 : :
711 : : /**
712 : : * snd_info_create_module_entry - create an info entry for the given module
713 : : * @module: the module pointer
714 : : * @name: the file name
715 : : * @parent: the parent directory
716 : : *
717 : : * Creates a new info entry and assigns it to the given module.
718 : : *
719 : : * Return: The pointer of the new instance, or %NULL on failure.
720 : : */
721 : 3 : struct snd_info_entry *snd_info_create_module_entry(struct module * module,
722 : : const char *name,
723 : : struct snd_info_entry *parent)
724 : : {
725 : 3 : if (!parent)
726 : 3 : parent = snd_proc_root;
727 : 3 : return snd_info_create_entry(name, parent, module);
728 : : }
729 : : EXPORT_SYMBOL(snd_info_create_module_entry);
730 : :
731 : : /**
732 : : * snd_info_create_card_entry - create an info entry for the given card
733 : : * @card: the card instance
734 : : * @name: the file name
735 : : * @parent: the parent directory
736 : : *
737 : : * Creates a new info entry and assigns it to the given card.
738 : : *
739 : : * Return: The pointer of the new instance, or %NULL on failure.
740 : : */
741 : 3 : struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
742 : : const char *name,
743 : : struct snd_info_entry * parent)
744 : : {
745 : 3 : if (!parent)
746 : 0 : parent = card->proc_root;
747 : 3 : return snd_info_create_entry(name, parent, card->module);
748 : : }
749 : : EXPORT_SYMBOL(snd_info_create_card_entry);
750 : :
751 : 0 : static void snd_info_disconnect(struct snd_info_entry *entry)
752 : : {
753 : : struct snd_info_entry *p;
754 : :
755 : 0 : if (!entry->p)
756 : 0 : return;
757 : 0 : list_for_each_entry(p, &entry->children, list)
758 : 0 : snd_info_disconnect(p);
759 : 0 : proc_remove(entry->p);
760 : 0 : entry->p = NULL;
761 : : }
762 : :
763 : : /**
764 : : * snd_info_free_entry - release the info entry
765 : : * @entry: the info entry
766 : : *
767 : : * Releases the info entry.
768 : : */
769 : 0 : void snd_info_free_entry(struct snd_info_entry * entry)
770 : : {
771 : : struct snd_info_entry *p, *n;
772 : :
773 : 0 : if (!entry)
774 : 0 : return;
775 : 0 : if (entry->p) {
776 : 0 : mutex_lock(&info_mutex);
777 : 0 : snd_info_disconnect(entry);
778 : 0 : mutex_unlock(&info_mutex);
779 : : }
780 : :
781 : : /* free all children at first */
782 : 0 : list_for_each_entry_safe(p, n, &entry->children, list)
783 : 0 : snd_info_free_entry(p);
784 : :
785 : 0 : p = entry->parent;
786 : 0 : if (p) {
787 : 0 : mutex_lock(&p->access);
788 : : list_del(&entry->list);
789 : 0 : mutex_unlock(&p->access);
790 : : }
791 : 0 : kfree(entry->name);
792 : 0 : if (entry->private_free)
793 : 0 : entry->private_free(entry);
794 : 0 : kfree(entry);
795 : : }
796 : : EXPORT_SYMBOL(snd_info_free_entry);
797 : :
798 : 3 : static int __snd_info_register(struct snd_info_entry *entry)
799 : : {
800 : : struct proc_dir_entry *root, *p = NULL;
801 : :
802 : 3 : if (snd_BUG_ON(!entry))
803 : : return -ENXIO;
804 : 3 : root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
805 : 3 : mutex_lock(&info_mutex);
806 : 3 : if (entry->p || !root)
807 : : goto unlock;
808 : 3 : if (S_ISDIR(entry->mode)) {
809 : 3 : p = proc_mkdir_mode(entry->name, entry->mode, root);
810 : 3 : if (!p) {
811 : 0 : mutex_unlock(&info_mutex);
812 : 0 : return -ENOMEM;
813 : : }
814 : : } else {
815 : : const struct file_operations *ops;
816 : 3 : if (entry->content == SNDRV_INFO_CONTENT_DATA)
817 : : ops = &snd_info_entry_operations;
818 : : else
819 : : ops = &snd_info_text_entry_ops;
820 : 3 : p = proc_create_data(entry->name, entry->mode, root,
821 : : ops, entry);
822 : 3 : if (!p) {
823 : 0 : mutex_unlock(&info_mutex);
824 : 0 : return -ENOMEM;
825 : : }
826 : 3 : proc_set_size(p, entry->size);
827 : : }
828 : 3 : entry->p = p;
829 : : unlock:
830 : 3 : mutex_unlock(&info_mutex);
831 : 3 : return 0;
832 : : }
833 : :
834 : : /**
835 : : * snd_info_register - register the info entry
836 : : * @entry: the info entry
837 : : *
838 : : * Registers the proc info entry.
839 : : * The all children entries are registered recursively.
840 : : *
841 : : * Return: Zero if successful, or a negative error code on failure.
842 : : */
843 : 3 : int snd_info_register(struct snd_info_entry *entry)
844 : : {
845 : : struct snd_info_entry *p;
846 : : int err;
847 : :
848 : 3 : if (!entry->p) {
849 : 3 : err = __snd_info_register(entry);
850 : 3 : if (err < 0)
851 : : return err;
852 : : }
853 : :
854 : 3 : list_for_each_entry(p, &entry->children, list) {
855 : 3 : err = snd_info_register(p);
856 : 3 : if (err < 0)
857 : 0 : return err;
858 : : }
859 : :
860 : : return 0;
861 : : }
862 : : EXPORT_SYMBOL(snd_info_register);
863 : :
864 : : /**
865 : : * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
866 : : * @card: the card instance
867 : : * @name: the file name
868 : : * @private_data: the arbitrary private data
869 : : * @read: the read callback
870 : : * @write: the write callback, NULL for read-only
871 : : *
872 : : * This proc file entry will be registered via snd_card_register() call, and
873 : : * it will be removed automatically at the card removal, too.
874 : : */
875 : 3 : int snd_card_rw_proc_new(struct snd_card *card, const char *name,
876 : : void *private_data,
877 : : void (*read)(struct snd_info_entry *,
878 : : struct snd_info_buffer *),
879 : : void (*write)(struct snd_info_entry *entry,
880 : : struct snd_info_buffer *buffer))
881 : : {
882 : : struct snd_info_entry *entry;
883 : :
884 : 3 : entry = snd_info_create_card_entry(card, name, card->proc_root);
885 : 3 : if (!entry)
886 : : return -ENOMEM;
887 : : snd_info_set_text_ops(entry, private_data, read);
888 : 3 : if (write) {
889 : 0 : entry->mode |= 0200;
890 : 0 : entry->c.text.write = write;
891 : : }
892 : : return 0;
893 : : }
894 : : EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
895 : :
896 : : /*
897 : :
898 : : */
899 : :
900 : 0 : static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
901 : : {
902 : 0 : snd_iprintf(buffer,
903 : : "Advanced Linux Sound Architecture Driver Version k%s.\n",
904 : : init_utsname()->release);
905 : 0 : }
906 : :
907 : 3 : static int __init snd_info_version_init(void)
908 : : {
909 : : struct snd_info_entry *entry;
910 : :
911 : : entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
912 : 3 : if (entry == NULL)
913 : : return -ENOMEM;
914 : 3 : entry->c.text.read = snd_info_version_read;
915 : 3 : return snd_info_register(entry); /* freed in error path */
916 : : }
|