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