1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/types.h>
26 #include <sys/open.h>
27 #include <sys/errno.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/audio/audio_oss.h>
31 #include <sys/file.h>
32 #include <sys/note.h>
33 #include <sys/sysmacros.h>
34 #include <sys/list.h>
35 #include "audio_client.h"
36
37 #define OSS_FMT AFMT_S16_LE
38 #define OSS_RATE 48000
39 #define OSS_CHANNELS 2
40
41 typedef struct ossclient ossclient_t;
42 typedef struct ossdev ossdev_t;
43
44 static const struct {
45 int oss;
46 int fmt;
47 } oss_formats[] = {
48 { AFMT_MU_LAW, AUDIO_FORMAT_ULAW },
49 { AFMT_A_LAW, AUDIO_FORMAT_ALAW },
50 { AFMT_U8, AUDIO_FORMAT_U8 },
51 { AFMT_S8, AUDIO_FORMAT_S8 },
52 { AFMT_S16_BE, AUDIO_FORMAT_S16_BE },
53 { AFMT_S16_LE, AUDIO_FORMAT_S16_LE },
54 { AFMT_U16_BE, AUDIO_FORMAT_U16_BE },
55 { AFMT_U16_LE, AUDIO_FORMAT_U16_LE },
56 { AFMT_S24_BE, AUDIO_FORMAT_S24_BE },
57 { AFMT_S24_LE, AUDIO_FORMAT_S24_LE },
58 { AFMT_S32_BE, AUDIO_FORMAT_S32_BE },
59 { AFMT_S32_LE, AUDIO_FORMAT_S32_LE },
60 { AFMT_S24_PACKED, AUDIO_FORMAT_S24_PACKED },
61 { AFMT_AC3, AUDIO_FORMAT_AC3 },
62 { AFMT_QUERY, AUDIO_FORMAT_NONE }
63 };
64
65 /* common structure shared between both mixer and dsp nodes */
66 struct ossclient {
67 ossdev_t *o_ossdev;
68 audio_client_t *o_client;
69 /* sndstat */
70 kmutex_t o_ss_lock;
71 char *o_ss_buf;
72 size_t o_ss_len;
73 size_t o_ss_sz;
74 size_t o_ss_off;
75 };
76
77 struct ossdev {
78 audio_dev_t *d_dev;
79
80 uint_t d_nctrl; /* num actual controls */
81 uint_t d_nalloc; /* num allocated controls */
82 audio_ctrl_t **d_ctrls; /* array of control handles */
83 oss_mixext *d_exts; /* array of mixer descs */
84
85 int d_play_grp;
86 int d_rec_grp;
87 int d_mon_grp;
88 int d_misc_grp;
89
90 kmutex_t d_mx;
91 kcondvar_t d_cv;
92 };
93
94 static int
oss_cnt_controls(audio_ctrl_t * ctrl,void * arg)95 oss_cnt_controls(audio_ctrl_t *ctrl, void *arg)
96 {
97 int *pint = (int *)arg;
98 int cnt;
99 audio_ctrl_desc_t desc;
100
101 cnt = *pint;
102 cnt++;
103 *pint = cnt;
104
105 if (auclnt_control_describe(ctrl, &desc) != 0)
106 return (AUDIO_WALK_CONTINUE);
107
108 if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
109 for (uint64_t mask = desc.acd_maxvalue; mask; mask >>= 1) {
110 if (mask & 1) {
111 cnt++;
112 }
113 }
114 *pint = cnt;
115 }
116
117 return (AUDIO_WALK_CONTINUE);
118 }
119
120 /*
121 * Add one entry to the OSS user control table to internal control
122 * helper table.
123 *
124 * This is used with auimpl_walk_controls. The table must be pre-
125 * allocated before it is walk'd. This includes the root and
126 * extended control markers!
127 */
128 static int
oss_add_control(audio_ctrl_t * ctrl,void * arg)129 oss_add_control(audio_ctrl_t *ctrl, void *arg)
130 {
131 ossdev_t *odev = arg;
132 audio_ctrl_desc_t desc;
133 oss_mixext *ext;
134 int bit;
135 uint64_t mask;
136 const char *name;
137 int parent;
138 int flags;
139 unsigned scope;
140
141 if (auclnt_control_describe(ctrl, &desc))
142 return (AUDIO_WALK_CONTINUE);
143
144 parent = 0;
145
146 /*
147 * Add appropriate group if not already done so.
148 */
149 if (desc.acd_flags & AUDIO_CTRL_FLAG_PLAY) {
150 if (!odev->d_play_grp) {
151 ext = &odev->d_exts[odev->d_nctrl];
152 ext->ctrl = odev->d_nctrl;
153 ext->control_no = -1;
154 ext->type = MIXT_GROUP;
155 ext->desc = MIXEXT_SCOPE_OUTPUT;
156 ext->timestamp = gethrtime();
157 (void) snprintf(ext->id, sizeof (ext->id), "PLAYBACK");
158 odev->d_play_grp = odev->d_nctrl;
159 odev->d_nctrl++;
160 }
161 scope = MIXEXT_SCOPE_OUTPUT;
162 parent = odev->d_play_grp;
163 } else if (desc.acd_flags & AUDIO_CTRL_FLAG_REC) {
164 if (!odev->d_rec_grp) {
165 ext = &odev->d_exts[odev->d_nctrl];
166 ext->ctrl = odev->d_nctrl;
167 ext->control_no = -1;
168 ext->type = MIXT_GROUP;
169 ext->desc = MIXEXT_SCOPE_INPUT;
170 ext->timestamp = gethrtime();
171 (void) snprintf(ext->id, sizeof (ext->id), "RECORD");
172 odev->d_rec_grp = odev->d_nctrl;
173 odev->d_nctrl++;
174 }
175 scope = MIXEXT_SCOPE_INPUT;
176 parent = odev->d_rec_grp;
177 } else if (desc.acd_flags & AUDIO_CTRL_FLAG_MONITOR) {
178 if (!odev->d_mon_grp) {
179 ext = &odev->d_exts[odev->d_nctrl];
180 ext->ctrl = odev->d_nctrl;
181 ext->control_no = -1;
182 ext->type = MIXT_GROUP;
183 ext->desc = MIXEXT_SCOPE_MONITOR;
184 ext->timestamp = gethrtime();
185 (void) snprintf(ext->id, sizeof (ext->id), "MONITOR");
186 odev->d_mon_grp = odev->d_nctrl;
187 odev->d_nctrl++;
188 }
189 scope = MIXEXT_SCOPE_MONITOR;
190 parent = odev->d_mon_grp;
191 } else {
192 if (!odev->d_misc_grp) {
193 ext = &odev->d_exts[odev->d_nctrl];
194 ext->ctrl = odev->d_nctrl;
195 ext->control_no = -1;
196 ext->type = MIXT_GROUP;
197 ext->desc = MIXEXT_SCOPE_OTHER;
198 ext->timestamp = gethrtime();
199 (void) snprintf(ext->id, sizeof (ext->id), "MISC");
200 odev->d_misc_grp = odev->d_nctrl;
201 odev->d_nctrl++;
202 }
203 scope = MIXEXT_SCOPE_OTHER;
204 parent = odev->d_misc_grp;
205 }
206
207 name = desc.acd_name ? desc.acd_name : "";
208
209 if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
210 ext = &odev->d_exts[odev->d_nctrl];
211 ext->ctrl = odev->d_nctrl;
212 ext->control_no = -1;
213 ext->type = MIXT_GROUP;
214 ext->timestamp = gethrtime();
215 ext->parent = parent;
216 ext->desc = scope;
217 (void) snprintf(ext->id, sizeof (ext->id), "%s", name);
218 (void) snprintf(ext->extname, sizeof (ext->extname),
219 "%s", name);
220 parent = odev->d_nctrl++;
221 }
222
223 /* Next available open entry */
224 ext = &odev->d_exts[odev->d_nctrl];
225
226 /* Record the underlying control handle */
227 odev->d_ctrls[odev->d_nctrl] = ctrl;
228
229 /*
230 * Now setup the oss entry
231 */
232
233 ext->ctrl = odev->d_nctrl;
234 ext->control_no = -1;
235 ext->maxvalue = (int)desc.acd_maxvalue;
236 ext->minvalue = (int)desc.acd_minvalue;
237 ext->timestamp = gethrtime();
238 ext->parent = parent;
239 ext->desc = scope;
240 /* all controls should be pollable for now */
241 flags = MIXF_POLL;
242
243 /*
244 * The following flags are intended to help out applications
245 * which need to figure out where to place certain controls.
246 * A few further words of guidance:
247 *
248 * Apps that just want a single master volume control should
249 * adjust the control(s) that are labelled with MIXF_PCMVOL if
250 * present. They can fall back to adjusting all MAINVOL
251 * levels instead, if no PCMVOL is present.
252 *
253 * Controls that are one type on a certain device might be a
254 * different type on another device. For example,
255 * audiopci/ak4531 can adjust input gains for individual
256 * levels, but lacks a master record gain. AC'97, on the
257 * other hand, has individual monitor gains for inputs, but
258 * only a single master recording gain.
259 */
260 if (desc.acd_flags & AUDIO_CTRL_FLAG_READABLE)
261 flags |= MIXF_READABLE;
262 if (desc.acd_flags & AUDIO_CTRL_FLAG_WRITEABLE)
263 flags |= MIXF_WRITEABLE;
264 if (desc.acd_flags & AUDIO_CTRL_FLAG_CENTIBEL)
265 flags |= MIXF_CENTIBEL;
266 if (desc.acd_flags & AUDIO_CTRL_FLAG_DECIBEL)
267 flags |= MIXF_DECIBEL;
268 if (desc.acd_flags & AUDIO_CTRL_FLAG_MAINVOL)
269 flags |= MIXF_MAINVOL;
270 if (desc.acd_flags & AUDIO_CTRL_FLAG_PCMVOL)
271 flags |= MIXF_PCMVOL;
272 if (desc.acd_flags & AUDIO_CTRL_FLAG_RECVOL)
273 flags |= MIXF_RECVOL;
274 if (desc.acd_flags & AUDIO_CTRL_FLAG_MONVOL)
275 flags |= MIXF_MONVOL;
276 ext->flags = flags;
277
278 (void) snprintf(ext->id, sizeof (ext->id), "%s", name);
279
280 /*
281 * For now just use the same extname as the real name.
282 */
283 (void) snprintf(ext->extname, sizeof (ext->extname), name);
284
285 /*
286 * Now we deal with various control types.
287 */
288 switch (desc.acd_type) {
289 case AUDIO_CTRL_TYPE_BOOLEAN:
290 ext->type = MIXT_ONOFF;
291 ext->enumbit = -1;
292 break;
293 case AUDIO_CTRL_TYPE_STEREO:
294 ext->type = MIXT_STEREOSLIDER;
295 break;
296 case AUDIO_CTRL_TYPE_MONO:
297 ext->type = MIXT_MONOSLIDER;
298 break;
299 case AUDIO_CTRL_TYPE_ENUM:
300
301 if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
302 /*
303 * We turn AUDIO_CTRL_FLAG_MULTI into a group
304 * of checkboxes, since OSS can't represent it
305 * natively.
306 */
307 mask = desc.acd_maxvalue;
308 bit = 0;
309 while (mask) {
310 if (mask & 1) {
311 ext = &odev->d_exts[odev->d_nctrl];
312 (void) snprintf(ext->extname,
313 sizeof (ext->extname), "%s.%s",
314 name, desc.acd_enum[bit]);
315 (void) snprintf(ext->id,
316 sizeof (ext->id), "%s",
317 desc.acd_enum[bit]);
318 ext->ctrl = odev->d_nctrl;
319 ext->control_no = -1;
320 ext->parent = parent;
321 ext->timestamp = gethrtime();
322 ext->type = MIXT_ONOFF;
323 ext->minvalue = 0;
324 ext->maxvalue = 1;
325 ext->enumbit = bit;
326 ext->flags = flags;
327 odev->d_ctrls[odev->d_nctrl] = ctrl;
328 odev->d_nctrl++;
329 }
330 bit++;
331 mask >>= 1;
332 }
333 return (AUDIO_WALK_CONTINUE);
334 } else {
335 /*
336 * NB: This is sufficient only for controls
337 * with a single value. It cannot express the
338 * richer bitmask capabilities.
339 */
340 ext->type = MIXT_ENUM;
341 ext->minvalue = 0;
342
343 /*
344 * For an enumaration, we need to figure out
345 * which values are present, and set the
346 * appropriate mask and max value.
347 */
348 bzero(ext->enum_present, sizeof (ext->enum_present));
349 mask = desc.acd_maxvalue;
350 bit = 0;
351 while (mask) {
352 if (mask & 1) {
353 ext->enum_present[bit / 8] |=
354 (1 << (bit % 8));
355 }
356 mask >>= 1;
357 bit++;
358 }
359 ext->maxvalue = bit;
360 }
361 break;
362
363 case AUDIO_CTRL_TYPE_METER:
364 default:
365 /* Its an unknown or unsupported (for now) control, skip */
366 return (AUDIO_WALK_CONTINUE);
367 }
368
369 odev->d_nctrl++;
370
371 return (AUDIO_WALK_CONTINUE);
372 }
373
374 /*
375 * Free up an OSS user land control to internal control,
376 * helper table.
377 */
378 static void
oss_free_controls(ossdev_t * odev)379 oss_free_controls(ossdev_t *odev)
380 {
381 kmem_free(odev->d_ctrls, sizeof (audio_ctrl_t *) * odev->d_nalloc);
382 kmem_free(odev->d_exts, sizeof (oss_mixext) * odev->d_nalloc);
383 odev->d_nctrl = 0;
384 odev->d_nalloc = 0;
385 }
386
387 /*
388 * Allocate and fill in an OSS user land controls to internal controls
389 * helper table. This is done on one audio_dev device.
390 */
391 static void
oss_alloc_controls(ossdev_t * odev)392 oss_alloc_controls(ossdev_t *odev)
393 {
394 audio_dev_t *d = odev->d_dev;
395 int nctrl = 0;
396 oss_mixext *ext;
397 oss_mixext_root *root_data;
398
399 /* Find out who many entries we need */
400 auclnt_walk_controls(d, oss_cnt_controls, &nctrl);
401 nctrl++; /* Needs space for the device root node */
402 nctrl++; /* Needs space for the device ext marker */
403 nctrl++; /* Needs space for the play group */
404 nctrl++; /* Needs space for the record group */
405 nctrl++; /* Needs space for the monitor group */
406 nctrl++; /* Needs space for the tone group */
407 nctrl++; /* Needs space for the 3D group */
408 nctrl++; /* Needs space for the misc group */
409
410 /* Allocate the OSS to boomer helper table */
411 odev->d_nalloc = nctrl;
412 odev->d_ctrls = kmem_zalloc(sizeof (audio_ctrl_t *) * nctrl, KM_SLEEP);
413 odev->d_exts = kmem_zalloc(sizeof (oss_mixext) * nctrl, KM_SLEEP);
414
415 /*
416 * Setup special case outputs to output OSS routes helper tables
417 */
418
419 /*
420 * Root node is first, that way all others parent is this one
421 */
422 ext = &odev->d_exts[odev->d_nctrl];
423 ext->ctrl = 0;
424 ext->parent = -1;
425 ext->type = MIXT_DEVROOT;
426 ext->timestamp = gethrtime();
427 (void) snprintf(ext->id, sizeof (ext->id), "DEVROOT");
428 /*
429 * Root data... nobody should be using this though.
430 */
431 root_data = (oss_mixext_root *)&ext->data;
432 (void) snprintf(root_data->name, sizeof (root_data->name), "%s",
433 auclnt_get_dev_name(d));
434 (void) snprintf(root_data->id, sizeof (root_data->id), "%s",
435 auclnt_get_dev_name(d));
436
437 odev->d_nctrl++;
438
439 /*
440 * Insert an extra marker -- needed to keep layout apps hapy.
441 * This prevents some apps from assuming we are in "LEGACY" mode.
442 */
443 ext = &odev->d_exts[odev->d_nctrl];
444 ext->ctrl = odev->d_nctrl;
445 ext->control_no = -1;
446 ext->type = MIXT_MARKER;
447 ext->timestamp = gethrtime();
448 ext->parent = 0;
449 odev->d_nctrl++;
450
451 /* Fill in the complete table now */
452 auclnt_walk_controls(d, oss_add_control, odev);
453
454 /* Update the update_counter reference counter for groups */
455 for (nctrl = 0; nctrl < odev->d_nctrl; nctrl++) {
456 int i;
457
458 ext = &odev->d_exts[nctrl];
459 i = ext->parent;
460 while ((i >= 0) && (i < odev->d_nctrl)) {
461
462 ext = &odev->d_exts[i];
463 ASSERT(ext->parent < i);
464 ASSERT((ext->type == MIXT_GROUP) ||
465 (ext->type == MIXT_DEVROOT));
466 ext->update_counter++;
467 i = ext->parent;
468 }
469 }
470
471 ASSERT(odev->d_nctrl <= odev->d_nalloc);
472 }
473
474 static int
oss_open(audio_client_t * c,int oflag)475 oss_open(audio_client_t *c, int oflag)
476 {
477 int rv;
478 ossdev_t *odev;
479 ossclient_t *sc;
480 audio_stream_t *isp, *osp;
481
482 isp = auclnt_input_stream(c);
483 osp = auclnt_output_stream(c);
484
485 /* note that OSS always uses nonblocking open() semantics */
486 if ((rv = auclnt_open(c, oflag | FNDELAY)) != 0) {
487 return (rv);
488 }
489
490 if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
491 auclnt_close(c);
492 return (ENOMEM);
493 }
494 auclnt_set_private(c, sc);
495
496 odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
497
498 /* set a couple of common fields */
499 sc->o_client = c;
500 sc->o_ossdev = odev;
501
502 /* set all default parameters */
503 if (oflag & FWRITE) {
504 if (((rv = auclnt_set_format(osp, OSS_FMT)) != 0) ||
505 ((rv = auclnt_set_rate(osp, OSS_RATE)) != 0) ||
506 ((rv = auclnt_set_channels(osp, OSS_CHANNELS)) != 0)) {
507 goto failed;
508 }
509 /* default to 5 fragments to provide reasonable latency */
510 auclnt_set_latency(osp, 5, 0);
511 }
512
513 if (oflag & FREAD) {
514 if (((rv = auclnt_set_format(isp, OSS_FMT)) != 0) ||
515 ((rv = auclnt_set_rate(isp, OSS_RATE)) != 0) ||
516 ((rv = auclnt_set_channels(isp, OSS_CHANNELS)) != 0)) {
517 goto failed;
518 }
519 /* default to 5 fragments to provide reasonable latency */
520 auclnt_set_latency(isp, 5, 0);
521 }
522
523 return (0);
524
525 failed:
526 auclnt_close(c);
527 return (rv);
528 }
529
530 static void
oss_close(audio_client_t * c)531 oss_close(audio_client_t *c)
532 {
533 ossclient_t *sc;
534
535 sc = auclnt_get_private(c);
536
537 if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
538 (void) auclnt_drain(c);
539 }
540
541 kmem_free(sc, sizeof (*sc));
542
543 auclnt_close(c);
544 }
545
546 /*
547 * This is used to generate an array of names for an enumeration
548 */
549 static ushort_t
oss_set_enum(oss_mixer_enuminfo * ei,ushort_t nxt,const char * name)550 oss_set_enum(oss_mixer_enuminfo *ei, ushort_t nxt, const char *name)
551 {
552 uint32_t n;
553
554 /* Get current entry to fill in */
555 n = ei->nvalues;
556 (void) snprintf(&ei->strings[nxt], ((sizeof (ei->strings) - nxt) - 1),
557 "%s", name);
558 ei->strindex[n] = nxt;
559
560 /* Adjust everything for next entry */
561 nxt += strnlen(name, ((sizeof (ei->strings) - nxt) - 1));
562 ei->strings[nxt++] = '\0';
563
564 ei->nvalues++;
565 return (nxt);
566 }
567
568 /*
569 * The following two functions are used to count the number of devices
570 * in under the boomer framework.
571 *
572 * We actually report the highest "index", and then if an audio device
573 * is not found, we report a bogus removed device for it in the actual
574 * ioctls. This goofiness is required to make the OSS API happy.
575 */
576 int
oss_dev_walker(audio_dev_t * d,void * arg)577 oss_dev_walker(audio_dev_t *d, void *arg)
578 {
579 int *pcnt = arg;
580 int cnt;
581 int index;
582
583 cnt = *pcnt;
584 index = auclnt_get_dev_index(d);
585 if ((index + 1) > cnt) {
586 cnt = index + 1;
587 *pcnt = cnt;
588 }
589
590 return (AUDIO_WALK_CONTINUE);
591 }
592
593 static int
oss_cnt_devs(void)594 oss_cnt_devs(void)
595 {
596 int cnt = 0;
597
598 auclnt_walk_devs(oss_dev_walker, &cnt);
599 return (cnt);
600 }
601
602 static int
sndctl_dsp_speed(audio_client_t * c,int * ratep)603 sndctl_dsp_speed(audio_client_t *c, int *ratep)
604 {
605 int rate;
606 int oflag;
607
608 rate = *ratep;
609
610 oflag = auclnt_get_oflag(c);
611 if (oflag & FREAD) {
612 (void) auclnt_set_rate(auclnt_input_stream(c), rate);
613 *ratep = auclnt_get_rate(auclnt_input_stream(c));
614 }
615
616 if (oflag & FWRITE) {
617 (void) auclnt_set_rate(auclnt_output_stream(c), rate);
618 *ratep = auclnt_get_rate(auclnt_output_stream(c));
619 }
620
621 return (0);
622 }
623
624 static int
sndctl_dsp_setfmt(audio_client_t * c,int * fmtp)625 sndctl_dsp_setfmt(audio_client_t *c, int *fmtp)
626 {
627 int fmt;
628 int i;
629 int oflag;
630
631 oflag = auclnt_get_oflag(c);
632
633 if (*fmtp != AFMT_QUERY) {
634 /* convert from OSS */
635 for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
636 if (oss_formats[i].oss == *fmtp) {
637 fmt = oss_formats[i].fmt;
638 break;
639 }
640 }
641 if (fmt == AUDIO_FORMAT_NONE) {
642 /* if format not known, return */
643 goto done;
644 }
645
646 if (oflag & FWRITE) {
647 (void) auclnt_set_format(auclnt_output_stream(c), fmt);
648 }
649
650 if (oflag & FREAD) {
651 (void) auclnt_set_format(auclnt_input_stream(c), fmt);
652 }
653 }
654
655 done:
656 if (oflag & FWRITE) {
657 fmt = auclnt_get_format(auclnt_output_stream(c));
658 } else if (oflag & FREAD) {
659 fmt = auclnt_get_format(auclnt_input_stream(c));
660 }
661
662 /* convert back to OSS */
663 *(int *)fmtp = AFMT_QUERY;
664 for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
665 if (oss_formats[i].fmt == fmt) {
666 *(int *)fmtp = oss_formats[i].oss;
667 }
668 }
669
670 return (0);
671 }
672
673 static int
sndctl_dsp_getfmts(audio_client_t * c,int * fmtsp)674 sndctl_dsp_getfmts(audio_client_t *c, int *fmtsp)
675 {
676 _NOTE(ARGUNUSED(c));
677
678 /*
679 * For now, we support all the standard ones. Later we might
680 * add in conditional support for AC3.
681 */
682 *fmtsp = (AFMT_MU_LAW | AFMT_A_LAW |
683 AFMT_U8 | AFMT_S8 |
684 AFMT_S16_LE |AFMT_S16_BE |
685 AFMT_S24_LE | AFMT_S24_BE |
686 AFMT_S32_LE | AFMT_S32_BE |
687 AFMT_S24_PACKED);
688
689 return (0);
690 }
691
692 static int
sndctl_dsp_channels(audio_client_t * c,int * chanp)693 sndctl_dsp_channels(audio_client_t *c, int *chanp)
694 {
695 int nchan;
696 int oflag;
697
698 oflag = auclnt_get_oflag(c);
699
700 nchan = *chanp;
701 if (nchan != 0) {
702 if (oflag & FWRITE) {
703 (void) auclnt_set_channels(auclnt_output_stream(c),
704 nchan);
705 }
706
707 if (oflag & FREAD) {
708 (void) auclnt_set_channels(auclnt_input_stream(c),
709 nchan);
710 }
711 }
712
713 if (oflag & FWRITE) {
714 nchan = auclnt_get_channels(auclnt_output_stream(c));
715 } else if (oflag & FREAD) {
716 nchan = auclnt_get_channels(auclnt_input_stream(c));
717 }
718 *chanp = nchan;
719 return (0);
720 }
721
722 static int
sndctl_dsp_stereo(audio_client_t * c,int * onoff)723 sndctl_dsp_stereo(audio_client_t *c, int *onoff)
724 {
725 int nchan;
726
727 switch (*onoff) {
728 case 0:
729 nchan = 1;
730 break;
731 case 1:
732 nchan = 2;
733 break;
734 default:
735 return (EINVAL);
736 }
737
738 return (sndctl_dsp_channels(c, &nchan));
739 }
740
741 static int
sndctl_dsp_post(audio_client_t * c)742 sndctl_dsp_post(audio_client_t *c)
743 {
744 if (auclnt_get_oflag(c) & FWRITE) {
745 audio_stream_t *sp = auclnt_output_stream(c);
746 auclnt_flush(sp);
747 auclnt_clear_paused(sp);
748 }
749 return (0);
750 }
751
752 static int
sndctl_dsp_getcaps(audio_client_t * c,int * capsp)753 sndctl_dsp_getcaps(audio_client_t *c, int *capsp)
754 {
755 int ncaps;
756 int osscaps = 0;
757
758 ncaps = auclnt_get_dev_capab(auclnt_get_dev(c));
759
760 if (ncaps & AUDIO_CLIENT_CAP_PLAY)
761 osscaps |= PCM_CAP_OUTPUT;
762 if (ncaps & AUDIO_CLIENT_CAP_RECORD)
763 osscaps |= PCM_CAP_INPUT;
764 if (ncaps & AUDIO_CLIENT_CAP_DUPLEX)
765 osscaps |= PCM_CAP_DUPLEX;
766
767 if (osscaps != 0) {
768 osscaps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
769 if (!(ncaps & AUDIO_CLIENT_CAP_OPAQUE)) {
770 osscaps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
771 }
772 } else {
773 /* This is the sndstat device! */
774 osscaps = PCM_CAP_VIRTUAL;
775 }
776
777 *capsp = osscaps;
778 return (0);
779 }
780
781 static int
sndctl_dsp_gettrigger(audio_client_t * c,int * trigp)782 sndctl_dsp_gettrigger(audio_client_t *c, int *trigp)
783 {
784 int triggers = 0;
785 int oflag;
786
787 oflag = auclnt_get_oflag(c);
788
789 if (oflag & FWRITE) {
790 if (!auclnt_is_paused(auclnt_output_stream(c))) {
791 triggers |= PCM_ENABLE_OUTPUT;
792 }
793 }
794
795 if (oflag & FREAD) {
796 if (!auclnt_is_paused(auclnt_input_stream(c))) {
797 triggers |= PCM_ENABLE_INPUT;
798 }
799 }
800 *trigp = triggers;
801
802 return (0);
803 }
804
805 static int
sndctl_dsp_settrigger(audio_client_t * c,int * trigp)806 sndctl_dsp_settrigger(audio_client_t *c, int *trigp)
807 {
808 int triggers;
809 int oflag;
810 audio_stream_t *sp;
811
812 oflag = auclnt_get_oflag(c);
813 triggers = *trigp;
814
815 if ((oflag & FWRITE) && (triggers & PCM_ENABLE_OUTPUT)) {
816 sp = auclnt_output_stream(c);
817 auclnt_clear_paused(sp);
818 auclnt_start(sp);
819 }
820
821 if ((oflag & FREAD) && (triggers & PCM_ENABLE_INPUT)) {
822 sp = auclnt_input_stream(c);
823 auclnt_clear_paused(sp);
824 auclnt_start(sp);
825 }
826
827 return (0);
828 }
829
830 struct oss_legacy_volume {
831 pid_t pid;
832 uint8_t ogain;
833 uint8_t igain;
834 };
835
836 static int
oss_legacy_volume_walker(audio_client_t * c,void * arg)837 oss_legacy_volume_walker(audio_client_t *c, void *arg)
838 {
839 struct oss_legacy_volume *olv = arg;
840
841 if (auclnt_get_pid(c) == olv->pid) {
842 if (olv->ogain <= 100) {
843 auclnt_set_gain(auclnt_output_stream(c), olv->ogain);
844 }
845 if (olv->igain <= 100) {
846 auclnt_set_gain(auclnt_input_stream(c), olv->igain);
847 }
848 }
849 return (AUDIO_WALK_CONTINUE);
850 }
851
852 static void
oss_set_legacy_volume(audio_client_t * c,uint8_t ogain,uint8_t igain)853 oss_set_legacy_volume(audio_client_t *c, uint8_t ogain, uint8_t igain)
854 {
855 struct oss_legacy_volume olv;
856
857 olv.pid = auclnt_get_pid(c);
858 olv.ogain = ogain;
859 olv.igain = igain;
860 auclnt_dev_walk_clients(auclnt_get_dev(c),
861 oss_legacy_volume_walker, &olv);
862 }
863
864 static int
sndctl_dsp_getplayvol(audio_client_t * c,int * volp)865 sndctl_dsp_getplayvol(audio_client_t *c, int *volp)
866 {
867 int vol;
868
869 /* convert monophonic soft value to OSS stereo value */
870 vol = auclnt_get_gain(auclnt_output_stream(c));
871 *volp = vol | (vol << 8);
872 return (0);
873 }
874
875 static int
sndctl_dsp_setplayvol(audio_client_t * c,int * volp)876 sndctl_dsp_setplayvol(audio_client_t *c, int *volp)
877 {
878 uint8_t vol;
879
880 vol = *volp & 0xff;
881 if (vol > 100) {
882 return (EINVAL);
883 }
884
885 auclnt_set_gain(auclnt_output_stream(c), vol);
886 *volp = (vol | (vol << 8));
887
888 return (0);
889 }
890
891 static int
sndctl_dsp_getrecvol(audio_client_t * c,int * volp)892 sndctl_dsp_getrecvol(audio_client_t *c, int *volp)
893 {
894 int vol;
895
896 vol = auclnt_get_gain(auclnt_input_stream(c));
897 *volp = (vol | (vol << 8));
898 return (0);
899 }
900
901 static int
sndctl_dsp_setrecvol(audio_client_t * c,int * volp)902 sndctl_dsp_setrecvol(audio_client_t *c, int *volp)
903 {
904 uint8_t vol;
905
906 vol = *volp & 0xff;
907 if (vol > 100) {
908 return (EINVAL);
909 }
910
911 auclnt_set_gain(auclnt_input_stream(c), vol);
912 *volp = (vol | (vol << 8));
913
914 return (0);
915 }
916
917 static int
sound_mixer_write_ogain(audio_client_t * c,int * volp)918 sound_mixer_write_ogain(audio_client_t *c, int *volp)
919 {
920 uint8_t vol;
921
922 vol = *volp & 0xff;
923 if (vol > 100) {
924 return (EINVAL);
925 }
926 oss_set_legacy_volume(c, vol, 255);
927 *volp = (vol | (vol << 8));
928 return (0);
929 }
930
931 static int
sound_mixer_write_igain(audio_client_t * c,int * volp)932 sound_mixer_write_igain(audio_client_t *c, int *volp)
933 {
934 uint8_t vol;
935
936 vol = *volp & 0xff;
937 if (vol > 100) {
938 return (EINVAL);
939 }
940 oss_set_legacy_volume(c, 255, vol);
941 *volp = (vol | (vol << 8));
942 return (0);
943 }
944
945 static int
sndctl_dsp_readctl(audio_client_t * c,oss_digital_control * ctl)946 sndctl_dsp_readctl(audio_client_t *c, oss_digital_control *ctl)
947 {
948 /* SPDIF: need to add support with spdif */
949 _NOTE(ARGUNUSED(c));
950 _NOTE(ARGUNUSED(ctl));
951 return (ENOTSUP);
952 }
953
954 static int
sndctl_dsp_writectl(audio_client_t * c,oss_digital_control * ctl)955 sndctl_dsp_writectl(audio_client_t *c, oss_digital_control *ctl)
956 {
957 /* SPDIF: need to add support with spdif */
958 _NOTE(ARGUNUSED(c));
959 _NOTE(ARGUNUSED(ctl));
960 return (ENOTSUP);
961 }
962
963 static int
sndctl_dsp_cookedmode(audio_client_t * c,int * rvp)964 sndctl_dsp_cookedmode(audio_client_t *c, int *rvp)
965 {
966 _NOTE(ARGUNUSED(c));
967
968 /* We are *always* in cooked mode -- at least until we have AC3. */
969 if (*rvp == 0) {
970 return (ENOTSUP);
971 } else {
972 return (0);
973 }
974 }
975
976 static int
sndctl_dsp_silence(audio_client_t * c)977 sndctl_dsp_silence(audio_client_t *c)
978 {
979 if (auclnt_get_oflag(c) & FWRITE) {
980 audio_stream_t *sp = auclnt_output_stream(c);
981 auclnt_set_paused(sp);
982 auclnt_flush(sp);
983 }
984 return (0);
985 }
986
987 static int
sndctl_dsp_skip(audio_client_t * c)988 sndctl_dsp_skip(audio_client_t *c)
989 {
990 if (auclnt_get_oflag(c) & FWRITE) {
991 audio_stream_t *sp = auclnt_output_stream(c);
992 auclnt_set_paused(sp);
993 auclnt_flush(sp);
994 auclnt_clear_paused(sp);
995 }
996 return (0);
997 }
998
999 static int
sndctl_dsp_halt_input(audio_client_t * c)1000 sndctl_dsp_halt_input(audio_client_t *c)
1001 {
1002 if (auclnt_get_oflag(c) & FREAD) {
1003 audio_stream_t *sp = auclnt_input_stream(c);
1004 auclnt_set_paused(sp);
1005 auclnt_flush(sp);
1006 }
1007 return (0);
1008 }
1009
1010 static int
sndctl_dsp_halt_output(audio_client_t * c)1011 sndctl_dsp_halt_output(audio_client_t *c)
1012 {
1013 if (auclnt_get_oflag(c) & FWRITE) {
1014 audio_stream_t *sp = auclnt_output_stream(c);
1015 auclnt_set_paused(sp);
1016 auclnt_flush(sp);
1017 }
1018 return (0);
1019 }
1020
1021 static int
sndctl_dsp_halt(audio_client_t * c)1022 sndctl_dsp_halt(audio_client_t *c)
1023 {
1024 (void) sndctl_dsp_halt_input(c);
1025 (void) sndctl_dsp_halt_output(c);
1026 return (0);
1027 }
1028
1029 static int
sndctl_dsp_sync(audio_client_t * c)1030 sndctl_dsp_sync(audio_client_t *c)
1031 {
1032 return (auclnt_drain(c));
1033 }
1034
1035 static int
sndctl_dsp_setfragment(audio_client_t * c,int * fragp)1036 sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
1037 {
1038 int bufsz;
1039 int nfrags;
1040 int fragsz;
1041
1042 nfrags = (*fragp) >> 16;
1043 if ((nfrags >= 0x7fffU) || (nfrags < 2)) {
1044 /* use infinite setting... no change */
1045 return (0);
1046 }
1047
1048 fragsz = (*fragp) & 0xffff;
1049 if (fragsz > 16) {
1050 /* basically too big, so, no change */
1051 return (0);
1052 }
1053 bufsz = (1U << fragsz) * nfrags;
1054
1055 /*
1056 * Now we have our desired buffer size, but we have to
1057 * make sure we have a whole number of fragments >= 2, and
1058 * less than the maximum.
1059 */
1060 bufsz = ((*fragp) >> 16) * (1U << (*fragp));
1061 if (bufsz >= 65536) {
1062 return (0);
1063 }
1064
1065 /*
1066 * We set the latency hints in terms of bytes, not fragments.
1067 */
1068 auclnt_set_latency(auclnt_output_stream(c), 0, bufsz);
1069 auclnt_set_latency(auclnt_input_stream(c), 0, bufsz);
1070
1071 /*
1072 * According to the OSS API documentation, the values provided
1073 * are nothing more than a "hint" and not to be relied upon
1074 * anyway. And we aren't obligated to report the actual
1075 * values back!
1076 */
1077 return (0);
1078 }
1079
1080 static int
sndctl_dsp_policy(audio_client_t * c,int * policy)1081 sndctl_dsp_policy(audio_client_t *c, int *policy)
1082 {
1083 int hint = *policy;
1084 if ((hint >= 2) && (hint <= 10)) {
1085 auclnt_set_latency(auclnt_input_stream(c), hint, 0);
1086 auclnt_set_latency(auclnt_output_stream(c), hint, 0);
1087 }
1088 return (0);
1089 }
1090
1091 /*
1092 * A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
1093 * applications to change port configurations, because these could have a
1094 * bad effect for other applications. Instead, these settings have to
1095 * be changed using the master mixer panel. In order to make applications
1096 * happy, we just present a single "default" source/target.
1097 */
1098 static int
sndctl_dsp_get_recsrc_names(audio_client_t * c,oss_mixer_enuminfo * ei)1099 sndctl_dsp_get_recsrc_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1100 {
1101 _NOTE(ARGUNUSED(c));
1102
1103 ei->nvalues = 1;
1104 (void) snprintf(ei->strings, sizeof (ei->strings), "default");
1105 ei->strindex[0] = 0;
1106
1107 return (0);
1108 }
1109
1110 static int
sndctl_dsp_get_recsrc(audio_client_t * c,int * srcp)1111 sndctl_dsp_get_recsrc(audio_client_t *c, int *srcp)
1112 {
1113 _NOTE(ARGUNUSED(c));
1114 *srcp = 0;
1115 return (0);
1116 }
1117
1118 static int
sndctl_dsp_set_recsrc(audio_client_t * c,int * srcp)1119 sndctl_dsp_set_recsrc(audio_client_t *c, int *srcp)
1120 {
1121 _NOTE(ARGUNUSED(c));
1122 *srcp = 0;
1123 return (0);
1124 }
1125
1126 static int
sndctl_dsp_get_playtgt_names(audio_client_t * c,oss_mixer_enuminfo * ei)1127 sndctl_dsp_get_playtgt_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1128 {
1129 _NOTE(ARGUNUSED(c));
1130
1131 ei->nvalues = 1;
1132 (void) snprintf(ei->strings, sizeof (ei->strings), "default");
1133 ei->strindex[0] = 0;
1134
1135 return (0);
1136 }
1137
1138 static int
sndctl_dsp_get_playtgt(audio_client_t * c,int * tgtp)1139 sndctl_dsp_get_playtgt(audio_client_t *c, int *tgtp)
1140 {
1141 _NOTE(ARGUNUSED(c));
1142 *tgtp = 0;
1143 return (0);
1144 }
1145
1146 static int
sndctl_dsp_set_playtgt(audio_client_t * c,int * tgtp)1147 sndctl_dsp_set_playtgt(audio_client_t *c, int *tgtp)
1148 {
1149 _NOTE(ARGUNUSED(c));
1150 *tgtp = 0;
1151 return (0);
1152 }
1153
1154 static int
sndctl_sysinfo(oss_sysinfo * si)1155 sndctl_sysinfo(oss_sysinfo *si)
1156 {
1157 bzero(si, sizeof (*si));
1158 (void) snprintf(si->product, sizeof (si->product), "SunOS Audio");
1159 (void) snprintf(si->version, sizeof (si->version), "4.0");
1160 si->versionnum = OSS_VERSION;
1161 si->numcards = oss_cnt_devs();
1162 si->nummixers = si->numcards - 1;
1163 si->numaudios = si->numcards - 1;
1164 si->numaudioengines = si->numaudios;
1165 (void) snprintf(si->license, sizeof (si->license), "CDDL");
1166 return (0);
1167 }
1168
1169 static int
sndctl_cardinfo(audio_client_t * c,oss_card_info * ci)1170 sndctl_cardinfo(audio_client_t *c, oss_card_info *ci)
1171 {
1172 audio_dev_t *d;
1173 void *iter;
1174 const char *info;
1175 int n;
1176 boolean_t release;
1177
1178 if ((n = ci->card) == -1) {
1179 release = B_FALSE;
1180 d = auclnt_get_dev(c);
1181 n = auclnt_get_dev_index(d);
1182 } else {
1183 release = B_TRUE;
1184 d = auclnt_hold_dev_by_index(n);
1185 }
1186
1187 bzero(ci, sizeof (*ci));
1188 ci->card = n;
1189
1190 if (d == NULL) {
1191 /*
1192 * If device removed (e.g. for DR), then
1193 * report a bogus removed entry.
1194 */
1195 (void) snprintf(ci->shortname, sizeof (ci->shortname),
1196 "<removed>");
1197 (void) snprintf(ci->longname, sizeof (ci->longname),
1198 "<removed>");
1199 return (0);
1200 }
1201
1202 (void) snprintf(ci->shortname, sizeof (ci->shortname),
1203 "%s", auclnt_get_dev_name(d));
1204 (void) snprintf(ci->longname, sizeof (ci->longname),
1205 "%s (%s)", auclnt_get_dev_description(d),
1206 auclnt_get_dev_version(d));
1207
1208 iter = NULL;
1209 while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
1210 (void) strlcat(ci->hw_info, info, sizeof (ci->hw_info));
1211 (void) strlcat(ci->hw_info, "\n", sizeof (ci->hw_info));
1212 }
1213
1214 /*
1215 * We don't report interrupt counts, ack counts (which are
1216 * just "read" interrupts, not spurious), or any other flags.
1217 * Nothing should be using any of this data anyway ... these
1218 * values were intended for 4Front's debugging purposes. In
1219 * Solaris, drivers should use interrupt kstats to report
1220 * interrupt related statistics.
1221 */
1222 if (release)
1223 auclnt_release_dev(d);
1224 return (0);
1225 }
1226
1227 static int
audioinfo_walker(audio_engine_t * e,void * a)1228 audioinfo_walker(audio_engine_t *e, void *a)
1229 {
1230 oss_audioinfo *si = a;
1231 int fmt, nchan, rate, cap;
1232
1233 fmt = auclnt_engine_get_format(e);
1234 nchan = auclnt_engine_get_channels(e);
1235 rate = auclnt_engine_get_rate(e);
1236 cap = auclnt_engine_get_capab(e);
1237
1238 for (int i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
1239 if (fmt == oss_formats[i].fmt) {
1240 if (cap & AUDIO_CLIENT_CAP_PLAY) {
1241 si->oformats |= oss_formats[i].oss;
1242 }
1243 if (cap & AUDIO_CLIENT_CAP_RECORD) {
1244 si->iformats |= oss_formats[i].oss;
1245 }
1246 break;
1247 }
1248 }
1249 si->max_channels = max(nchan, si->max_channels);
1250 si->max_rate = max(rate, si->max_rate);
1251
1252 return (AUDIO_WALK_CONTINUE);
1253 }
1254
1255 static int
sndctl_audioinfo(audio_client_t * c,oss_audioinfo * si)1256 sndctl_audioinfo(audio_client_t *c, oss_audioinfo *si)
1257 {
1258 audio_dev_t *d;
1259 const char *name;
1260 int n;
1261 boolean_t release;
1262 unsigned cap;
1263
1264 if ((n = si->dev) == -1) {
1265 release = B_FALSE;
1266 d = auclnt_get_dev(c);
1267 n = auclnt_get_dev_index(d);
1268 } else {
1269 release = B_TRUE;
1270 n++; /* skip pseudo device */
1271 d = auclnt_hold_dev_by_index(n);
1272 }
1273
1274 bzero(si, sizeof (*si));
1275 si->dev = n - 1;
1276
1277 if (d == NULL) {
1278 /* if device not present, forge a false entry */
1279 si->card_number = n;
1280 si->mixer_dev = n - 1;
1281 si->legacy_device = -1;
1282 si->enabled = 0;
1283 (void) snprintf(si->name, sizeof (si->name), "<removed>");
1284 return (0);
1285 }
1286
1287 name = auclnt_get_dev_name(d);
1288 (void) snprintf(si->name, sizeof (si->name), "%s", name);
1289
1290 si->legacy_device = auclnt_get_dev_number(d);
1291 si->caps = 0;
1292
1293 auclnt_dev_walk_engines(d, audioinfo_walker, si);
1294
1295 cap = auclnt_get_dev_capab(d);
1296
1297 if (cap & AUDIO_CLIENT_CAP_DUPLEX) {
1298 si->caps |= PCM_CAP_DUPLEX;
1299 }
1300 if (cap & AUDIO_CLIENT_CAP_PLAY) {
1301 si->caps |= PCM_CAP_OUTPUT;
1302 }
1303 if (cap & AUDIO_CLIENT_CAP_RECORD) {
1304 si->caps |= PCM_CAP_INPUT;
1305 }
1306
1307 if (si->caps != 0) {
1308 /* MMAP: we add PCM_CAP_MMAP when we we support it */
1309 si->caps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
1310 si->enabled = 1;
1311 si->rate_source = si->dev;
1312
1313 /* we can convert and mix PCM formats */
1314 if (!(cap & AUDIO_CLIENT_CAP_OPAQUE)) {
1315 si->min_channels = min(2, si->max_channels);
1316 si->min_rate = min(5000, si->max_rate);
1317 si->caps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
1318 }
1319 (void) snprintf(si->devnode, sizeof (si->devnode),
1320 "/dev/sound/%s:%ddsp",
1321 auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1322 } else {
1323 si->enabled = 0; /* stops apps from using us directly */
1324 si->caps = PCM_CAP_VIRTUAL;
1325 (void) snprintf(si->devnode, sizeof (si->devnode),
1326 "/dev/sndstat");
1327 }
1328
1329 si->pid = -1;
1330 (void) snprintf(si->handle, sizeof (si->handle), "%s", name);
1331 (void) snprintf(si->label, sizeof (si->label), "%s", name);
1332 si->latency = -1;
1333 si->card_number = n;
1334 si->mixer_dev = n - 1;
1335
1336 if (release)
1337 auclnt_release_dev(d);
1338
1339 return (0);
1340 }
1341
1342 static int
sound_mixer_info(audio_client_t * c,mixer_info * mi)1343 sound_mixer_info(audio_client_t *c, mixer_info *mi)
1344 {
1345 audio_dev_t *d;
1346 const char *name;
1347
1348 d = auclnt_get_dev(c);
1349
1350 name = auclnt_get_dev_name(d);
1351 (void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1352 (void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1353 (void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1354 mi->modify_counter = (int)auclnt_dev_get_serial(d);
1355 mi->card_number = auclnt_get_dev_index(d);
1356 mi->port_number = 0;
1357 return (0);
1358 }
1359
1360 static int
sound_mixer_read_devmask(audio_client_t * c,int * devmask)1361 sound_mixer_read_devmask(audio_client_t *c, int *devmask)
1362 {
1363 _NOTE(ARGUNUSED(c));
1364 *devmask = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_IGAIN;
1365 return (0);
1366 }
1367
1368 static int
sound_mixer_read_recmask(audio_client_t * c,int * recmask)1369 sound_mixer_read_recmask(audio_client_t *c, int *recmask)
1370 {
1371 _NOTE(ARGUNUSED(c));
1372 *recmask = 0;
1373 return (0);
1374 }
1375
1376 static int
sound_mixer_read_recsrc(audio_client_t * c,int * recsrc)1377 sound_mixer_read_recsrc(audio_client_t *c, int *recsrc)
1378 {
1379 _NOTE(ARGUNUSED(c));
1380 *recsrc = 0;
1381 return (0);
1382 }
1383
1384 static int
sound_mixer_read_caps(audio_client_t * c,int * caps)1385 sound_mixer_read_caps(audio_client_t *c, int *caps)
1386 {
1387 _NOTE(ARGUNUSED(c));
1388 /* single recording source... sort of */
1389 *caps = SOUND_CAP_EXCL_INPUT;
1390 return (0);
1391 }
1392
1393 static int
sndctl_mixerinfo(audio_client_t * c,oss_mixerinfo * mi)1394 sndctl_mixerinfo(audio_client_t *c, oss_mixerinfo *mi)
1395 {
1396 audio_dev_t *d;
1397 ossdev_t *odev;
1398 const char *name;
1399 int n;
1400 boolean_t release = B_FALSE;
1401
1402 if ((n = mi->dev) == -1) {
1403 release = B_FALSE;
1404 d = auclnt_get_dev(c);
1405 n = auclnt_get_dev_index(d);
1406 } else {
1407 release = B_TRUE;
1408 n++;
1409 d = auclnt_hold_dev_by_index(n);
1410 }
1411
1412 bzero(mi, sizeof (*mi));
1413 mi->dev = n - 1;
1414
1415 if (d == NULL) {
1416 mi->card_number = n;
1417 mi->enabled = 0;
1418 mi->legacy_device = -1;
1419 (void) snprintf(mi->name, sizeof (mi->name), "<removed>");
1420 (void) snprintf(mi->id, sizeof (mi->id), "<removed>");
1421 return (0);
1422 }
1423
1424 if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1425 if (release)
1426 auclnt_release_dev(d);
1427 return (EINVAL);
1428 }
1429
1430 name = auclnt_get_dev_name(d);
1431 (void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1432 (void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1433 (void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1434 mi->modify_counter = (int)auclnt_dev_get_serial(d);
1435 mi->card_number = auclnt_get_dev_index(d);
1436 mi->legacy_device = auclnt_get_dev_number(d);
1437 if (mi->legacy_device >= 0) {
1438 (void) snprintf(mi->devnode, sizeof (mi->devnode),
1439 "/dev/sound/%s:%dmixer",
1440 auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1441 mi->enabled = 1;
1442 } else {
1443 /* special nodes use generic sndstat node */
1444 (void) snprintf(mi->devnode, sizeof (mi->devnode),
1445 "/dev/sndstat");
1446 mi->enabled = 0;
1447 }
1448 mi->nrext = odev->d_nctrl;
1449
1450 if (release)
1451 auclnt_release_dev(d);
1452
1453 return (0);
1454 }
1455
1456 static int
sndctl_dsp_getblksize(audio_client_t * c,int * fragsz)1457 sndctl_dsp_getblksize(audio_client_t *c, int *fragsz)
1458 {
1459 int oflag = auclnt_get_oflag(c);
1460
1461 if (oflag & FWRITE)
1462 *fragsz = auclnt_get_fragsz(auclnt_output_stream(c));
1463 else if (oflag & FREAD)
1464 *fragsz = auclnt_get_fragsz(auclnt_input_stream(c));
1465
1466 return (0);
1467 }
1468
1469 static int
sndctl_dsp_getospace(audio_client_t * c,audio_buf_info * bi)1470 sndctl_dsp_getospace(audio_client_t *c, audio_buf_info *bi)
1471 {
1472 audio_stream_t *sp;
1473 unsigned n;
1474
1475 if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1476 return (EACCES);
1477 }
1478
1479 sp = auclnt_output_stream(c);
1480 n = auclnt_get_nframes(sp) - auclnt_get_count(sp);
1481
1482 bi->fragsize = auclnt_get_fragsz(sp);
1483 bi->fragstotal = auclnt_get_nfrags(sp);
1484 bi->bytes = (n * auclnt_get_framesz(sp));
1485 bi->fragments = bi->bytes / bi->fragsize;
1486
1487 return (0);
1488 }
1489
1490 static int
sndctl_dsp_getispace(audio_client_t * c,audio_buf_info * bi)1491 sndctl_dsp_getispace(audio_client_t *c, audio_buf_info *bi)
1492 {
1493 audio_stream_t *sp;
1494 unsigned n;
1495
1496 if ((auclnt_get_oflag(c) & FREAD) == 0) {
1497 return (EACCES);
1498 }
1499
1500 sp = auclnt_input_stream(c);
1501 n = auclnt_get_count(sp);
1502
1503 bi->fragsize = auclnt_get_fragsz(sp);
1504 bi->fragstotal = auclnt_get_nfrags(sp);
1505 bi->bytes = (n * auclnt_get_framesz(sp));
1506 bi->fragments = bi->bytes / bi->fragsize;
1507
1508 return (0);
1509 }
1510
1511 static int
sndctl_dsp_getodelay(audio_client_t * c,int * bytes)1512 sndctl_dsp_getodelay(audio_client_t *c, int *bytes)
1513 {
1514 unsigned framesz;
1515 unsigned slen, flen;
1516
1517 if (auclnt_get_oflag(c) & FWRITE) {
1518 audio_stream_t *sp = auclnt_output_stream(c);
1519 framesz = auclnt_get_framesz(sp);
1520 auclnt_get_output_qlen(c, &slen, &flen);
1521 *bytes = (slen + flen) * framesz;
1522 } else {
1523 *bytes = 0;
1524 }
1525 return (0);
1526 }
1527
1528 static int
sndctl_dsp_current_iptr(audio_client_t * c,oss_count_t * count)1529 sndctl_dsp_current_iptr(audio_client_t *c, oss_count_t *count)
1530 {
1531 if (auclnt_get_oflag(c) & FREAD) {
1532 count->samples = auclnt_get_samples(auclnt_input_stream(c));
1533 count->fifo_samples = 0; /* not quite accurate */
1534 } else {
1535 count->samples = 0;
1536 count->fifo_samples = 0;
1537 }
1538 return (0);
1539 }
1540
1541 static int
sndctl_dsp_current_optr(audio_client_t * c,oss_count_t * count)1542 sndctl_dsp_current_optr(audio_client_t *c, oss_count_t *count)
1543 {
1544 unsigned samples, fifo;
1545
1546 if (auclnt_get_oflag(c) & FWRITE) {
1547 auclnt_get_output_qlen(c, &samples, &fifo);
1548 count->samples = samples;
1549 count->fifo_samples = fifo;
1550 } else {
1551 count->samples = 0;
1552 count->fifo_samples = 0;
1553 }
1554 return (0);
1555 }
1556
1557 static int
sndctl_dsp_getoptr(audio_client_t * c,count_info * ci)1558 sndctl_dsp_getoptr(audio_client_t *c, count_info *ci)
1559 {
1560 audio_stream_t *sp;
1561 unsigned framesz;
1562 unsigned fragsz;
1563
1564 bzero(ci, sizeof (*ci));
1565 if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1566 return (0);
1567 }
1568 sp = auclnt_output_stream(c);
1569 framesz = auclnt_get_framesz(sp);
1570 fragsz = auclnt_get_fragsz(sp);
1571 ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1572 auclnt_set_samples(sp, 0);
1573 ci->bytes = auclnt_get_tail(sp) * framesz;
1574 ci->ptr = auclnt_get_tidx(sp) * framesz;
1575 return (0);
1576 }
1577
1578 static int
sndctl_dsp_getiptr(audio_client_t * c,count_info * ci)1579 sndctl_dsp_getiptr(audio_client_t *c, count_info *ci)
1580 {
1581 audio_stream_t *sp;
1582 unsigned framesz;
1583 unsigned fragsz;
1584
1585 bzero(ci, sizeof (*ci));
1586 if ((auclnt_get_oflag(c) & FREAD) == 0) {
1587 return (0);
1588 }
1589 sp = auclnt_input_stream(c);
1590 framesz = auclnt_get_framesz(sp);
1591 fragsz = auclnt_get_fragsz(sp);
1592 ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1593 auclnt_set_samples(sp, 0);
1594 ci->bytes = auclnt_get_head(sp) * framesz;
1595 ci->ptr = auclnt_get_hidx(sp) * framesz;
1596 return (0);
1597 }
1598
1599 static int
sndctl_dsp_geterror(audio_client_t * c,audio_errinfo * bi)1600 sndctl_dsp_geterror(audio_client_t *c, audio_errinfo *bi)
1601 {
1602 audio_stream_t *sp;
1603 unsigned fragsz;
1604 /*
1605 * Note: The use of this structure is unsafe... different
1606 * meanings for error codes are used by different implementations,
1607 * according to the spec. (Even different versions of the same
1608 * implementation could have different values.)
1609 *
1610 * Rather than try to come up with a reliable solution here, we
1611 * don't use it. If you want to report errors, or see the result
1612 * of errors, use syslog.
1613 */
1614 bzero(bi, sizeof (*bi));
1615
1616 sp = auclnt_output_stream(c);
1617 fragsz = max(auclnt_get_fragsz(sp), 1);
1618 bi->play_underruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1619 fragsz);
1620 auclnt_set_errors(sp, 0);
1621
1622 sp = auclnt_input_stream(c);
1623 fragsz = max(auclnt_get_fragsz(sp), 1);
1624 bi->rec_overruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1625 fragsz);
1626 auclnt_set_errors(sp, 0);
1627
1628 return (0);
1629 }
1630
1631 static int
sndctl_sun_send_number(audio_client_t * c,int * num,cred_t * cr)1632 sndctl_sun_send_number(audio_client_t *c, int *num, cred_t *cr)
1633 {
1634 audio_dev_t *dev;
1635 int rv;
1636
1637 if ((rv = drv_priv(cr)) != 0) {
1638 return (rv);
1639 }
1640
1641 dev = auclnt_get_dev(c);
1642 auclnt_set_dev_number(dev, *num);
1643 return (0);
1644 }
1645
1646 static int
oss_getversion(int * versp)1647 oss_getversion(int *versp)
1648 {
1649 *versp = OSS_VERSION;
1650 return (0);
1651 }
1652
1653 static int
oss_ioctl(audio_client_t * c,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1654 oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
1655 int *rvalp)
1656 {
1657 int sz;
1658 void *data;
1659 int rv = 0;
1660
1661 _NOTE(ARGUNUSED(credp));
1662
1663 sz = OSSIOC_GETSZ(cmd);
1664
1665 if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
1666 if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1667 return (ENOMEM);
1668 }
1669 } else {
1670 sz = 0;
1671 }
1672
1673 if (cmd & OSSIOC_IN) {
1674 if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
1675 goto done;
1676 }
1677 }
1678
1679 switch (cmd) {
1680 /*
1681 * DSP specific ioctls
1682 */
1683 case SNDCTL_DSP_HALT:
1684 rv = sndctl_dsp_halt(c);
1685 break;
1686
1687 case SNDCTL_DSP_SYNC:
1688 rv = sndctl_dsp_sync(c);
1689 break;
1690
1691 case SNDCTL_DSP_SPEED:
1692 rv = sndctl_dsp_speed(c, (int *)data);
1693 break;
1694 case SNDCTL_DSP_SETFMT:
1695 rv = sndctl_dsp_setfmt(c, (int *)data);
1696 break;
1697 case SNDCTL_DSP_GETFMTS:
1698 rv = sndctl_dsp_getfmts(c, (int *)data);
1699 break;
1700 case SNDCTL_DSP_STEREO:
1701 rv = sndctl_dsp_stereo(c, (int *)data);
1702 break;
1703 case SNDCTL_DSP_CHANNELS:
1704 rv = sndctl_dsp_channels(c, (int *)data);
1705 break;
1706 case SNDCTL_DSP_POST:
1707 rv = sndctl_dsp_post(c);
1708 break;
1709 case SNDCTL_DSP_GETCAPS:
1710 rv = sndctl_dsp_getcaps(c, (int *)data);
1711 break;
1712 case SNDCTL_DSP_GETTRIGGER:
1713 rv = sndctl_dsp_gettrigger(c, (int *)data);
1714 break;
1715 case SNDCTL_DSP_SETTRIGGER:
1716 rv = sndctl_dsp_settrigger(c, (int *)data);
1717 break;
1718 case SNDCTL_DSP_GETPLAYVOL:
1719 case SOUND_MIXER_READ_VOLUME: /* legacy mixer on dsp */
1720 case SOUND_MIXER_READ_PCM: /* legacy mixer on dsp */
1721 case SOUND_MIXER_READ_OGAIN: /* legacy mixer on dsp */
1722 rv = sndctl_dsp_getplayvol(c, (int *)data);
1723 break;
1724 case SOUND_MIXER_WRITE_VOLUME: /* legacy mixer on dsp */
1725 case SOUND_MIXER_WRITE_PCM: /* legacy mixer on dsp */
1726 case SOUND_MIXER_WRITE_OGAIN: /* legacy mixer on dsp */
1727 rv = sound_mixer_write_ogain(c, (int *)data);
1728 break;
1729 case SNDCTL_DSP_SETPLAYVOL:
1730 rv = sndctl_dsp_setplayvol(c, (int *)data);
1731 break;
1732 case SNDCTL_DSP_READCTL:
1733 rv = sndctl_dsp_readctl(c, (oss_digital_control *)data);
1734 break;
1735 case SNDCTL_DSP_WRITECTL:
1736 rv = sndctl_dsp_writectl(c, (oss_digital_control *)data);
1737 break;
1738 case SNDCTL_DSP_COOKEDMODE:
1739 rv = sndctl_dsp_cookedmode(c, (int *)data);
1740 break;
1741 case SNDCTL_DSP_SILENCE:
1742 rv = sndctl_dsp_silence(c);
1743 break;
1744 case SNDCTL_DSP_SKIP:
1745 rv = sndctl_dsp_skip(c);
1746 break;
1747 case SNDCTL_DSP_HALT_INPUT:
1748 rv = sndctl_dsp_halt_input(c);
1749 break;
1750 case SNDCTL_DSP_HALT_OUTPUT:
1751 rv = sndctl_dsp_halt_output(c);
1752 break;
1753 case SNDCTL_DSP_GET_RECSRC_NAMES:
1754 rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
1755 break;
1756 case SNDCTL_DSP_SETFRAGMENT:
1757 rv = sndctl_dsp_setfragment(c, (int *)data);
1758 break;
1759 case SNDCTL_DSP_GET_RECSRC:
1760 rv = sndctl_dsp_get_recsrc(c, (int *)data);
1761 break;
1762 case SNDCTL_DSP_SET_RECSRC:
1763 rv = sndctl_dsp_set_recsrc(c, (int *)data);
1764 break;
1765 case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1766 rv = sndctl_dsp_get_playtgt_names(c,
1767 (oss_mixer_enuminfo *)data);
1768 break;
1769 case SNDCTL_DSP_GET_PLAYTGT:
1770 rv = sndctl_dsp_get_playtgt(c, (int *)data);
1771 break;
1772 case SNDCTL_DSP_SET_PLAYTGT:
1773 rv = sndctl_dsp_set_playtgt(c, (int *)data);
1774 break;
1775 case SNDCTL_DSP_GETRECVOL:
1776 case SOUND_MIXER_READ_RECGAIN: /* legacy mixer on dsp */
1777 case SOUND_MIXER_READ_RECLEV: /* legacy mixer on dsp */
1778 case SOUND_MIXER_READ_IGAIN: /* legacy mixer on dsp */
1779 rv = sndctl_dsp_getrecvol(c, (int *)data);
1780 break;
1781 case SOUND_MIXER_WRITE_RECGAIN: /* legacy mixer on dsp */
1782 case SOUND_MIXER_WRITE_RECLEV: /* legacy mixer on dsp */
1783 case SOUND_MIXER_WRITE_IGAIN: /* legacy mixer on dsp */
1784 rv = sound_mixer_write_igain(c, (int *)data);
1785 break;
1786 case SNDCTL_DSP_SETRECVOL:
1787 rv = sndctl_dsp_setrecvol(c, (int *)data);
1788 break;
1789 case SNDCTL_DSP_SUBDIVIDE: /* Ignored */
1790 case SNDCTL_DSP_SETDUPLEX: /* Ignored */
1791 case SNDCTL_DSP_LOW_WATER: /* Ignored */
1792 case SNDCTL_DSP_PROFILE: /* Ignored */
1793 rv = 0;
1794 break;
1795 case SNDCTL_DSP_POLICY:
1796 rv = sndctl_dsp_policy(c, (int *)data);
1797 break;
1798 case SNDCTL_DSP_GETBLKSIZE:
1799 rv = sndctl_dsp_getblksize(c, (int *)data);
1800 break;
1801 case SNDCTL_DSP_GETOSPACE:
1802 rv = sndctl_dsp_getospace(c, (audio_buf_info *)data);
1803 break;
1804 case SNDCTL_DSP_GETISPACE:
1805 rv = sndctl_dsp_getispace(c, (audio_buf_info *)data);
1806 break;
1807 case SNDCTL_DSP_GETODELAY:
1808 rv = sndctl_dsp_getodelay(c, (int *)data);
1809 break;
1810 case SNDCTL_DSP_GETOPTR:
1811 rv = sndctl_dsp_getoptr(c, (count_info *)data);
1812 break;
1813 case SNDCTL_DSP_GETIPTR:
1814 rv = sndctl_dsp_getiptr(c, (count_info *)data);
1815 break;
1816 case SNDCTL_DSP_GETERROR:
1817 rv = sndctl_dsp_geterror(c, (audio_errinfo *)data);
1818 break;
1819 case SNDCTL_DSP_CURRENT_IPTR:
1820 rv = sndctl_dsp_current_iptr(c, (oss_count_t *)data);
1821 break;
1822 case SNDCTL_DSP_CURRENT_OPTR:
1823 rv = sndctl_dsp_current_optr(c, (oss_count_t *)data);
1824 break;
1825
1826 /*
1827 * Shared ioctls with /dev/mixer.
1828 */
1829 case OSS_GETVERSION:
1830 rv = oss_getversion((int *)data);
1831 break;
1832 case SNDCTL_CARDINFO:
1833 rv = sndctl_cardinfo(c, (oss_card_info *)data);
1834 break;
1835 case SNDCTL_ENGINEINFO:
1836 case SNDCTL_AUDIOINFO:
1837 case SNDCTL_AUDIOINFO_EX:
1838 rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
1839 break;
1840 case SNDCTL_SYSINFO:
1841 rv = sndctl_sysinfo((oss_sysinfo *)data);
1842 break;
1843 case SNDCTL_MIXERINFO:
1844 rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
1845 break;
1846 case SOUND_MIXER_INFO:
1847 rv = sound_mixer_info(c, (mixer_info *)data);
1848 break;
1849
1850 /*
1851 * These are mixer ioctls that are virtualized for the DSP
1852 * device. They are accessible via either /dev/mixer or
1853 * /dev/dsp.
1854 */
1855 case SOUND_MIXER_READ_RECSRC:
1856 case SOUND_MIXER_WRITE_RECSRC:
1857 rv = sound_mixer_read_recsrc(c, (int *)data);
1858 break;
1859
1860 case SOUND_MIXER_READ_DEVMASK:
1861 case SOUND_MIXER_READ_STEREODEVS:
1862 rv = sound_mixer_read_devmask(c, (int *)data);
1863 break;
1864
1865 case SOUND_MIXER_READ_RECMASK:
1866 rv = sound_mixer_read_recmask(c, (int *)data);
1867 break;
1868
1869 case SOUND_MIXER_READ_CAPS:
1870 rv = sound_mixer_read_caps(c, (int *)data);
1871 break;
1872
1873 /*
1874 * Ioctls we have chosen not to support for now. Some
1875 * of these are of legacy interest only.
1876 */
1877 case SNDCTL_SETSONG:
1878 case SNDCTL_GETSONG:
1879 case SNDCTL_DSP_SYNCGROUP:
1880 case SNDCTL_DSP_SYNCSTART:
1881 case SNDCTL_DSP_GET_CHNORDER:
1882 case SNDCTL_DSP_SET_CHNORDER:
1883 case SNDCTL_DSP_GETIPEAKS:
1884 case SNDCTL_DSP_GETOPEAKS:
1885 case SNDCTL_DSP_GETCHANNELMASK:
1886 case SNDCTL_DSP_BIND_CHANNEL:
1887 case SNDCTL_DSP_SETSYNCRO:
1888 case SNDCTL_DSP_NONBLOCK:
1889 default:
1890 rv = EINVAL;
1891 break;
1892 }
1893
1894 if ((rv == 0) && (cmd & OSSIOC_OUT)) {
1895 rv = ddi_copyout(data, (void *)arg, sz, mode);
1896 }
1897 if (rv == 0) {
1898 *rvalp = 0;
1899 }
1900
1901 done:
1902 if (sz) {
1903 kmem_free(data, sz);
1904 }
1905 return (rv);
1906 }
1907
1908 static void
oss_output(audio_client_t * c)1909 oss_output(audio_client_t *c)
1910 {
1911 auclnt_pollwakeup(c, POLLOUT);
1912 }
1913
1914 static void
oss_input(audio_client_t * c)1915 oss_input(audio_client_t *c)
1916 {
1917 auclnt_pollwakeup(c, POLLIN | POLLRDNORM);
1918 }
1919
1920 static int
ossmix_open(audio_client_t * c,int oflag)1921 ossmix_open(audio_client_t *c, int oflag)
1922 {
1923 int rv;
1924 ossclient_t *sc;
1925 ossdev_t *odev;
1926
1927 _NOTE(ARGUNUSED(oflag));
1928
1929 if ((rv = auclnt_open(c, 0)) != 0) {
1930 return (rv);
1931 }
1932
1933 if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
1934 return (ENOMEM);
1935 }
1936 sc->o_ss_sz = 8192;
1937 if ((sc->o_ss_buf = kmem_zalloc(sc->o_ss_sz, KM_NOSLEEP)) == NULL) {
1938 kmem_free(sc, sizeof (*sc));
1939 return (ENOMEM);
1940 }
1941 auclnt_set_private(c, sc);
1942
1943 odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
1944
1945 /* set a couple of common fields */
1946 sc->o_client = c;
1947 sc->o_ossdev = odev;
1948
1949 return (rv);
1950 }
1951
1952 static void
ossmix_close(audio_client_t * c)1953 ossmix_close(audio_client_t *c)
1954 {
1955 ossclient_t *sc;
1956
1957 sc = auclnt_get_private(c);
1958
1959 kmem_free(sc->o_ss_buf, sc->o_ss_sz);
1960 kmem_free(sc, sizeof (*sc));
1961
1962 auclnt_close(c);
1963 }
1964
1965 static int
sndctl_mix_nrext(audio_client_t * c,int * ncp)1966 sndctl_mix_nrext(audio_client_t *c, int *ncp)
1967 {
1968 audio_dev_t *d;
1969 ossdev_t *odev;
1970
1971 d = auclnt_get_dev(c);
1972
1973 if ((*ncp != -1) && (*ncp != (auclnt_get_dev_index(d) - 1))) {
1974 return (ENXIO);
1975 }
1976
1977 if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1978 return (EINVAL);
1979 }
1980
1981 *ncp = odev->d_nctrl;
1982
1983 return (0);
1984 }
1985
1986 static int
sndctl_mix_extinfo(audio_client_t * c,oss_mixext * pext)1987 sndctl_mix_extinfo(audio_client_t *c, oss_mixext *pext)
1988 {
1989 audio_dev_t *d;
1990 ossdev_t *odev;
1991 int rv = 0;
1992 int dev;
1993
1994 d = auclnt_get_dev(c);
1995
1996 if (((dev = pext->dev) != -1) && (dev != (auclnt_get_dev_index(d) - 1)))
1997 return (ENXIO);
1998
1999 if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2000 (pext->ctrl >= odev->d_nctrl)) {
2001 return (EINVAL);
2002 }
2003
2004 bcopy(&odev->d_exts[pext->ctrl], pext, sizeof (*pext));
2005 pext->enumbit = 0;
2006 pext->dev = dev;
2007
2008 return (rv);
2009 }
2010
2011 static int
sndctl_mix_enuminfo(audio_client_t * c,oss_mixer_enuminfo * ei)2012 sndctl_mix_enuminfo(audio_client_t *c, oss_mixer_enuminfo *ei)
2013 {
2014 audio_dev_t *d;
2015 audio_ctrl_desc_t desc;
2016 audio_ctrl_t *ctrl;
2017 ossdev_t *odev;
2018 uint64_t mask;
2019 int bit;
2020 ushort_t nxt;
2021
2022 d = auclnt_get_dev(c);
2023
2024 if ((ei->dev != -1) && (ei->dev != (auclnt_get_dev_index(d) - 1)))
2025 return (ENXIO);
2026
2027 if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2028 (ei->ctrl >= odev->d_nctrl) ||
2029 (odev->d_exts[ei->ctrl].type != MIXT_ENUM) ||
2030 ((ctrl = odev->d_ctrls[ei->ctrl]) == NULL) ||
2031 (auclnt_control_describe(ctrl, &desc) != 0)) {
2032 return (EINVAL);
2033 }
2034
2035 mask = desc.acd_maxvalue;
2036 bit = 0;
2037 nxt = 0;
2038 ei->nvalues = 0;
2039 bzero(ei->strings, sizeof (ei->strings));
2040 bzero(ei->strindex, sizeof (ei->strindex));
2041
2042 while (mask) {
2043 const char *name = desc.acd_enum[bit];
2044 nxt = oss_set_enum(ei, nxt, name ? name : "");
2045 mask >>= 1;
2046 bit++;
2047 }
2048
2049 return (0);
2050 }
2051
2052 static int
sndctl_mix_read(audio_client_t * c,oss_mixer_value * vr)2053 sndctl_mix_read(audio_client_t *c, oss_mixer_value *vr)
2054 {
2055 int rv;
2056 uint64_t v;
2057 audio_dev_t *d;
2058 audio_ctrl_t *ctrl;
2059 ossdev_t *odev;
2060
2061 d = auclnt_get_dev(c);
2062
2063 if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2064 return (ENXIO);
2065
2066 if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2067 (vr->ctrl >= odev->d_nctrl) ||
2068 ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2069 return (EINVAL);
2070 }
2071 if ((rv = auclnt_control_read(ctrl, &v)) == 0) {
2072 switch (odev->d_exts[vr->ctrl].type) {
2073 case MIXT_ENUM:
2074 /* translate this from an enum style bit mask */
2075 vr->value = ddi_ffs((unsigned long)v) - 1;
2076 break;
2077 case MIXT_STEREOSLIDER:
2078 vr->value = (int)ddi_swap16(v & 0xffff);
2079 break;
2080 case MIXT_MONOSLIDER:
2081 vr->value = (int)(v | (v << 8));
2082 break;
2083 case MIXT_ONOFF:
2084 /* this could be simple, or could be part of a multi */
2085 if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2086 uint64_t mask;
2087 mask = 1;
2088 mask <<= (odev->d_exts[vr->ctrl].enumbit);
2089 vr->value = (v & mask) ? 1 : 0;
2090 } else {
2091 vr->value = v ? 1 : 0;
2092 }
2093 break;
2094
2095 default:
2096 vr->value = (int)v;
2097 break;
2098 }
2099 }
2100
2101 return (rv);
2102 }
2103
2104 static int
sndctl_mix_write(audio_client_t * c,oss_mixer_value * vr)2105 sndctl_mix_write(audio_client_t *c, oss_mixer_value *vr)
2106 {
2107 int rv;
2108 uint64_t v;
2109 audio_dev_t *d;
2110 audio_ctrl_t *ctrl;
2111 ossdev_t *odev;
2112
2113 d = auclnt_get_dev(c);
2114
2115 if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2116 return (ENXIO);
2117
2118 if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2119 (vr->ctrl >= odev->d_nctrl) ||
2120 ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2121 return (EINVAL);
2122 }
2123
2124 switch (odev->d_exts[vr->ctrl].type) {
2125 case MIXT_ONOFF:
2126 /* this could be standalone, or it could be part of a multi */
2127 if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2128 uint64_t mask;
2129 if ((rv = auclnt_control_read(ctrl, &v)) != 0) {
2130 return (EINVAL);
2131 }
2132 mask = 1;
2133 mask <<= (odev->d_exts[vr->ctrl].enumbit);
2134 if (vr->value) {
2135 v |= mask;
2136 } else {
2137 v &= ~mask;
2138 }
2139 } else {
2140 v = vr->value;
2141 }
2142 break;
2143 case MIXT_ENUM:
2144 /* translate this to an enum style bit mask */
2145 v = 1U << vr->value;
2146 break;
2147 case MIXT_MONOSLIDER:
2148 /* mask off high order bits */
2149 v = vr->value & 0xff;
2150 break;
2151 case MIXT_STEREOSLIDER:
2152 /* OSS uses reverse byte ordering */
2153 v = vr->value;
2154 v = ddi_swap16(vr->value & 0xffff);
2155 break;
2156 default:
2157 v = vr->value;
2158 }
2159 rv = auclnt_control_write(ctrl, v);
2160
2161 return (rv);
2162 }
2163
2164 static int
sndctl_mix_nrmix(audio_client_t * c,int * nmixp)2165 sndctl_mix_nrmix(audio_client_t *c, int *nmixp)
2166 {
2167 _NOTE(ARGUNUSED(c));
2168 *nmixp = oss_cnt_devs() - 1;
2169 return (0);
2170 }
2171
2172 static int
ossmix_ioctl(audio_client_t * c,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)2173 ossmix_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
2174 int *rvalp)
2175 {
2176 int sz;
2177 void *data;
2178 int rv = 0;
2179
2180 sz = OSSIOC_GETSZ(cmd);
2181
2182 if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
2183 if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
2184 return (ENOMEM);
2185 }
2186 } else {
2187 sz = 0;
2188 }
2189
2190 if (cmd & OSSIOC_IN) {
2191 if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
2192 goto done;
2193 }
2194 }
2195
2196 switch (cmd) {
2197 /*
2198 * Mixer specific ioctls
2199 */
2200 case SNDCTL_MIX_NREXT:
2201 rv = sndctl_mix_nrext(c, (int *)data);
2202 break;
2203 case SNDCTL_MIX_EXTINFO:
2204 rv = sndctl_mix_extinfo(c, (oss_mixext *)data);
2205 break;
2206 case SNDCTL_MIX_ENUMINFO:
2207 rv = sndctl_mix_enuminfo(c, (oss_mixer_enuminfo *)data);
2208 break;
2209 case SNDCTL_MIX_READ:
2210 rv = sndctl_mix_read(c, (oss_mixer_value *)data);
2211 break;
2212 case SNDCTL_MIX_WRITE:
2213 rv = sndctl_mix_write(c, (oss_mixer_value *)data);
2214 break;
2215 case SNDCTL_MIX_NRMIX:
2216 rv = sndctl_mix_nrmix(c, (int *)data);
2217 break;
2218
2219 /*
2220 * Legacy ioctls. These are treated as soft values only,
2221 * and do not affect global hardware state. For use by
2222 * legacy DSP applications.
2223 */
2224 case SOUND_MIXER_READ_VOLUME:
2225 case SOUND_MIXER_READ_PCM:
2226 case SOUND_MIXER_READ_OGAIN:
2227 rv = sndctl_dsp_getplayvol(c, (int *)data);
2228 break;
2229
2230 case SOUND_MIXER_WRITE_VOLUME:
2231 case SOUND_MIXER_WRITE_PCM:
2232 case SOUND_MIXER_WRITE_OGAIN:
2233 rv = sound_mixer_write_ogain(c, (int *)data);
2234 break;
2235
2236 case SOUND_MIXER_READ_RECGAIN:
2237 case SOUND_MIXER_READ_RECLEV:
2238 case SOUND_MIXER_READ_IGAIN:
2239 rv = sndctl_dsp_getrecvol(c, (int *)data);
2240 break;
2241
2242 case SOUND_MIXER_WRITE_RECGAIN:
2243 case SOUND_MIXER_WRITE_RECLEV:
2244 case SOUND_MIXER_WRITE_IGAIN:
2245 rv = sound_mixer_write_igain(c, (int *)data);
2246 break;
2247
2248 case SOUND_MIXER_READ_RECSRC:
2249 case SOUND_MIXER_WRITE_RECSRC:
2250 rv = sound_mixer_read_recsrc(c, (int *)data);
2251 break;
2252
2253 case SOUND_MIXER_READ_DEVMASK:
2254 case SOUND_MIXER_READ_STEREODEVS:
2255 rv = sound_mixer_read_devmask(c, (int *)data);
2256 break;
2257
2258 case SOUND_MIXER_READ_RECMASK:
2259 rv = sound_mixer_read_recmask(c, (int *)data);
2260 break;
2261
2262 case SOUND_MIXER_READ_CAPS:
2263 rv = sound_mixer_read_caps(c, (int *)data);
2264 break;
2265
2266 /*
2267 * Common ioctls shared with DSP
2268 */
2269 case OSS_GETVERSION:
2270 rv = oss_getversion((int *)data);
2271 break;
2272
2273 case SNDCTL_CARDINFO:
2274 rv = sndctl_cardinfo(c, (oss_card_info *)data);
2275 break;
2276
2277 case SNDCTL_ENGINEINFO:
2278 case SNDCTL_AUDIOINFO:
2279 case SNDCTL_AUDIOINFO_EX:
2280 rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
2281 break;
2282
2283 case SNDCTL_SYSINFO:
2284 rv = sndctl_sysinfo((oss_sysinfo *)data);
2285 break;
2286
2287 case SNDCTL_MIXERINFO:
2288 rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
2289 break;
2290
2291 case SOUND_MIXER_INFO:
2292 rv = sound_mixer_info(c, (mixer_info *)data);
2293 break;
2294
2295 case SNDCTL_MIX_DESCRIPTION: /* NOT SUPPORTED: tooltip */
2296 rv = EIO; /* OSS returns EIO for this one */
2297 break;
2298
2299 /*
2300 * Special implementation-private ioctls.
2301 */
2302 case SNDCTL_SUN_SEND_NUMBER:
2303 rv = sndctl_sun_send_number(c, (int *)data, credp);
2304 break;
2305
2306 /*
2307 * Legacy ioctls we don't support.
2308 */
2309 case SOUND_MIXER_WRITE_MONGAIN:
2310 case SOUND_MIXER_READ_MONGAIN:
2311 case SOUND_MIXER_READ_BASS:
2312 case SOUND_MIXER_READ_TREBLE:
2313 case SOUND_MIXER_READ_SPEAKER:
2314 case SOUND_MIXER_READ_LINE:
2315 case SOUND_MIXER_READ_MIC:
2316 case SOUND_MIXER_READ_CD:
2317 case SOUND_MIXER_READ_IMIX:
2318 case SOUND_MIXER_READ_ALTPCM:
2319 case SOUND_MIXER_READ_SYNTH:
2320 case SOUND_MIXER_READ_LINE1:
2321 case SOUND_MIXER_READ_LINE2:
2322 case SOUND_MIXER_READ_LINE3:
2323 case SOUND_MIXER_WRITE_BASS:
2324 case SOUND_MIXER_WRITE_TREBLE:
2325 case SOUND_MIXER_WRITE_SPEAKER:
2326 case SOUND_MIXER_WRITE_LINE:
2327 case SOUND_MIXER_WRITE_MIC:
2328 case SOUND_MIXER_WRITE_CD:
2329 case SOUND_MIXER_WRITE_IMIX:
2330 case SOUND_MIXER_WRITE_ALTPCM:
2331 case SOUND_MIXER_WRITE_SYNTH:
2332 case SOUND_MIXER_WRITE_LINE1:
2333 case SOUND_MIXER_WRITE_LINE2:
2334 case SOUND_MIXER_WRITE_LINE3:
2335 /*
2336 * Additional ioctls we *could* support, but don't.
2337 */
2338 case SNDCTL_SETSONG:
2339 case SNDCTL_SETLABEL:
2340 case SNDCTL_GETSONG:
2341 case SNDCTL_GETLABEL:
2342 case SNDCTL_MIDIINFO:
2343 case SNDCTL_SETNAME:
2344 default:
2345 rv = EINVAL;
2346 break;
2347 }
2348
2349 if ((rv == 0) && (cmd & OSSIOC_OUT)) {
2350 rv = ddi_copyout(data, (void *)arg, sz, mode);
2351 }
2352 if (rv == 0) {
2353 *rvalp = 0;
2354 }
2355
2356 done:
2357 if (sz) {
2358 kmem_free(data, sz);
2359 }
2360 return (rv);
2361 }
2362
2363 static void *
oss_dev_init(audio_dev_t * dev)2364 oss_dev_init(audio_dev_t *dev)
2365 {
2366 ossdev_t *odev;
2367
2368 odev = kmem_zalloc(sizeof (*odev), KM_SLEEP);
2369 odev->d_dev = dev;
2370
2371 mutex_init(&odev->d_mx, NULL, MUTEX_DRIVER, NULL);
2372 cv_init(&odev->d_cv, NULL, CV_DRIVER, NULL);
2373 oss_alloc_controls(odev);
2374
2375 return (odev);
2376 }
2377
2378 static void
oss_dev_fini(void * arg)2379 oss_dev_fini(void *arg)
2380 {
2381 ossdev_t *odev = arg;
2382
2383 if (odev != NULL) {
2384 oss_free_controls(odev);
2385 mutex_destroy(&odev->d_mx);
2386 cv_destroy(&odev->d_cv);
2387 kmem_free(odev, sizeof (*odev));
2388 }
2389 }
2390
2391 static void
sndstat_printf(ossclient_t * oc,const char * fmt,...)2392 sndstat_printf(ossclient_t *oc, const char *fmt, ...)
2393 {
2394 va_list va;
2395
2396 va_start(va, fmt);
2397 (void) vsnprintf(oc->o_ss_buf + oc->o_ss_len,
2398 oc->o_ss_sz - oc->o_ss_len, fmt, va);
2399 va_end(va);
2400 oc->o_ss_len = strlen(oc->o_ss_buf);
2401 }
2402
2403 static int
sndstat_dev_walker(audio_dev_t * d,void * arg)2404 sndstat_dev_walker(audio_dev_t *d, void *arg)
2405 {
2406 ossclient_t *oc = arg;
2407 const char *capstr;
2408 unsigned cap;
2409
2410 cap = auclnt_get_dev_capab(d);
2411
2412 if (cap & AUDIO_CLIENT_CAP_DUPLEX) {
2413 capstr = "DUPLEX";
2414 } else if ((cap & AUDIO_CLIENT_CAP_PLAY) &&
2415 (cap & AUDIO_CLIENT_CAP_RECORD)) {
2416 capstr = "INPUT,OUTPUT";
2417 } else if (cap & AUDIO_CLIENT_CAP_PLAY) {
2418 capstr = "OUTPUT";
2419 } else if (cap & AUDIO_CLIENT_CAP_RECORD) {
2420 capstr = "INPUT";
2421 } else {
2422 capstr = NULL;
2423 }
2424
2425 if (capstr == NULL)
2426 return (AUDIO_WALK_CONTINUE);
2427
2428 sndstat_printf(oc, "%d: %s %s, %s (%s)\n",
2429 auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2430 auclnt_get_dev_description(d), auclnt_get_dev_version(d), capstr);
2431
2432 return (AUDIO_WALK_CONTINUE);
2433 }
2434
2435 static int
sndstat_mixer_walker(audio_dev_t * d,void * arg)2436 sndstat_mixer_walker(audio_dev_t *d, void *arg)
2437 {
2438 ossclient_t *oc = arg;
2439 unsigned cap;
2440 void *iter;
2441 const char *info;
2442
2443 cap = auclnt_get_dev_capab(d);
2444
2445 if ((cap & (AUDIO_CLIENT_CAP_PLAY|AUDIO_CLIENT_CAP_RECORD)) == 0)
2446 return (AUDIO_WALK_CONTINUE);
2447
2448 sndstat_printf(oc, "%d: %s %s, %s\n",
2449 auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2450 auclnt_get_dev_description(d), auclnt_get_dev_version(d));
2451 iter = NULL;
2452 while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
2453 sndstat_printf(oc, "\t%s\n", info);
2454 }
2455 return (AUDIO_WALK_CONTINUE);
2456 }
2457
2458 static int
ossmix_write(audio_client_t * c,struct uio * uio,cred_t * cr)2459 ossmix_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2460 {
2461 /* write on sndstat is a no-op */
2462 _NOTE(ARGUNUSED(c));
2463 _NOTE(ARGUNUSED(uio));
2464 _NOTE(ARGUNUSED(cr));
2465
2466 return (0);
2467 }
2468
2469 static int
ossmix_read(audio_client_t * c,struct uio * uio,cred_t * cr)2470 ossmix_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2471 {
2472 ossclient_t *oc;
2473 unsigned n;
2474 int rv;
2475
2476 _NOTE(ARGUNUSED(cr));
2477
2478 if (uio->uio_resid == 0) {
2479 return (0);
2480 }
2481
2482 oc = auclnt_get_private(c);
2483
2484 mutex_enter(&oc->o_ss_lock);
2485
2486 if (oc->o_ss_off == 0) {
2487
2488 sndstat_printf(oc, "SunOS Audio Framework\n");
2489
2490 sndstat_printf(oc, "\nAudio Devices:\n");
2491 auclnt_walk_devs_by_number(sndstat_dev_walker, oc);
2492
2493 sndstat_printf(oc, "\nMixers:\n");
2494 auclnt_walk_devs_by_number(sndstat_mixer_walker, oc);
2495 }
2496
2497 /*
2498 * For simplicity's sake, we implement a non-seekable device. We could
2499 * support seekability, but offsets would be rather meaningless between
2500 * changes.
2501 */
2502 n = min(uio->uio_resid, (oc->o_ss_len - oc->o_ss_off));
2503
2504 rv = uiomove(oc->o_ss_buf + oc->o_ss_off, n, UIO_READ, uio);
2505 if (rv != 0) {
2506 n = 0;
2507 }
2508 oc->o_ss_off += n;
2509
2510 if (n == 0) {
2511 /*
2512 * end-of-file reached... clear the sndstat buffer so that
2513 * subsequent reads will get the latest data.
2514 */
2515 oc->o_ss_off = oc->o_ss_len = 0;
2516 }
2517 mutex_exit(&oc->o_ss_lock);
2518 return (rv);
2519 }
2520
2521 int
oss_read(audio_client_t * c,struct uio * uio,cred_t * cr)2522 oss_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2523 {
2524 _NOTE(ARGUNUSED(cr));
2525
2526 auclnt_clear_paused(auclnt_input_stream(c));
2527
2528 return (auclnt_read(c, uio));
2529 }
2530
2531 int
oss_write(audio_client_t * c,struct uio * uio,cred_t * cr)2532 oss_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2533 {
2534 _NOTE(ARGUNUSED(cr));
2535
2536 auclnt_clear_paused(auclnt_output_stream(c));
2537
2538 return (auclnt_write(c, uio));
2539 }
2540
2541 int
oss_chpoll(audio_client_t * c,short events,int anyyet,short * reventsp,struct pollhead ** phpp)2542 oss_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
2543 struct pollhead **phpp)
2544 {
2545 return (auclnt_chpoll(c, events, anyyet, reventsp, phpp));
2546 }
2547
2548 static struct audio_client_ops oss_ops = {
2549 "sound,dsp",
2550 oss_dev_init,
2551 oss_dev_fini,
2552 oss_open,
2553 oss_close,
2554 oss_read,
2555 oss_write,
2556 oss_ioctl,
2557 oss_chpoll,
2558 NULL, /* mmap */
2559 oss_input,
2560 oss_output,
2561 NULL, /* drain */
2562 };
2563
2564 static struct audio_client_ops ossmix_ops = {
2565 "sound,mixer",
2566 NULL,
2567 NULL,
2568 ossmix_open,
2569 ossmix_close,
2570 ossmix_read,
2571 ossmix_write,
2572 ossmix_ioctl,
2573 NULL, /* chpoll */
2574 NULL, /* mmap */
2575 NULL, /* input */
2576 NULL, /* output */
2577 NULL, /* drain */
2578 NULL, /* wput */
2579 NULL, /* wsrv */
2580 };
2581
2582 /* nearly the same as ossxmix; different minor name helps devfsadm */
2583 static struct audio_client_ops sndstat_ops = {
2584 "sound,sndstat",
2585 NULL, /* dev_init */
2586 NULL, /* dev_fini */
2587 ossmix_open,
2588 ossmix_close,
2589 ossmix_read,
2590 ossmix_write,
2591 ossmix_ioctl,
2592 NULL, /* chpoll */
2593 NULL, /* mmap */
2594 NULL, /* input */
2595 NULL, /* output */
2596 NULL, /* drain */
2597 NULL, /* wput */
2598 NULL, /* wsrv */
2599 };
2600
2601 void
auimpl_oss_init(void)2602 auimpl_oss_init(void)
2603 {
2604 auclnt_register_ops(AUDIO_MINOR_DSP, &oss_ops);
2605 auclnt_register_ops(AUDIO_MINOR_MIXER, &ossmix_ops);
2606 auclnt_register_ops(AUDIO_MINOR_SNDSTAT, &sndstat_ops);
2607 }
2608