Merge pull request #90 from rfvirgil/control-update

mixer: add ability to update control list with new controls
This commit is contained in:
Taylor Holberton
2016-12-02 11:33:56 -08:00
committed by GitHub
2 changed files with 127 additions and 41 deletions
+2
View File
@@ -69,6 +69,8 @@ struct mixer *mixer_open(unsigned int card);
void mixer_close(struct mixer *mixer);
int mixer_add_new_ctls(struct mixer *mixer);
const char *mixer_get_name(const struct mixer *mixer);
unsigned int mixer_get_num_ctls(const struct mixer *mixer);
+125 -41
View File
@@ -73,13 +73,25 @@ struct mixer {
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);
}
}
/** 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;
unsigned int n;
if (!mixer)
return;
@@ -88,14 +100,8 @@ void mixer_close(struct mixer *mixer)
close(mixer->fd);
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);
}
}
for (n = 0; n < mixer->count; n++)
mixer_cleanup_control(&mixer->ctl[n]);
free(mixer->ctl);
}
@@ -104,6 +110,88 @@ void mixer_close(struct mixer *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 snd_ctl_elem_list elist;
struct snd_ctl_elem_id *eid = NULL;
struct mixer_ctl *ctl;
int fd = mixer->fd;
const unsigned int old_count = mixer->count;
unsigned int new_count;
unsigned int n;
memset(&elist, 0, sizeof(elist));
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(mixer->ctl, old_count, elist.count,
sizeof(struct mixer_ctl));
if (!ctl)
goto fail;
mixer->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 (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
goto fail;
for (n = old_count; n < new_count; n++) {
struct snd_ctl_elem_info *ei = &mixer->ctl[n].info;
ei->id.numid = eid[n - old_count].numid;
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
goto fail_extend;
ctl[n].mixer = mixer;
}
mixer->count = new_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]);
mixer->count = n; /* keep controls we successfully added */
/* fall through... */
fail:
free(eid);
return -1;
}
/** Opens a mixer for a given card.
* @param card The card to open the mixer for.
* @returns An initialized mixer handle.
@@ -111,10 +199,7 @@ void mixer_close(struct mixer *mixer)
*/
struct mixer *mixer_open(unsigned int card)
{
struct snd_ctl_elem_list elist;
struct snd_ctl_elem_id *eid = NULL;
struct mixer *mixer = NULL;
unsigned int n;
int fd;
char fn[256];
@@ -123,52 +208,51 @@ struct mixer *mixer_open(unsigned int card)
if (fd < 0)
return 0;
memset(&elist, 0, sizeof(elist));
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
goto fail;
mixer = calloc(1, sizeof(*mixer));
if (!mixer)
goto fail;
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
if (!mixer->ctl)
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)
if (add_controls(mixer) != 0)
goto fail;
for (n = 0; n < mixer->count; n++) {
struct snd_ctl_elem_info *ei = &mixer->ctl[n].info;
ei->id.numid = eid[n].numid;
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
goto fail;
mixer->ctl[n].mixer = mixer;
}
free(eid);
return mixer;
fail:
/* TODO: verify frees in failure case */
if (eid)
free(eid);
if (mixer)
mixer_close(mixer);
else if (fd >= 0)
close(fd);
return 0;
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)
{
if (!mixer)
return 0;
return add_controls(mixer);
}
/** Gets the name of the mixer's card.