Revert "Sync from GitHub"

Revert submission 1483902-sync tinyalsa

Reason for revert: b/174994516, b/175089243
Reverted Changes:
Id3370e3c7:fix build breakage
I296a47fcb:Sync from GitHub
I6b86da455:reorg file structure to match it on GitHub
I5155d7856:add const qualifier to the pcm_mask variables and ...

Change-Id: Ie050892efdadd2a8e199c5375611fae954c2de99
diff --git a/src/limits.c b/src/limits.c
deleted file mode 100644
index 25bd352..0000000
--- a/src/limits.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <tinyalsa/limits.h>
-
-const struct tinyalsa_unsigned_interval tinyalsa_channels_limit = {
-    .max = TINYALSA_CHANNELS_MAX,
-    .min = TINYALSA_CHANNELS_MIN
-};
-
-const struct tinyalsa_unsigned_interval tinyalsa_frames_limit = {
-    .max = TINYALSA_FRAMES_MAX,
-    .min = TINYALSA_FRAMES_MIN
-};
-
diff --git a/src/mixer.c b/src/mixer.c
index fe590e8..f3fdb62 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -26,758 +26,236 @@
 ** DAMAGE.
 */
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include <stdbool.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <ctype.h>
-#include <limits.h>
-#include <time.h>
 #include <poll.h>
 
 #include <sys/ioctl.h>
 
 #include <linux/ioctl.h>
-
-#ifndef __force
 #define __force
-#endif
-
-#ifndef __bitwise
 #define __bitwise
-#endif
-
-#ifndef __user
 #define __user
-#endif
-
 #include <sound/asound.h>
 
-#include <tinyalsa/mixer.h>
-#include <tinyalsa/plugin.h>
+#ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
+#define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
+#endif
 
-#include "mixer_io.h"
+#include <tinyalsa/asoundlib.h>
 
-/** A mixer control.
- * @ingroup libtinyalsa-mixer
- */
 struct mixer_ctl {
-    /** The mixer that the mixer control belongs to */
     struct mixer *mixer;
-    /** Information on the control's value (i.e. type, number of values) */
-    struct snd_ctl_elem_info info;
-    /** A list of string representations of enumerated values (only valid for enumerated controls) */
+    struct snd_ctl_elem_info *info;
     char **ename;
-    /** Pointer to the group that the control belongs to */
-    struct mixer_ctl_group *grp;
+    bool info_retrieved;
 };
 
-struct mixer_ctl_group {
-    /** A continuous array of mixer controls */
-    struct mixer_ctl *ctl;
-    /** The number of mixer controls */
-    unsigned int count;
-    /** The number of events associated with this group */
-    unsigned int event_cnt;
-    /** The operations corresponding to this group */
-    const struct mixer_ops *ops;
-    /** Private data for storing group specific data */
-    void *data;
-};
-
-/** A mixer handle.
- * @ingroup libtinyalsa-mixer
- */
 struct mixer {
-    /** File descriptor for the card */
     int fd;
-    /** Card information */
     struct snd_ctl_card_info card_info;
-    /* Hardware (kernel interface) mixer control group */
-    struct mixer_ctl_group *h_grp;
-    /* Virtual (Plugin interface) mixer control group */
-    struct mixer_ctl_group *v_grp;
-    /* Total count of mixer controls from both  groups */
-    unsigned int total_count;
-    /* Flag to track if card information is already retrieved */
-    bool is_card_info_retrieved;
-
+    struct snd_ctl_elem_info *elem_info;
+    struct mixer_ctl *ctl;
+    unsigned int count;
 };
 
-static void mixer_cleanup_control(struct mixer_ctl *ctl)
-{
-    unsigned int m;
-
-    if (ctl->ename) {
-        unsigned int max = ctl->info.value.enumerated.items;
-        for (m = 0; m < max; m++)
-            free(ctl->ename[m]);
-        free(ctl->ename);
-    }
-}
-
-static void mixer_grp_close(struct mixer *mixer, struct mixer_ctl_group *grp)
-{
-    unsigned int n;
-
-    if (!grp)
-        return;
-
-    if (grp->ctl) {
-        for (n = 0; n < grp->count; n++)
-            mixer_cleanup_control(&grp->ctl[n]);
-        free(grp->ctl);
-    }
-
-    free(grp);
-
-    mixer->is_card_info_retrieved = false;
-}
-
-/** Closes a mixer returned by @ref mixer_open.
- * @param mixer A mixer handle.
- * @ingroup libtinyalsa-mixer
- */
 void mixer_close(struct mixer *mixer)
 {
+    unsigned int n,m;
+
     if (!mixer)
         return;
 
-    if (mixer->fd >= 0 && mixer->h_grp)
-        mixer->h_grp->ops->close(mixer->h_grp->data);
-    mixer_grp_close(mixer, mixer->h_grp);
+    if (mixer->fd >= 0)
+        close(mixer->fd);
 
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp)
-        mixer->v_grp->ops->close(mixer->v_grp->data);
-    mixer_grp_close(mixer, mixer->v_grp);
-#endif
+    if (mixer->ctl) {
+        for (n = 0; n < mixer->count; n++) {
+            if (mixer->ctl[n].ename) {
+                unsigned int max = mixer->ctl[n].info->value.enumerated.items;
+                for (m = 0; m < max; m++)
+                    free(mixer->ctl[n].ename[m]);
+                free(mixer->ctl[n].ename);
+            }
+        }
+        free(mixer->ctl);
+    }
+
+    if (mixer->elem_info)
+        free(mixer->elem_info);
 
     free(mixer);
 
     /* TODO: verify frees */
 }
 
-static void *mixer_realloc_z(void *ptr, size_t curnum, size_t newnum, size_t size)
-{
-        int8_t *newp;
-
-        newp = realloc(ptr, size * newnum);
-        if (!newp)
-            return NULL;
-
-        memset(newp + (curnum * size), 0, (newnum - curnum) * size);
-        return newp;
-}
-
-static int add_controls(struct mixer *mixer, struct mixer_ctl_group *grp)
+struct mixer *mixer_open(unsigned int card)
 {
     struct snd_ctl_elem_list elist;
     struct snd_ctl_elem_id *eid = NULL;
-    struct mixer_ctl *ctl;
-    const unsigned int old_count = grp->count;
-    unsigned int new_count;
+    struct mixer *mixer = NULL;
     unsigned int n;
+    int fd;
+    char fn[256];
+
+    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+    fd = open(fn, O_RDWR);
+    if (fd < 0)
+        return 0;
 
     memset(&elist, 0, sizeof(elist));
-    if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
         goto fail;
 
-    if (old_count == elist.count)
-        return 0; /* no new controls return unchanged */
-
-    if (old_count > elist.count)
-        return -1; /* driver has removed controls - this is bad */
-
-    ctl = mixer_realloc_z(grp->ctl, old_count, elist.count,
-                          sizeof(struct mixer_ctl));
-    if (!ctl)
-        goto fail;
-
-    grp->ctl = ctl;
-
-    /* ALSA drivers are not supposed to remove or re-order controls that
-     * have already been created so we know that any new controls must
-     * be after the ones we have already collected
-     */
-    new_count = elist.count;
-    elist.space = new_count - old_count; /* controls we haven't seen before */
-    elist.offset = old_count; /* first control we haven't seen */
-
-    eid = calloc(elist.space, sizeof(struct snd_ctl_elem_id));
-    if (!eid)
-        goto fail;
-
-    elist.pids = eid;
-
-    if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
-        goto fail;
-
-    for (n = old_count; n < new_count; n++) {
-        struct snd_ctl_elem_info *ei = &grp->ctl[n].info;
-        ei->id.numid = eid[n - old_count].numid;
-        if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
-            goto fail_extend;
-        ctl[n].mixer = mixer;
-        ctl[n].grp = grp;
-    }
-
-    grp->count = new_count;
-    mixer->total_count += (new_count - old_count);
-
-    free(eid);
-    return 0;
-
-fail_extend:
-    /* cleanup the control we failed on but leave the ones that were already
-     * added. Also no advantage to shrinking the resized memory block, we
-     * might want to extend the controls again later
-     */
-    mixer_cleanup_control(&ctl[n]);
-
-    grp->count = n;   /* keep controls we successfully added */
-    mixer->total_count += (n - old_count);
-    /* fall through... */
-fail:
-    free(eid);
-    return -1;
-}
-
-static int mixer_grp_open(struct mixer *mixer, unsigned int card, bool is_hw)
-{
-    struct mixer_ctl_group *grp = NULL;
-    const struct mixer_ops *ops = NULL;
-    void *data = NULL;
-    int fd, ret;
-
-    grp = calloc(1, sizeof(*grp));
-    if (!grp)
-        return -ENOMEM;
-
-    if (is_hw) {
-        mixer->fd = -1;
-        fd = mixer_hw_open(card, &data, &ops);
-        if (fd < 0) {
-            ret = fd;
-            goto err_open;
-        }
-        mixer->fd = fd;
-        mixer->h_grp = grp;
-    }
-#ifdef TINYALSA_USES_PLUGINS
-    else {
-        ret = mixer_plugin_open(card, &data, &ops);
-        if (ret < 0)
-            goto err_open;
-        mixer->v_grp = grp;
-    }
-#endif
-    grp->ops = ops;
-    grp->data = data;
-
-    if (!mixer->is_card_info_retrieved) {
-        ret = grp->ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO,
-                              &mixer->card_info);
-        if (ret < 0)
-            goto err_card_info;
-        mixer->is_card_info_retrieved = true;
-    }
-
-    ret = add_controls(mixer, grp);
-    if (ret < 0)
-        goto err_card_info;
-
-    return 0;
-
-err_card_info:
-    grp->ops->close(grp->data);
-
-err_open:
-    free(grp);
-    return ret;
-
-}
-
-/** Opens a mixer for a given card.
- * @param card The card to open the mixer for.
- * @returns An initialized mixer handle.
- * @ingroup libtinyalsa-mixer
- */
-struct mixer *mixer_open(unsigned int card)
-{
-    struct mixer *mixer = NULL;
-    int h_status, v_status = -1;
-
     mixer = calloc(1, sizeof(*mixer));
     if (!mixer)
         goto fail;
 
-    h_status = mixer_grp_open(mixer, card, true);
-
-#ifdef TINYALSA_USES_PLUGINS
-    v_status = mixer_grp_open(mixer, card, false);
-#endif
-
-    /* both hw and virtual should fail for mixer_open to fail */
-    if (h_status < 0 && v_status < 0)
+    mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
+    mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+    if (!mixer->ctl || !mixer->elem_info)
         goto fail;
 
+    if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
+        goto fail;
+
+    eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
+    if (!eid)
+        goto fail;
+
+    mixer->count = elist.count;
+    mixer->fd = fd;
+    elist.space = mixer->count;
+    elist.pids = eid;
+    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+        goto fail;
+
+    for (n = 0; n < mixer->count; n++) {
+        struct mixer_ctl *ctl = mixer->ctl + n;
+
+        ctl->mixer = mixer;
+        ctl->info = mixer->elem_info + n;
+        ctl->info->id.numid = eid[n].numid;
+        strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
+                SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+        ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+    }
+
+    free(eid);
     return mixer;
 
 fail:
+    /* TODO: verify frees in failure case */
+    if (eid)
+        free(eid);
     if (mixer)
         mixer_close(mixer);
-
-    return NULL;
-}
-
-/** Some controls may not be present at boot time, e.g. controls from runtime
- * loadable DSP firmware. This function adds any new controls that have appeared
- * since mixer_open() or the last call to this function. This assumes a well-
- * behaved codec driver that does not delete controls that already exists, so
- * any added controls must be after the last one we already saw. Scanning only
- * the new controls is much faster than calling mixer_close() then mixer_open()
- * to re-scan all controls.
- *
- * NOTE: this invalidates any struct mixer_ctl pointers previously obtained
- * from mixer_get_ctl() and mixer_get_ctl_by_name(). Either refresh all your
- * stored pointers after calling mixer_update_ctls(), or (better) do not
- * store struct mixer_ctl pointers, instead lookup the control by name or
- * id only when you are about to use it. The overhead of lookup by id
- * using mixer_get_ctl() is negligible.
- * @param mixer An initialized mixer handle.
- * @returns 0 on success, -1 on failure
- */
-int mixer_add_new_ctls(struct mixer *mixer)
-{
-    int rc1 = 0, rc2 = 0;
-
-    if (!mixer)
-        return 0;
-
-    /* add the h_grp controls */
-    if (mixer->h_grp)
-        rc1 = add_controls(mixer, mixer->h_grp);
-
-#ifdef TINYALSA_USES_PLUGINS
-    /* add the v_grp controls */
-    if (mixer->v_grp)
-        rc2 = add_controls(mixer, mixer->v_grp);
-#endif
-
-    if (rc1 < 0)
-        return rc1;
-    if (rc2 < 0)
-        return rc2;
-
+    else if (fd >= 0)
+        close(fd);
     return 0;
 }
 
-/** Gets the name of the mixer's card.
- * @param mixer An initialized mixer handle.
- * @returns The name of the mixer's card.
- * @ingroup libtinyalsa-mixer
- */
-const char *mixer_get_name(const struct mixer *mixer)
+static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
+{
+    if (!ctl->info_retrieved) {
+        if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0)
+            return false;
+        ctl->info_retrieved = true;
+    }
+
+    if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
+        return true;
+
+    struct snd_ctl_elem_info tmp;
+    char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
+    if (!enames)
+        return false;
+
+    for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) {
+        memset(&tmp, 0, sizeof(tmp));
+        tmp.id.numid = ctl->info->id.numid;
+        tmp.value.enumerated.item = i;
+        if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+            goto fail;
+        enames[i] = strdup(tmp.value.enumerated.name);
+        if (!enames[i])
+            goto fail;
+    }
+    ctl->ename = enames;
+    return true;
+
+fail:
+    free(enames);
+    return false;
+}
+
+const char *mixer_get_name(struct mixer *mixer)
 {
     return (const char *)mixer->card_info.name;
 }
 
-/** Gets the number of mixer controls for a given mixer.
- * @param mixer An initialized mixer handle.
- * @returns The number of mixer controls for the given mixer.
- * @ingroup libtinyalsa-mixer
- */
-unsigned int mixer_get_num_ctls(const struct mixer *mixer)
+unsigned int mixer_get_num_ctls(struct mixer *mixer)
 {
     if (!mixer)
         return 0;
 
-    return mixer->total_count;
+    return mixer->count;
 }
 
-/** Gets the number of mixer controls, that go by a specified name, for a given mixer.
- * @param mixer An initialized mixer handle.
- * @param name The name of the mixer control
- * @returns The number of mixer controls, specified by @p name, for the given mixer.
- * @ingroup libtinyalsa-mixer
- */
-unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *name)
-{
-    struct mixer_ctl_group *grp;
-    unsigned int n;
-    unsigned int count = 0;
-    struct mixer_ctl *ctl;
-
-    if (!mixer)
-        return 0;
-
-    if (mixer->h_grp) {
-        grp = mixer->h_grp;
-        ctl = grp->ctl;
-
-        for (n = 0; n < grp->count; n++)
-            if (!strcmp(name, (char*) ctl[n].info.id.name))
-                count++;
-    }
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp) {
-        grp = mixer->v_grp;
-        ctl = grp->ctl;
-
-        for (n = 0; n < grp->count; n++)
-            if (!strcmp(name, (char*) ctl[n].info.id.name))
-                count++;
-    }
-#endif
-
-    return count;
-}
-
-/** Subscribes for the mixer events.
- * @param mixer A mixer handle.
- * @param subscribe value indicating subscribe or unsubscribe for events
- * @returns On success, zero.
- *  On failure, non-zero.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_subscribe_events(struct mixer *mixer, int subscribe)
-{
-    struct mixer_ctl_group *grp;
-
-    if (mixer->h_grp) {
-        grp = mixer->h_grp;
-        if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
-            return -1;
-    }
-
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp) {
-        grp = mixer->v_grp;
-        if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
-            return -1;
-    }
-#endif
-    return 0;
-}
-
-/** Wait for mixer events.
- * @param mixer A mixer handle.
- * @param timeout timout value
- * @returns On success, 1.
- *  On failure, -errno.
- *  On timeout, 0
- * @ingroup libtinyalsa-mixer
- */
-int mixer_wait_event(struct mixer *mixer, int timeout)
-{
-    struct pollfd *pfd;
-    struct mixer_ctl_group *grp;
-    int count = 0, num_fds = 0, i, ret = 0;
-
-    if (mixer->fd >= 0)
-        num_fds++;
-
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp)
-        num_fds++;
-#endif
-
-    pfd = (struct pollfd *)calloc(num_fds, sizeof(struct pollfd));
-    if (!pfd)
-        return -ENOMEM;
-
-    if (mixer->fd >= 0) {
-        pfd[count].fd = mixer->fd;
-        pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
-        count++;
-    }
-
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp) {
-        grp = mixer->v_grp;
-        if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
-            pfd[count].events = POLLIN | POLLERR | POLLNVAL;
-            count++;
-        }
-    }
-#endif
-
-    if (!count)
-        goto exit;
-
-    for (;;) {
-        int err;
-        err = poll(pfd, count, timeout);
-        if (err < 0) {
-            ret = -errno;
-            goto exit;
-        }
-        if (!err)
-            goto exit;
-
-        for (i = 0; i < count; i++) {
-            if (pfd[i].revents & (POLLERR | POLLNVAL)) {
-                ret = -EIO;
-                goto exit;
-            }
-            if (pfd[i].revents & (POLLIN | POLLOUT)) {
-                if ((i == 0) && mixer->fd >= 0) {
-                    grp = mixer->h_grp;
-                    grp->event_cnt++;
-                }
-#ifdef TINYALSA_USES_PLUGINS
-                 else {
-                    grp = mixer->v_grp;
-                    grp->event_cnt++;
-                }
-#endif
-                ret = 1;
-                goto exit;
-            }
-        }
-    }
-exit:
-    free(pfd);
-    return ret;
-}
-
-/** Consume a mixer event.
- * If mixer_subscribe_events has been called,
- * mixer_wait_event will identify when a control value has changed.
- * This function will clear a single event from the mixer so that
- * further events can be alerted.
- *
- * @param mixer A mixer handle.
- * @returns 0 on success.  -errno on failure.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_consume_event(struct mixer *mixer)
-{
-    struct snd_ctl_event ev;
-
-    return mixer_read_event(mixer, &ev);
-}
-
-int mixer_read_event(struct mixer *mixer, struct snd_ctl_event *ev)
-{
-    struct mixer_ctl_group *grp;
-    ssize_t count = 0;
-
-    if (mixer->h_grp) {
-        grp = mixer->h_grp;
-        if (grp->event_cnt) {
-            grp->event_cnt--;
-            count = grp->ops->read_event(grp->data, ev, sizeof(*ev));
-            return (count >= 0) ? 0 : -errno;
-        }
-    }
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp) {
-        grp = mixer->v_grp;
-        if (grp->event_cnt) {
-            grp->event_cnt--;
-            count = grp->ops->read_event(grp->data, ev, sizeof(*ev));
-            return (count >= 0) ? 0 : -errno;
-        }
-    }
-#endif
-    return 0;
-}
-
-static unsigned int mixer_grp_get_count(struct mixer_ctl_group *grp)
-{
-    if (!grp)
-        return 0;
-
-    return grp->count;
-}
-
-/** Gets a mixer control handle, by the mixer control's id.
- * For non-const access, see @ref mixer_get_ctl
- * @param mixer An initialized mixer handle.
- * @param id The control's id in the given mixer.
- * @returns A handle to the mixer control.
- * @ingroup libtinyalsa-mixer
- */
-const struct mixer_ctl *mixer_get_ctl_const(const struct mixer *mixer, unsigned int id)
-{
-    unsigned int h_count;
-
-    if (!mixer || (id >= mixer->total_count))
-        return NULL;
-
-    h_count = mixer_grp_get_count(mixer->h_grp);
-
-    if (id < h_count)
-        return mixer->h_grp->ctl + id;
-#ifdef TINYALSA_USES_PLUGINS
-    else {
-        unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
-	    if ((id - h_count) < v_count)
-            return mixer->v_grp->ctl + (id - h_count);
-    }
-#endif
-
-    return NULL;
-}
-
-/** Gets a mixer control handle, by the mixer control's id.
- * For const access, see @ref mixer_get_ctl_const
- * @param mixer An initialized mixer handle.
- * @param id The control's id in the given mixer.
- * @returns A handle to the mixer control.
- * @ingroup libtinyalsa-mixer
- */
 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
 {
-    unsigned int h_count;
+    struct mixer_ctl *ctl;
 
-    if (!mixer || (id >= mixer->total_count))
+    if (!mixer || (id >= mixer->count))
         return NULL;
 
-    h_count = mixer_grp_get_count(mixer->h_grp);
+    ctl = mixer->ctl + id;
+    if (!mixer_ctl_get_elem_info(ctl))
+        return NULL;
 
-    if (id < h_count)
-        return mixer->h_grp->ctl + id;
-#ifdef TINYALSA_USES_PLUGINS
-    else {
-        unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
-	    if ((id - h_count) < v_count)
-            return mixer->v_grp->ctl + (id - h_count);
-    }
-#endif
-    return NULL;
+    return ctl;
 }
 
-/** Gets the first instance of mixer control handle, by the mixer control's name.
- * @param mixer An initialized mixer handle.
- * @param name The control's name in the given mixer.
- * @returns A handle to the mixer control.
- * @ingroup libtinyalsa-mixer
- */
 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
 {
-    return mixer_get_ctl_by_name_and_index(mixer, name, 0);
-}
-
-/** Gets an instance of mixer control handle, by the mixer control's name and index.
- *  For instance, if two controls have the name of 'Volume', then and index of 1 would return the second control.
- * @param mixer An initialized mixer handle.
- * @param name The control's name in the given mixer.
- * @param index The control's index.
- * @returns A handle to the mixer control.
- * @ingroup libtinyalsa-mixer
- */
-struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
-                                                  const char *name,
-                                                  unsigned int index)
-{
-    struct mixer_ctl_group *grp;
     unsigned int n;
-    struct mixer_ctl *ctl;
 
     if (!mixer)
         return NULL;
 
-    if (mixer->h_grp) {
-        grp = mixer->h_grp;
-        ctl = grp->ctl;
+    for (n = 0; n < mixer->count; n++)
+        if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
+            return mixer_get_ctl(mixer, n);
 
-        for (n = 0; n < grp->count; n++)
-            if (!strcmp(name, (char*) ctl[n].info.id.name))
-                if (index-- == 0)
-                    return ctl + n;
-    }
-
-#ifdef TINYALSA_USES_PLUGINS
-    if (mixer->v_grp) {
-        grp = mixer->v_grp;
-        ctl = grp->ctl;
-
-        for (n = 0; n < grp->count; n++)
-            if (!strcmp(name, (char*) ctl[n].info.id.name))
-                if (index-- == 0)
-                    return ctl + n;
-    }
-#endif
     return NULL;
 }
 
-/** Updates the control's info.
- * This is useful for a program that may be idle for a period of time.
- * @param ctl An initialized control handle.
- * @ingroup libtinyalsa-mixer
- */
 void mixer_ctl_update(struct mixer_ctl *ctl)
 {
-    struct mixer_ctl_group *grp;
-
-    if (!ctl)
-        return;
-
-    grp  = ctl->grp;
-    grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &ctl->info);
+    ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
 }
 
-/** Checks the control for TLV Read/Write access.
- * @param ctl An initialized control handle.
- * @returns On success, non-zero.
- *  On failure, zero.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_is_access_tlv_rw(const struct mixer_ctl *ctl)
-{
-    return (ctl->info.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
-}
-
-/** Gets the control's ID.
- * @param ctl An initialized control handle.
- * @returns On success, the control's ID is returned.
- *  On error, UINT_MAX is returned instead.
- * @ingroup libtinyalsa-mixer
- */
-unsigned int mixer_ctl_get_id(const struct mixer_ctl *ctl)
-{
-    if (!ctl)
-        return UINT_MAX;
-
-    /* numid values start at 1, return a 0-base value that
-     * can be passed to mixer_get_ctl()
-     */
-    return ctl->info.id.numid - 1;
-}
-
-/** Gets the name of the control.
- * @param ctl An initialized control handle.
- * @returns On success, the name of the control.
- *  On error, NULL.
- * @ingroup libtinyalsa-mixer
- */
-const char *mixer_ctl_get_name(const struct mixer_ctl *ctl)
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
 {
     if (!ctl)
         return NULL;
 
-    return (const char *)ctl->info.id.name;
+    return (const char *)ctl->info->id.name;
 }
 
-/** Gets the value type of the control.
- * @param ctl An initialized control handle
- * @returns On success, the type of mixer control.
- *  On failure, it returns @ref MIXER_CTL_TYPE_UNKNOWN
- * @ingroup libtinyalsa-mixer
- */
-enum mixer_ctl_type mixer_ctl_get_type(const struct mixer_ctl *ctl)
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
 {
     if (!ctl)
         return MIXER_CTL_TYPE_UNKNOWN;
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return MIXER_CTL_TYPE_BOOL;
     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return MIXER_CTL_TYPE_INT;
     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
@@ -788,17 +266,12 @@
     };
 }
 
-/** Gets the string that describes the value type of the control.
- * @param ctl An initialized control handle
- * @returns On success, a string describing type of mixer control.
- * @ingroup libtinyalsa-mixer
- */
-const char *mixer_ctl_get_type_string(const struct mixer_ctl *ctl)
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
 {
     if (!ctl)
         return "";
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return "BOOL";
     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return "INT";
     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
@@ -809,95 +282,69 @@
     };
 }
 
-/** Gets the number of values in the control.
- * @param ctl An initialized control handle
- * @returns The number of values in the mixer control
- * @ingroup libtinyalsa-mixer
- */
-unsigned int mixer_ctl_get_num_values(const struct mixer_ctl *ctl)
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
 {
     if (!ctl)
         return 0;
 
-    return ctl->info.count;
+    return ctl->info->count;
 }
 
-static int percent_to_int(const struct snd_ctl_elem_info *ei, int percent)
+static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
 {
-    if ((percent > 100) || (percent < 0)) {
-        return -EINVAL;
-    }
+    int range;
 
-    int range = (ei->value.integer.max - ei->value.integer.min);
+    if (percent > 100)
+        percent = 100;
+    else if (percent < 0)
+        percent = 0;
+
+    range = (ei->value.integer.max - ei->value.integer.min);
 
     return ei->value.integer.min + (range * percent) / 100;
 }
 
-static int int_to_percent(const struct snd_ctl_elem_info *ei, int value)
+static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
 {
     int range = (ei->value.integer.max - ei->value.integer.min);
 
     if (range == 0)
         return 0;
 
-    return ((value - ei->value.integer.min) * 100) / range;
+    return ((value - ei->value.integer.min) / range) * 100;
 }
 
-/** Gets a percentage representation of a specified control value.
- * @param ctl An initialized control handle.
- * @param id The index of the value within the control.
- * @returns On success, the percentage representation of the control value.
- *  On failure, -EINVAL is returned.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_get_percent(const struct mixer_ctl *ctl, unsigned int id)
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
 {
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
         return -EINVAL;
 
-    return int_to_percent(&ctl->info, mixer_ctl_get_value(ctl, id));
+    return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
 }
 
-/** Sets the value of a control by percent, specified by the value index.
- * @param ctl An initialized control handle.
- * @param id The index of the value to set
- * @param percent A percentage value between 0 and 100.
- * @returns On success, zero is returned.
- *  On failure, non-zero is returned.
- * @ingroup libtinyalsa-mixer
- */
 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
 {
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
         return -EINVAL;
 
-    return mixer_ctl_set_value(ctl, id, percent_to_int(&ctl->info, percent));
+    return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
 }
 
-/** Gets the value of a control.
- * @param ctl An initialized control handle.
- * @param id The index of the control value.
- * @returns On success, the specified value is returned.
- *  On failure, -EINVAL is returned.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
+int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
 {
-    struct mixer_ctl_group *grp;
     struct snd_ctl_elem_value ev;
     int ret;
 
-    if (!ctl || (id >= ctl->info.count))
+    if (!ctl || (id >= ctl->info->count))
         return -EINVAL;
 
-    grp = ctl->grp;
     memset(&ev, 0, sizeof(ev));
-    ev.id.numid = ctl->info.id.numid;
-    ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+    ev.id.numid = ctl->info->id.numid;
+    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
     if (ret < 0)
         return ret;
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
         return !!ev.value.integer.value[id];
 
@@ -917,41 +364,40 @@
     return 0;
 }
 
-/** Gets the contents of a control's value array.
- * @param ctl An initialized control handle.
- * @param array A pointer to write the array data to.
- *  The size of this array must be equal to the number of items in the array
- *  multiplied by the size of each item.
- * @param count The number of items in the array.
- *  This parameter must match the number of items in the control.
- *  The number of items in the control may be accessed via @ref mixer_ctl_get_num_values
- * @returns On success, zero.
- *  On failure, non-zero.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
+int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
 {
-    struct mixer_ctl_group *grp;
+    return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
+}
+
+int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
+{
     struct snd_ctl_elem_value ev;
     int ret = 0;
     size_t size;
     void *source;
+    size_t total_count;
 
-    if (!ctl || !count || !array)
+    if ((!ctl) || !count || !array)
         return -EINVAL;
 
-    grp = ctl->grp;
+    total_count = ctl->info->count;
 
-    if (count > ctl->info.count)
+    if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
+        mixer_ctl_is_access_tlv_rw(ctl)) {
+            /* Additional two words is for the TLV header */
+            total_count += TLV_HEADER_SIZE;
+    }
+
+    if (count > total_count)
         return -EINVAL;
 
     memset(&ev, 0, sizeof(ev));
-    ev.id.numid = ctl->info.id.numid;
+    ev.id.numid = ctl->info->id.numid;
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
     case SNDRV_CTL_ELEM_TYPE_INTEGER:
-        ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+        ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
         if (ret < 0)
             return ret;
         size = sizeof(ev.value.integer.value[0]);
@@ -966,14 +412,12 @@
 
             if (count > SIZE_MAX - sizeof(*tlv))
                 return -EINVAL;
-
             tlv = calloc(1, sizeof(*tlv) + count);
             if (!tlv)
                 return -ENOMEM;
-
-            tlv->numid = ctl->info.id.numid;
+            tlv->numid = ctl->info->id.numid;
             tlv->length = count;
-            ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
+            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
 
             source = tlv->tlv;
             memcpy(array, source, count);
@@ -982,7 +426,7 @@
 
             return ret;
         } else {
-            ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
             if (ret < 0)
                 return ret;
             size = sizeof(ev.value.bytes.data[0]);
@@ -1004,43 +448,26 @@
     return 0;
 }
 
-/** Sets the value of a control, specified by the value index.
- * @param ctl An initialized control handle.
- * @param id The index of the value within the control.
- * @param value The value to set.
- *  This must be in a range specified by @ref mixer_ctl_get_range_min
- *  and @ref mixer_ctl_get_range_max.
- * @returns On success, zero is returned.
- *  On failure, non-zero is returned.
- * @ingroup libtinyalsa-mixer
- */
 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
 {
-    struct mixer_ctl_group *grp;
     struct snd_ctl_elem_value ev;
     int ret;
 
-    if (!ctl || (id >= ctl->info.count))
+    if (!ctl || (id >= ctl->info->count))
         return -EINVAL;
 
-    grp = ctl->grp;
     memset(&ev, 0, sizeof(ev));
-    ev.id.numid = ctl->info.id.numid;
-    ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+    ev.id.numid = ctl->info->id.numid;
+    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
     if (ret < 0)
         return ret;
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
         ev.value.integer.value[id] = !!value;
         break;
 
     case SNDRV_CTL_ELEM_TYPE_INTEGER:
-        if ((value < mixer_ctl_get_range_min(ctl)) ||
-            (value > mixer_ctl_get_range_max(ctl))) {
-            return -EINVAL;
-        }
-
         ev.value.integer.value[id] = value;
         break;
 
@@ -1056,38 +483,34 @@
         return -EINVAL;
     }
 
-    return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
 }
 
-/** Sets the contents of a control's value array.
- * @param ctl An initialized control handle.
- * @param array The array containing control values.
- * @param count The number of values in the array.
- *  This must match the number of values in the control.
- *  The number of values in a control may be accessed via @ref mixer_ctl_get_num_values
- * @returns On success, zero.
- *  On failure, non-zero.
- * @ingroup libtinyalsa-mixer
- */
 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
 {
-    struct mixer_ctl_group *grp;
     struct snd_ctl_elem_value ev;
     size_t size;
     void *dest;
+    size_t total_count;
 
     if ((!ctl) || !count || !array)
         return -EINVAL;
 
-    grp = ctl->grp;
+    total_count = ctl->info->count;
 
-    if (count > ctl->info.count)
+    if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
+        mixer_ctl_is_access_tlv_rw(ctl)) {
+            /* Additional two words is for the TLV header */
+            total_count += TLV_HEADER_SIZE;
+    }
+
+    if (count > total_count)
         return -EINVAL;
 
     memset(&ev, 0, sizeof(ev));
-    ev.id.numid = ctl->info.id.numid;
+    ev.id.numid = ctl->info->id.numid;
 
-    switch (ctl->info.type) {
+    switch (ctl->info->type) {
     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
     case SNDRV_CTL_ELEM_TYPE_INTEGER:
         size = sizeof(ev.value.integer.value[0]);
@@ -1099,19 +522,16 @@
         if (mixer_ctl_is_access_tlv_rw(ctl)) {
             struct snd_ctl_tlv *tlv;
             int ret = 0;
-
             if (count > SIZE_MAX - sizeof(*tlv))
                 return -EINVAL;
-
             tlv = calloc(1, sizeof(*tlv) + count);
             if (!tlv)
                 return -ENOMEM;
-
-            tlv->numid = ctl->info.id.numid;
+            tlv->numid = ctl->info->id.numid;
             tlv->length = count;
             memcpy(tlv->tlv, array, count);
 
-            ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
+            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
             free(tlv);
 
             return ret;
@@ -1132,136 +552,59 @@
 
     memcpy(dest, array, size * count);
 
-    return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
 }
 
-/** Gets the minimum value of an control.
- * The control must have an integer type.
- * The type of the control can be checked with @ref mixer_ctl_get_type.
- * @param ctl An initialized control handle.
- * @returns On success, the minimum value of the control.
- *  On failure, -EINVAL.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_get_range_min(const struct mixer_ctl *ctl)
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
 {
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
         return -EINVAL;
 
-    return ctl->info.value.integer.min;
+    return ctl->info->value.integer.min;
 }
 
-/** Gets the maximum value of an control.
- * The control must have an integer type.
- * The type of the control can be checked with @ref mixer_ctl_get_type.
- * @param ctl An initialized control handle.
- * @returns On success, the maximum value of the control.
- *  On failure, -EINVAL.
- * @ingroup libtinyalsa-mixer
- */
-int mixer_ctl_get_range_max(const struct mixer_ctl *ctl)
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
 {
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
         return -EINVAL;
 
-    return ctl->info.value.integer.max;
+    return ctl->info->value.integer.max;
 }
 
-/** Get the number of enumerated items in the control.
- * @param ctl An initialized control handle.
- * @returns The number of enumerated items in the control.
- * @ingroup libtinyalsa-mixer
- */
-unsigned int mixer_ctl_get_num_enums(const struct mixer_ctl *ctl)
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
 {
     if (!ctl)
         return 0;
 
-    return ctl->info.value.enumerated.items;
+    return ctl->info->value.enumerated.items;
 }
 
-int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
-{
-    struct mixer_ctl_group *grp = ctl->grp;
-    struct snd_ctl_elem_info tmp;
-    unsigned int m;
-    char **enames;
-
-    if (ctl->ename) {
-        return 0;
-    }
-
-    enames = calloc(ctl->info.value.enumerated.items, sizeof(char*));
-    if (!enames)
-        goto fail;
-    for (m = 0; m < ctl->info.value.enumerated.items; m++) {
-        memset(&tmp, 0, sizeof(tmp));
-        tmp.id.numid = ctl->info.id.numid;
-        tmp.value.enumerated.item = m;
-        if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
-            goto fail;
-        enames[m] = strdup(tmp.value.enumerated.name);
-        if (!enames[m])
-            goto fail;
-    }
-    ctl->ename = enames;
-    return 0;
-
-fail:
-    if (enames) {
-        for (m = 0; m < ctl->info.value.enumerated.items; m++) {
-            if (enames[m]) {
-                free(enames[m]);
-            }
-        }
-        free(enames);
-    }
-    return -1;
-}
-
-/** Gets the string representation of an enumerated item.
- * @param ctl An initialized control handle.
- * @param enum_id The index of the enumerated value.
- * @returns A string representation of the enumerated item.
- * @ingroup libtinyalsa-mixer
- */
 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
                                       unsigned int enum_id)
 {
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
-        (enum_id >= ctl->info.value.enumerated.items) ||
-        mixer_ctl_fill_enum_string(ctl) != 0)
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
+        (enum_id >= ctl->info->value.enumerated.items))
         return NULL;
 
     return (const char *)ctl->ename[enum_id];
 }
 
-/** Set an enumeration value by string value.
- * @param ctl An enumerated mixer control.
- * @param string The string representation of an enumeration.
- * @returns On success, zero.
- *  On failure, zero.
- * @ingroup libtinyalsa-mixer
- */
 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
 {
-    struct mixer_ctl_group *grp;
     unsigned int i, num_enums;
     struct snd_ctl_elem_value ev;
     int ret;
 
-    if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
-        mixer_ctl_fill_enum_string(ctl) != 0)
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
         return -EINVAL;
 
-    grp = ctl->grp;
-    num_enums = ctl->info.value.enumerated.items;
+    num_enums = ctl->info->value.enumerated.items;
     for (i = 0; i < num_enums; i++) {
         if (!strcmp(string, ctl->ename[i])) {
             memset(&ev, 0, sizeof(ev));
             ev.value.enumerated.item[0] = i;
-            ev.id.numid = ctl->info.id.numid;
-            ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+            ev.id.numid = ctl->info->id.numid;
+            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
             if (ret < 0)
                 return ret;
             return 0;
@@ -1270,3 +613,68 @@
 
     return -EINVAL;
 }
+
+/** Subscribes for the mixer events.
+ * @param mixer A mixer handle.
+ * @param subscribe value indicating subscribe or unsubscribe for events
+ * @returns On success, zero.
+ *  On failure, non-zero.
+ * @ingroup libtinyalsa-mixer
+ */
+int mixer_subscribe_events(struct mixer *mixer, int subscribe)
+{
+    if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+/** Wait for mixer events.
+ * @param mixer A mixer handle.
+ * @param timeout timout value
+ * @returns On success, 1.
+ *  On failure, -errno.
+ *  On timeout, 0
+ * @ingroup libtinyalsa-mixer
+ */
+int mixer_wait_event(struct mixer *mixer, int timeout)
+{
+    struct pollfd pfd;
+
+    pfd.fd = mixer->fd;
+    pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+
+    for (;;) {
+        int err;
+        err = poll(&pfd, 1, timeout);
+        if (err < 0)
+            return -errno;
+        if (!err)
+            return 0;
+        if (pfd.revents & (POLLERR | POLLNVAL))
+            return -EIO;
+        if (pfd.revents & (POLLIN | POLLOUT))
+            return 1;
+    }
+}
+
+/** Consume a mixer event.
+ * If mixer_subscribe_events has been called,
+ * mixer_wait_event will identify when a control value has changed.
+ * This function will clear a single event from the mixer so that
+ * further events can be alerted.
+ *
+ * @param mixer A mixer handle.
+ * @returns 0 on success.  -errno on failure.
+ * @ingroup libtinyalsa-mixer
+ */
+int mixer_consume_event(struct mixer *mixer) {
+    struct snd_ctl_event ev;
+    ssize_t count = read(mixer->fd, &ev, sizeof(ev));
+    // Exporting the actual event would require exposing snd_ctl_event
+    // via the header file, and all associated structs.
+    // The events generally tell you exactly which value changed,
+    // but reading values you're interested isn't hard and simplifies
+    // the interface greatly.
+    return (count >= 0) ? 0 : -errno;
+}
diff --git a/src/mixer_hw.c b/src/mixer_hw.c
deleted file mode 100644
index da5a390..0000000
--- a/src/mixer_hw.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/* mixer_hw.c
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-#include <poll.h>
-
-#include <sys/ioctl.h>
-
-#include <linux/ioctl.h>
-#include <sound/asound.h>
-
-#include "mixer_io.h"
-
-/** Store the hardware (kernel interface) mixer data */
-struct mixer_hw_data {
-    /* Card number for the mixer */
-    unsigned int card;
-    /* File descriptor of the mixer device node */
-    int fd;
-};
-
-static void mixer_hw_close(void *data)
-{
-    struct mixer_hw_data *hw_data = data;
-
-    if (!hw_data)
-        return;
-
-    if (hw_data->fd >= 0)
-        close(hw_data->fd);
-
-    hw_data->fd = -1;
-    free(hw_data);
-    hw_data = NULL;
-}
-
-static int mixer_hw_ioctl(void *data, unsigned int cmd, ...)
-{
-    struct mixer_hw_data *hw_data = data;
-    va_list ap;
-    void *arg;
-
-    va_start(ap, cmd);
-    arg = va_arg(ap, void *);
-    va_end(ap);
-
-    return ioctl(hw_data->fd, cmd, arg);
-}
-
-static ssize_t mixer_hw_read_event(void *data, struct snd_ctl_event *ev,
-                                   size_t size)
-{
-    struct mixer_hw_data *hw_data = data;
-
-    return read(hw_data->fd, ev, size);
-}
-
-static const struct mixer_ops mixer_hw_ops = {
-    .close = mixer_hw_close,
-    .ioctl = mixer_hw_ioctl,
-    .read_event = mixer_hw_read_event,
-};
-
-int mixer_hw_open(unsigned int card, void **data,
-                  const struct mixer_ops **ops)
-{
-    struct mixer_hw_data *hw_data;
-    int fd;
-    char fn[256];
-
-    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
-    fd = open(fn, O_RDWR);
-    if (fd < 0)
-        return fd;
-
-    hw_data = calloc(1, sizeof(*hw_data));
-    if (!hw_data)
-        return -1;
-
-    hw_data->card = card;
-    hw_data->fd = fd;
-    *data = hw_data;
-    *ops = &mixer_hw_ops;
-
-    return fd;
-}
diff --git a/src/mixer_io.h b/src/mixer_io.h
deleted file mode 100644
index bb3bc44..0000000
--- a/src/mixer_io.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* mixer_io.h
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#ifndef TINYALSA_SRC_MIXER_H
-#define TINYALSA_SRC_MIXER_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sound/asound.h>
-
-struct mixer_ops;
-
-int mixer_hw_open(unsigned int card, void **data,
-                  const struct mixer_ops **ops);
-int mixer_plugin_open(unsigned int card, void **data,
-                      const struct mixer_ops **ops);
-
-struct mixer_ops {
-    void (*close) (void *data);
-    int (*get_poll_fd) (void *data, struct pollfd *pfd, int count);
-    ssize_t (*read_event) (void *data, struct snd_ctl_event *ev, size_t size);
-    int (*ioctl) (void *data, unsigned int cmd, ...);
-};
-
-#endif /* TINYALSA_SRC_MIXER_H */
diff --git a/src/mixer_plugin.c b/src/mixer_plugin.c
deleted file mode 100644
index 34117a9..0000000
--- a/src/mixer_plugin.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/* mixer_plugin.c
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#include <tinyalsa/plugin.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-#include <poll.h>
-#include <dlfcn.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <sys/ioctl.h>
-
-#include <linux/ioctl.h>
-#include <sound/asound.h>
-
-#include "snd_card_plugin.h"
-#include "mixer_io.h"
-
-/** Encapulates the mixer plugin specific data */
-struct mixer_plug_data {
-    /** Card number associated with the plugin */
-    int card;
-    /** Device node for mixer */
-    void *mixer_node;
-    /** Pointer to the plugin's ops */
-    const struct mixer_plugin_ops *ops;
-    /** Pointer to plugin responsible to service the controls */
-    struct mixer_plugin *plugin;
-    /** Handle to the plugin library */
-    void *dl_hdl;
-};
-
-static int mixer_plug_get_elem_id(struct mixer_plug_data *plug_data,
-                struct snd_ctl_elem_id *id, unsigned int offset)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-
-    if (offset >= plugin->num_controls) {
-        fprintf(stderr, "%s: invalid offset %u\n",
-				__func__, offset);
-        return -EINVAL;
-    }
-
-    ctl = plugin->controls + offset;
-    id->numid = offset;
-    id->iface = ctl->iface;
-
-    strncpy((char *)id->name, (char *)ctl->name,
-            sizeof(id->name));
-
-    return 0;
-}
-
-static int mixer_plug_info_enum(struct snd_control *ctl,
-                struct snd_ctl_elem_info *einfo)
-{
-    struct snd_value_enum *val = ctl->value;
-
-    einfo->count = 1;
-    einfo->value.enumerated.items = val->items;
-
-    if (einfo->value.enumerated.item >= val->items)
-        return -EINVAL;
-
-    strncpy(einfo->value.enumerated.name,
-            val->texts[einfo->value.enumerated.item],
-            sizeof(einfo->value.enumerated.name));
-
-    return 0;
-}
-
-static int mixer_plug_info_bytes(struct snd_control *ctl,
-                struct snd_ctl_elem_info *einfo)
-{
-    struct snd_value_bytes *val;
-    struct snd_value_tlv_bytes *val_tlv;
-
-    if (ctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
-        val_tlv = ctl->value;
-        einfo->count = val_tlv->size;
-    } else {
-        val = ctl->value;
-        einfo->count = val->size;
-    }
-
-    return 0;
-}
-
-static int mixer_plug_info_integer(struct snd_control *ctl,
-                struct snd_ctl_elem_info *einfo)
-{
-    struct snd_value_int *val = ctl->value;
-
-    einfo->count = val->count;
-    einfo->value.integer.min = val->min;
-    einfo->value.integer.max = val->max;
-    einfo->value.integer.step = val->step;
-
-    return 0;
-}
-
-void mixer_plug_notifier_cb(struct mixer_plugin *plugin)
-{
-    plugin->event_cnt++;
-    eventfd_write(plugin->eventfd, 1);
-}
-
-/* In consume_event/read, do not call eventfd_read until all events are read from list.
-   This will make poll getting unblocked until all events are read */
-static ssize_t mixer_plug_read_event(void *data, struct snd_ctl_event *ev, size_t size)
-{
-    struct mixer_plug_data *plug_data = data;
-    struct mixer_plugin *plugin = plug_data->plugin;
-    eventfd_t evfd;
-    ssize_t result = 0;
-
-    result = plug_data->ops->read_event(plugin, ev, size);
-
-    if (result > 0) {
-        plugin->event_cnt -=  result / sizeof(struct snd_ctl_event);
-        if (plugin->event_cnt == 0)
-            eventfd_read(plugin->eventfd, &evfd);
-    }
-
-    return result;
-}
-
-static int mixer_plug_subscribe_events(struct mixer_plug_data *plug_data,
-                int *subscribe)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    eventfd_t evfd;
-
-    if (*subscribe < 0 || *subscribe > 1) {
-        *subscribe = plugin->subscribed;
-        return -EINVAL;
-    }
-
-    if (*subscribe && !plugin->subscribed) {
-        plug_data->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
-    } else if (plugin->subscribed && !*subscribe) {
-        plug_data->ops->subscribe_events(plugin, NULL);
-
-        if (plugin->event_cnt)
-            eventfd_read(plugin->eventfd, &evfd);
-
-        plugin->event_cnt = 0;
-    }
-
-    plugin->subscribed = *subscribe;
-    return 0;
-}
-
-static int mixer_plug_get_poll_fd(void *data, struct pollfd *pfd, int count)
-{
-    struct mixer_plug_data *plug_data = data;
-    struct mixer_plugin *plugin = plug_data->plugin;
-
-    if (plugin->eventfd != -1) {
-        pfd[count].fd = plugin->eventfd;
-        return 0;
-    }
-    return -ENODEV;
-}
-
-static int mixer_plug_tlv_write(struct mixer_plug_data *plug_data,
-                struct snd_ctl_tlv *tlv)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-    struct snd_value_tlv_bytes *val_tlv;
-
-    ctl = plugin->controls + tlv->numid;
-    val_tlv = ctl->value;
-
-    return val_tlv->put(plugin, ctl, tlv);
-}
-
-static int mixer_plug_tlv_read(struct mixer_plug_data *plug_data,
-                struct snd_ctl_tlv *tlv)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-    struct snd_value_tlv_bytes *val_tlv;
-
-    ctl = plugin->controls + tlv->numid;
-    val_tlv = ctl->value;
-
-    return val_tlv->get(plugin, ctl, tlv);
-}
-
-static int mixer_plug_elem_write(struct mixer_plug_data *plug_data,
-                struct snd_ctl_elem_value *ev)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-    int ret;
-
-    ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
-    if (ret < 0)
-        return ret;
-
-    ctl = plugin->controls + ev->id.numid;
-
-    return ctl->put(plugin, ctl, ev);
-}
-
-static int mixer_plug_elem_read(struct mixer_plug_data *plug_data,
-                struct snd_ctl_elem_value *ev)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-    int ret;
-
-    ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
-    if (ret < 0)
-        return ret;
-
-    ctl = plugin->controls + ev->id.numid;
-
-    return ctl->get(plugin, ctl, ev);
-
-}
-
-static int mixer_plug_get_elem_info(struct mixer_plug_data *plug_data,
-                struct snd_ctl_elem_info *einfo)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    struct snd_control *ctl;
-    int ret;
-
-    ret = mixer_plug_get_elem_id(plug_data, &einfo->id,
-                    einfo->id.numid);
-    if (ret < 0)
-        return ret;
-
-    ctl = plugin->controls + einfo->id.numid;
-    einfo->type = ctl->type;
-    einfo->access = ctl->access;
-
-    switch (einfo->type) {
-    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
-        ret = mixer_plug_info_enum(ctl, einfo);
-        if (ret < 0)
-            return ret;
-        break;
-    case SNDRV_CTL_ELEM_TYPE_BYTES:
-        ret = mixer_plug_info_bytes(ctl, einfo);
-        if (ret < 0)
-            return ret;
-        break;
-    case SNDRV_CTL_ELEM_TYPE_INTEGER:
-        ret = mixer_plug_info_integer(ctl, einfo);
-        if (ret < 0)
-            return ret;
-        break;
-    default:
-        fprintf(stderr,"%s: unknown type %d\n",
-				__func__, einfo->type);
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static int mixer_plug_get_elem_list(struct mixer_plug_data *plug_data,
-                struct snd_ctl_elem_list *elist)
-{
-    struct mixer_plugin *plugin = plug_data->plugin;
-    unsigned int avail;
-    struct snd_ctl_elem_id *id;
-    int ret;
-
-    elist->count = plugin->num_controls;
-    elist->used = 0;
-    avail = elist->space;
-
-    while (avail > 0) {
-        id = elist->pids + elist->used;
-        ret = mixer_plug_get_elem_id(plug_data, id, elist->used);
-        if (ret < 0)
-            return ret;
-
-        avail--;
-        elist->used++;
-    }
-
-    return 0;
-}
-
-static int mixer_plug_get_card_info(struct mixer_plug_data *plug_data,
-                struct snd_ctl_card_info *card_info)
-{
-    /*TODO: Fill card_info here from snd-card-def */
-    memset(card_info, 0, sizeof(*card_info));
-    card_info->card = plug_data->card;
-
-    return 0;
-}
-
-static void mixer_plug_close(void *data)
-{
-    struct mixer_plug_data *plug_data = data;
-    struct mixer_plugin *plugin = plug_data->plugin;
-    eventfd_t evfd;
-
-    if (plugin->event_cnt)
-        eventfd_read(plugin->eventfd, &evfd);
-
-    plug_data->ops->close(&plugin);
-    dlclose(plug_data->dl_hdl);
-
-    free(plug_data);
-    plug_data = NULL;
-}
-
-static int mixer_plug_ioctl(void *data, unsigned int cmd, ...)
-{
-    struct mixer_plug_data *plug_data = data;
-    int ret;
-    va_list ap;
-    void *arg;
-
-    va_start(ap, cmd);
-    arg = va_arg(ap, void *);
-    va_end(ap);
-
-    switch (cmd) {
-    case SNDRV_CTL_IOCTL_CARD_INFO:
-        ret = mixer_plug_get_card_info(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_ELEM_LIST:
-        ret = mixer_plug_get_elem_list(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_ELEM_INFO:
-        ret = mixer_plug_get_elem_info(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_ELEM_READ:
-        ret = mixer_plug_elem_read(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_ELEM_WRITE:
-        ret = mixer_plug_elem_write(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_TLV_READ:
-        ret = mixer_plug_tlv_read(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_TLV_WRITE:
-        ret = mixer_plug_tlv_write(plug_data, arg);
-        break;
-    case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
-        ret = mixer_plug_subscribe_events(plug_data, arg);
-        break;
-    default:
-        /* TODO: plugin should support ioctl */
-        ret = -EFAULT;
-        break;
-    }
-
-    return ret;
-}
-
-static const struct mixer_ops mixer_plug_ops = {
-    .close = mixer_plug_close,
-    .read_event = mixer_plug_read_event,
-    .get_poll_fd = mixer_plug_get_poll_fd,
-    .ioctl = mixer_plug_ioctl,
-};
-
-int mixer_plugin_open(unsigned int card, void **data,
-                      const struct mixer_ops **ops)
-{
-    struct mixer_plug_data *plug_data;
-    struct mixer_plugin *plugin = NULL;
-    void *dl_hdl;
-    char *so_name;
-    int ret;
-
-    plug_data = calloc(1, sizeof(*plug_data));
-    if (!plug_data)
-        return -ENOMEM;
-
-    plug_data->mixer_node = snd_utils_open_mixer(card);
-    if (!plug_data->mixer_node) {
-        /* Do not print error here.
-         * It is valid for card to not have virtual mixer node
-         */
-        goto err_get_mixer_node;
-    }
-
-    ret = snd_utils_get_str(plug_data->mixer_node, "so-name",
-                               &so_name);
-    if(ret) {
-        fprintf(stderr, "%s: mixer so-name not found for card %u\n",
-                __func__, card);
-        goto err_get_lib_name;
-
-    }
-
-    dl_hdl = dlopen(so_name, RTLD_NOW);
-    if (!dl_hdl) {
-        fprintf(stderr, "%s: unable to open %s\n",
-                __func__, so_name);
-        goto err_dlopen;
-    }
-
-    dlerror();
-    plug_data->ops = dlsym(dl_hdl, "mixer_plugin_ops");
-    if (!plug_data->ops) {
-        fprintf(stderr, "%s: dlsym open fn failed: %s\n",
-                __func__, dlerror());
-        goto err_ops;
-    }
-
-    ret = plug_data->ops->open(&plugin, card);
-    if (ret) {
-        fprintf(stderr, "%s: failed to open plugin, err: %d\n",
-                __func__, ret);
-        goto err_ops;
-    }
-
-    plug_data->plugin = plugin;
-    plug_data->card = card;
-    plug_data->dl_hdl = dl_hdl;
-    plugin->eventfd = eventfd(0, 0);
-
-    *data = plug_data;
-    *ops = &mixer_plug_ops;
-
-    return 0;
-
-err_ops:
-    dlclose(dl_hdl);
-err_dlopen:
-err_get_lib_name:
-    snd_utils_close_dev_node(plug_data->mixer_node);
-err_get_mixer_node:
-    free(plug_data);
-    return -1;
-}
diff --git a/src/pcm.c b/src/pcm.c
index 352ddc6..735fb1d 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -38,37 +38,17 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/time.h>
-#include <time.h>
 #include <limits.h>
 
 #include <linux/ioctl.h>
-
-#ifndef __force
 #define __force
-#endif
-
-#ifndef __bitwise
 #define __bitwise
-#endif
-
-#ifndef __user
 #define __user
-#endif
-
 #include <sound/asound.h>
 
-#include <tinyalsa/pcm.h>
-#include <tinyalsa/limits.h>
-#include "pcm_io.h"
-#include "snd_card_plugin.h"
+#include <tinyalsa/asoundlib.h>
 
-#ifndef PARAM_MAX
 #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
-#endif /* PARAM_MAX */
-
-#ifndef SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
-#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
-#endif /* SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP */
 
 /* Logs information into a string; follows snprintf() in that
  * offset may be greater than size, and though no characters are copied
@@ -160,11 +140,6 @@
         (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
 }
 
-static inline const struct snd_interval *param_get_interval(const struct snd_pcm_hw_params *p, int n)
-{
-    return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
-}
-
 static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
 {
     return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
@@ -195,19 +170,27 @@
     }
 }
 
-static unsigned int param_get_min(const struct snd_pcm_hw_params *p, int n)
+static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
 {
     if (param_is_interval(n)) {
-        const struct snd_interval *i = param_get_interval(p, n);
+        struct snd_interval *i = param_to_interval(p, n);
         return i->min;
     }
     return 0;
 }
 
-static unsigned int param_get_max(const struct snd_pcm_hw_params *p, int n)
+static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
 {
     if (param_is_interval(n)) {
-        const struct snd_interval *i = param_get_interval(p, n);
+        struct snd_interval *i = param_to_interval(p, n);
+        i->max = val;
+    }
+}
+
+static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
         return i->max;
     }
     return 0;
@@ -255,73 +238,42 @@
     p->info = ~0U;
 }
 
-static unsigned int pcm_format_to_alsa(enum pcm_format format)
-{
-    switch (format) {
-
-    case PCM_FORMAT_S8:
-        return SNDRV_PCM_FORMAT_S8;
-
-    default:
-    case PCM_FORMAT_S16_LE:
-        return SNDRV_PCM_FORMAT_S16_LE;
-    case PCM_FORMAT_S16_BE:
-        return SNDRV_PCM_FORMAT_S16_BE;
-
-    case PCM_FORMAT_S24_LE:
-        return SNDRV_PCM_FORMAT_S24_LE;
-    case PCM_FORMAT_S24_BE:
-        return SNDRV_PCM_FORMAT_S24_BE;
-
-    case PCM_FORMAT_S24_3LE:
-        return SNDRV_PCM_FORMAT_S24_3LE;
-    case PCM_FORMAT_S24_3BE:
-        return SNDRV_PCM_FORMAT_S24_3BE;
-
-    case PCM_FORMAT_S32_LE:
-        return SNDRV_PCM_FORMAT_S32_LE;
-    case PCM_FORMAT_S32_BE:
-        return SNDRV_PCM_FORMAT_S32_BE;
-    };
-}
-
 #define PCM_ERROR_MAX 128
 
-/** A PCM handle.
- * @ingroup libtinyalsa-pcm
- */
 struct pcm {
-    /** The PCM's file descriptor */
     int fd;
-    /** Flags that were passed to @ref pcm_open */
     unsigned int flags;
-    /** The number of (under/over)runs that have occured */
-    int xruns;
-    /** Size of the buffer */
+    int running:1;
+    int prepared:1;
+    int underruns;
     unsigned int buffer_size;
-    /** The boundary for ring buffer pointers */
     unsigned int boundary;
-    /** Description of the last error that occured */
     char error[PCM_ERROR_MAX];
-    /** Configuration that was passed to @ref pcm_open */
     struct pcm_config config;
     struct snd_pcm_mmap_status *mmap_status;
     struct snd_pcm_mmap_control *mmap_control;
     struct snd_pcm_sync_ptr *sync_ptr;
     void *mmap_buffer;
     unsigned int noirq_frames_per_msec;
-    /** The delay of the PCM, in terms of frames */
-    long pcm_delay;
-    /** The subdevice corresponding to the PCM */
+    int wait_for_avail_min;
     unsigned int subdevice;
-    /** Pointer to the pcm ops, either hw or plugin */
-    const struct pcm_ops *ops;
-    /** Private data for pcm_hw or pcm_plugin */
-    void *data;
-    /** Pointer to the pcm node from snd card definition */
-    struct snd_node *snd_node;
 };
 
+unsigned int pcm_get_buffer_size(struct pcm *pcm)
+{
+    return pcm->buffer_size;
+}
+
+const char* pcm_get_error(struct pcm *pcm)
+{
+    return pcm->error;
+}
+
+unsigned int pcm_get_subdevice(struct pcm *pcm)
+{
+    return pcm->subdevice;
+}
+
 static int oops(struct pcm *pcm, int e, const char *fmt, ...)
 {
     va_list ap;
@@ -338,306 +290,84 @@
     return -1;
 }
 
-/** Gets the buffer size of the PCM.
- * @param pcm A PCM handle.
- * @return The buffer size of the PCM.
- * @ingroup libtinyalsa-pcm
- */
-unsigned int pcm_get_buffer_size(const struct pcm *pcm)
+static unsigned int pcm_format_to_alsa(enum pcm_format format)
 {
-    return pcm->buffer_size;
+    switch (format) {
+    case PCM_FORMAT_S32_LE:
+        return SNDRV_PCM_FORMAT_S32_LE;
+    case PCM_FORMAT_S8:
+        return SNDRV_PCM_FORMAT_S8;
+    case PCM_FORMAT_S24_3LE:
+        return SNDRV_PCM_FORMAT_S24_3LE;
+    case PCM_FORMAT_S24_LE:
+        return SNDRV_PCM_FORMAT_S24_LE;
+    default:
+    case PCM_FORMAT_S16_LE:
+        return SNDRV_PCM_FORMAT_S16_LE;
+    };
 }
 
-/** Gets the channel count of the PCM.
- * @param pcm A PCM handle.
- * @return The channel count of the PCM.
- * @ingroup libtinyalsa-pcm
- */
-unsigned int pcm_get_channels(const struct pcm *pcm)
-{
-    return pcm->config.channels;
-}
-
-/** Gets the PCM configuration.
- * @param pcm A PCM handle.
- * @return The PCM configuration.
- *  This function only returns NULL if
- *  @p pcm is NULL.
- * @ingroup libtinyalsa-pcm
- * */
-const struct pcm_config * pcm_get_config(const struct pcm *pcm)
-{
-    if (pcm == NULL)
-        return NULL;
-    return &pcm->config;
-}
-
-/** Gets the rate of the PCM.
- * The rate is given in frames per second.
- * @param pcm A PCM handle.
- * @return The rate of the PCM.
- * @ingroup libtinyalsa-pcm
- */
-unsigned int pcm_get_rate(const struct pcm *pcm)
-{
-    return pcm->config.rate;
-}
-
-/** Gets the format of the PCM.
- * @param pcm A PCM handle.
- * @return The format of the PCM.
- * @ingroup libtinyalsa-pcm
- */
-enum pcm_format pcm_get_format(const struct pcm *pcm)
-{
-    return pcm->config.format;
-}
-
-/** Gets the file descriptor of the PCM.
- * Useful for extending functionality of the PCM when needed.
- * @param pcm A PCM handle.
- * @return The file descriptor of the PCM.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_get_file_descriptor(const struct pcm *pcm)
-{
-    return pcm->fd;
-}
-
-/** Gets the error message for the last error that occured.
- * If no error occured and this function is called, the results are undefined.
- * @param pcm A PCM handle.
- * @return The error message of the last error that occured.
- * @ingroup libtinyalsa-pcm
- */
-const char* pcm_get_error(const struct pcm *pcm)
-{
-    return pcm->error;
-}
-
-/** Sets the PCM configuration.
- * @param pcm A PCM handle.
- * @param config The configuration to use for the
- *  PCM. This parameter may be NULL, in which case
- *  the default configuration is used.
- * @returns Zero on success, a negative errno value
- *  on failure.
- * @ingroup libtinyalsa-pcm
- * */
-int pcm_set_config(struct pcm *pcm, const struct pcm_config *config)
-{
-    if (pcm == NULL)
-        return -EFAULT;
-    else if (config == NULL) {
-        config = &pcm->config;
-        pcm->config.channels = 2;
-        pcm->config.rate = 48000;
-        pcm->config.period_size = 1024;
-        pcm->config.period_count = 4;
-        pcm->config.format = PCM_FORMAT_S16_LE;
-        pcm->config.start_threshold = config->period_count * config->period_size;
-        pcm->config.stop_threshold = config->period_count * config->period_size;
-        pcm->config.silence_threshold = 0;
-        pcm->config.silence_size = 0;
-    } else
-        pcm->config = *config;
-
-    struct snd_pcm_hw_params params;
-    param_init(&params);
-    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
-                   pcm_format_to_alsa(config->format));
-    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
-    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
-                  config->channels);
-    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
-    param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
-
-    if (pcm->flags & PCM_NOIRQ) {
-
-        if (!(pcm->flags & PCM_MMAP)) {
-            oops(pcm, EINVAL, "noirq only currently supported with mmap().");
-            return -EINVAL;
-        }
-
-        params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
-        pcm->noirq_frames_per_msec = config->rate / 1000;
-    }
-
-    if (pcm->flags & PCM_MMAP)
-        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
-                   SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
-    else
-        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
-                   SNDRV_PCM_ACCESS_RW_INTERLEAVED);
-
-    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
-        int errno_copy = errno;
-        oops(pcm, errno, "cannot set hw params");
-        return -errno_copy;
-    }
-
-    /* get our refined hw_params */
-    pcm->config.period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
-    pcm->config.period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
-    pcm->buffer_size = config->period_count * config->period_size;
-
-    if (pcm->flags & PCM_MMAP) {
-        pcm->mmap_buffer = pcm->ops->mmap(pcm->data, NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
-                                PROT_READ | PROT_WRITE, MAP_SHARED, 0);
-        if (pcm->mmap_buffer == MAP_FAILED) {
-            int errno_copy = errno;
-            oops(pcm, errno, "failed to mmap buffer %d bytes\n",
-                 pcm_frames_to_bytes(pcm, pcm->buffer_size));
-            return -errno_copy;
-        }
-    }
-
-    struct snd_pcm_sw_params sparams;
-    memset(&sparams, 0, sizeof(sparams));
-    sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
-    sparams.period_step = 1;
-    sparams.avail_min = config->period_size;
-
-    if (!config->start_threshold) {
-        if (pcm->flags & PCM_IN)
-            pcm->config.start_threshold = sparams.start_threshold = 1;
-        else
-            pcm->config.start_threshold = sparams.start_threshold =
-                config->period_count * config->period_size / 2;
-    } else
-        sparams.start_threshold = config->start_threshold;
-
-    /* pick a high stop threshold - todo: does this need further tuning */
-    if (!config->stop_threshold) {
-        if (pcm->flags & PCM_IN)
-            pcm->config.stop_threshold = sparams.stop_threshold =
-                config->period_count * config->period_size * 10;
-        else
-            pcm->config.stop_threshold = sparams.stop_threshold =
-                config->period_count * config->period_size;
-    }
-    else
-        sparams.stop_threshold = config->stop_threshold;
-
-    sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
-    sparams.silence_size = config->silence_size;
-    sparams.silence_threshold = config->silence_threshold;
-    pcm->boundary = sparams.boundary = pcm->buffer_size;
-
-    while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
-        pcm->boundary *= 2;
-
-    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
-        int errno_copy = errno;
-        oops(pcm, errno, "cannot set sw params");
-        return -errno_copy;
-    }
-
-    return 0;
-}
-
-/** Gets the subdevice on which the pcm has been opened.
- * @param pcm A PCM handle.
- * @return The subdevice on which the pcm has been opened */
-unsigned int pcm_get_subdevice(const struct pcm *pcm)
-{
-    return pcm->subdevice;
-}
-
-/** Determines the number of bits occupied by a @ref pcm_format.
- * @param format A PCM format.
- * @return The number of bits associated with @p format
- * @ingroup libtinyalsa-pcm
- */
 unsigned int pcm_format_to_bits(enum pcm_format format)
 {
     switch (format) {
     case PCM_FORMAT_S32_LE:
-    case PCM_FORMAT_S32_BE:
     case PCM_FORMAT_S24_LE:
-    case PCM_FORMAT_S24_BE:
         return 32;
     case PCM_FORMAT_S24_3LE:
-    case PCM_FORMAT_S24_3BE:
         return 24;
     default:
     case PCM_FORMAT_S16_LE:
-    case PCM_FORMAT_S16_BE:
         return 16;
-    case PCM_FORMAT_S8:
-        return 8;
     };
 }
 
-/** Determines how many frames of a PCM can fit into a number of bytes.
- * @param pcm A PCM handle.
- * @param bytes The number of bytes.
- * @return The number of frames that may fit into @p bytes
- * @ingroup libtinyalsa-pcm
- */
-unsigned int pcm_bytes_to_frames(const struct pcm *pcm, unsigned int bytes)
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
 {
     return bytes / (pcm->config.channels *
         (pcm_format_to_bits(pcm->config.format) >> 3));
 }
 
-/** Determines how many bytes are occupied by a number of frames of a PCM.
- * @param pcm A PCM handle.
- * @param frames The number of frames of a PCM.
- * @return The bytes occupied by @p frames.
- * @ingroup libtinyalsa-pcm
- */
-unsigned int pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames)
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
 {
     return frames * pcm->config.channels *
         (pcm_format_to_bits(pcm->config.format) >> 3);
 }
 
-static int pcm_sync_ptr(struct pcm *pcm, int flags)
-{
-    if (pcm->sync_ptr == NULL) {
-        /* status and control are mmaped */
-
-        if (flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
-            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HWSYNC) == -1) {
-                oops(pcm, errno, "failed to sync hardware pointer");
-                return -1;
-            }
-        }
-    } else {
+static int pcm_sync_ptr(struct pcm *pcm, int flags) {
+    if (pcm->sync_ptr) {
         pcm->sync_ptr->flags = flags;
-        if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SYNC_PTR,
-                            pcm->sync_ptr) < 0) {
-            oops(pcm, errno, "failed to sync mmap ptr");
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
             return -1;
-        }
     }
-
     return 0;
 }
 
-static int pcm_hw_mmap_status(struct pcm *pcm)
-{
+static int pcm_hw_mmap_status(struct pcm *pcm) {
+
     if (pcm->sync_ptr)
         return 0;
 
     int page_size = sysconf(_SC_PAGE_SIZE);
-    pcm->mmap_status = pcm->ops->mmap(pcm->data, NULL, page_size, PROT_READ, MAP_SHARED,
-                            SNDRV_PCM_MMAP_OFFSET_STATUS);
+    pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
+                            pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
     if (pcm->mmap_status == MAP_FAILED)
         pcm->mmap_status = NULL;
     if (!pcm->mmap_status)
         goto mmap_error;
 
-    pcm->mmap_control = pcm->ops->mmap(pcm->data, NULL, page_size, PROT_READ | PROT_WRITE,
-                             MAP_SHARED, SNDRV_PCM_MMAP_OFFSET_CONTROL);
+    pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+                             MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
     if (pcm->mmap_control == MAP_FAILED)
         pcm->mmap_control = NULL;
     if (!pcm->mmap_control) {
-        pcm->ops->munmap(pcm->data, pcm->mmap_status, page_size);
+        munmap(pcm->mmap_status, page_size);
         pcm->mmap_status = NULL;
         goto mmap_error;
     }
+    if (pcm->flags & PCM_MMAP)
+        pcm->mmap_control->avail_min = pcm->config.avail_min;
+    else
+        pcm->mmap_control->avail_min = 1;
 
     return 0;
 
@@ -648,6 +378,12 @@
         return -ENOMEM;
     pcm->mmap_status = &pcm->sync_ptr->s.status;
     pcm->mmap_control = &pcm->sync_ptr->c.control;
+    if (pcm->flags & PCM_MMAP)
+        pcm->mmap_control->avail_min = pcm->config.avail_min;
+    else
+        pcm->mmap_control->avail_min = 1;
+
+    pcm_sync_ptr(pcm, 0);
 
     return 0;
 }
@@ -659,58 +395,215 @@
     } else {
         int page_size = sysconf(_SC_PAGE_SIZE);
         if (pcm->mmap_status)
-            pcm->ops->munmap(pcm->data, pcm->mmap_status, page_size);
+            munmap(pcm->mmap_status, page_size);
         if (pcm->mmap_control)
-            pcm->ops->munmap(pcm->data, pcm->mmap_control, page_size);
+            munmap(pcm->mmap_control, page_size);
     }
     pcm->mmap_status = NULL;
     pcm->mmap_control = NULL;
 }
 
+static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
+                          char *buf, unsigned int src_offset,
+                          unsigned int frames)
+{
+    int size_bytes = pcm_frames_to_bytes(pcm, frames);
+    int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
+    int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
+
+    /* interleaved only atm */
+    if (pcm->flags & PCM_IN)
+        memcpy(buf + src_offset_bytes,
+               (char*)pcm->mmap_buffer + pcm_offset_bytes,
+               size_bytes);
+    else
+        memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
+               buf + src_offset_bytes,
+               size_bytes);
+    return 0;
+}
+
+static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
+                                unsigned int offset, unsigned int size)
+{
+    void *pcm_areas;
+    int commit;
+    unsigned int pcm_offset, frames, count = 0;
+
+    while (size > 0) {
+        frames = size;
+        pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+        pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
+        commit = pcm_mmap_commit(pcm, pcm_offset, frames);
+        if (commit < 0) {
+            oops(pcm, errno, "failed to commit %d frames\n", frames);
+            return commit;
+        }
+
+        offset += commit;
+        count += commit;
+        size -= commit;
+    }
+    return count;
+}
+
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+                       struct timespec *tstamp)
+{
+    int frames;
+    int rc;
+    snd_pcm_uframes_t hw_ptr;
+
+    if (!pcm_is_ready(pcm))
+        return -1;
+
+    rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
+    if (rc < 0)
+        return -1;
+
+    if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+            (pcm->mmap_status->state != PCM_STATE_DRAINING))
+        return -1;
+
+    *tstamp = pcm->mmap_status->tstamp;
+    if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+        return -1;
+
+    hw_ptr = pcm->mmap_status->hw_ptr;
+    if (pcm->flags & PCM_IN)
+        frames = hw_ptr - pcm->mmap_control->appl_ptr;
+    else
+        frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+    if (frames < 0)
+        frames += pcm->boundary;
+    else if (frames > (int)pcm->boundary)
+        frames -= pcm->boundary;
+
+    *avail = (unsigned int)frames;
+
+    return 0;
+}
+
+int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp)
+{
+    int frames;
+    int rc;
+
+    if (pcm == NULL || hw_ptr == NULL || tstamp == NULL)
+        return oops(pcm, EINVAL, "pcm %p, hw_ptr %p, tstamp %p", pcm, hw_ptr, tstamp);
+
+    if (!pcm_is_ready(pcm))
+        return oops(pcm, errno, "pcm_is_ready failed");
+
+    rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
+    if (rc < 0)
+        return oops(pcm, errno, "pcm_sync_ptr failed");
+
+    if (pcm->mmap_status == NULL)
+        return oops(pcm, EINVAL, "pcm %p, mmap_status is NULL", pcm);
+
+    if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+            (pcm->mmap_status->state != PCM_STATE_DRAINING))
+        return oops(pcm, ENOSYS, "invalid stream state %d", pcm->mmap_status->state);
+
+    *tstamp = pcm->mmap_status->tstamp;
+    if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+        return oops(pcm, errno, "invalid time stamp");
+
+    *hw_ptr = pcm->mmap_status->hw_ptr;
+
+    return 0;
+}
+
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+    struct snd_xferi x;
+
+    if (pcm->flags & PCM_IN)
+        return -EINVAL;
+
+    x.buf = (void*)data;
+    x.frames = count / (pcm->config.channels *
+                        pcm_format_to_bits(pcm->config.format) / 8);
+
+    for (;;) {
+        if (!pcm->running) {
+            int prepare_error = pcm_prepare(pcm);
+            if (prepare_error)
+                return prepare_error;
+            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
+                return oops(pcm, errno, "cannot write initial data");
+            pcm->running = 1;
+            return 0;
+        }
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
+            pcm->prepared = 0;
+            pcm->running = 0;
+            if (errno == EPIPE) {
+                /* we failed to make our window -- try to restart if we are
+                 * allowed to do so.  Otherwise, simply allow the EPIPE error to
+                 * propagate up to the app level */
+                pcm->underruns++;
+                if (pcm->flags & PCM_NORESTART)
+                    return -EPIPE;
+                continue;
+            }
+            return oops(pcm, errno, "cannot write stream data");
+        }
+        return 0;
+    }
+}
+
+int pcm_read(struct pcm *pcm, void *data, unsigned int count)
+{
+    struct snd_xferi x;
+
+    if (!(pcm->flags & PCM_IN))
+        return -EINVAL;
+
+    x.buf = data;
+    x.frames = count / (pcm->config.channels *
+                        pcm_format_to_bits(pcm->config.format) / 8);
+
+    for (;;) {
+        if (!pcm->running) {
+            if (pcm_start(pcm) < 0) {
+                fprintf(stderr, "start error");
+                return -errno;
+            }
+        }
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
+            pcm->prepared = 0;
+            pcm->running = 0;
+            if (errno == EPIPE) {
+                    /* we failed to make our window -- try to restart */
+                pcm->underruns++;
+                continue;
+            }
+            return oops(pcm, errno, "cannot read stream data");
+        }
+        return 0;
+    }
+}
+
 static struct pcm bad_pcm = {
     .fd = -1,
 };
 
-/** Gets the hardware parameters of a PCM, without created a PCM handle.
- * @param card The card of the PCM.
- *  The default card is zero.
- * @param device The device of the PCM.
- *  The default device is zero.
- * @param flags Specifies whether the PCM is an input or output.
- *  May be one of the following:
- *   - @ref PCM_IN
- *   - @ref PCM_OUT
- * @return On success, the hardware parameters of the PCM; on failure, NULL.
- * @ingroup libtinyalsa-pcm
- */
 struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
                                   unsigned int flags)
 {
     struct snd_pcm_hw_params *params;
-    void *snd_node = NULL, *data;
-    const struct pcm_ops *ops;
+    char fn[256];
     int fd;
 
-    ops = &hw_ops;
-    fd = ops->open(card, device, flags, &data, snd_node);
+    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+             flags & PCM_IN ? 'c' : 'p');
 
-#ifdef TINYALSA_USES_PLUGINS
+    fd = open(fn, O_RDWR);
     if (fd < 0) {
-        int pcm_type;
-        snd_node = snd_utils_open_pcm(card, device);
-        pcm_type = snd_utils_get_node_type(snd_node);
-        if (!snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
-            fprintf(stderr, "no device (hw/plugin) for card(%u), device(%u)",
-                 card, device);
-            goto err_open;
-        }
-        ops = &plug_ops;
-        fd = ops->open(card, device, flags, &data, snd_node);
-    }
-#endif
-    if (fd < 0) {
-        fprintf(stderr, "cannot open card(%d) device (%d): %s\n",
-                card, device, strerror(errno));
+        fprintf(stderr, "cannot open device '%s'\n", fn);
         goto err_open;
     }
 
@@ -719,36 +612,23 @@
         goto err_calloc;
 
     param_init(params);
-    if (ops->ioctl(data, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
+    if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
         fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
         goto err_hw_refine;
     }
 
-#ifdef TINYALSA_USES_PLUGINS
-    if (snd_node)
-        snd_utils_close_dev_node(snd_node);
-#endif
-    ops->close(data);
+    close(fd);
 
     return (struct pcm_params *)params;
 
 err_hw_refine:
     free(params);
 err_calloc:
-#ifdef TINYALSA_USES_PLUGINS
-    if (snd_node)
-        snd_utils_close_dev_node(snd_node);
-#endif
-    ops->close(data);
+    close(fd);
 err_open:
     return NULL;
 }
 
-/** Frees the hardware parameters returned by @ref pcm_params_get.
- * @param pcm_params Hardware parameters of a PCM.
- *  May be NULL.
- * @ingroup libtinyalsa-pcm
- */
 void pcm_params_free(struct pcm_params *pcm_params)
 {
     struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
@@ -808,14 +688,7 @@
     }
 }
 
-/** Gets a mask from a PCM's hardware parameters.
- * @param pcm_params A PCM's hardware parameters.
- * @param param The parameter to get.
- * @return If @p pcm_params is NULL or @p param is not a mask, NULL is returned.
- *  Otherwise, the mask associated with @p param is returned.
- * @ingroup libtinyalsa-pcm
- */
-const struct pcm_mask *pcm_params_get_mask(const struct pcm_params *pcm_params,
+struct pcm_mask *pcm_params_get_mask(const struct pcm_params *pcm_params,
                                      enum pcm_param param)
 {
     int p;
@@ -829,15 +702,9 @@
         return NULL;
     }
 
-    return (const struct pcm_mask *)param_to_mask(params, p);
+    return (struct pcm_mask *)param_to_mask(params, p);
 }
 
-/** Get the minimum of a specified PCM parameter.
- * @param pcm_params A PCM parameters structure.
- * @param param The specified parameter to get the minimum of.
- * @returns On success, the parameter minimum.
- *  On failure, zero.
- */
 unsigned int pcm_params_get_min(const struct pcm_params *pcm_params,
                                 enum pcm_param param)
 {
@@ -854,16 +721,26 @@
     return param_get_min(params, p);
 }
 
-/** Get the maximum of a specified PCM parameter.
- * @param pcm_params A PCM parameters structure.
- * @param param The specified parameter to get the maximum of.
- * @returns On success, the parameter maximum.
- *  On failure, zero.
- */
+void pcm_params_set_min(struct pcm_params *pcm_params,
+                                enum pcm_param param, unsigned int val)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return;
+
+    param_set_min(params, p, val);
+}
+
 unsigned int pcm_params_get_max(const struct pcm_params *pcm_params,
                                 enum pcm_param param)
 {
-    const struct snd_pcm_hw_params *params = (const struct snd_pcm_hw_params *)pcm_params;
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
     int p;
 
     if (!params)
@@ -876,7 +753,23 @@
     return param_get_max(params, p);
 }
 
-static int pcm_mask_test(const struct pcm_mask *m, unsigned int index)
+void pcm_params_set_max(struct pcm_params *pcm_params,
+                                enum pcm_param param, unsigned int val)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return;
+
+    param_set_max(params, p, val);
+}
+
+static int pcm_mask_test(struct pcm_mask *m, unsigned int index)
 {
     const unsigned int bitshift = 5; /* for 32 bit integer */
     const unsigned int bitmask = (1 << bitshift) - 1;
@@ -888,7 +781,7 @@
     return (m->bits[element] >> (index & bitmask)) & 1;
 }
 
-static int pcm_mask_to_string(const struct pcm_mask *m, char *string, unsigned int size,
+static int pcm_mask_to_string(struct pcm_mask *m, char *string, unsigned int size,
                               char *mask_name,
                               const char * const *bit_array_name, size_t bit_array_size)
 {
@@ -915,7 +808,7 @@
 
 int pcm_params_to_string(struct pcm_params *params, char *string, unsigned int size)
 {
-    const struct pcm_mask *m;
+    struct pcm_mask *m;
     unsigned int min, max;
     unsigned int clipoffset, offset;
 
@@ -957,12 +850,6 @@
     return pcm_mask_test(pcm_params_get_mask(params, PCM_PARAM_FORMAT), alsa_format);
 }
 
-/** Closes a PCM returned by @ref pcm_open.
- * @param pcm A PCM returned by @ref pcm_open.
- *  May not be NULL.
- * @return Always returns zero.
- * @ingroup libtinyalsa-pcm
- */
 int pcm_close(struct pcm *pcm)
 {
     if (pcm == &bad_pcm)
@@ -972,113 +859,157 @@
 
     if (pcm->flags & PCM_MMAP) {
         pcm_stop(pcm);
-        pcm->ops->munmap(pcm->data, pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+        munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
     }
 
-    snd_utils_close_dev_node(pcm->snd_node);
-    pcm->ops->close(pcm->data);
+    if (pcm->fd >= 0)
+        close(pcm->fd);
+    pcm->prepared = 0;
+    pcm->running = 0;
     pcm->buffer_size = 0;
     pcm->fd = -1;
     free(pcm);
     return 0;
 }
 
-/** Opens a PCM by it's name.
- * @param name The name of the PCM.
- *  The name is given in the format: <i>hw</i>:<b>card</b>,<b>device</b>
- * @param flags Specify characteristics and functionality about the pcm.
- *  May be a bitwise AND of the following:
- *   - @ref PCM_IN
- *   - @ref PCM_OUT
- *   - @ref PCM_MMAP
- *   - @ref PCM_NOIRQ
- *   - @ref PCM_MONOTONIC
- * @param config The hardware and software parameters to open the PCM with.
- * @returns A PCM structure.
- *  If an error occurs allocating memory for the PCM, NULL is returned.
- *  Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
- *  If @ref pcm_is_ready, check @ref pcm_get_error for more information.
- * @ingroup libtinyalsa-pcm
- */
-struct pcm *pcm_open_by_name(const char *name,
-                             unsigned int flags,
-                             const struct pcm_config *config)
-{
-  unsigned int card, device;
-  if ((name[0] != 'h')
-   || (name[1] != 'w')
-   || (name[2] != ':')) {
-    return NULL;
-  } else if (sscanf(&name[3], "%u,%u", &card, &device) != 2) {
-    return NULL;
-  }
-  return pcm_open(card, device, flags, config);
-}
-
-/** Opens a PCM.
- * @param card The card that the pcm belongs to.
- *  The default card is zero.
- * @param device The device that the pcm belongs to.
- *  The default device is zero.
- * @param flags Specify characteristics and functionality about the pcm.
- *  May be a bitwise AND of the following:
- *   - @ref PCM_IN
- *   - @ref PCM_OUT
- *   - @ref PCM_MMAP
- *   - @ref PCM_NOIRQ
- *   - @ref PCM_MONOTONIC
- * @param config The hardware and software parameters to open the PCM with.
- * @returns A PCM structure.
- *  If an error occurs allocating memory for the PCM, NULL is returned.
- *  Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
- *  If @ref pcm_is_ready, check @ref pcm_get_error for more information.
- * @ingroup libtinyalsa-pcm
- */
 struct pcm *pcm_open(unsigned int card, unsigned int device,
-                     unsigned int flags, const struct pcm_config *config)
+                     unsigned int flags, struct pcm_config *config)
 {
     struct pcm *pcm;
     struct snd_pcm_info info;
+    struct snd_pcm_hw_params params;
+    struct snd_pcm_sw_params sparams;
+    char fn[256];
     int rc;
 
+    if (!config) {
+        return &bad_pcm; /* TODO: could support default config here */
+    }
     pcm = calloc(1, sizeof(struct pcm));
     if (!pcm)
-        return &bad_pcm;
+        return &bad_pcm; /* TODO: could support default config here */
 
-    /* Default to hw_ops, attemp plugin open only if hw (/dev/snd/pcm*) open fails */
-    pcm->ops = &hw_ops;
-    pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, NULL);
+    pcm->config = *config;
 
-#ifdef TINYALSA_USES_PLUGINS
-    if (pcm->fd < 0) {
-        int pcm_type;
-        pcm->snd_node = snd_utils_open_pcm(card, device);
-        pcm_type = snd_utils_get_node_type(pcm->snd_node);
-        if (!pcm->snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
-            oops(pcm, -ENODEV, "no device (hw/plugin) for card(%u), device(%u)",
-                 card, device);
-            goto fail_close_dev_node;
-        }
-        pcm->ops = &plug_ops;
-        pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, pcm->snd_node);
-    }
-#endif
-    if (pcm->fd < 0) {
-        oops(pcm, errno, "cannot open device (%u) for card (%u)",
-             device, card);
-        goto fail_close_dev_node;
-    }
+    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+             flags & PCM_IN ? 'c' : 'p');
 
     pcm->flags = flags;
+    pcm->fd = open(fn, O_RDWR|O_NONBLOCK);
+    if (pcm->fd < 0) {
+        oops(pcm, errno, "cannot open device '%s'", fn);
+        return pcm;
+    }
 
-    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)) {
+    if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) &
+              ~O_NONBLOCK) < 0) {
+        oops(pcm, errno, "failed to reset blocking mode '%s'", fn);
+        goto fail_close;
+    }
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
         oops(pcm, errno, "cannot get info");
         goto fail_close;
     }
     pcm->subdevice = info.subdevice;
 
-    if (pcm_set_config(pcm, config) != 0)
+    param_init(&params);
+    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
+                   pcm_format_to_alsa(config->format));
+    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+                   SNDRV_PCM_SUBFORMAT_STD);
+    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                  pcm_format_to_bits(config->format));
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                  pcm_format_to_bits(config->format) * config->channels);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
+                  config->channels);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
+
+    if (flags & PCM_NOIRQ) {
+        if (!(flags & PCM_MMAP)) {
+            oops(pcm, EINVAL, "noirq only currently supported with mmap().");
+            goto fail_close;
+        }
+
+        params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
+        pcm->noirq_frames_per_msec = config->rate / 1000;
+    }
+
+    if (flags & PCM_MMAP)
+        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+                       SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+    else
+        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+                       SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
+        oops(pcm, errno, "cannot set hw params");
         goto fail_close;
+    }
+
+    /* get our refined hw_params */
+    config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+    config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
+    pcm->buffer_size = config->period_count * config->period_size;
+
+    if (flags & PCM_MMAP) {
+        pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
+                                PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
+        if (pcm->mmap_buffer == MAP_FAILED) {
+            oops(pcm, errno, "failed to mmap buffer %d bytes\n",
+                 pcm_frames_to_bytes(pcm, pcm->buffer_size));
+            goto fail_close;
+        }
+    }
+
+    memset(&sparams, 0, sizeof(sparams));
+    sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
+    sparams.period_step = 1;
+
+    if (!config->start_threshold) {
+        if (pcm->flags & PCM_IN)
+            pcm->config.start_threshold = sparams.start_threshold = 1;
+        else
+            pcm->config.start_threshold = sparams.start_threshold =
+                config->period_count * config->period_size / 2;
+    } else
+        sparams.start_threshold = config->start_threshold;
+
+    /* pick a high stop threshold - todo: does this need further tuning */
+    if (!config->stop_threshold) {
+        if (pcm->flags & PCM_IN)
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size * 10;
+        else
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size;
+    }
+    else
+        sparams.stop_threshold = config->stop_threshold;
+
+    if (!pcm->config.avail_min) {
+        if (pcm->flags & PCM_MMAP)
+            pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
+        else
+            pcm->config.avail_min = sparams.avail_min = 1;
+    } else
+        sparams.avail_min = config->avail_min;
+
+    sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
+    sparams.silence_threshold = config->silence_threshold;
+    sparams.silence_size = config->silence_size;
+    pcm->boundary = sparams.boundary = pcm->buffer_size;
+
+    while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
+        pcm->boundary *= 2;
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
+        oops(pcm, errno, "cannot set sw params");
+        goto fail;
+    }
 
     rc = pcm_hw_mmap_status(pcm);
     if (rc < 0) {
@@ -1089,7 +1020,7 @@
 #ifdef SNDRV_PCM_IOCTL_TTSTAMP
     if (pcm->flags & PCM_MONOTONIC) {
         int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
-        rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
+        rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
         if (rc < 0) {
             oops(pcm, errno, "cannot set timestamp type");
             goto fail;
@@ -1097,120 +1028,58 @@
     }
 #endif
 
-    /* prepare here so the user does not need to do this later */
-    if (pcm_prepare(pcm))
-        goto fail;
-
-    pcm->xruns = 0;
+    pcm->underruns = 0;
     return pcm;
 
 fail:
-    pcm_hw_munmap_status(pcm);
     if (flags & PCM_MMAP)
-        pcm->ops->munmap(pcm->data, pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+        munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
 fail_close:
-    pcm->ops->close(pcm->data);
-fail_close_dev_node:
-#ifdef TINYALSA_USES_PLUGINS
-    if (pcm->snd_node)
-        snd_utils_close_dev_node(pcm->snd_node);
-#endif
-    free(pcm);
-    return &bad_pcm;
+    close(pcm->fd);
+    pcm->fd = -1;
+    return pcm;
 }
 
-/** Checks if a PCM file has been opened without error.
- * @param pcm A PCM handle.
- *  May be NULL.
- * @return If a PCM's file descriptor is not valid or the pointer is NULL, it returns zero.
- *  Otherwise, the function returns one.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_is_ready(const struct pcm *pcm)
+int pcm_is_ready(struct pcm *pcm)
 {
-    if (pcm != NULL) {
-        return pcm->fd >= 0;
-    }
-    return 0;
+    return pcm->fd >= 0;
 }
 
-/** Links two PCMs.
- * After this function is called, the two PCMs will prepare, start and stop in sync (at the same time).
- * If an error occurs, the error message will be written to @p pcm1.
- * @param pcm1 A PCM handle.
- * @param pcm2 Another PCM handle.
- * @return On success, zero; on failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_link(struct pcm *pcm1, struct pcm *pcm2)
-{
-    int err = ioctl(pcm1->fd, SNDRV_PCM_IOCTL_LINK, pcm2->fd);
-    if (err == -1) {
-        return oops(pcm1, errno, "cannot link PCM");
-    }
-    return 0;
-}
-
-/** Unlinks a PCM.
- * @see @ref pcm_link
- * @param pcm A PCM handle.
- * @return On success, zero; on failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_unlink(struct pcm *pcm)
-{
-    int err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_UNLINK);
-    if (err == -1) {
-        return oops(pcm, errno, "cannot unlink PCM");
-    }
-    return 0;
-}
-
-/** Prepares a PCM, if it has not been prepared already.
- * @param pcm A PCM handle.
- * @return On success, zero; on failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
 int pcm_prepare(struct pcm *pcm)
 {
-    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_PREPARE) < 0)
+    if (pcm->prepared)
+        return 0;
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
         return oops(pcm, errno, "cannot prepare channel");
 
-    /* get appl_ptr and avail_min from kernel */
-    pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
-
+    pcm->prepared = 1;
     return 0;
 }
 
-/** Starts a PCM.
- * @param pcm A PCM handle.
- * @return On success, zero; on failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
 int pcm_start(struct pcm *pcm)
 {
-    /* set appl_ptr and avail_min in kernel */
-    if (pcm_sync_ptr(pcm, 0) < 0)
-        return -1;
+    int prepare_error = pcm_prepare(pcm);
+    if (prepare_error)
+        return prepare_error;
 
-    if (pcm->mmap_status->state != PCM_STATE_RUNNING) {
-        if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START) < 0)
-            return oops(pcm, errno, "cannot start channel");
-    }
+    if (pcm->flags & PCM_MMAP)
+	    pcm_sync_ptr(pcm, 0);
 
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
+        return oops(pcm, errno, "cannot start channel");
+
+    pcm->running = 1;
     return 0;
 }
 
-/** Stops a PCM.
- * @param pcm A PCM handle.
- * @return On success, zero; on failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
 int pcm_stop(struct pcm *pcm)
 {
-    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_DROP) < 0)
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
         return oops(pcm, errno, "cannot stop channel");
 
+    pcm->prepared = 0;
+    pcm->running = 0;
     return 0;
 }
 
@@ -1222,7 +1091,7 @@
 
     if (avail < 0)
         avail += pcm->boundary;
-    else if (avail >= (int)pcm->boundary)
+    else if (avail > (int)pcm->boundary)
         avail -= pcm->boundary;
 
     return avail;
@@ -1238,6 +1107,7 @@
 
 int pcm_mmap_avail(struct pcm *pcm)
 {
+    pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
     if (pcm->flags & PCM_IN)
         return pcm_mmap_capture_avail(pcm);
     else
@@ -1282,125 +1152,21 @@
     return 0;
 }
 
-static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
-                          char *buf, unsigned int src_offset,
-                          unsigned int frames)
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset __attribute__((unused)), unsigned int frames)
 {
-    int size_bytes = pcm_frames_to_bytes(pcm, frames);
-    int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
-    int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
-
-    /* interleaved only atm */
-    if (pcm->flags & PCM_IN)
-        memcpy(buf + src_offset_bytes,
-               (char*)pcm->mmap_buffer + pcm_offset_bytes,
-               size_bytes);
-    else
-        memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
-               buf + src_offset_bytes,
-               size_bytes);
-    return 0;
-}
-
-int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
-{
-    int ret;
-
-    /* not used */
-    (void) offset;
-
     /* update the application pointer in userspace and kernel */
     pcm_mmap_appl_forward(pcm, frames);
-    ret = pcm_sync_ptr(pcm, 0);
-    if (ret != 0){
-        printf("%d\n", ret);
-        return ret;
-    }
+    pcm_sync_ptr(pcm, 0);
 
     return frames;
 }
 
-static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
-                                unsigned int offset, unsigned int size)
-{
-    void *pcm_areas;
-    int commit;
-    unsigned int pcm_offset, frames, count = 0;
-
-    while (pcm_mmap_avail(pcm) && size) {
-        frames = size;
-        pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
-        pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
-        commit = pcm_mmap_commit(pcm, pcm_offset, frames);
-        if (commit < 0) {
-            oops(pcm, commit, "failed to commit %d frames\n", frames);
-            return commit;
-        }
-
-        offset += commit;
-        count += commit;
-        size -= commit;
-    }
-    return count;
-}
-
-int pcm_get_poll_fd(struct pcm *pcm)
-{
-    return pcm->fd;
-}
-
 int pcm_avail_update(struct pcm *pcm)
 {
-    pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
+    pcm_sync_ptr(pcm, 0);
     return pcm_mmap_avail(pcm);
 }
 
-/** Returns available frames in pcm buffer and corresponding time stamp.
- * The clock is CLOCK_MONOTONIC if flag @ref PCM_MONOTONIC was specified in @ref pcm_open,
- * otherwise the clock is CLOCK_REALTIME.
- * For an input stream, frames available are frames ready for the application to read.
- * For an output stream, frames available are the number of empty frames available for the application to write.
- * @param pcm A PCM handle.
- * @param avail The number of available frames
- * @param tstamp The timestamp
- * @return On success, zero is returned; on failure, negative one.
- */
-int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
-                       struct timespec *tstamp)
-{
-    int checking;
-    int tmp;
-
-    if (!pcm_is_ready(pcm))
-        return -1;
-
-    checking = 0;
-
-again:
-
-    tmp = pcm_avail_update(pcm);
-    if (tmp < 0)
-        return tmp; /* error */
-
-    if (checking && (unsigned int) tmp == *avail)
-        return 0;
-
-    *avail = (unsigned int) tmp;
-    *tstamp = pcm->mmap_status->tstamp;
-
-    /*
-     * When status is mmaped, get avail again to ensure
-     * valid timestamp.
-     */
-    if (!pcm->sync_ptr) {
-        checking = 1;
-        goto again;
-    }
-
-    /* SYNC_PTR ioctl was used, no need to check avail */
-    return 0;
-}
-
 int pcm_state(struct pcm *pcm)
 {
     int err = pcm_sync_ptr(pcm, 0);
@@ -1410,25 +1176,26 @@
     return pcm->mmap_status->state;
 }
 
-/** Waits for frames to be available for read or write operations.
- * @param pcm A PCM handle.
- * @param timeout The maximum amount of time to wait for, in terms of milliseconds.
- * @returns If frames became available, one is returned.
- *  If a timeout occured, zero is returned.
- *  If an error occured, a negative number is returned.
- * @ingroup libtinyalsa-pcm
- */
+int pcm_set_avail_min(struct pcm *pcm, int avail_min)
+{
+    if ((~pcm->flags) & (PCM_MMAP | PCM_NOIRQ))
+        return -ENOSYS;
+
+    pcm->config.avail_min = avail_min;
+    return 0;
+}
+
 int pcm_wait(struct pcm *pcm, int timeout)
 {
     struct pollfd pfd;
     int err;
 
     pfd.fd = pcm->fd;
-    pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+    pfd.events = POLLOUT | POLLERR | POLLNVAL;
 
     do {
         /* let's wait for avail or timeout */
-        err = pcm->ops->poll(pcm->data, &pfd, 1, timeout);
+        err = poll(&pfd, 1, timeout);
         if (err < 0)
             return -errno;
 
@@ -1459,88 +1226,97 @@
     return 1;
 }
 
-/*
- * Transfer data to/from mmaped buffer. This imitates the
- * behavior of read/write system calls.
- *
- * However, this doesn't seems to offer any advantage over
- * the read/write syscalls. Should it be removed?
- */
-int pcm_mmap_transfer(struct pcm *pcm, void *buffer, unsigned int frames)
+int pcm_get_poll_fd(struct pcm *pcm)
 {
-    int is_playback;
+    return pcm->fd;
+}
 
-    int state;
-    unsigned int avail;
-    unsigned int user_offset;
+int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
+{
+    int err = 0, frames, avail;
+    unsigned int offset = 0, count;
 
-    int err;
-    int tmp;
-
-    is_playback = !(pcm->flags & PCM_IN);
-
-    if (frames == 0)
+    if (bytes == 0)
         return 0;
 
-    /* update hardware pointer and get state */
-    err = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC |
-                            SNDRV_PCM_SYNC_PTR_APPL |
-                            SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
-    if (err == -1)
-        return -1;
-    state = pcm->mmap_status->state;
+    count = pcm_bytes_to_frames(pcm, bytes);
 
-    /*
-     * If frames < start_threshold, wait indefinitely.
-     * Another thread may start capture
-     */
-    if (!is_playback && state == PCM_STATE_PREPARED &&
-        frames >= pcm->config.start_threshold) {
-            err = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START);
-        if (err == -1)
-            return -1;
-        /* state = PCM_STATE_RUNNING */
-    }
+    while (count > 0) {
 
-    avail = pcm_mmap_avail(pcm);
-    user_offset = 0;
-
-    while (frames) {
-        if (!avail) {
-            if (pcm->flags & PCM_NONBLOCK) {
-                errno = EAGAIN;
-                break;
-            }
-
-            /* wait for interrupt */
-            err = pcm_wait(pcm, -1);
-            if (err < 0) {
-                errno = -err;
-                break;
-            }
-
-            /* get hardware pointer */
-            avail = pcm_avail_update(pcm);
+        /* get the available space for writing new frames */
+        avail = pcm_avail_update(pcm);
+        if (avail < 0) {
+            fprintf(stderr, "cannot determine available mmap frames");
+            return err;
         }
 
-        tmp = pcm_mmap_transfer_areas(pcm, buffer, user_offset, frames);
-        if (tmp < 0)
+        /* start the audio if we reach the threshold */
+	    if (!pcm->running &&
+            (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
+            if (pcm_start(pcm) < 0) {
+               fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
+                    (unsigned int)pcm->mmap_status->hw_ptr,
+                    (unsigned int)pcm->mmap_control->appl_ptr,
+                    avail);
+                return -errno;
+            }
+            pcm->wait_for_avail_min = 0;
+        }
+
+        /* sleep until we have space to write new frames */
+        if (pcm->running) {
+            /* enable waiting for avail_min threshold when less frames than we have to write
+             * are available. */
+            if (!pcm->wait_for_avail_min && (count > (unsigned int)avail))
+                pcm->wait_for_avail_min = 1;
+
+            if (pcm->wait_for_avail_min && (avail < pcm->config.avail_min)) {
+                int time = -1;
+
+                /* disable waiting for avail_min threshold to allow small amounts of data to be
+                 * written without waiting as long as there is enough room in buffer. */
+                pcm->wait_for_avail_min = 0;
+
+                if (pcm->flags & PCM_NOIRQ)
+                    time = (pcm->config.avail_min - avail) / pcm->noirq_frames_per_msec;
+
+                err = pcm_wait(pcm, time);
+                if (err < 0) {
+                    pcm->prepared = 0;
+                    pcm->running = 0;
+                    oops(pcm, errno, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
+                        (unsigned int)pcm->mmap_status->hw_ptr,
+                        (unsigned int)pcm->mmap_control->appl_ptr,
+                        avail);
+                    pcm->mmap_control->appl_ptr = 0;
+                    return err;
+                }
+                continue;
+            }
+        }
+
+        frames = count;
+        if (frames > avail)
+            frames = avail;
+
+        if (!frames)
             break;
 
-        user_offset += tmp;
-        frames -= tmp;
-        avail -= tmp;
-
-        /* start playback if written >= start_threshold */
-        if (is_playback && state == PCM_STATE_PREPARED &&
-            pcm->buffer_size - avail >= pcm->config.start_threshold) {
-            err = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START);
-            if (err == -1)
-                break;
+        /* copy frames from buffer */
+        frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
+        if (frames < 0) {
+            fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
+                    (unsigned int)pcm->mmap_status->hw_ptr,
+                    (unsigned int)pcm->mmap_control->appl_ptr,
+                    avail);
+            return frames;
         }
+
+        offset += frames;
+        count -= frames;
     }
 
-    return user_offset ? (int) user_offset : -1;
+    return 0;
 }
 
 int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
@@ -1548,8 +1324,7 @@
     if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
         return -ENOSYS;
 
-    return pcm_mmap_transfer(pcm, (void *)data,
-                             pcm_bytes_to_frames(pcm, count));
+    return pcm_mmap_transfer(pcm, (void *)data, count);
 }
 
 int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
@@ -1557,196 +1332,20 @@
     if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
         return -ENOSYS;
 
-    return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count));
+    return pcm_mmap_transfer(pcm, data, count);
 }
 
-/* Returns current read/write position in the mmap buffer with associated time stamp. */
-int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp)
+int pcm_ioctl(struct pcm *pcm, int request, ...)
 {
-    int rc;
-
-    if (pcm == NULL || hw_ptr == NULL || tstamp == NULL)
-        return oops(pcm, EINVAL, "pcm %p, hw_ptr %p, tstamp %p", pcm, hw_ptr, tstamp);
+    va_list ap;
+    void * arg;
 
     if (!pcm_is_ready(pcm))
-        return oops(pcm, errno, "pcm_is_ready failed");
-
-    rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
-    if (rc < 0)
-        return oops(pcm, errno, "pcm_sync_ptr failed");
-
-    if (pcm->mmap_status == NULL)
-        return oops(pcm, EINVAL, "pcm %p, mmap_status is NULL", pcm);
-
-    if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
-            (pcm->mmap_status->state != PCM_STATE_DRAINING))
-        return oops(pcm, ENOSYS, "invalid stream state %d", pcm->mmap_status->state);
-
-    *tstamp = pcm->mmap_status->tstamp;
-    if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
-        return oops(pcm, errno, "invalid time stamp");
-
-    *hw_ptr = pcm->mmap_status->hw_ptr;
-
-    return 0;
-}
-
-static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames)
-{
-    int is_playback;
-
-    struct snd_xferi transfer;
-    int res;
-
-    is_playback = !(pcm->flags & PCM_IN);
-
-    transfer.buf = data;
-    transfer.frames = frames;
-    transfer.result = 0;
-
-    res = pcm->ops->ioctl(pcm->data, is_playback
-                          ? SNDRV_PCM_IOCTL_WRITEI_FRAMES
-                          : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer);
-
-    return res == 0 ? (int) transfer.result : -1;
-}
-
-static int pcm_generic_transfer(struct pcm *pcm, void *data,
-                                unsigned int frames)
-{
-    int res;
-
-#if UINT_MAX > TINYALSA_FRAMES_MAX
-    if (frames > TINYALSA_FRAMES_MAX)
-        return -EINVAL;
-#endif
-    if (frames > INT_MAX)
-        return -EINVAL;
-
-again:
-
-    if (pcm->flags & PCM_MMAP)
-        res = pcm_mmap_transfer(pcm, data, frames);
-    else
-        res = pcm_rw_transfer(pcm, data, frames);
-
-    if (res < 0) {
-        switch (errno) {
-        case EPIPE:
-            pcm->xruns++;
-            /* fallthrough */
-        case ESTRPIPE:
-            /*
-             * Try to restart if we are allowed to do so.
-             * Otherwise, return error.
-             */
-            if (pcm->flags & PCM_NORESTART || pcm_prepare(pcm))
-                return -1;
-            goto again;
-        case EAGAIN:
-            if (pcm->flags & PCM_NONBLOCK)
-                return -1;
-            /* fallthrough */
-        default:
-            return oops(pcm, errno, "cannot read/write stream data");
-        }
-    }
-
-    return res;
-}
-
-/** Writes audio samples to PCM.
- * If the PCM has not been started, it is started in this function.
- * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
- * @param pcm A PCM handle.
- * @param data The audio sample array
- * @param frame_count The number of frames occupied by the sample array.
- *  This value should not be greater than @ref TINYALSA_FRAMES_MAX
- *  or INT_MAX.
- * @return On success, this function returns the number of frames written; otherwise, a negative number.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count)
-{
-    if (pcm->flags & PCM_IN)
-        return -EINVAL;
-
-    return pcm_generic_transfer(pcm, (void*) data, frame_count);
-}
-
-/** Reads audio samples from PCM.
- * If the PCM has not been started, it is started in this function.
- * This function is only valid for PCMs opened with the @ref PCM_IN flag.
- * @param pcm A PCM handle.
- * @param data The audio sample array
- * @param frame_count The number of frames occupied by the sample array.
- *  This value should not be greater than @ref TINYALSA_FRAMES_MAX
- *  or INT_MAX.
- * @return On success, this function returns the number of frames written; otherwise, a negative number.
- * @ingroup libtinyalsa-pcm
- */
-int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count)
-{
-    if (!(pcm->flags & PCM_IN))
-        return -EINVAL;
-
-    return pcm_generic_transfer(pcm, data, frame_count);
-}
-
-/** Writes audio samples to PCM.
- * If the PCM has not been started, it is started in this function.
- * This function is only valid for PCMs opened with the @ref PCM_OUT flag.
- * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
- * @param pcm A PCM handle.
- * @param data The audio sample array
- * @param count The number of bytes occupied by the sample array.
- * @return On success, this function returns zero; otherwise, a negative number.
- * @deprecated
- * @ingroup libtinyalsa-pcm
- */
-int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
-{
-    unsigned int requested_frames = pcm_bytes_to_frames(pcm, count);
-    int ret = pcm_writei(pcm, data, requested_frames);
-
-    if (ret < 0)
-        return ret;
-
-    return ((unsigned int )ret == requested_frames) ? 0 : -EIO;
-}
-
-/** Reads audio samples from PCM.
- * If the PCM has not been started, it is started in this function.
- * This function is only valid for PCMs opened with the @ref PCM_IN flag.
- * This function is not valid for PCMs opened with the @ref PCM_MMAP flag.
- * @param pcm A PCM handle.
- * @param data The audio sample array
- * @param count The number of bytes occupied by the sample array.
- * @return On success, this function returns zero; otherwise, a negative number.
- * @deprecated
- * @ingroup libtinyalsa-pcm
- */
-int pcm_read(struct pcm *pcm, void *data, unsigned int count)
-{
-    unsigned int requested_frames = pcm_bytes_to_frames(pcm, count);
-    int ret = pcm_readi(pcm, data, requested_frames);
-
-    if (ret < 0)
-        return ret;
-
-    return ((unsigned int )ret == requested_frames) ? 0 : -EIO;
-}
-
-/** Gets the delay of the PCM, in terms of frames.
- * @param pcm A PCM handle.
- * @returns On success, the delay of the PCM.
- *  On failure, a negative number.
- * @ingroup libtinyalsa-pcm
- */
-long pcm_get_delay(struct pcm *pcm)
-{
-    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DELAY, &pcm->pcm_delay) < 0)
         return -1;
 
-    return pcm->pcm_delay;
+    va_start(ap, request);
+    arg = va_arg(ap, void *);
+    va_end(ap);
+
+    return ioctl(pcm->fd, request, arg);
 }
diff --git a/src/pcm_hw.c b/src/pcm_hw.c
deleted file mode 100644
index 38b2e83..0000000
--- a/src/pcm_hw.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/* pcm_hw.c
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#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 <sys/ioctl.h>
-#include <sys/mman.h>
-#include <linux/ioctl.h>
-#include <sound/asound.h>
-#include <tinyalsa/asoundlib.h>
-
-#include "pcm_io.h"
-
-struct pcm_hw_data {
-    /** Card number of the pcm device */
-    unsigned int card;
-    /** Device number for the pcm device */
-    unsigned int device;
-    /** File descriptor to the pcm device file node */
-    unsigned int fd;
-    /** Pointer to the pcm node from snd card definiton */
-    struct snd_node *node;
-};
-
-static void pcm_hw_close(void *data)
-{
-    struct pcm_hw_data *hw_data = data;
-
-    if (hw_data->fd > 0)
-        close(hw_data->fd);
-
-    free(hw_data);
-}
-
-static int pcm_hw_ioctl(void *data, unsigned int cmd, ...)
-{
-    struct pcm_hw_data *hw_data = data;
-    va_list ap;
-    void *arg;
-
-    va_start(ap, cmd);
-    arg = va_arg(ap, void *);
-    va_end(ap);
-
-    return ioctl(hw_data->fd, cmd, arg);
-}
-
-static int pcm_hw_poll(void *data __attribute__((unused)),
-                        struct pollfd *pfd, nfds_t nfds, int timeout)
-{
-    return poll(pfd, nfds, timeout);
-}
-
-static void *pcm_hw_mmap(void *data, void *addr, size_t length, int prot,
-                       int flags, off_t offset)
-{
-    struct pcm_hw_data *hw_data = data;
-
-    return mmap(addr, length, prot, flags, hw_data->fd, offset);
-}
-
-static int pcm_hw_munmap(void *data __attribute__((unused)), void *addr, size_t length)
-{
-    return munmap(addr, length);
-}
-
-static int pcm_hw_open(unsigned int card, unsigned int device,
-                unsigned int flags, void **data, struct snd_node *node)
-{
-    struct pcm_hw_data *hw_data;
-    char fn[256];
-    int fd;
-
-    hw_data = calloc(1, sizeof(*hw_data));
-    if (!hw_data) {
-        return -ENOMEM;
-    }
-
-    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
-             flags & PCM_IN ? 'c' : 'p');
-    if (flags & PCM_NONBLOCK)
-        fd = open(fn, O_RDWR|O_NONBLOCK);
-    else
-        fd = open(fn, O_RDWR);
-
-    if (fd < 0) {
-        free(hw_data);
-        return fd;
-    }
-
-    hw_data->card = card;
-    hw_data->device = device;
-    hw_data->fd = fd;
-    hw_data->node = node;
-
-    *data = hw_data;
-
-    return fd;
-}
-
-const struct pcm_ops hw_ops = {
-    .open = pcm_hw_open,
-    .close = pcm_hw_close,
-    .ioctl = pcm_hw_ioctl,
-    .mmap = pcm_hw_mmap,
-    .munmap = pcm_hw_munmap,
-    .poll = pcm_hw_poll,
-};
-
diff --git a/src/pcm_io.h b/src/pcm_io.h
deleted file mode 100644
index 3c622fd..0000000
--- a/src/pcm_io.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* pcm_io.h
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#ifndef TINYALSA_SRC_PCM_IO_H
-#define TINYALSA_SRC_PCM_IO_H
-
-#include <poll.h>
-#include <sound/asound.h>
-
-struct snd_node;
-
-struct pcm_ops {
-    int (*open) (unsigned int card, unsigned int device,
-                 unsigned int flags, void **data, struct snd_node *node);
-    void (*close) (void *data);
-    int (*ioctl) (void *data, unsigned int cmd, ...);
-    void *(*mmap) (void *data, void *addr, size_t length, int prot, int flags,
-                   off_t offset);
-    int (*munmap) (void *data, void *addr, size_t length);
-    int (*poll) (void *data, struct pollfd *pfd, nfds_t nfds, int timeout);
-};
-
-extern const struct pcm_ops hw_ops;
-extern const struct pcm_ops plug_ops;
-
-#endif /* TINYALSA_SRC_PCM_IO_H */
diff --git a/src/pcm_plugin.c b/src/pcm_plugin.c
deleted file mode 100644
index 15bfc80..0000000
--- a/src/pcm_plugin.c
+++ /dev/null
@@ -1,778 +0,0 @@
-/* pcm_plugin.c
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <poll.h>
-#include <dlfcn.h>
-
-#include <sys/ioctl.h>
-#include <linux/ioctl.h>
-#include <sound/asound.h>
-#include <tinyalsa/asoundlib.h>
-#include <tinyalsa/plugin.h>
-
-#include "pcm_io.h"
-#include "snd_card_plugin.h"
-
-/* 2 words of uint32_t = 64 bits of mask */
-#define PCM_MASK_SIZE (2)
-#define ARRAY_SIZE(a)         \
-    (sizeof(a) / sizeof(a[0]))
-
-#define PCM_PARAM_GET_MASK(p, n)    \
-    &p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK];
-
-enum {
-    PCM_PLUG_HW_PARAM_SELECT_MIN,
-    PCM_PLUG_HW_PARAM_SELECT_MAX,
-    PCM_PLUG_HW_PARAM_SELECT_VAL,
-};
-
-enum {
-    PCM_PLUG_STATE_OPEN,
-    PCM_PLUG_STATE_SETUP,
-    PCM_PLUG_STATE_PREPARED,
-    PCM_PLUG_STATE_RUNNING,
-};
-
-struct pcm_plug_data {
-    unsigned int card;
-    unsigned int device;
-    unsigned int fd;
-    unsigned int flags;
-
-    void *dl_hdl;
-    /** pointer to plugin operation */
-    const struct pcm_plugin_ops *ops;
-    struct pcm_plugin *plugin;
-    void *dev_node;
-};
-
-static unsigned int param_list[] = {
-    SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-    SNDRV_PCM_HW_PARAM_CHANNELS,
-    SNDRV_PCM_HW_PARAM_RATE,
-    SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-    SNDRV_PCM_HW_PARAM_PERIODS,
-};
-
-static int convert_plugin_to_pcm_state(int plugin_state)
-{
-    switch (plugin_state) {
-    case PCM_PLUG_STATE_SETUP:
-        return PCM_STATE_SETUP;
-    case PCM_PLUG_STATE_RUNNING:
-        return PCM_STATE_RUNNING;
-    case PCM_PLUG_STATE_PREPARED:
-        return PCM_STATE_PREPARED;
-    case PCM_PLUG_STATE_OPEN:
-        return PCM_STATE_OPEN;
-    default:
-        break;
-    }
-
-    return PCM_STATE_OPEN;
-}
-
-static void pcm_plug_close(void *data)
-{
-    struct pcm_plug_data *plug_data = data;
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    plug_data->ops->close(plugin);
-    dlclose(plug_data->dl_hdl);
-
-    free(plug_data);
-}
-
-static int pcm_plug_info(struct pcm_plug_data *plug_data,
-                struct snd_pcm_info *info)
-{
-    int stream = SNDRV_PCM_STREAM_PLAYBACK;
-    int ret = 0, val = -1;
-    char *name;
-
-    memset(info, 0, sizeof(*info));
-
-    if (plug_data->flags & PCM_IN) {
-        stream = SNDRV_PCM_STREAM_CAPTURE;
-        ret = snd_utils_get_int(plug_data->dev_node, "capture", &val);
-        if (ret || !val) {
-            fprintf(stderr, "%s: not a capture device\n", __func__);
-            return -EINVAL;
-        }
-    } else {
-        stream = SNDRV_PCM_STREAM_PLAYBACK;
-        ret = snd_utils_get_int(plug_data->dev_node, "playback", &val);
-        if (ret || !val) {
-            fprintf(stderr, "%s: not a playback device\n", __func__);
-            return -EINVAL;
-        }
-    }
-
-    info->stream = stream;
-    info->card = plug_data->card;
-    info->device = plug_data->device;
-
-    ret = snd_utils_get_str(plug_data->dev_node, "name", &name);
-    if (ret) {
-        fprintf(stderr, "%s: failed to get pcm device name\n", __func__);
-        return ret;
-    }
-
-    strncpy((char *)info->id, name, sizeof(info->id));
-    strncpy((char *)info->name, name, sizeof(info->name));
-    strncpy((char *)info->subname, name, sizeof(info->subname));
-
-    info->subdevices_count = 1;
-
-    return ret;
-}
-
-static void pcm_plug_set_mask(struct snd_pcm_hw_params *p, int n, uint64_t v)
-{
-    struct snd_mask *mask;
-
-    mask = PCM_PARAM_GET_MASK(p, n);
-
-    mask->bits[0] |= (v & 0xFFFFFFFF);
-    mask->bits[1] |= ((v >> 32) & 0xFFFFFFFF);
-    /*
-     * currently only supporting 64 bits, may need to update to support
-     * more than 64 bits
-     */
-}
-
-static void pcm_plug_set_interval(struct snd_pcm_hw_params *params,
-                    int p, struct pcm_plugin_min_max *v, int is_integer)
-{
-    struct snd_interval *i;
-
-    i = &params->intervals[p - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
-
-    i->min = v->min;
-    i->max = v->max;
-
-    if (is_integer)
-        i->integer = 1;
-}
-
-static int pcm_plug_frames_to_bytes(unsigned int frames,
-                                    unsigned int frame_bits)
-{
-    return (frames * (frame_bits / 8));
-}
-
-static int pcm_plug_bytes_to_frames(unsigned int size,
-                                    unsigned int frame_bits)
-{
-    return (size * 8)  / frame_bits;
-}
-
-static int pcm_plug_get_params(struct pcm_plugin *plugin,
-                struct snd_pcm_hw_params *params)
-{
-    struct pcm_plugin_min_max bw, ch, pb, periods;
-    struct pcm_plugin_min_max val;
-    struct pcm_plugin_min_max frame_bits, buffer_bytes;
-
-    /*
-     * populate the struct snd_pcm_hw_params structure
-     * using the hw_param constraints provided by plugin
-     * via the plugin->constraints
-     */
-
-    /* Set the mask params */
-    pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
-                      plugin->constraints->access);
-    pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
-                      plugin->constraints->format);
-    pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
-                      SNDRV_PCM_SUBFORMAT_STD);
-
-    /* Set the standard interval params */
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                          &plugin->constraints->bit_width, 1);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
-                          &plugin->constraints->channels, 1);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_RATE,
-                          &plugin->constraints->rate, 1);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                          &plugin->constraints->period_bytes, 0);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIODS,
-                          &plugin->constraints->periods, 1);
-
-    /* set the calculated interval params */
-
-    bw.min = plugin->constraints->bit_width.min;
-    bw.max = plugin->constraints->bit_width.max;
-
-    ch.min = plugin->constraints->channels.min;
-    ch.max = plugin->constraints->channels.max;
-
-    pb.min = plugin->constraints->period_bytes.min;
-    pb.max = plugin->constraints->period_bytes.max;
-
-    periods.min = plugin->constraints->periods.min;
-    periods.max = plugin->constraints->periods.max;
-
-    /* Calculate and set frame bits */
-    frame_bits.min = bw.min * ch.min;
-    frame_bits.max = bw.max * ch.max;
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
-                          &frame_bits, 1);
-
-
-    /* Calculate and set period_size in frames */
-    val.min = pcm_plug_bytes_to_frames(pb.min, frame_bits.min);
-    val.max = pcm_plug_bytes_to_frames(pb.max, frame_bits.min);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                          &val, 1);
-
-    /* Calculate and set buffer_bytes */
-    buffer_bytes.min = pb.min * periods.min;
-    buffer_bytes.max = pb.max * periods.max;
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                          &buffer_bytes, 1);
-
-    /* Calculate and set buffer_size in frames */
-    val.min = pcm_plug_bytes_to_frames(buffer_bytes.min, frame_bits.min);
-    val.max = pcm_plug_bytes_to_frames(buffer_bytes.max, frame_bits.min);
-    pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-                          &val, 1);
-    return 0;
-}
-
-static int pcm_plug_masks_refine(struct snd_pcm_hw_params *p,
-                struct snd_pcm_hw_params *c)
-{
-    struct snd_mask *req_mask;
-    struct snd_mask *con_mask;
-    unsigned int idx, i, masks;
-
-    masks = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK;
-
-    for (idx = 0; idx <= masks; idx++) {
-
-        if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK))))
-            continue;
-
-        req_mask = PCM_PARAM_GET_MASK(p, idx);
-        con_mask = PCM_PARAM_GET_MASK(c, idx);
-
-        /*
-         * set the changed mask if requested mask value is not the same as
-         * constrained mask value
-         */
-        if (memcmp(req_mask, con_mask, PCM_MASK_SIZE * sizeof(uint32_t)))
-            p->cmask |= 1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK);
-
-        /* Actually change the requested mask to constrained mask */
-        for (i = 0; i < PCM_MASK_SIZE; i++)
-            req_mask->bits[i] &= con_mask->bits[i];
-    }
-
-    return 0;
-}
-
-static int pcm_plug_interval_refine(struct snd_pcm_hw_params *p,
-                struct snd_pcm_hw_params *c)
-{
-    struct snd_interval *ri;
-    struct snd_interval *ci;
-    unsigned int idx;
-    unsigned int intervals;
-    int changed = 0;
-
-    intervals = SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
-                SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
-
-    for (idx = 0; idx <= intervals; idx++) {
-        ri = &p->intervals[idx];
-        ci = &c->intervals[idx];
-
-        if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL)) ))
-            continue;
-
-        if (ri->min < ci->min) {
-            ri->min = ci->min;
-            ri->openmin = ci->openmin;
-            changed = 1;
-        } else if (ri->min == ci->min && !ri->openmin && ci->openmin) {
-            ri->openmin = 1;
-            changed = 1;
-        }
-
-        if (ri->max > ci->max) {
-            ri->max = ci->max;
-            ri->openmax = ci->openmax;
-            changed = 1;
-        } else if (ri->max == ci->max && !ri->openmax && ci->openmax) {
-            ri->openmax = 1;
-            changed = 1;
-        };
-
-        if (!ri->integer && ci->integer) {
-            ri->integer = 1;
-            changed = 1;
-        }
-
-        if (ri->integer) {
-            if (ri->openmin) {
-                ri->min++;
-                ri->openmin = 0;
-            }
-            if (ri->openmax) {
-                ri->max--;
-                ri->openmax = 0;
-            }
-        } else if (!ri->openmin && !ri->openmax && ri->min == ri->max) {
-            ri->integer = 1;
-        }
-
-        /* Set the changed mask */
-        if (changed)
-            p->cmask |= (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL));
-    }
-
-    return 0;
-}
-
-
-static int pcm_plug_hw_params_refine(struct snd_pcm_hw_params *p,
-                struct snd_pcm_hw_params *c)
-{
-    int rc;
-
-    rc = pcm_plug_masks_refine(p, c);
-    if (rc) {
-        fprintf(stderr, "%s: masks refine failed %d\n", __func__, rc);
-        return rc;
-    }
-
-    rc = pcm_plug_interval_refine(p, c);
-    if (rc) {
-        fprintf(stderr, "%s: interval refine failed %d\n", __func__, rc);
-        return rc;
-    }
-
-    /* clear the requested params */
-    p->rmask = 0;
-
-    return rc;
-}
-
-static int __pcm_plug_hrefine(struct pcm_plug_data *plug_data,
-                struct snd_pcm_hw_params *params)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    struct snd_pcm_hw_params plug_params;
-    int rc;
-
-    memset(&plug_params, 0, sizeof(plug_params));
-    rc = pcm_plug_get_params(plugin, &plug_params);
-    if (rc) {
-        fprintf(stderr, "%s: pcm_plug_get_params failed %d\n",
-               __func__, rc);
-        return -EINVAL;
-    }
-
-    return pcm_plug_hw_params_refine(params, &plug_params);
-
-}
-
-static int pcm_plug_hrefine(struct pcm_plug_data *plug_data,
-                struct snd_pcm_hw_params *params)
-{
-    return __pcm_plug_hrefine(plug_data, params);
-}
-
-static int pcm_plug_interval_select(struct snd_pcm_hw_params *p,
-        unsigned int param, unsigned int select, unsigned int val)
-{
-    struct snd_interval *i;
-
-    if (param < SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ||
-        param > SNDRV_PCM_HW_PARAM_LAST_INTERVAL)
-        return -EINVAL;
-
-    i = &p->intervals[param - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
-
-    if (!i->min)
-        return -EINVAL;
-
-    switch (select) {
-
-    case PCM_PLUG_HW_PARAM_SELECT_MIN:
-        i->max = i->min;
-        break;
-
-    case PCM_PLUG_HW_PARAM_SELECT_MAX:
-        i->min = i->max;
-        break;
-
-    case PCM_PLUG_HW_PARAM_SELECT_VAL:
-        i->min = i->max = val;
-        break;
-
-    default:
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static void pcm_plug_hw_params_set(struct snd_pcm_hw_params *p)
-{
-    unsigned int i, select;
-    unsigned int bw, ch, period_sz, periods;
-    unsigned int val1, val2, offset;
-
-    offset = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
-
-    /* Select the min values first */
-    select = PCM_PLUG_HW_PARAM_SELECT_MIN;
-    for (i = 0; i < ARRAY_SIZE(param_list); i++)
-        pcm_plug_interval_select(p, param_list[i], select, 0);
-
-    /* Select calculated values */
-    select = PCM_PLUG_HW_PARAM_SELECT_VAL;
-    bw = (p->intervals[SNDRV_PCM_HW_PARAM_SAMPLE_BITS - offset]).min;
-    ch = (p->intervals[SNDRV_PCM_HW_PARAM_CHANNELS - offset]).min;
-    period_sz = (p->intervals[SNDRV_PCM_HW_PARAM_PERIOD_SIZE - offset]).min;
-    periods = (p->intervals[SNDRV_PCM_HW_PARAM_PERIODS - offset]).min;
-
-    val1 = bw * ch;        // frame_bits;
-    pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_FRAME_BITS, select, val1);
-
-    val2 = pcm_plug_frames_to_bytes(period_sz, val1); // period_bytes;
-    pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, select,
-                             val2);
-
-    val2 = period_sz * periods; //buffer_size;
-    pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, select, val2);
-
-    val2 = pcm_plug_frames_to_bytes(period_sz * periods, val1); //buffer_bytes;
-    pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, select, val2);
-}
-
-static int pcm_plug_hparams(struct pcm_plug_data *plug_data,
-                struct snd_pcm_hw_params *params)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int rc;
-
-    if (plugin->state != PCM_PLUG_STATE_OPEN)
-            return -EBADFD;
-
-    params->rmask = ~0U;
-
-    rc = __pcm_plug_hrefine(plug_data, params);
-    if (rc) {
-        fprintf(stderr, "%s: __pcm_plug_hrefine failed %d\n",
-               __func__, rc);
-        return rc;
-    }
-
-    pcm_plug_hw_params_set(params);
-
-    rc = plug_data->ops->hw_params(plugin, params);
-    if (!rc)
-        plugin->state = PCM_PLUG_STATE_SETUP;
-
-    return rc;
-}
-
-static int pcm_plug_sparams(struct pcm_plug_data *plug_data,
-                struct snd_pcm_sw_params *params)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state != PCM_PLUG_STATE_SETUP)
-        return -EBADFD;
-
-    return plug_data->ops->sw_params(plugin, params);
-}
-
-static int pcm_plug_sync_ptr(struct pcm_plug_data *plug_data,
-                struct snd_pcm_sync_ptr *sync_ptr)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int ret = -EBADFD;
-
-    if (plugin->state >= PCM_PLUG_STATE_SETUP) {
-        ret = plug_data->ops->sync_ptr(plugin, sync_ptr);
-        if (ret == 0)
-            sync_ptr->s.status.state = convert_plugin_to_pcm_state(plugin->state);
-    }
-
-    return ret;
-}
-
-static int pcm_plug_writei_frames(struct pcm_plug_data *plug_data,
-                struct snd_xferi *x)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state != PCM_PLUG_STATE_PREPARED &&
-        plugin->state != PCM_PLUG_STATE_RUNNING)
-        return -EBADFD;
-
-    return plug_data->ops->writei_frames(plugin, x);
-}
-
-static int pcm_plug_readi_frames(struct pcm_plug_data *plug_data,
-                struct snd_xferi *x)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state != PCM_PLUG_STATE_RUNNING)
-        return -EBADFD;
-
-    return plug_data->ops->readi_frames(plugin, x);
-}
-
-static int pcm_plug_ttstamp(struct pcm_plug_data *plug_data,
-                int *tstamp)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state < PCM_PLUG_STATE_SETUP)
-        return -EBADFD;
-
-    return plug_data->ops->ttstamp(plugin, tstamp);
-}
-
-static int pcm_plug_prepare(struct pcm_plug_data *plug_data)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int rc;
-
-    if (plugin->state != PCM_PLUG_STATE_SETUP)
-        return -EBADFD;
-
-    rc = plug_data->ops->prepare(plugin);
-    if (!rc)
-        plugin->state = PCM_PLUG_STATE_PREPARED;
-
-    return rc;
-}
-
-static int pcm_plug_start(struct pcm_plug_data *plug_data)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int rc;
-
-    if (plugin->state != PCM_PLUG_STATE_PREPARED)
-        return -EBADFD;
-
-    rc = plug_data->ops->start(plugin);
-    if (!rc)
-        plugin->state = PCM_PLUG_STATE_RUNNING;
-
-    return rc;
-}
-
-static int pcm_plug_drop(struct pcm_plug_data *plug_data)
-{
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int rc;
-
-    rc = plug_data->ops->drop(plugin);
-    if (!rc)
-        plugin->state = PCM_PLUG_STATE_SETUP;
-
-    return rc;
-}
-
-static int pcm_plug_ioctl(void *data, unsigned int cmd, ...)
-{
-    struct pcm_plug_data *plug_data = data;
-    struct pcm_plugin *plugin = plug_data->plugin;
-    int ret;
-    va_list ap;
-    void *arg;
-
-    va_start(ap, cmd);
-    arg = va_arg(ap, void *);
-    va_end(ap);
-
-    switch (cmd) {
-    case SNDRV_PCM_IOCTL_INFO:
-        ret = pcm_plug_info(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_TTSTAMP:
-        ret = pcm_plug_ttstamp(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_HW_REFINE:
-        ret = pcm_plug_hrefine(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_HW_PARAMS:
-        ret = pcm_plug_hparams(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_SW_PARAMS:
-        ret = pcm_plug_sparams(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_SYNC_PTR:
-        ret = pcm_plug_sync_ptr(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_PREPARE:
-        ret = pcm_plug_prepare(plug_data);
-        break;
-    case SNDRV_PCM_IOCTL_START:
-        ret = pcm_plug_start(plug_data);
-        break;
-    case SNDRV_PCM_IOCTL_DROP:
-        ret = pcm_plug_drop(plug_data);
-        break;
-    case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
-        ret = pcm_plug_writei_frames(plug_data, arg);
-        break;
-    case SNDRV_PCM_IOCTL_READI_FRAMES:
-        ret = pcm_plug_readi_frames(plug_data, arg);
-        break;
-    default:
-        ret = plug_data->ops->ioctl(plugin, cmd, arg);
-        break;
-    }
-
-    return ret;
-}
-
-static int pcm_plug_poll(void *data, struct pollfd *pfd, nfds_t nfds,
-        int timeout)
-{
-    struct pcm_plug_data *plug_data = data;
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    return plug_data->ops->poll(plugin, pfd, nfds, timeout);
-}
-
-static void *pcm_plug_mmap(void *data, void *addr, size_t length, int prot,
-                       int flags, off_t offset)
-{
-    struct pcm_plug_data *plug_data = data;
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state != PCM_PLUG_STATE_SETUP)
-        return NULL;
-
-    return plug_data->ops->mmap(plugin, addr, length, prot, flags, offset);
-}
-
-static int pcm_plug_munmap(void *data, void *addr, size_t length)
-{
-    struct pcm_plug_data *plug_data = data;
-    struct pcm_plugin *plugin = plug_data->plugin;
-
-    if (plugin->state != PCM_PLUG_STATE_SETUP)
-        return -EBADFD;
-
-    return plug_data->ops->munmap(plugin, addr, length);
-}
-
-static int pcm_plug_open(unsigned int card, unsigned int device,
-                         unsigned int flags, void **data, struct snd_node *pcm_node)
-{
-    struct pcm_plug_data *plug_data;
-    void *dl_hdl;
-    int rc = 0;
-    char *so_name;
-
-    plug_data = calloc(1, sizeof(*plug_data));
-    if (!plug_data) {
-        return -ENOMEM;
-    }
-
-    rc = snd_utils_get_str(pcm_node, "so-name", &so_name);
-    if (rc) {
-        fprintf(stderr, "%s: failed to get plugin lib name\n", __func__);
-        goto err_get_lib;
-    }
-
-    dl_hdl = dlopen(so_name, RTLD_NOW);
-    if (!dl_hdl) {
-        fprintf(stderr, "%s: unable to open %s\n", __func__, so_name);
-        goto err_dl_open;
-    } else {
-        fprintf(stderr, "%s: dlopen successful for %s\n", __func__, so_name);
-    }
-
-    dlerror();
-
-    plug_data->ops = dlsym(dl_hdl, "pcm_plugin_ops");
-    if (!plug_data->ops) {
-        fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
-                __func__, dlerror());
-        goto err_dlsym;
-    }
-
-    rc = plug_data->ops->open(&plug_data->plugin, card, device, flags);
-    if (rc) {
-        fprintf(stderr, "%s: failed to open plugin\n", __func__);
-        goto err_open;
-    }
-
-    plug_data->dl_hdl = dl_hdl;
-    plug_data->card = card;
-    plug_data->device = device;
-    plug_data->dev_node = pcm_node;
-    plug_data->flags = flags;
-
-    *data = plug_data;
-
-    plug_data->plugin->state = PCM_PLUG_STATE_OPEN;
-
-    return 0;
-
-err_open:
-err_dlsym:
-    dlclose(dl_hdl);
-err_get_lib:
-err_dl_open:
-    free(plug_data);
-
-    return rc;
-}
-
-const struct pcm_ops plug_ops = {
-    .open = pcm_plug_open,
-    .close = pcm_plug_close,
-    .ioctl = pcm_plug_ioctl,
-    .mmap = pcm_plug_mmap,
-    .munmap = pcm_plug_munmap,
-    .poll = pcm_plug_poll,
-};
diff --git a/src/snd_card_plugin.c b/src/snd_card_plugin.c
deleted file mode 100644
index c6d4baf..0000000
--- a/src/snd_card_plugin.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/* snd_card_plugin.c
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#include "snd_card_plugin.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#define SND_DLSYM(h, p, s, err) \
-do {                            \
-    err = 0;                    \
-    p = dlsym(h, s);            \
-    if (!p)                        \
-        err = -ENODEV;            \
-} while(0)
-
-int snd_utils_get_int(struct snd_node *node, const char *prop, int *val)
-{
-    if (!node || !node->card_node || !node->dev_node)
-        return SND_NODE_TYPE_HW;
-
-    return node->ops->get_int(node->dev_node, prop, val);
-}
-
-int snd_utils_get_str(struct snd_node *node, const char *prop, char **val)
-{
-    if (!node || !node->card_node || !node->dev_node)
-        return SND_NODE_TYPE_HW;
-
-    return node->ops->get_str(node->dev_node, prop, val);
-}
-
-void snd_utils_close_dev_node(struct snd_node *node)
-{
-    if (!node)
-        return;
-
-    if (node->card_node)
-        node->ops->close_card(node->card_node);
-
-    if (node->dl_hdl)
-        dlclose(node->dl_hdl);
-
-    free(node);
-}
-
-enum snd_node_type snd_utils_get_node_type(struct snd_node *node)
-{
-    int val = SND_NODE_TYPE_HW;
-
-    if (!node || !node->card_node || !node->dev_node)
-        return SND_NODE_TYPE_HW;
-
-    node->ops->get_int(node->dev_node, "type", &val);
-
-    return val;
-}
-
-static int snd_utils_resolve_symbols(struct snd_node *node)
-{
-    void *dl = node->dl_hdl;
-    int err;
-    SND_DLSYM(dl, node->ops, "snd_card_ops", err);
-    return err;
-}
-
-static struct snd_node *snd_utils_open_dev_node(unsigned int card,
-                                                unsigned int device,
-                                                int dev_type)
-{
-    struct snd_node *node;
-    int rc = 0;
-
-    node = calloc(1, sizeof(*node));
-    if (!node)
-        return NULL;
-
-    node->dl_hdl = dlopen("libsndcardparser.so", RTLD_NOW);
-    if (!node->dl_hdl) {
-        goto err_dl_open;
-    }
-
-    rc = snd_utils_resolve_symbols(node);
-    if (rc < 0)
-        goto err_resolve_symbols;
-
-    node->card_node = node->ops->open_card(card);
-    if (!node->card_node)
-        goto err_resolve_symbols;
-
-    if (dev_type == NODE_PCM) {
-      node->dev_node = node->ops->get_pcm(node->card_node, device);
-    } else {
-      node->dev_node = node->ops->get_mixer(node->card_node);
-    }
-
-    if (!node->dev_node)
-        goto err_get_node;
-
-    return node;
-
-err_get_node:
-    node->ops->close_card(node->card_node);
-
-err_resolve_symbols:
-    dlclose(node->dl_hdl);
-
-err_dl_open:
-    free(node);
-    return NULL;
-}
-
-struct snd_node* snd_utils_open_pcm(unsigned int card,
-                                    unsigned int device)
-{
-  return snd_utils_open_dev_node(card, device, NODE_PCM);
-}
-
-struct snd_node* snd_utils_open_mixer(unsigned int card)
-{
-  return snd_utils_open_dev_node(card, 0, NODE_MIXER);
-}
diff --git a/src/snd_card_plugin.h b/src/snd_card_plugin.h
deleted file mode 100644
index b80695e..0000000
--- a/src/snd_card_plugin.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* snd_utils.h
-** Copyright (c) 2019, The Linux Foundation.
-**
-** Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-**   * Redistributions of source code must retain the above copyright
-**     notice, this list of conditions and the following disclaimer.
-**   * Redistributions in binary form must reproduce the above
-**     copyright notice, this list of conditions and the following
-**     disclaimer in the documentation and/or other materials provided
-**     with the distribution.
-**   * Neither the name of The Linux Foundation nor the names of its
-**     contributors may be used to endorse or promote products derived
-**     from this software without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**/
-
-#ifndef TINYALSA_SRC_SND_CARD_UTILS_H
-#define TINYALSA_SRC_SND_CARD_UTILS_H
-
-#include <tinyalsa/plugin.h>
-
-#include <dlfcn.h>
-
-/** Encapsulates the pcm device definition from
- * the sound card definition configuration file.
- */
-struct snd_node {
-    /** Pointer the card definition */
-    void *card_node;
-    /** Pointer to device definition, either PCM or MIXER device */
-    void *dev_node;
-    /** Pointer to the sound card parser library */
-    void *dl_hdl;
-    /** A pointer to the operations structure. */
-    const struct snd_node_ops* ops;
-};
-
-enum snd_node_type {
-    SND_NODE_TYPE_HW = 0,
-    SND_NODE_TYPE_PLUGIN,
-    SND_NODE_TYPE_INVALID,
-};
-
-enum {
-  NODE_PCM,
-  NODE_MIXER
-};
-
-struct snd_node *snd_utils_open_pcm(unsigned int card, unsigned int device);
-
-struct snd_node *snd_utils_open_mixer(unsigned int card);
-
-void snd_utils_close_dev_node(struct snd_node *node);
-
-enum snd_node_type snd_utils_get_node_type(struct snd_node *node);
-
-int snd_utils_get_int(struct snd_node *node, const char *prop, int *val);
-
-int snd_utils_get_str(struct snd_node *node, const char *prop, char **val);
-
-#endif /* end of TINYALSA_SRC_SND_CARD_UTILS_H */