1 /* $NetBSD: audiodev.c,v 1.4 2010/09/03 19:20:37 jmcneill Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/queue.h> 30 #include <sys/ioctl.h> 31 #include <sys/stat.h> 32 #include <sys/drvctlio.h> 33 34 #include <fcntl.h> 35 #include <paths.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include "audiodev.h" 42 #include "drvctl.h" 43 #include "dtmf.h" 44 45 static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist = 46 TAILQ_HEAD_INITIALIZER(audiodevlist); 47 48 #define AUDIODEV_SAMPLE_RATE 44100 49 50 static unsigned int 51 audiodev_probe_pchans(struct audiodev *adev) 52 { 53 audio_info_t info; 54 unsigned int nchans = 0, n; 55 int error; 56 57 AUDIO_INITINFO(&info); 58 info.play.sample_rate = AUDIODEV_SAMPLE_RATE; 59 info.play.precision = 16; 60 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; 61 info.play.channels = 1; 62 info.mode = AUMODE_PLAY; 63 error = ioctl(adev->fd, AUDIO_SETINFO, &info); 64 if (error == -1) 65 return 0; 66 nchans = 1; 67 68 for (n = 2; n <= 16; n += 2) { 69 info.play.channels = n; 70 error = ioctl(adev->fd, AUDIO_SETINFO, &info); 71 if (error == -1) 72 break; 73 nchans = info.play.channels; 74 } 75 76 return nchans; 77 } 78 79 static int 80 audiodev_getinfo(struct audiodev *adev) 81 { 82 struct stat st; 83 84 if (stat(adev->path, &st) == -1) 85 return -1; 86 adev->dev = st.st_rdev; 87 88 if (stat(_PATH_AUDIO, &st) != -1 && st.st_rdev == adev->dev) 89 adev->defaultdev = true; 90 91 adev->fd = open(adev->path, O_RDWR); 92 if (adev->fd == -1) 93 return -1; 94 if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) { 95 close(adev->fd); 96 return -1; 97 } 98 99 adev->pchan = audiodev_probe_pchans(adev); 100 101 return 0; 102 } 103 104 static int 105 audiodev_add(const char *pdev, const char *dev, unsigned int unit) 106 { 107 struct audiodev *adev; 108 109 adev = calloc(1, sizeof(*adev)); 110 if (adev == NULL) 111 return -1; 112 113 strlcpy(adev->pxname, pdev, sizeof(adev->pxname)); 114 strlcpy(adev->xname, dev, sizeof(adev->xname)); 115 snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev); 116 adev->unit = unit; 117 118 if (audiodev_getinfo(adev) == -1) { 119 free(adev); 120 return -1; 121 } 122 123 #ifdef DEBUG 124 printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ', 125 adev->path, adev->audio_device.name); 126 #endif 127 128 TAILQ_INSERT_TAIL(&audiodevlist, adev, next); 129 130 return 0; 131 } 132 133 static void 134 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit) 135 { 136 audiodev_add(pdev, dev, unit); 137 } 138 139 int 140 audiodev_refresh(void) 141 { 142 struct audiodev *adev; 143 int fd, error; 144 145 fd = open(DRVCTLDEV, O_RDONLY); 146 if (fd == -1) { 147 perror("open " DRVCTLDEV); 148 return -1; 149 } 150 151 while (!TAILQ_EMPTY(&audiodevlist)) { 152 adev = TAILQ_FIRST(&audiodevlist); 153 if (adev->fd != -1) 154 close(adev->fd); 155 TAILQ_REMOVE(&audiodevlist, adev, next); 156 free(adev); 157 } 158 159 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL); 160 if (error == -1) { 161 perror("drvctl"); 162 return -1; 163 } 164 165 close(fd); 166 167 return 0; 168 } 169 170 unsigned int 171 audiodev_count(void) 172 { 173 struct audiodev *adev; 174 unsigned int n; 175 176 n = 0; 177 TAILQ_FOREACH(adev, &audiodevlist, next) 178 ++n; 179 180 return n; 181 } 182 183 struct audiodev * 184 audiodev_get(unsigned int i) 185 { 186 struct audiodev *adev; 187 unsigned int n; 188 189 n = 0; 190 TAILQ_FOREACH(adev, &audiodevlist, next) { 191 if (n == i) 192 return adev; 193 ++n; 194 } 195 196 return NULL; 197 } 198 199 int 200 audiodev_set_default(struct audiodev *adev) 201 { 202 char audiopath[PATH_MAX+1]; 203 char soundpath[PATH_MAX+1]; 204 char audioctlpath[PATH_MAX+1]; 205 char mixerpath[PATH_MAX+1]; 206 207 snprintf(audiopath, sizeof(audiopath) - 1, 208 _PATH_AUDIO "%u", adev->unit); 209 snprintf(soundpath, sizeof(soundpath) - 1, 210 _PATH_SOUND "%u", adev->unit); 211 snprintf(audioctlpath, sizeof(audioctlpath) - 1, 212 _PATH_AUDIOCTL "%u", adev->unit); 213 snprintf(mixerpath, sizeof(mixerpath) - 1, 214 _PATH_MIXER "%u", adev->unit); 215 216 unlink(_PATH_AUDIO); 217 unlink(_PATH_SOUND); 218 unlink(_PATH_AUDIOCTL); 219 unlink(_PATH_MIXER); 220 221 if (symlink(audiopath, _PATH_AUDIO) == -1) { 222 perror("symlink " _PATH_AUDIO); 223 return -1; 224 } 225 if (symlink(soundpath, _PATH_SOUND) == -1) { 226 perror("symlink " _PATH_SOUND); 227 return -1; 228 } 229 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) { 230 perror("symlink " _PATH_AUDIOCTL); 231 return -1; 232 } 233 if (symlink(mixerpath, _PATH_MIXER) == -1) { 234 perror("symlink " _PATH_MIXER); 235 return -1; 236 } 237 238 return 0; 239 } 240 241 int 242 audiodev_test(struct audiodev *adev, unsigned int chanmask) 243 { 244 audio_info_t info; 245 int16_t *buf; 246 size_t buflen; 247 off_t off; 248 int rv = 0; 249 250 AUDIO_INITINFO(&info); 251 info.play.sample_rate = AUDIODEV_SAMPLE_RATE; 252 info.play.channels = adev->pchan; 253 info.play.precision = 16; 254 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; 255 info.mode = AUMODE_PLAY; 256 if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) { 257 perror("ioctl AUDIO_SETINFO"); 258 return -1; 259 } 260 if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) { 261 perror("ioctl AUDIO_GETINFO"); 262 return -1; 263 } 264 265 dtmf_new(&buf, &buflen, info.play.sample_rate, 2, 266 adev->pchan, chanmask, 350.0, 440.0); 267 if (buf == NULL) 268 return -1; 269 270 off = 0; 271 while (buflen > 0) { 272 size_t wlen = info.play.buffer_size; 273 if (wlen > buflen) 274 wlen = buflen; 275 wlen = write(adev->fd, (char *)buf + off, wlen); 276 if (wlen == -1) { 277 perror("write"); 278 rv = -1; 279 goto done; 280 } 281 off += wlen; 282 buflen -= wlen; 283 } 284 285 if (ioctl(adev->fd, AUDIO_DRAIN) == -1) 286 perror("ioctl AUDIO_DRAIN"); 287 288 done: 289 free(buf); 290 291 return rv; 292 } 293