Root/
1 | /* |
2 | * compress_core.c - compress offload core |
3 | * |
4 | * Copyright (C) 2011 Intel Corporation |
5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> |
6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; version 2 of the License. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
21 | * |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
23 | * |
24 | */ |
25 | #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ |
26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) |
27 | |
28 | #include <linux/file.h> |
29 | #include <linux/fs.h> |
30 | #include <linux/list.h> |
31 | #include <linux/math64.h> |
32 | #include <linux/mm.h> |
33 | #include <linux/mutex.h> |
34 | #include <linux/poll.h> |
35 | #include <linux/slab.h> |
36 | #include <linux/sched.h> |
37 | #include <linux/types.h> |
38 | #include <linux/uio.h> |
39 | #include <linux/uaccess.h> |
40 | #include <linux/module.h> |
41 | #include <sound/core.h> |
42 | #include <sound/initval.h> |
43 | #include <sound/compress_params.h> |
44 | #include <sound/compress_offload.h> |
45 | #include <sound/compress_driver.h> |
46 | |
47 | /* TODO: |
48 | * - add substream support for multiple devices in case of |
49 | * SND_DYNAMIC_MINORS is not used |
50 | * - Multiple node representation |
51 | * driver should be able to register multiple nodes |
52 | */ |
53 | |
54 | static DEFINE_MUTEX(device_mutex); |
55 | |
56 | struct snd_compr_file { |
57 | unsigned long caps; |
58 | struct snd_compr_stream stream; |
59 | }; |
60 | |
61 | /* |
62 | * a note on stream states used: |
63 | * we use follwing states in the compressed core |
64 | * SNDRV_PCM_STATE_OPEN: When stream has been opened. |
65 | * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by |
66 | * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this |
67 | * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. |
68 | * SNDRV_PCM_STATE_RUNNING: When stream has been started and is |
69 | * decoding/encoding and rendering/capturing data. |
70 | * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done |
71 | * by calling SNDRV_COMPRESS_DRAIN. |
72 | * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling |
73 | * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling |
74 | * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. |
75 | */ |
76 | static int snd_compr_open(struct inode *inode, struct file *f) |
77 | { |
78 | struct snd_compr *compr; |
79 | struct snd_compr_file *data; |
80 | struct snd_compr_runtime *runtime; |
81 | enum snd_compr_direction dirn; |
82 | int maj = imajor(inode); |
83 | int ret; |
84 | |
85 | if ((f->f_flags & O_ACCMODE) == O_WRONLY) |
86 | dirn = SND_COMPRESS_PLAYBACK; |
87 | else if ((f->f_flags & O_ACCMODE) == O_RDONLY) |
88 | dirn = SND_COMPRESS_CAPTURE; |
89 | else |
90 | return -EINVAL; |
91 | |
92 | if (maj == snd_major) |
93 | compr = snd_lookup_minor_data(iminor(inode), |
94 | SNDRV_DEVICE_TYPE_COMPRESS); |
95 | else |
96 | return -EBADFD; |
97 | |
98 | if (compr == NULL) { |
99 | pr_err("no device data!!!\n"); |
100 | return -ENODEV; |
101 | } |
102 | |
103 | if (dirn != compr->direction) { |
104 | pr_err("this device doesn't support this direction\n"); |
105 | snd_card_unref(compr->card); |
106 | return -EINVAL; |
107 | } |
108 | |
109 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
110 | if (!data) { |
111 | snd_card_unref(compr->card); |
112 | return -ENOMEM; |
113 | } |
114 | data->stream.ops = compr->ops; |
115 | data->stream.direction = dirn; |
116 | data->stream.private_data = compr->private_data; |
117 | data->stream.device = compr; |
118 | runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); |
119 | if (!runtime) { |
120 | kfree(data); |
121 | snd_card_unref(compr->card); |
122 | return -ENOMEM; |
123 | } |
124 | runtime->state = SNDRV_PCM_STATE_OPEN; |
125 | init_waitqueue_head(&runtime->sleep); |
126 | data->stream.runtime = runtime; |
127 | f->private_data = (void *)data; |
128 | mutex_lock(&compr->lock); |
129 | ret = compr->ops->open(&data->stream); |
130 | mutex_unlock(&compr->lock); |
131 | if (ret) { |
132 | kfree(runtime); |
133 | kfree(data); |
134 | } |
135 | snd_card_unref(compr->card); |
136 | return 0; |
137 | } |
138 | |
139 | static int snd_compr_free(struct inode *inode, struct file *f) |
140 | { |
141 | struct snd_compr_file *data = f->private_data; |
142 | struct snd_compr_runtime *runtime = data->stream.runtime; |
143 | |
144 | switch (runtime->state) { |
145 | case SNDRV_PCM_STATE_RUNNING: |
146 | case SNDRV_PCM_STATE_DRAINING: |
147 | case SNDRV_PCM_STATE_PAUSED: |
148 | data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); |
149 | break; |
150 | default: |
151 | break; |
152 | } |
153 | |
154 | data->stream.ops->free(&data->stream); |
155 | kfree(data->stream.runtime->buffer); |
156 | kfree(data->stream.runtime); |
157 | kfree(data); |
158 | return 0; |
159 | } |
160 | |
161 | static int snd_compr_update_tstamp(struct snd_compr_stream *stream, |
162 | struct snd_compr_tstamp *tstamp) |
163 | { |
164 | if (!stream->ops->pointer) |
165 | return -ENOTSUPP; |
166 | stream->ops->pointer(stream, tstamp); |
167 | pr_debug("dsp consumed till %d total %d bytes\n", |
168 | tstamp->byte_offset, tstamp->copied_total); |
169 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
170 | stream->runtime->total_bytes_transferred = tstamp->copied_total; |
171 | else |
172 | stream->runtime->total_bytes_available = tstamp->copied_total; |
173 | return 0; |
174 | } |
175 | |
176 | static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, |
177 | struct snd_compr_avail *avail) |
178 | { |
179 | memset(avail, 0, sizeof(*avail)); |
180 | snd_compr_update_tstamp(stream, &avail->tstamp); |
181 | /* Still need to return avail even if tstamp can't be filled in */ |
182 | |
183 | if (stream->runtime->total_bytes_available == 0 && |
184 | stream->runtime->state == SNDRV_PCM_STATE_SETUP && |
185 | stream->direction == SND_COMPRESS_PLAYBACK) { |
186 | pr_debug("detected init and someone forgot to do a write\n"); |
187 | return stream->runtime->buffer_size; |
188 | } |
189 | pr_debug("app wrote %lld, DSP consumed %lld\n", |
190 | stream->runtime->total_bytes_available, |
191 | stream->runtime->total_bytes_transferred); |
192 | if (stream->runtime->total_bytes_available == |
193 | stream->runtime->total_bytes_transferred) { |
194 | if (stream->direction == SND_COMPRESS_PLAYBACK) { |
195 | pr_debug("both pointers are same, returning full avail\n"); |
196 | return stream->runtime->buffer_size; |
197 | } else { |
198 | pr_debug("both pointers are same, returning no avail\n"); |
199 | return 0; |
200 | } |
201 | } |
202 | |
203 | avail->avail = stream->runtime->total_bytes_available - |
204 | stream->runtime->total_bytes_transferred; |
205 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
206 | avail->avail = stream->runtime->buffer_size - avail->avail; |
207 | |
208 | pr_debug("ret avail as %lld\n", avail->avail); |
209 | return avail->avail; |
210 | } |
211 | |
212 | static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) |
213 | { |
214 | struct snd_compr_avail avail; |
215 | |
216 | return snd_compr_calc_avail(stream, &avail); |
217 | } |
218 | |
219 | static int |
220 | snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) |
221 | { |
222 | struct snd_compr_avail ioctl_avail; |
223 | size_t avail; |
224 | |
225 | avail = snd_compr_calc_avail(stream, &ioctl_avail); |
226 | ioctl_avail.avail = avail; |
227 | |
228 | if (copy_to_user((__u64 __user *)arg, |
229 | &ioctl_avail, sizeof(ioctl_avail))) |
230 | return -EFAULT; |
231 | return 0; |
232 | } |
233 | |
234 | static int snd_compr_write_data(struct snd_compr_stream *stream, |
235 | const char __user *buf, size_t count) |
236 | { |
237 | void *dstn; |
238 | size_t copy; |
239 | struct snd_compr_runtime *runtime = stream->runtime; |
240 | /* 64-bit Modulus */ |
241 | u64 app_pointer = div64_u64(runtime->total_bytes_available, |
242 | runtime->buffer_size); |
243 | app_pointer = runtime->total_bytes_available - |
244 | (app_pointer * runtime->buffer_size); |
245 | |
246 | dstn = runtime->buffer + app_pointer; |
247 | pr_debug("copying %ld at %lld\n", |
248 | (unsigned long)count, app_pointer); |
249 | if (count < runtime->buffer_size - app_pointer) { |
250 | if (copy_from_user(dstn, buf, count)) |
251 | return -EFAULT; |
252 | } else { |
253 | copy = runtime->buffer_size - app_pointer; |
254 | if (copy_from_user(dstn, buf, copy)) |
255 | return -EFAULT; |
256 | if (copy_from_user(runtime->buffer, buf + copy, count - copy)) |
257 | return -EFAULT; |
258 | } |
259 | /* if DSP cares, let it know data has been written */ |
260 | if (stream->ops->ack) |
261 | stream->ops->ack(stream, count); |
262 | return count; |
263 | } |
264 | |
265 | static ssize_t snd_compr_write(struct file *f, const char __user *buf, |
266 | size_t count, loff_t *offset) |
267 | { |
268 | struct snd_compr_file *data = f->private_data; |
269 | struct snd_compr_stream *stream; |
270 | size_t avail; |
271 | int retval; |
272 | |
273 | if (snd_BUG_ON(!data)) |
274 | return -EFAULT; |
275 | |
276 | stream = &data->stream; |
277 | mutex_lock(&stream->device->lock); |
278 | /* write is allowed when stream is running or has been steup */ |
279 | if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && |
280 | stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { |
281 | mutex_unlock(&stream->device->lock); |
282 | return -EBADFD; |
283 | } |
284 | |
285 | avail = snd_compr_get_avail(stream); |
286 | pr_debug("avail returned %ld\n", (unsigned long)avail); |
287 | /* calculate how much we can write to buffer */ |
288 | if (avail > count) |
289 | avail = count; |
290 | |
291 | if (stream->ops->copy) { |
292 | char __user* cbuf = (char __user*)buf; |
293 | retval = stream->ops->copy(stream, cbuf, avail); |
294 | } else { |
295 | retval = snd_compr_write_data(stream, buf, avail); |
296 | } |
297 | if (retval > 0) |
298 | stream->runtime->total_bytes_available += retval; |
299 | |
300 | /* while initiating the stream, write should be called before START |
301 | * call, so in setup move state */ |
302 | if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { |
303 | stream->runtime->state = SNDRV_PCM_STATE_PREPARED; |
304 | pr_debug("stream prepared, Houston we are good to go\n"); |
305 | } |
306 | |
307 | mutex_unlock(&stream->device->lock); |
308 | return retval; |
309 | } |
310 | |
311 | |
312 | static ssize_t snd_compr_read(struct file *f, char __user *buf, |
313 | size_t count, loff_t *offset) |
314 | { |
315 | struct snd_compr_file *data = f->private_data; |
316 | struct snd_compr_stream *stream; |
317 | size_t avail; |
318 | int retval; |
319 | |
320 | if (snd_BUG_ON(!data)) |
321 | return -EFAULT; |
322 | |
323 | stream = &data->stream; |
324 | mutex_lock(&stream->device->lock); |
325 | |
326 | /* read is allowed when stream is running, paused, draining and setup |
327 | * (yes setup is state which we transition to after stop, so if user |
328 | * wants to read data after stop we allow that) |
329 | */ |
330 | switch (stream->runtime->state) { |
331 | case SNDRV_PCM_STATE_OPEN: |
332 | case SNDRV_PCM_STATE_PREPARED: |
333 | case SNDRV_PCM_STATE_XRUN: |
334 | case SNDRV_PCM_STATE_SUSPENDED: |
335 | case SNDRV_PCM_STATE_DISCONNECTED: |
336 | retval = -EBADFD; |
337 | goto out; |
338 | } |
339 | |
340 | avail = snd_compr_get_avail(stream); |
341 | pr_debug("avail returned %ld\n", (unsigned long)avail); |
342 | /* calculate how much we can read from buffer */ |
343 | if (avail > count) |
344 | avail = count; |
345 | |
346 | if (stream->ops->copy) { |
347 | retval = stream->ops->copy(stream, buf, avail); |
348 | } else { |
349 | retval = -ENXIO; |
350 | goto out; |
351 | } |
352 | if (retval > 0) |
353 | stream->runtime->total_bytes_transferred += retval; |
354 | |
355 | out: |
356 | mutex_unlock(&stream->device->lock); |
357 | return retval; |
358 | } |
359 | |
360 | static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) |
361 | { |
362 | return -ENXIO; |
363 | } |
364 | |
365 | static inline int snd_compr_get_poll(struct snd_compr_stream *stream) |
366 | { |
367 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
368 | return POLLOUT | POLLWRNORM; |
369 | else |
370 | return POLLIN | POLLRDNORM; |
371 | } |
372 | |
373 | static unsigned int snd_compr_poll(struct file *f, poll_table *wait) |
374 | { |
375 | struct snd_compr_file *data = f->private_data; |
376 | struct snd_compr_stream *stream; |
377 | size_t avail; |
378 | int retval = 0; |
379 | |
380 | if (snd_BUG_ON(!data)) |
381 | return -EFAULT; |
382 | stream = &data->stream; |
383 | if (snd_BUG_ON(!stream)) |
384 | return -EFAULT; |
385 | |
386 | mutex_lock(&stream->device->lock); |
387 | if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
388 | retval = -EBADFD; |
389 | goto out; |
390 | } |
391 | poll_wait(f, &stream->runtime->sleep, wait); |
392 | |
393 | avail = snd_compr_get_avail(stream); |
394 | pr_debug("avail is %ld\n", (unsigned long)avail); |
395 | /* check if we have at least one fragment to fill */ |
396 | switch (stream->runtime->state) { |
397 | case SNDRV_PCM_STATE_DRAINING: |
398 | /* stream has been woken up after drain is complete |
399 | * draining done so set stream state to stopped |
400 | */ |
401 | retval = snd_compr_get_poll(stream); |
402 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; |
403 | break; |
404 | case SNDRV_PCM_STATE_RUNNING: |
405 | case SNDRV_PCM_STATE_PREPARED: |
406 | case SNDRV_PCM_STATE_PAUSED: |
407 | if (avail >= stream->runtime->fragment_size) |
408 | retval = snd_compr_get_poll(stream); |
409 | break; |
410 | default: |
411 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
412 | retval = POLLOUT | POLLWRNORM | POLLERR; |
413 | else |
414 | retval = POLLIN | POLLRDNORM | POLLERR; |
415 | break; |
416 | } |
417 | out: |
418 | mutex_unlock(&stream->device->lock); |
419 | return retval; |
420 | } |
421 | |
422 | static int |
423 | snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) |
424 | { |
425 | int retval; |
426 | struct snd_compr_caps caps; |
427 | |
428 | if (!stream->ops->get_caps) |
429 | return -ENXIO; |
430 | |
431 | memset(&caps, 0, sizeof(caps)); |
432 | retval = stream->ops->get_caps(stream, &caps); |
433 | if (retval) |
434 | goto out; |
435 | if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) |
436 | retval = -EFAULT; |
437 | out: |
438 | return retval; |
439 | } |
440 | |
441 | static int |
442 | snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) |
443 | { |
444 | int retval; |
445 | struct snd_compr_codec_caps *caps; |
446 | |
447 | if (!stream->ops->get_codec_caps) |
448 | return -ENXIO; |
449 | |
450 | caps = kzalloc(sizeof(*caps), GFP_KERNEL); |
451 | if (!caps) |
452 | return -ENOMEM; |
453 | |
454 | retval = stream->ops->get_codec_caps(stream, caps); |
455 | if (retval) |
456 | goto out; |
457 | if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) |
458 | retval = -EFAULT; |
459 | |
460 | out: |
461 | kfree(caps); |
462 | return retval; |
463 | } |
464 | |
465 | /* revisit this with snd_pcm_preallocate_xxx */ |
466 | static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, |
467 | struct snd_compr_params *params) |
468 | { |
469 | unsigned int buffer_size; |
470 | void *buffer; |
471 | |
472 | buffer_size = params->buffer.fragment_size * params->buffer.fragments; |
473 | if (stream->ops->copy) { |
474 | buffer = NULL; |
475 | /* if copy is defined the driver will be required to copy |
476 | * the data from core |
477 | */ |
478 | } else { |
479 | buffer = kmalloc(buffer_size, GFP_KERNEL); |
480 | if (!buffer) |
481 | return -ENOMEM; |
482 | } |
483 | stream->runtime->fragment_size = params->buffer.fragment_size; |
484 | stream->runtime->fragments = params->buffer.fragments; |
485 | stream->runtime->buffer = buffer; |
486 | stream->runtime->buffer_size = buffer_size; |
487 | return 0; |
488 | } |
489 | |
490 | static int snd_compress_check_input(struct snd_compr_params *params) |
491 | { |
492 | /* first let's check the buffer parameter's */ |
493 | if (params->buffer.fragment_size == 0 || |
494 | params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) |
495 | return -EINVAL; |
496 | |
497 | /* now codec parameters */ |
498 | if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) |
499 | return -EINVAL; |
500 | |
501 | if (params->codec.ch_in == 0 || params->codec.ch_out == 0) |
502 | return -EINVAL; |
503 | |
504 | if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000)) |
505 | return -EINVAL; |
506 | |
507 | return 0; |
508 | } |
509 | |
510 | static int |
511 | snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) |
512 | { |
513 | struct snd_compr_params *params; |
514 | int retval; |
515 | |
516 | if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
517 | /* |
518 | * we should allow parameter change only when stream has been |
519 | * opened not in other cases |
520 | */ |
521 | params = kmalloc(sizeof(*params), GFP_KERNEL); |
522 | if (!params) |
523 | return -ENOMEM; |
524 | if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { |
525 | retval = -EFAULT; |
526 | goto out; |
527 | } |
528 | |
529 | retval = snd_compress_check_input(params); |
530 | if (retval) |
531 | goto out; |
532 | |
533 | retval = snd_compr_allocate_buffer(stream, params); |
534 | if (retval) { |
535 | retval = -ENOMEM; |
536 | goto out; |
537 | } |
538 | |
539 | retval = stream->ops->set_params(stream, params); |
540 | if (retval) |
541 | goto out; |
542 | |
543 | stream->metadata_set = false; |
544 | stream->next_track = false; |
545 | |
546 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
547 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; |
548 | else |
549 | stream->runtime->state = SNDRV_PCM_STATE_PREPARED; |
550 | } else { |
551 | return -EPERM; |
552 | } |
553 | out: |
554 | kfree(params); |
555 | return retval; |
556 | } |
557 | |
558 | static int |
559 | snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) |
560 | { |
561 | struct snd_codec *params; |
562 | int retval; |
563 | |
564 | if (!stream->ops->get_params) |
565 | return -EBADFD; |
566 | |
567 | params = kzalloc(sizeof(*params), GFP_KERNEL); |
568 | if (!params) |
569 | return -ENOMEM; |
570 | retval = stream->ops->get_params(stream, params); |
571 | if (retval) |
572 | goto out; |
573 | if (copy_to_user((char __user *)arg, params, sizeof(*params))) |
574 | retval = -EFAULT; |
575 | |
576 | out: |
577 | kfree(params); |
578 | return retval; |
579 | } |
580 | |
581 | static int |
582 | snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) |
583 | { |
584 | struct snd_compr_metadata metadata; |
585 | int retval; |
586 | |
587 | if (!stream->ops->get_metadata) |
588 | return -ENXIO; |
589 | |
590 | if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) |
591 | return -EFAULT; |
592 | |
593 | retval = stream->ops->get_metadata(stream, &metadata); |
594 | if (retval != 0) |
595 | return retval; |
596 | |
597 | if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) |
598 | return -EFAULT; |
599 | |
600 | return 0; |
601 | } |
602 | |
603 | static int |
604 | snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) |
605 | { |
606 | struct snd_compr_metadata metadata; |
607 | int retval; |
608 | |
609 | if (!stream->ops->set_metadata) |
610 | return -ENXIO; |
611 | /* |
612 | * we should allow parameter change only when stream has been |
613 | * opened not in other cases |
614 | */ |
615 | if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) |
616 | return -EFAULT; |
617 | |
618 | retval = stream->ops->set_metadata(stream, &metadata); |
619 | stream->metadata_set = true; |
620 | |
621 | return retval; |
622 | } |
623 | |
624 | static inline int |
625 | snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) |
626 | { |
627 | struct snd_compr_tstamp tstamp = {0}; |
628 | int ret; |
629 | |
630 | ret = snd_compr_update_tstamp(stream, &tstamp); |
631 | if (ret == 0) |
632 | ret = copy_to_user((struct snd_compr_tstamp __user *)arg, |
633 | &tstamp, sizeof(tstamp)) ? -EFAULT : 0; |
634 | return ret; |
635 | } |
636 | |
637 | static int snd_compr_pause(struct snd_compr_stream *stream) |
638 | { |
639 | int retval; |
640 | |
641 | if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) |
642 | return -EPERM; |
643 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); |
644 | if (!retval) |
645 | stream->runtime->state = SNDRV_PCM_STATE_PAUSED; |
646 | return retval; |
647 | } |
648 | |
649 | static int snd_compr_resume(struct snd_compr_stream *stream) |
650 | { |
651 | int retval; |
652 | |
653 | if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) |
654 | return -EPERM; |
655 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); |
656 | if (!retval) |
657 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; |
658 | return retval; |
659 | } |
660 | |
661 | static int snd_compr_start(struct snd_compr_stream *stream) |
662 | { |
663 | int retval; |
664 | |
665 | if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) |
666 | return -EPERM; |
667 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); |
668 | if (!retval) |
669 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; |
670 | return retval; |
671 | } |
672 | |
673 | static int snd_compr_stop(struct snd_compr_stream *stream) |
674 | { |
675 | int retval; |
676 | |
677 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || |
678 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) |
679 | return -EPERM; |
680 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); |
681 | if (!retval) { |
682 | snd_compr_drain_notify(stream); |
683 | stream->runtime->total_bytes_available = 0; |
684 | stream->runtime->total_bytes_transferred = 0; |
685 | } |
686 | return retval; |
687 | } |
688 | |
689 | static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) |
690 | { |
691 | int ret; |
692 | |
693 | /* |
694 | * We are called with lock held. So drop the lock while we wait for |
695 | * drain complete notfication from the driver |
696 | * |
697 | * It is expected that driver will notify the drain completion and then |
698 | * stream will be moved to SETUP state, even if draining resulted in an |
699 | * error. We can trigger next track after this. |
700 | */ |
701 | stream->runtime->state = SNDRV_PCM_STATE_DRAINING; |
702 | mutex_unlock(&stream->device->lock); |
703 | |
704 | /* we wait for drain to complete here, drain can return when |
705 | * interruption occurred, wait returned error or success. |
706 | * For the first two cases we don't do anything different here and |
707 | * return after waking up |
708 | */ |
709 | |
710 | ret = wait_event_interruptible(stream->runtime->sleep, |
711 | (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); |
712 | if (ret == -ERESTARTSYS) |
713 | pr_debug("wait aborted by a signal"); |
714 | else if (ret) |
715 | pr_debug("wait for drain failed with %d\n", ret); |
716 | |
717 | |
718 | wake_up(&stream->runtime->sleep); |
719 | mutex_lock(&stream->device->lock); |
720 | |
721 | return ret; |
722 | } |
723 | |
724 | static int snd_compr_drain(struct snd_compr_stream *stream) |
725 | { |
726 | int retval; |
727 | |
728 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || |
729 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) |
730 | return -EPERM; |
731 | |
732 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); |
733 | if (retval) { |
734 | pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); |
735 | wake_up(&stream->runtime->sleep); |
736 | return retval; |
737 | } |
738 | |
739 | return snd_compress_wait_for_drain(stream); |
740 | } |
741 | |
742 | static int snd_compr_next_track(struct snd_compr_stream *stream) |
743 | { |
744 | int retval; |
745 | |
746 | /* only a running stream can transition to next track */ |
747 | if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) |
748 | return -EPERM; |
749 | |
750 | /* you can signal next track isf this is intended to be a gapless stream |
751 | * and current track metadata is set |
752 | */ |
753 | if (stream->metadata_set == false) |
754 | return -EPERM; |
755 | |
756 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); |
757 | if (retval != 0) |
758 | return retval; |
759 | stream->metadata_set = false; |
760 | stream->next_track = true; |
761 | return 0; |
762 | } |
763 | |
764 | static int snd_compr_partial_drain(struct snd_compr_stream *stream) |
765 | { |
766 | int retval; |
767 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || |
768 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) |
769 | return -EPERM; |
770 | /* stream can be drained only when next track has been signalled */ |
771 | if (stream->next_track == false) |
772 | return -EPERM; |
773 | |
774 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); |
775 | if (retval) { |
776 | pr_debug("Partial drain returned failure\n"); |
777 | wake_up(&stream->runtime->sleep); |
778 | return retval; |
779 | } |
780 | |
781 | stream->next_track = false; |
782 | return snd_compress_wait_for_drain(stream); |
783 | } |
784 | |
785 | static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) |
786 | { |
787 | struct snd_compr_file *data = f->private_data; |
788 | struct snd_compr_stream *stream; |
789 | int retval = -ENOTTY; |
790 | |
791 | if (snd_BUG_ON(!data)) |
792 | return -EFAULT; |
793 | stream = &data->stream; |
794 | if (snd_BUG_ON(!stream)) |
795 | return -EFAULT; |
796 | mutex_lock(&stream->device->lock); |
797 | switch (_IOC_NR(cmd)) { |
798 | case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): |
799 | retval = put_user(SNDRV_COMPRESS_VERSION, |
800 | (int __user *)arg) ? -EFAULT : 0; |
801 | break; |
802 | case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): |
803 | retval = snd_compr_get_caps(stream, arg); |
804 | break; |
805 | case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): |
806 | retval = snd_compr_get_codec_caps(stream, arg); |
807 | break; |
808 | case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): |
809 | retval = snd_compr_set_params(stream, arg); |
810 | break; |
811 | case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): |
812 | retval = snd_compr_get_params(stream, arg); |
813 | break; |
814 | case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): |
815 | retval = snd_compr_set_metadata(stream, arg); |
816 | break; |
817 | case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): |
818 | retval = snd_compr_get_metadata(stream, arg); |
819 | break; |
820 | case _IOC_NR(SNDRV_COMPRESS_TSTAMP): |
821 | retval = snd_compr_tstamp(stream, arg); |
822 | break; |
823 | case _IOC_NR(SNDRV_COMPRESS_AVAIL): |
824 | retval = snd_compr_ioctl_avail(stream, arg); |
825 | break; |
826 | case _IOC_NR(SNDRV_COMPRESS_PAUSE): |
827 | retval = snd_compr_pause(stream); |
828 | break; |
829 | case _IOC_NR(SNDRV_COMPRESS_RESUME): |
830 | retval = snd_compr_resume(stream); |
831 | break; |
832 | case _IOC_NR(SNDRV_COMPRESS_START): |
833 | retval = snd_compr_start(stream); |
834 | break; |
835 | case _IOC_NR(SNDRV_COMPRESS_STOP): |
836 | retval = snd_compr_stop(stream); |
837 | break; |
838 | case _IOC_NR(SNDRV_COMPRESS_DRAIN): |
839 | retval = snd_compr_drain(stream); |
840 | break; |
841 | case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): |
842 | retval = snd_compr_partial_drain(stream); |
843 | break; |
844 | case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): |
845 | retval = snd_compr_next_track(stream); |
846 | break; |
847 | |
848 | } |
849 | mutex_unlock(&stream->device->lock); |
850 | return retval; |
851 | } |
852 | |
853 | static const struct file_operations snd_compr_file_ops = { |
854 | .owner = THIS_MODULE, |
855 | .open = snd_compr_open, |
856 | .release = snd_compr_free, |
857 | .write = snd_compr_write, |
858 | .read = snd_compr_read, |
859 | .unlocked_ioctl = snd_compr_ioctl, |
860 | .mmap = snd_compr_mmap, |
861 | .poll = snd_compr_poll, |
862 | }; |
863 | |
864 | static int snd_compress_dev_register(struct snd_device *device) |
865 | { |
866 | int ret = -EINVAL; |
867 | char str[16]; |
868 | struct snd_compr *compr; |
869 | |
870 | if (snd_BUG_ON(!device || !device->device_data)) |
871 | return -EBADFD; |
872 | compr = device->device_data; |
873 | |
874 | sprintf(str, "comprC%iD%i", compr->card->number, compr->device); |
875 | pr_debug("reg %s for device %s, direction %d\n", str, compr->name, |
876 | compr->direction); |
877 | /* register compressed device */ |
878 | ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, |
879 | compr->device, &snd_compr_file_ops, compr, str); |
880 | if (ret < 0) { |
881 | pr_err("snd_register_device failed\n %d", ret); |
882 | return ret; |
883 | } |
884 | return ret; |
885 | |
886 | } |
887 | |
888 | static int snd_compress_dev_disconnect(struct snd_device *device) |
889 | { |
890 | struct snd_compr *compr; |
891 | |
892 | compr = device->device_data; |
893 | snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, |
894 | compr->device); |
895 | return 0; |
896 | } |
897 | |
898 | /* |
899 | * snd_compress_new: create new compress device |
900 | * @card: sound card pointer |
901 | * @device: device number |
902 | * @dirn: device direction, should be of type enum snd_compr_direction |
903 | * @compr: compress device pointer |
904 | */ |
905 | int snd_compress_new(struct snd_card *card, int device, |
906 | int dirn, struct snd_compr *compr) |
907 | { |
908 | static struct snd_device_ops ops = { |
909 | .dev_free = NULL, |
910 | .dev_register = snd_compress_dev_register, |
911 | .dev_disconnect = snd_compress_dev_disconnect, |
912 | }; |
913 | |
914 | compr->card = card; |
915 | compr->device = device; |
916 | compr->direction = dirn; |
917 | return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); |
918 | } |
919 | EXPORT_SYMBOL_GPL(snd_compress_new); |
920 | |
921 | static int snd_compress_add_device(struct snd_compr *device) |
922 | { |
923 | int ret; |
924 | |
925 | if (!device->card) |
926 | return -EINVAL; |
927 | |
928 | /* register the card */ |
929 | ret = snd_card_register(device->card); |
930 | if (ret) |
931 | goto out; |
932 | return 0; |
933 | |
934 | out: |
935 | pr_err("failed with %d\n", ret); |
936 | return ret; |
937 | |
938 | } |
939 | |
940 | static int snd_compress_remove_device(struct snd_compr *device) |
941 | { |
942 | return snd_card_free(device->card); |
943 | } |
944 | |
945 | /** |
946 | * snd_compress_register - register compressed device |
947 | * |
948 | * @device: compressed device to register |
949 | */ |
950 | int snd_compress_register(struct snd_compr *device) |
951 | { |
952 | int retval; |
953 | |
954 | if (device->name == NULL || device->dev == NULL || device->ops == NULL) |
955 | return -EINVAL; |
956 | |
957 | pr_debug("Registering compressed device %s\n", device->name); |
958 | if (snd_BUG_ON(!device->ops->open)) |
959 | return -EINVAL; |
960 | if (snd_BUG_ON(!device->ops->free)) |
961 | return -EINVAL; |
962 | if (snd_BUG_ON(!device->ops->set_params)) |
963 | return -EINVAL; |
964 | if (snd_BUG_ON(!device->ops->trigger)) |
965 | return -EINVAL; |
966 | |
967 | mutex_init(&device->lock); |
968 | |
969 | /* register a compressed card */ |
970 | mutex_lock(&device_mutex); |
971 | retval = snd_compress_add_device(device); |
972 | mutex_unlock(&device_mutex); |
973 | return retval; |
974 | } |
975 | EXPORT_SYMBOL_GPL(snd_compress_register); |
976 | |
977 | int snd_compress_deregister(struct snd_compr *device) |
978 | { |
979 | pr_debug("Removing compressed device %s\n", device->name); |
980 | mutex_lock(&device_mutex); |
981 | snd_compress_remove_device(device); |
982 | mutex_unlock(&device_mutex); |
983 | return 0; |
984 | } |
985 | EXPORT_SYMBOL_GPL(snd_compress_deregister); |
986 | |
987 | static int __init snd_compress_init(void) |
988 | { |
989 | return 0; |
990 | } |
991 | |
992 | static void __exit snd_compress_exit(void) |
993 | { |
994 | } |
995 | |
996 | module_init(snd_compress_init); |
997 | module_exit(snd_compress_exit); |
998 | |
999 | MODULE_DESCRIPTION("ALSA Compressed offload framework"); |
1000 | MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); |
1001 | MODULE_LICENSE("GPL v2"); |
1002 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9