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#define __force
27#define __bitwise
28#define __user
29#include <sound/asound.h>
30#include <sys/types.h>
31#include <tinyalsa/asoundlib.h>
32
33#define LOG_TAG "pcmtest"
34#include <utils/Log.h>
35#include <testUtil.h>
36
37#define PCM_PREFIX	"pcm"
38#define MIXER_PREFIX	"control"
39#define TIMER_PREFIX	"timer"
40
41const char kSoundDir[] = "/dev/snd";
42
43typedef struct PCM_NODE {
44    unsigned int card;
45    unsigned int device;
46    unsigned int flags;
47} pcm_node_t;
48
49static pcm_node_t *pcmnodes;
50
51static unsigned int pcms;
52static unsigned int cards;
53static unsigned int mixers;
54static unsigned int timers;
55
56int getPcmNodes(void)
57{
58    DIR *d;
59    struct dirent *de;
60    unsigned int pcount = 0;
61
62    d = opendir(kSoundDir);
63    if (d == 0)
64        return 0;
65    while ((de = readdir(d)) != NULL) {
66        if (de->d_name[0] == '.')
67            continue;
68        if (strstr(de->d_name, PCM_PREFIX))
69            pcount++;
70    }
71    closedir(d);
72    return pcount;
73}
74
75int getSndDev(unsigned int pcmdevs)
76{
77    DIR *d;
78    struct dirent *de;
79    unsigned int prevcard = -1;
80
81    d = opendir(kSoundDir);
82    if (d == 0)
83        return -ENXIO;
84    pcmnodes = (pcm_node_t *)malloc(pcmdevs * sizeof(pcm_node_t));
85    if (!pcmnodes)
86        return -ENOMEM;
87    pcms = 0;
88    while ((de = readdir(d)) != NULL) {
89        if (de->d_name[0] == '.')
90            continue;
91        /* printf("%s\n", de->d_name); */
92        if (strstr(de->d_name, PCM_PREFIX)) {
93            char flags;
94
95            EXPECT_LE(pcms, pcmdevs) << "Too many PCMs";
96            if (pcms >= pcmdevs)
97                continue;
98            sscanf(de->d_name, PCM_PREFIX "C%uD%u", &(pcmnodes[pcms].card),
99                   &(pcmnodes[pcms].device));
100            flags = de->d_name[strlen(de->d_name)-1];
101            if (flags == 'c') {
102                pcmnodes[pcms].flags = PCM_IN;
103            } else if(flags == 'p') {
104                pcmnodes[pcms].flags = PCM_OUT;
105            } else {
106                pcmnodes[pcms].flags = -1;
107                testPrintI("Unknown PCM type = %c", flags);
108            }
109            if (prevcard != pcmnodes[pcms].card)
110                cards++;
111            prevcard = pcmnodes[pcms].card;
112            pcms++;
113            continue;
114        }
115        if (strstr(de->d_name, MIXER_PREFIX)) {
116            unsigned int mixer = -1;
117            sscanf(de->d_name, MIXER_PREFIX "C%u", &mixer);
118            mixers++;
119            continue;
120        }
121        if (strstr(de->d_name, TIMER_PREFIX)) {
122            timers++;
123            continue;
124        }
125    }
126    closedir(d);
127    return 0;
128}
129
130int getPcmParams(unsigned int i)
131{
132    struct pcm_params *params;
133    unsigned int min;
134    unsigned int max;
135
136    params = pcm_params_get(pcmnodes[i].card, pcmnodes[i].device,
137                            pcmnodes[i].flags);
138    if (params == NULL)
139        return -ENODEV;
140
141    min = pcm_params_get_min(params, PCM_PARAM_RATE);
142    max = pcm_params_get_max(params, PCM_PARAM_RATE);
143    EXPECT_LE(min, max);
144    /* printf("        Rate:\tmin=%uHz\tmax=%uHz\n", min, max); */
145    min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
146    max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
147    EXPECT_LE(min, max);
148    /* printf("    Channels:\tmin=%u\t\tmax=%u\n", min, max); */
149    min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
150    max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
151    EXPECT_LE(min, max);
152    /* printf(" Sample bits:\tmin=%u\t\tmax=%u\n", min, max); */
153    min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
154    max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
155    EXPECT_LE(min, max);
156    /* printf(" Period size:\tmin=%u\t\tmax=%u\n", min, max); */
157    min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
158    max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
159    EXPECT_LE(min, max);
160    /* printf("Period count:\tmin=%u\t\tmax=%u\n", min, max); */
161
162    pcm_params_free(params);
163    return 0;
164}
165
166TEST(pcmtest, CheckAudioDir) {
167    pcms = getPcmNodes();
168    ASSERT_GT(pcms, 0);
169}
170
171TEST(pcmtest, GetSoundDevs) {
172    int err = getSndDev(pcms);
173    testPrintI(" DEVICES = PCMS:%u CARDS:%u MIXERS:%u TIMERS:%u",
174               pcms, cards, mixers, timers);
175    ASSERT_EQ(0, err);
176}
177
178TEST(pcmtest, CheckPcmSanity0) {
179    ASSERT_NE(0, pcms);
180}
181
182TEST(pcmtest, CheckPcmSanity1) {
183    EXPECT_NE(1, pcms % 2);
184}
185
186TEST(pcmtests, CheckMixerSanity) {
187    ASSERT_NE(0, mixers);
188    ASSERT_EQ(mixers, cards);
189}
190
191TEST(pcmtest, CheckTimesSanity0) {
192    ASSERT_NE(0, timers);
193}
194
195TEST(pcmtest, CheckTimesSanity1) {
196    EXPECT_EQ(1, timers);
197}
198
199TEST(pcmtest, CheckPcmDevices) {
200    for (unsigned int i = 0; i < pcms; i++) {
201        EXPECT_EQ(0, getPcmParams(i));
202    }
203    free(pcmnodes);
204}
205
206TEST(pcmtest, CheckMixerDevices) {
207    struct mixer *mixer;
208    for (unsigned int i = 0; i < mixers; i++) {
209         mixer = mixer_open(i);
210         EXPECT_TRUE(mixer != NULL);
211         if (mixer)
212             mixer_close(mixer);
213    }
214}
215
216TEST(pcmtest, CheckTimer) {
217    int ver = 0;
218    int fd = open("/dev/snd/timer", O_RDWR | O_NONBLOCK);
219    ASSERT_GE(fd, 0);
220    int ret = ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver);
221    EXPECT_EQ(0, ret);
222    testPrintI(" Timer Version = 0x%x", ver);
223    close(fd);
224}
225