diff --git a/Android.mk b/Android.mk index 5b64934c0788f5e3d1e8958808bdc09bb12530f1..4f6db22651253e3072f79378d6ebf02130b0cc36 100644 --- a/Android.mk +++ b/Android.mk @@ -45,3 +45,12 @@ LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_C_INCLUDES:= external/tinyalsa/include +LOCAL_SRC_FILES:= tinypcminfo.c +LOCAL_MODULE := tinypcminfo +LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h index 6148361081b0a68bff5a1b078f04bca394c094d7..bb4005a954ccaf366a5307fa2c5fbeb7153d4b31 100644 --- a/include/tinyalsa/asoundlib.h +++ b/include/tinyalsa/asoundlib.h @@ -104,6 +104,23 @@ struct pcm_config { int avail_min; }; +/* PCM parameters */ +enum pcm_param +{ + PCM_PARAM_SAMPLE_BITS, + PCM_PARAM_FRAME_BITS, + PCM_PARAM_CHANNELS, + PCM_PARAM_RATE, + PCM_PARAM_PERIOD_TIME, + PCM_PARAM_PERIOD_SIZE, + PCM_PARAM_PERIOD_BYTES, + PCM_PARAM_PERIODS, + PCM_PARAM_BUFFER_TIME, + PCM_PARAM_BUFFER_SIZE, + PCM_PARAM_BUFFER_BYTES, + PCM_PARAM_TICK_TIME, +}; + /* Mixer control types */ enum mixer_ctl_type { MIXER_CTL_TYPE_BOOL, @@ -123,6 +140,15 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, int pcm_close(struct pcm *pcm); int pcm_is_ready(struct pcm *pcm); +/* Obtain the parameters for a PCM */ +struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, + unsigned int flags); +void pcm_params_free(struct pcm_params *pcm_params); +unsigned int pcm_params_get_min(struct pcm_params *pcm_params, + enum pcm_param param); +unsigned int pcm_params_get_max(struct pcm_params *pcm_params, + enum pcm_param param); + /* Set and get config */ int pcm_get_config(struct pcm *pcm, struct pcm_config *config); int pcm_set_config(struct pcm *pcm, struct pcm_config *config); diff --git a/pcm.c b/pcm.c index d841bd9719b251af6fff4e50d07c358c98587b35..4b31c4d9204ab25360cb8108b0ade53c98008b78 100644 --- a/pcm.c +++ b/pcm.c @@ -93,6 +93,24 @@ static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val) } } +static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + return i->min; + } + return 0; +} + +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; +} + static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val) { if (param_is_interval(n)) { @@ -130,6 +148,9 @@ static void param_init(struct snd_pcm_hw_params *p) i->min = 0; i->max = ~0; } + p->rmask = ~0U; + p->cmask = 0; + p->info = ~0U; } #define PCM_ERROR_MAX 128 @@ -433,6 +454,129 @@ static struct pcm bad_pcm = { .fd = -1, }; +struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, + unsigned int flags) +{ + struct snd_pcm_hw_params *params; + char fn[256]; + int fd; + + snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device, + flags & PCM_IN ? 'c' : 'p'); + + fd = open(fn, O_RDWR); + if (fd < 0) { + fprintf(stderr, "cannot open device '%s'\n", fn); + goto err_open; + } + + params = calloc(1, sizeof(struct snd_pcm_hw_params)); + if (!params) + goto err_calloc; + + param_init(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; + } + + close(fd); + + return (struct pcm_params *)params; + +err_hw_refine: + free(params); +err_calloc: + close(fd); +err_open: + return NULL; +} + +void pcm_params_free(struct pcm_params *pcm_params) +{ + struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; + + if (params) + free(params); +} + +static int pcm_param_to_alsa(enum pcm_param param) +{ + switch (param) { + case PCM_PARAM_SAMPLE_BITS: + return SNDRV_PCM_HW_PARAM_SAMPLE_BITS; + break; + case PCM_PARAM_FRAME_BITS: + return SNDRV_PCM_HW_PARAM_FRAME_BITS; + break; + case PCM_PARAM_CHANNELS: + return SNDRV_PCM_HW_PARAM_CHANNELS; + break; + case PCM_PARAM_RATE: + return SNDRV_PCM_HW_PARAM_RATE; + break; + case PCM_PARAM_PERIOD_TIME: + return SNDRV_PCM_HW_PARAM_PERIOD_TIME; + break; + case PCM_PARAM_PERIOD_SIZE: + return SNDRV_PCM_HW_PARAM_PERIOD_SIZE; + break; + case PCM_PARAM_PERIOD_BYTES: + return SNDRV_PCM_HW_PARAM_PERIOD_BYTES; + break; + case PCM_PARAM_PERIODS: + return SNDRV_PCM_HW_PARAM_PERIODS; + break; + case PCM_PARAM_BUFFER_TIME: + return SNDRV_PCM_HW_PARAM_BUFFER_TIME; + break; + case PCM_PARAM_BUFFER_SIZE: + return SNDRV_PCM_HW_PARAM_BUFFER_SIZE; + break; + case PCM_PARAM_BUFFER_BYTES: + return SNDRV_PCM_HW_PARAM_BUFFER_BYTES; + break; + case PCM_PARAM_TICK_TIME: + return SNDRV_PCM_HW_PARAM_TICK_TIME; + break; + + default: + return -1; + } +} + +unsigned int pcm_params_get_min(struct pcm_params *pcm_params, + enum pcm_param param) +{ + struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; + int p; + + if (!params) + return 0; + + p = pcm_param_to_alsa(param); + if (p < 0) + return 0; + + return param_get_min(params, p); +} + +unsigned int pcm_params_get_max(struct pcm_params *pcm_params, + enum pcm_param param) +{ + struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params; + int p; + + if (!params) + return 0; + + p = pcm_param_to_alsa(param); + if (p < 0) + return 0; + + return param_get_max(params, p); +} + int pcm_close(struct pcm *pcm) { if (pcm == &bad_pcm) diff --git a/tinypcminfo.c b/tinypcminfo.c new file mode 100644 index 0000000000000000000000000000000000000000..32821867037297a4fc50df4d9a1d44642f1195ad --- /dev/null +++ b/tinypcminfo.c @@ -0,0 +1,97 @@ +/* tinypcminfo.c +** +** Copyright 2012, The Android Open Source Project +** +** 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 Android Open Source Project 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 BY The Android Open Source Project ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project 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/asoundlib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) +{ + unsigned int device = 0; + unsigned int card = 0; + int i; + + if (argc < 3) { + fprintf(stderr, "Usage: %s -D card -d device\n", argv[0]); + return 1; + } + + /* parse command line arguments */ + argv += 1; + while (*argv) { + if (strcmp(*argv, "-D") == 0) { + argv++; + if (*argv) + card = atoi(*argv); + } + if (strcmp(*argv, "-d") == 0) { + argv++; + if (*argv) + device = atoi(*argv); + } + if (*argv) + argv++; + } + + printf("Info for card %d, device %d:\n", card, device); + + for (i = 0; i < 2; i++) { + struct pcm_params *params; + unsigned int min; + unsigned int max; + + printf("\nPCM %s:\n", i == 0 ? "out" : "in"); + + params = pcm_params_get(card, device, i == 0 ? PCM_OUT : PCM_IN); + if (params == NULL) { + printf("Device does not exist.\n"); + continue; + } + + min = pcm_params_get_min(params, PCM_PARAM_RATE); + max = pcm_params_get_max(params, PCM_PARAM_RATE); + printf(" Rate:\tmin=%uHz\tmax=%uHz\n", min, max); + min = pcm_params_get_min(params, PCM_PARAM_CHANNELS); + max = pcm_params_get_max(params, PCM_PARAM_CHANNELS); + printf(" Channels:\tmin=%u\t\tmax=%u\n", min, max); + min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS); + max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS); + printf(" Sample bits:\tmin=%u\t\tmax=%u\n", min, max); + min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE); + max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE); + printf(" Period size:\tmin=%u\t\tmax=%u\n", min, max); + min = pcm_params_get_min(params, PCM_PARAM_PERIODS); + max = pcm_params_get_max(params, PCM_PARAM_PERIODS); + printf("Period count:\tmin=%u\t\tmax=%u\n", min, max); + + pcm_params_free(params); + } + + return 0; +}