1 /* $OpenBSD: ossaudio.c,v 1.20 2019/06/28 13:32:42 deraadt Exp $ */ 2 /* $NetBSD: ossaudio.c,v 1.14 2001/05/10 01:53:48 augustss Exp $ */ 3 4 /*- 5 * Copyright (c) 1997 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * This is an OSS (Linux) sound API emulator. 32 * It provides the essentials of the API. 33 */ 34 35 #include <stdarg.h> 36 #include <string.h> 37 #include <sys/types.h> 38 #include <sys/ioctl.h> 39 #include <sys/audioio.h> 40 #include <sys/stat.h> 41 #include <errno.h> 42 43 #include "soundcard.h" 44 #undef ioctl 45 46 #define GET_DEV(com) ((com) & 0xff) 47 48 #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) 49 #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) 50 51 static struct audiodevinfo *getdevinfo(int); 52 53 static int mixer_ioctl(int, unsigned long, void *); 54 static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); 55 static int enum_to_ord(struct audiodevinfo *di, int enm); 56 static int enum_to_mask(struct audiodevinfo *di, int enm); 57 58 #define INTARG (*(int*)argp) 59 60 int 61 _oss_ioctl(int fd, unsigned long com, ...) 62 { 63 va_list ap; 64 void *argp; 65 66 va_start(ap, com); 67 argp = va_arg(ap, void *); 68 va_end(ap); 69 if (IOCGROUP(com) == 'P') 70 return ENOTTY; 71 else if (IOCGROUP(com) == 'M') 72 return mixer_ioctl(fd, com, argp); 73 else 74 return ioctl(fd, com, argp); 75 } 76 77 /* If the mixer device should have more than MAX_MIXER_DEVS devices 78 * some will not be available to Linux */ 79 #define MAX_MIXER_DEVS 64 80 struct audiodevinfo { 81 int done; 82 dev_t dev; 83 ino_t ino; 84 int16_t devmap[SOUND_MIXER_NRDEVICES], 85 rdevmap[MAX_MIXER_DEVS]; 86 char names[MAX_MIXER_DEVS][MAX_AUDIO_DEV_LEN]; 87 int enum2opaque[MAX_MIXER_DEVS]; 88 u_long devmask, recmask, stereomask; 89 u_long caps, recsource; 90 }; 91 92 static int 93 opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) 94 { 95 int i, o; 96 97 for (i = 0; i < MAX_MIXER_DEVS; i++) { 98 o = di->enum2opaque[i]; 99 if (o == opq) 100 break; 101 if (o == -1 && label != NULL && 102 !strncmp(di->names[i], label->name, sizeof di->names[i])) { 103 di->enum2opaque[i] = opq; 104 break; 105 } 106 } 107 if (i >= MAX_MIXER_DEVS) 108 i = -1; 109 /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ 110 return (i); 111 } 112 113 static int 114 enum_to_ord(struct audiodevinfo *di, int enm) 115 { 116 if (enm >= MAX_MIXER_DEVS) 117 return (-1); 118 119 /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ 120 return (di->enum2opaque[enm]); 121 } 122 123 static int 124 enum_to_mask(struct audiodevinfo *di, int enm) 125 { 126 int m; 127 if (enm >= MAX_MIXER_DEVS) 128 return (0); 129 130 m = di->enum2opaque[enm]; 131 if (m == -1) 132 m = 0; 133 /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ 134 return (m); 135 } 136 137 /* 138 * Collect the audio device information to allow faster 139 * emulation of the Linux mixer ioctls. Cache the information 140 * to eliminate the overhead of repeating all the ioctls needed 141 * to collect the information. 142 */ 143 static struct audiodevinfo * 144 getdevinfo(int fd) 145 { 146 mixer_devinfo_t mi, cl; 147 int i, j, e; 148 static struct { 149 char *name; 150 int code; 151 } *dp, devs[] = { 152 { AudioNmicrophone, SOUND_MIXER_MIC }, 153 { AudioNline, SOUND_MIXER_LINE }, 154 { AudioNcd, SOUND_MIXER_CD }, 155 { AudioNdac, SOUND_MIXER_PCM }, 156 { AudioNaux, SOUND_MIXER_LINE1 }, 157 { AudioNrecord, SOUND_MIXER_IMIX }, 158 { AudioNmaster, SOUND_MIXER_VOLUME }, 159 { AudioNtreble, SOUND_MIXER_TREBLE }, 160 { AudioNbass, SOUND_MIXER_BASS }, 161 { AudioNspeaker, SOUND_MIXER_SPEAKER }, 162 { AudioNoutput, SOUND_MIXER_OGAIN }, 163 { AudioNinput, SOUND_MIXER_IGAIN }, 164 { AudioNfmsynth, SOUND_MIXER_SYNTH }, 165 { AudioNmidi, SOUND_MIXER_SYNTH }, 166 { 0, -1 } 167 }; 168 static struct audiodevinfo devcache = { 0 }; 169 struct audiodevinfo *di = &devcache; 170 struct stat sb; 171 172 /* Figure out what device it is so we can check if the 173 * cached data is valid. 174 */ 175 if (fstat(fd, &sb) < 0) 176 return 0; 177 if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino)) 178 return di; 179 180 di->done = 1; 181 di->dev = sb.st_dev; 182 di->ino = sb.st_ino; 183 di->devmask = 0; 184 di->recmask = 0; 185 di->stereomask = 0; 186 di->recsource = ~0; 187 di->caps = 0; 188 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 189 di->devmap[i] = -1; 190 for(i = 0; i < MAX_MIXER_DEVS; i++) { 191 di->rdevmap[i] = -1; 192 di->names[i][0] = '\0'; 193 di->enum2opaque[i] = -1; 194 } 195 for(i = 0; i < MAX_MIXER_DEVS; i++) { 196 mi.index = i; 197 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) == -1) 198 break; 199 switch(mi.type) { 200 case AUDIO_MIXER_VALUE: 201 for(dp = devs; dp->name; dp++) 202 if (strcmp(dp->name, mi.label.name) == 0) 203 break; 204 if (dp->code >= 0) { 205 di->devmap[dp->code] = i; 206 di->rdevmap[i] = dp->code; 207 di->devmask |= 1 << dp->code; 208 if (mi.un.v.num_channels == 2) 209 di->stereomask |= 1 << dp->code; 210 strncpy(di->names[i], mi.label.name, 211 sizeof di->names[i]); 212 } 213 break; 214 } 215 } 216 for(i = 0; i < MAX_MIXER_DEVS; i++) { 217 mi.index = i; 218 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) == -1) 219 break; 220 if (strcmp(mi.label.name, AudioNsource) != 0) 221 continue; 222 cl.index = mi.mixer_class; 223 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) == -1) 224 break; 225 if ((cl.type != AUDIO_MIXER_CLASS) || 226 (strcmp(cl.label.name, AudioCrecord) != 0)) 227 continue; 228 di->recsource = i; 229 switch(mi.type) { 230 case AUDIO_MIXER_ENUM: 231 for(j = 0; j < mi.un.e.num_mem; j++) { 232 e = opaque_to_enum(di, 233 &mi.un.e.member[j].label, 234 mi.un.e.member[j].ord); 235 if (e >= 0) 236 di->recmask |= 1 << di->rdevmap[e]; 237 } 238 di->caps = SOUND_CAP_EXCL_INPUT; 239 break; 240 case AUDIO_MIXER_SET: 241 for(j = 0; j < mi.un.s.num_mem; j++) { 242 e = opaque_to_enum(di, 243 &mi.un.s.member[j].label, 244 mi.un.s.member[j].mask); 245 if (e >= 0) 246 di->recmask |= 1 << di->rdevmap[e]; 247 } 248 break; 249 } 250 } 251 return di; 252 } 253 254 int 255 mixer_ioctl(int fd, unsigned long com, void *argp) 256 { 257 struct audiodevinfo *di; 258 struct mixer_info *omi; 259 struct audio_device adev; 260 mixer_ctrl_t mc; 261 int idat = 0; 262 int i; 263 int retval; 264 int l, r, n, error, e; 265 266 di = getdevinfo(fd); 267 if (di == 0) 268 return -1; 269 270 switch (com) { 271 case OSS_GETVERSION: 272 idat = SOUND_VERSION; 273 break; 274 case SOUND_MIXER_INFO: 275 case SOUND_OLD_MIXER_INFO: 276 error = ioctl(fd, AUDIO_GETDEV, &adev); 277 if (error == -1) 278 return (error); 279 omi = argp; 280 if (com == SOUND_MIXER_INFO) 281 omi->modify_counter = 1; 282 strncpy(omi->id, adev.name, sizeof omi->id); 283 strncpy(omi->name, adev.name, sizeof omi->name); 284 return 0; 285 case SOUND_MIXER_READ_RECSRC: 286 if (di->recsource == -1) 287 return EINVAL; 288 mc.dev = di->recsource; 289 if (di->caps & SOUND_CAP_EXCL_INPUT) { 290 mc.type = AUDIO_MIXER_ENUM; 291 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 292 if (retval == -1) 293 return retval; 294 e = opaque_to_enum(di, NULL, mc.un.ord); 295 if (e >= 0) 296 idat = 1 << di->rdevmap[e]; 297 } else { 298 mc.type = AUDIO_MIXER_SET; 299 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 300 if (retval == -1) 301 return retval; 302 e = opaque_to_enum(di, NULL, mc.un.mask); 303 if (e >= 0) 304 idat = 1 << di->rdevmap[e]; 305 } 306 break; 307 case SOUND_MIXER_READ_DEVMASK: 308 idat = di->devmask; 309 break; 310 case SOUND_MIXER_READ_RECMASK: 311 idat = di->recmask; 312 break; 313 case SOUND_MIXER_READ_STEREODEVS: 314 idat = di->stereomask; 315 break; 316 case SOUND_MIXER_READ_CAPS: 317 idat = di->caps; 318 break; 319 case SOUND_MIXER_WRITE_RECSRC: 320 case SOUND_MIXER_WRITE_R_RECSRC: 321 if (di->recsource == -1) 322 return EINVAL; 323 mc.dev = di->recsource; 324 idat = INTARG; 325 if (di->caps & SOUND_CAP_EXCL_INPUT) { 326 mc.type = AUDIO_MIXER_ENUM; 327 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 328 if (idat & (1 << i)) 329 break; 330 if (i >= SOUND_MIXER_NRDEVICES || 331 di->devmap[i] == -1) 332 return EINVAL; 333 mc.un.ord = enum_to_ord(di, di->devmap[i]); 334 } else { 335 mc.type = AUDIO_MIXER_SET; 336 mc.un.mask = 0; 337 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 338 if (idat & (1 << i)) { 339 if (di->devmap[i] == -1) 340 return EINVAL; 341 mc.un.mask |= enum_to_mask(di, di->devmap[i]); 342 } 343 } 344 } 345 return ioctl(fd, AUDIO_MIXER_WRITE, &mc); 346 default: 347 if (MIXER_READ(SOUND_MIXER_FIRST) <= com && 348 com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { 349 n = GET_DEV(com); 350 if (di->devmap[n] == -1) 351 return EINVAL; 352 mc.dev = di->devmap[n]; 353 mc.type = AUDIO_MIXER_VALUE; 354 doread: 355 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 356 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 357 if (retval == -1) 358 return retval; 359 if (mc.type != AUDIO_MIXER_VALUE) 360 return EINVAL; 361 if (mc.un.value.num_channels != 2) { 362 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 363 } else { 364 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 365 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 366 } 367 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 368 break; 369 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && 370 com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || 371 (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 372 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { 373 n = GET_DEV(com); 374 if (di->devmap[n] == -1) 375 return EINVAL; 376 idat = INTARG; 377 l = FROM_OSSVOL( idat & 0xff); 378 r = FROM_OSSVOL((idat >> 8) & 0xff); 379 mc.dev = di->devmap[n]; 380 mc.type = AUDIO_MIXER_VALUE; 381 if (di->stereomask & (1<<n)) { 382 mc.un.value.num_channels = 2; 383 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 384 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 385 } else { 386 mc.un.value.num_channels = 1; 387 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 388 } 389 retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); 390 if (retval == -1) 391 return retval; 392 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 393 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 394 return 0; 395 goto doread; 396 } else { 397 errno = EINVAL; 398 return -1; 399 } 400 } 401 INTARG = idat; 402 return 0; 403 } 404