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