blob: 5398e107714b152dc610408ab74001751444175a [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 }
243 if (flags & COMPRESS_OUT) {
244 /* this should be removed once we have capture tested */
245 oops(&bad_compress, -EINVAL, "this version doesnt support capture");
246 goto config_fail;
247 }
248
249 compress->fd = open(fn, O_WRONLY);
250 if (compress->fd < 0) {
251 oops(&bad_compress, errno, "cannot open device '%s'", fn);
252 goto config_fail;
253 }
254
255 if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
256 oops(compress, errno, "cannot get device caps");
257 goto codec_fail;
258 }
259
260 /* If caller passed "don't care" fill in default values */
261 if ((config->fragment_size == 0) || (config->fragments == 0)) {
262 config->fragment_size = caps.min_fragment_size;
263 config->fragments = caps.max_fragments;
264 }
265
266#if 0
267 /* FIXME need to turn this On when DSP supports
268 * and treat in no support case
269 */
270 if (_is_codec_supported(compress, config, &caps) == false) {
271 oops(compress, errno, "codec not supported\n");
272 goto codec_fail;
273 }
274#endif
275 fill_compress_params(config, &params);
276
277 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, &params)) {
278 oops(&bad_compress, errno, "cannot set device");
279 goto codec_fail;
280 }
281
282 return compress;
283
284codec_fail:
285 close(compress->fd);
286 compress->fd = -1;
287config_fail:
288 free(compress->config);
289input_fail:
290 free(compress);
291 return &bad_compress;
292}
293
294void compress_close(struct compress *compress)
295{
296 if (compress == &bad_compress)
297 return;
298
299 if (compress->fd >= 0)
300 close(compress->fd);
301 compress->running = 0;
302 compress->fd = -1;
303 free(compress->config);
304 free(compress);
305}
306
307int compress_get_hpointer(struct compress *compress,
308 unsigned int *avail, struct timespec *tstamp)
309{
310 struct snd_compr_avail kavail;
311 __u64 time;
312
313 if (!is_compress_ready(compress))
314 return oops(compress, -ENODEV, "device not ready");
315
316 if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail))
317 return oops(compress, errno, "cannot get avail");
318 if (0 == kavail.tstamp.sampling_rate)
319 return oops(compress, errno, "invalid paramter");
320 *avail = (unsigned int)kavail.avail;
321 time = kavail.tstamp.pcm_io_frames / kavail.tstamp.sampling_rate;
322 tstamp->tv_sec = time;
323 time = kavail.tstamp.pcm_io_frames % kavail.tstamp.sampling_rate;
324 tstamp->tv_nsec = time * 1000000000 / kavail.tstamp.sampling_rate;
325 return 0;
326}
327
328int compress_get_tstamp(struct compress *compress,
329 unsigned long *samples, unsigned int *sampling_rate)
330{
331 struct snd_compr_tstamp ktstamp;
332
333 if (!is_compress_ready(compress))
334 return oops(compress, -ENODEV, "device not ready");
335
336 if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp))
337 return oops(compress, errno, "cannot get tstamp");
338
339 *samples = ktstamp.pcm_io_frames;
340 *sampling_rate = ktstamp.sampling_rate;
341 return 0;
342}
343
344int compress_write(struct compress *compress, const void *buf, unsigned int size)
345{
346 struct snd_compr_avail avail;
347 struct pollfd fds;
348 int to_write = 0; /* zero indicates we haven't written yet */
349 int written, total = 0, ret;
350 const char* cbuf = buf;
351 const unsigned int frag_size = compress->config->fragment_size;
352
353 if (!(compress->flags & COMPRESS_IN))
354 return oops(compress, -EINVAL, "Invalid flag set");
355 if (!is_compress_ready(compress))
356 return oops(compress, -ENODEV, "device not ready");
357 fds.fd = compress->fd;
358 fds.events = POLLOUT;
359
360 /*TODO: treat auto start here first */
361 while (size) {
362 if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
363 return oops(compress, errno, "cannot get avail");
364
365 if ( (avail.avail < frag_size)
366 || ((to_write != 0) && (avail.avail < size)) ) {
367 /* not enough space for one fragment, or we have done
368 * a short write and there isn't enough space for all
369 * the remaining data
370 */
371 ret = poll(&fds, 1, compress->max_poll_wait_ms);
372 /* A pause will cause -EBADFD or zero.
373 * This is not an error, just stop writing */
374 if ((ret == 0) || (ret == -EBADFD))
375 break;
376 if (ret < 0)
377 return oops(compress, errno, "poll error");
378 if (fds.revents & POLLOUT) {
379 continue;
380 }
381 if (fds.revents & POLLERR) {
382 return oops(compress, -EIO, "poll returned error!");
383 }
384 }
385 /* write avail bytes */
386 if (size > avail.avail)
387 to_write = avail.avail;
388 else
389 to_write = size;
390 written = write(compress->fd, cbuf, to_write);
391 /* If play was paused the write returns -EBADFD */
392 if (written == -EBADFD)
393 break;
394 if (written < 0)
395 return oops(compress, errno, "write failed!");
396
397 size -= written;
398 cbuf += written;
399 total += written;
400 }
401 return total;
402}
403
404int compress_read(struct compress *compress, void *buf, unsigned int size)
405{
406 return oops(compress, -ENOTTY, "Not supported yet in lib");
407}
408
409int compress_start(struct compress *compress)
410{
411 if (!is_compress_ready(compress))
412 return oops(compress, -ENODEV, "device not ready");
413 if (ioctl(compress->fd, SNDRV_COMPRESS_START))
414 return oops(compress, errno, "cannot start the stream");
415 compress->running = 1;
416 return 0;
417
418}
419
420int compress_stop(struct compress *compress)
421{
422 if (!is_compress_running(compress))
423 return oops(compress, -ENODEV, "device not ready");
424 if (ioctl(compress->fd, SNDRV_COMPRESS_STOP))
425 return oops(compress, errno, "cannot stop the stream");
426 return 0;
427}
428
429int compress_pause(struct compress *compress)
430{
431 if (!is_compress_running(compress))
432 return oops(compress, -ENODEV, "device not ready");
433 if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE))
434 return oops(compress, errno, "cannot pause the stream");
435 return 0;
436}
437
438int compress_resume(struct compress *compress)
439{
440 if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME))
441 return oops(compress, errno, "cannot resume the stream");
442 return 0;
443}
444
445int compress_drain(struct compress *compress)
446{
447 if (!is_compress_running(compress))
448 return oops(compress, -ENODEV, "device not ready");
449 if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN))
450 return oops(compress, errno, "cannot drain the stream");
451 return 0;
452}
453
454int compress_partial_drain(struct compress *compress)
455{
456 if (!is_compress_running(compress))
457 return oops(compress, -ENODEV, "device not ready");
458
459 if (!compress->next_track)
460 return oops(compress, -EPERM, "next track not signalled");
461 if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN))
462 return oops(compress, errno, "cannot drain the stream\n");
463 compress->next_track = 0;
464 return 0;
465}
466
467int compress_next_track(struct compress *compress)
468{
469 if (!is_compress_running(compress))
470 return oops(compress, -ENODEV, "device not ready");
471
472 if (!compress->gapless_metadata)
473 return oops(compress, -EPERM, "metadata not set");
474 if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK))
475 return oops(compress, errno, "cannot set next track\n");
476 compress->next_track = 1;
477 compress->gapless_metadata = 0;
478 return 0;
479}
480
481int compress_set_gapless_metadata(struct compress *compress,
482 struct compr_gapless_mdata *mdata)
483{
484 struct snd_compr_metadata metadata;
485 int version;
486
487 if (!is_compress_ready(compress))
488 return oops(compress, -ENODEV, "device not ready");
489
490 version = get_compress_version(compress);
491 if (version <= 0)
492 return -1;
493
494 if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1))
495 return oops(compress, -ENXIO, "gapless apis not supported in kernel");
496
497 metadata.key = SNDRV_COMPRESS_ENCODER_PADDING;
498 metadata.value[0] = mdata->encoder_padding;
499 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
500 return oops(compress, errno, "can't set metadata for stream\n");
501
502 metadata.key = SNDRV_COMPRESS_ENCODER_DELAY;
503 metadata.value[0] = mdata->encoder_delay;
504 if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
505 return oops(compress, errno, "can't set metadata for stream\n");
506 compress->gapless_metadata = 1;
507 return 0;
508}
509
510bool is_codec_supported(unsigned int card, unsigned int device,
511 unsigned int flags, struct snd_codec *codec)
512{
513 unsigned int dev_flag;
514 bool ret;
515 int fd;
516 char fn[256];
517
518 snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
519
520 if (flags & COMPRESS_OUT)
521 dev_flag = O_RDONLY;
522 else
523 dev_flag = O_WRONLY;
524
525 fd = open(fn, dev_flag);
526 if (fd < 0)
527 return oops(&bad_compress, errno, "cannot open device '%s'", fn);
528
529 ret = _is_codec_type_supported(fd, codec);
530
531 close(fd);
532 return ret;
533}
534
535void compress_set_max_poll_wait(struct compress *compress, int milliseconds)
536{
537 compress->max_poll_wait_ms = milliseconds;
538}
539