mirror of
https://github.com/openharmony/third_party_tinyalsa.git
synced 2026-07-19 23:33:30 -04:00
Merge pull request #90 from rfvirgil/control-update
mixer: add ability to update control list with new controls
This commit is contained in:
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user