]> git.alsa-project.org Git - tinycompress.git/commitdiff
src: lib: Move functionality to compress_hw.c
authorRohit kumar <rohitkr@codeaurora.org>
Tue, 22 Sep 2020 10:53:39 +0000 (16:23 +0530)
committerRohit kumar <rohitkr@codeaurora.org>
Thu, 1 Oct 2020 08:09:55 +0000 (13:39 +0530)
Add compress_hw.c to handle all operations associated
with HW compress nodes. Move handling to compress_hw
using compress_hw_ops.

Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
Android.mk
src/lib/Makefile.am
src/lib/compress.c
src/lib/compress_hw.c [new file with mode: 0644]

index d201cfa1abfff9018bc9cdb769aab2af53536813..6cb024f4ca89eb2a1ddc6ee333bcfbd61c25e2c8 100644 (file)
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include
-LOCAL_SRC_FILES:= src/lib/compress.c
+LOCAL_SRC_FILES:= src/lib/compress.c src/lib/compress_hw.c
 LOCAL_MODULE := libtinycompress
 LOCAL_SHARED_LIBRARIES:= libcutils libutils
 LOCAL_MODULE_TAGS := optional
index 4baeab5dd07adea1b71fd06afb435e5225da30da..db7ee276b894c2539ec46b94dfe80e0e8275114e 100644 (file)
@@ -1,5 +1,5 @@
 tinycompressdir = $(libdir)
 
 tinycompress_LTLIBRARIES = libtinycompress.la
-libtinycompress_la_SOURCES = compress.c
+libtinycompress_la_SOURCES = compress.c compress_hw.c
 libtinycompress_la_CFLAGS = -I$(top_srcdir)/include
index bba4fcfcc5f20b98551e64888ba9d4a356d81009..b473a00a18b5c9b66e166b7f80c22893a6b6db26 100644 (file)
  * 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 "tinycompress/tinycompress.h"
-
-#define COMPR_ERR_MAX 128
-
-/* Default maximum time we will wait in a poll() - 20 seconds */
-#define DEFAULT_MAX_POLL_WAIT_MS    20000
+#include "tinycompress/compress_ops.h"
 
 struct compress {
-       int fd;
-       unsigned int flags;
-       char error[COMPR_ERR_MAX];
-       struct compr_config *config;
-       int running;
-       int max_poll_wait_ms;
-       int nonblocking;
-       unsigned int gapless_metadata;
-       unsigned int next_track;
+       struct compress_ops *ops;
+       void *data;
 };
 
-static int oops(struct compress *compress, int e, const char *fmt, ...)
-{
-       va_list ap;
-       int sz;
-
-       va_start(ap, fmt);
-       vsnprintf(compress->error, COMPR_ERR_MAX, fmt, ap);
-       va_end(ap);
-       sz = strlen(compress->error);
-
-       snprintf(compress->error + sz, COMPR_ERR_MAX - sz,
-               ": %s", strerror(e));
-       errno = e;
-
-       return -1;
-}
+extern struct compress_ops compress_hw_ops;
 
 const char *compress_get_error(struct compress *compress)
 {
-       return compress->error;
+       return compress->ops->get_error(compress->data);
 }
-static struct compress bad_compress = {
-       .fd = -1,
-};
 
 int is_compress_running(struct compress *compress)
 {
-       return ((compress->fd > 0) && compress->running) ? 1 : 0;
+       return compress->ops->is_compress_running(compress->data);
 }
 
 int is_compress_ready(struct compress *compress)
 {
-       return (compress->fd > 0) ? 1 : 0;
-}
-
-static int get_compress_version(struct compress *compress)
-{
-       int version = 0;
-
-       if (ioctl(compress->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) {
-               oops(compress, errno, "cant read version");
-               return -1;
-       }
-       return version;
-}
-
-static bool _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
-fill_compress_params(struct 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));
+       return compress->ops->is_compress_ready(compress->data);
 }
 
 struct compress *compress_open(unsigned int card, unsigned int device,
                unsigned int flags, struct compr_config *config)
 {
        struct compress *compress;
-       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;
-       }
 
        compress = calloc(1, sizeof(struct compress));
-       if (!compress) {
-               oops(&bad_compress, errno, "cannot allocate compress object");
-               return &bad_compress;
-       }
-
-       compress->next_track = 0;
-       compress->gapless_metadata = 0;
-       compress->config = calloc(1, sizeof(*config));
-       if (!compress->config)
-               goto input_fail;
-
-       snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
-
-       compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS;
-
-       compress->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) {
-               compress->fd = open(fn, O_RDONLY);
-       } else {
-               compress->fd = open(fn, O_WRONLY);
-       }
-       if (compress->fd < 0) {
-               oops(&bad_compress, errno, "cannot open device '%s'", fn);
-               goto config_fail;
-       }
-
-       if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
-               oops(compress, 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;
-       }
-
-       memcpy(compress->config, config, sizeof(*compress->config));
-       fill_compress_params(config, &params);
-
-       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, &params)) {
-               oops(&bad_compress, errno, "cannot set device");
-               goto codec_fail;
+       if (!compress)
+               return NULL;
+
+       compress->ops = &compress_hw_ops;
+       compress->data =  compress->ops->open(card, device, flags, config);
+       if (compress->data == NULL) {
+               free(compress);
+               return NULL;
        }
-
        return compress;
-
-codec_fail:
-       close(compress->fd);
-       compress->fd = -1;
-config_fail:
-       free(compress->config);
-input_fail:
-       free(compress);
-       return &bad_compress;
 }
 
 void compress_close(struct compress *compress)
 {
-       if (compress == &bad_compress)
-               return;
-
-       if (compress->fd >= 0)
-               close(compress->fd);
-       compress->running = 0;
-       compress->fd = -1;
-       free(compress->config);
+       compress->ops->close(compress->data);
        free(compress);
 }
 
 int compress_get_hpointer(struct compress *compress,
                unsigned int *avail, struct timespec *tstamp)
 {
-       struct snd_compr_avail kavail;
-       __u64 time;
-
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-
-       if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail))
-               return oops(compress, errno, "cannot get avail");
-       if (0 == kavail.tstamp.sampling_rate)
-               return oops(compress, 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;
+       return compress->ops->get_hpointer(compress->data, avail, tstamp);
 }
 
 int compress_get_tstamp(struct compress *compress,
                        unsigned int *samples, unsigned int *sampling_rate)
 {
-       struct snd_compr_tstamp ktstamp;
-
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-
-       if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp))
-               return oops(compress, errno, "cannot get tstamp");
-
-       *samples = ktstamp.pcm_io_frames;
-       *sampling_rate = ktstamp.sampling_rate;
-       return 0;
+       return compress->ops->get_tstamp(compress->data, samples, sampling_rate);
 }
 
 int compress_write(struct compress *compress, 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 = compress->config->fragment_size;
-
-       if (!(compress->flags & COMPRESS_IN))
-               return oops(compress, EINVAL, "Invalid flag set");
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-       fds.fd = compress->fd;
-       fds.events = POLLOUT;
-
-       /*TODO: treat auto start here first */
-       while (size) {
-               if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
-                       return oops(compress, 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 (compress->nonblocking)
-                               return total;
-
-                       ret = poll(&fds, 1, compress->max_poll_wait_ms);
-                       if (fds.revents & POLLERR) {
-                               return oops(compress, EIO, "poll returned error!");
-                       }
-                       /* A pause will cause -EBADFD or zero.
-                        * This is not an error, just stop writing */
-                       if ((ret == 0) || (ret < 0 && errno == EBADFD))
-                               break;
-                       if (ret < 0)
-                               return oops(compress, 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(compress->fd, cbuf, to_write);
-               if (written < 0) {
-                       /* If play was paused the write returns -EBADFD */
-                       if (errno == EBADFD)
-                               break;
-                       return oops(compress, errno, "write failed!");
-               }
-
-               size -= written;
-               cbuf += written;
-               total += written;
-       }
-       return total;
+       return compress->ops->write(compress->data, buf, size);
 }
 
 int compress_read(struct compress *compress, 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 = compress->config->fragment_size;
-
-       if (!(compress->flags & COMPRESS_OUT))
-               return oops(compress, EINVAL, "Invalid flag set");
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-       fds.fd = compress->fd;
-       fds.events = POLLIN;
-
-       while (size) {
-               if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
-                       return oops(compress, 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 (compress->nonblocking)
-                               return total;
-
-                       ret = poll(&fds, 1, compress->max_poll_wait_ms);
-                       if (fds.revents & POLLERR) {
-                               return oops(compress, EIO, "poll returned error!");
-                       }
-                       /* A pause will cause -EBADFD or zero.
-                        * This is not an error, just stop reading */
-                       if ((ret == 0) || (ret < 0 && errno == EBADFD))
-                               break;
-                       if (ret < 0)
-                               return oops(compress, 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(compress->fd, cbuf, to_read);
-               if (num_read < 0) {
-                       /* If play was paused the read returns -EBADFD */
-                       if (errno == EBADFD)
-                               break;
-                       return oops(compress, errno, "read failed!");
-               }
-
-               size -= num_read;
-               cbuf += num_read;
-               total += num_read;
-       }
-
-       return total;
+       return compress->ops->read(compress->data, buf, size);
 }
 
 int compress_start(struct compress *compress)
 {
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_START))
-               return oops(compress, errno, "cannot start the stream");
-       compress->running = 1;
-       return 0;
-
+       return compress->ops->start(compress->data);
 }
 
 int compress_stop(struct compress *compress)
 {
-       if (!is_compress_running(compress))
-               return oops(compress, ENODEV, "device not ready");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_STOP))
-               return oops(compress, errno, "cannot stop the stream");
-       return 0;
+       return compress->ops->stop(compress->data);
 }
 
 int compress_pause(struct compress *compress)
 {
-       if (!is_compress_running(compress))
-               return oops(compress, ENODEV, "device not ready");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE))
-               return oops(compress, errno, "cannot pause the stream");
-       return 0;
+       return compress->ops->pause(compress->data);
 }
 
 int compress_resume(struct compress *compress)
 {
-       if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME))
-               return oops(compress, errno, "cannot resume the stream");
-       return 0;
+       return compress->ops->resume(compress->data);
 }
 
 int compress_drain(struct compress *compress)
 {
-       if (!is_compress_running(compress))
-               return oops(compress, ENODEV, "device not ready");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN))
-               return oops(compress, errno, "cannot drain the stream");
-       return 0;
+       return compress->ops->drain(compress->data);
 }
 
 int compress_partial_drain(struct compress *compress)
 {
-       if (!is_compress_running(compress))
-               return oops(compress, ENODEV, "device not ready");
-
-       if (!compress->next_track)
-               return oops(compress, EPERM, "next track not signalled");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN))
-               return oops(compress, errno, "cannot drain the stream\n");
-       compress->next_track = 0;
-       return 0;
+       return compress->ops->partial_drain(compress->data);
 }
 
 int compress_next_track(struct compress *compress)
 {
-       if (!is_compress_running(compress))
-               return oops(compress, ENODEV, "device not ready");
-
-       if (!compress->gapless_metadata)
-               return oops(compress, EPERM, "metadata not set");
-       if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK))
-               return oops(compress, errno, "cannot set next track\n");
-       compress->next_track = 1;
-       compress->gapless_metadata = 0;
-       return 0;
+       return compress->ops->next_track(compress->data);
 }
 
 int compress_set_gapless_metadata(struct compress *compress,
        struct compr_gapless_mdata *mdata)
 {
-       struct snd_compr_metadata metadata;
-       int version;
-
-       if (!is_compress_ready(compress))
-               return oops(compress, ENODEV, "device not ready");
-
-       version = get_compress_version(compress);
-       if (version <= 0)
-               return -1;
-
-       if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1))
-               return oops(compress, ENXIO, "gapless apis not supported in kernel");
-
-       metadata.key = SNDRV_COMPRESS_ENCODER_PADDING;
-       metadata.value[0] = mdata->encoder_padding;
-       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
-               return oops(compress, errno, "can't set metadata for stream\n");
-
-       metadata.key = SNDRV_COMPRESS_ENCODER_DELAY;
-       metadata.value[0] = mdata->encoder_delay;
-       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
-               return oops(compress, errno, "can't set metadata for stream\n");
-       compress->gapless_metadata = 1;
-       return 0;
+       return compress->ops->set_gapless_metadata(compress->data, mdata);
 }
 
 bool 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);
+       struct compress_ops *ops = &compress_hw_ops;
 
-       ret = _is_codec_type_supported(fd, codec);
-
-       close(fd);
-       return ret;
+       return ops->is_codec_supported(card, device, flags, codec);
 }
 
 void compress_set_max_poll_wait(struct compress *compress, int milliseconds)
 {
-       compress->max_poll_wait_ms = milliseconds;
+       compress->ops->set_max_poll_wait(compress->data, milliseconds);
 }
 
 void compress_nonblock(struct compress *compress, int nonblock)
 {
-       compress->nonblocking = !!nonblock;
+       compress->ops->set_nonblock(compress->data, nonblock);
 }
 
 int compress_wait(struct compress *compress, int timeout_ms)
 {
-       struct pollfd fds;
-       int ret;
-
-       fds.fd = compress->fd;
-       fds.events = POLLOUT | POLLIN;
-
-       ret = poll(&fds, 1, timeout_ms);
-       if (ret > 0) {
-               if (fds.revents & POLLERR)
-                       return oops(compress, EIO, "poll returned error!");
-               if (fds.revents & (POLLOUT | POLLIN))
-                       return 0;
-       }
-       if (ret == 0)
-               return oops(compress, ETIME, "poll timed out");
-       if (ret < 0)
-               return oops(compress, errno, "poll error");
-
-       return oops(compress, EIO, "poll signalled unhandled event");
+       return compress->ops->wait(compress->data, timeout_ms);
 }
 
diff --git a/src/lib/compress_hw.c b/src/lib/compress_hw.c
new file mode 100644 (file)
index 0000000..a09bc18
--- /dev/null
@@ -0,0 +1,589 @@
+/* SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) */
+/* Copyright (c) 2011-2012, Intel Corporation. All rights reserved. */
+/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */
+
+#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 "tinycompress/tinycompress.h"
+#include "tinycompress/compress_ops.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 compress_hw_data {
+       int fd;
+       unsigned int flags;
+       char error[COMPR_ERR_MAX];
+       struct compr_config *config;
+       int running;
+       int max_poll_wait_ms;
+       int nonblocking;
+       unsigned int gapless_metadata;
+       unsigned int next_track;
+};
+
+static int oops(struct compress_hw_data *compress, int e, const char *fmt, ...)
+{
+       va_list ap;
+       int sz;
+
+       va_start(ap, fmt);
+       vsnprintf(compress->error, COMPR_ERR_MAX, fmt, ap);
+       va_end(ap);
+       sz = strlen(compress->error);
+
+       snprintf(compress->error + sz, COMPR_ERR_MAX - sz,
+               ": %s", strerror(e));
+       errno = e;
+
+       return -1;
+}
+
+static struct compress_hw_data bad_compress = {
+       .fd = -1,
+};
+
+static const char *compress_hw_get_error(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       return compress->error;
+}
+
+static int is_compress_hw_running(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       return ((compress->fd > 0) && compress->running) ? 1 : 0;
+}
+
+static int is_compress_hw_ready(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       return (compress->fd > 0) ? 1 : 0;
+}
+
+static int get_compress_hw_version(struct compress_hw_data *compress)
+{
+       int version = 0;
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) {
+               oops(compress, errno, "cant read version");
+               return -1;
+       }
+       return version;
+}
+
+static bool _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
+fill_compress_hw_params(struct 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));
+}
+
+static void *compress_hw_open(unsigned int card, unsigned int device,
+               unsigned int flags, struct compr_config *config)
+{
+       struct compress_hw_data *compress;
+       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;
+       }
+
+       compress = calloc(1, sizeof(struct compress_hw_data));
+       if (!compress) {
+               oops(&bad_compress, errno, "cannot allocate compress object");
+               return &bad_compress;
+       }
+
+       compress->next_track = 0;
+       compress->gapless_metadata = 0;
+       compress->config = calloc(1, sizeof(*config));
+       if (!compress->config)
+               goto input_fail;
+
+       snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device);
+
+       compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS;
+
+       compress->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) {
+               compress->fd = open(fn, O_RDONLY);
+       } else {
+               compress->fd = open(fn, O_WRONLY);
+       }
+       if (compress->fd < 0) {
+               oops(&bad_compress, errno, "cannot open device '%s'", fn);
+               goto config_fail;
+       }
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) {
+               oops(compress, 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;
+       }
+
+       memcpy(compress->config, config, sizeof(*compress->config));
+       fill_compress_hw_params(config, &params);
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, &params)) {
+               oops(&bad_compress, errno, "cannot set device");
+               goto codec_fail;
+       }
+
+       return compress;
+
+codec_fail:
+       close(compress->fd);
+       compress->fd = -1;
+config_fail:
+       free(compress->config);
+input_fail:
+       free(compress);
+       return &bad_compress;
+}
+
+static void compress_hw_close(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (compress == &bad_compress)
+               return;
+
+       if (compress->fd >= 0)
+               close(compress->fd);
+       compress->running = 0;
+       compress->fd = -1;
+       free(compress->config);
+       free(compress);
+}
+
+static int compress_hw_get_hpointer(void *data,
+               unsigned int *avail, struct timespec *tstamp)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+       struct snd_compr_avail kavail;
+       __u64 time;
+
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail))
+               return oops(compress, errno, "cannot get avail");
+       if (0 == kavail.tstamp.sampling_rate)
+               return oops(compress, 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;
+}
+
+static int compress_hw_get_tstamp(void *data,
+                       unsigned int *samples, unsigned int *sampling_rate)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+       struct snd_compr_tstamp ktstamp;
+
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp))
+               return oops(compress, errno, "cannot get tstamp");
+
+       *samples = ktstamp.pcm_io_frames;
+       *sampling_rate = ktstamp.sampling_rate;
+       return 0;
+}
+
+static int compress_hw_write(void *data, const void *buf, size_t size)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+       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 = compress->config->fragment_size;
+
+       if (!(compress->flags & COMPRESS_IN))
+               return oops(compress, EINVAL, "Invalid flag set");
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+       fds.fd = compress->fd;
+       fds.events = POLLOUT;
+
+       /*TODO: treat auto start here first */
+       while (size) {
+               if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
+                       return oops(compress, 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 (compress->nonblocking)
+                               return total;
+
+                       ret = poll(&fds, 1, compress->max_poll_wait_ms);
+                       if (fds.revents & POLLERR) {
+                               return oops(compress, EIO, "poll returned error!");
+                       }
+                       /* A pause will cause -EBADFD or zero.
+                        * This is not an error, just stop writing */
+                       if ((ret == 0) || (ret < 0 && errno == EBADFD))
+                               break;
+                       if (ret < 0)
+                               return oops(compress, 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(compress->fd, cbuf, to_write);
+               if (written < 0) {
+                       /* If play was paused the write returns -EBADFD */
+                       if (errno == EBADFD)
+                               break;
+                       return oops(compress, errno, "write failed!");
+               }
+
+               size -= written;
+               cbuf += written;
+               total += written;
+       }
+       return total;
+}
+
+static int compress_hw_read(void *data, void *buf, size_t size)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+       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 = compress->config->fragment_size;
+
+       if (!(compress->flags & COMPRESS_OUT))
+               return oops(compress, EINVAL, "Invalid flag set");
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+       fds.fd = compress->fd;
+       fds.events = POLLIN;
+
+       while (size) {
+               if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
+                       return oops(compress, 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 (compress->nonblocking)
+                               return total;
+
+                       ret = poll(&fds, 1, compress->max_poll_wait_ms);
+                       if (fds.revents & POLLERR) {
+                               return oops(compress, EIO, "poll returned error!");
+                       }
+                       /* A pause will cause -EBADFD or zero.
+                        * This is not an error, just stop reading */
+                       if ((ret == 0) || (ret < 0 && errno == EBADFD))
+                               break;
+                       if (ret < 0)
+                               return oops(compress, 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(compress->fd, cbuf, to_read);
+               if (num_read < 0) {
+                       /* If play was paused the read returns -EBADFD */
+                       if (errno == EBADFD)
+                               break;
+                       return oops(compress, errno, "read failed!");
+               }
+
+               size -= num_read;
+               cbuf += num_read;
+               total += num_read;
+       }
+
+       return total;
+}
+
+static int compress_hw_start(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_START))
+               return oops(compress, errno, "cannot start the stream");
+       compress->running = 1;
+       return 0;
+
+}
+
+static int compress_hw_stop(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_running(compress))
+               return oops(compress, ENODEV, "device not ready");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_STOP))
+               return oops(compress, errno, "cannot stop the stream");
+       return 0;
+}
+
+static int compress_hw_pause(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_running(compress))
+               return oops(compress, ENODEV, "device not ready");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE))
+               return oops(compress, errno, "cannot pause the stream");
+       return 0;
+}
+
+static int compress_hw_resume(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME))
+               return oops(compress, errno, "cannot resume the stream");
+       return 0;
+}
+
+static int compress_hw_drain(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_running(compress))
+               return oops(compress, ENODEV, "device not ready");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN))
+               return oops(compress, errno, "cannot drain the stream");
+       return 0;
+}
+
+static int compress_hw_partial_drain(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_running(compress))
+               return oops(compress, ENODEV, "device not ready");
+
+       if (!compress->next_track)
+               return oops(compress, EPERM, "next track not signalled");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN))
+               return oops(compress, errno, "cannot drain the stream\n");
+       compress->next_track = 0;
+       return 0;
+}
+
+static int compress_hw_next_track(void *data)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       if (!is_compress_hw_running(compress))
+               return oops(compress, ENODEV, "device not ready");
+
+       if (!compress->gapless_metadata)
+               return oops(compress, EPERM, "metadata not set");
+       if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK))
+               return oops(compress, errno, "cannot set next track\n");
+       compress->next_track = 1;
+       compress->gapless_metadata = 0;
+       return 0;
+}
+
+static int compress_hw_set_gapless_metadata(void *data,
+       struct compr_gapless_mdata *mdata)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+       struct snd_compr_metadata metadata;
+       int version;
+
+       if (!is_compress_hw_ready(compress))
+               return oops(compress, ENODEV, "device not ready");
+
+       version = get_compress_hw_version(compress);
+       if (version <= 0)
+               return -1;
+
+       if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1))
+               return oops(compress, ENXIO, "gapless apis not supported in kernel");
+
+       metadata.key = SNDRV_COMPRESS_ENCODER_PADDING;
+       metadata.value[0] = mdata->encoder_padding;
+       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
+               return oops(compress, errno, "can't set metadata for stream\n");
+
+       metadata.key = SNDRV_COMPRESS_ENCODER_DELAY;
+       metadata.value[0] = mdata->encoder_delay;
+       if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata))
+               return oops(compress, errno, "can't set metadata for stream\n");
+       compress->gapless_metadata = 1;
+       return 0;
+}
+
+static bool compress_hw_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 = _is_codec_type_supported(fd, codec);
+
+       close(fd);
+       return ret;
+}
+
+static void compress_hw_set_max_poll_wait(void *data, int milliseconds)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       compress->max_poll_wait_ms = milliseconds;
+}
+
+static void compress_hw_set_nonblock(void *data, int nonblock)
+{
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       compress->nonblocking = !!nonblock;
+}
+
+static int compress_hw_wait(void *data, int timeout_ms)
+{
+       struct pollfd fds;
+       int ret;
+       struct compress_hw_data *compress = (struct compress_hw_data *)data;
+
+       fds.fd = compress->fd;
+       fds.events = POLLOUT | POLLIN;
+
+       ret = poll(&fds, 1, timeout_ms);
+       if (ret > 0) {
+               if (fds.revents & POLLERR)
+                       return oops(compress, EIO, "poll returned error!");
+               if (fds.revents & (POLLOUT | POLLIN))
+                       return 0;
+       }
+       if (ret == 0)
+               return oops(compress, ETIME, "poll timed out");
+       if (ret < 0)
+               return oops(compress, errno, "poll error");
+
+       return oops(compress, EIO, "poll signalled unhandled event");
+}
+
+struct compress_ops compress_hw_ops = {
+       .open = compress_hw_open,
+       .close = compress_hw_close,
+       .get_hpointer = compress_hw_get_hpointer,
+       .get_tstamp = compress_hw_get_tstamp,
+       .write = compress_hw_write,
+       .read = compress_hw_read,
+       .start = compress_hw_start,
+       .stop = compress_hw_stop,
+       .pause = compress_hw_pause,
+       .resume = compress_hw_resume,
+       .drain = compress_hw_drain,
+       .partial_drain = compress_hw_partial_drain,
+       .next_track = compress_hw_next_track,
+       .set_gapless_metadata = compress_hw_set_gapless_metadata,
+       .set_max_poll_wait = compress_hw_set_max_poll_wait,
+       .set_nonblock = compress_hw_set_nonblock,
+       .wait = compress_hw_wait,
+       .is_codec_supported = compress_hw_is_codec_supported,
+       .is_compress_running = is_compress_hw_running,
+       .is_compress_ready = is_compress_hw_ready,
+       .get_error = compress_hw_get_error,
+};
+