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