1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless requied by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18#include <assert.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <gtest/gtest.h>
25#include <linux/ioctl.h>
26#include <sound/asound.h>
27#include <sys/types.h>
28#include <tinyalsa/asoundlib.h>
29
30#define LOG_TAG "pcmtest"
31#include <utils/Log.h>
32#include <testUtil.h>
33
34#define PCM_PREFIX	"pcm"
35#define MIXER_PREFIX	"control"
36#define TIMER_PREFIX	"timer"
37
38const char kSoundDir[] = "/dev/snd";
39
40typedef struct PCM_NODE {
41    unsigned int card;
42    unsigned int device;
43    unsigned int flags;
44} pcm_node_t;
45
46static pcm_node_t *pcmnodes;
47
48static unsigned int pcms;
49static unsigned int cards;
50static unsigned int mixers;
51static unsigned int timers;
52
53unsigned int getPcmNodes(void)
54{
55    DIR *d;
56    struct dirent *de;
57    unsigned int pcount = 0;
58
59    d = opendir(kSoundDir);
60    if (d == 0)
61        return 0;
62    while ((de = readdir(d)) != NULL) {
63        if (de->d_name[0] == '.')
64            continue;
65        if (strstr(de->d_name, PCM_PREFIX))
66            pcount++;
67    }
68    closedir(d);
69    return pcount;
70}
71
72int getSndDev(unsigned int pcmdevs)
73{
74    DIR *d;
75    struct dirent *de;
76    unsigned int prevcard = -1;
77
78    d = opendir(kSoundDir);
79    if (d == 0)
80        return -ENXIO;
81    pcmnodes = (pcm_node_t *)malloc(pcmdevs * sizeof(pcm_node_t));
82    if (!pcmnodes)
83        return -ENOMEM;
84    pcms = 0;
85    while ((de = readdir(d)) != NULL) {
86        if (de->d_name[0] == '.')
87            continue;
88        /* printf("%s\n", de->d_name); */
89        if (strstr(de->d_name, PCM_PREFIX)) {
90            char flags;
91
92            EXPECT_LE(pcms, pcmdevs) << "Too many PCMs";
93            if (pcms >= pcmdevs)
94                continue;
95            sscanf(de->d_name, PCM_PREFIX "C%uD%u", &(pcmnodes[pcms].card),
96                   &(pcmnodes[pcms].device));
97            flags = de->d_name[strlen(de->d_name)-1];
98            if (flags == 'c') {
99                pcmnodes[pcms].flags = PCM_IN;
100            } else if(flags == 'p') {
101                pcmnodes[pcms].flags = PCM_OUT;
102            } else {
103                pcmnodes[pcms].flags = -1;
104                testPrintI("Unknown PCM type = %c", flags);
105            }
106            if (prevcard != pcmnodes[pcms].card)
107                cards++;
108            prevcard = pcmnodes[pcms].card;
109            pcms++;
110            continue;
111        }
112        if (strstr(de->d_name, MIXER_PREFIX)) {
113            unsigned int mixer = -1;
114            sscanf(de->d_name, MIXER_PREFIX "C%u", &mixer);
115            mixers++;
116            continue;
117        }
118        if (strstr(de->d_name, TIMER_PREFIX)) {
119            timers++;
120            continue;
121        }
122    }
123    closedir(d);
124    return 0;
125}
126
127int getPcmParams(unsigned int i)
128{
129    struct pcm_params *params;
130    unsigned int min;
131    unsigned int max;
132
133    params = pcm_params_get(pcmnodes[i].card, pcmnodes[i].device,
134                            pcmnodes[i].flags);
135    if (params == NULL)
136        return -ENODEV;
137
138    min = pcm_params_get_min(params, PCM_PARAM_RATE);
139    max = pcm_params_get_max(params, PCM_PARAM_RATE);
140    EXPECT_LE(min, max);
141    /* printf("        Rate:\tmin=%uHz\tmax=%uHz\n", min, max); */
142    min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
143    max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
144    EXPECT_LE(min, max);
145    /* printf("    Channels:\tmin=%u\t\tmax=%u\n", min, max); */
146    min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
147    max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
148    EXPECT_LE(min, max);
149    /* printf(" Sample bits:\tmin=%u\t\tmax=%u\n", min, max); */
150    min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
151    max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
152    EXPECT_LE(min, max);
153    /* printf(" Period size:\tmin=%u\t\tmax=%u\n", min, max); */
154    min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
155    max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
156    EXPECT_LE(min, max);
157    /* printf("Period count:\tmin=%u\t\tmax=%u\n", min, max); */
158
159    pcm_params_free(params);
160    return 0;
161}
162
163TEST(pcmtest, CheckAudioDir) {
164    pcms = getPcmNodes();
165    ASSERT_GT(pcms, 0U);
166}
167
168TEST(pcmtest, GetSoundDevs) {
169    int err = getSndDev(pcms);
170    testPrintI(" DEVICES = PCMS:%u CARDS:%u MIXERS:%u TIMERS:%u",
171               pcms, cards, mixers, timers);
172    ASSERT_EQ(0, err);
173}
174
175TEST(pcmtest, CheckPcmSanity0) {
176    ASSERT_NE(0U, pcms);
177}
178
179TEST(pcmtest, CheckPcmSanity1) {
180    EXPECT_NE(1U, pcms % 2);
181}
182
183TEST(pcmtests, CheckMixerSanity) {
184    ASSERT_NE(0U, mixers);
185    ASSERT_EQ(mixers, cards);
186}
187
188TEST(pcmtest, CheckTimesSanity0) {
189    ASSERT_NE(0U, timers);
190}
191
192TEST(pcmtest, CheckTimesSanity1) {
193    EXPECT_EQ(1U, timers);
194}
195
196TEST(pcmtest, CheckPcmDevices) {
197    for (unsigned int i = 0; i < pcms; i++) {
198        EXPECT_EQ(0, getPcmParams(i));
199    }
200    free(pcmnodes);
201}
202
203TEST(pcmtest, CheckMixerDevices) {
204    struct mixer *mixer;
205    for (unsigned int i = 0; i < mixers; i++) {
206         mixer = mixer_open(i);
207         EXPECT_TRUE(mixer != NULL);
208         if (mixer)
209             mixer_close(mixer);
210    }
211}
212
213TEST(pcmtest, CheckTimer) {
214    int ver = 0;
215    int fd = open("/dev/snd/timer", O_RDWR | O_NONBLOCK);
216    ASSERT_GE(fd, 0);
217    int ret = ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver);
218    EXPECT_EQ(0, ret);
219    testPrintI(" Timer Version = 0x%x", ver);
220    close(fd);
221}
222