1 /* $OpenBSD: ossaudio.c,v 1.18 2015/04/19 08:42:19 ratchov 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 /* XXX This file is essentially the same as sys/compat/ossaudio.c. 36 * With some preprocessor magic it could be the same file. 37 */ 38 39 #include <stdarg.h> 40 #include <string.h> 41 #include <sys/types.h> 42 #include <sys/ioctl.h> 43 #include <sys/audioio.h> 44 #include <sys/stat.h> 45 #include <errno.h> 46 47 #include "soundcard.h" 48 #undef ioctl 49 50 #define GET_DEV(com) ((com) & 0xff) 51 52 #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) 53 #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) 54 55 static struct audiodevinfo *getdevinfo(int); 56 57 static int mixer_ioctl(int, unsigned long, void *); 58 static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); 59 static int enum_to_ord(struct audiodevinfo *di, int enm); 60 static int enum_to_mask(struct audiodevinfo *di, int enm); 61 62 #define INTARG (*(int*)argp) 63 64 int 65 _oss_ioctl(int fd, unsigned long com, ...) 66 { 67 va_list ap; 68 void *argp; 69 70 va_start(ap, com); 71 argp = va_arg(ap, void *); 72 va_end(ap); 73 if (IOCGROUP(com) == 'P') 74 return ENOTTY; 75 else if (IOCGROUP(com) == 'M') 76 return mixer_ioctl(fd, com, argp); 77 else 78 return ioctl(fd, com, argp); 79 } 80 81 /* If the mixer device should have more than MAX_MIXER_DEVS devices 82 * some will not be available to Linux */ 83 #define MAX_MIXER_DEVS 64 84 struct audiodevinfo { 85 int done; 86 dev_t dev; 87 ino_t ino; 88 int16_t devmap[SOUND_MIXER_NRDEVICES], 89 rdevmap[MAX_MIXER_DEVS]; 90 char names[MAX_MIXER_DEVS][MAX_AUDIO_DEV_LEN]; 91 int enum2opaque[MAX_MIXER_DEVS]; 92 u_long devmask, recmask, stereomask; 93 u_long caps, recsource; 94 }; 95 96 static int 97 opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) 98 { 99 int i, o; 100 101 for (i = 0; i < MAX_MIXER_DEVS; i++) { 102 o = di->enum2opaque[i]; 103 if (o == opq) 104 break; 105 if (o == -1 && label != NULL && 106 !strncmp(di->names[i], label->name, sizeof di->names[i])) { 107 di->enum2opaque[i] = opq; 108 break; 109 } 110 } 111 if (i >= MAX_MIXER_DEVS) 112 i = -1; 113 /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ 114 return (i); 115 } 116 117 static int 118 enum_to_ord(struct audiodevinfo *di, int enm) 119 { 120 if (enm >= MAX_MIXER_DEVS) 121 return (-1); 122 123 /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ 124 return (di->enum2opaque[enm]); 125 } 126 127 static int 128 enum_to_mask(struct audiodevinfo *di, int enm) 129 { 130 int m; 131 if (enm >= MAX_MIXER_DEVS) 132 return (0); 133 134 m = di->enum2opaque[enm]; 135 if (m == -1) 136 m = 0; 137 /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ 138 return (m); 139 } 140 141 /* 142 * Collect the audio device information to allow faster 143 * emulation of the Linux mixer ioctls. Cache the information 144 * to eliminate the overhead of repeating all the ioctls needed 145 * to collect the information. 146 */ 147 static struct audiodevinfo * 148 getdevinfo(int fd) 149 { 150 mixer_devinfo_t mi, cl; 151 int i, j, e; 152 static struct { 153 char *name; 154 int code; 155 } *dp, devs[] = { 156 { AudioNmicrophone, SOUND_MIXER_MIC }, 157 { AudioNline, SOUND_MIXER_LINE }, 158 { AudioNcd, SOUND_MIXER_CD }, 159 { AudioNdac, SOUND_MIXER_PCM }, 160 { AudioNaux, SOUND_MIXER_LINE1 }, 161 { AudioNrecord, SOUND_MIXER_IMIX }, 162 { AudioNmaster, SOUND_MIXER_VOLUME }, 163 { AudioNtreble, SOUND_MIXER_TREBLE }, 164 { AudioNbass, SOUND_MIXER_BASS }, 165 { AudioNspeaker, SOUND_MIXER_SPEAKER }, 166 { AudioNoutput, SOUND_MIXER_OGAIN }, 167 { AudioNinput, SOUND_MIXER_IGAIN }, 168 { AudioNfmsynth, SOUND_MIXER_SYNTH }, 169 { AudioNmidi, SOUND_MIXER_SYNTH }, 170 { 0, -1 } 171 }; 172 static struct audiodevinfo devcache = { 0 }; 173 struct audiodevinfo *di = &devcache; 174 struct stat sb; 175 176 /* Figure out what device it is so we can check if the 177 * cached data is valid. 178 */ 179 if (fstat(fd, &sb) < 0) 180 return 0; 181 if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino)) 182 return di; 183 184 di->done = 1; 185 di->dev = sb.st_dev; 186 di->ino = sb.st_ino; 187 di->devmask = 0; 188 di->recmask = 0; 189 di->stereomask = 0; 190 di->recsource = ~0; 191 di->caps = 0; 192 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 193 di->devmap[i] = -1; 194 for(i = 0; i < MAX_MIXER_DEVS; i++) { 195 di->rdevmap[i] = -1; 196 di->names[i][0] = '\0'; 197 di->enum2opaque[i] = -1; 198 } 199 for(i = 0; i < MAX_MIXER_DEVS; i++) { 200 mi.index = i; 201 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 202 break; 203 switch(mi.type) { 204 case AUDIO_MIXER_VALUE: 205 for(dp = devs; dp->name; dp++) 206 if (strcmp(dp->name, mi.label.name) == 0) 207 break; 208 if (dp->code >= 0) { 209 di->devmap[dp->code] = i; 210 di->rdevmap[i] = dp->code; 211 di->devmask |= 1 << dp->code; 212 if (mi.un.v.num_channels == 2) 213 di->stereomask |= 1 << dp->code; 214 strncpy(di->names[i], mi.label.name, 215 sizeof di->names[i]); 216 } 217 break; 218 } 219 } 220 for(i = 0; i < MAX_MIXER_DEVS; i++) { 221 mi.index = i; 222 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 223 break; 224 if (strcmp(mi.label.name, AudioNsource) != 0) 225 continue; 226 cl.index = mi.mixer_class; 227 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) < 0) 228 break; 229 if ((cl.type != AUDIO_MIXER_CLASS) || 230 (strcmp(cl.label.name, AudioCrecord) != 0)) 231 continue; 232 di->recsource = i; 233 switch(mi.type) { 234 case AUDIO_MIXER_ENUM: 235 for(j = 0; j < mi.un.e.num_mem; j++) { 236 e = opaque_to_enum(di, 237 &mi.un.e.member[j].label, 238 mi.un.e.member[j].ord); 239 if (e >= 0) 240 di->recmask |= 1 << di->rdevmap[e]; 241 } 242 di->caps = SOUND_CAP_EXCL_INPUT; 243 break; 244 case AUDIO_MIXER_SET: 245 for(j = 0; j < mi.un.s.num_mem; j++) { 246 e = opaque_to_enum(di, 247 &mi.un.s.member[j].label, 248 mi.un.s.member[j].mask); 249 if (e >= 0) 250 di->recmask |= 1 << di->rdevmap[e]; 251 } 252 break; 253 } 254 } 255 return di; 256 } 257 258 int 259 mixer_ioctl(int fd, unsigned long com, void *argp) 260 { 261 struct audiodevinfo *di; 262 struct mixer_info *omi; 263 struct audio_device adev; 264 mixer_ctrl_t mc; 265 int idat = 0; 266 int i; 267 int retval; 268 int l, r, n, error, e; 269 270 di = getdevinfo(fd); 271 if (di == 0) 272 return -1; 273 274 switch (com) { 275 case OSS_GETVERSION: 276 idat = SOUND_VERSION; 277 break; 278 case SOUND_MIXER_INFO: 279 case SOUND_OLD_MIXER_INFO: 280 error = ioctl(fd, AUDIO_GETDEV, &adev); 281 if (error) 282 return (error); 283 omi = argp; 284 if (com == SOUND_MIXER_INFO) 285 omi->modify_counter = 1; 286 strncpy(omi->id, adev.name, sizeof omi->id); 287 strncpy(omi->name, adev.name, sizeof omi->name); 288 return 0; 289 case SOUND_MIXER_READ_RECSRC: 290 if (di->recsource == -1) 291 return EINVAL; 292 mc.dev = di->recsource; 293 if (di->caps & SOUND_CAP_EXCL_INPUT) { 294 mc.type = AUDIO_MIXER_ENUM; 295 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 296 if (retval < 0) 297 return retval; 298 e = opaque_to_enum(di, NULL, mc.un.ord); 299 if (e >= 0) 300 idat = 1 << di->rdevmap[e]; 301 } else { 302 mc.type = AUDIO_MIXER_SET; 303 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 304 if (retval < 0) 305 return retval; 306 e = opaque_to_enum(di, NULL, mc.un.mask); 307 if (e >= 0) 308 idat = 1 << di->rdevmap[e]; 309 } 310 break; 311 case SOUND_MIXER_READ_DEVMASK: 312 idat = di->devmask; 313 break; 314 case SOUND_MIXER_READ_RECMASK: 315 idat = di->recmask; 316 break; 317 case SOUND_MIXER_READ_STEREODEVS: 318 idat = di->stereomask; 319 break; 320 case SOUND_MIXER_READ_CAPS: 321 idat = di->caps; 322 break; 323 case SOUND_MIXER_WRITE_RECSRC: 324 case SOUND_MIXER_WRITE_R_RECSRC: 325 if (di->recsource == -1) 326 return EINVAL; 327 mc.dev = di->recsource; 328 idat = INTARG; 329 if (di->caps & SOUND_CAP_EXCL_INPUT) { 330 mc.type = AUDIO_MIXER_ENUM; 331 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 332 if (idat & (1 << i)) 333 break; 334 if (i >= SOUND_MIXER_NRDEVICES || 335 di->devmap[i] == -1) 336 return EINVAL; 337 mc.un.ord = enum_to_ord(di, di->devmap[i]); 338 } else { 339 mc.type = AUDIO_MIXER_SET; 340 mc.un.mask = 0; 341 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 342 if (idat & (1 << i)) { 343 if (di->devmap[i] == -1) 344 return EINVAL; 345 mc.un.mask |= enum_to_mask(di, di->devmap[i]); 346 } 347 } 348 } 349 return ioctl(fd, AUDIO_MIXER_WRITE, &mc); 350 default: 351 if (MIXER_READ(SOUND_MIXER_FIRST) <= com && 352 com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { 353 n = GET_DEV(com); 354 if (di->devmap[n] == -1) 355 return EINVAL; 356 mc.dev = di->devmap[n]; 357 mc.type = AUDIO_MIXER_VALUE; 358 doread: 359 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 360 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 361 if (retval < 0) 362 return retval; 363 if (mc.type != AUDIO_MIXER_VALUE) 364 return EINVAL; 365 if (mc.un.value.num_channels != 2) { 366 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 367 } else { 368 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 369 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 370 } 371 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 372 break; 373 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && 374 com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || 375 (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 376 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { 377 n = GET_DEV(com); 378 if (di->devmap[n] == -1) 379 return EINVAL; 380 idat = INTARG; 381 l = FROM_OSSVOL( idat & 0xff); 382 r = FROM_OSSVOL((idat >> 8) & 0xff); 383 mc.dev = di->devmap[n]; 384 mc.type = AUDIO_MIXER_VALUE; 385 if (di->stereomask & (1<<n)) { 386 mc.un.value.num_channels = 2; 387 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 388 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 389 } else { 390 mc.un.value.num_channels = 1; 391 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 392 } 393 retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); 394 if (retval < 0) 395 return retval; 396 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 397 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 398 return 0; 399 goto doread; 400 } else { 401 errno = EINVAL; 402 return -1; 403 } 404 } 405 INTARG = idat; 406 return 0; 407 } 408