diff options
Diffstat (limited to 'sound/core/info.c')
-rw-r--r-- | sound/core/info.c | 569 |
1 files changed, 248 insertions, 321 deletions
diff --git a/sound/core/info.c b/sound/core/info.c index 9f404e965ea2..8c1275f0fcbd 100644 --- a/sound/core/info.c +++ b/sound/core/info.c | |||
@@ -81,66 +81,6 @@ static int snd_info_version_init(void); | |||
81 | static int snd_info_version_done(void); | 81 | static int snd_info_version_done(void); |
82 | static void snd_info_disconnect(struct snd_info_entry *entry); | 82 | static void snd_info_disconnect(struct snd_info_entry *entry); |
83 | 83 | ||
84 | |||
85 | /* resize the proc r/w buffer */ | ||
86 | static int resize_info_buffer(struct snd_info_buffer *buffer, | ||
87 | unsigned int nsize) | ||
88 | { | ||
89 | char *nbuf; | ||
90 | |||
91 | nsize = PAGE_ALIGN(nsize); | ||
92 | nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO); | ||
93 | if (! nbuf) | ||
94 | return -ENOMEM; | ||
95 | |||
96 | buffer->buffer = nbuf; | ||
97 | buffer->len = nsize; | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * snd_iprintf - printf on the procfs buffer | ||
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 | { | ||
112 | va_list args; | ||
113 | int len, res; | ||
114 | int err = 0; | ||
115 | |||
116 | might_sleep(); | ||
117 | if (buffer->stop || buffer->error) | ||
118 | return 0; | ||
119 | len = buffer->len - buffer->size; | ||
120 | va_start(args, fmt); | ||
121 | for (;;) { | ||
122 | va_list ap; | ||
123 | va_copy(ap, args); | ||
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 | } | ||
133 | va_end(args); | ||
134 | |||
135 | if (err < 0) | ||
136 | return err; | ||
137 | buffer->curr += res; | ||
138 | buffer->size += res; | ||
139 | return res; | ||
140 | } | ||
141 | |||
142 | EXPORT_SYMBOL(snd_iprintf); | ||
143 | |||
144 | /* | 84 | /* |
145 | 85 | ||
146 | */ | 86 | */ |
@@ -153,6 +93,37 @@ EXPORT_SYMBOL(snd_seq_root); | |||
153 | struct snd_info_entry *snd_oss_root; | 93 | struct snd_info_entry *snd_oss_root; |
154 | #endif | 94 | #endif |
155 | 95 | ||
96 | static int alloc_info_private(struct snd_info_entry *entry, | ||
97 | struct snd_info_private_data **ret) | ||
98 | { | ||
99 | struct snd_info_private_data *data; | ||
100 | |||
101 | if (!entry || !entry->p) | ||
102 | return -ENODEV; | ||
103 | if (!try_module_get(entry->module)) | ||
104 | return -EFAULT; | ||
105 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
106 | if (!data) { | ||
107 | module_put(entry->module); | ||
108 | return -ENOMEM; | ||
109 | } | ||
110 | data->entry = entry; | ||
111 | *ret = data; | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static bool valid_pos(loff_t pos, size_t count) | ||
116 | { | ||
117 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | ||
118 | return false; | ||
119 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
120 | return false; | ||
121 | return true; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * file ops for binary proc files | ||
126 | */ | ||
156 | static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | 127 | static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) |
157 | { | 128 | { |
158 | struct snd_info_private_data *data; | 129 | struct snd_info_private_data *data; |
@@ -162,17 +133,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) | |||
162 | data = file->private_data; | 133 | data = file->private_data; |
163 | entry = data->entry; | 134 | entry = data->entry; |
164 | mutex_lock(&entry->access); | 135 | mutex_lock(&entry->access); |
165 | if (entry->content == SNDRV_INFO_CONTENT_DATA && | 136 | if (entry->c.ops->llseek) { |
166 | entry->c.ops->llseek) { | ||
167 | offset = entry->c.ops->llseek(entry, | 137 | offset = entry->c.ops->llseek(entry, |
168 | data->file_private_data, | 138 | data->file_private_data, |
169 | file, offset, orig); | 139 | file, offset, orig); |
170 | goto out; | 140 | goto out; |
171 | } | 141 | } |
172 | if (entry->content == SNDRV_INFO_CONTENT_DATA) | 142 | |
173 | size = entry->size; | 143 | size = entry->size; |
174 | else | ||
175 | size = 0; | ||
176 | switch (orig) { | 144 | switch (orig) { |
177 | case SEEK_SET: | 145 | case SEEK_SET: |
178 | break; | 146 | break; |
@@ -201,45 +169,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, | 169 | static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, |
202 | size_t count, loff_t * offset) | 170 | size_t count, loff_t * offset) |
203 | { | 171 | { |
204 | struct snd_info_private_data *data; | 172 | struct snd_info_private_data *data = file->private_data; |
205 | struct snd_info_entry *entry; | 173 | struct snd_info_entry *entry = data->entry; |
206 | struct snd_info_buffer *buf; | 174 | size_t size; |
207 | size_t size = 0; | ||
208 | loff_t pos; | 175 | loff_t pos; |
209 | 176 | ||
210 | data = file->private_data; | ||
211 | if (snd_BUG_ON(!data)) | ||
212 | return -ENXIO; | ||
213 | pos = *offset; | 177 | pos = *offset; |
214 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | 178 | if (!valid_pos(pos, count)) |
215 | return -EIO; | ||
216 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | ||
217 | return -EIO; | 179 | return -EIO; |
218 | entry = data->entry; | 180 | if (pos >= entry->size) |
219 | switch (entry->content) { | 181 | return 0; |
220 | case SNDRV_INFO_CONTENT_TEXT: | 182 | size = entry->size - pos; |
221 | buf = data->rbuffer; | 183 | size = min(count, size); |
222 | if (buf == NULL) | 184 | size = entry->c.ops->read(entry, data->file_private_data, |
223 | return -EIO; | 185 | 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) | 186 | if ((ssize_t) size > 0) |
244 | *offset = pos + size; | 187 | *offset = pos + size; |
245 | return size; | 188 | return size; |
@@ -248,280 +191,259 @@ 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, | 191 | static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, |
249 | size_t count, loff_t * offset) | 192 | size_t count, loff_t * offset) |
250 | { | 193 | { |
251 | struct snd_info_private_data *data; | 194 | struct snd_info_private_data *data = file->private_data; |
252 | struct snd_info_entry *entry; | 195 | struct snd_info_entry *entry = data->entry; |
253 | struct snd_info_buffer *buf; | ||
254 | ssize_t size = 0; | 196 | ssize_t size = 0; |
255 | loff_t pos; | 197 | loff_t pos; |
256 | 198 | ||
257 | data = file->private_data; | ||
258 | if (snd_BUG_ON(!data)) | ||
259 | return -ENXIO; | ||
260 | entry = data->entry; | ||
261 | pos = *offset; | 199 | pos = *offset; |
262 | if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) | 200 | if (!valid_pos(pos, count)) |
263 | return -EIO; | 201 | return -EIO; |
264 | if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) | 202 | if (count > 0) { |
265 | return -EIO; | 203 | size_t maxsize = entry->size - pos; |
266 | switch (entry->content) { | 204 | count = min(count, maxsize); |
267 | case SNDRV_INFO_CONTENT_TEXT: | 205 | size = entry->c.ops->write(entry, data->file_private_data, |
268 | buf = data->wbuffer; | 206 | file, buffer, count, pos); |
269 | if (buf == NULL) | ||
270 | return -EIO; | ||
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 | } | 207 | } |
296 | if ((ssize_t) size > 0) | 208 | if (size > 0) |
297 | *offset = pos + size; | 209 | *offset = pos + size; |
298 | return size; | 210 | return size; |
299 | } | 211 | } |
300 | 212 | ||
301 | static int snd_info_entry_open(struct inode *inode, struct file *file) | 213 | static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait) |
214 | { | ||
215 | struct snd_info_private_data *data = file->private_data; | ||
216 | struct snd_info_entry *entry = data->entry; | ||
217 | unsigned int mask = 0; | ||
218 | |||
219 | if (entry->c.ops->poll) | ||
220 | return entry->c.ops->poll(entry, | ||
221 | data->file_private_data, | ||
222 | file, wait); | ||
223 | if (entry->c.ops->read) | ||
224 | mask |= POLLIN | POLLRDNORM; | ||
225 | if (entry->c.ops->write) | ||
226 | mask |= POLLOUT | POLLWRNORM; | ||
227 | return mask; | ||
228 | } | ||
229 | |||
230 | static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, | ||
231 | unsigned long arg) | ||
232 | { | ||
233 | struct snd_info_private_data *data = file->private_data; | ||
234 | struct snd_info_entry *entry = data->entry; | ||
235 | |||
236 | if (!entry->c.ops->ioctl) | ||
237 | return -ENOTTY; | ||
238 | return entry->c.ops->ioctl(entry, data->file_private_data, | ||
239 | file, cmd, arg); | ||
240 | } | ||
241 | |||
242 | static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) | ||
302 | { | 243 | { |
244 | struct inode *inode = file_inode(file); | ||
245 | struct snd_info_private_data *data; | ||
303 | struct snd_info_entry *entry; | 246 | struct snd_info_entry *entry; |
247 | |||
248 | data = file->private_data; | ||
249 | if (data == NULL) | ||
250 | return 0; | ||
251 | entry = data->entry; | ||
252 | if (!entry->c.ops->mmap) | ||
253 | return -ENXIO; | ||
254 | return entry->c.ops->mmap(entry, data->file_private_data, | ||
255 | inode, file, vma); | ||
256 | } | ||
257 | |||
258 | static int snd_info_entry_open(struct inode *inode, struct file *file) | ||
259 | { | ||
260 | struct snd_info_entry *entry = PDE_DATA(inode); | ||
304 | struct snd_info_private_data *data; | 261 | struct snd_info_private_data *data; |
305 | struct snd_info_buffer *buffer; | ||
306 | int mode, err; | 262 | int mode, err; |
307 | 263 | ||
308 | mutex_lock(&info_mutex); | 264 | mutex_lock(&info_mutex); |
309 | entry = PDE_DATA(inode); | 265 | err = alloc_info_private(entry, &data); |
310 | if (entry == NULL || ! entry->p) { | 266 | if (err < 0) |
311 | mutex_unlock(&info_mutex); | 267 | goto unlock; |
312 | return -ENODEV; | 268 | |
313 | } | ||
314 | if (!try_module_get(entry->module)) { | ||
315 | err = -EFAULT; | ||
316 | goto __error1; | ||
317 | } | ||
318 | mode = file->f_flags & O_ACCMODE; | 269 | mode = file->f_flags & O_ACCMODE; |
319 | if (mode == O_RDONLY || mode == O_RDWR) { | 270 | if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || |
320 | if ((entry->content == SNDRV_INFO_CONTENT_DATA && | 271 | ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { |
321 | entry->c.ops->read == NULL)) { | 272 | err = -ENODEV; |
322 | err = -ENODEV; | 273 | goto error; |
323 | goto __error; | ||
324 | } | ||
325 | } | ||
326 | if (mode == O_WRONLY || mode == O_RDWR) { | ||
327 | if ((entry->content == SNDRV_INFO_CONTENT_DATA && | ||
328 | entry->c.ops->write == NULL)) { | ||
329 | err = -ENODEV; | ||
330 | goto __error; | ||
331 | } | ||
332 | } | 274 | } |
333 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 275 | |
334 | if (data == NULL) { | 276 | if (entry->c.ops->open) { |
335 | err = -ENOMEM; | 277 | err = entry->c.ops->open(entry, mode, &data->file_private_data); |
336 | goto __error; | 278 | if (err < 0) |
337 | } | 279 | goto error; |
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 | } | 280 | } |
281 | |||
372 | file->private_data = data; | 282 | file->private_data = data; |
373 | mutex_unlock(&info_mutex); | 283 | 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; | 284 | return 0; |
383 | 285 | ||
384 | __nomem: | 286 | 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); | 287 | kfree(data); |
394 | err = -ENOMEM; | ||
395 | __error: | ||
396 | module_put(entry->module); | 288 | module_put(entry->module); |
397 | __error1: | 289 | unlock: |
398 | mutex_unlock(&info_mutex); | 290 | mutex_unlock(&info_mutex); |
399 | return err; | 291 | return err; |
400 | } | 292 | } |
401 | 293 | ||
402 | static int snd_info_entry_release(struct inode *inode, struct file *file) | 294 | static int snd_info_entry_release(struct inode *inode, struct file *file) |
403 | { | 295 | { |
404 | struct snd_info_entry *entry; | 296 | struct snd_info_private_data *data = file->private_data; |
405 | struct snd_info_private_data *data; | 297 | struct snd_info_entry *entry = data->entry; |
406 | int mode; | ||
407 | 298 | ||
408 | mode = file->f_flags & O_ACCMODE; | 299 | if (entry->c.ops->release) |
409 | data = file->private_data; | 300 | entry->c.ops->release(entry, file->f_flags & O_ACCMODE, |
410 | entry = data->entry; | 301 | 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); | 302 | module_put(entry->module); |
442 | kfree(data); | 303 | kfree(data); |
443 | return 0; | 304 | return 0; |
444 | } | 305 | } |
445 | 306 | ||
446 | static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) | 307 | static const struct file_operations snd_info_entry_operations = |
447 | { | 308 | { |
448 | struct snd_info_private_data *data; | 309 | .owner = THIS_MODULE, |
449 | struct snd_info_entry *entry; | 310 | .llseek = snd_info_entry_llseek, |
450 | unsigned int mask; | 311 | .read = snd_info_entry_read, |
312 | .write = snd_info_entry_write, | ||
313 | .poll = snd_info_entry_poll, | ||
314 | .unlocked_ioctl = snd_info_entry_ioctl, | ||
315 | .mmap = snd_info_entry_mmap, | ||
316 | .open = snd_info_entry_open, | ||
317 | .release = snd_info_entry_release, | ||
318 | }; | ||
451 | 319 | ||
452 | data = file->private_data; | 320 | /* |
453 | if (data == NULL) | 321 | * file ops for text proc files |
454 | return 0; | 322 | */ |
455 | entry = data->entry; | 323 | static ssize_t snd_info_text_entry_write(struct file *file, |
456 | mask = 0; | 324 | const char __user *buffer, |
457 | switch (entry->content) { | 325 | size_t count, loff_t *offset) |
458 | case SNDRV_INFO_CONTENT_DATA: | 326 | { |
459 | if (entry->c.ops->poll) | 327 | struct seq_file *m = file->private_data; |
460 | return entry->c.ops->poll(entry, | 328 | struct snd_info_private_data *data = m->private; |
461 | data->file_private_data, | 329 | struct snd_info_entry *entry = data->entry; |
462 | file, wait); | 330 | struct snd_info_buffer *buf; |
463 | if (entry->c.ops->read) | 331 | loff_t pos; |
464 | mask |= POLLIN | POLLRDNORM; | 332 | size_t next; |
465 | if (entry->c.ops->write) | 333 | int err = 0; |
466 | mask |= POLLOUT | POLLWRNORM; | 334 | |
467 | break; | 335 | pos = *offset; |
336 | if (!valid_pos(pos, count)) | ||
337 | return -EIO; | ||
338 | next = pos + count; | ||
339 | mutex_lock(&entry->access); | ||
340 | buf = data->wbuffer; | ||
341 | if (!buf) { | ||
342 | data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
343 | if (!buf) { | ||
344 | err = -ENOMEM; | ||
345 | goto error; | ||
346 | } | ||
468 | } | 347 | } |
469 | return mask; | 348 | if (next > buf->len) { |
349 | char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), | ||
350 | GFP_KERNEL | __GFP_ZERO); | ||
351 | if (!nbuf) { | ||
352 | err = -ENOMEM; | ||
353 | goto error; | ||
354 | } | ||
355 | buf->buffer = nbuf; | ||
356 | buf->len = PAGE_ALIGN(next); | ||
357 | } | ||
358 | if (copy_from_user(buf->buffer + pos, buffer, count)) { | ||
359 | err = -EFAULT; | ||
360 | goto error; | ||
361 | } | ||
362 | buf->size = next; | ||
363 | error: | ||
364 | mutex_unlock(&entry->access); | ||
365 | if (err < 0) | ||
366 | return err; | ||
367 | *offset = next; | ||
368 | return count; | ||
470 | } | 369 | } |
471 | 370 | ||
472 | static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, | 371 | static int snd_info_seq_show(struct seq_file *seq, void *p) |
473 | unsigned long arg) | ||
474 | { | 372 | { |
475 | struct snd_info_private_data *data; | 373 | struct snd_info_private_data *data = seq->private; |
476 | struct snd_info_entry *entry; | 374 | struct snd_info_entry *entry = data->entry; |
477 | 375 | ||
478 | data = file->private_data; | 376 | if (entry->c.text.read) { |
479 | if (data == NULL) | 377 | data->rbuffer->buffer = (char *)seq; /* XXX hack! */ |
480 | return 0; | 378 | 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 | } | 379 | } |
490 | return -ENOTTY; | 380 | return 0; |
491 | } | 381 | } |
492 | 382 | ||
493 | static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) | 383 | static int snd_info_text_entry_open(struct inode *inode, struct file *file) |
494 | { | 384 | { |
495 | struct inode *inode = file_inode(file); | 385 | struct snd_info_entry *entry = PDE_DATA(inode); |
496 | struct snd_info_private_data *data; | 386 | struct snd_info_private_data *data; |
497 | struct snd_info_entry *entry; | 387 | int err; |
498 | 388 | ||
499 | data = file->private_data; | 389 | mutex_lock(&info_mutex); |
500 | if (data == NULL) | 390 | err = alloc_info_private(entry, &data); |
501 | return 0; | 391 | if (err < 0) |
502 | entry = data->entry; | 392 | goto unlock; |
503 | switch (entry->content) { | 393 | |
504 | case SNDRV_INFO_CONTENT_DATA: | 394 | data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); |
505 | if (entry->c.ops->mmap) | 395 | if (!data->rbuffer) { |
506 | return entry->c.ops->mmap(entry, | 396 | err = -ENOMEM; |
507 | data->file_private_data, | 397 | goto error; |
508 | inode, file, vma); | 398 | } |
509 | break; | 399 | if (entry->size) |
400 | err = single_open_size(file, snd_info_seq_show, data, | ||
401 | entry->size); | ||
402 | else | ||
403 | err = single_open(file, snd_info_seq_show, data); | ||
404 | if (err < 0) | ||
405 | goto error; | ||
406 | mutex_unlock(&info_mutex); | ||
407 | return 0; | ||
408 | |||
409 | error: | ||
410 | kfree(data->rbuffer); | ||
411 | kfree(data); | ||
412 | module_put(entry->module); | ||
413 | unlock: | ||
414 | mutex_unlock(&info_mutex); | ||
415 | return err; | ||
416 | } | ||
417 | |||
418 | static int snd_info_text_entry_release(struct inode *inode, struct file *file) | ||
419 | { | ||
420 | struct seq_file *m = file->private_data; | ||
421 | struct snd_info_private_data *data = m->private; | ||
422 | struct snd_info_entry *entry = data->entry; | ||
423 | |||
424 | if (data->wbuffer && entry->c.text.write) | ||
425 | entry->c.text.write(entry, data->wbuffer); | ||
426 | |||
427 | single_release(inode, file); | ||
428 | kfree(data->rbuffer); | ||
429 | if (data->wbuffer) { | ||
430 | kfree(data->wbuffer->buffer); | ||
431 | kfree(data->wbuffer); | ||
510 | } | 432 | } |
511 | return -ENXIO; | 433 | |
434 | module_put(entry->module); | ||
435 | kfree(data); | ||
436 | return 0; | ||
512 | } | 437 | } |
513 | 438 | ||
514 | static const struct file_operations snd_info_entry_operations = | 439 | static const struct file_operations snd_info_text_entry_ops = |
515 | { | 440 | { |
516 | .owner = THIS_MODULE, | 441 | .owner = THIS_MODULE, |
517 | .llseek = snd_info_entry_llseek, | 442 | .open = snd_info_text_entry_open, |
518 | .read = snd_info_entry_read, | 443 | .release = snd_info_text_entry_release, |
519 | .write = snd_info_entry_write, | 444 | .write = snd_info_text_entry_write, |
520 | .poll = snd_info_entry_poll, | 445 | .llseek = seq_lseek, |
521 | .unlocked_ioctl = snd_info_entry_ioctl, | 446 | .read = seq_read, |
522 | .mmap = snd_info_entry_mmap, | ||
523 | .open = snd_info_entry_open, | ||
524 | .release = snd_info_entry_release, | ||
525 | }; | 447 | }; |
526 | 448 | ||
527 | int __init snd_info_init(void) | 449 | int __init snd_info_init(void) |
@@ -955,8 +877,13 @@ int snd_info_register(struct snd_info_entry * entry) | |||
955 | return -ENOMEM; | 877 | return -ENOMEM; |
956 | } | 878 | } |
957 | } else { | 879 | } else { |
880 | const struct file_operations *ops; | ||
881 | if (entry->content == SNDRV_INFO_CONTENT_DATA) | ||
882 | ops = &snd_info_entry_operations; | ||
883 | else | ||
884 | ops = &snd_info_text_entry_ops; | ||
958 | p = proc_create_data(entry->name, entry->mode, root, | 885 | p = proc_create_data(entry->name, entry->mode, root, |
959 | &snd_info_entry_operations, entry); | 886 | ops, entry); |
960 | if (!p) { | 887 | if (!p) { |
961 | mutex_unlock(&info_mutex); | 888 | mutex_unlock(&info_mutex); |
962 | return -ENOMEM; | 889 | return -ENOMEM; |