1 /* $NetBSD: audiodev.c,v 1.6 2016/03/05 22:10:39 mrg 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 adev->fd = open(adev->path, O_WRONLY); 94 if (adev->fd == -1) 95 return -1; 96 } 97 if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) { 98 close(adev->fd); 99 return -1; 100 } 101 102 adev->pchan = audiodev_probe_pchans(adev); 103 104 return 0; 105 } 106 107 static int 108 audiodev_add(const char *pdev, const char *dev, unsigned int unit) 109 { 110 struct audiodev *adev; 111 112 adev = calloc(1, sizeof(*adev)); 113 if (adev == NULL) 114 return -1; 115 116 strlcpy(adev->pxname, pdev, sizeof(adev->pxname)); 117 strlcpy(adev->xname, dev, sizeof(adev->xname)); 118 snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev); 119 adev->unit = unit; 120 121 if (audiodev_getinfo(adev) == -1) { 122 free(adev); 123 return -1; 124 } 125 126 #ifdef DEBUG 127 printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ', 128 adev->path, adev->audio_device.name); 129 #endif 130 131 TAILQ_INSERT_TAIL(&audiodevlist, adev, next); 132 133 return 0; 134 } 135 136 static void 137 audiodev_cb(void *args, const char *pdev, const char *dev, unsigned int unit) 138 { 139 audiodev_add(pdev, dev, unit); 140 } 141 142 int 143 audiodev_refresh(void) 144 { 145 struct audiodev *adev; 146 int fd, error; 147 148 fd = open(DRVCTLDEV, O_RDONLY); 149 if (fd == -1) { 150 perror("open " DRVCTLDEV); 151 return -1; 152 } 153 154 while (!TAILQ_EMPTY(&audiodevlist)) { 155 adev = TAILQ_FIRST(&audiodevlist); 156 if (adev->fd != -1) 157 close(adev->fd); 158 TAILQ_REMOVE(&audiodevlist, adev, next); 159 free(adev); 160 } 161 162 error = drvctl_foreach(fd, "audio", audiodev_cb, NULL); 163 if (error == -1) { 164 perror("drvctl"); 165 return -1; 166 } 167 168 close(fd); 169 170 return 0; 171 } 172 173 unsigned int 174 audiodev_count(void) 175 { 176 struct audiodev *adev; 177 unsigned int n; 178 179 n = 0; 180 TAILQ_FOREACH(adev, &audiodevlist, next) 181 ++n; 182 183 return n; 184 } 185 186 struct audiodev * 187 audiodev_get(unsigned int i) 188 { 189 struct audiodev *adev; 190 unsigned int n; 191 192 n = 0; 193 TAILQ_FOREACH(adev, &audiodevlist, next) { 194 if (n == i) 195 return adev; 196 ++n; 197 } 198 199 return NULL; 200 } 201 202 int 203 audiodev_set_default(struct audiodev *adev) 204 { 205 char audiopath[PATH_MAX+1]; 206 char soundpath[PATH_MAX+1]; 207 char audioctlpath[PATH_MAX+1]; 208 char mixerpath[PATH_MAX+1]; 209 210 snprintf(audiopath, sizeof(audiopath) - 1, 211 _PATH_AUDIO "%u", adev->unit); 212 snprintf(soundpath, sizeof(soundpath) - 1, 213 _PATH_SOUND "%u", adev->unit); 214 snprintf(audioctlpath, sizeof(audioctlpath) - 1, 215 _PATH_AUDIOCTL "%u", adev->unit); 216 snprintf(mixerpath, sizeof(mixerpath) - 1, 217 _PATH_MIXER "%u", adev->unit); 218 219 unlink(_PATH_AUDIO); 220 unlink(_PATH_SOUND); 221 unlink(_PATH_AUDIOCTL); 222 unlink(_PATH_MIXER); 223 224 if (symlink(audiopath, _PATH_AUDIO) == -1) { 225 perror("symlink " _PATH_AUDIO); 226 return -1; 227 } 228 if (symlink(soundpath, _PATH_SOUND) == -1) { 229 perror("symlink " _PATH_SOUND); 230 return -1; 231 } 232 if (symlink(audioctlpath, _PATH_AUDIOCTL) == -1) { 233 perror("symlink " _PATH_AUDIOCTL); 234 return -1; 235 } 236 if (symlink(mixerpath, _PATH_MIXER) == -1) { 237 perror("symlink " _PATH_MIXER); 238 return -1; 239 } 240 241 return 0; 242 } 243 244 int 245 audiodev_test(struct audiodev *adev, unsigned int chanmask) 246 { 247 audio_info_t info; 248 int16_t *buf; 249 size_t buflen; 250 off_t off; 251 int rv = 0; 252 253 AUDIO_INITINFO(&info); 254 info.play.sample_rate = AUDIODEV_SAMPLE_RATE; 255 info.play.channels = adev->pchan; 256 info.play.precision = 16; 257 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; 258 info.mode = AUMODE_PLAY; 259 if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) { 260 perror("ioctl AUDIO_SETINFO"); 261 return -1; 262 } 263 if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) { 264 perror("ioctl AUDIO_GETINFO"); 265 return -1; 266 } 267 268 dtmf_new(&buf, &buflen, info.play.sample_rate, 2, 269 adev->pchan, chanmask, 350.0, 440.0); 270 if (buf == NULL) 271 return -1; 272 273 off = 0; 274 while (buflen > 0) { 275 size_t wlen; 276 ssize_t ret; 277 278 wlen = info.play.buffer_size; 279 if (wlen > buflen) 280 wlen = buflen; 281 ret = write(adev->fd, (char *)buf + off, wlen); 282 if (ret == -1) { 283 perror("write"); 284 rv = -1; 285 goto done; 286 } 287 wlen = ret; 288 off += wlen; 289 buflen -= wlen; 290 } 291 292 if (ioctl(adev->fd, AUDIO_DRAIN) == -1) 293 perror("ioctl AUDIO_DRAIN"); 294 295 done: 296 free(buf); 297 298 return rv; 299 } 300