From 01dc0e6825b5620510faaefaef40ff8cfb692b6e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Sep 2012 14:15:04 +0200 Subject: [PATCH 10/30] PCM: Implement snd_pcm_query_chmaps_from_hw() This is a function similar like snd_pcm_query_chmaps() but performs the query without a PCM handle. The card, device and substream numbers are passed as well as stream direction. Signed-off-by: Takashi Iwai --- include/pcm.h | 3 + src/pcm/pcm_hw.c | 117 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 41 deletions(-) --- a/include/pcm.h +++ b/include/pcm.h @@ -526,6 +526,9 @@ typedef struct snd_pcm_chmap_query { snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm); +snd_pcm_chmap_query_t **snd_pcm_query_chmaps_from_hw(int card, int dev, + int subdev, + snd_pcm_stream_t stream); void snd_pcm_free_chmaps(snd_pcm_chmap_query_t **maps); snd_pcm_chmap_t *snd_pcm_get_chmap(snd_pcm_t *pcm); int snd_pcm_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -1006,80 +1006,82 @@ static int snd_pcm_hw_htimestamp(snd_pcm return 0; } -static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id) +static void __fill_chmap_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev, + int stream) { - snd_pcm_hw_t *hw = pcm->private_data; snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM); - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + if (stream == SND_PCM_STREAM_PLAYBACK) snd_ctl_elem_id_set_name(id, "Playback Channel Map"); else snd_ctl_elem_id_set_name(id, "Capture Channel Map"); - snd_ctl_elem_id_set_device(id, hw->device); - snd_ctl_elem_id_set_index(id, hw->subdevice); -} - -static int is_chmap_type(int type) -{ - return (type >= SND_CTL_TLVT_CHMAP_FIXED && - type <= SND_CTL_TLVT_CHMAP_PAIRED); -} - -enum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET }; - -static int chmap_caps(snd_pcm_hw_t *hw, int type) -{ - if (hw->chmap_caps & (1 << type)) - return 1; - if (hw->chmap_caps & (1 << (type + 8))) - return 0; - return 1; + snd_ctl_elem_id_set_device(id, dev); + snd_ctl_elem_id_set_index(id, subdev); } -static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type) +static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id) { - hw->chmap_caps |= (1 << type); + snd_pcm_hw_t *hw = pcm->private_data; + return __fill_chmap_ctl_id(id, hw->device, hw->subdevice, pcm->stream); } -static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type) +static int is_chmap_type(int type) { - hw->chmap_caps |= (1 << (type + 8)); + return (type >= SND_CTL_TLVT_CHMAP_FIXED && + type <= SND_CTL_TLVT_CHMAP_PAIRED); } -static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm) +/** + * \!brief Query the available channel maps + * \param card the card number + * \param dev the PCM device number + * \param subdev the PCM substream index + * \param stream the direction of PCM stream + * \return the NULL-terminated array of integer pointers, or NULL at error. + * + * This function works like snd_pcm_query_chmaps() but it takes the card, + * device, substream and stream numbers instead of the already opened + * snd_pcm_t instance, so that you can query available channel maps of + * a PCM before actually opening it. + * + * As the parameters stand, the query is performed only to the hw PCM + * devices, not the abstracted PCM object in alsa-lib. + */ +snd_pcm_chmap_query_t ** +snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, + snd_pcm_stream_t stream) { - snd_pcm_hw_t *hw = pcm->private_data; snd_ctl_t *ctl; snd_ctl_elem_id_t *id; unsigned int tlv[256], *start; snd_pcm_chmap_query_t **map; int i, ret, nums; - if (!chmap_caps(hw, CHMAP_CTL_QUERY)) - return NULL; - - ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); + ret = snd_ctl_hw_open(&ctl, NULL, card, 0); if (ret < 0) { SYSMSG("Cannot open the associated CTL\n"); - goto error; + return NULL; } snd_ctl_elem_id_alloca(&id); - fill_chmap_ctl_id(pcm, id); + __fill_chmap_ctl_id(id, dev, subdev, stream); ret = snd_ctl_elem_tlv_read(ctl, id, tlv, sizeof(tlv)); snd_ctl_close(ctl); if (ret < 0) { SYSMSG("Cannot read Channel Map TLV\n"); - goto error; + return NULL; } #if 0 for (i = 0; i < 32; i++) fprintf(stderr, "%02x: %08x\n", i, tlv[i]); #endif + /* FIXME: the parser below assumes that the TLV only contains + * chmap-related blocks + */ if (tlv[0] != SND_CTL_TLVT_CONTAINER) { if (!is_chmap_type(tlv[0])) { SYSMSG("Invalid TLV type %d\n", tlv[0]); - goto error; + return NULL; } start = tlv; nums = 1; @@ -1092,7 +1094,7 @@ static snd_pcm_chmap_query_t **snd_pcm_h for (p = start; size > 0; ) { if (!is_chmap_type(p[0])) { SYSMSG("Invalid TLV type %d\n", p[0]); - goto error; + return NULL; } nums++; size -= p[1] + 8; @@ -1113,12 +1115,45 @@ static snd_pcm_chmap_query_t **snd_pcm_h memcpy(map[i]->map.pos, start + 2, start[1]); start += start[1] / 4 + 2; } - chmap_caps_set_ok(hw, CHMAP_CTL_QUERY); return map; +} + +enum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET }; + +static int chmap_caps(snd_pcm_hw_t *hw, int type) +{ + if (hw->chmap_caps & (1 << type)) + return 1; + if (hw->chmap_caps & (1 << (type + 8))) + return 0; + return 1; +} + +static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type) +{ + hw->chmap_caps |= (1 << type); +} - error: - chmap_caps_set_error(hw, CHMAP_CTL_QUERY); - return NULL; +static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type) +{ + hw->chmap_caps |= (1 << (type + 8)); +} + +static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm) +{ + snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_chmap_query_t **map; + + if (!chmap_caps(hw, CHMAP_CTL_QUERY)) + return NULL; + + map = snd_pcm_query_chmaps_from_hw(hw->card, hw->device, + hw->subdevice, pcm->stream); + if (map) + chmap_caps_set_ok(hw, CHMAP_CTL_QUERY); + else + chmap_caps_set_error(hw, CHMAP_CTL_QUERY); + return map; } static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)