1/*
2 * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#define USE_ERROR
27#define USE_TRACE
28
29#include "PLATFORM_API_SolarisOS_Utils.h"
30
31#define MAX_AUDIO_DEVICES 20
32
33// not thread safe...
34static AudioDevicePath globalADPaths[MAX_AUDIO_DEVICES];
35static int globalADCount = -1;
36static int globalADCacheTime = -1;
37/* how many seconds do we cache devices */
38#define AD_CACHE_TIME 30
39
40// return seconds
41long getTimeInSeconds() {
42    struct timeval tv;
43    gettimeofday(&tv, NULL);
44    return tv.tv_sec;
45}
46
47
48int getAudioDeviceCount() {
49    int count = MAX_AUDIO_DEVICES;
50
51    getAudioDevices(globalADPaths, &count);
52    return count;
53}
54
55/* returns TRUE if the path exists at all */
56int addAudioDevice(char* path, AudioDevicePath* adPath, int* count) {
57    int i;
58    int found = 0;
59    int fileExists = 0;
60    // not thread safe...
61    static struct stat statBuf;
62
63    // get stats on the file
64    if (stat(path, &statBuf) == 0) {
65        // file exists.
66        fileExists = 1;
67        // If it is not yet in the adPath array, add it to the array
68        for (i = 0; i < *count; i++) {
69            if (adPath[i].st_ino == statBuf.st_ino
70                && adPath[i].st_dev == statBuf.st_dev) {
71                found = 1;
72                break;
73            }
74        }
75        if (!found) {
76            adPath[*count].st_ino = statBuf.st_ino;
77            adPath[*count].st_dev = statBuf.st_dev;
78            strncpy(adPath[*count].path, path, MAX_NAME_LENGTH);
79            adPath[*count].path[MAX_NAME_LENGTH - 1] = 0;
80            (*count)++;
81            TRACE1("Added audio device %s\n", path);
82        }
83    }
84    return fileExists;
85}
86
87
88void getAudioDevices(AudioDevicePath* adPath, int* count) {
89    int maxCount = *count;
90    char* audiodev;
91    char devsound[15];
92    int i;
93    long timeInSeconds = getTimeInSeconds();
94
95    if (globalADCount < 0
96        || (getTimeInSeconds() - globalADCacheTime) > AD_CACHE_TIME
97        || (adPath != globalADPaths)) {
98        *count = 0;
99        // first device, if set, is AUDIODEV variable
100        audiodev = getenv("AUDIODEV");
101        if (audiodev != NULL && audiodev[0] != 0) {
102            addAudioDevice(audiodev, adPath, count);
103        }
104        // then try /dev/audio
105        addAudioDevice("/dev/audio", adPath, count);
106        // then go through all of the /dev/sound/? devices
107        for (i = 0; i < 100; i++) {
108            sprintf(devsound, "/dev/sound/%d", i);
109            if (!addAudioDevice(devsound, adPath, count)) {
110                break;
111            }
112        }
113        if (adPath == globalADPaths) {
114            /* commit cache */
115            globalADCount = *count;
116            /* set cache time */
117            globalADCacheTime = timeInSeconds;
118        }
119    } else {
120        /* return cache */
121        *count = globalADCount;
122    }
123    // that's it
124}
125
126int getAudioDeviceDescriptionByIndex(int index, AudioDeviceDescription* adDesc, int getNames) {
127    int count = MAX_AUDIO_DEVICES;
128    int ret = 0;
129
130    getAudioDevices(globalADPaths, &count);
131    if (index>=0 && index < count) {
132        ret = getAudioDeviceDescription(globalADPaths[index].path, adDesc, getNames);
133    }
134    return ret;
135}
136
137int getAudioDeviceDescription(char* path, AudioDeviceDescription* adDesc, int getNames) {
138    int fd;
139    int mixerMode;
140    int len;
141    audio_info_t info;
142    audio_device_t deviceInfo;
143
144    strncpy(adDesc->path, path, MAX_NAME_LENGTH);
145    adDesc->path[MAX_NAME_LENGTH] = 0;
146    strcpy(adDesc->pathctl, adDesc->path);
147    strcat(adDesc->pathctl, "ctl");
148    strcpy(adDesc->name, adDesc->path);
149    adDesc->vendor[0] = 0;
150    adDesc->version[0] = 0;
151    adDesc->description[0] = 0;
152    adDesc->maxSimulLines = 1;
153
154    // try to open the pseudo device and get more information
155    fd = open(adDesc->pathctl, O_WRONLY | O_NONBLOCK);
156    if (fd >= 0) {
157        close(fd);
158        if (getNames) {
159            fd = open(adDesc->pathctl, O_RDONLY);
160            if (fd >= 0) {
161                if (ioctl(fd, AUDIO_GETDEV, &deviceInfo) >= 0) {
162                    strncpy(adDesc->vendor, deviceInfo.name, MAX_AUDIO_DEV_LEN);
163                    adDesc->vendor[MAX_AUDIO_DEV_LEN] = 0;
164                    strncpy(adDesc->version, deviceInfo.version, MAX_AUDIO_DEV_LEN);
165                    adDesc->version[MAX_AUDIO_DEV_LEN] = 0;
166                    /* add config string to the dev name
167                     * creates a string like "/dev/audio (onboard1)"
168                     */
169                    len = strlen(adDesc->name) + 1;
170                    if (MAX_NAME_LENGTH - len > 3) {
171                        strcat(adDesc->name, " (");
172                        strncat(adDesc->name, deviceInfo.config, MAX_NAME_LENGTH - len);
173                        strcat(adDesc->name, ")");
174                    }
175                    adDesc->name[MAX_NAME_LENGTH-1] = 0;
176                }
177                if (ioctl(fd, AUDIO_MIXERCTL_GET_MODE, &mixerMode) >= 0) {
178                    if (mixerMode == AM_MIXER_MODE) {
179                        TRACE1(" getAudioDeviceDescription: %s is in mixer mode\n", adDesc->path);
180                        adDesc->maxSimulLines = -1;
181                    }
182                } else {
183                    ERROR1("ioctl AUDIO_MIXERCTL_GET_MODE failed on %s!\n", adDesc->path);
184                }
185                close(fd);
186            } else {
187                ERROR1("could not open %s!\n", adDesc->pathctl);
188            }
189        }
190        return 1;
191    }
192    return 0;
193}
194