[alsa-devel] [ALSA-LIB][PATCH] Add support for compress offload API

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Wed Mar 4 23:31:43 CET 2015


I am no lawyer but I think there is work to do on the license and 
copyright parts, see below.
-Pierre

On 3/4/15 10:22 AM, Qais Yousef wrote:
> Merge tinycompress library into alsa-lib using LGPL license.
> Only function names were modified to match the coding style in alsa-lib,
> prepending snd_compr to function names and structs.
>
> Signed-off-by: Qais Yousef <qais.yousef at imgtec.com>
> Cc: Takashi Iwai <tiwai at suse.de>
> Cc: Vinod Koul <vinod.koul at intel.com>
> Cc: Mark Brown <broonie at kernel.org>
> ---
>   configure.ac                     |   9 +
>   include/Makefile.am              |   4 +
>   include/compress.h               | 245 ++++++++++++++++
>   include/sound/compress_offload.h | 191 +++++++++++++
>   include/sound/compress_params.h  | 404 ++++++++++++++++++++++++++
>   src/Makefile.am                  |   7 +
>   src/compress/Makefile.am         |   8 +
>   src/compress/compress.c          | 599 +++++++++++++++++++++++++++++++++++++++
>   8 files changed, 1467 insertions(+)
>   create mode 100644 include/compress.h
>   create mode 100644 include/sound/compress_offload.h
>   create mode 100644 include/sound/compress_params.h
>   create mode 100644 src/compress/Makefile.am
>   create mode 100644 src/compress/compress.c
>
> diff --git a/configure.ac b/configure.ac
> index f0995e3ae787..a768730781e0 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -368,6 +368,9 @@ AC_ARG_ENABLE(mixer,
>   AC_ARG_ENABLE(pcm,
>     AS_HELP_STRING([--disable-pcm], [disable the PCM component]),
>     [build_pcm="$enableval"], [build_pcm="yes"])
> +AC_ARG_ENABLE(compress,
> +  AS_HELP_STRING([--disable-compress], [disable the compress component]),
> +  [build_compress="$enableval"], [build_compress="yes"])
>   AC_ARG_ENABLE(rawmidi,
>     AS_HELP_STRING([--disable-rawmidi], [disable the raw MIDI component]),
>     [build_rawmidi="$enableval"], [build_rawmidi="yes"])
> @@ -418,6 +421,7 @@ AC_SUBST(PYTHON_INCLUDES)
>
>   AM_CONDITIONAL([BUILD_MIXER], [test x$build_mixer = xyes])
>   AM_CONDITIONAL([BUILD_PCM], [test x$build_pcm = xyes])
> +AM_CONDITIONAL([BUILD_COMPRESS], [test x$build_compress = xyes])
>   AM_CONDITIONAL([BUILD_RAWMIDI], [test x$build_rawmidi = xyes])
>   AM_CONDITIONAL([BUILD_HWDEP], [test x$build_hwdep = xyes])
>   AM_CONDITIONAL([BUILD_SEQ], [test x$build_seq = xyes])
> @@ -431,6 +435,9 @@ fi
>   if test "$build_pcm" = "yes"; then
>     AC_DEFINE([BUILD_PCM], "1", [Build PCM component])
>   fi
> +if test "$build_compress" = "yes"; then
> +  AC_DEFINE([BUILD_COMPRESS], "1", [Build compress component])
> +fi
>   if test "$build_rawmidi" = "yes"; then
>     AC_DEFINE([BUILD_RAWMIDI], "1", [Build raw MIDI component])
>   fi
> @@ -641,6 +648,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
>   	  include/Makefile include/sound/Makefile src/Versions src/Makefile \
>             src/control/Makefile src/mixer/Makefile \
>   	  src/pcm/Makefile src/pcm/scopes/Makefile \
> +	  src/compress/Makefile \
>   	  src/rawmidi/Makefile src/timer/Makefile \
>             src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile \
>             src/alisp/Makefile \
> @@ -693,6 +701,7 @@ cat >> include/asoundlib.h <<EOF
>   #include <alsa/conf.h>
>   EOF
>   test "$build_pcm" = "yes" && echo "#include <alsa/pcm.h>" >> include/asoundlib.h
> +test "$build_compress" = "yes" && echo "#include <alsa/compress.h>" >> include/asoundlib.h
>   test "$build_rawmidi" = "yes" && echo "#include <alsa/rawmidi.h>" >> include/asoundlib.h
>   test "$build_pcm" = "yes" && echo "#include <alsa/timer.h>" >> include/asoundlib.h
>   test "$build_hwdep" = "yes" && echo "#include <alsa/hwdep.h>" >> include/asoundlib.h
> diff --git a/include/Makefile.am b/include/Makefile.am
> index 4baa03af69e1..395a2ed60d70 100644
> --- a/include/Makefile.am
> +++ b/include/Makefile.am
> @@ -30,6 +30,10 @@ alsainclude_HEADERS += pcm_ioplug.h
>   endif
>   endif
>
> +if BUILD_COMPRESS
> +alsainclude_HEADERS += compress.h
> +endif
> +
>   if BUILD_RAWMIDI
>   alsainclude_HEADERS += rawmidi.h
>   endif
> diff --git a/include/compress.h b/include/compress.h
> new file mode 100644
> index 000000000000..250ce0c3f7c4
> --- /dev/null
> +++ b/include/compress.h
> @@ -0,0 +1,245 @@
> +/*
> + * tinycompress library for compress audio offload in alsa
> + * Copyright (c) 2011-2012, Intel Corporation.
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU Lesser General Public License,
> + * version 2.1, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
> + * License for more details.

It looks like this is pulled from 
tinycompress/include/tinycompress/tinycompress.h, which was released 
with a dual BSD and LGPL license. I wonder if you can remove the BSD 
license really? At the very least you should make it clear where this 
code came from.

> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to
> + * the Free Software Foundation, Inc.,
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +
> +#ifndef __ALSA_COMPRESS_H
> +#define __ALSA_COMPRESS_H
> +
> +#include <stdbool.h>
> +#include <linux/types.h>
> +
> +#if defined(__cplusplus)
> +extern "C" {
> +#endif
> +/*
> + * struct compr_config: config structure, needs to be filled by app
> + * If fragment_size or fragments are zero, this means "don't care"
> + * and tinycompress will choose values that the driver supports
> + *
> + * @fragment_size: size of fragment requested, in bytes
> + * @fragments: number of fragments
> + * @codec: codec type and parameters requested
> + */
> +struct snd_compr_config {
> +	__u32 fragment_size;
> +	__u32 fragments;
> +	struct snd_codec *codec;
> +};
> +
> +struct snd_compr_gapless_mdata {
> +	__u32 encoder_delay;
> +	__u32 encoder_padding;
> +};
> +
> +#define COMPRESS_OUT        0x20000000
> +#define COMPRESS_IN         0x10000000
> +
> +struct snd_compr;
> +struct snd_compr_tstamp;
> +
> +/*
> + * snd_compr_open: open a new compress stream
> + * returns the valid struct snd_compr on success, NULL on failure
> + * If config does not specify a requested fragment size, on return
> + * it will be updated with the size and number of fragments that
> + * were configured
> + *
> + * @card: sound card number
> + * @device: device number
> + * @flags: device flags can be COMPRESS_OUT or COMPRESS_IN
> + * @config: stream config requested. Returns actual fragment config
> + */
> +struct snd_compr *snd_compr_open(unsigned int card, unsigned int device,
> +		unsigned int flags, struct snd_compr_config *config);
> +
> +/*
> + * snd_compr_close: close the compress stream
> + *
> + * @compr: compress stream to be closed
> + */
> +void snd_compr_close(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_get_hpointer: get the hw timestamp
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream on which query is made
> + * @avail: buffer availble for write/read, in bytes
> + * @tstamp: hw time
> + */
> +int snd_compr_get_hpointer(struct snd_compr *compr,
> +		unsigned int *avail, struct timespec *tstamp);
> +
> +
> +/*
> + * snd_compr_get_tstamp: get the raw hw timestamp
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream on which query is made
> + * @samples: number of decoded samples played
> + * @sampling_rate: sampling rate of decoded samples
> + */
> +int snd_compr_get_tstamp(struct snd_compr *compr,
> +		unsigned int *samples, unsigned int *sampling_rate);
> +
> +/*
> + * snd_compr_write: write data to the compress stream
> + * return bytes written on success, negative on error
> + * By default this is a blocking call and will not return
> + * until all bytes have been written or there was a
> + * write error.
> + * If non-blocking mode has been enabled with snd_compr_nonblock(),
> + * this function will write all bytes that can be written without
> + * blocking and will then return the number of bytes successfully
> + * written. If the return value is not an error and is < size
> + * the caller can use snd_compr_wait() to block until the driver
> + * is ready for more data.
> + *
> + * @compr: compress stream to be written to
> + * @buf: pointer to data
> + * @size: number of bytes to be written
> + */
> +int snd_compr_write(struct snd_compr *compr, const void *buf, unsigned int size);
> +
> +/*
> + * snd_compr_read: read data from the compress stream
> + * return bytes read on success, negative on error
> + * By default this is a blocking call and will block until
> + * size bytes have been written or there was a read error.
> + * If non-blocking mode was enabled using snd_compr_nonblock()
> + * the behaviour will change to read only as many bytes as
> + * are currently available (if no bytes are available it
> + * will return immediately). The caller can then use
> + * snd_compr_wait() to block until more bytes are available.
> + *
> + * @compr: compress stream from where data is to be read
> + * @buf: pointer to data buffer
> + * @size: size of given buffer
> + */
> +int snd_compr_read(struct snd_compr *compr, void *buf, unsigned int size);
> +
> +/*
> + * snd_compr_start: start the compress stream
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be started
> + */
> +int snd_compr_start(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_stop: stop the compress stream
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be stopped
> + */
> +int snd_compr_stop(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_pause: pause the compress stream
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be paused
> + */
> +int snd_compr_pause(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_resume: resume the compress stream
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be resumed
> + */
> +int snd_compr_resume(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_drain: drain the compress stream
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be drain
> + */
> +int snd_compr_drain(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_next_track: set the next track for stream
> + *
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be transistioned to next track
> + */
> +int snd_compr_next_track(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_partial_drain: drain will return after the last frame is decoded
> + * by DSP and will play the , All the data written into compressed
> + * ring buffer is decoded
> + *
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream to be drain
> + */
> +int snd_compr_partial_drain(struct snd_compr *compr);
> +
> +/*
> + * snd_compr_set_gapless_metadata: set gapless metadata of a compress strem
> + *
> + * return 0 on success, negative on error
> + *
> + * @compr: compress stream for which metadata has to set
> + * @mdata: metadata encoder delay and  padding
> + */
> +
> +int snd_compr_set_gapless_metadata(struct snd_compr *compr,
> +			struct snd_compr_gapless_mdata *mdata);
> +
> +/*
> + * snd_compr_is_codec_supported: check if the given codec is supported
> + * returns true when supported, false if not
> + *
> + * @card: sound card number
> + * @device: device number
> + * @flags: stream flags
> + * @codec: codec type and parameters to be checked
> + */
> +bool snd_compr_is_codec_supported(unsigned int card, unsigned int device,
> +	       unsigned int flags, struct snd_codec *codec);
> +
> +/*
> + * snd_compr_set_max_poll_wait: set the maximum time tinycompress
> + * will wait for driver to signal a poll(). Interval is in
> + * milliseconds.
> + * Pass interval of -1 to disable timeout and make poll() wait
> + * until driver signals.
> + * If this function is not used the timeout defaults to 20 seconds.
> + */
> +void snd_compr_set_max_poll_wait(struct snd_compr *compr, int milliseconds);
> +
> +/* Enable or disable non-blocking mode for write and read */
> +void snd_compr_nonblock(struct snd_compr *compr, int nonblock);
> +
> +/* Wait for ring buffer to ready for next read or write */
> +int snd_compr_wait(struct snd_compr *compr, int timeout_ms);
> +
> +int snd_compr_is_running(struct snd_compr *compr);
> +
> +int snd_compr_is_ready(struct snd_compr *compr);
> +
> +/* Returns a human readable reason for the last error */
> +const char *snd_compr_get_error(struct snd_compr *compr);
> +
> +#endif
> diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h
> new file mode 100644
> index 000000000000..22ed8cb7800b
> --- /dev/null
> +++ b/include/sound/compress_offload.h
> @@ -0,0 +1,191 @@
> +/*
> + *  compress_offload.h - compress offload header definations
> + *
> + *  Copyright (C) 2011 Intel Corporation
> + *  Authors:	Vinod Koul <vinod.koul at linux.intel.com>
> + *		Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
> + *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; version 2 of the License.

So this one is pulled form the kernel and with GPL, not LGPL. Whoa.

> + *
> + *  This program is distributed in the hope that it will be useful, but
> + *  WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License along
> + *  with this program; if not, write to the Free Software Foundation, Inc.,
> + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + */
> +#ifndef __COMPRESS_OFFLOAD_H
> +#define __COMPRESS_OFFLOAD_H
> +
> +#include <linux/types.h>
> +#include <sound/asound.h>
> +#include <sound/compress_params.h>
> +
> +
> +#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2)
> +/**
> + * struct snd_compressed_buffer - compressed buffer
> + * @fragment_size: size of buffer fragment in bytes
> + * @fragments: number of such fragments
> + */
> +struct snd_compressed_buffer {
> +	__u32 fragment_size;
> +	__u32 fragments;
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * struct snd_compr_params - compressed stream params
> + * @buffer: buffer description
> + * @codec: codec parameters
> + * @no_wake_mode: dont wake on fragment elapsed
> + */
> +struct snd_compr_params {
> +	struct snd_compressed_buffer buffer;
> +	struct snd_codec codec;
> +	__u8 no_wake_mode;
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * struct snd_compr_tstamp - timestamp descriptor
> + * @byte_offset: Byte offset in ring buffer to DSP
> + * @copied_total: Total number of bytes copied from/to ring buffer to/by DSP
> + * @pcm_frames: Frames decoded or encoded by DSP. This field will evolve by
> + *	large steps and should only be used to monitor encoding/decoding
> + *	progress. It shall not be used for timing estimates.
> + * @pcm_io_frames: Frames rendered or received by DSP into a mixer or an audio
> + * output/input. This field should be used for A/V sync or time estimates.
> + * @sampling_rate: sampling rate of audio
> + */
> +struct snd_compr_tstamp {
> +	__u32 byte_offset;
> +	__u32 copied_total;
> +	__u32 pcm_frames;
> +	__u32 pcm_io_frames;
> +	__u32 sampling_rate;
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * struct snd_compr_avail - avail descriptor
> + * @avail: Number of bytes available in ring buffer for writing/reading
> + * @tstamp: timestamp infomation
> + */
> +struct snd_compr_avail {
> +	__u64 avail;
> +	struct snd_compr_tstamp tstamp;
> +} __attribute__((packed, aligned(4)));
> +
> +enum snd_compr_direction {
> +	SND_COMPRESS_PLAYBACK = 0,
> +	SND_COMPRESS_CAPTURE
> +};
> +
> +/**
> + * struct snd_compr_caps - caps descriptor
> + * @codecs: pointer to array of codecs
> + * @direction: direction supported. Of type snd_compr_direction
> + * @min_fragment_size: minimum fragment supported by DSP
> + * @max_fragment_size: maximum fragment supported by DSP
> + * @min_fragments: min fragments supported by DSP
> + * @max_fragments: max fragments supported by DSP
> + * @num_codecs: number of codecs supported
> + * @reserved: reserved field
> + */
> +struct snd_compr_caps {
> +	__u32 num_codecs;
> +	__u32 direction;
> +	__u32 min_fragment_size;
> +	__u32 max_fragment_size;
> +	__u32 min_fragments;
> +	__u32 max_fragments;
> +	__u32 codecs[MAX_NUM_CODECS];
> +	__u32 reserved[11];
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * struct snd_compr_codec_caps - query capability of codec
> + * @codec: codec for which capability is queried
> + * @num_descriptors: number of codec descriptors
> + * @descriptor: array of codec capability descriptor
> + */
> +struct snd_compr_codec_caps {
> +	__u32 codec;
> +	__u32 num_descriptors;
> +	struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS];
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * enum sndrv_compress_encoder
> + * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the
> + * end of the track
> + * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
> + * beginning of the track
> + */
> +enum sndrv_compress_encoder {
> +	SNDRV_COMPRESS_ENCODER_PADDING = 1,
> +	SNDRV_COMPRESS_ENCODER_DELAY = 2,
> +};
> +
> +/**
> + * struct snd_compr_metadata - compressed stream metadata
> + * @key: key id
> + * @value: key value
> + */
> +struct snd_compr_metadata {
> +	 __u32 key;
> +	 __u32 value[8];
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * compress path ioctl definitions
> + * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP
> + * SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec
> + * SNDRV_COMPRESS_SET_PARAMS: Set codec and stream parameters
> + * Note: only codec params can be changed runtime and stream params cant be
> + * SNDRV_COMPRESS_GET_PARAMS: Query codec params
> + * SNDRV_COMPRESS_TSTAMP: get the current timestamp value
> + * SNDRV_COMPRESS_AVAIL: get the current buffer avail value.
> + * This also queries the tstamp properties
> + * SNDRV_COMPRESS_PAUSE: Pause the running stream
> + * SNDRV_COMPRESS_RESUME: resume a paused stream
> + * SNDRV_COMPRESS_START: Start a stream
> + * SNDRV_COMPRESS_STOP: stop a running stream, discarding ring buffer content
> + * and the buffers currently with DSP
> + * SNDRV_COMPRESS_DRAIN: Play till end of buffers and stop after that
> + * SNDRV_COMPRESS_IOCTL_VERSION: Query the API version
> + */
> +#define SNDRV_COMPRESS_IOCTL_VERSION	_IOR('C', 0x00, int)
> +#define SNDRV_COMPRESS_GET_CAPS		_IOWR('C', 0x10, struct snd_compr_caps)
> +#define SNDRV_COMPRESS_GET_CODEC_CAPS	_IOWR('C', 0x11,\
> +						struct snd_compr_codec_caps)
> +#define SNDRV_COMPRESS_SET_PARAMS	_IOW('C', 0x12, struct snd_compr_params)
> +#define SNDRV_COMPRESS_GET_PARAMS	_IOR('C', 0x13, struct snd_codec)
> +#define SNDRV_COMPRESS_SET_METADATA	_IOW('C', 0x14,\
> +						 struct snd_compr_metadata)
> +#define SNDRV_COMPRESS_GET_METADATA	_IOWR('C', 0x15,\
> +						 struct snd_compr_metadata)
> +#define SNDRV_COMPRESS_TSTAMP		_IOR('C', 0x20, struct snd_compr_tstamp)
> +#define SNDRV_COMPRESS_AVAIL		_IOR('C', 0x21, struct snd_compr_avail)
> +#define SNDRV_COMPRESS_PAUSE		_IO('C', 0x30)
> +#define SNDRV_COMPRESS_RESUME		_IO('C', 0x31)
> +#define SNDRV_COMPRESS_START		_IO('C', 0x32)
> +#define SNDRV_COMPRESS_STOP		_IO('C', 0x33)
> +#define SNDRV_COMPRESS_DRAIN		_IO('C', 0x34)
> +#define SNDRV_COMPRESS_NEXT_TRACK	_IO('C', 0x35)
> +#define SNDRV_COMPRESS_PARTIAL_DRAIN	_IO('C', 0x36)
> +/*
> + * TODO
> + * 1. add mmap support
> + *
> + */
> +#define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */
> +#define SND_COMPR_TRIGGER_NEXT_TRACK 8
> +#define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9
> +#endif
> diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h
> new file mode 100644
> index 000000000000..d9bd9ca0d5b0
> --- /dev/null
> +++ b/include/sound/compress_params.h
> @@ -0,0 +1,404 @@
> +/*
> + *  compress_params.h - codec types and parameters for compressed data
> + *  streaming interface
> + *
> + *  Copyright (C) 2011 Intel Corporation
> + *  Authors:	Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
> + *              Vinod Koul <vinod.koul at linux.intel.com>
> + *
> + *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; version 2 of the License.

same here.

> + *
> + *  This program is distributed in the hope that it will be useful, but
> + *  WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License along
> + *  with this program; if not, write to the Free Software Foundation, Inc.,
> + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * The definitions in this file are derived from the OpenMAX AL version 1.1
> + * and OpenMAX IL v 1.1.2 header files which contain the copyright notice below.
> + *
> + * Copyright (c) 2007-2010 The Khronos Group Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and/or associated documentation files (the
> + * "Materials "), to deal in the Materials without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Materials, and to
> + * permit persons to whom the Materials are furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included
> + * in all copies or substantial portions of the Materials.
> + *
> + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
> + *
> + */
> +#ifndef __SND_COMPRESS_PARAMS_H
> +#define __SND_COMPRESS_PARAMS_H
> +
> +#include <linux/types.h>
> +
> +/* AUDIO CODECS SUPPORTED */
> +#define MAX_NUM_CODECS 32
> +#define MAX_NUM_CODEC_DESCRIPTORS 32
> +#define MAX_NUM_BITRATES 32
> +#define MAX_NUM_SAMPLE_RATES 32
> +
> +/* Codecs are listed linearly to allow for extensibility */
> +#define SND_AUDIOCODEC_PCM                   ((__u32) 0x00000001)
> +#define SND_AUDIOCODEC_MP3                   ((__u32) 0x00000002)
> +#define SND_AUDIOCODEC_AMR                   ((__u32) 0x00000003)
> +#define SND_AUDIOCODEC_AMRWB                 ((__u32) 0x00000004)
> +#define SND_AUDIOCODEC_AMRWBPLUS             ((__u32) 0x00000005)
> +#define SND_AUDIOCODEC_AAC                   ((__u32) 0x00000006)
> +#define SND_AUDIOCODEC_WMA                   ((__u32) 0x00000007)
> +#define SND_AUDIOCODEC_REAL                  ((__u32) 0x00000008)
> +#define SND_AUDIOCODEC_VORBIS                ((__u32) 0x00000009)
> +#define SND_AUDIOCODEC_FLAC                  ((__u32) 0x0000000A)
> +#define SND_AUDIOCODEC_IEC61937              ((__u32) 0x0000000B)
> +#define SND_AUDIOCODEC_G723_1                ((__u32) 0x0000000C)
> +#define SND_AUDIOCODEC_G729                  ((__u32) 0x0000000D)
> +#define SND_AUDIOCODEC_MAX                   SND_AUDIOCODEC_G729
> +
> +/*
> + * Profile and modes are listed with bit masks. This allows for a
> + * more compact representation of fields that will not evolve
> + * (in contrast to the list of codecs)
> + */
> +
> +#define SND_AUDIOPROFILE_PCM                 ((__u32) 0x00000001)
> +
> +/* MP3 modes are only useful for encoders */
> +#define SND_AUDIOCHANMODE_MP3_MONO           ((__u32) 0x00000001)
> +#define SND_AUDIOCHANMODE_MP3_STEREO         ((__u32) 0x00000002)
> +#define SND_AUDIOCHANMODE_MP3_JOINTSTEREO    ((__u32) 0x00000004)
> +#define SND_AUDIOCHANMODE_MP3_DUAL           ((__u32) 0x00000008)
> +
> +#define SND_AUDIOPROFILE_AMR                 ((__u32) 0x00000001)
> +
> +/* AMR modes are only useful for encoders */
> +#define SND_AUDIOMODE_AMR_DTX_OFF            ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_AMR_VAD1               ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_AMR_VAD2               ((__u32) 0x00000004)
> +
> +#define SND_AUDIOSTREAMFORMAT_UNDEFINED	     ((__u32) 0x00000000)
> +#define SND_AUDIOSTREAMFORMAT_CONFORMANCE    ((__u32) 0x00000001)
> +#define SND_AUDIOSTREAMFORMAT_IF1            ((__u32) 0x00000002)
> +#define SND_AUDIOSTREAMFORMAT_IF2            ((__u32) 0x00000004)
> +#define SND_AUDIOSTREAMFORMAT_FSF            ((__u32) 0x00000008)
> +#define SND_AUDIOSTREAMFORMAT_RTPPAYLOAD     ((__u32) 0x00000010)
> +#define SND_AUDIOSTREAMFORMAT_ITU            ((__u32) 0x00000020)
> +
> +#define SND_AUDIOPROFILE_AMRWB               ((__u32) 0x00000001)
> +
> +/* AMRWB modes are only useful for encoders */
> +#define SND_AUDIOMODE_AMRWB_DTX_OFF          ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_AMRWB_VAD1             ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_AMRWB_VAD2             ((__u32) 0x00000004)
> +
> +#define SND_AUDIOPROFILE_AMRWBPLUS           ((__u32) 0x00000001)
> +
> +#define SND_AUDIOPROFILE_AAC                 ((__u32) 0x00000001)
> +
> +/* AAC modes are required for encoders and decoders */
> +#define SND_AUDIOMODE_AAC_MAIN               ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_AAC_LC                 ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_AAC_SSR                ((__u32) 0x00000004)
> +#define SND_AUDIOMODE_AAC_LTP                ((__u32) 0x00000008)
> +#define SND_AUDIOMODE_AAC_HE                 ((__u32) 0x00000010)
> +#define SND_AUDIOMODE_AAC_SCALABLE           ((__u32) 0x00000020)
> +#define SND_AUDIOMODE_AAC_ERLC               ((__u32) 0x00000040)
> +#define SND_AUDIOMODE_AAC_LD                 ((__u32) 0x00000080)
> +#define SND_AUDIOMODE_AAC_HE_PS              ((__u32) 0x00000100)
> +#define SND_AUDIOMODE_AAC_HE_MPS             ((__u32) 0x00000200)
> +
> +/* AAC formats are required for encoders and decoders */
> +#define SND_AUDIOSTREAMFORMAT_MP2ADTS        ((__u32) 0x00000001)
> +#define SND_AUDIOSTREAMFORMAT_MP4ADTS        ((__u32) 0x00000002)
> +#define SND_AUDIOSTREAMFORMAT_MP4LOAS        ((__u32) 0x00000004)
> +#define SND_AUDIOSTREAMFORMAT_MP4LATM        ((__u32) 0x00000008)
> +#define SND_AUDIOSTREAMFORMAT_ADIF           ((__u32) 0x00000010)
> +#define SND_AUDIOSTREAMFORMAT_MP4FF          ((__u32) 0x00000020)
> +#define SND_AUDIOSTREAMFORMAT_RAW            ((__u32) 0x00000040)
> +
> +#define SND_AUDIOPROFILE_WMA7                ((__u32) 0x00000001)
> +#define SND_AUDIOPROFILE_WMA8                ((__u32) 0x00000002)
> +#define SND_AUDIOPROFILE_WMA9                ((__u32) 0x00000004)
> +#define SND_AUDIOPROFILE_WMA10               ((__u32) 0x00000008)
> +
> +#define SND_AUDIOMODE_WMA_LEVEL1             ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_WMA_LEVEL2             ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_WMA_LEVEL3             ((__u32) 0x00000004)
> +#define SND_AUDIOMODE_WMA_LEVEL4             ((__u32) 0x00000008)
> +#define SND_AUDIOMODE_WMAPRO_LEVELM0         ((__u32) 0x00000010)
> +#define SND_AUDIOMODE_WMAPRO_LEVELM1         ((__u32) 0x00000020)
> +#define SND_AUDIOMODE_WMAPRO_LEVELM2         ((__u32) 0x00000040)
> +#define SND_AUDIOMODE_WMAPRO_LEVELM3         ((__u32) 0x00000080)
> +
> +#define SND_AUDIOSTREAMFORMAT_WMA_ASF        ((__u32) 0x00000001)
> +/*
> + * Some implementations strip the ASF header and only send ASF packets
> + * to the DSP
> + */
> +#define SND_AUDIOSTREAMFORMAT_WMA_NOASF_HDR  ((__u32) 0x00000002)
> +
> +#define SND_AUDIOPROFILE_REALAUDIO           ((__u32) 0x00000001)
> +
> +#define SND_AUDIOMODE_REALAUDIO_G2           ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_REALAUDIO_8            ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_REALAUDIO_10           ((__u32) 0x00000004)
> +#define SND_AUDIOMODE_REALAUDIO_SURROUND     ((__u32) 0x00000008)
> +
> +#define SND_AUDIOPROFILE_VORBIS              ((__u32) 0x00000001)
> +
> +#define SND_AUDIOMODE_VORBIS                 ((__u32) 0x00000001)
> +
> +#define SND_AUDIOPROFILE_FLAC                ((__u32) 0x00000001)
> +
> +/*
> + * Define quality levels for FLAC encoders, from LEVEL0 (fast)
> + * to LEVEL8 (best)
> + */
> +#define SND_AUDIOMODE_FLAC_LEVEL0            ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_FLAC_LEVEL1            ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_FLAC_LEVEL2            ((__u32) 0x00000004)
> +#define SND_AUDIOMODE_FLAC_LEVEL3            ((__u32) 0x00000008)
> +#define SND_AUDIOMODE_FLAC_LEVEL4            ((__u32) 0x00000010)
> +#define SND_AUDIOMODE_FLAC_LEVEL5            ((__u32) 0x00000020)
> +#define SND_AUDIOMODE_FLAC_LEVEL6            ((__u32) 0x00000040)
> +#define SND_AUDIOMODE_FLAC_LEVEL7            ((__u32) 0x00000080)
> +#define SND_AUDIOMODE_FLAC_LEVEL8            ((__u32) 0x00000100)
> +
> +#define SND_AUDIOSTREAMFORMAT_FLAC           ((__u32) 0x00000001)
> +#define SND_AUDIOSTREAMFORMAT_FLAC_OGG       ((__u32) 0x00000002)
> +
> +/* IEC61937 payloads without CUVP and preambles */
> +#define SND_AUDIOPROFILE_IEC61937            ((__u32) 0x00000001)
> +/* IEC61937 with S/PDIF preambles+CUVP bits in 32-bit containers */
> +#define SND_AUDIOPROFILE_IEC61937_SPDIF      ((__u32) 0x00000002)
> +
> +/*
> + * IEC modes are mandatory for decoders. Format autodetection
> + * will only happen on the DSP side with mode 0. The PCM mode should
> + * not be used, the PCM codec should be used instead.
> + */
> +#define SND_AUDIOMODE_IEC_REF_STREAM_HEADER  ((__u32) 0x00000000)
> +#define SND_AUDIOMODE_IEC_LPCM		     ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_IEC_AC3		     ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_IEC_MPEG1		     ((__u32) 0x00000004)
> +#define SND_AUDIOMODE_IEC_MP3		     ((__u32) 0x00000008)
> +#define SND_AUDIOMODE_IEC_MPEG2		     ((__u32) 0x00000010)
> +#define SND_AUDIOMODE_IEC_AACLC		     ((__u32) 0x00000020)
> +#define SND_AUDIOMODE_IEC_DTS		     ((__u32) 0x00000040)
> +#define SND_AUDIOMODE_IEC_ATRAC		     ((__u32) 0x00000080)
> +#define SND_AUDIOMODE_IEC_SACD		     ((__u32) 0x00000100)
> +#define SND_AUDIOMODE_IEC_EAC3		     ((__u32) 0x00000200)
> +#define SND_AUDIOMODE_IEC_DTS_HD	     ((__u32) 0x00000400)
> +#define SND_AUDIOMODE_IEC_MLP		     ((__u32) 0x00000800)
> +#define SND_AUDIOMODE_IEC_DST		     ((__u32) 0x00001000)
> +#define SND_AUDIOMODE_IEC_WMAPRO	     ((__u32) 0x00002000)
> +#define SND_AUDIOMODE_IEC_REF_CXT            ((__u32) 0x00004000)
> +#define SND_AUDIOMODE_IEC_HE_AAC	     ((__u32) 0x00008000)
> +#define SND_AUDIOMODE_IEC_HE_AAC2	     ((__u32) 0x00010000)
> +#define SND_AUDIOMODE_IEC_MPEG_SURROUND	     ((__u32) 0x00020000)
> +
> +#define SND_AUDIOPROFILE_G723_1              ((__u32) 0x00000001)
> +
> +#define SND_AUDIOMODE_G723_1_ANNEX_A         ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_G723_1_ANNEX_B         ((__u32) 0x00000002)
> +#define SND_AUDIOMODE_G723_1_ANNEX_C         ((__u32) 0x00000004)
> +
> +#define SND_AUDIOPROFILE_G729                ((__u32) 0x00000001)
> +
> +#define SND_AUDIOMODE_G729_ANNEX_A           ((__u32) 0x00000001)
> +#define SND_AUDIOMODE_G729_ANNEX_B           ((__u32) 0x00000002)
> +
> +/* <FIXME: multichannel encoders aren't supported for now. Would need
> +   an additional definition of channel arrangement> */
> +
> +/* VBR/CBR definitions */
> +#define SND_RATECONTROLMODE_CONSTANTBITRATE  ((__u32) 0x00000001)
> +#define SND_RATECONTROLMODE_VARIABLEBITRATE  ((__u32) 0x00000002)
> +
> +/* Encoder options */
> +
> +struct snd_enc_wma {
> +	__u32 super_block_align; /* WMA Type-specific data */
> +};
> +
> +
> +/**
> + * struct snd_enc_vorbis
> + * @quality: Sets encoding quality to n, between -1 (low) and 10 (high).
> + * In the default mode of operation, the quality level is 3.
> + * Normal quality range is 0 - 10.
> + * @managed: Boolean. Set  bitrate  management  mode. This turns off the
> + * normal VBR encoding, but allows hard or soft bitrate constraints to be
> + * enforced by the encoder. This mode can be slower, and may also be
> + * lower quality. It is primarily useful for streaming.
> + * @max_bit_rate: Enabled only if managed is TRUE
> + * @min_bit_rate: Enabled only if managed is TRUE
> + * @downmix: Boolean. Downmix input from stereo to mono (has no effect on
> + * non-stereo streams). Useful for lower-bitrate encoding.
> + *
> + * These options were extracted from the OpenMAX IL spec and Gstreamer vorbisenc
> + * properties
> + *
> + * For best quality users should specify VBR mode and set quality levels.
> + */
> +
> +struct snd_enc_vorbis {
> +	__s32 quality;
> +	__u32 managed;
> +	__u32 max_bit_rate;
> +	__u32 min_bit_rate;
> +	__u32 downmix;
> +} __attribute__((packed, aligned(4)));
> +
> +
> +/**
> + * struct snd_enc_real
> + * @quant_bits: number of coupling quantization bits in the stream
> + * @start_region: coupling start region in the stream
> + * @num_regions: number of regions value
> + *
> + * These options were extracted from the OpenMAX IL spec
> + */
> +
> +struct snd_enc_real {
> +	__u32 quant_bits;
> +	__u32 start_region;
> +	__u32 num_regions;
> +} __attribute__((packed, aligned(4)));
> +
> +/**
> + * struct snd_enc_flac
> + * @num: serial number, valid only for OGG formats
> + *	needs to be set by application
> + * @gain: Add replay gain tags
> + *
> + * These options were extracted from the FLAC online documentation
> + * at http://flac.sourceforge.net/documentation_tools_flac.html
> + *
> + * To make the API simpler, it is assumed that the user will select quality
> + * profiles. Additional options that affect encoding quality and speed can
> + * be added at a later stage if needed.
> + *
> + * By default the Subset format is used by encoders.
> + *
> + * TAGS such as pictures, etc, cannot be handled by an offloaded encoder and are
> + * not supported in this API.
> + */
> +
> +struct snd_enc_flac {
> +	__u32 num;
> +	__u32 gain;
> +} __attribute__((packed, aligned(4)));
> +
> +struct snd_enc_generic {
> +	__u32 bw;	/* encoder bandwidth */
> +	__s32 reserved[15];
> +} __attribute__((packed, aligned(4)));
> +
> +union snd_codec_options {
> +	struct snd_enc_wma wma;
> +	struct snd_enc_vorbis vorbis;
> +	struct snd_enc_real real;
> +	struct snd_enc_flac flac;
> +	struct snd_enc_generic generic;
> +} __attribute__((packed, aligned(4)));
> +
> +/** struct snd_codec_desc - description of codec capabilities
> + * @max_ch: Maximum number of audio channels
> + * @sample_rates: Sampling rates in Hz, use values like 48000 for this
> + * @num_sample_rates: Number of valid values in sample_rates array
> + * @bit_rate: Indexed array containing supported bit rates
> + * @num_bitrates: Number of valid values in bit_rate array
> + * @rate_control: value is specified by SND_RATECONTROLMODE defines.
> + * @profiles: Supported profiles. See SND_AUDIOPROFILE defines.
> + * @modes: Supported modes. See SND_AUDIOMODE defines
> + * @formats: Supported formats. See SND_AUDIOSTREAMFORMAT defines
> + * @min_buffer: Minimum buffer size handled by codec implementation
> + * @reserved: reserved for future use
> + *
> + * This structure provides a scalar value for profiles, modes and stream
> + * format fields.
> + * If an implementation supports multiple combinations, they will be listed as
> + * codecs with different descriptors, for example there would be 2 descriptors
> + * for AAC-RAW and AAC-ADTS.
> + * This entails some redundancy but makes it easier to avoid invalid
> + * configurations.
> + *
> + */
> +
> +struct snd_codec_desc {
> +	__u32 max_ch;
> +	__u32 sample_rates[MAX_NUM_SAMPLE_RATES];
> +	__u32 num_sample_rates;
> +	__u32 bit_rate[MAX_NUM_BITRATES];
> +	__u32 num_bitrates;
> +	__u32 rate_control;
> +	__u32 profiles;
> +	__u32 modes;
> +	__u32 formats;
> +	__u32 min_buffer;
> +	__u32 reserved[15];
> +} __attribute__((packed, aligned(4)));
> +
> +/** struct snd_codec
> + * @id: Identifies the supported audio encoder/decoder.
> + *		See SND_AUDIOCODEC macros.
> + * @ch_in: Number of input audio channels
> + * @ch_out: Number of output channels. In case of contradiction between
> + *		this field and the channelMode field, the channelMode field
> + *		overrides.
> + * @sample_rate: Audio sample rate of input data in Hz, use values like 48000
> + *		for this.
> + * @bit_rate: Bitrate of encoded data. May be ignored by decoders
> + * @rate_control: Encoding rate control. See SND_RATECONTROLMODE defines.
> + *               Encoders may rely on profiles for quality levels.
> + *		 May be ignored by decoders.
> + * @profile: Mandatory for encoders, can be mandatory for specific
> + *		decoders as well. See SND_AUDIOPROFILE defines.
> + * @level: Supported level (Only used by WMA at the moment)
> + * @ch_mode: Channel mode for encoder. See SND_AUDIOCHANMODE defines
> + * @format: Format of encoded bistream. Mandatory when defined.
> + *		See SND_AUDIOSTREAMFORMAT defines.
> + * @align: Block alignment in bytes of an audio sample.
> + *		Only required for PCM or IEC formats.
> + * @options: encoder-specific settings
> + * @reserved: reserved for future use
> + */
> +
> +struct snd_codec {
> +	__u32 id;
> +	__u32 ch_in;
> +	__u32 ch_out;
> +	__u32 sample_rate;
> +	__u32 bit_rate;
> +	__u32 rate_control;
> +	__u32 profile;
> +	__u32 level;
> +	__u32 ch_mode;
> +	__u32 format;
> +	__u32 align;
> +	union snd_codec_options options;
> +	__u32 reserved[3];
> +} __attribute__((packed, aligned(4)));
> +
> +#endif
> diff --git a/src/Makefile.am b/src/Makefile.am
> index fa255ff43ee0..3930986946cf 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -26,6 +26,10 @@ if BUILD_PCM
>   SUBDIRS += pcm timer
>   libasound_la_LIBADD += pcm/libpcm.la timer/libtimer.la
>   endif
> +if BUILD_COMPRESS
> +SUBDIRS += compress
> +libasound_la_LIBADD += compress/libcompress.la
> +endif
>   if BUILD_RAWMIDI
>   SUBDIRS += rawmidi
>   libasound_la_LIBADD += rawmidi/librawmidi.la
> @@ -66,6 +70,9 @@ pcm/libpcm.la:
>   ordinary_pcm/libordinarypcm.la:
>   	$(MAKE) -C ordinary_pcm libordinarypcm.la
>
> +pcm/libcompress.la:
> +	$(MAKE) -C compress libcompress.la
> +
>   rawmidi/librawmidi.la:
>   	$(MAKE) -C rawmidi librawmidi.la
>
> diff --git a/src/compress/Makefile.am b/src/compress/Makefile.am
> new file mode 100644
> index 000000000000..893871ab00a5
> --- /dev/null
> +++ b/src/compress/Makefile.am
> @@ -0,0 +1,8 @@
> +EXTRA_LTLIBRARIES=libcompress.la
> +
> +libcompress_la_SOURCES = compress.c
> +
> +all: libcompress.la
> +
> +
> +AM_CPPFLAGS=-I$(top_srcdir)/include
> diff --git a/src/compress/compress.c b/src/compress/compress.c
> new file mode 100644
> index 000000000000..e3fe828f2b1b
> --- /dev/null
> +++ b/src/compress/compress.c
> @@ -0,0 +1,599 @@
> +/*
> + * tinycompress library for compress audio offload in alsa
> + * Copyright (c) 2011-2012, Intel Corporation.
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU Lesser General Public License,
> + * version 2.1, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
> + * License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to
> + * the Free Software Foundation, Inc.,
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <unistd.h>
> +#include <poll.h>
> +#include <stdbool.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <sys/time.h>
> +#include <limits.h>
> +
> +#include <linux/types.h>
> +#include <linux/ioctl.h>
> +#define __force
> +#define __bitwise
> +#define __user
> +#include <sound/asound.h>
> +#include "sound/compress_params.h"
> +#include "sound/compress_offload.h"
> +
> +#include "compress.h"
> +
> +#define COMPR_ERR_MAX 128
> +
> +/* Default maximum time we will wait in a poll() - 20 seconds */
> +#define DEFAULT_MAX_POLL_WAIT_MS    20000
> +
> +struct snd_compr {
> +	int fd;
> +	unsigned int flags;
> +	char error[COMPR_ERR_MAX];
> +	struct snd_compr_config *config;
> +	int running;
> +	int max_poll_wait_ms;
> +	int nonblocking;
> +	unsigned int gapless_metadata;
> +	unsigned int next_track;
> +};
> +
> +static int oops(struct snd_compr *compr, int e, const char *fmt, ...)
> +{
> +	va_list ap;
> +	int sz;
> +
> +	va_start(ap, fmt);
> +	vsnprintf(compr->error, COMPR_ERR_MAX, fmt, ap);
> +	va_end(ap);
> +	sz = strlen(compr->error);
> +
> +	snprintf(compr->error + sz, COMPR_ERR_MAX - sz,
> +		": %s", strerror(e));
> +	errno = e;
> +
> +	return -1;
> +}
> +
> +const char *snd_compr_get_error(struct snd_compr *compr)
> +{
> +	return compr->error;
> +}
> +static struct snd_compr bad_compress = {
> +	.fd = -1,
> +};
> +
> +int snd_compr_is_running(struct snd_compr *compr)
> +{
> +	return ((compr->fd > 0) && compr->running) ? 1 : 0;
> +}
> +
> +int snd_compr_is_ready(struct snd_compr *compr)
> +{
> +	return (compr->fd > 0) ? 1 : 0;
> +}
> +
> +static int snd_compr_get_version(struct snd_compr *compr)
> +{
> +	int version = 0;
> +
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) {
> +		oops(compr, errno, "cant read version");
> +		return -1;
> +	}
> +	return version;
> +}
> +
> +static bool _snd_compr_snd_compr_is_codec_supported(struct snd_compr *compr,
> +		struct snd_compr_config *config, const struct snd_compr_caps *caps)
> +{
> +	bool codec = false;
> +	unsigned int i;
> +
> +	for (i = 0; i < caps->num_codecs; i++) {
> +		if (caps->codecs[i] == config->codec->id) {
> +			/* found the codec */
> +			codec = true;
> +			break;
> +		}
> +	}
> +	if (codec == false) {
> +		oops(compr, ENXIO, "this codec is not supported");
> +		return false;
> +	}
> +
> +	if (config->fragment_size < caps->min_fragment_size) {
> +		oops(compr, EINVAL, "requested fragment size %d is below min supported %d",
> +			config->fragment_size, caps->min_fragment_size);
> +		return false;
> +	}
> +	if (config->fragment_size > caps->max_fragment_size) {
> +		oops(compr, EINVAL, "requested fragment size %d is above max supported %d",
> +			config->fragment_size, caps->max_fragment_size);
> +		return false;
> +	}
> +	if (config->fragments < caps->min_fragments) {
> +		oops(compr, EINVAL, "requested fragments %d are below min supported %d",
> +			config->fragments, caps->min_fragments);
> +		return false;
> +	}
> +	if (config->fragments > caps->max_fragments) {
> +		oops(compr, EINVAL, "requested fragments %d are above max supported %d",
> +			config->fragments, caps->max_fragments);
> +		return false;
> +	}
> +
> +	/* TODO: match the codec properties */
> +	return true;
> +}
> +
> +static bool _snd_compr_is_codec_type_supported(int fd, struct snd_codec *codec)
> +{
> +	struct snd_compr_caps caps;
> +	bool found = false;
> +	unsigned int i;
> +
> +	if (ioctl(fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
> +		oops(&bad_compress, errno, "cannot get device caps");
> +		return false;
> +	}
> +
> +	for (i = 0; i < caps.num_codecs; i++) {
> +		if (caps.codecs[i] == codec->id) {
> +			/* found the codec */
> +			found = true;
> +			break;
> +		}
> +	}
> +	/* TODO: match the codec properties */
> +	return found;
> +}
> +
> +static inline void
> +snd_compr_fill_params(struct snd_compr_config *config, struct snd_compr_params *params)
> +{
> +	params->buffer.fragment_size = config->fragment_size;
> +	params->buffer.fragments = config->fragments;
> +	memcpy(&params->codec, config->codec, sizeof(params->codec));
> +}
> +
> +struct snd_compr *snd_compr_open(unsigned int card, unsigned int device,
> +		unsigned int flags, struct snd_compr_config *config)
> +{
> +	struct snd_compr *compr;
> +	struct snd_compr_params params;
> +	struct snd_compr_caps caps;
> +	char fn[256];
> +
> +	if (!config) {
> +		oops(&bad_compress, EINVAL, "passed bad config");
> +		return &bad_compress;
> +	}
> +
> +	compr = calloc(1, sizeof(struct snd_compr));
> +	if (!compr) {
> +		oops(&bad_compress, errno, "cannot allocate compress object");
> +		return &bad_compress;
> +	}
> +
> +	compr->next_track = 0;
> +	compr->gapless_metadata = 0;
> +	compr->config = calloc(1, sizeof(*config));
> +	if (!compr->config)
> +		goto input_fail;
> +
> +	snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
> +
> +	compr->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS;
> +
> +	compr->flags = flags;
> +	if (!((flags & COMPRESS_OUT) || (flags & COMPRESS_IN))) {
> +		oops(&bad_compress, EINVAL, "can't deduce device direction from given flags");
> +		goto config_fail;
> +	}
> +
> +	if (flags & COMPRESS_OUT) {
> +		compr->fd = open(fn, O_RDONLY);
> +	} else {
> +		compr->fd = open(fn, O_WRONLY);
> +	}
> +	if (compr->fd < 0) {
> +		oops(&bad_compress, errno, "cannot open device '%s'", fn);
> +		goto config_fail;
> +	}
> +
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
> +		oops(compr, errno, "cannot get device caps");
> +		goto codec_fail;
> +	}
> +
> +	/* If caller passed "don't care" fill in default values */
> +	if ((config->fragment_size == 0) || (config->fragments == 0)) {
> +		config->fragment_size = caps.min_fragment_size;
> +		config->fragments = caps.max_fragments;
> +	}
> +
> +#if 0
> +	/* FIXME need to turn this On when DSP supports
> +	 * and treat in no support case
> +	 */
> +	if (_snd_compr_snd_compr_is_codec_supported(compr, config, &caps) == false) {
> +		oops(compr, errno, "codec not supported\n");
> +		goto codec_fail;
> +	}
> +#endif
> +
> +	memcpy(compr->config, config, sizeof(*compr->config));
> +	snd_compr_fill_params(config, &params);
> +
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_SET_PARAMS, &params)) {
> +		oops(&bad_compress, errno, "cannot set device");
> +		goto codec_fail;
> +	}
> +
> +	return compr;
> +
> +codec_fail:
> +	close(compr->fd);
> +	compr->fd = -1;
> +config_fail:
> +	free(compr->config);
> +input_fail:
> +	free(compr);
> +	return &bad_compress;
> +}
> +
> +void snd_compr_close(struct snd_compr *compr)
> +{
> +	if (compr == &bad_compress)
> +		return;
> +
> +	if (compr->fd >= 0)
> +		close(compr->fd);
> +	compr->running = 0;
> +	compr->fd = -1;
> +	free(compr->config);
> +	free(compr);
> +}
> +
> +int snd_compr_get_hpointer(struct snd_compr *compr,
> +		unsigned int *avail, struct timespec *tstamp)
> +{
> +	struct snd_compr_avail kavail;
> +	__u64 time;
> +
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_AVAIL, &kavail))
> +		return oops(compr, errno, "cannot get avail");
> +	if (0 == kavail.tstamp.sampling_rate)
> +		return oops(compr, ENODATA, "sample rate unknown");
> +	*avail = (unsigned int)kavail.avail;
> +	time = kavail.tstamp.pcm_io_frames / kavail.tstamp.sampling_rate;
> +	tstamp->tv_sec = time;
> +	time = kavail.tstamp.pcm_io_frames % kavail.tstamp.sampling_rate;
> +	tstamp->tv_nsec = time * 1000000000 / kavail.tstamp.sampling_rate;
> +	return 0;
> +}
> +
> +int snd_compr_get_tstamp(struct snd_compr *compr,
> +			unsigned int *samples, unsigned int *sampling_rate)
> +{
> +	struct snd_compr_tstamp ktstamp;
> +
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp))
> +		return oops(compr, errno, "cannot get tstamp");
> +
> +	*samples = ktstamp.pcm_io_frames;
> +	*sampling_rate = ktstamp.sampling_rate;
> +	return 0;
> +}
> +
> +int snd_compr_write(struct snd_compr *compr, const void *buf, unsigned int size)
> +{
> +	struct snd_compr_avail avail;
> +	struct pollfd fds;
> +	int to_write = 0;	/* zero indicates we haven't written yet */
> +	int written, total = 0, ret;
> +	const char* cbuf = buf;
> +	const unsigned int frag_size = compr->config->fragment_size;
> +
> +	if (!(compr->flags & COMPRESS_IN))
> +		return oops(compr, EINVAL, "Invalid flag set");
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	fds.fd = compr->fd;
> +	fds.events = POLLOUT;
> +
> +	/*TODO: treat auto start here first */
> +	while (size) {
> +		if (ioctl(compr->fd, SNDRV_COMPRESS_AVAIL, &avail))
> +			return oops(compr, errno, "cannot get avail");
> +
> +		/* We can write if we have at least one fragment available
> +		 * or there is enough space for all remaining data
> +		 */
> +		if ((avail.avail < frag_size) && (avail.avail < size)) {
> +
> +			if (compr->nonblocking)
> +				return total;
> +
> +			ret = poll(&fds, 1, compr->max_poll_wait_ms);
> +			if (fds.revents & POLLERR) {
> +				return oops(compr, EIO, "poll returned error!");
> +			}
> +			/* A pause will cause -EBADFD or zero.
> +			 * This is not an error, just stop writing */
> +			if ((ret == 0) || (ret == -EBADFD))
> +				break;
> +			if (ret < 0)
> +				return oops(compr, errno, "poll error");
> +			if (fds.revents & POLLOUT) {
> +				continue;
> +			}
> +		}
> +		/* write avail bytes */
> +		if (size > avail.avail)
> +			to_write =  avail.avail;
> +		else
> +			to_write = size;
> +		written = write(compr->fd, cbuf, to_write);
> +		/* If play was paused the write returns -EBADFD */
> +		if (written == -EBADFD)
> +			break;
> +		if (written < 0)
> +			return oops(compr, errno, "write failed!");
> +
> +		size -= written;
> +		cbuf += written;
> +		total += written;
> +	}
> +	return total;
> +}
> +
> +int snd_compr_read(struct snd_compr *compr, void *buf, unsigned int size)
> +{
> +	struct snd_compr_avail avail;
> +	struct pollfd fds;
> +	int to_read = 0;
> +	int num_read, total = 0, ret;
> +	char* cbuf = buf;
> +	const unsigned int frag_size = compr->config->fragment_size;
> +
> +	if (!(compr->flags & COMPRESS_OUT))
> +		return oops(compr, EINVAL, "Invalid flag set");
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	fds.fd = compr->fd;
> +	fds.events = POLLIN;
> +
> +	while (size) {
> +		if (ioctl(compr->fd, SNDRV_COMPRESS_AVAIL, &avail))
> +			return oops(compr, errno, "cannot get avail");
> +
> +		if ( (avail.avail < frag_size) && (avail.avail < size) ) {
> +			/* Less than one fragment available and not at the
> +			 * end of the read, so poll
> +			 */
> +			if (compr->nonblocking)
> +				return total;
> +
> +			ret = poll(&fds, 1, compr->max_poll_wait_ms);
> +			if (fds.revents & POLLERR) {
> +				return oops(compr, EIO, "poll returned error!");
> +			}
> +			/* A pause will cause -EBADFD or zero.
> +			 * This is not an error, just stop reading */
> +			if ((ret == 0) || (ret == -EBADFD))
> +				break;
> +			if (ret < 0)
> +				return oops(compr, errno, "poll error");
> +			if (fds.revents & POLLIN) {
> +				continue;
> +			}
> +		}
> +		/* read avail bytes */
> +		if (size > avail.avail)
> +			to_read = avail.avail;
> +		else
> +			to_read = size;
> +		num_read = read(compr->fd, cbuf, to_read);
> +		/* If play was paused the read returns -EBADFD */
> +		if (num_read == -EBADFD)
> +			break;
> +		if (num_read < 0)
> +			return oops(compr, errno, "read failed!");
> +
> +		size -= num_read;
> +		cbuf += num_read;
> +		total += num_read;
> +	}
> +
> +	return total;
> +}
> +
> +int snd_compr_start(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_START))
> +		return oops(compr, errno, "cannot start the stream");
> +	compr->running = 1;
> +	return 0;
> +
> +}
> +
> +int snd_compr_stop(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_running(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_STOP))
> +		return oops(compr, errno, "cannot stop the stream");
> +	return 0;
> +}
> +
> +int snd_compr_pause(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_running(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_PAUSE))
> +		return oops(compr, errno, "cannot pause the stream");
> +	return 0;
> +}
> +
> +int snd_compr_resume(struct snd_compr *compr)
> +{
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_RESUME))
> +		return oops(compr, errno, "cannot resume the stream");
> +	return 0;
> +}
> +
> +int snd_compr_drain(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_running(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_DRAIN))
> +		return oops(compr, errno, "cannot drain the stream");
> +	return 0;
> +}
> +
> +int snd_compr_partial_drain(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_running(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +
> +	if (!compr->next_track)
> +		return oops(compr, EPERM, "next track not signalled");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_PARTIAL_DRAIN))
> +		return oops(compr, errno, "cannot drain the stream\n");
> +	compr->next_track = 0;
> +	return 0;
> +}
> +
> +int snd_compr_next_track(struct snd_compr *compr)
> +{
> +	if (!snd_compr_is_running(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +
> +	if (!compr->gapless_metadata)
> +		return oops(compr, EPERM, "metadata not set");
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_NEXT_TRACK))
> +		return oops(compr, errno, "cannot set next track\n");
> +	compr->next_track = 1;
> +	compr->gapless_metadata = 0;
> +	return 0;
> +}
> +
> +int snd_compr_set_gapless_metadata(struct snd_compr *compr,
> +	struct snd_compr_gapless_mdata *mdata)
> +{
> +	struct snd_compr_metadata metadata;
> +	int version;
> +
> +	if (!snd_compr_is_ready(compr))
> +		return oops(compr, ENODEV, "device not ready");
> +
> +	version = snd_compr_get_version(compr);
> +	if (version <= 0)
> +		return -1;
> +
> +	if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1))
> +		return oops(compr, ENXIO, "gapless apis not supported in kernel");
> +
> +	metadata.key = SNDRV_COMPRESS_ENCODER_PADDING;
> +	metadata.value[0] = mdata->encoder_padding;
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
> +		return oops(compr, errno, "can't set metadata for stream\n");
> +
> +	metadata.key = SNDRV_COMPRESS_ENCODER_DELAY;
> +	metadata.value[0] = mdata->encoder_delay;
> +	if (ioctl(compr->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
> +		return oops(compr, errno, "can't set metadata for stream\n");
> +	compr->gapless_metadata = 1;
> +	return 0;
> +}
> +
> +bool snd_compr_is_codec_supported(unsigned int card, unsigned int device,
> +		unsigned int flags, struct snd_codec *codec)
> +{
> +	unsigned int dev_flag;
> +	bool ret;
> +	int fd;
> +	char fn[256];
> +
> +	snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
> +
> +	if (flags & COMPRESS_OUT)
> +		dev_flag = O_RDONLY;
> +	else
> +		dev_flag = O_WRONLY;
> +
> +	fd = open(fn, dev_flag);
> +	if (fd < 0)
> +		return oops(&bad_compress, errno, "cannot open device '%s'", fn);
> +
> +	ret = _snd_compr_is_codec_type_supported(fd, codec);
> +
> +	close(fd);
> +	return ret;
> +}
> +
> +void snd_compr_set_max_poll_wait(struct snd_compr *compr, int milliseconds)
> +{
> +	compr->max_poll_wait_ms = milliseconds;
> +}
> +
> +void snd_compr_nonblock(struct snd_compr *compr, int nonblock)
> +{
> +	compr->nonblocking = !!nonblock;
> +}
> +
> +int snd_compr_wait(struct snd_compr *compr, int timeout_ms)
> +{
> +	struct pollfd fds;
> +	int ret;
> +
> +	fds.fd = compr->fd;
> +	fds.events = POLLOUT | POLLIN;
> +
> +	ret = poll(&fds, 1, timeout_ms);
> +	if (ret > 0) {
> +		if (fds.revents & POLLERR)
> +			return oops(compr, EIO, "poll returned error!");
> +		if (fds.revents & (POLLOUT | POLLIN))
> +			return 0;
> +	}
> +	if (ret == 0)
> +		return oops(compr, ETIME, "poll timed out");
> +	if (ret < 0)
> +		return oops(compr, errno, "poll error");
> +
> +	return oops(compr, EIO, "poll signalled unhandled event");
> +}
> +
>



More information about the Alsa-devel mailing list