1 1.1 nia /* $NetBSD: oss4_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) 2020-2021 The NetBSD Foundation, Inc. 5 1.1 nia * All rights reserved. 6 1.1 nia * 7 1.1 nia * This code is derived from software contributed to The NetBSD Foundation 8 1.1 nia * by Nia Alarie. 9 1.1 nia * 10 1.1 nia * Redistribution and use in source and binary forms, with or without 11 1.1 nia * modification, are permitted provided that the following conditions 12 1.1 nia * are met: 13 1.1 nia * 1. Redistributions of source code must retain the above copyright 14 1.1 nia * notice, this list of conditions and the following disclaimer. 15 1.1 nia * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 nia * notice, this list of conditions and the following disclaimer in the 17 1.1 nia * documentation and/or other materials provided with the distribution. 18 1.1 nia * 19 1.1 nia * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 nia * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 nia * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 nia * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 nia * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 nia * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 nia * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 nia * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 nia * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 nia * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 nia * POSSIBILITY OF SUCH DAMAGE. 30 1.1 nia */ 31 1.1 nia #include <sys/audioio.h> 32 1.1 nia #include <sys/fcntl.h> 33 1.1 nia #include <sys/stat.h> 34 1.1 nia #include <errno.h> 35 1.1 nia #include <limits.h> 36 1.1 nia #include <stdio.h> 37 1.1 nia #include <unistd.h> 38 1.1 nia #include "internal.h" 39 1.1 nia 40 1.1 nia static int get_audio_count(void); 41 1.1 nia static int get_mixer_count(void); 42 1.1 nia static int get_mixer_control_count(int); 43 1.1 nia 44 1.1 nia oss_private int 45 1.1 nia _oss4_mixer_ioctl(int fd, unsigned long com, void *argp) 46 1.1 nia { 47 1.1 nia oss_audioinfo *tmpai; 48 1.1 nia oss_card_info *cardinfo; 49 1.1 nia oss_mixext *ext; 50 1.1 nia oss_mixext_root root; 51 1.1 nia oss_mixer_enuminfo *ei; 52 1.1 nia oss_mixer_value *mv; 53 1.1 nia oss_mixerinfo *mi; 54 1.1 nia oss_sysinfo sysinfo; 55 1.1 nia dev_t devno; 56 1.1 nia struct stat tmpstat; 57 1.1 nia struct audio_device dev; 58 1.1 nia struct audio_format_query fmtq; 59 1.1 nia struct mixer_devinfo mdi; 60 1.1 nia struct mixer_ctrl mc; 61 1.1 nia char devname[32]; 62 1.1 nia size_t len; 63 1.1 nia int newfd = -1, tmperrno; 64 1.1 nia int i, noffs; 65 1.1 nia int retval; 66 1.1 nia 67 1.1 nia /* 68 1.1 nia * Note: it is difficult to translate the NetBSD concept of a "set" 69 1.1 nia * mixer control type to the OSSv4 API, as far as I can tell. 70 1.1 nia * 71 1.1 nia * This means they are treated like enums, i.e. only one entry in the 72 1.1 nia * set can be selected at a time. 73 1.1 nia */ 74 1.1 nia 75 1.1 nia switch (com) { 76 1.1 nia case SNDCTL_AUDIOINFO: 77 1.1 nia /* 78 1.1 nia * SNDCTL_AUDIOINFO_EX is intended for underlying hardware devices 79 1.1 nia * that are to be opened in "exclusive mode" (bypassing the normal 80 1.1 nia * kernel mixer for exclusive control). NetBSD does not support 81 1.1 nia * bypassing the kernel mixer, so it's an alias of SNDCTL_AUDIOINFO. 82 1.1 nia */ 83 1.1 nia case SNDCTL_AUDIOINFO_EX: 84 1.1 nia case SNDCTL_ENGINEINFO: 85 1.1 nia devno = 0; 86 1.1 nia tmpai = (struct oss_audioinfo*)argp; 87 1.1 nia if (tmpai == NULL) { 88 1.1 nia errno = EINVAL; 89 1.1 nia return -1; 90 1.1 nia } 91 1.1 nia 92 1.1 nia /* 93 1.1 nia * If the input device is -1, guess the device related to 94 1.1 nia * the open mixer device. 95 1.1 nia */ 96 1.1 nia if (tmpai->dev < 0) { 97 1.1 nia fstat(fd, &tmpstat); 98 1.1 nia if ((tmpstat.st_rdev & 0xff00) == 0x2a00) 99 1.1 nia devno = tmpstat.st_rdev & 0xff; 100 1.1 nia if (devno >= 0x80) 101 1.1 nia tmpai->dev = devno & 0x7f; 102 1.1 nia } 103 1.1 nia if (tmpai->dev < 0) 104 1.1 nia tmpai->dev = 0; 105 1.1 nia 106 1.1 nia snprintf(tmpai->devnode, sizeof(tmpai->devnode), 107 1.1 nia "/dev/audio%d", tmpai->dev); 108 1.1 nia 109 1.1 nia if ((newfd = open(tmpai->devnode, O_WRONLY)) < 0) { 110 1.1 nia if ((newfd = open(tmpai->devnode, O_RDONLY)) < 0) { 111 1.1 nia return newfd; 112 1.1 nia } 113 1.1 nia } 114 1.1 nia 115 1.1 nia retval = ioctl(newfd, AUDIO_GETDEV, &dev); 116 1.1 nia if (retval < 0) { 117 1.1 nia tmperrno = errno; 118 1.1 nia close(newfd); 119 1.1 nia errno = tmperrno; 120 1.1 nia return retval; 121 1.1 nia } 122 1.1 nia if (_oss_get_caps(newfd, &tmpai->caps) < 0) { 123 1.1 nia tmperrno = errno; 124 1.1 nia close(newfd); 125 1.1 nia errno = tmperrno; 126 1.1 nia return retval; 127 1.1 nia } 128 1.1 nia snprintf(tmpai->name, sizeof(tmpai->name), 129 1.1 nia "%s %s", dev.name, dev.version); 130 1.1 nia tmpai->busy = 0; 131 1.1 nia tmpai->pid = -1; 132 1.1 nia _oss_dsp_ioctl(newfd, SNDCTL_DSP_GETFMTS, &tmpai->iformats); 133 1.1 nia tmpai->oformats = tmpai->iformats; 134 1.1 nia tmpai->magic = -1; /* reserved for "internal use" */ 135 1.1 nia memset(tmpai->cmd, 0, sizeof(tmpai->cmd)); 136 1.1 nia tmpai->card_number = -1; 137 1.1 nia memset(tmpai->song_name, 0, 138 1.1 nia sizeof(tmpai->song_name)); 139 1.1 nia memset(tmpai->label, 0, sizeof(tmpai->label)); 140 1.1 nia tmpai->port_number = 0; 141 1.1 nia tmpai->mixer_dev = tmpai->dev; 142 1.1 nia tmpai->legacy_device = tmpai->dev; 143 1.1 nia tmpai->enabled = 1; 144 1.1 nia tmpai->flags = -1; /* reserved for "future versions" */ 145 1.1 nia tmpai->min_rate = 1000; 146 1.1 nia tmpai->max_rate = 192000; 147 1.1 nia tmpai->nrates = 0; 148 1.1 nia tmpai->min_channels = 1; 149 1.1 nia tmpai->max_channels = 2; 150 1.1 nia for (fmtq.index = 0; 151 1.1 nia ioctl(newfd, AUDIO_QUERYFORMAT, &fmtq) != -1; ++fmtq.index) { 152 1.1 nia if (fmtq.fmt.channels > (unsigned)tmpai->max_channels) 153 1.1 nia tmpai->max_channels = fmtq.fmt.channels; 154 1.1 nia } 155 1.1 nia tmpai->binding = -1; /* reserved for "future versions" */ 156 1.1 nia tmpai->rate_source = -1; 157 1.1 nia /* 158 1.1 nia * 'handle' is supposed to be globally unique. The closest 159 1.1 nia * we have to that is probably device nodes. 160 1.1 nia */ 161 1.1 nia strlcpy(tmpai->handle, tmpai->devnode, 162 1.1 nia sizeof(tmpai->handle)); 163 1.1 nia tmpai->next_play_engine = 0; 164 1.1 nia tmpai->next_rec_engine = 0; 165 1.1 nia argp = tmpai; 166 1.1 nia close(newfd); 167 1.1 nia break; 168 1.1 nia case SNDCTL_CARDINFO: 169 1.1 nia cardinfo = (oss_card_info *)argp; 170 1.1 nia if (cardinfo == NULL) { 171 1.1 nia errno = EINVAL; 172 1.1 nia return -1; 173 1.1 nia } 174 1.1 nia if (cardinfo->card != -1) { 175 1.1 nia snprintf(devname, sizeof(devname), 176 1.1 nia "/dev/audio%d", cardinfo->card); 177 1.1 nia newfd = open(devname, O_RDONLY); 178 1.1 nia if (newfd < 0) 179 1.1 nia return newfd; 180 1.1 nia } else { 181 1.1 nia newfd = fd; 182 1.1 nia } 183 1.1 nia retval = ioctl(newfd, AUDIO_GETDEV, &dev); 184 1.1 nia tmperrno = errno; 185 1.1 nia if (newfd != fd) 186 1.1 nia close(newfd); 187 1.1 nia if (retval < 0) { 188 1.1 nia errno = tmperrno; 189 1.1 nia return retval; 190 1.1 nia } 191 1.1 nia strlcpy(cardinfo->shortname, dev.name, 192 1.1 nia sizeof(cardinfo->shortname)); 193 1.1 nia snprintf(cardinfo->longname, sizeof(cardinfo->longname), 194 1.1 nia "%s %s %s", dev.name, dev.version, dev.config); 195 1.1 nia memset(cardinfo->hw_info, 0, sizeof(cardinfo->hw_info)); 196 1.1 nia /* 197 1.1 nia * OSSv4 does not document this ioctl, and claims it should 198 1.1 nia * not be used by applications and is provided for "utiltiy 199 1.1 nia * programs included in OSS". We follow the Solaris 200 1.1 nia * implementation (which is documented) and leave these fields 201 1.1 nia * unset. 202 1.1 nia */ 203 1.1 nia cardinfo->flags = 0; 204 1.1 nia cardinfo->intr_count = 0; 205 1.1 nia cardinfo->ack_count = 0; 206 1.1 nia break; 207 1.1 nia case SNDCTL_SYSINFO: 208 1.1 nia memset(&sysinfo, 0, sizeof(sysinfo)); 209 1.1 nia strlcpy(sysinfo.product, 210 1.1 nia "OSS/NetBSD", sizeof(sysinfo.product)); 211 1.1 nia strlcpy(sysinfo.version, 212 1.1 nia "4.01", sizeof(sysinfo.version)); 213 1.1 nia strlcpy(sysinfo.license, 214 1.1 nia "BSD", sizeof(sysinfo.license)); 215 1.1 nia sysinfo.versionnum = SOUND_VERSION; 216 1.1 nia sysinfo.numaudios = 217 1.1 nia sysinfo.numcards = 218 1.1 nia get_audio_count(); 219 1.1 nia sysinfo.numaudioengines = 1; 220 1.1 nia sysinfo.numsynths = 1; 221 1.1 nia sysinfo.nummidis = -1; 222 1.1 nia sysinfo.numtimers = -1; 223 1.1 nia sysinfo.nummixers = get_mixer_count(); 224 1.1 nia *(struct oss_sysinfo *)argp = sysinfo; 225 1.1 nia break; 226 1.1 nia case SNDCTL_MIXERINFO: 227 1.1 nia mi = (oss_mixerinfo *)argp; 228 1.1 nia if (mi == NULL) { 229 1.1 nia errno = EINVAL; 230 1.1 nia return -1; 231 1.1 nia } 232 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", mi->dev); 233 1.1 nia if ((newfd = open(devname, O_RDONLY)) < 0) 234 1.1 nia return newfd; 235 1.1 nia retval = ioctl(newfd, AUDIO_GETDEV, &dev); 236 1.1 nia if (retval < 0) { 237 1.1 nia tmperrno = errno; 238 1.1 nia close(newfd); 239 1.1 nia errno = tmperrno; 240 1.1 nia return retval; 241 1.1 nia } 242 1.1 nia strlcpy(mi->id, devname, sizeof(mi->id)); 243 1.1 nia strlcpy(mi->handle, devname, sizeof(mi->handle)); 244 1.1 nia snprintf(mi->name, sizeof(mi->name), 245 1.1 nia "%s %s", dev.name, dev.version); 246 1.1 nia mi->card_number = mi->dev; 247 1.1 nia mi->port_number = 0; 248 1.1 nia mi->magic = 0; 249 1.1 nia mi->enabled = 1; 250 1.1 nia mi->caps = 0; 251 1.1 nia mi->flags = 0; 252 1.1 nia mi->nrext = get_mixer_control_count(newfd) + 1; 253 1.1 nia mi->priority = UCHAR_MAX - mi->dev; 254 1.1 nia strlcpy(mi->devnode, devname, sizeof(mi->devnode)); 255 1.1 nia mi->legacy_device = mi->dev; 256 1.1 nia break; 257 1.1 nia case SNDCTL_MIX_DESCRIPTION: 258 1.1 nia /* No description available. */ 259 1.1 nia errno = ENOSYS; 260 1.1 nia return -1; 261 1.1 nia case SNDCTL_MIX_NRMIX: 262 1.1 nia INTARG = get_mixer_count(); 263 1.1 nia break; 264 1.1 nia case SNDCTL_MIX_NREXT: 265 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", INTARG); 266 1.1 nia if ((newfd = open(devname, O_RDONLY)) < 0) 267 1.1 nia return newfd; 268 1.1 nia INTARG = get_mixer_control_count(newfd) + 1; 269 1.1 nia close(newfd); 270 1.1 nia break; 271 1.1 nia case SNDCTL_MIX_EXTINFO: 272 1.1 nia ext = (oss_mixext *)argp; 273 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", ext->dev); 274 1.1 nia if ((newfd = open(devname, O_RDONLY)) < 0) 275 1.1 nia return newfd; 276 1.1 nia if (ext->ctrl == 0) { 277 1.1 nia /* 278 1.1 nia * NetBSD has no concept of a "root mixer control", but 279 1.1 nia * OSSv4 requires one to work. We fake one at 0 and 280 1.1 nia * simply add 1 to all real control indexes. 281 1.1 nia */ 282 1.1 nia retval = ioctl(newfd, AUDIO_GETDEV, &dev); 283 1.1 nia tmperrno = errno; 284 1.1 nia close(newfd); 285 1.1 nia if (retval < 0) { 286 1.1 nia errno = tmperrno; 287 1.1 nia return -1; 288 1.1 nia } 289 1.1 nia memset(&root, 0, sizeof(root)); 290 1.1 nia strlcpy(root.id, devname, sizeof(root.id)); 291 1.1 nia snprintf(root.name, sizeof(root.name), 292 1.1 nia "%s %s", dev.name, dev.version); 293 1.1 nia strlcpy(ext->id, devname, sizeof(ext->id)); 294 1.1 nia snprintf(ext->extname, sizeof(ext->extname), 295 1.1 nia "%s %s", dev.name, dev.version); 296 1.1 nia strlcpy(ext->extname, "root", sizeof(ext->extname)); 297 1.1 nia ext->type = MIXT_DEVROOT; 298 1.1 nia ext->minvalue = 0; 299 1.1 nia ext->maxvalue = 0; 300 1.1 nia ext->flags = 0; 301 1.1 nia ext->parent = -1; 302 1.1 nia ext->control_no = -1; 303 1.1 nia ext->update_counter = 0; 304 1.1 nia ext->rgbcolor = 0; 305 1.1 nia memcpy(&ext->data, &root, 306 1.1 nia sizeof(root) > sizeof(ext->data) ? 307 1.1 nia sizeof(ext->data) : sizeof(root)); 308 1.1 nia return 0; 309 1.1 nia } 310 1.1 nia mdi.index = ext->ctrl - 1; 311 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); 312 1.1 nia if (retval < 0) { 313 1.1 nia tmperrno = errno; 314 1.1 nia close(newfd); 315 1.1 nia errno = tmperrno; 316 1.1 nia return retval; 317 1.1 nia } 318 1.1 nia ext->flags = MIXF_READABLE | MIXF_WRITEABLE | MIXF_POLL; 319 1.1 nia ext->parent = mdi.mixer_class + 1; 320 1.1 nia strlcpy(ext->id, mdi.label.name, sizeof(ext->id)); 321 1.1 nia strlcpy(ext->extname, mdi.label.name, sizeof(ext->extname)); 322 1.1 nia len = strlen(ext->extname); 323 1.1 nia memset(ext->data, 0, sizeof(ext->data)); 324 1.1 nia ext->control_no = -1; 325 1.1 nia ext->update_counter = 0; 326 1.1 nia ext->rgbcolor = 0; 327 1.1 nia switch (mdi.type) { 328 1.1 nia case AUDIO_MIXER_CLASS: 329 1.1 nia ext->type = MIXT_GROUP; 330 1.1 nia ext->parent = 0; 331 1.1 nia ext->minvalue = 0; 332 1.1 nia ext->maxvalue = 0; 333 1.1 nia break; 334 1.1 nia case AUDIO_MIXER_ENUM: 335 1.1 nia ext->maxvalue = mdi.un.e.num_mem; 336 1.1 nia ext->minvalue = 0; 337 1.1 nia for (i = 0; i < mdi.un.e.num_mem; ++i) { 338 1.1 nia ext->enum_present[i / 8] |= (1 << (i % 8)); 339 1.1 nia } 340 1.1 nia if (mdi.un.e.num_mem == 2) { 341 1.1 nia if (!strcmp(mdi.un.e.member[0].label.name, AudioNoff) && 342 1.1 nia !strcmp(mdi.un.e.member[1].label.name, AudioNon)) { 343 1.1 nia ext->type = MIXT_MUTE; 344 1.1 nia } else { 345 1.1 nia ext->type = MIXT_ENUM; 346 1.1 nia } 347 1.1 nia } else { 348 1.1 nia ext->type = MIXT_ENUM; 349 1.1 nia } 350 1.1 nia break; 351 1.1 nia case AUDIO_MIXER_SET: 352 1.1 nia ext->maxvalue = mdi.un.s.num_mem; 353 1.1 nia ext->minvalue = 0; 354 1.1 nia #ifdef notyet 355 1.1 nia /* 356 1.1 nia * XXX: This is actually the correct type for "set" 357 1.1 nia * controls, but it seems no real world software 358 1.1 nia * supports it. The only documentation exists in 359 1.1 nia * the OSSv4 headers and describes it as "reserved 360 1.1 nia * for Sun's implementation". 361 1.1 nia */ 362 1.1 nia ext->type = MIXT_ENUM_MULTI; 363 1.1 nia #else 364 1.1 nia ext->type = MIXT_ENUM; 365 1.1 nia #endif 366 1.1 nia for (i = 0; i < mdi.un.s.num_mem; ++i) { 367 1.1 nia ext->enum_present[i / 8] |= (1 << (i % 8)); 368 1.1 nia } 369 1.1 nia break; 370 1.1 nia case AUDIO_MIXER_VALUE: 371 1.1 nia ext->maxvalue = UCHAR_MAX + 1; 372 1.1 nia ext->minvalue = 0; 373 1.1 nia if (mdi.un.v.num_channels == 2) { 374 1.1 nia ext->type = MIXT_STEREOSLIDER; 375 1.1 nia } else { 376 1.1 nia ext->type = MIXT_MONOSLIDER; 377 1.1 nia } 378 1.1 nia break; 379 1.1 nia } 380 1.1 nia close(newfd); 381 1.1 nia break; 382 1.1 nia case SNDCTL_MIX_ENUMINFO: 383 1.1 nia ei = (oss_mixer_enuminfo *)argp; 384 1.1 nia if (ei == NULL) { 385 1.1 nia errno = EINVAL; 386 1.1 nia return -1; 387 1.1 nia } 388 1.1 nia if (ei->ctrl == 0) { 389 1.1 nia errno = EINVAL; 390 1.1 nia return -1; 391 1.1 nia } 392 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", ei->dev); 393 1.1 nia if ((newfd = open(devname, O_RDONLY)) < 0) 394 1.1 nia return newfd; 395 1.1 nia mdi.index = ei->ctrl - 1; 396 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); 397 1.1 nia tmperrno = errno; 398 1.1 nia close(newfd); 399 1.1 nia if (retval < 0) { 400 1.1 nia errno = tmperrno; 401 1.1 nia return retval; 402 1.1 nia } 403 1.1 nia ei->version = 0; 404 1.1 nia switch (mdi.type) { 405 1.1 nia case AUDIO_MIXER_ENUM: 406 1.1 nia ei->nvalues = mdi.un.e.num_mem; 407 1.1 nia noffs = 0; 408 1.1 nia for (i = 0; i < ei->nvalues; ++i) { 409 1.1 nia ei->strindex[i] = noffs; 410 1.1 nia len = strlen(mdi.un.e.member[i].label.name) + 1; 411 1.1 nia if ((noffs + len) >= sizeof(ei->strings)) { 412 1.1 nia errno = ENOMEM; 413 1.1 nia return -1; 414 1.1 nia } 415 1.1 nia memcpy(ei->strings + noffs, 416 1.1 nia mdi.un.e.member[i].label.name, len); 417 1.1 nia noffs += len; 418 1.1 nia } 419 1.1 nia break; 420 1.1 nia case AUDIO_MIXER_SET: 421 1.1 nia ei->nvalues = mdi.un.s.num_mem; 422 1.1 nia noffs = 0; 423 1.1 nia for (i = 0; i < ei->nvalues; ++i) { 424 1.1 nia ei->strindex[i] = noffs; 425 1.1 nia len = strlen(mdi.un.s.member[i].label.name) + 1; 426 1.1 nia if ((noffs + len) >= sizeof(ei->strings)) { 427 1.1 nia errno = ENOMEM; 428 1.1 nia return -1; 429 1.1 nia } 430 1.1 nia memcpy(ei->strings + noffs, 431 1.1 nia mdi.un.s.member[i].label.name, len); 432 1.1 nia noffs += len; 433 1.1 nia } 434 1.1 nia break; 435 1.1 nia default: 436 1.1 nia errno = EINVAL; 437 1.1 nia return -1; 438 1.1 nia } 439 1.1 nia break; 440 1.1 nia case SNDCTL_MIX_WRITE: 441 1.1 nia mv = (oss_mixer_value *)argp; 442 1.1 nia if (mv == NULL) { 443 1.1 nia errno = EINVAL; 444 1.1 nia return -1; 445 1.1 nia } 446 1.1 nia if (mv->ctrl == 0) { 447 1.1 nia errno = EINVAL; 448 1.1 nia return -1; 449 1.1 nia } 450 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); 451 1.1 nia if ((newfd = open(devname, O_RDWR)) < 0) 452 1.1 nia return newfd; 453 1.1 nia mdi.index = mc.dev = mv->ctrl - 1; 454 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); 455 1.1 nia if (retval < 0) { 456 1.1 nia tmperrno = errno; 457 1.1 nia close(newfd); 458 1.1 nia errno = tmperrno; 459 1.1 nia return retval; 460 1.1 nia } 461 1.1 nia mc.type = mdi.type; 462 1.1 nia switch (mdi.type) { 463 1.1 nia case AUDIO_MIXER_ENUM: 464 1.1 nia if (mv->value >= mdi.un.e.num_mem) { 465 1.1 nia close(newfd); 466 1.1 nia errno = EINVAL; 467 1.1 nia return -1; 468 1.1 nia } 469 1.1 nia mc.un.ord = mdi.un.e.member[mv->value].ord; 470 1.1 nia break; 471 1.1 nia case AUDIO_MIXER_SET: 472 1.1 nia if (mv->value >= mdi.un.s.num_mem) { 473 1.1 nia close(newfd); 474 1.1 nia errno = EINVAL; 475 1.1 nia return -1; 476 1.1 nia } 477 1.1 nia #ifdef notyet 478 1.1 nia mc.un.mask = 0; 479 1.1 nia for (i = 0; i < mdi.un.s.num_mem; ++i) { 480 1.1 nia if (mv->value & (1 << i)) { 481 1.1 nia mc.un.mask |= mdi.un.s.member[mv->value].mask; 482 1.1 nia } 483 1.1 nia } 484 1.1 nia #else 485 1.1 nia mc.un.mask = mdi.un.s.member[mv->value].mask; 486 1.1 nia #endif 487 1.1 nia break; 488 1.1 nia case AUDIO_MIXER_VALUE: 489 1.1 nia mc.un.value.num_channels = mdi.un.v.num_channels; 490 1.1 nia if (mdi.un.v.num_channels != 2) { 491 1.1 nia for (i = 0; i < mdi.un.v.num_channels; ++i) { 492 1.1 nia mc.un.value.level[i] = mv->value; 493 1.1 nia } 494 1.1 nia } else { 495 1.1 nia mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 496 1.1 nia (mv->value >> 0) & 0xFF; 497 1.1 nia mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 498 1.1 nia (mv->value >> 8) & 0xFF; 499 1.1 nia } 500 1.1 nia break; 501 1.1 nia } 502 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_WRITE, &mc); 503 1.1 nia if (retval < 0) { 504 1.1 nia tmperrno = errno; 505 1.1 nia close(newfd); 506 1.1 nia errno = tmperrno; 507 1.1 nia return retval; 508 1.1 nia } 509 1.1 nia close(newfd); 510 1.1 nia break; 511 1.1 nia case SNDCTL_MIX_READ: 512 1.1 nia mv = (oss_mixer_value *)argp; 513 1.1 nia if (mv == NULL) { 514 1.1 nia errno = EINVAL; 515 1.1 nia return -1; 516 1.1 nia } 517 1.1 nia if (mv->ctrl == 0) { 518 1.1 nia errno = EINVAL; 519 1.1 nia return -1; 520 1.1 nia } 521 1.1 nia snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); 522 1.1 nia if ((newfd = open(devname, O_RDWR)) < 0) 523 1.1 nia return newfd; 524 1.1 nia mdi.index = mc.dev = (mv->ctrl - 1); 525 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); 526 1.1 nia if (retval < 0) { 527 1.1 nia tmperrno = errno; 528 1.1 nia close(newfd); 529 1.1 nia errno = tmperrno; 530 1.1 nia return retval; 531 1.1 nia } 532 1.1 nia mc.dev = mdi.index; 533 1.1 nia mc.type = mdi.type; 534 1.1 nia if (mdi.type == AUDIO_MIXER_VALUE) 535 1.1 nia mc.un.value.num_channels = mdi.un.v.num_channels; 536 1.1 nia retval = ioctl(newfd, AUDIO_MIXER_READ, &mc); 537 1.1 nia if (retval < 0) { 538 1.1 nia tmperrno = errno; 539 1.1 nia close(newfd); 540 1.1 nia errno = tmperrno; 541 1.1 nia return retval; 542 1.1 nia } 543 1.1 nia close(newfd); 544 1.1 nia mv->value = 0; 545 1.1 nia switch (mdi.type) { 546 1.1 nia case AUDIO_MIXER_ENUM: 547 1.1 nia for (i = 0; i < mdi.un.e.num_mem; ++i) { 548 1.1 nia if (mc.un.ord == mdi.un.e.member[i].ord) { 549 1.1 nia mv->value = i; 550 1.1 nia break; 551 1.1 nia } 552 1.1 nia } 553 1.1 nia break; 554 1.1 nia case AUDIO_MIXER_SET: 555 1.1 nia for (i = 0; i < mdi.un.s.num_mem; ++i) { 556 1.1 nia #ifdef notyet 557 1.1 nia if (mc.un.mask & mdi.un.s.member[i].mask) 558 1.1 nia mv->value |= (1 << i); 559 1.1 nia #else 560 1.1 nia if (mc.un.mask == mdi.un.s.member[i].mask) { 561 1.1 nia mv->value = i; 562 1.1 nia break; 563 1.1 nia } 564 1.1 nia #endif 565 1.1 nia } 566 1.1 nia break; 567 1.1 nia case AUDIO_MIXER_VALUE: 568 1.1 nia if (mdi.un.v.num_channels != 2) { 569 1.1 nia mv->value = mc.un.value.level[0]; 570 1.1 nia } else { 571 1.1 nia mv->value = \ 572 1.1 nia ((mc.un.value.level[1] & 0xFF) << 8) | 573 1.1 nia ((mc.un.value.level[0] & 0xFF) << 0); 574 1.1 nia } 575 1.1 nia break; 576 1.1 nia default: 577 1.1 nia errno = EINVAL; 578 1.1 nia return -1; 579 1.1 nia } 580 1.1 nia break; 581 1.1 nia default: 582 1.1 nia errno = EINVAL; 583 1.1 nia return -1; 584 1.1 nia } 585 1.1 nia return 0; 586 1.1 nia } 587 1.1 nia 588 1.1 nia static int 589 1.1 nia get_audio_count(void) 590 1.1 nia { 591 1.1 nia char devname[32]; 592 1.1 nia int ndevs = 0; 593 1.1 nia int tmpfd; 594 1.1 nia int tmperrno = errno; 595 1.1 nia 596 1.1 nia do { 597 1.1 nia snprintf(devname, sizeof(devname), 598 1.1 nia "/dev/audio%d", ndevs); 599 1.1 nia if ((tmpfd = open(devname, O_RDONLY)) != -1 || 600 1.1 nia (tmpfd = open(devname, O_WRONLY)) != -1) { 601 1.1 nia ndevs++; 602 1.1 nia close(tmpfd); 603 1.1 nia } 604 1.1 nia } while (tmpfd != -1); 605 1.1 nia errno = tmperrno; 606 1.1 nia return ndevs; 607 1.1 nia } 608 1.1 nia 609 1.1 nia static int 610 1.1 nia get_mixer_count(void) 611 1.1 nia { 612 1.1 nia char devname[32]; 613 1.1 nia int ndevs = 0; 614 1.1 nia int tmpfd; 615 1.1 nia int tmperrno = errno; 616 1.1 nia 617 1.1 nia do { 618 1.1 nia snprintf(devname, sizeof(devname), 619 1.1 nia "/dev/mixer%d", ndevs); 620 1.1 nia if ((tmpfd = open(devname, O_RDONLY)) != -1) { 621 1.1 nia ndevs++; 622 1.1 nia close(tmpfd); 623 1.1 nia } 624 1.1 nia } while (tmpfd != -1); 625 1.1 nia errno = tmperrno; 626 1.1 nia return ndevs; 627 1.1 nia } 628 1.1 nia 629 1.1 nia static int 630 1.1 nia get_mixer_control_count(int fd) 631 1.1 nia { 632 1.1 nia struct mixer_devinfo mdi; 633 1.1 nia int ndevs = 0; 634 1.1 nia 635 1.1 nia do { 636 1.1 nia mdi.index = ndevs++; 637 1.1 nia } while (ioctl(fd, AUDIO_MIXER_DEVINFO, &mdi) != -1); 638 1.1 nia 639 1.1 nia return ndevs > 0 ? ndevs - 1 : 0; 640 1.1 nia } 641