|  | /* | 
|  | * 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> | 
|  |  | 
|  | #define PCM_PREFIX	"pcm" | 
|  | #define MIXER_PREFIX	"control" | 
|  | #define TIMER_PREFIX	"timer" | 
|  |  | 
|  | #define MAXSTR 200 | 
|  | #define testPrintI(...)                 \ | 
|  | do {                                \ | 
|  | testPrint(stdout, __VA_ARGS__); \ | 
|  | } while (0) | 
|  |  | 
|  | 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; | 
|  |  | 
|  | void testPrint(FILE* stream, const char* fmt, ...) { | 
|  | char line[MAXSTR]; | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | vsnprintf(line, sizeof(line), fmt, args); | 
|  | if (stream == stderr) { | 
|  | ALOG(LOG_ERROR, LOG_TAG, "%s", line); | 
|  | } else { | 
|  | ALOG(LOG_INFO, LOG_TAG, "%s", line); | 
|  | } | 
|  | vfprintf(stream, fmt, args); | 
|  | va_end(args); | 
|  | fputc('\n', stream); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } |