Commit | Line | Data |
---|---|---|
558a398b | 1 | /*- |
2a1ad637 FT |
2 | * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> |
3 | * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 | |
4 | * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> | |
984263bc MD |
5 | * All rights reserved. |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
26 | * SUCH DAMAGE. | |
27 | */ | |
28 | ||
2a1ad637 FT |
29 | #ifdef HAVE_KERNEL_OPTION_HEADERS |
30 | #include "opt_snd.h" | |
31 | #endif | |
32 | ||
984263bc | 33 | #include <dev/sound/pcm/sound.h> |
88a31edf | 34 | #include <sys/device.h> |
67931cc4 | 35 | #include <sys/eventhandler.h> |
984263bc | 36 | |
2a1ad637 | 37 | #include "feeder_if.h" |
984263bc MD |
38 | #include "mixer_if.h" |
39 | ||
2a1ad637 | 40 | SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/mixer.c 274035 2014-11-03 11:11:45Z bapt $"); |
984263bc | 41 | |
2a1ad637 FT |
42 | static MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); |
43 | ||
44 | static int mixer_bypass = 1; | |
c4356965 FT |
45 | TUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass); |
46 | SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW, | |
2a1ad637 FT |
47 | &mixer_bypass, 0, |
48 | "control channel pcm/rec volume, bypassing real mixer device"); | |
984263bc MD |
49 | |
50 | #define MIXER_NAMELEN 16 | |
51 | struct snd_mixer { | |
52 | KOBJ_FIELDS; | |
984263bc MD |
53 | void *devinfo; |
54 | int busy; | |
55 | int hwvol_muted; | |
56 | int hwvol_mixer; | |
57 | int hwvol_step; | |
2a1ad637 | 58 | int type; |
558a398b | 59 | device_t dev; |
984263bc MD |
60 | u_int32_t hwvol_mute_level; |
61 | u_int32_t devs; | |
62 | u_int32_t recdevs; | |
63 | u_int32_t recsrc; | |
64 | u_int16_t level[32]; | |
4886ec58 HT |
65 | u_int8_t parent[32]; |
66 | u_int32_t child[32]; | |
67 | u_int8_t realdev[32]; | |
984263bc | 68 | char name[MIXER_NAMELEN]; |
67931cc4 | 69 | struct lock *lock; |
2a1ad637 FT |
70 | oss_mixer_enuminfo enuminfo; |
71 | /** | |
72 | * Counter is incremented when applications change any of this | |
73 | * mixer's controls. A change in value indicates that persistent | |
74 | * mixer applications should update their displays. | |
75 | */ | |
76 | int modify_counter; | |
984263bc MD |
77 | }; |
78 | ||
79 | static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { | |
80 | [SOUND_MIXER_VOLUME] = 75, | |
81 | [SOUND_MIXER_BASS] = 50, | |
82 | [SOUND_MIXER_TREBLE] = 50, | |
83 | [SOUND_MIXER_SYNTH] = 75, | |
84 | [SOUND_MIXER_PCM] = 75, | |
85 | [SOUND_MIXER_SPEAKER] = 75, | |
86 | [SOUND_MIXER_LINE] = 75, | |
87 | [SOUND_MIXER_MIC] = 0, | |
88 | [SOUND_MIXER_CD] = 75, | |
558a398b | 89 | [SOUND_MIXER_IGAIN] = 0, |
984263bc MD |
90 | [SOUND_MIXER_LINE1] = 75, |
91 | [SOUND_MIXER_VIDEO] = 75, | |
2a1ad637 | 92 | [SOUND_MIXER_RECLEV] = 75, |
984263bc MD |
93 | [SOUND_MIXER_OGAIN] = 50, |
94 | [SOUND_MIXER_MONITOR] = 75, | |
95 | }; | |
96 | ||
97 | static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; | |
98 | ||
99 | static d_open_t mixer_open; | |
100 | static d_close_t mixer_close; | |
2a1ad637 | 101 | static d_ioctl_t mixer_ioctl; |
984263bc | 102 | |
8bd5748c | 103 | struct dev_ops mixer_ops = { |
481d12aa | 104 | { "mixer", 0, D_TRACKCLOSE | D_MPSAFE }, |
fef8985e MD |
105 | .d_open = mixer_open, |
106 | .d_close = mixer_close, | |
107 | .d_ioctl = mixer_ioctl, | |
984263bc MD |
108 | }; |
109 | ||
2a1ad637 FT |
110 | /** |
111 | * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. | |
112 | */ | |
113 | int mixer_count = 0; | |
114 | ||
115 | static eventhandler_tag mixer_ehtag = NULL; | |
984263bc | 116 | |
558a398b | 117 | static struct cdev * |
984263bc MD |
118 | mixer_get_devt(device_t dev) |
119 | { | |
558a398b | 120 | struct snddev_info *snddev; |
984263bc | 121 | |
558a398b | 122 | snddev = device_get_softc(dev); |
984263bc | 123 | |
558a398b | 124 | return snddev->mixer_dev; |
984263bc MD |
125 | } |
126 | ||
984263bc MD |
127 | static int |
128 | mixer_lookup(char *devname) | |
129 | { | |
130 | int i; | |
131 | ||
132 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | |
133 | if (strncmp(devname, snd_mixernames[i], | |
134 | strlen(snd_mixernames[i])) == 0) | |
135 | return i; | |
136 | return -1; | |
137 | } | |
984263bc | 138 | |
2a1ad637 FT |
139 | #define MIXER_SET_UNLOCK(x, y) do { \ |
140 | if ((y) != 0) \ | |
141 | snd_mtxunlock((x)->lock); \ | |
142 | } while (0) | |
143 | ||
144 | #define MIXER_SET_LOCK(x, y) do { \ | |
145 | if ((y) != 0) \ | |
146 | snd_mtxlock((x)->lock); \ | |
147 | } while (0) | |
148 | ||
984263bc | 149 | static int |
2a1ad637 FT |
150 | mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, |
151 | u_int left, u_int right) | |
152 | { | |
153 | struct pcm_channel *c; | |
154 | int dropmtx, acquiremtx; | |
155 | ||
156 | if (!PCM_REGISTERED(d)) | |
157 | return (EINVAL); | |
158 | ||
5f097292 | 159 | if (lockstatus(m->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
160 | dropmtx = 1; |
161 | else | |
162 | dropmtx = 0; | |
163 | ||
5f097292 | 164 | if (!(d->flags & SD_F_MPSAFE) || lockstatus(d->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
165 | acquiremtx = 0; |
166 | else | |
167 | acquiremtx = 1; | |
168 | ||
169 | /* | |
170 | * Be careful here. If we're coming from cdev ioctl, it is OK to | |
171 | * not doing locking AT ALL (except on individual channel) since | |
172 | * we've been heavily guarded by pcm cv, or if we're still | |
173 | * under Giant influence. Since we also have mix_* calls, we cannot | |
174 | * assume such protection and just do the lock as usuall. | |
175 | */ | |
176 | MIXER_SET_UNLOCK(m, dropmtx); | |
177 | MIXER_SET_LOCK(d, acquiremtx); | |
178 | ||
179 | CHN_FOREACH(c, d, channels.pcm.busy) { | |
180 | CHN_LOCK(c); | |
181 | if (c->direction == PCMDIR_PLAY && | |
182 | (c->feederflags & (1 << FEEDER_VOLUME))) | |
183 | chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, | |
184 | (left + right) >> 1); | |
185 | CHN_UNLOCK(c); | |
186 | } | |
187 | ||
188 | MIXER_SET_UNLOCK(d, acquiremtx); | |
189 | MIXER_SET_LOCK(m, dropmtx); | |
190 | ||
191 | return (0); | |
192 | } | |
193 | ||
194 | static int | |
195 | mixer_set_eq(struct snd_mixer *m, struct snddev_info *d, | |
196 | u_int dev, u_int level) | |
197 | { | |
198 | struct pcm_channel *c; | |
199 | struct pcm_feeder *f; | |
200 | int tone, dropmtx, acquiremtx; | |
201 | ||
202 | if (dev == SOUND_MIXER_TREBLE) | |
203 | tone = FEEDEQ_TREBLE; | |
204 | else if (dev == SOUND_MIXER_BASS) | |
205 | tone = FEEDEQ_BASS; | |
206 | else | |
207 | return (EINVAL); | |
208 | ||
209 | if (!PCM_REGISTERED(d)) | |
210 | return (EINVAL); | |
211 | ||
5f097292 | 212 | if (lockstatus(m->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
213 | dropmtx = 1; |
214 | else | |
215 | dropmtx = 0; | |
216 | ||
5f097292 | 217 | if (!(d->flags & SD_F_MPSAFE) || lockstatus(d->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
218 | acquiremtx = 0; |
219 | else | |
220 | acquiremtx = 1; | |
221 | ||
222 | /* | |
223 | * Be careful here. If we're coming from cdev ioctl, it is OK to | |
224 | * not doing locking AT ALL (except on individual channel) since | |
225 | * we've been heavily guarded by pcm cv, or if we're still | |
226 | * under Giant influence. Since we also have mix_* calls, we cannot | |
227 | * assume such protection and just do the lock as usuall. | |
228 | */ | |
229 | MIXER_SET_UNLOCK(m, dropmtx); | |
230 | MIXER_SET_LOCK(d, acquiremtx); | |
231 | ||
232 | CHN_FOREACH(c, d, channels.pcm.busy) { | |
233 | CHN_LOCK(c); | |
234 | f = chn_findfeeder(c, FEEDER_EQ); | |
235 | if (f != NULL) | |
236 | (void)FEEDER_SET(f, tone, level); | |
237 | CHN_UNLOCK(c); | |
238 | } | |
239 | ||
240 | MIXER_SET_UNLOCK(d, acquiremtx); | |
241 | MIXER_SET_LOCK(m, dropmtx); | |
242 | ||
243 | return (0); | |
4886ec58 HT |
244 | } |
245 | ||
246 | static int | |
2a1ad637 | 247 | mixer_set(struct snd_mixer *m, u_int dev, u_int lev) |
984263bc | 248 | { |
558a398b | 249 | struct snddev_info *d; |
2a1ad637 | 250 | u_int l, r, tl, tr; |
4886ec58 HT |
251 | u_int32_t parent = SOUND_MIXER_NONE, child = 0; |
252 | u_int32_t realdev; | |
2a1ad637 | 253 | int i, dropmtx; |
984263bc | 254 | |
4886ec58 HT |
255 | if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || |
256 | (0 == (m->devs & (1 << dev)))) | |
984263bc MD |
257 | return -1; |
258 | ||
259 | l = min((lev & 0x00ff), 100); | |
260 | r = min(((lev & 0xff00) >> 8), 100); | |
4886ec58 HT |
261 | realdev = m->realdev[dev]; |
262 | ||
263 | d = device_get_softc(m->dev); | |
264 | if (d == NULL) | |
265 | return -1; | |
984263bc | 266 | |
2a1ad637 | 267 | /* It is safe to drop this mutex due to Giant. */ |
5f097292 | 268 | if (!(d->flags & SD_F_MPSAFE) && lockstatus(m->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
269 | dropmtx = 1; |
270 | else | |
271 | dropmtx = 0; | |
272 | ||
273 | MIXER_SET_UNLOCK(m, dropmtx); | |
274 | ||
4886ec58 HT |
275 | /* TODO: recursive handling */ |
276 | parent = m->parent[dev]; | |
277 | if (parent >= SOUND_MIXER_NRDEVICES) | |
278 | parent = SOUND_MIXER_NONE; | |
279 | if (parent == SOUND_MIXER_NONE) | |
280 | child = m->child[dev]; | |
281 | ||
282 | if (parent != SOUND_MIXER_NONE) { | |
283 | tl = (l * (m->level[parent] & 0x00ff)) / 100; | |
284 | tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; | |
285 | if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) | |
2a1ad637 | 286 | (void)mixer_set_softpcmvol(m, d, tl, tr); |
4886ec58 | 287 | else if (realdev != SOUND_MIXER_NONE && |
2a1ad637 FT |
288 | MIXER_SET(m, realdev, tl, tr) < 0) { |
289 | MIXER_SET_LOCK(m, dropmtx); | |
4886ec58 | 290 | return -1; |
2a1ad637 | 291 | } |
4886ec58 HT |
292 | } else if (child != 0) { |
293 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
294 | if (!(child & (1 << i)) || m->parent[i] != dev) | |
295 | continue; | |
296 | realdev = m->realdev[i]; | |
297 | tl = (l * (m->level[i] & 0x00ff)) / 100; | |
298 | tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; | |
2a1ad637 FT |
299 | if (i == SOUND_MIXER_PCM && |
300 | (d->flags & SD_F_SOFTPCMVOL)) | |
301 | (void)mixer_set_softpcmvol(m, d, tl, tr); | |
4886ec58 HT |
302 | else if (realdev != SOUND_MIXER_NONE) |
303 | MIXER_SET(m, realdev, tl, tr); | |
558a398b | 304 | } |
4886ec58 HT |
305 | realdev = m->realdev[dev]; |
306 | if (realdev != SOUND_MIXER_NONE && | |
2a1ad637 FT |
307 | MIXER_SET(m, realdev, l, r) < 0) { |
308 | MIXER_SET_LOCK(m, dropmtx); | |
309 | return -1; | |
310 | } | |
558a398b | 311 | } else { |
4886ec58 | 312 | if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) |
2a1ad637 FT |
313 | (void)mixer_set_softpcmvol(m, d, l, r); |
314 | else if ((dev == SOUND_MIXER_TREBLE || | |
315 | dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ)) | |
316 | (void)mixer_set_eq(m, d, dev, (l + r) >> 1); | |
4886ec58 | 317 | else if (realdev != SOUND_MIXER_NONE && |
2a1ad637 FT |
318 | MIXER_SET(m, realdev, l, r) < 0) { |
319 | MIXER_SET_LOCK(m, dropmtx); | |
558a398b | 320 | return -1; |
2a1ad637 | 321 | } |
558a398b | 322 | } |
984263bc | 323 | |
2a1ad637 FT |
324 | MIXER_SET_LOCK(m, dropmtx); |
325 | ||
4886ec58 HT |
326 | m->level[dev] = l | (r << 8); |
327 | ||
984263bc MD |
328 | return 0; |
329 | } | |
330 | ||
331 | static int | |
332 | mixer_get(struct snd_mixer *mixer, int dev) | |
333 | { | |
334 | if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) | |
335 | return mixer->level[dev]; | |
2a1ad637 FT |
336 | else |
337 | return -1; | |
984263bc MD |
338 | } |
339 | ||
340 | static int | |
341 | mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) | |
342 | { | |
2a1ad637 FT |
343 | struct snddev_info *d; |
344 | u_int32_t recsrc; | |
345 | int dropmtx; | |
346 | ||
347 | d = device_get_softc(mixer->dev); | |
348 | if (d == NULL) | |
349 | return -1; | |
5f097292 | 350 | if (!(d->flags & SD_F_MPSAFE) && lockstatus(mixer->lock, curthread) == LK_EXCLUSIVE) |
2a1ad637 FT |
351 | dropmtx = 1; |
352 | else | |
353 | dropmtx = 0; | |
984263bc MD |
354 | src &= mixer->recdevs; |
355 | if (src == 0) | |
2a1ad637 FT |
356 | src = mixer->recdevs & SOUND_MASK_MIC; |
357 | if (src == 0) | |
358 | src = mixer->recdevs & SOUND_MASK_MONITOR; | |
359 | if (src == 0) | |
360 | src = mixer->recdevs & SOUND_MASK_LINE; | |
361 | if (src == 0 && mixer->recdevs != 0) | |
362 | src = (1 << (ffs(mixer->recdevs) - 1)); | |
363 | /* It is safe to drop this mutex due to Giant. */ | |
364 | MIXER_SET_UNLOCK(mixer, dropmtx); | |
365 | recsrc = MIXER_SETRECSRC(mixer, src); | |
366 | MIXER_SET_LOCK(mixer, dropmtx); | |
367 | ||
368 | mixer->recsrc = recsrc; | |
369 | ||
984263bc MD |
370 | return 0; |
371 | } | |
372 | ||
373 | static int | |
374 | mixer_getrecsrc(struct snd_mixer *mixer) | |
375 | { | |
376 | return mixer->recsrc; | |
377 | } | |
378 | ||
2a1ad637 FT |
379 | /** |
380 | * @brief Retrieve the route number of the current recording device | |
381 | * | |
382 | * OSSv4 assigns routing numbers to recording devices, unlike the previous | |
383 | * API which relied on a fixed table of device numbers and names. This | |
384 | * function returns the routing number of the device currently selected | |
385 | * for recording. | |
386 | * | |
387 | * For now, this function is kind of a goofy compatibility stub atop the | |
388 | * existing sound system. (For example, in theory, the old sound system | |
389 | * allows multiple recording devices to be specified via a bitmask.) | |
390 | * | |
391 | * @param m mixer context container thing | |
392 | * | |
393 | * @retval 0 success | |
394 | * @retval EIDRM no recording device found (generally not possible) | |
395 | * @todo Ask about error code | |
396 | */ | |
397 | static int | |
398 | mixer_get_recroute(struct snd_mixer *m, int *route) | |
399 | { | |
400 | int i, cnt; | |
401 | ||
402 | cnt = 0; | |
403 | ||
404 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
405 | /** @todo can user set a multi-device mask? (== or &?) */ | |
406 | if ((1 << i) == m->recsrc) | |
407 | break; | |
408 | if ((1 << i) & m->recdevs) | |
409 | ++cnt; | |
410 | } | |
411 | ||
412 | if (i == SOUND_MIXER_NRDEVICES) | |
413 | return EIDRM; | |
414 | ||
415 | *route = cnt; | |
416 | return 0; | |
417 | } | |
418 | ||
419 | /** | |
420 | * @brief Select a device for recording | |
421 | * | |
422 | * This function sets a recording source based on a recording device's | |
423 | * routing number. Said number is translated to an old school recdev | |
424 | * mask and passed over mixer_setrecsrc. | |
425 | * | |
426 | * @param m mixer context container thing | |
427 | * | |
428 | * @retval 0 success(?) | |
429 | * @retval EINVAL User specified an invalid device number | |
430 | * @retval otherwise error from mixer_setrecsrc | |
431 | */ | |
432 | static int | |
433 | mixer_set_recroute(struct snd_mixer *m, int route) | |
434 | { | |
435 | int i, cnt, ret; | |
436 | ||
437 | ret = 0; | |
438 | cnt = 0; | |
439 | ||
440 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
441 | if ((1 << i) & m->recdevs) { | |
442 | if (route == cnt) | |
443 | break; | |
444 | ++cnt; | |
445 | } | |
446 | } | |
447 | ||
448 | if (i == SOUND_MIXER_NRDEVICES) | |
449 | ret = EINVAL; | |
450 | else | |
451 | ret = mixer_setrecsrc(m, (1 << i)); | |
452 | ||
453 | return ret; | |
454 | } | |
455 | ||
984263bc MD |
456 | void |
457 | mix_setdevs(struct snd_mixer *m, u_int32_t v) | |
458 | { | |
4886ec58 HT |
459 | struct snddev_info *d; |
460 | int i; | |
461 | ||
462 | if (m == NULL) | |
463 | return; | |
464 | ||
465 | d = device_get_softc(m->dev); | |
466 | if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) | |
558a398b | 467 | v |= SOUND_MASK_PCM; |
2a1ad637 FT |
468 | if (d != NULL && (d->flags & SD_F_EQ)) |
469 | v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS; | |
4886ec58 HT |
470 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { |
471 | if (m->parent[i] < SOUND_MIXER_NRDEVICES) | |
472 | v |= 1 << m->parent[i]; | |
473 | v |= m->child[i]; | |
474 | } | |
984263bc MD |
475 | m->devs = v; |
476 | } | |
477 | ||
2a1ad637 FT |
478 | /** |
479 | * @brief Record mask of available recording devices | |
480 | * | |
481 | * Calling functions are responsible for defining the mask of available | |
482 | * recording devices. This function records that value in a structure | |
483 | * used by the rest of the mixer code. | |
484 | * | |
485 | * This function also populates a structure used by the SNDCTL_DSP_*RECSRC* | |
486 | * family of ioctls that are part of OSSV4. All recording device labels | |
487 | * are concatenated in ascending order corresponding to their routing | |
488 | * numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line', | |
489 | * etc.) For now, these labels are just the standard recording device | |
490 | * names (cd, line1, etc.), but will eventually be fully dynamic and user | |
491 | * controlled. | |
492 | * | |
493 | * @param m mixer device context container thing | |
494 | * @param v mask of recording devices | |
495 | */ | |
984263bc MD |
496 | void |
497 | mix_setrecdevs(struct snd_mixer *m, u_int32_t v) | |
498 | { | |
2a1ad637 FT |
499 | oss_mixer_enuminfo *ei; |
500 | char *loc; | |
501 | int i, nvalues, nwrote, nleft, ncopied; | |
502 | ||
503 | ei = &m->enuminfo; | |
504 | ||
505 | nvalues = 0; | |
506 | nwrote = 0; | |
507 | nleft = sizeof(ei->strings); | |
508 | loc = ei->strings; | |
509 | ||
510 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
511 | if ((1 << i) & v) { | |
512 | ei->strindex[nvalues] = nwrote; | |
513 | ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1; | |
514 | /* strlcpy retval doesn't include terminator */ | |
515 | ||
516 | nwrote += ncopied; | |
517 | nleft -= ncopied; | |
518 | nvalues++; | |
519 | ||
520 | /* | |
521 | * XXX I don't think this should ever be possible. | |
522 | * Even with a move to dynamic device/channel names, | |
523 | * each label is limited to ~16 characters, so that'd | |
524 | * take a LOT to fill this buffer. | |
525 | */ | |
526 | if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) { | |
527 | device_printf(m->dev, | |
528 | "mix_setrecdevs: Not enough room to store device names--please file a bug report.\n"); | |
529 | device_printf(m->dev, | |
530 | "mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n"); | |
531 | break; | |
532 | } | |
533 | ||
534 | loc = &ei->strings[nwrote]; | |
535 | } | |
536 | } | |
537 | ||
538 | /* | |
539 | * NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev | |
540 | * and ctrl fields. | |
541 | */ | |
542 | ei->nvalues = nvalues; | |
984263bc MD |
543 | m->recdevs = v; |
544 | } | |
545 | ||
4886ec58 HT |
546 | void |
547 | mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) | |
548 | { | |
549 | u_int32_t mask = 0; | |
550 | int i; | |
551 | ||
552 | if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) | |
553 | return; | |
554 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
555 | if (i == parent) | |
556 | continue; | |
557 | if (childs & (1 << i)) { | |
558 | mask |= 1 << i; | |
559 | if (m->parent[i] < SOUND_MIXER_NRDEVICES) | |
560 | m->child[m->parent[i]] &= ~(1 << i); | |
561 | m->parent[i] = parent; | |
562 | m->child[i] = 0; | |
563 | } | |
564 | } | |
565 | mask &= ~(1 << parent); | |
566 | m->child[parent] = mask; | |
567 | } | |
568 | ||
569 | void | |
570 | mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) | |
571 | { | |
572 | if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || | |
573 | !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) | |
574 | return; | |
575 | m->realdev[dev] = realdev; | |
576 | } | |
577 | ||
578 | u_int32_t | |
579 | mix_getparent(struct snd_mixer *m, u_int32_t dev) | |
580 | { | |
581 | if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) | |
582 | return SOUND_MIXER_NONE; | |
583 | return m->parent[dev]; | |
584 | } | |
585 | ||
586 | u_int32_t | |
587 | mix_getchild(struct snd_mixer *m, u_int32_t dev) | |
588 | { | |
589 | if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) | |
590 | return 0; | |
591 | return m->child[dev]; | |
592 | } | |
593 | ||
984263bc MD |
594 | u_int32_t |
595 | mix_getdevs(struct snd_mixer *m) | |
596 | { | |
597 | return m->devs; | |
598 | } | |
599 | ||
600 | u_int32_t | |
601 | mix_getrecdevs(struct snd_mixer *m) | |
602 | { | |
603 | return m->recdevs; | |
604 | } | |
605 | ||
606 | void * | |
607 | mix_getdevinfo(struct snd_mixer *m) | |
608 | { | |
609 | return m->devinfo; | |
610 | } | |
611 | ||
2a1ad637 FT |
612 | static struct snd_mixer * |
613 | mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo, | |
614 | int type, const char *desc) | |
984263bc MD |
615 | { |
616 | struct snd_mixer *m; | |
2a1ad637 FT |
617 | int i; |
618 | ||
619 | KASSERT(dev != NULL && cls != NULL && devinfo != NULL, | |
620 | ("%s(): NULL data dev=%p cls=%p devinfo=%p", | |
621 | __func__, dev, cls, devinfo)); | |
622 | KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY, | |
623 | ("invalid mixer type=%d", type)); | |
984263bc MD |
624 | |
625 | m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); | |
67931cc4 | 626 | ksnprintf(m->name, sizeof(m->name), "%s:mixer", |
2a1ad637 FT |
627 | device_get_nameunit(dev)); |
628 | if (desc != NULL) { | |
629 | strlcat(m->name, ":", sizeof(m->name)); | |
630 | strlcat(m->name, desc, sizeof(m->name)); | |
631 | } | |
632 | m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ? | |
633 | "primary pcm mixer" : "secondary pcm mixer"); | |
634 | m->type = type; | |
984263bc MD |
635 | m->devinfo = devinfo; |
636 | m->busy = 0; | |
558a398b | 637 | m->dev = dev; |
2a1ad637 | 638 | for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) { |
4886ec58 HT |
639 | m->parent[i] = SOUND_MIXER_NONE; |
640 | m->child[i] = 0; | |
641 | m->realdev[i] = i; | |
642 | } | |
984263bc | 643 | |
2a1ad637 FT |
644 | if (MIXER_INIT(m)) { |
645 | snd_mtxlock(m->lock); | |
646 | snd_mtxfree(m->lock); | |
647 | kobj_delete((kobj_t)m, M_MIXER); | |
648 | return (NULL); | |
649 | } | |
650 | ||
651 | return (m); | |
652 | } | |
653 | ||
654 | int | |
655 | mixer_delete(struct snd_mixer *m) | |
656 | { | |
657 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
658 | KASSERT(m->type == MIXER_TYPE_SECONDARY, | |
659 | ("%s(): illegal mixer type=%d", __func__, m->type)); | |
660 | ||
661 | /* mixer uninit can sleep --hps */ | |
662 | ||
663 | MIXER_UNINIT(m); | |
664 | ||
665 | snd_mtxfree(m->lock); | |
666 | kobj_delete((kobj_t)m, M_MIXER); | |
667 | ||
668 | --mixer_count; | |
669 | ||
670 | return (0); | |
671 | } | |
672 | ||
673 | struct snd_mixer * | |
674 | mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc) | |
675 | { | |
676 | struct snd_mixer *m; | |
677 | ||
678 | m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc); | |
679 | ||
680 | if (m != NULL) | |
681 | ++mixer_count; | |
682 | ||
683 | return (m); | |
684 | } | |
685 | ||
686 | int | |
687 | mixer_init(device_t dev, kobj_class_t cls, void *devinfo) | |
688 | { | |
689 | struct snddev_info *snddev; | |
690 | struct snd_mixer *m; | |
691 | u_int16_t v; | |
692 | struct cdev *pdev; | |
693 | int i, unit, devunit, val; | |
694 | ||
695 | snddev = device_get_softc(dev); | |
696 | if (snddev == NULL) | |
697 | return (-1); | |
698 | ||
699 | if (resource_int_value(device_get_name(dev), | |
700 | device_get_unit(dev), "eq", &val) == 0 && val != 0) { | |
701 | snddev->flags |= SD_F_EQ; | |
702 | if ((val & SD_F_EQ_MASK) == val) | |
703 | snddev->flags |= val; | |
704 | else | |
705 | snddev->flags |= SD_F_EQ_DEFAULT; | |
706 | snddev->eqpreamp = 0; | |
707 | } | |
708 | ||
709 | m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); | |
710 | if (m == NULL) | |
711 | return (-1); | |
984263bc MD |
712 | |
713 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
714 | v = snd_mixerdefaults[i]; | |
558a398b SS |
715 | |
716 | if (resource_int_value(device_get_name(dev), | |
717 | device_get_unit(dev), snd_mixernames[i], &val) == 0) { | |
718 | if (val >= 0 && val <= 100) { | |
719 | v = (u_int16_t) val; | |
720 | } | |
721 | } | |
722 | ||
984263bc MD |
723 | mixer_set(m, i, v | (v << 8)); |
724 | } | |
725 | ||
2a1ad637 | 726 | mixer_setrecsrc(m, 0); /* Set default input. */ |
984263bc MD |
727 | |
728 | unit = device_get_unit(dev); | |
2a1ad637 | 729 | devunit = snd_mkunit(unit, SND_DEV_CTL, 0); |
8bd5748c | 730 | pdev = make_dev(&mixer_ops, PCMMKMINOR(unit, 0, 0), |
2a1ad637 | 731 | UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); |
984263bc | 732 | pdev->si_drv1 = m; |
558a398b | 733 | snddev->mixer_dev = pdev; |
984263bc | 734 | |
2a1ad637 FT |
735 | ++mixer_count; |
736 | ||
4886ec58 HT |
737 | if (bootverbose) { |
738 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | |
739 | if (!(m->devs & (1 << i))) | |
740 | continue; | |
741 | if (m->realdev[i] != i) { | |
742 | device_printf(dev, "Mixer \"%s\" -> \"%s\":", | |
743 | snd_mixernames[i], | |
744 | (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? | |
745 | snd_mixernames[m->realdev[i]] : "none"); | |
746 | } else { | |
747 | device_printf(dev, "Mixer \"%s\":", | |
748 | snd_mixernames[i]); | |
749 | } | |
750 | if (m->parent[i] < SOUND_MIXER_NRDEVICES) | |
67931cc4 | 751 | kprintf(" parent=\"%s\"", |
4886ec58 HT |
752 | snd_mixernames[m->parent[i]]); |
753 | if (m->child[i] != 0) | |
67931cc4 FT |
754 | kprintf(" child=0x%08x", m->child[i]); |
755 | kprintf("\n"); | |
4886ec58 HT |
756 | } |
757 | if (snddev->flags & SD_F_SOFTPCMVOL) | |
758 | device_printf(dev, "Soft PCM mixer ENABLED\n"); | |
2a1ad637 FT |
759 | if (snddev->flags & SD_F_EQ) |
760 | device_printf(dev, "EQ Treble/Bass ENABLED\n"); | |
4886ec58 HT |
761 | } |
762 | ||
2a1ad637 | 763 | return (0); |
984263bc MD |
764 | } |
765 | ||
766 | int | |
767 | mixer_uninit(device_t dev) | |
768 | { | |
769 | int i; | |
558a398b | 770 | struct snddev_info *d; |
984263bc | 771 | struct snd_mixer *m; |
558a398b | 772 | struct cdev *pdev; |
984263bc | 773 | |
558a398b | 774 | d = device_get_softc(dev); |
984263bc | 775 | pdev = mixer_get_devt(dev); |
558a398b SS |
776 | if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) |
777 | return EBADF; | |
2a1ad637 | 778 | |
984263bc | 779 | m = pdev->si_drv1; |
2a1ad637 FT |
780 | KASSERT(m != NULL, ("NULL snd_mixer")); |
781 | KASSERT(m->type == MIXER_TYPE_PRIMARY, | |
782 | ("%s(): illegal mixer type=%d", __func__, m->type)); | |
783 | ||
984263bc MD |
784 | snd_mtxlock(m->lock); |
785 | ||
786 | if (m->busy) { | |
787 | snd_mtxunlock(m->lock); | |
788 | return EBUSY; | |
789 | } | |
790 | ||
2a1ad637 FT |
791 | /* destroy dev can sleep --hps */ |
792 | ||
793 | snd_mtxunlock(m->lock); | |
794 | ||
984263bc | 795 | pdev->si_drv1 = NULL; |
2a1ad637 FT |
796 | destroy_dev(pdev); |
797 | ||
798 | snd_mtxlock(m->lock); | |
984263bc MD |
799 | |
800 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | |
801 | mixer_set(m, i, 0); | |
802 | ||
803 | mixer_setrecsrc(m, SOUND_MASK_MIC); | |
804 | ||
2a1ad637 FT |
805 | snd_mtxunlock(m->lock); |
806 | ||
807 | /* mixer uninit can sleep --hps */ | |
808 | ||
984263bc MD |
809 | MIXER_UNINIT(m); |
810 | ||
811 | snd_mtxfree(m->lock); | |
812 | kobj_delete((kobj_t)m, M_MIXER); | |
813 | ||
558a398b SS |
814 | d->mixer_dev = NULL; |
815 | ||
2a1ad637 FT |
816 | --mixer_count; |
817 | ||
984263bc MD |
818 | return 0; |
819 | } | |
820 | ||
821 | int | |
822 | mixer_reinit(device_t dev) | |
823 | { | |
824 | struct snd_mixer *m; | |
558a398b | 825 | struct cdev *pdev; |
984263bc MD |
826 | int i; |
827 | ||
828 | pdev = mixer_get_devt(dev); | |
829 | m = pdev->si_drv1; | |
830 | snd_mtxlock(m->lock); | |
831 | ||
832 | i = MIXER_REINIT(m); | |
833 | if (i) { | |
834 | snd_mtxunlock(m->lock); | |
835 | return i; | |
836 | } | |
837 | ||
838 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | |
839 | mixer_set(m, i, m->level[i]); | |
840 | ||
841 | mixer_setrecsrc(m, m->recsrc); | |
842 | snd_mtxunlock(m->lock); | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
984263bc MD |
847 | static int |
848 | sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) | |
849 | { | |
850 | char devname[32]; | |
851 | int error, dev; | |
852 | struct snd_mixer *m; | |
853 | ||
854 | m = oidp->oid_arg1; | |
855 | snd_mtxlock(m->lock); | |
2a1ad637 | 856 | strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); |
558a398b | 857 | snd_mtxunlock(m->lock); |
984263bc | 858 | error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); |
558a398b | 859 | snd_mtxlock(m->lock); |
984263bc MD |
860 | if (error == 0 && req->newptr != NULL) { |
861 | dev = mixer_lookup(devname); | |
862 | if (dev == -1) { | |
863 | snd_mtxunlock(m->lock); | |
864 | return EINVAL; | |
865 | } | |
866 | else if (dev != m->hwvol_mixer) { | |
867 | m->hwvol_mixer = dev; | |
868 | m->hwvol_muted = 0; | |
869 | } | |
870 | } | |
871 | snd_mtxunlock(m->lock); | |
872 | return error; | |
873 | } | |
984263bc MD |
874 | |
875 | int | |
876 | mixer_hwvol_init(device_t dev) | |
877 | { | |
878 | struct snd_mixer *m; | |
558a398b | 879 | struct cdev *pdev; |
984263bc MD |
880 | |
881 | pdev = mixer_get_devt(dev); | |
882 | m = pdev->si_drv1; | |
984263bc MD |
883 | |
884 | m->hwvol_mixer = SOUND_MIXER_VOLUME; | |
885 | m->hwvol_step = 5; | |
2a1ad637 FT |
886 | SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), |
887 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |
984263bc | 888 | OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); |
2a1ad637 FT |
889 | SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), |
890 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |
984263bc | 891 | OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, |
558a398b | 892 | sysctl_hw_snd_hwvol_mixer, "A", ""); |
984263bc MD |
893 | return 0; |
894 | } | |
895 | ||
896 | void | |
2a1ad637 | 897 | mixer_hwvol_mute_locked(struct snd_mixer *m) |
984263bc | 898 | { |
984263bc MD |
899 | if (m->hwvol_muted) { |
900 | m->hwvol_muted = 0; | |
901 | mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); | |
902 | } else { | |
903 | m->hwvol_muted++; | |
904 | m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); | |
905 | mixer_set(m, m->hwvol_mixer, 0); | |
906 | } | |
984263bc MD |
907 | } |
908 | ||
909 | void | |
2a1ad637 | 910 | mixer_hwvol_mute(device_t dev) |
984263bc MD |
911 | { |
912 | struct snd_mixer *m; | |
558a398b | 913 | struct cdev *pdev; |
984263bc MD |
914 | |
915 | pdev = mixer_get_devt(dev); | |
916 | m = pdev->si_drv1; | |
917 | snd_mtxlock(m->lock); | |
2a1ad637 FT |
918 | mixer_hwvol_mute_locked(m); |
919 | snd_mtxunlock(m->lock); | |
920 | } | |
921 | ||
922 | void | |
923 | mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) | |
924 | { | |
925 | int level, left, right; | |
926 | ||
984263bc MD |
927 | if (m->hwvol_muted) { |
928 | m->hwvol_muted = 0; | |
929 | level = m->hwvol_mute_level; | |
930 | } else | |
931 | level = mixer_get(m, m->hwvol_mixer); | |
932 | if (level != -1) { | |
933 | left = level & 0xff; | |
2a1ad637 | 934 | right = (level >> 8) & 0xff; |
984263bc MD |
935 | left += left_step * m->hwvol_step; |
936 | if (left < 0) | |
937 | left = 0; | |
2a1ad637 FT |
938 | else if (left > 100) |
939 | left = 100; | |
984263bc MD |
940 | right += right_step * m->hwvol_step; |
941 | if (right < 0) | |
942 | right = 0; | |
2a1ad637 FT |
943 | else if (right > 100) |
944 | right = 100; | |
984263bc MD |
945 | mixer_set(m, m->hwvol_mixer, left | right << 8); |
946 | } | |
2a1ad637 FT |
947 | } |
948 | ||
949 | void | |
950 | mixer_hwvol_step(device_t dev, int left_step, int right_step) | |
951 | { | |
952 | struct snd_mixer *m; | |
953 | struct cdev *pdev; | |
954 | ||
955 | pdev = mixer_get_devt(dev); | |
956 | m = pdev->si_drv1; | |
957 | snd_mtxlock(m->lock); | |
958 | mixer_hwvol_step_locked(m, left_step, right_step); | |
984263bc MD |
959 | snd_mtxunlock(m->lock); |
960 | } | |
961 | ||
2a1ad637 FT |
962 | int |
963 | mixer_busy(struct snd_mixer *m) | |
964 | { | |
965 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
984263bc | 966 | |
2a1ad637 FT |
967 | return (m->busy); |
968 | } | |
969 | ||
970 | int | |
971 | mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) | |
34e3e97f | 972 | { |
2a1ad637 | 973 | int ret; |
34e3e97f | 974 | |
2a1ad637 | 975 | KASSERT(m != NULL, ("NULL snd_mixer")); |
34e3e97f | 976 | |
2a1ad637 FT |
977 | snd_mtxlock(m->lock); |
978 | ret = mixer_set(m, dev, left | (right << 8)); | |
979 | snd_mtxunlock(m->lock); | |
980 | ||
981 | return ((ret != 0) ? ENXIO : 0); | |
982 | } | |
983 | ||
984 | int | |
985 | mix_get(struct snd_mixer *m, u_int dev) | |
986 | { | |
987 | int ret; | |
988 | ||
989 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
990 | ||
991 | snd_mtxlock(m->lock); | |
992 | ret = mixer_get(m, dev); | |
993 | snd_mtxunlock(m->lock); | |
994 | ||
995 | return (ret); | |
996 | } | |
997 | ||
998 | int | |
999 | mix_setrecsrc(struct snd_mixer *m, u_int32_t src) | |
1000 | { | |
1001 | int ret; | |
1002 | ||
1003 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
1004 | ||
1005 | snd_mtxlock(m->lock); | |
1006 | ret = mixer_setrecsrc(m, src); | |
1007 | snd_mtxunlock(m->lock); | |
1008 | ||
1009 | return ((ret != 0) ? ENXIO : 0); | |
1010 | } | |
1011 | ||
1012 | u_int32_t | |
1013 | mix_getrecsrc(struct snd_mixer *m) | |
1014 | { | |
1015 | u_int32_t ret; | |
1016 | ||
1017 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
1018 | ||
1019 | snd_mtxlock(m->lock); | |
1020 | ret = mixer_getrecsrc(m); | |
1021 | snd_mtxunlock(m->lock); | |
1022 | ||
1023 | return (ret); | |
1024 | } | |
1025 | ||
1026 | int | |
1027 | mix_get_type(struct snd_mixer *m) | |
1028 | { | |
1029 | KASSERT(m != NULL, ("NULL snd_mixer")); | |
1030 | ||
1031 | return (m->type); | |
34e3e97f SS |
1032 | } |
1033 | ||
1034 | /* ----------------------------------------------------------------------- */ | |
1035 | ||
984263bc | 1036 | static int |
88a31edf | 1037 | mixer_open(struct dev_open_args *ap) |
984263bc | 1038 | { |
88a31edf | 1039 | struct cdev *i_dev = ap->a_head.a_dev; |
2a1ad637 | 1040 | struct snddev_info *d; |
984263bc | 1041 | struct snd_mixer *m; |
984263bc | 1042 | |
2a1ad637 FT |
1043 | |
1044 | if (i_dev == NULL || i_dev->si_drv1 == NULL) | |
1045 | return (EBADF); | |
1046 | ||
558a398b | 1047 | m = i_dev->si_drv1; |
2a1ad637 FT |
1048 | d = device_get_softc(m->dev); |
1049 | if (!PCM_REGISTERED(d)) | |
1050 | return (EBADF); | |
984263bc | 1051 | |
2a1ad637 | 1052 | /* XXX Need Giant magic entry ??? */ |
984263bc | 1053 | |
2a1ad637 FT |
1054 | snd_mtxlock(m->lock); |
1055 | m->busy = 1; | |
984263bc | 1056 | snd_mtxunlock(m->lock); |
2a1ad637 FT |
1057 | |
1058 | return (0); | |
984263bc MD |
1059 | } |
1060 | ||
1061 | static int | |
88a31edf | 1062 | mixer_close(struct dev_close_args *ap) |
984263bc | 1063 | { |
88a31edf | 1064 | struct cdev *i_dev = ap->a_head.a_dev; |
2a1ad637 | 1065 | struct snddev_info *d; |
984263bc | 1066 | struct snd_mixer *m; |
2a1ad637 FT |
1067 | int ret; |
1068 | ||
1069 | if (i_dev == NULL || i_dev->si_drv1 == NULL) | |
1070 | return (EBADF); | |
984263bc | 1071 | |
558a398b | 1072 | m = i_dev->si_drv1; |
2a1ad637 FT |
1073 | d = device_get_softc(m->dev); |
1074 | if (!PCM_REGISTERED(d)) | |
1075 | return (EBADF); | |
1076 | ||
1077 | /* XXX Need Giant magic entry ??? */ | |
1078 | ||
984263bc | 1079 | snd_mtxlock(m->lock); |
2a1ad637 FT |
1080 | ret = (m->busy == 0) ? EBADF : 0; |
1081 | m->busy = 0; | |
1082 | snd_mtxunlock(m->lock); | |
984263bc | 1083 | |
2a1ad637 FT |
1084 | return (ret); |
1085 | } | |
1086 | ||
1087 | static int | |
1088 | mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, | |
a9dbef8b | 1089 | int from) |
2a1ad637 FT |
1090 | { |
1091 | struct snddev_info *d; | |
1092 | struct snd_mixer *m; | |
1093 | struct pcm_channel *c, *rdch, *wrch; | |
1094 | pid_t pid; | |
1095 | int j, ret; | |
1096 | ||
a9dbef8b | 1097 | if (curproc == NULL) |
2a1ad637 FT |
1098 | return (-1); |
1099 | ||
1100 | m = dev->si_drv1; | |
1101 | d = device_get_softc(m->dev); | |
1102 | j = cmd & 0xff; | |
1103 | ||
1104 | switch (j) { | |
1105 | case SOUND_MIXER_PCM: | |
1106 | case SOUND_MIXER_RECLEV: | |
1107 | case SOUND_MIXER_DEVMASK: | |
1108 | case SOUND_MIXER_CAPS: | |
1109 | case SOUND_MIXER_STEREODEVS: | |
1110 | break; | |
1111 | default: | |
1112 | return (-1); | |
1113 | break; | |
1114 | } | |
1115 | ||
a9dbef8b | 1116 | pid = curproc->p_pid; |
2a1ad637 FT |
1117 | rdch = NULL; |
1118 | wrch = NULL; | |
1119 | c = NULL; | |
1120 | ret = -1; | |
1121 | ||
1122 | /* | |
1123 | * This is unfair. Imagine single proc opening multiple | |
1124 | * instances of same direction. What we do right now | |
1125 | * is looking for the first matching proc/pid, and just | |
1126 | * that. Nothing more. Consider it done. | |
1127 | * | |
1128 | * The better approach of controlling specific channel | |
1129 | * pcm or rec volume is by doing mixer ioctl | |
1130 | * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] | |
1131 | * on its open fd, rather than cracky mixer bypassing here. | |
1132 | */ | |
1133 | CHN_FOREACH(c, d, channels.pcm.opened) { | |
1134 | CHN_LOCK(c); | |
1135 | if (c->pid != pid || | |
1136 | !(c->feederflags & (1 << FEEDER_VOLUME))) { | |
1137 | CHN_UNLOCK(c); | |
1138 | continue; | |
1139 | } | |
1140 | if (rdch == NULL && c->direction == PCMDIR_REC) { | |
1141 | rdch = c; | |
1142 | if (j == SOUND_MIXER_RECLEV) | |
1143 | goto mixer_ioctl_channel_proc; | |
1144 | } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { | |
1145 | wrch = c; | |
1146 | if (j == SOUND_MIXER_PCM) | |
1147 | goto mixer_ioctl_channel_proc; | |
1148 | } | |
1149 | CHN_UNLOCK(c); | |
1150 | if (rdch != NULL && wrch != NULL) | |
1151 | break; | |
1152 | } | |
1153 | ||
1154 | if (rdch == NULL && wrch == NULL) | |
1155 | return (-1); | |
1156 | ||
1157 | if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || | |
1158 | j == SOUND_MIXER_STEREODEVS) && | |
1159 | (cmd & ~0xff) == MIXER_READ(0)) { | |
1160 | snd_mtxlock(m->lock); | |
1161 | *(int *)arg = mix_getdevs(m); | |
984263bc | 1162 | snd_mtxunlock(m->lock); |
2a1ad637 FT |
1163 | if (rdch != NULL) |
1164 | *(int *)arg |= SOUND_MASK_RECLEV; | |
1165 | if (wrch != NULL) | |
1166 | *(int *)arg |= SOUND_MASK_PCM; | |
1167 | ret = 0; | |
984263bc | 1168 | } |
984263bc | 1169 | |
2a1ad637 FT |
1170 | return (ret); |
1171 | ||
1172 | mixer_ioctl_channel_proc: | |
1173 | ||
1174 | KASSERT(c != NULL, ("%s(): NULL channel", __func__)); | |
1175 | CHN_LOCKASSERT(c); | |
1176 | ||
1177 | if ((cmd & ~0xff) == MIXER_WRITE(0)) { | |
1178 | int left, right, center; | |
1179 | ||
1180 | left = *(int *)arg & 0x7f; | |
1181 | right = (*(int *)arg >> 8) & 0x7f; | |
1182 | center = (left + right) >> 1; | |
1183 | chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center); | |
1184 | } else if ((cmd & ~0xff) == MIXER_READ(0)) { | |
1185 | *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); | |
1186 | *(int *)arg |= | |
1187 | CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; | |
1188 | } | |
1189 | ||
1190 | CHN_UNLOCK(c); | |
1191 | ||
1192 | return (0); | |
1193 | } | |
1194 | ||
1195 | static int | |
88a31edf | 1196 | mixer_ioctl(struct dev_ioctl_args *ap) |
2a1ad637 | 1197 | { |
88a31edf FT |
1198 | struct cdev *i_dev = ap->a_head.a_dev; |
1199 | u_long cmd = ap->a_cmd; | |
1200 | caddr_t arg = ap->a_data; | |
1201 | int mode = ap->a_fflag; | |
2a1ad637 FT |
1202 | struct snddev_info *d; |
1203 | int ret; | |
1204 | ||
1205 | if (i_dev == NULL || i_dev->si_drv1 == NULL) | |
1206 | return (EBADF); | |
1207 | ||
1208 | d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev); | |
1209 | if (!PCM_REGISTERED(d)) | |
1210 | return (EBADF); | |
1211 | ||
1212 | PCM_GIANT_ENTER(d); | |
1213 | PCM_ACQUIRE_QUICK(d); | |
1214 | ||
1215 | ret = -1; | |
1216 | ||
1217 | if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) | |
a9dbef8b | 1218 | ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, |
2a1ad637 FT |
1219 | MIXER_CMD_CDEV); |
1220 | ||
1221 | if (ret == -1) | |
a9dbef8b | 1222 | ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, |
2a1ad637 FT |
1223 | MIXER_CMD_CDEV); |
1224 | ||
1225 | PCM_RELEASE_QUICK(d); | |
1226 | PCM_GIANT_LEAVE(d); | |
1227 | ||
1228 | return (ret); | |
984263bc MD |
1229 | } |
1230 | ||
2a1ad637 FT |
1231 | static void |
1232 | mixer_mixerinfo(struct snd_mixer *m, mixer_info *mi) | |
1233 | { | |
1234 | bzero((void *)mi, sizeof(*mi)); | |
1235 | strlcpy(mi->id, m->name, sizeof(mi->id)); | |
1236 | strlcpy(mi->name, device_get_desc(m->dev), sizeof(mi->name)); | |
1237 | mi->modify_counter = m->modify_counter; | |
1238 | } | |
1239 | ||
1240 | /* | |
1241 | * XXX Make sure you can guarantee concurrency safety before calling this | |
1242 | * function, be it through Giant, PCM_*, etc ! | |
1243 | */ | |
984263bc | 1244 | int |
2a1ad637 | 1245 | mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, |
a9dbef8b | 1246 | int from) |
984263bc MD |
1247 | { |
1248 | struct snd_mixer *m; | |
2a1ad637 | 1249 | int ret = EINVAL, *arg_i = (int *)arg; |
558a398b SS |
1250 | int v = -1, j = cmd & 0xff; |
1251 | ||
34e3e97f | 1252 | /* |
2a1ad637 FT |
1253 | * Certain ioctls may be made on any type of device (audio, mixer, |
1254 | * and MIDI). Handle those special cases here. | |
34e3e97f | 1255 | */ |
2a1ad637 FT |
1256 | if (IOCGROUP(cmd) == 'X') { |
1257 | switch (cmd) { | |
1258 | case SNDCTL_SYSINFO: | |
1259 | sound_oss_sysinfo((oss_sysinfo *)arg); | |
1260 | return (0); | |
1261 | case SNDCTL_CARDINFO: | |
1262 | return (sound_oss_card_info((oss_card_info *)arg)); | |
1263 | case SNDCTL_AUDIOINFO: | |
1264 | case SNDCTL_AUDIOINFO_EX: | |
1265 | case SNDCTL_ENGINEINFO: | |
1266 | return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg)); | |
1267 | case SNDCTL_MIXERINFO: | |
1268 | return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg)); | |
1269 | } | |
1270 | return (EINVAL); | |
34e3e97f SS |
1271 | } |
1272 | ||
2a1ad637 FT |
1273 | m = i_dev->si_drv1; |
1274 | ||
1275 | if (m == NULL) | |
1276 | return (EBADF); | |
1277 | ||
984263bc | 1278 | snd_mtxlock(m->lock); |
2a1ad637 | 1279 | if (from == MIXER_CMD_CDEV && !m->busy) { |
558a398b | 1280 | snd_mtxunlock(m->lock); |
2a1ad637 | 1281 | return (EBADF); |
558a398b | 1282 | } |
2a1ad637 FT |
1283 | switch (cmd) { |
1284 | case SNDCTL_DSP_GET_RECSRC_NAMES: | |
1285 | bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo)); | |
1286 | ret = 0; | |
1287 | goto done; | |
1288 | case SNDCTL_DSP_GET_RECSRC: | |
1289 | ret = mixer_get_recroute(m, arg_i); | |
1290 | goto done; | |
1291 | case SNDCTL_DSP_SET_RECSRC: | |
1292 | ret = mixer_set_recroute(m, *arg_i); | |
1293 | goto done; | |
1294 | case OSS_GETVERSION: | |
cd2badf6 | 1295 | *arg_i = SOUND_VERSION; |
2a1ad637 FT |
1296 | ret = 0; |
1297 | goto done; | |
1298 | case SOUND_MIXER_INFO: | |
1299 | mixer_mixerinfo(m, (mixer_info *)arg); | |
1300 | ret = 0; | |
1301 | goto done; | |
2f1b68b9 | 1302 | } |
2a1ad637 | 1303 | if ((cmd & ~0xff) == MIXER_WRITE(0)) { |
984263bc MD |
1304 | if (j == SOUND_MIXER_RECSRC) |
1305 | ret = mixer_setrecsrc(m, *arg_i); | |
1306 | else | |
1307 | ret = mixer_set(m, j, *arg_i); | |
1308 | snd_mtxunlock(m->lock); | |
2a1ad637 | 1309 | return ((ret == 0) ? 0 : ENXIO); |
984263bc | 1310 | } |
2a1ad637 | 1311 | if ((cmd & ~0xff) == MIXER_READ(0)) { |
984263bc | 1312 | switch (j) { |
2a1ad637 FT |
1313 | case SOUND_MIXER_DEVMASK: |
1314 | case SOUND_MIXER_CAPS: | |
1315 | case SOUND_MIXER_STEREODEVS: | |
984263bc MD |
1316 | v = mix_getdevs(m); |
1317 | break; | |
2a1ad637 | 1318 | case SOUND_MIXER_RECMASK: |
984263bc MD |
1319 | v = mix_getrecdevs(m); |
1320 | break; | |
2a1ad637 | 1321 | case SOUND_MIXER_RECSRC: |
984263bc MD |
1322 | v = mixer_getrecsrc(m); |
1323 | break; | |
984263bc MD |
1324 | default: |
1325 | v = mixer_get(m, j); | |
1326 | } | |
1327 | *arg_i = v; | |
1328 | snd_mtxunlock(m->lock); | |
2a1ad637 | 1329 | return ((v != -1) ? 0 : ENXIO); |
984263bc | 1330 | } |
2a1ad637 | 1331 | done: |
984263bc | 1332 | snd_mtxunlock(m->lock); |
2a1ad637 | 1333 | return (ret); |
984263bc MD |
1334 | } |
1335 | ||
8bd5748c MD |
1336 | /* |
1337 | * mixer_clone is just used to convert /dev/mixer to the correct mixer unit. | |
1338 | */ | |
1339 | int | |
1340 | mixer_clone(struct dev_clone_args *ap) | |
984263bc | 1341 | { |
8bd5748c | 1342 | struct cdev *i_dev = ap->a_head.a_dev; |
2a1ad637 | 1343 | struct snddev_info *d; |
8bd5748c | 1344 | int unit; |
984263bc | 1345 | |
8bd5748c MD |
1346 | /* |
1347 | * The default dsp device has a special unit which must be adjusted, | |
1348 | * otherwise the unit number is already correct. | |
1349 | */ | |
1350 | unit = PCMUNIT(i_dev); | |
1351 | if (unit == PCMUNIT_DEFAULT) | |
1352 | unit = snd_unit; | |
1353 | d = devclass_get_softc(pcm_devclass, unit); | |
1354 | if (d == NULL) | |
1355 | return ENODEV; | |
1356 | if (!PCM_REGISTERED(d)) | |
1357 | return (ENODEV); | |
1358 | ||
86ffaa1e | 1359 | ap->a_dev = d->mixer_dev; |
8bd5748c MD |
1360 | |
1361 | return (0); | |
984263bc MD |
1362 | } |
1363 | ||
1364 | static void | |
1365 | mixer_sysinit(void *p) | |
1366 | { | |
2a1ad637 FT |
1367 | if (mixer_ehtag != NULL) |
1368 | return; | |
984263bc MD |
1369 | mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); |
1370 | } | |
1371 | ||
1372 | static void | |
1373 | mixer_sysuninit(void *p) | |
1374 | { | |
2a1ad637 FT |
1375 | if (mixer_ehtag == NULL) |
1376 | return; | |
1377 | EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); | |
1378 | mixer_ehtag = NULL; | |
984263bc MD |
1379 | } |
1380 | ||
1381 | SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); | |
1382 | SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); | |
984263bc | 1383 | |
2a1ad637 FT |
1384 | /** |
1385 | * @brief Handler for SNDCTL_MIXERINFO | |
1386 | * | |
1387 | * This function searches for a mixer based on the numeric ID stored | |
1388 | * in oss_miserinfo::dev. If set to -1, then information about the | |
1389 | * current mixer handling the request is provided. Note, however, that | |
1390 | * this ioctl may be made with any sound device (audio, mixer, midi). | |
1391 | * | |
1392 | * @note Caller must not hold any PCM device, channel, or mixer locks. | |
1393 | * | |
1394 | * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for | |
1395 | * more information. | |
1396 | * | |
1397 | * @param i_dev character device on which the ioctl arrived | |
1398 | * @param arg user argument (oss_mixerinfo *) | |
1399 | * | |
1400 | * @retval EINVAL oss_mixerinfo::dev specified a bad value | |
1401 | * @retval 0 success | |
1402 | */ | |
1403 | int | |
1404 | mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) | |
1405 | { | |
1406 | struct snddev_info *d; | |
1407 | struct snd_mixer *m; | |
1408 | int nmix, i; | |
1409 | ||
1410 | /* | |
1411 | * If probing the device handling the ioctl, make sure it's a mixer | |
1412 | * device. (This ioctl is valid on audio, mixer, and midi devices.) | |
1413 | */ | |
8bd5748c | 1414 | if (mi->dev == -1 && i_dev->si_ops != &mixer_ops) |
2a1ad637 FT |
1415 | return (EINVAL); |
1416 | ||
1417 | d = NULL; | |
1418 | m = NULL; | |
1419 | nmix = 0; | |
1420 | ||
1421 | /* | |
1422 | * There's a 1:1 relationship between mixers and PCM devices, so | |
1423 | * begin by iterating over PCM devices and search for our mixer. | |
1424 | */ | |
1425 | for (i = 0; pcm_devclass != NULL && | |
1426 | i < devclass_get_maxunit(pcm_devclass); i++) { | |
1427 | d = devclass_get_softc(pcm_devclass, i); | |
1428 | if (!PCM_REGISTERED(d)) | |
1429 | continue; | |
984263bc | 1430 | |
2a1ad637 FT |
1431 | /* XXX Need Giant magic entry */ |
1432 | ||
1433 | /* See the note in function docblock. */ | |
1434 | PCM_UNLOCKASSERT(d); | |
1435 | PCM_LOCK(d); | |
1436 | ||
1437 | if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL && | |
1438 | ((mi->dev == -1 && d->mixer_dev == i_dev) || | |
1439 | mi->dev == nmix)) { | |
1440 | m = d->mixer_dev->si_drv1; | |
67931cc4 | 1441 | lockmgr(m->lock, LK_EXCLUSIVE); |
2a1ad637 FT |
1442 | |
1443 | /* | |
1444 | * At this point, the following synchronization stuff | |
1445 | * has happened: | |
1446 | * - a specific PCM device is locked. | |
1447 | * - a specific mixer device has been locked, so be | |
1448 | * sure to unlock when existing. | |
1449 | */ | |
1450 | bzero((void *)mi, sizeof(*mi)); | |
1451 | mi->dev = nmix; | |
67931cc4 | 1452 | ksnprintf(mi->id, sizeof(mi->id), "mixer%d", i); |
2a1ad637 FT |
1453 | strlcpy(mi->name, m->name, sizeof(mi->name)); |
1454 | mi->modify_counter = m->modify_counter; | |
1455 | mi->card_number = i; | |
1456 | /* | |
1457 | * Currently, FreeBSD assumes 1:1 relationship between | |
1458 | * a pcm and mixer devices, so this is hardcoded to 0. | |
1459 | */ | |
1460 | mi->port_number = 0; | |
1461 | ||
1462 | /** | |
1463 | * @todo Fill in @sa oss_mixerinfo::mixerhandle. | |
1464 | * @note From 4Front: "mixerhandle is an arbitrary | |
1465 | * string that identifies the mixer better than | |
1466 | * the device number (mixerinfo.dev). Device | |
1467 | * numbers may change depending on the order the | |
1468 | * drivers are loaded. However the handle should | |
1469 | * remain the same provided that the sound card | |
1470 | * is not moved to another PCI slot." | |
1471 | */ | |
1472 | ||
1473 | /** | |
1474 | * @note | |
1475 | * @sa oss_mixerinfo::magic is a reserved field. | |
1476 | * | |
1477 | * @par | |
1478 | * From 4Front: "magic is usually 0. However some | |
1479 | * devices may have dedicated setup utilities and the | |
1480 | * magic field may contain an unique driver specific | |
1481 | * value (managed by [4Front])." | |
1482 | */ | |
1483 | ||
1484 | mi->enabled = device_is_attached(m->dev) ? 1 : 0; | |
1485 | /** | |
1486 | * The only flag for @sa oss_mixerinfo::caps is | |
1487 | * currently MIXER_CAP_VIRTUAL, which I'm not sure we | |
1488 | * really worry about. | |
1489 | */ | |
1490 | /** | |
1491 | * Mixer extensions currently aren't supported, so | |
1492 | * leave @sa oss_mixerinfo::nrext blank for now. | |
1493 | */ | |
1494 | /** | |
1495 | * @todo Fill in @sa oss_mixerinfo::priority (requires | |
1496 | * touching drivers?) | |
1497 | * @note The priority field is for mixer applets to | |
1498 | * determine which mixer should be the default, with 0 | |
1499 | * being least preferred and 10 being most preferred. | |
1500 | * From 4Front: "OSS drivers like ICH use higher | |
1501 | * values (10) because such chips are known to be used | |
1502 | * only on motherboards. Drivers for high end pro | |
1503 | * devices use 0 because they will never be the | |
1504 | * default mixer. Other devices use values 1 to 9 | |
1505 | * depending on the estimated probability of being the | |
1506 | * default device. | |
1507 | * | |
1508 | * XXX Described by Hannu@4Front, but not found in | |
1509 | * soundcard.h. | |
1510 | strlcpy(mi->devnode, devtoname(d->mixer_dev), | |
1511 | sizeof(mi->devnode)); | |
1512 | mi->legacy_device = i; | |
1513 | */ | |
67931cc4 | 1514 | lockmgr(m->lock, LK_RELEASE); |
2a1ad637 FT |
1515 | } else |
1516 | ++nmix; | |
1517 | ||
1518 | PCM_UNLOCK(d); | |
1519 | ||
1520 | if (m != NULL) | |
1521 | return (0); | |
1522 | } | |
1523 | ||
1524 | return (EINVAL); | |
1525 | } | |
1526 | ||
1527 | /* | |
1528 | * Allow the sound driver to use the mixer lock to protect its mixer | |
1529 | * data: | |
1530 | */ | |
67931cc4 | 1531 | struct lock * |
2a1ad637 FT |
1532 | mixer_get_lock(struct snd_mixer *m) |
1533 | { | |
67931cc4 | 1534 | KASSERT(m->lock != NULL, ("mixer_get_lock() called with NULL parameter")); |
2a1ad637 FT |
1535 | return (m->lock); |
1536 | } | |
1537 | ||
1538 | int | |
1539 | mix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright) | |
1540 | { | |
1541 | int level; | |
1542 | ||
1543 | level = mixer_get(m, dev); | |
1544 | if (level < 0) { | |
1545 | *pright = *pleft = -1; | |
1546 | return (-1); | |
1547 | } | |
1548 | ||
1549 | *pleft = level & 0xFF; | |
1550 | *pright = (level >> 8) & 0xFF; | |
1551 | ||
1552 | return (0); | |
1553 | } | |
1554 | ||
1555 | int | |
1556 | mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right) | |
1557 | { | |
1558 | int level; | |
1559 | ||
1560 | level = (left & 0xFF) | ((right & 0xFF) << 8); | |
1561 | ||
1562 | return (mixer_set(m, dev, level)); | |
1563 | } |