/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless requied by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include <assert.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <gtest/gtest.h> #include <linux/ioctl.h> #include <sound/asound.h> #include <sys/types.h> #include <tinyalsa/asoundlib.h> #define LOG_TAG "pcmtest" #include <utils/Log.h> #include <testUtil.h> #define PCM_PREFIX "pcm" #define MIXER_PREFIX "control" #define TIMER_PREFIX "timer" const char kSoundDir[] = "/dev/snd"; typedef struct PCM_NODE { unsigned int card; unsigned int device; unsigned int flags; } pcm_node_t; static pcm_node_t *pcmnodes; static unsigned int pcms; static unsigned int cards; static unsigned int mixers; static unsigned int timers; unsigned int getPcmNodes(void) { DIR *d; struct dirent *de; unsigned int pcount = 0; d = opendir(kSoundDir); if (d == 0) return 0; while ((de = readdir(d)) != NULL) { if (de->d_name[0] == '.') continue; if (strstr(de->d_name, PCM_PREFIX)) pcount++; } closedir(d); return pcount; } int getSndDev(unsigned int pcmdevs) { DIR *d; struct dirent *de; unsigned int prevcard = -1; d = opendir(kSoundDir); if (d == 0) return -ENXIO; pcmnodes = (pcm_node_t *)malloc(pcmdevs * sizeof(pcm_node_t)); if (!pcmnodes) return -ENOMEM; pcms = 0; while ((de = readdir(d)) != NULL) { if (de->d_name[0] == '.') continue; /* printf("%s\n", de->d_name); */ if (strstr(de->d_name, PCM_PREFIX)) { char flags; EXPECT_LE(pcms, pcmdevs) << "Too many PCMs"; if (pcms >= pcmdevs) continue; sscanf(de->d_name, PCM_PREFIX "C%uD%u", &(pcmnodes[pcms].card), &(pcmnodes[pcms].device)); flags = de->d_name[strlen(de->d_name)-1]; if (flags == 'c') { pcmnodes[pcms].flags = PCM_IN; } else if(flags == 'p') { pcmnodes[pcms].flags = PCM_OUT; } else { pcmnodes[pcms].flags = -1; testPrintI("Unknown PCM type = %c", flags); } if (prevcard != pcmnodes[pcms].card) cards++; prevcard = pcmnodes[pcms].card; pcms++; continue; } if (strstr(de->d_name, MIXER_PREFIX)) { unsigned int mixer = -1; sscanf(de->d_name, MIXER_PREFIX "C%u", &mixer); mixers++; continue; } if (strstr(de->d_name, TIMER_PREFIX)) { timers++; continue; } } closedir(d); return 0; } int getPcmParams(unsigned int i) { struct pcm_params *params; unsigned int min; unsigned int max; params = pcm_params_get(pcmnodes[i].card, pcmnodes[i].device, pcmnodes[i].flags); if (params == NULL) return -ENODEV; min = pcm_params_get_min(params, PCM_PARAM_RATE); max = pcm_params_get_max(params, PCM_PARAM_RATE); EXPECT_LE(min, max); /* 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); EXPECT_LE(min, max); /* 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); EXPECT_LE(min, max); /* 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); EXPECT_LE(min, max); /* 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); EXPECT_LE(min, max); /* printf("Period count:\tmin=%u\t\tmax=%u\n", min, max); */ pcm_params_free(params); return 0; } TEST(pcmtest, CheckAudioDir) { pcms = getPcmNodes(); ASSERT_GT(pcms, 0U); } TEST(pcmtest, GetSoundDevs) { int err = getSndDev(pcms); testPrintI(" DEVICES = PCMS:%u CARDS:%u MIXERS:%u TIMERS:%u", pcms, cards, mixers, timers); ASSERT_EQ(0, err); } TEST(pcmtest, CheckPcmSanity0) { ASSERT_NE(0U, pcms); } TEST(pcmtest, CheckPcmSanity1) { EXPECT_NE(1U, pcms % 2); } TEST(pcmtests, CheckMixerSanity) { ASSERT_NE(0U, mixers); ASSERT_EQ(mixers, cards); } TEST(pcmtest, CheckTimesSanity0) { ASSERT_NE(0U, timers); } TEST(pcmtest, CheckTimesSanity1) { EXPECT_EQ(1U, timers); } TEST(pcmtest, CheckPcmDevices) { for (unsigned int i = 0; i < pcms; i++) { EXPECT_EQ(0, getPcmParams(i)); } free(pcmnodes); } TEST(pcmtest, CheckMixerDevices) { struct mixer *mixer; for (unsigned int i = 0; i < mixers; i++) { mixer = mixer_open(i); EXPECT_TRUE(mixer != NULL); if (mixer) mixer_close(mixer); } } TEST(pcmtest, CheckTimer) { int ver = 0; int fd = open("/dev/snd/timer", O_RDWR | O_NONBLOCK); ASSERT_GE(fd, 0); int ret = ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver); EXPECT_EQ(0, ret); testPrintI(" Timer Version = 0x%x", ver); close(fd); }