diff options
Diffstat (limited to 'sound/core/info.c')
-rw-r--r-- | sound/core/info.c | 833 |
1 files changed, 350 insertions, 483 deletions
diff --git a/sound/core/info.c b/sound/core/info.c index 9f404e965ea2..895362a696c9 100644 --- a/sound/core/info.c +++ b/sound/core/info.c | |||
@@ -33,12 +33,6 @@ | |||
33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
34 | #include <stdarg.h> | 34 | #include <stdarg.h> |
35 | 35 | ||
36 | /* | ||
37 | * | ||
38 | */ | ||
39 | |||
40 | #ifdef CONFIG_PROC_FS | ||
41 | |||
42 | int snd_info_check_reserved_words(const char *str) | 36 | int snd_info_check_reserved_words(const char *str) |
43 | { | 37 | { |
44 | static char *reserved[] = | 38 | static char *reserved[] = |
@@ -78,81 +72,51 @@ struct snd_info_private_data { | |||
78 | }; | 72 | }; |
79 | 73 | ||
80 | static int snd_info_version_init(void); | 74 | static int snd_info_version_init(void); |
81 | static int snd_info_version_done(void); | ||
82 | static void snd_info_disconnect(struct snd_info_entry *entry); | 75 | static void snd_info_disconnect(struct snd_info_entry *entry); |
83 | 76 | ||
77 | /* | ||
84 | 78 | ||
85 | /* resize the proc r/w buffer */ | 79 | */ |
86 | static int resize_info_buffer(struct snd_info_buffer *buffer, | ||
87 | unsigned int nsize) | ||
88 | { | ||
89 | char *nbuf; | ||
90 | 80 | ||
91 | nsize = PAGE_ALIGN(nsize); | 81 | static struct snd_info_entry *snd_proc_root; |
92 | nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO); | 82 | struct snd_info_entry *snd_seq_root; |
93 | if (! nbuf) | 83 | EXPORT_SYMBOL(snd_seq_root); |
94 | return -ENOMEM; | ||
95 | 84 | ||
96 | buffer->buffer = nbuf; | 85 | #ifdef CONFIG_SND_OSSEMUL |
97 | buffer->len = nsize; | 86 | struct snd_info_entry *snd_oss_root; |
98 | return 0; | 87 | #endif |
99 | } | ||
100 | 88 | ||
101 | /** | 89 | static int alloc_info_private(struct snd_info_entry *entry, |
102 | * snd_iprintf - printf on the procfs buffer | 90 | struct snd_info_private_data **ret) |
103 | * @buffer: the procfs buffer | ||
104 | * @fmt: the printf format | ||
105 | * | ||
106 | * Outputs the string on the procfs buffer just like printf(). | ||
107 | * | ||
108 | * Return: The size of output string, or a negative error code. | ||
109 | */ | ||
110 | int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) | ||
111 | { | 91 | { |
112 | va_list args; | 92 | struct snd_info_private_data *data; |
113 | int len, res; | ||
114 | int err = 0; | ||
115 | 93 | ||
116 | might_sleep(); | 94 | if (!entry || !entry->p) |
117 | if (buffer->stop || buffer->error) | 95 | return -ENODEV; |
118 | return 0; | 96 | if (!try_module_get(entry->module)) |
119 | len = buffer->len - buffer->size; | 97 | return -EFAULT; |
120 | va_start(args, fmt); | 98 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
121 | for (;;) { | 99 | if (!data) { |
122 | va_list ap; | 100 | module_put(entry->module); |
123 | va_copy(ap, args); | 101 | return -ENOMEM; |
124 | res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap); | ||
125 | va_end(ap); | ||
126 | if (res < len) | ||
127 | break; | ||
128 | err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); | ||
129 | if (err < 0) | ||
130 | break; | ||
131 | len = buffer->len - buffer->size; | ||
132 | } | 102 | } |
133 | va_end(args); | 103 | data->entry = entry; |
134 | 104 | *ret = data; | |
135 | if (err < 0) | 105 | return 0; |
136 | return err; | ||
137 | buffer->curr += res; | ||
138 | buffer->size += res; | ||
139 | return res; | ||
140 | } | 106 | } |
141 | 107 | ||
142 | EXPORT_SYMBOL(snd_iprintf); | 108 | static bool valid_pos(loff_t pos, size_t count) |
109 | { | ||
110 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | ||
111 | return false; | ||
112 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
113 | return false; | ||
114 | return true; | ||
115 | } | ||
143 | 116 | ||
144 | /* | 117 | /* |
145 | 118 | * file ops for binary proc files | |
146 | */ | 119 | */ |
147 | |||
148 | static struct proc_dir_entry *snd_proc_root; | ||
149 | struct snd_info_entry *snd_seq_root; | ||
150 | EXPORT_SYMBOL(snd_seq_root); | ||
151 | |||
152 | #ifdef CONFIG_SND_OSSEMUL | ||
153 | struct snd_info_entry *snd_oss_root; | ||
154 | #endif | ||
155 | |||
156 | static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | 120 | static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) |
157 | { | 121 | { |
158 | struct snd_info_private_data *data; | 122 | struct snd_info_private_data *data; |
@@ -162,17 +126,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | |||
162 | data = file->private_data; | 126 | data = file->private_data; |
163 | entry = data->entry; | 127 | entry = data->entry; |
164 | mutex_lock(&entry->access); | 128 | mutex_lock(&entry->access); |
165 | if (entry->content == SNDRV_INFO_CONTENT_DATA && | 129 | if (entry->c.ops->llseek) { |
166 | entry->c.ops->llseek) { | ||
167 | offset = entry->c.ops->llseek(entry, | 130 | offset = entry->c.ops->llseek(entry, |
168 | data->file_private_data, | 131 | data->file_private_data, |
169 | file, offset, orig); | 132 | file, offset, orig); |
170 | goto out; | 133 | goto out; |
171 | } | 134 | } |
172 | if (entry->content == SNDRV_INFO_CONTENT_DATA) | 135 | |
173 | size = entry->size; | 136 | size = entry->size; |
174 | else | ||
175 | size = 0; | ||
176 | switch (orig) { | 137 | switch (orig) { |
177 | case SEEK_SET: | 138 | case SEEK_SET: |
178 | break; | 139 | break; |
@@ -201,45 +162,20 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | |||
201 | static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, | 162 | static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, |
202 | size_t count, loff_t * offset) | 163 | size_t count, loff_t * offset) |
203 | { | 164 | { |
204 | struct snd_info_private_data *data; | 165 | struct snd_info_private_data *data = file->private_data; |
205 | struct snd_info_entry *entry; | 166 | struct snd_info_entry *entry = data->entry; |
206 | struct snd_info_buffer *buf; | 167 | size_t size; |
207 | size_t size = 0; | ||
208 | loff_t pos; | 168 | loff_t pos; |
209 | 169 | ||
210 | data = file->private_data; | ||
211 | if (snd_BUG_ON(!data)) | ||
212 | return -ENXIO; | ||
213 | pos = *offset; | 170 | pos = *offset; |
214 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | 171 | if (!valid_pos(pos, count)) |
215 | return -EIO; | ||
216 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
217 | return -EIO; | 172 | return -EIO; |
218 | entry = data->entry; | 173 | if (pos >= entry->size) |
219 | switch (entry->content) { | 174 | return 0; |
220 | case SNDRV_INFO_CONTENT_TEXT: | 175 | size = entry->size - pos; |
221 | buf = data->rbuffer; | 176 | size = min(count, size); |
222 | if (buf == NULL) | 177 | size = entry->c.ops->read(entry, data->file_private_data, |
223 | return -EIO; | 178 | file, buffer, size, pos); |
224 | if (pos >= buf->size) | ||
225 | return 0; | ||
226 | size = buf->size - pos; | ||
227 | size = min(count, size); | ||
228 | if (copy_to_user(buffer, buf->buffer + pos, size)) | ||
229 | return -EFAULT; | ||
230 | break; | ||
231 | case SNDRV_INFO_CONTENT_DATA: | ||
232 | if (pos >= entry->size) | ||
233 | return 0; | ||
234 | if (entry->c.ops->read) { | ||
235 | size = entry->size - pos; | ||
236 | size = min(count, size); | ||
237 | size = entry->c.ops->read(entry, | ||
238 | data->file_private_data, | ||
239 | file, buffer, size, pos); | ||
240 | } | ||
241 | break; | ||
242 | } | ||
243 | if ((ssize_t) size > 0) | 179 | if ((ssize_t) size > 0) |
244 | *offset = pos + size; | 180 | *offset = pos + size; |
245 | return size; | 181 | return size; |
@@ -248,347 +184,319 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, | |||
248 | static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, | 184 | static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, |
249 | size_t count, loff_t * offset) | 185 | size_t count, loff_t * offset) |
250 | { | 186 | { |
251 | struct snd_info_private_data *data; | 187 | struct snd_info_private_data *data = file->private_data; |
252 | struct snd_info_entry *entry; | 188 | struct snd_info_entry *entry = data->entry; |
253 | struct snd_info_buffer *buf; | ||
254 | ssize_t size = 0; | 189 | ssize_t size = 0; |
255 | loff_t pos; | 190 | loff_t pos; |
256 | 191 | ||
257 | data = file->private_data; | ||
258 | if (snd_BUG_ON(!data)) | ||
259 | return -ENXIO; | ||
260 | entry = data->entry; | ||
261 | pos = *offset; | 192 | pos = *offset; |
262 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | 193 | if (!valid_pos(pos, count)) |
263 | return -EIO; | ||
264 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
265 | return -EIO; | 194 | return -EIO; |
266 | switch (entry->content) { | 195 | if (count > 0) { |
267 | case SNDRV_INFO_CONTENT_TEXT: | 196 | size_t maxsize = entry->size - pos; |
268 | buf = data->wbuffer; | 197 | count = min(count, maxsize); |
269 | if (buf == NULL) | 198 | size = entry->c.ops->write(entry, data->file_private_data, |
270 | return -EIO; | 199 | file, buffer, count, pos); |
271 | mutex_lock(&entry->access); | ||
272 | if (pos + count >= buf->len) { | ||
273 | if (resize_info_buffer(buf, pos + count)) { | ||
274 | mutex_unlock(&entry->access); | ||
275 | return -ENOMEM; | ||
276 | } | ||
277 | } | ||
278 | if (copy_from_user(buf->buffer + pos, buffer, count)) { | ||
279 | mutex_unlock(&entry->access); | ||
280 | return -EFAULT; | ||
281 | } | ||
282 | buf->size = pos + count; | ||
283 | mutex_unlock(&entry->access); | ||
284 | size = count; | ||
285 | break; | ||
286 | case SNDRV_INFO_CONTENT_DATA: | ||
287 | if (entry->c.ops->write && count > 0) { | ||
288 | size_t maxsize = entry->size - pos; | ||
289 | count = min(count, maxsize); | ||
290 | size = entry->c.ops->write(entry, | ||
291 | data->file_private_data, | ||
292 | file, buffer, count, pos); | ||
293 | } | ||
294 | break; | ||
295 | } | 200 | } |
296 | if ((ssize_t) size > 0) | 201 | if (size > 0) |
297 | *offset = pos + size; | 202 | *offset = pos + size; |
298 | return size; | 203 | return size; |
299 | } | 204 | } |
300 | 205 | ||
301 | static int snd_info_entry_open(struct inode *inode, struct file *file) | 206 | static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait) |
302 | { | 207 | { |
208 | struct snd_info_private_data *data = file->private_data; | ||
209 | struct snd_info_entry *entry = data->entry; | ||
210 | unsigned int mask = 0; | ||
211 | |||
212 | if (entry->c.ops->poll) | ||
213 | return entry->c.ops->poll(entry, | ||
214 | data->file_private_data, | ||
215 | file, wait); | ||
216 | if (entry->c.ops->read) | ||
217 | mask |= POLLIN | POLLRDNORM; | ||
218 | if (entry->c.ops->write) | ||
219 | mask |= POLLOUT | POLLWRNORM; | ||
220 | return mask; | ||
221 | } | ||
222 | |||
223 | static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, | ||
224 | unsigned long arg) | ||
225 | { | ||
226 | struct snd_info_private_data *data = file->private_data; | ||
227 | struct snd_info_entry *entry = data->entry; | ||
228 | |||
229 | if (!entry->c.ops->ioctl) | ||
230 | return -ENOTTY; | ||
231 | return entry->c.ops->ioctl(entry, data->file_private_data, | ||
232 | file, cmd, arg); | ||
233 | } | ||
234 | |||
235 | static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) | ||
236 | { | ||
237 | struct inode *inode = file_inode(file); | ||
238 | struct snd_info_private_data *data; | ||
303 | struct snd_info_entry *entry; | 239 | struct snd_info_entry *entry; |
240 | |||
241 | data = file->private_data; | ||
242 | if (data == NULL) | ||
243 | return 0; | ||
244 | entry = data->entry; | ||
245 | if (!entry->c.ops->mmap) | ||
246 | return -ENXIO; | ||
247 | return entry->c.ops->mmap(entry, data->file_private_data, | ||
248 | inode, file, vma); | ||
249 | } | ||
250 | |||
251 | static int snd_info_entry_open(struct inode *inode, struct file *file) | ||
252 | { | ||
253 | struct snd_info_entry *entry = PDE_DATA(inode); | ||
304 | struct snd_info_private_data *data; | 254 | struct snd_info_private_data *data; |
305 | struct snd_info_buffer *buffer; | ||
306 | int mode, err; | 255 | int mode, err; |
307 | 256 | ||
308 | mutex_lock(&info_mutex); | 257 | mutex_lock(&info_mutex); |
309 | entry = PDE_DATA(inode); | 258 | err = alloc_info_private(entry, &data); |
310 | if (entry == NULL || ! entry->p) { | 259 | if (err < 0) |
311 | mutex_unlock(&info_mutex); | 260 | goto unlock; |
312 | return -ENODEV; | 261 | |
313 | } | ||
314 | if (!try_module_get(entry->module)) { | ||
315 | err = -EFAULT; | ||
316 | goto __error1; | ||
317 | } | ||
318 | mode = file->f_flags & O_ACCMODE; | 262 | mode = file->f_flags & O_ACCMODE; |
319 | if (mode == O_RDONLY || mode == O_RDWR) { | 263 | if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || |
320 | if ((entry->content == SNDRV_INFO_CONTENT_DATA && | 264 | ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { |
321 | entry->c.ops->read == NULL)) { | 265 | err = -ENODEV; |
322 | err = -ENODEV; | 266 | goto error; |
323 | goto __error; | ||
324 | } | ||
325 | } | 267 | } |
326 | if (mode == O_WRONLY || mode == O_RDWR) { | 268 | |
327 | if ((entry->content == SNDRV_INFO_CONTENT_DATA && | 269 | if (entry->c.ops->open) { |
328 | entry->c.ops->write == NULL)) { | 270 | err = entry->c.ops->open(entry, mode, &data->file_private_data); |
329 | err = -ENODEV; | 271 | if (err < 0) |
330 | goto __error; | 272 | goto error; |
331 | } | ||
332 | } | ||
333 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
334 | if (data == NULL) { | ||
335 | err = -ENOMEM; | ||
336 | goto __error; | ||
337 | } | ||
338 | data->entry = entry; | ||
339 | switch (entry->content) { | ||
340 | case SNDRV_INFO_CONTENT_TEXT: | ||
341 | if (mode == O_RDONLY || mode == O_RDWR) { | ||
342 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | ||
343 | if (buffer == NULL) | ||
344 | goto __nomem; | ||
345 | data->rbuffer = buffer; | ||
346 | buffer->len = PAGE_SIZE; | ||
347 | buffer->buffer = kzalloc(buffer->len, GFP_KERNEL); | ||
348 | if (buffer->buffer == NULL) | ||
349 | goto __nomem; | ||
350 | } | ||
351 | if (mode == O_WRONLY || mode == O_RDWR) { | ||
352 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | ||
353 | if (buffer == NULL) | ||
354 | goto __nomem; | ||
355 | data->wbuffer = buffer; | ||
356 | buffer->len = PAGE_SIZE; | ||
357 | buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); | ||
358 | if (buffer->buffer == NULL) | ||
359 | goto __nomem; | ||
360 | } | ||
361 | break; | ||
362 | case SNDRV_INFO_CONTENT_DATA: /* data */ | ||
363 | if (entry->c.ops->open) { | ||
364 | if ((err = entry->c.ops->open(entry, mode, | ||
365 | &data->file_private_data)) < 0) { | ||
366 | kfree(data); | ||
367 | goto __error; | ||
368 | } | ||
369 | } | ||
370 | break; | ||
371 | } | 273 | } |
274 | |||
372 | file->private_data = data; | 275 | file->private_data = data; |
373 | mutex_unlock(&info_mutex); | 276 | mutex_unlock(&info_mutex); |
374 | if (entry->content == SNDRV_INFO_CONTENT_TEXT && | ||
375 | (mode == O_RDONLY || mode == O_RDWR)) { | ||
376 | if (entry->c.text.read) { | ||
377 | mutex_lock(&entry->access); | ||
378 | entry->c.text.read(entry, data->rbuffer); | ||
379 | mutex_unlock(&entry->access); | ||
380 | } | ||
381 | } | ||
382 | return 0; | 277 | return 0; |
383 | 278 | ||
384 | __nomem: | 279 | error: |
385 | if (data->rbuffer) { | ||
386 | kfree(data->rbuffer->buffer); | ||
387 | kfree(data->rbuffer); | ||
388 | } | ||
389 | if (data->wbuffer) { | ||
390 | kfree(data->wbuffer->buffer); | ||
391 | kfree(data->wbuffer); | ||
392 | } | ||
393 | kfree(data); | 280 | kfree(data); |
394 | err = -ENOMEM; | ||
395 | __error: | ||
396 | module_put(entry->module); | 281 | module_put(entry->module); |
397 | __error1: | 282 | unlock: |
398 | mutex_unlock(&info_mutex); | 283 | mutex_unlock(&info_mutex); |
399 | return err; | 284 | return err; |
400 | } | 285 | } |
401 | 286 | ||
402 | static int snd_info_entry_release(struct inode *inode, struct file *file) | 287 | static int snd_info_entry_release(struct inode *inode, struct file *file) |
403 | { | 288 | { |
404 | struct snd_info_entry *entry; | 289 | struct snd_info_private_data *data = file->private_data; |
405 | struct snd_info_private_data *data; | 290 | struct snd_info_entry *entry = data->entry; |
406 | int mode; | ||
407 | 291 | ||
408 | mode = file->f_flags & O_ACCMODE; | 292 | if (entry->c.ops->release) |
409 | data = file->private_data; | 293 | entry->c.ops->release(entry, file->f_flags & O_ACCMODE, |
410 | entry = data->entry; | 294 | data->file_private_data); |
411 | switch (entry->content) { | ||
412 | case SNDRV_INFO_CONTENT_TEXT: | ||
413 | if (data->rbuffer) { | ||
414 | kfree(data->rbuffer->buffer); | ||
415 | kfree(data->rbuffer); | ||
416 | } | ||
417 | if (data->wbuffer) { | ||
418 | if (entry->c.text.write) { | ||
419 | entry->c.text.write(entry, data->wbuffer); | ||
420 | if (data->wbuffer->error) { | ||
421 | if (entry->card) | ||
422 | dev_warn(entry->card->dev, "info: data write error to %s (%i)\n", | ||
423 | entry->name, | ||
424 | data->wbuffer->error); | ||
425 | else | ||
426 | pr_warn("ALSA: info: data write error to %s (%i)\n", | ||
427 | entry->name, | ||
428 | data->wbuffer->error); | ||
429 | } | ||
430 | } | ||
431 | kfree(data->wbuffer->buffer); | ||
432 | kfree(data->wbuffer); | ||
433 | } | ||
434 | break; | ||
435 | case SNDRV_INFO_CONTENT_DATA: | ||
436 | if (entry->c.ops->release) | ||
437 | entry->c.ops->release(entry, mode, | ||
438 | data->file_private_data); | ||
439 | break; | ||
440 | } | ||
441 | module_put(entry->module); | 295 | module_put(entry->module); |
442 | kfree(data); | 296 | kfree(data); |
443 | return 0; | 297 | return 0; |
444 | } | 298 | } |
445 | 299 | ||
446 | static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) | 300 | static const struct file_operations snd_info_entry_operations = |
447 | { | 301 | { |
448 | struct snd_info_private_data *data; | 302 | .owner = THIS_MODULE, |
449 | struct snd_info_entry *entry; | 303 | .llseek = snd_info_entry_llseek, |
450 | unsigned int mask; | 304 | .read = snd_info_entry_read, |
305 | .write = snd_info_entry_write, | ||
306 | .poll = snd_info_entry_poll, | ||
307 | .unlocked_ioctl = snd_info_entry_ioctl, | ||
308 | .mmap = snd_info_entry_mmap, | ||
309 | .open = snd_info_entry_open, | ||
310 | .release = snd_info_entry_release, | ||
311 | }; | ||
451 | 312 | ||
452 | data = file->private_data; | 313 | /* |
453 | if (data == NULL) | 314 | * file ops for text proc files |
454 | return 0; | 315 | */ |
455 | entry = data->entry; | 316 | static ssize_t snd_info_text_entry_write(struct file *file, |
456 | mask = 0; | 317 | const char __user *buffer, |
457 | switch (entry->content) { | 318 | size_t count, loff_t *offset) |
458 | case SNDRV_INFO_CONTENT_DATA: | 319 | { |
459 | if (entry->c.ops->poll) | 320 | struct seq_file *m = file->private_data; |
460 | return entry->c.ops->poll(entry, | 321 | struct snd_info_private_data *data = m->private; |
461 | data->file_private_data, | 322 | struct snd_info_entry *entry = data->entry; |
462 | file, wait); | 323 | struct snd_info_buffer *buf; |
463 | if (entry->c.ops->read) | 324 | loff_t pos; |
464 | mask |= POLLIN | POLLRDNORM; | 325 | size_t next; |
465 | if (entry->c.ops->write) | 326 | int err = 0; |
466 | mask |= POLLOUT | POLLWRNORM; | 327 | |
467 | break; | 328 | pos = *offset; |
329 | if (!valid_pos(pos, count)) | ||
330 | return -EIO; | ||
331 | next = pos + count; | ||
332 | mutex_lock(&entry->access); | ||
333 | buf = data->wbuffer; | ||
334 | if (!buf) { | ||
335 | data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
336 | if (!buf) { | ||
337 | err = -ENOMEM; | ||
338 | goto error; | ||
339 | } | ||
468 | } | 340 | } |
469 | return mask; | 341 | if (next > buf->len) { |
342 | char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), | ||
343 | GFP_KERNEL | __GFP_ZERO); | ||
344 | if (!nbuf) { | ||
345 | err = -ENOMEM; | ||
346 | goto error; | ||
347 | } | ||
348 | buf->buffer = nbuf; | ||
349 | buf->len = PAGE_ALIGN(next); | ||
350 | } | ||
351 | if (copy_from_user(buf->buffer + pos, buffer, count)) { | ||
352 | err = -EFAULT; | ||
353 | goto error; | ||
354 | } | ||
355 | buf->size = next; | ||
356 | error: | ||
357 | mutex_unlock(&entry->access); | ||
358 | if (err < 0) | ||
359 | return err; | ||
360 | *offset = next; | ||
361 | return count; | ||
470 | } | 362 | } |
471 | 363 | ||
472 | static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, | 364 | static int snd_info_seq_show(struct seq_file *seq, void *p) |
473 | unsigned long arg) | ||
474 | { | 365 | { |
475 | struct snd_info_private_data *data; | 366 | struct snd_info_private_data *data = seq->private; |
476 | struct snd_info_entry *entry; | 367 | struct snd_info_entry *entry = data->entry; |
477 | 368 | ||
478 | data = file->private_data; | 369 | if (entry->c.text.read) { |
479 | if (data == NULL) | 370 | data->rbuffer->buffer = (char *)seq; /* XXX hack! */ |
480 | return 0; | 371 | entry->c.text.read(entry, data->rbuffer); |
481 | entry = data->entry; | ||
482 | switch (entry->content) { | ||
483 | case SNDRV_INFO_CONTENT_DATA: | ||
484 | if (entry->c.ops->ioctl) | ||
485 | return entry->c.ops->ioctl(entry, | ||
486 | data->file_private_data, | ||
487 | file, cmd, arg); | ||
488 | break; | ||
489 | } | 372 | } |
490 | return -ENOTTY; | 373 | return 0; |
491 | } | 374 | } |
492 | 375 | ||
493 | static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) | 376 | static int snd_info_text_entry_open(struct inode *inode, struct file *file) |
494 | { | 377 | { |
495 | struct inode *inode = file_inode(file); | 378 | struct snd_info_entry *entry = PDE_DATA(inode); |
496 | struct snd_info_private_data *data; | 379 | struct snd_info_private_data *data; |
497 | struct snd_info_entry *entry; | 380 | int err; |
498 | 381 | ||
499 | data = file->private_data; | 382 | mutex_lock(&info_mutex); |
500 | if (data == NULL) | 383 | err = alloc_info_private(entry, &data); |
501 | return 0; | 384 | if (err < 0) |
502 | entry = data->entry; | 385 | goto unlock; |
503 | switch (entry->content) { | 386 | |
504 | case SNDRV_INFO_CONTENT_DATA: | 387 | data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); |
505 | if (entry->c.ops->mmap) | 388 | if (!data->rbuffer) { |
506 | return entry->c.ops->mmap(entry, | 389 | err = -ENOMEM; |
507 | data->file_private_data, | 390 | goto error; |
508 | inode, file, vma); | ||
509 | break; | ||
510 | } | 391 | } |
511 | return -ENXIO; | 392 | if (entry->size) |
393 | err = single_open_size(file, snd_info_seq_show, data, | ||
394 | entry->size); | ||
395 | else | ||
396 | err = single_open(file, snd_info_seq_show, data); | ||
397 | if (err < 0) | ||
398 | goto error; | ||
399 | mutex_unlock(&info_mutex); | ||
400 | return 0; | ||
401 | |||
402 | error: | ||
403 | kfree(data->rbuffer); | ||
404 | kfree(data); | ||
405 | module_put(entry->module); | ||
406 | unlock: | ||
407 | mutex_unlock(&info_mutex); | ||
408 | return err; | ||
512 | } | 409 | } |
513 | 410 | ||
514 | static const struct file_operations snd_info_entry_operations = | 411 | static int snd_info_text_entry_release(struct inode *inode, struct file *file) |
412 | { | ||
413 | struct seq_file *m = file->private_data; | ||
414 | struct snd_info_private_data *data = m->private; | ||
415 | struct snd_info_entry *entry = data->entry; | ||
416 | |||
417 | if (data->wbuffer && entry->c.text.write) | ||
418 | entry->c.text.write(entry, data->wbuffer); | ||
419 | |||
420 | single_release(inode, file); | ||
421 | kfree(data->rbuffer); | ||
422 | if (data->wbuffer) { | ||
423 | kfree(data->wbuffer->buffer); | ||
424 | kfree(data->wbuffer); | ||
425 | } | ||
426 | |||
427 | module_put(entry->module); | ||
428 | kfree(data); | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static const struct file_operations snd_info_text_entry_ops = | ||
515 | { | 433 | { |
516 | .owner = THIS_MODULE, | 434 | .owner = THIS_MODULE, |
517 | .llseek = snd_info_entry_llseek, | 435 | .open = snd_info_text_entry_open, |
518 | .read = snd_info_entry_read, | 436 | .release = snd_info_text_entry_release, |
519 | .write = snd_info_entry_write, | 437 | .write = snd_info_text_entry_write, |
520 | .poll = snd_info_entry_poll, | 438 | .llseek = seq_lseek, |
521 | .unlocked_ioctl = snd_info_entry_ioctl, | 439 | .read = seq_read, |
522 | .mmap = snd_info_entry_mmap, | ||
523 | .open = snd_info_entry_open, | ||
524 | .release = snd_info_entry_release, | ||
525 | }; | 440 | }; |
526 | 441 | ||
527 | int __init snd_info_init(void) | 442 | static struct snd_info_entry *create_subdir(struct module *mod, |
443 | const char *name) | ||
528 | { | 444 | { |
529 | struct proc_dir_entry *p; | 445 | struct snd_info_entry *entry; |
530 | 446 | ||
531 | p = proc_mkdir("asound", NULL); | 447 | entry = snd_info_create_module_entry(mod, name, NULL); |
532 | if (p == NULL) | 448 | if (!entry) |
449 | return NULL; | ||
450 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
451 | if (snd_info_register(entry) < 0) { | ||
452 | snd_info_free_entry(entry); | ||
453 | return NULL; | ||
454 | } | ||
455 | return entry; | ||
456 | } | ||
457 | |||
458 | static struct snd_info_entry * | ||
459 | snd_info_create_entry(const char *name, struct snd_info_entry *parent); | ||
460 | |||
461 | int __init snd_info_init(void) | ||
462 | { | ||
463 | snd_proc_root = snd_info_create_entry("asound", NULL); | ||
464 | if (!snd_proc_root) | ||
533 | return -ENOMEM; | 465 | return -ENOMEM; |
534 | snd_proc_root = p; | 466 | snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO; |
467 | snd_proc_root->p = proc_mkdir("asound", NULL); | ||
468 | if (!snd_proc_root->p) | ||
469 | goto error; | ||
535 | #ifdef CONFIG_SND_OSSEMUL | 470 | #ifdef CONFIG_SND_OSSEMUL |
536 | { | 471 | snd_oss_root = create_subdir(THIS_MODULE, "oss"); |
537 | struct snd_info_entry *entry; | 472 | if (!snd_oss_root) |
538 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) | 473 | goto error; |
539 | return -ENOMEM; | ||
540 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
541 | if (snd_info_register(entry) < 0) { | ||
542 | snd_info_free_entry(entry); | ||
543 | return -ENOMEM; | ||
544 | } | ||
545 | snd_oss_root = entry; | ||
546 | } | ||
547 | #endif | 474 | #endif |
548 | #if IS_ENABLED(CONFIG_SND_SEQUENCER) | 475 | #if IS_ENABLED(CONFIG_SND_SEQUENCER) |
549 | { | 476 | snd_seq_root = create_subdir(THIS_MODULE, "seq"); |
550 | struct snd_info_entry *entry; | 477 | if (!snd_seq_root) |
551 | if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) | 478 | goto error; |
552 | return -ENOMEM; | ||
553 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
554 | if (snd_info_register(entry) < 0) { | ||
555 | snd_info_free_entry(entry); | ||
556 | return -ENOMEM; | ||
557 | } | ||
558 | snd_seq_root = entry; | ||
559 | } | ||
560 | #endif | 479 | #endif |
561 | snd_info_version_init(); | 480 | if (snd_info_version_init() < 0 || |
562 | snd_minor_info_init(); | 481 | snd_minor_info_init() < 0 || |
563 | snd_minor_info_oss_init(); | 482 | snd_minor_info_oss_init() < 0 || |
564 | snd_card_info_init(); | 483 | snd_card_info_init() < 0 || |
484 | snd_info_minor_register() < 0) | ||
485 | goto error; | ||
565 | return 0; | 486 | return 0; |
487 | |||
488 | error: | ||
489 | snd_info_free_entry(snd_proc_root); | ||
490 | return -ENOMEM; | ||
566 | } | 491 | } |
567 | 492 | ||
568 | int __exit snd_info_done(void) | 493 | int __exit snd_info_done(void) |
569 | { | 494 | { |
570 | snd_card_info_done(); | 495 | snd_info_free_entry(snd_proc_root); |
571 | snd_minor_info_oss_done(); | ||
572 | snd_minor_info_done(); | ||
573 | snd_info_version_done(); | ||
574 | if (snd_proc_root) { | ||
575 | #if IS_ENABLED(CONFIG_SND_SEQUENCER) | ||
576 | snd_info_free_entry(snd_seq_root); | ||
577 | #endif | ||
578 | #ifdef CONFIG_SND_OSSEMUL | ||
579 | snd_info_free_entry(snd_oss_root); | ||
580 | #endif | ||
581 | proc_remove(snd_proc_root); | ||
582 | } | ||
583 | return 0; | 496 | return 0; |
584 | } | 497 | } |
585 | 498 | ||
586 | /* | 499 | /* |
587 | |||
588 | */ | ||
589 | |||
590 | |||
591 | /* | ||
592 | * create a card proc file | 500 | * create a card proc file |
593 | * called from init.c | 501 | * called from init.c |
594 | */ | 502 | */ |
@@ -601,33 +509,58 @@ int snd_info_card_create(struct snd_card *card) | |||
601 | return -ENXIO; | 509 | return -ENXIO; |
602 | 510 | ||
603 | sprintf(str, "card%i", card->number); | 511 | sprintf(str, "card%i", card->number); |
604 | if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) | 512 | entry = create_subdir(card->module, str); |
605 | return -ENOMEM; | 513 | if (!entry) |
606 | entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; | ||
607 | if (snd_info_register(entry) < 0) { | ||
608 | snd_info_free_entry(entry); | ||
609 | return -ENOMEM; | 514 | return -ENOMEM; |
610 | } | ||
611 | card->proc_root = entry; | 515 | card->proc_root = entry; |
612 | return 0; | 516 | return 0; |
613 | } | 517 | } |
614 | 518 | ||
519 | /* register all pending info entries */ | ||
520 | static int snd_info_register_recursive(struct snd_info_entry *entry) | ||
521 | { | ||
522 | struct snd_info_entry *p; | ||
523 | int err; | ||
524 | |||
525 | if (!entry->p) { | ||
526 | err = snd_info_register(entry); | ||
527 | if (err < 0) | ||
528 | return err; | ||
529 | } | ||
530 | |||
531 | list_for_each_entry(p, &entry->children, list) { | ||
532 | err = snd_info_register_recursive(p); | ||
533 | if (err < 0) | ||
534 | return err; | ||
535 | } | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
615 | /* | 540 | /* |
616 | * register the card proc file | 541 | * register the card proc file |
617 | * called from init.c | 542 | * called from init.c |
543 | * can be called multiple times for reinitialization | ||
618 | */ | 544 | */ |
619 | int snd_info_card_register(struct snd_card *card) | 545 | int snd_info_card_register(struct snd_card *card) |
620 | { | 546 | { |
621 | struct proc_dir_entry *p; | 547 | struct proc_dir_entry *p; |
548 | int err; | ||
622 | 549 | ||
623 | if (snd_BUG_ON(!card)) | 550 | if (snd_BUG_ON(!card)) |
624 | return -ENXIO; | 551 | return -ENXIO; |
625 | 552 | ||
553 | err = snd_info_register_recursive(card->proc_root); | ||
554 | if (err < 0) | ||
555 | return err; | ||
556 | |||
626 | if (!strcmp(card->id, card->proc_root->name)) | 557 | if (!strcmp(card->id, card->proc_root->name)) |
627 | return 0; | 558 | return 0; |
628 | 559 | ||
629 | p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); | 560 | if (card->proc_root_link) |
630 | if (p == NULL) | 561 | return 0; |
562 | p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); | ||
563 | if (!p) | ||
631 | return -ENOMEM; | 564 | return -ENOMEM; |
632 | card->proc_root_link = p; | 565 | card->proc_root_link = p; |
633 | return 0; | 566 | return 0; |
@@ -645,7 +578,7 @@ void snd_info_card_id_change(struct snd_card *card) | |||
645 | } | 578 | } |
646 | if (strcmp(card->id, card->proc_root->name)) | 579 | if (strcmp(card->id, card->proc_root->name)) |
647 | card->proc_root_link = proc_symlink(card->id, | 580 | card->proc_root_link = proc_symlink(card->id, |
648 | snd_proc_root, | 581 | snd_proc_root->p, |
649 | card->proc_root->name); | 582 | card->proc_root->name); |
650 | mutex_unlock(&info_mutex); | 583 | mutex_unlock(&info_mutex); |
651 | } | 584 | } |
@@ -753,9 +686,10 @@ const char *snd_info_get_str(char *dest, const char *src, int len) | |||
753 | 686 | ||
754 | EXPORT_SYMBOL(snd_info_get_str); | 687 | EXPORT_SYMBOL(snd_info_get_str); |
755 | 688 | ||
756 | /** | 689 | /* |
757 | * snd_info_create_entry - create an info entry | 690 | * snd_info_create_entry - create an info entry |
758 | * @name: the proc file name | 691 | * @name: the proc file name |
692 | * @parent: the parent directory | ||
759 | * | 693 | * |
760 | * Creates an info entry with the given file name and initializes as | 694 | * Creates an info entry with the given file name and initializes as |
761 | * the default state. | 695 | * the default state. |
@@ -765,7 +699,8 @@ EXPORT_SYMBOL(snd_info_get_str); | |||
765 | * | 699 | * |
766 | * Return: The pointer of the new instance, or %NULL on failure. | 700 | * Return: The pointer of the new instance, or %NULL on failure. |
767 | */ | 701 | */ |
768 | static struct snd_info_entry *snd_info_create_entry(const char *name) | 702 | static struct snd_info_entry * |
703 | snd_info_create_entry(const char *name, struct snd_info_entry *parent) | ||
769 | { | 704 | { |
770 | struct snd_info_entry *entry; | 705 | struct snd_info_entry *entry; |
771 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | 706 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
@@ -781,6 +716,9 @@ static struct snd_info_entry *snd_info_create_entry(const char *name) | |||
781 | mutex_init(&entry->access); | 716 | mutex_init(&entry->access); |
782 | INIT_LIST_HEAD(&entry->children); | 717 | INIT_LIST_HEAD(&entry->children); |
783 | INIT_LIST_HEAD(&entry->list); | 718 | INIT_LIST_HEAD(&entry->list); |
719 | entry->parent = parent; | ||
720 | if (parent) | ||
721 | list_add_tail(&entry->list, &parent->children); | ||
784 | return entry; | 722 | return entry; |
785 | } | 723 | } |
786 | 724 | ||
@@ -798,11 +736,9 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module, | |||
798 | const char *name, | 736 | const char *name, |
799 | struct snd_info_entry *parent) | 737 | struct snd_info_entry *parent) |
800 | { | 738 | { |
801 | struct snd_info_entry *entry = snd_info_create_entry(name); | 739 | struct snd_info_entry *entry = snd_info_create_entry(name, parent); |
802 | if (entry) { | 740 | if (entry) |
803 | entry->module = module; | 741 | entry->module = module; |
804 | entry->parent = parent; | ||
805 | } | ||
806 | return entry; | 742 | return entry; |
807 | } | 743 | } |
808 | 744 | ||
@@ -822,11 +758,10 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, | |||
822 | const char *name, | 758 | const char *name, |
823 | struct snd_info_entry * parent) | 759 | struct snd_info_entry * parent) |
824 | { | 760 | { |
825 | struct snd_info_entry *entry = snd_info_create_entry(name); | 761 | struct snd_info_entry *entry = snd_info_create_entry(name, parent); |
826 | if (entry) { | 762 | if (entry) { |
827 | entry->module = card->module; | 763 | entry->module = card->module; |
828 | entry->card = card; | 764 | entry->card = card; |
829 | entry->parent = parent; | ||
830 | } | 765 | } |
831 | return entry; | 766 | return entry; |
832 | } | 767 | } |
@@ -835,95 +770,39 @@ EXPORT_SYMBOL(snd_info_create_card_entry); | |||
835 | 770 | ||
836 | static void snd_info_disconnect(struct snd_info_entry *entry) | 771 | static void snd_info_disconnect(struct snd_info_entry *entry) |
837 | { | 772 | { |
838 | struct list_head *p, *n; | 773 | struct snd_info_entry *p; |
839 | struct proc_dir_entry *root; | ||
840 | |||
841 | list_for_each_safe(p, n, &entry->children) { | ||
842 | snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); | ||
843 | } | ||
844 | 774 | ||
845 | if (! entry->p) | 775 | if (!entry->p) |
846 | return; | 776 | return; |
847 | list_del_init(&entry->list); | 777 | list_for_each_entry(p, &entry->children, list) |
848 | root = entry->parent == NULL ? snd_proc_root : entry->parent->p; | 778 | snd_info_disconnect(p); |
849 | snd_BUG_ON(!root); | ||
850 | proc_remove(entry->p); | 779 | proc_remove(entry->p); |
851 | entry->p = NULL; | 780 | entry->p = NULL; |
852 | } | 781 | } |
853 | 782 | ||
854 | static int snd_info_dev_free_entry(struct snd_device *device) | ||
855 | { | ||
856 | struct snd_info_entry *entry = device->device_data; | ||
857 | snd_info_free_entry(entry); | ||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | static int snd_info_dev_register_entry(struct snd_device *device) | ||
862 | { | ||
863 | struct snd_info_entry *entry = device->device_data; | ||
864 | return snd_info_register(entry); | ||
865 | } | ||
866 | |||
867 | /** | ||
868 | * snd_card_proc_new - create an info entry for the given card | ||
869 | * @card: the card instance | ||
870 | * @name: the file name | ||
871 | * @entryp: the pointer to store the new info entry | ||
872 | * | ||
873 | * Creates a new info entry and assigns it to the given card. | ||
874 | * Unlike snd_info_create_card_entry(), this function registers the | ||
875 | * info entry as an ALSA device component, so that it can be | ||
876 | * unregistered/released without explicit call. | ||
877 | * Also, you don't have to register this entry via snd_info_register(), | ||
878 | * since this will be registered by snd_card_register() automatically. | ||
879 | * | ||
880 | * The parent is assumed as card->proc_root. | ||
881 | * | ||
882 | * For releasing this entry, use snd_device_free() instead of | ||
883 | * snd_info_free_entry(). | ||
884 | * | ||
885 | * Return: Zero if successful, or a negative error code on failure. | ||
886 | */ | ||
887 | int snd_card_proc_new(struct snd_card *card, const char *name, | ||
888 | struct snd_info_entry **entryp) | ||
889 | { | ||
890 | static struct snd_device_ops ops = { | ||
891 | .dev_free = snd_info_dev_free_entry, | ||
892 | .dev_register = snd_info_dev_register_entry, | ||
893 | /* disconnect is done via snd_info_card_disconnect() */ | ||
894 | }; | ||
895 | struct snd_info_entry *entry; | ||
896 | int err; | ||
897 | |||
898 | entry = snd_info_create_card_entry(card, name, card->proc_root); | ||
899 | if (! entry) | ||
900 | return -ENOMEM; | ||
901 | if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { | ||
902 | snd_info_free_entry(entry); | ||
903 | return err; | ||
904 | } | ||
905 | if (entryp) | ||
906 | *entryp = entry; | ||
907 | return 0; | ||
908 | } | ||
909 | |||
910 | EXPORT_SYMBOL(snd_card_proc_new); | ||
911 | |||
912 | /** | 783 | /** |
913 | * snd_info_free_entry - release the info entry | 784 | * snd_info_free_entry - release the info entry |
914 | * @entry: the info entry | 785 | * @entry: the info entry |
915 | * | 786 | * |
916 | * Releases the info entry. Don't call this after registered. | 787 | * Releases the info entry. |
917 | */ | 788 | */ |
918 | void snd_info_free_entry(struct snd_info_entry * entry) | 789 | void snd_info_free_entry(struct snd_info_entry * entry) |
919 | { | 790 | { |
920 | if (entry == NULL) | 791 | struct snd_info_entry *p, *n; |
792 | |||
793 | if (!entry) | ||
921 | return; | 794 | return; |
922 | if (entry->p) { | 795 | if (entry->p) { |
923 | mutex_lock(&info_mutex); | 796 | mutex_lock(&info_mutex); |
924 | snd_info_disconnect(entry); | 797 | snd_info_disconnect(entry); |
925 | mutex_unlock(&info_mutex); | 798 | mutex_unlock(&info_mutex); |
926 | } | 799 | } |
800 | |||
801 | /* free all children at first */ | ||
802 | list_for_each_entry_safe(p, n, &entry->children, list) | ||
803 | snd_info_free_entry(p); | ||
804 | |||
805 | list_del(&entry->list); | ||
927 | kfree(entry->name); | 806 | kfree(entry->name); |
928 | if (entry->private_free) | 807 | if (entry->private_free) |
929 | entry->private_free(entry); | 808 | entry->private_free(entry); |
@@ -946,7 +825,7 @@ int snd_info_register(struct snd_info_entry * entry) | |||
946 | 825 | ||
947 | if (snd_BUG_ON(!entry)) | 826 | if (snd_BUG_ON(!entry)) |
948 | return -ENXIO; | 827 | return -ENXIO; |
949 | root = entry->parent == NULL ? snd_proc_root : entry->parent->p; | 828 | root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; |
950 | mutex_lock(&info_mutex); | 829 | mutex_lock(&info_mutex); |
951 | if (S_ISDIR(entry->mode)) { | 830 | if (S_ISDIR(entry->mode)) { |
952 | p = proc_mkdir_mode(entry->name, entry->mode, root); | 831 | p = proc_mkdir_mode(entry->name, entry->mode, root); |
@@ -955,8 +834,13 @@ int snd_info_register(struct snd_info_entry * entry) | |||
955 | return -ENOMEM; | 834 | return -ENOMEM; |
956 | } | 835 | } |
957 | } else { | 836 | } else { |
837 | const struct file_operations *ops; | ||
838 | if (entry->content == SNDRV_INFO_CONTENT_DATA) | ||
839 | ops = &snd_info_entry_operations; | ||
840 | else | ||
841 | ops = &snd_info_text_entry_ops; | ||
958 | p = proc_create_data(entry->name, entry->mode, root, | 842 | p = proc_create_data(entry->name, entry->mode, root, |
959 | &snd_info_entry_operations, entry); | 843 | ops, entry); |
960 | if (!p) { | 844 | if (!p) { |
961 | mutex_unlock(&info_mutex); | 845 | mutex_unlock(&info_mutex); |
962 | return -ENOMEM; | 846 | return -ENOMEM; |
@@ -964,8 +848,6 @@ int snd_info_register(struct snd_info_entry * entry) | |||
964 | proc_set_size(p, entry->size); | 848 | proc_set_size(p, entry->size); |
965 | } | 849 | } |
966 | entry->p = p; | 850 | entry->p = p; |
967 | if (entry->parent) | ||
968 | list_add_tail(&entry->list, &entry->parent->children); | ||
969 | mutex_unlock(&info_mutex); | 851 | mutex_unlock(&info_mutex); |
970 | return 0; | 852 | return 0; |
971 | } | 853 | } |
@@ -976,8 +858,6 @@ EXPORT_SYMBOL(snd_info_register); | |||
976 | 858 | ||
977 | */ | 859 | */ |
978 | 860 | ||
979 | static struct snd_info_entry *snd_info_version_entry; | ||
980 | |||
981 | static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) | 861 | static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) |
982 | { | 862 | { |
983 | snd_iprintf(buffer, | 863 | snd_iprintf(buffer, |
@@ -993,18 +873,5 @@ static int __init snd_info_version_init(void) | |||
993 | if (entry == NULL) | 873 | if (entry == NULL) |
994 | return -ENOMEM; | 874 | return -ENOMEM; |
995 | entry->c.text.read = snd_info_version_read; | 875 | entry->c.text.read = snd_info_version_read; |
996 | if (snd_info_register(entry) < 0) { | 876 | return snd_info_register(entry); /* freed in error path */ |
997 | snd_info_free_entry(entry); | ||
998 | return -ENOMEM; | ||
999 | } | ||
1000 | snd_info_version_entry = entry; | ||
1001 | return 0; | ||
1002 | } | 877 | } |
1003 | |||
1004 | static int __exit snd_info_version_done(void) | ||
1005 | { | ||
1006 | snd_info_free_entry(snd_info_version_entry); | ||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1010 | #endif /* CONFIG_PROC_FS */ | ||