blob: d5115b633476b8b7f00852b2c7333d337e1e434f [file] [log] [blame]
Eric Laurentc902d7f2013-03-08 14:50:45 -08001/*
2 * BSD LICENSE
3 *
4 * tinycompress library for compress audio offload in alsa
5 * Copyright (c) 2011-2012, Intel Corporation
6 * All rights reserved.
7 *
8 * Author: Vinod Koul <vinod.koul@linux.intel.com>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * Neither the name of Intel Corporation nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * LGPL LICENSE
35 *
36 * tinycompress library for compress audio offload in alsa
37 * Copyright (c) 2011-2012, Intel Corporation.
38 *
39 *
40 * This program is free software; you can redistribute it and/or modify it
41 * under the terms and conditions of the GNU Lesser General Public License,
42 * version 2.1, as published by the Free Software Foundation.
43 *
44 * This program is distributed in the hope it will be useful, but WITHOUT
45 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
46 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
47 * License for more details.
48 *
49 * You should have received a copy of the GNU Lesser General Public License
50 * along with this program; if not, write to
51 * the Free Software Foundation, Inc.,
52 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
53 */
54
55#include <stdio.h>
56#include <stdlib.h>
57#include <fcntl.h>
58#include <stdarg.h>
59#include <string.h>
60#include <errno.h>
61#include <unistd.h>
62#include <poll.h>
63#include <stdbool.h>
64#include <sys/ioctl.h>
65#include <sys/mman.h>
66#include <sys/time.h>
67#include <limits.h>
68
69#include <linux/types.h>
70#include <linux/ioctl.h>
71#define __force
72#define __bitwise
73#define __user
74#include <sound/asound.h>
75#include <sound/compress_params.h>
76#include <sound/compress_offload.h>
77#include <tinycompress/tinycompress.h>
78
79#define COMPR_ERR_MAX 128
80
81/* Default maximum time we will wait in a poll() - 20 seconds */
82#define DEFAULT_MAX_POLL_WAIT_MS 20000
83
84struct compress {
85 int fd;
86 unsigned int flags;
87 char error[COMPR_ERR_MAX];
88 struct compr_config *config;
89 int running;
90 int max_poll_wait_ms;
91 unsigned int gapless_metadata;
92 unsigned int next_track;
93};
94
95static int oops(struct compress *compress, int e, const char *fmt, ...)
96{
97 va_list ap;
98 int sz;
99
100 va_start(ap, fmt);
101 vsnprintf(compress->error, COMPR_ERR_MAX, fmt, ap);
102 va_end(ap);
103 sz = strlen(compress->error);
104
105 if (errno)
106 snprintf(compress->error + sz, COMPR_ERR_MAX - sz,
107 ": %s", strerror(e));
108 return e;
109}
110
111const char *compress_get_error(struct compress *compress)
112{
113 return compress->error;
114}
115static struct compress bad_compress = {
116 .fd = -1,
117};
118
119int is_compress_running(struct compress *compress)
120{
121 return ((compress->fd > 0) && compress->running) ? 1 : 0;
122}
123
124int is_compress_ready(struct compress *compress)
125{
126 return (compress->fd > 0) ? 1 : 0;
127}
128
129static int get_compress_version(struct compress *compress)
130{
131 int version = 0;
132
133 if (ioctl(compress->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) {
134 oops(compress, errno, "cant read version");
135 return -1;
136 }
137 return version;
138}
139
140static bool _is_codec_supported(struct compress *compress, struct compr_config *config,
141 const struct snd_compr_caps *caps)
142{
143 bool codec = false;
144 unsigned int i;
145
146 for (i = 0; i < caps->num_codecs; i++) {
147 if (caps->codecs[i] == config->codec->id) {
148 /* found the codec */
149 codec = true;
150 break;
151 }
152 }
153 if (codec == false) {
154 oops(compress, -ENXIO, "this codec is not supported");
155 return false;
156 }
157
158 if (config->fragment_size < caps->min_fragment_size) {
159 oops(compress, -EINVAL, "requested fragment size %d is below min supported %d",
160 config->fragment_size, caps->min_fragment_size);
161 return false;
162 }
163 if (config->fragment_size > caps->max_fragment_size) {
164 oops(compress, -EINVAL, "requested fragment size %d is above max supported %d",
165 config->fragment_size, caps->max_fragment_size);
166 return false;
167 }
168 if (config->fragments < caps->min_fragments) {
169 oops(compress, -EINVAL, "requested fragments %d are below min supported %d",
170 config->fragments, caps->min_fragments);
171 return false;
172 }
173 if (config->fragments > caps->max_fragments) {
174 oops(compress, -EINVAL, "requested fragments %d are above max supported %d",
175 config->fragments, caps->max_fragments);
176 return false;
177 }
178
179 /* TODO: match the codec properties */
180 return true;
181}
182
183static bool _is_codec_type_supported(int fd, struct snd_codec *codec)
184{
185 struct snd_compr_caps caps;
186 bool found = false;
187 unsigned int i;
188
189 if (ioctl(fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
190 oops(&bad_compress, errno, "cannot get device caps");
191 return false;
192 }
193
194 for (i = 0; i < caps.num_codecs; i++) {
195 if (caps.codecs[i] == codec->id) {
196 /* found the codec */
197 found = true;
198 break;
199 }
200 }
201 /* TODO: match the codec properties */
202 return found;
203}
204
205static inline void
206fill_compress_params(struct compr_config *config, struct snd_compr_params *params)
207{
208 params->buffer.fragment_size = config->fragment_size;
209 params->buffer.fragments = config->fragments;
210 memcpy(&params->codec, config->codec, sizeof(params->codec));
211}
212
213struct compress *compress_open(unsigned int card, unsigned int device,
214 unsigned int flags, struct compr_config *config)
215{
216 struct compress *compress;
217 struct snd_compr_params params;
218 struct snd_compr_caps caps;
219 char fn[256];
220
221 compress = calloc(1, sizeof(struct compress));
222 if (!compress || !config) {
223 oops(&bad_compress, errno, "cannot allocate compress object");
224 return &bad_compress;
225 }
226
227 compress->next_track = 0;
228 compress->gapless_metadata = 0;
229 compress->config = calloc(1, sizeof(*config));
230 if (!compress->config)
231 goto input_fail;
232 memcpy(compress->config, config, sizeof(*compress->config));
233
234 snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
235
236 compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS;
237
238 compress->flags = flags;
239 if (!((flags & COMPRESS_OUT) || (flags & COMPRESS_IN))) {
240 oops(&bad_compress, -EINVAL, "can't deduce device direction from given flags");
241 goto config_fail;
242 }
Eric Laurentc902d7f2013-03-08 14:50:45 -0800243
Eric Laurentf0c40782013-05-02 18:10:20 -0700244 if (flags & COMPRESS_OUT) {
245 compress->fd = open(fn, O_RDONLY);
246 } else {
247 compress->fd = open(fn, O_WRONLY);
248 }
Eric Laurentc902d7f2013-03-08 14:50:45 -0800249 if (compress->fd < 0) {
250 oops(&bad_compress, errno, "cannot open device '%s'", fn);
251 goto config_fail;
252 }
253
254 if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
255 oops(compress, errno, "cannot get device caps");
256 goto codec_fail;
257 }
258
259 /* If caller passed "don't care" fill in default values */
260 if ((config->fragment_size == 0) || (config->fragments == 0)) {
261 config->fragment_size = caps.min_fragment_size;
262 config->fragments = caps.max_fragments;
263 }
264
265#if 0
266 /* FIXME need to turn this On when DSP supports
267 * and treat in no support case
268 */
269 if (_is_codec_supported(compress, config, &caps) == false) {
270 oops(compress, errno, "codec not supported\n");
271 goto codec_fail;
272 }
273#endif
274 fill_compress_params(config, &params);
275
276 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, &params)) {
277 oops(&bad_compress, errno, "cannot set device");
278 goto codec_fail;
279 }
280
281 return compress;
282
283codec_fail:
284 close(compress->fd);
285 compress->fd = -1;
286config_fail:
287 free(compress->config);
288input_fail:
289 free(compress);
290 return &bad_compress;
291}
292
293void compress_close(struct compress *compress)
294{
295 if (compress == &bad_compress)
296 return;
297
298 if (compress->fd >= 0)
299 close(compress->fd);
300 compress->running = 0;
301 compress->fd = -1;
302 free(compress->config);
303 free(compress);
304}
305
306int compress_get_hpointer(struct compress *compress,
307 unsigned int *avail, struct timespec *tstamp)
308{
309 struct snd_compr_avail kavail;
310 __u64 time;
311
312 if (!is_compress_ready(compress))
313 return oops(compress, -ENODEV, "device not ready");
314
315 if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail))
316 return oops(compress, errno, "cannot get avail");
317 if (0 == kavail.tstamp.sampling_rate)
318 return oops(compress, errno, "invalid paramter");
319 *avail = (unsigned int)kavail.avail;
320 time = kavail.tstamp.pcm_io_frames / kavail.tstamp.sampling_rate;
321 tstamp->tv_sec = time;
322 time = kavail.tstamp.pcm_io_frames % kavail.tstamp.sampling_rate;
323 tstamp->tv_nsec = time * 1000000000 / kavail.tstamp.sampling_rate;
324 return 0;
325}
326
327int compress_get_tstamp(struct compress *compress,
328 unsigned long *samples, unsigned int *sampling_rate)
329{
330 struct snd_compr_tstamp ktstamp;
331
332 if (!is_compress_ready(compress))
333 return oops(compress, -ENODEV, "device not ready");
334
335 if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp))
336 return oops(compress, errno, "cannot get tstamp");
337
338 *samples = ktstamp.pcm_io_frames;
339 *sampling_rate = ktstamp.sampling_rate;
340 return 0;
341}
342
343int compress_write(struct compress *compress, const void *buf, unsigned int size)
344{
345 struct snd_compr_avail avail;
346 struct pollfd fds;
347 int to_write = 0; /* zero indicates we haven't written yet */
348 int written, total = 0, ret;
349 const char* cbuf = buf;
350 const unsigned int frag_size = compress->config->fragment_size;
351
352 if (!(compress->flags & COMPRESS_IN))
353 return oops(compress, -EINVAL, "Invalid flag set");
354 if (!is_compress_ready(compress))
355 return oops(compress, -ENODEV, "device not ready");
356 fds.fd = compress->fd;
357 fds.events = POLLOUT;
358
359 /*TODO: treat auto start here first */
360 while (size) {
361 if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
362 return oops(compress, errno, "cannot get avail");
363
364 if ( (avail.avail < frag_size)
365 || ((to_write != 0) && (avail.avail < size)) ) {
366 /* not enough space for one fragment, or we have done
367 * a short write and there isn't enough space for all
368 * the remaining data
369 */
370 ret = poll(&fds, 1, compress->max_poll_wait_ms);
371 /* A pause will cause -EBADFD or zero.
372 * This is not an error, just stop writing */
373 if ((ret == 0) || (ret == -EBADFD))
374 break;
375 if (ret < 0)
376 return oops(compress, errno, "poll error");
377 if (fds.revents & POLLOUT) {
378 continue;
379 }
380 if (fds.revents & POLLERR) {
381 return oops(compress, -EIO, "poll returned error!");
382 }
383 }
384 /* write avail bytes */
385 if (size > avail.avail)
386 to_write = avail.avail;
387 else
388 to_write = size;
389 written = write(compress->fd, cbuf, to_write);
390 /* If play was paused the write returns -EBADFD */
391 if (written == -EBADFD)
392 break;
393 if (written < 0)
394 return oops(compress, errno, "write failed!");
395
396 size -= written;
397 cbuf += written;
398 total += written;
399 }
400 return total;
401}
402
403int compress_read(struct compress *compress, void *buf, unsigned int size)
404{
Eric Laurentf0c40782013-05-02 18:10:20 -0700405 struct snd_compr_avail avail;
406 struct pollfd fds;
407 int to_read = 0;
408 int num_read, total = 0, ret;
409 char* cbuf = buf;
410 const unsigned int frag_size = compress->config->fragment_size;
411
412 if (!(compress->flags & COMPRESS_OUT))
413 return oops(compress, -EINVAL, "Invalid flag set");
414 if (!is_compress_ready(compress))
415 return oops(compress, -ENODEV, "device not ready");
416 fds.fd = compress->fd;
417 fds.events = POLLIN;
418
419 while (size) {
420 if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
421 return oops(compress, errno, "cannot get avail");
422
423 if ( (avail.avail < frag_size) && (avail.avail < size) ) {
424 /* Less than one fragment available and not at the
425 * end of the read, so poll
426 */
427 ret = poll(&fds, 1, compress->max_poll_wait_ms);
428 /* A pause will cause -EBADFD or zero.
429 * This is not an error, just stop reading */
430 if ((ret == 0) || (ret == -EBADFD))
431 break;
432 if (ret < 0)
433 return oops(compress, errno, "poll error");
434 if (fds.revents & POLLIN) {
435 continue;
436 }
437 if (fds.revents & POLLERR) {
438 return oops(compress, -EIO, "poll returned error!");
439 }
440 }
441 /* read avail bytes */
442 if (size > avail.avail)
443 to_read = avail.avail;
444 else
445 to_read = size;
446 num_read = read(compress->fd, cbuf, to_read);
447 /* If play was paused the read returns -EBADFD */
448 if (num_read == -EBADFD)
449 break;
450 if (num_read < 0)
451 return oops(compress, errno, "read failed!");
452
453 size -= num_read;
454 cbuf += num_read;
455 total += num_read;
456 }
457
458 return total;
Eric Laurentc902d7f2013-03-08 14:50:45 -0800459}
460
461int compress_start(struct compress *compress)
462{
463 if (!is_compress_ready(compress))
464 return oops(compress, -ENODEV, "device not ready");
465 if (ioctl(compress->fd, SNDRV_COMPRESS_START))
466 return oops(compress, errno, "cannot start the stream");
467 compress->running = 1;
468 return 0;
469
470}
471
472int compress_stop(struct compress *compress)
473{
474 if (!is_compress_running(compress))
475 return oops(compress, -ENODEV, "device not ready");
476 if (ioctl(compress->fd, SNDRV_COMPRESS_STOP))
477 return oops(compress, errno, "cannot stop the stream");
478 return 0;
479}
480
481int compress_pause(struct compress *compress)
482{
483 if (!is_compress_running(compress))
484 return oops(compress, -ENODEV, "device not ready");
485 if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE))
486 return oops(compress, errno, "cannot pause the stream");
487 return 0;
488}
489
490int compress_resume(struct compress *compress)
491{
492 if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME))
493 return oops(compress, errno, "cannot resume the stream");
494 return 0;
495}
496
497int compress_drain(struct compress *compress)
498{
499 if (!is_compress_running(compress))
500 return oops(compress, -ENODEV, "device not ready");
501 if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN))
502 return oops(compress, errno, "cannot drain the stream");
503 return 0;
504}
505
506int compress_partial_drain(struct compress *compress)
507{
508 if (!is_compress_running(compress))
509 return oops(compress, -ENODEV, "device not ready");
510
511 if (!compress->next_track)
512 return oops(compress, -EPERM, "next track not signalled");
513 if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN))
514 return oops(compress, errno, "cannot drain the stream\n");
515 compress->next_track = 0;
516 return 0;
517}
518
519int compress_next_track(struct compress *compress)
520{
521 if (!is_compress_running(compress))
522 return oops(compress, -ENODEV, "device not ready");
523
524 if (!compress->gapless_metadata)
525 return oops(compress, -EPERM, "metadata not set");
526 if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK))
527 return oops(compress, errno, "cannot set next track\n");
528 compress->next_track = 1;
529 compress->gapless_metadata = 0;
530 return 0;
531}
532
533int compress_set_gapless_metadata(struct compress *compress,
534 struct compr_gapless_mdata *mdata)
535{
536 struct snd_compr_metadata metadata;
537 int version;
538
539 if (!is_compress_ready(compress))
540 return oops(compress, -ENODEV, "device not ready");
541
542 version = get_compress_version(compress);
543 if (version <= 0)
544 return -1;
545
546 if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1))
547 return oops(compress, -ENXIO, "gapless apis not supported in kernel");
548
549 metadata.key = SNDRV_COMPRESS_ENCODER_PADDING;
550 metadata.value[0] = mdata->encoder_padding;
551 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
552 return oops(compress, errno, "can't set metadata for stream\n");
553
554 metadata.key = SNDRV_COMPRESS_ENCODER_DELAY;
555 metadata.value[0] = mdata->encoder_delay;
556 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
557 return oops(compress, errno, "can't set metadata for stream\n");
558 compress->gapless_metadata = 1;
559 return 0;
560}
561
562bool is_codec_supported(unsigned int card, unsigned int device,
563 unsigned int flags, struct snd_codec *codec)
564{
565 unsigned int dev_flag;
566 bool ret;
567 int fd;
568 char fn[256];
569
570 snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
571
572 if (flags & COMPRESS_OUT)
573 dev_flag = O_RDONLY;
574 else
575 dev_flag = O_WRONLY;
576
577 fd = open(fn, dev_flag);
578 if (fd < 0)
579 return oops(&bad_compress, errno, "cannot open device '%s'", fn);
580
581 ret = _is_codec_type_supported(fd, codec);
582
583 close(fd);
584 return ret;
585}
586
587void compress_set_max_poll_wait(struct compress *compress, int milliseconds)
588{
589 compress->max_poll_wait_ms = milliseconds;
590}
591