vbscript: Added interp_xor implementation.
[wine] / dlls / wineoss.drv / mixer.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIXER Wine Driver for Linux
5  *
6  * Copyright    1997 Marcus Meissner
7  *              1999,2001 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 /* TODO:
25  * + implement notification mechanism when state of mixer's controls
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <string.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <assert.h>
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
43 #endif
44 #ifdef HAVE_SYS_ERRNO_H
45 #include <sys/errno.h>
46 #endif
47 #include <sys/soundcard.h>
48
49 #define NONAMELESSUNION
50 #define NONAMELESSSTRUCT
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winnls.h"
54 #include "mmddk.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
59
60 #define MAX_MIXERDRV     (6)
61
62 #define WINE_MIXER_MANUF_ID             0xAA
63 #define WINE_MIXER_PRODUCT_ID           0x55
64 #define WINE_MIXER_VERSION              0x0100
65 #define WINE_MIXER_NAME                 "WINE OSS Mixer"
66
67 #define WINE_CHN_MASK(_x)               (1L << (_x))
68 #define WINE_CHN_SUPPORTS(_c, _x)       ((_c) & WINE_CHN_MASK(_x))
69 /* Bass and Treble are no longer in the mask as Windows does not handle them */
70 #define WINE_MIXER_MASK_SPEAKER         (WINE_CHN_MASK(SOUND_MIXER_SYNTH)  | \
71                                          WINE_CHN_MASK(SOUND_MIXER_PCM)    | \
72                                          WINE_CHN_MASK(SOUND_MIXER_LINE)   | \
73                                          WINE_CHN_MASK(SOUND_MIXER_MIC)    | \
74                                          WINE_CHN_MASK(SOUND_MIXER_CD)     )
75
76 #define WINE_MIXER_MASK_RECORD          (WINE_CHN_MASK(SOUND_MIXER_SYNTH)  | \
77                                          WINE_CHN_MASK(SOUND_MIXER_LINE)   | \
78                                          WINE_CHN_MASK(SOUND_MIXER_MIC)    | \
79                                          WINE_CHN_MASK(SOUND_MIXER_IMIX)   )
80
81 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
82 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
83 static const char * const MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
84 static const char * const MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
85
86 struct mixerCtrl
87 {
88     DWORD               dwLineID;
89     MIXERCONTROLW       ctrl;
90 };
91
92 struct mixer
93 {
94     char*               name;
95     char*               dev_name;
96     int                 volume[SOUND_MIXER_NRDEVICES];
97     int                 devMask;
98     int                 stereoMask;
99     int                 recMask;
100     BOOL                singleRecChannel;
101     struct mixerCtrl*   ctrl;
102     int                 numCtrl;
103 };
104
105 #define LINEID_DST      0xFFFF
106 #define LINEID_SPEAKER  0x0000
107 #define LINEID_RECORD   0x0001
108
109 static int              MIX_NumMixers;
110 static struct mixer     MIX_Mixers[MAX_MIXERDRV];
111
112 /**************************************************************************
113  */
114
115 static const char * getMessage(UINT uMsg)
116 {
117 #define MSG_TO_STR(x) case x: return #x;
118     switch (uMsg) {
119     MSG_TO_STR(DRVM_INIT);
120     MSG_TO_STR(DRVM_EXIT);
121     MSG_TO_STR(DRVM_ENABLE);
122     MSG_TO_STR(DRVM_DISABLE);
123     MSG_TO_STR(MXDM_GETDEVCAPS);
124     MSG_TO_STR(MXDM_GETLINEINFO);
125     MSG_TO_STR(MXDM_GETNUMDEVS);
126     MSG_TO_STR(MXDM_OPEN);
127     MSG_TO_STR(MXDM_CLOSE);
128     MSG_TO_STR(MXDM_GETLINECONTROLS);
129     MSG_TO_STR(MXDM_GETCONTROLDETAILS);
130     MSG_TO_STR(MXDM_SETCONTROLDETAILS);
131     }
132 #undef MSG_TO_STR
133     return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg);
134 }
135
136 static const char * getIoctlCommand(int command)
137 {
138 #define IOCTL_TO_STR(x) case x: return #x;
139     switch (command) {
140     IOCTL_TO_STR(SOUND_MIXER_VOLUME);
141     IOCTL_TO_STR(SOUND_MIXER_BASS);
142     IOCTL_TO_STR(SOUND_MIXER_TREBLE);
143     IOCTL_TO_STR(SOUND_MIXER_SYNTH);
144     IOCTL_TO_STR(SOUND_MIXER_PCM);
145     IOCTL_TO_STR(SOUND_MIXER_SPEAKER);
146     IOCTL_TO_STR(SOUND_MIXER_LINE);
147     IOCTL_TO_STR(SOUND_MIXER_MIC);
148     IOCTL_TO_STR(SOUND_MIXER_CD);
149     IOCTL_TO_STR(SOUND_MIXER_IMIX);
150     IOCTL_TO_STR(SOUND_MIXER_ALTPCM);
151     IOCTL_TO_STR(SOUND_MIXER_RECLEV);
152     IOCTL_TO_STR(SOUND_MIXER_IGAIN);
153     IOCTL_TO_STR(SOUND_MIXER_OGAIN);
154     IOCTL_TO_STR(SOUND_MIXER_LINE1);
155     IOCTL_TO_STR(SOUND_MIXER_LINE2);
156     IOCTL_TO_STR(SOUND_MIXER_LINE3);
157     IOCTL_TO_STR(SOUND_MIXER_DIGITAL1);
158     IOCTL_TO_STR(SOUND_MIXER_DIGITAL2);
159     IOCTL_TO_STR(SOUND_MIXER_DIGITAL3);
160 #ifdef SOUND_MIXER_PHONEIN
161     IOCTL_TO_STR(SOUND_MIXER_PHONEIN);
162 #endif
163 #ifdef SOUND_MIXER_PHONEOUT
164     IOCTL_TO_STR(SOUND_MIXER_PHONEOUT);
165 #endif
166     IOCTL_TO_STR(SOUND_MIXER_VIDEO);
167     IOCTL_TO_STR(SOUND_MIXER_RADIO);
168 #ifdef SOUND_MIXER_MONITOR
169     IOCTL_TO_STR(SOUND_MIXER_MONITOR);
170 #endif
171     }
172 #undef IOCTL_TO_STR
173     return wine_dbg_sprintf("UNKNOWN(%08x)", command);
174 }
175
176 static const char * getControlType(DWORD dwControlType)
177 {
178 #define TYPE_TO_STR(x) case x: return #x
179     switch (dwControlType) {
180     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
181     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
182     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
183     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
184     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
185     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
186     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
187     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
188     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
189     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
190     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
191     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
192     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
193     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
194     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
195     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
196     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
197     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
198     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
199     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
200     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
201     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
202     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
203     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
204     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
205     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
206     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
207     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
208     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
209     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
210     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
211     }
212 #undef TYPE_TO_STR
213     return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
214 }
215
216 static const char * getComponentType(DWORD dwComponentType)
217 {
218 #define TYPE_TO_STR(x) case x: return #x;
219     switch (dwComponentType) {
220     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
221     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
222     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
223     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
224     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
225     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
226     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
227     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
228     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
229     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
230     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
231     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
232     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
233     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
234     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
235     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
236     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
237     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
238     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
239     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
240     }
241 #undef TYPE_TO_STR
242     return wine_dbg_sprintf("UNKNOWN(%08x)", dwComponentType);
243 }
244
245 static const char * getTargetType(DWORD dwType)
246 {
247 #define TYPE_TO_STR(x) case x: return #x;
248     switch (dwType) {
249     TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
250     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
251     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
252     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
253     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
254     TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
255     }
256 #undef TYPE_TO_STR
257     return wine_dbg_sprintf("UNKNOWN(%08x)", dwType);
258 }
259
260 static const WCHAR sz_short_volume [] = {'V','o','l',0};
261 static const WCHAR sz_long_volume  [] = {'V','o','l','u','m','e',0};
262 static const WCHAR sz_shrtlng_mute [] = {'M','u','t','e',0};
263 static const WCHAR sz_shrtlng_mixer[] = {'M','i','x','e','r',0};
264
265 /**************************************************************************
266  *                              MIX_FillLineControls            [internal]
267  */
268 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID,
269                                  DWORD dwControlType)
270 {
271     struct mixerCtrl*   mc = &mix->ctrl[c];
272     int                 j;
273
274     TRACE("(%p, %d, %08x, %s)\n", mix, c, lineID,
275           getControlType(dwControlType));
276
277     mc->dwLineID = lineID;
278     mc->ctrl.cbStruct = sizeof(MIXERCONTROLW);
279     mc->ctrl.dwControlID = c + 1;
280     mc->ctrl.dwControlType = dwControlType;
281
282     switch (dwControlType)
283     {
284     case MIXERCONTROL_CONTROLTYPE_VOLUME:
285         mc->ctrl.fdwControl = 0;
286         mc->ctrl.cMultipleItems = 0;
287         strcpyW(mc->ctrl.szShortName, sz_short_volume);
288         strcpyW(mc->ctrl.szName, sz_long_volume);
289         memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
290         /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
291          * [0, 100] is the range supported by OSS
292          * whatever the min and max values are they must match
293          * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
294          */
295         mc->ctrl.Bounds.s1.dwMinimum = 0;
296         mc->ctrl.Bounds.s1.dwMaximum = 65535;
297         memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
298         mc->ctrl.Metrics.cSteps = 656;
299         break;
300     case MIXERCONTROL_CONTROLTYPE_MUTE:
301     case MIXERCONTROL_CONTROLTYPE_ONOFF:
302         mc->ctrl.fdwControl = 0;
303         mc->ctrl.cMultipleItems = 0;
304         strcpyW(mc->ctrl.szShortName, sz_shrtlng_mute);
305         strcpyW(mc->ctrl.szName, sz_shrtlng_mute);
306         memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
307         mc->ctrl.Bounds.s1.dwMinimum = 0;
308         mc->ctrl.Bounds.s1.dwMaximum = 1;
309         memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
310         break;
311     case MIXERCONTROL_CONTROLTYPE_MUX:
312     case MIXERCONTROL_CONTROLTYPE_MIXER:
313         mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
314         mc->ctrl.cMultipleItems = 0;
315         for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
316             if (WINE_CHN_SUPPORTS(mix->recMask, j))
317                 mc->ctrl.cMultipleItems++;
318         strcpyW(mc->ctrl.szShortName, sz_shrtlng_mixer);
319         strcpyW(mc->ctrl.szName, sz_shrtlng_mixer);
320         memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
321         mc->ctrl.Bounds.s1.dwMaximum = mc->ctrl.cMultipleItems - 1;
322         memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
323         mc->ctrl.Metrics.cSteps = mc->ctrl.cMultipleItems;
324         break;
325
326     default:
327         FIXME("Internal error: unknown type: %08x\n", dwControlType);
328     }
329     TRACE("ctrl[%2d]: typ=%08x lin=%08x\n", c + 1, dwControlType, lineID);
330 }
331
332 /******************************************************************
333  *              MIX_GetMixer
334  *
335  *
336  */
337 static struct mixer*    MIX_Get(WORD wDevID)
338 {
339     TRACE("(%04x)\n", wDevID);
340
341     if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].dev_name == NULL)
342         return NULL;
343
344     return &MIX_Mixers[wDevID];
345 }
346
347 /**************************************************************************
348  *                              MIX_Open                        [internal]
349  */
350 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
351 {
352     int                 mixer, i, j;
353     unsigned            caps;
354     struct mixer*       mix;
355     DWORD               ret = MMSYSERR_NOERROR;
356
357     TRACE("(%04X, %p, %u);\n", wDevID, lpMod, flags);
358
359     /* as we partly init the mixer with MIX_Open, we can allow null open decs
360      * EPP     if (lpMod == NULL) return MMSYSERR_INVALPARAM;
361      * anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer
362      * device before sending messages to it... it seems to be linked to all
363      * the equivalent of mixer identification
364      * (with a reference to a wave, midi.. handle
365      */
366     if ((mix = MIX_Get(wDevID)) == NULL) {
367         WARN("bad device ID: %04X\n", wDevID);
368         return MMSYSERR_BADDEVICEID;
369     }
370
371     if ((mixer = open(mix->dev_name, O_RDWR)) < 0)
372     {
373         ERR("open(%s, O_RDWR) failed (%s)\n",
374             mix->dev_name, strerror(errno));
375
376         if (errno == ENODEV || errno == ENXIO)
377         {
378             /* no driver present */
379             WARN("no driver\n");
380             return MMSYSERR_NODRIVER;
381         }
382         return MMSYSERR_ERROR;
383     }
384
385     if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
386     {
387         ERR("ioctl(%s, SOUND_MIXER_DEVMASK) failed (%s)\n",
388             mix->dev_name, strerror(errno));
389         ret = MMSYSERR_ERROR;
390         goto error;
391     }
392
393     mix->devMask &= WINE_MIXER_MASK_SPEAKER;
394     if (mix->devMask == 0)
395     {
396         WARN("no driver\n");
397         ret = MMSYSERR_NODRIVER;
398         goto error;
399     }
400
401     if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
402     {
403         ERR("ioctl(%s, SOUND_MIXER_STEREODEVS) failed (%s)\n",
404             mix->dev_name, strerror(errno));
405         ret = MMSYSERR_ERROR;
406         goto error;
407     }
408     mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
409
410     if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
411     {
412         ERR("ioctl(%s, SOUND_MIXER_RECMASK) failed (%s)\n",
413             mix->dev_name, strerror(errno));
414         ret = MMSYSERR_ERROR;
415         goto error;
416     }
417     mix->recMask &= WINE_MIXER_MASK_RECORD;
418     /* FIXME: we may need to support both rec lev & igain */
419     if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
420     {
421         WARN("The sound card doesn't support rec level\n");
422         if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
423             WARN("but it does support IGain, please report\n");
424     }
425     if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
426     {
427         ERR("ioctl(%s, SOUND_MIXER_READ_CAPS) failed (%s)\n",
428             mix->dev_name, strerror(errno));
429         ret = MMSYSERR_ERROR;
430         goto error;
431     }
432     mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
433     TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
434           mix->devMask, mix->recMask, mix->stereoMask,
435           mix->singleRecChannel ? "single" : "multiple");
436     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
437     {
438         mix->volume[i] = -1;
439     }
440     mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
441     /* FIXME: do we always have RECLEV on all cards ??? */
442     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
443     {
444         if (WINE_CHN_SUPPORTS(mix->devMask, i))
445             mix->numCtrl += 2; /* volume & mute */
446         if (WINE_CHN_SUPPORTS(mix->recMask, i))
447             mix->numCtrl += 2; /* volume & onoff */
448
449     }
450     if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
451                                 sizeof(mix->ctrl[0]) * mix->numCtrl)))
452     {
453         ret = MMSYSERR_NOMEM;
454         goto error;
455     }
456
457     j = 0;
458     MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST),
459                          MIXERCONTROL_CONTROLTYPE_VOLUME);
460     MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST),
461                          MIXERCONTROL_CONTROLTYPE_MUTE);
462     MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
463                          mix->singleRecChannel ?
464                              MIXERCONTROL_CONTROLTYPE_MUX :
465                              MIXERCONTROL_CONTROLTYPE_MIXER);
466     MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
467                          MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
468     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
469     {
470         if (WINE_CHN_SUPPORTS(mix->devMask, i))
471         {
472             MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
473                                  MIXERCONTROL_CONTROLTYPE_VOLUME);
474             MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
475                                  MIXERCONTROL_CONTROLTYPE_MUTE);
476         }
477     }
478     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
479     {
480         if (WINE_CHN_SUPPORTS(mix->recMask, i))
481         {
482             MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
483                                  MIXERCONTROL_CONTROLTYPE_VOLUME);
484             MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
485                                  MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
486         }
487     }
488     assert(j == mix->numCtrl);
489  error:
490     close(mixer);
491     return ret;
492 }
493
494 /**************************************************************************
495  *                              MIX_GetVal                      [internal]
496  */
497 static  BOOL    MIX_GetVal(struct mixer* mix, int chn, int* val)
498 {
499     int         mixer;
500     BOOL        ret = FALSE;
501
502     TRACE("(%p, %s, %p\n", mix, getIoctlCommand(chn), val);
503
504     if ((mixer = open(mix->dev_name, O_RDWR)) < 0) {
505         /* FIXME: ENXIO => no mixer installed */
506         WARN("mixer device not available !\n");
507     } else {
508         if (ioctl(mixer, MIXER_READ(chn), val) >= 0) {
509             TRACE("Reading %04x for %s\n", *val, getIoctlCommand(chn));
510             ret = TRUE;
511         } else {
512             ERR("ioctl(%s, MIXER_READ(%s)) failed (%s)\n",
513                 mix->dev_name, getIoctlCommand(chn), strerror(errno));
514         }
515         close(mixer);
516     }
517     return ret;
518 }
519
520 /**************************************************************************
521  *                              MIX_SetVal                      [internal]
522  */
523 static  BOOL    MIX_SetVal(struct mixer* mix, int chn, int val)
524 {
525     int         mixer;
526     BOOL        ret = FALSE;
527
528     TRACE("(%p, %s, %x)\n", mix, getIoctlCommand(chn), val);
529
530     if ((mixer = open(mix->dev_name, O_RDWR)) < 0) {
531         /* FIXME: ENXIO => no mixer installed */
532         WARN("mixer device not available !\n");
533     } else {
534         if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) {
535             TRACE("Set %s to %04x\n", getIoctlCommand(chn), val);
536             ret = TRUE;
537         } else {
538             ERR("ioctl(%s, MIXER_WRITE(%s)) failed (%s)\n",
539                 mix->dev_name, getIoctlCommand(chn), strerror(errno));
540         }
541         close(mixer);
542     }
543     return ret;
544 }
545
546 /******************************************************************
547  *              MIX_GetRecSrc
548  *
549  *
550  */
551 static BOOL     MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
552 {
553     int         mixer;
554     BOOL        ret = FALSE;
555
556     TRACE("(%p, %p)\n", mix, mask);
557
558     if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) {
559         if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) {
560             ret = TRUE;
561         } else {
562             ERR("ioctl(%s, SOUND_MIXER_READ_RECSRC) failed (%s)\n",
563                 mix->dev_name, strerror(errno));
564         }
565         close(mixer);
566     }
567     return ret;
568 }
569
570 /******************************************************************
571  *              MIX_SetRecSrc
572  *
573  *
574  */
575 static BOOL     MIX_SetRecSrc(struct mixer* mix, unsigned mask)
576 {
577     int         mixer;
578     BOOL        ret = FALSE;
579
580     TRACE("(%p, %08x)\n", mix, mask);
581
582     if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) {
583         if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) >= 0) {
584             ret = TRUE;
585         } else {
586             ERR("ioctl(%s, SOUND_MIXER_WRITE_RECSRC) failed (%s)\n",
587                 mix->dev_name, strerror(errno));
588         }
589         close(mixer);
590     }
591     return ret;
592 }
593
594 /**************************************************************************
595  *                              MIX_GetDevCaps                  [internal]
596  */
597 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD dwSize)
598 {
599     struct mixer*       mix;
600     MIXERCAPSW          capsW;
601     const char*         name;
602
603     TRACE("(%04X, %p, %u);\n", wDevID, lpCaps, dwSize);
604
605     if (lpCaps == NULL) {
606         WARN("invalid parameter: lpCaps == NULL\n");
607         return MMSYSERR_INVALPARAM;
608     }
609
610     if ((mix = MIX_Get(wDevID)) == NULL) {
611         WARN("bad device ID: %04X\n", wDevID);
612         return MMSYSERR_BADDEVICEID;
613     }
614
615     capsW.wMid = WINE_MIXER_MANUF_ID;
616     capsW.wPid = WINE_MIXER_PRODUCT_ID;
617     capsW.vDriverVersion = WINE_MIXER_VERSION;
618     if (!(name = mix->name)) name = WINE_MIXER_NAME;
619     MultiByteToWideChar(CP_UNIXCP, 0, name, -1, capsW.szPname, sizeof(capsW.szPname) / sizeof(WCHAR));
620     capsW.cDestinations = 2; /* speakers & record */
621     capsW.fdwSupport = 0; /* No bits defined yet */
622
623     memcpy(lpCaps, &capsW, min(dwSize, sizeof(capsW)));
624
625     return MMSYSERR_NOERROR;
626 }
627
628 /**************************************************************************
629  *                              MIX_GetLineInfoDst      [internal]
630  */
631 static  DWORD   MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEW lpMl,
632                                    DWORD dst)
633 {
634     unsigned mask;
635     int j;
636
637     TRACE("(%p, %p, %08x)\n", mix, lpMl, dst);
638
639     lpMl->dwDestination = dst;
640     switch (dst)
641     {
642     case LINEID_SPEAKER:
643         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
644         mask = mix->devMask;
645         j = SOUND_MIXER_VOLUME;
646         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
647         break;
648     case LINEID_RECORD:
649         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
650         mask = mix->recMask;
651         j = SOUND_MIXER_RECLEV;
652         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
653         break;
654     default:
655         FIXME("shouldn't happen\n");
656         return MMSYSERR_ERROR;
657     }
658     lpMl->dwSource = 0xFFFFFFFF;
659     MultiByteToWideChar(CP_UNIXCP, 0, MIX_Labels[j], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
660     MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[j],  -1, lpMl->szName,      sizeof(lpMl->szName) / sizeof(WCHAR));
661
662     /* we have all connections found in the MIX_DevMask */
663     lpMl->cConnections = 0;
664     for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
665         if (WINE_CHN_SUPPORTS(mask, j))
666             lpMl->cConnections++;
667     lpMl->cChannels = 1;
668     if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
669         lpMl->cChannels++;
670     lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
671     lpMl->cControls = 0;
672     for (j = 0; j < mix->numCtrl; j++)
673         if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
674             lpMl->cControls++;
675
676     return MMSYSERR_NOERROR;
677 }
678
679 /**************************************************************************
680  *                              MIX_GetLineInfoSrc      [internal]
681  */
682 static  DWORD   MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEW lpMl,
683                                    DWORD idx, DWORD dst)
684 {
685     int         i, j;
686     unsigned    mask = (dst) ? mix->recMask : mix->devMask;
687
688     TRACE("(%p, %p, %d, %08x)\n", mix, lpMl, idx, dst);
689
690     MultiByteToWideChar(CP_UNIXCP, 0, MIX_Labels[idx], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
691     MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[idx],  -1, lpMl->szName,      sizeof(lpMl->szName) / sizeof(WCHAR));
692     lpMl->dwLineID = MAKELONG(dst, idx);
693     lpMl->dwDestination = dst;
694     lpMl->cConnections = 1;
695     lpMl->cControls = 0;
696     for (i = 0; i < mix->numCtrl; i++)
697         if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
698             lpMl->cControls++;
699
700     switch (idx)
701     {
702     case SOUND_MIXER_SYNTH:
703         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
704         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
705         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT;
706         break;
707     case SOUND_MIXER_CD:
708         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
709         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
710         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
711         break;
712     case SOUND_MIXER_LINE:
713         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
714         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
715         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
716         break;
717     case SOUND_MIXER_MIC:
718         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
719         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
720         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
721         break;
722     case SOUND_MIXER_PCM:
723         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
724         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
725         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
726         break;
727     case SOUND_MIXER_IMIX:
728         lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
729         lpMl->fdwLine    |= MIXERLINE_LINEF_SOURCE;
730         lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
731         break;
732     default:
733         WARN("Index %d not handled.\n", idx);
734         return MIXERR_INVALLINE;
735     }
736     lpMl->cChannels = 1;
737     if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
738         lpMl->cChannels++;
739     for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
740     {
741         if (WINE_CHN_SUPPORTS(mask, j))
742         {
743             if (j == idx) break;
744             i++;
745         }
746     }
747     lpMl->dwSource = i;
748     return MMSYSERR_NOERROR;
749 }
750
751 /******************************************************************
752  *              MIX_CheckLine
753  */
754 static BOOL MIX_CheckLine(DWORD lineID)
755 {
756     TRACE("(%08x)\n",lineID);
757
758     return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
759             (HIWORD(lineID) == LINEID_DST &&
760              LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
761 }
762
763 /**************************************************************************
764  *                              MIX_GetLineInfo                 [internal]
765  */
766 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD fdwInfo)
767 {
768     int                 i, j;
769     DWORD               ret = MMSYSERR_NOERROR;
770     unsigned            mask;
771     struct mixer*       mix;
772
773     TRACE("(%04X, %p, %u);\n", wDevID, lpMl, fdwInfo);
774
775     if (lpMl == NULL) {
776         WARN("invalid parameter: lpMl = NULL\n");
777         return MMSYSERR_INVALPARAM;
778     }
779
780     if (lpMl->cbStruct != sizeof(*lpMl)) {
781         WARN("invalid parameter: lpMl->cbStruct = %d\n",
782              lpMl->cbStruct);
783         return MMSYSERR_INVALPARAM;
784     }
785
786     if ((mix = MIX_Get(wDevID)) == NULL) {
787         WARN("bad device ID: %04X\n", wDevID);
788         return MMSYSERR_BADDEVICEID;
789     }
790
791     /* FIXME: set all the variables correctly... the lines below
792      * are very wrong...
793      */
794     lpMl->fdwLine       = MIXERLINE_LINEF_ACTIVE;
795     lpMl->dwUser        = 0;
796
797     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
798     {
799     case MIXER_GETLINEINFOF_DESTINATION:
800         TRACE("MIXER_GETLINEINFOF_DESTINATION (%08x)\n", lpMl->dwDestination);
801         if (lpMl->dwDestination >= 2) {
802             WARN("invalid parameter: lpMl->dwDestination = %d >= 2\n",
803                  lpMl->dwDestination);
804             return MMSYSERR_INVALPARAM;
805         }
806         ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination);
807         if (ret != MMSYSERR_NOERROR) {
808             WARN("error\n");
809             return ret;
810         }
811         break;
812     case MIXER_GETLINEINFOF_SOURCE:
813         TRACE("MIXER_GETLINEINFOF_SOURCE (%08x), dst=%08x\n", lpMl->dwSource,
814               lpMl->dwDestination);
815         switch (lpMl->dwDestination)
816         {
817         case LINEID_SPEAKER: mask = mix->devMask; break;
818         case LINEID_RECORD: mask = mix->recMask; break;
819         default:
820             WARN("invalid parameter\n");
821             return MMSYSERR_INVALPARAM;
822         }
823         i = lpMl->dwSource;
824         for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
825         {
826             if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
827                 break;
828         }
829         if (j >= SOUND_MIXER_NRDEVICES) {
830             WARN("invalid line\n");
831             return MIXERR_INVALLINE;
832         }
833         ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination);
834         if (ret != MMSYSERR_NOERROR) {
835             WARN("error\n");
836             return ret;
837         }
838         break;
839     case MIXER_GETLINEINFOF_LINEID:
840         TRACE("MIXER_GETLINEINFOF_LINEID (%08x)\n", lpMl->dwLineID);
841
842         if (!MIX_CheckLine(lpMl->dwLineID)) {
843             WARN("invalid line\n");
844             return MIXERR_INVALLINE;
845         }
846         if (HIWORD(lpMl->dwLineID) == LINEID_DST)
847             ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
848         else
849             ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID),
850                                      LOWORD(lpMl->dwLineID));
851         if (ret != MMSYSERR_NOERROR) {
852             WARN("error\n");
853             return ret;
854         }
855         break;
856     case MIXER_GETLINEINFOF_COMPONENTTYPE:
857         TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%s)\n",
858               getComponentType(lpMl->dwComponentType));
859         switch (lpMl->dwComponentType)
860         {
861         case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
862         case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
863             ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_SPEAKER);
864             break;
865         case MIXERLINE_COMPONENTTYPE_DST_LINE:
866         case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
867         case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
868             ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_RECORD);
869             break;
870         case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
871             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
872             break;
873         case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
874             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
875             break;
876         case MIXERLINE_COMPONENTTYPE_SRC_LINE:
877             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
878             break;
879         case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
880             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
881             break;
882         case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
883             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
884             break;
885         case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
886             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
887             break;
888         default:
889             FIXME("Unhandled component type (%s)\n",
890                   getComponentType(lpMl->dwComponentType));
891             return MMSYSERR_INVALPARAM;
892         }
893         break;
894     case MIXER_GETLINEINFOF_TARGETTYPE:
895         FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n");
896         TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n",
897               getTargetType(lpMl->Target.dwType));
898         switch (lpMl->Target.dwType) {
899         case MIXERLINE_TARGETTYPE_UNDEFINED:
900         case MIXERLINE_TARGETTYPE_WAVEOUT:
901         case MIXERLINE_TARGETTYPE_WAVEIN:
902         case MIXERLINE_TARGETTYPE_MIDIOUT:
903         case MIXERLINE_TARGETTYPE_MIDIIN:
904         case MIXERLINE_TARGETTYPE_AUX:
905         default:
906             FIXME("Unhandled target type (%s)\n",
907                   getTargetType(lpMl->Target.dwType));
908             return MMSYSERR_INVALPARAM;
909         }
910         break;
911     default:
912         WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
913         break;
914     }
915
916     if ((fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) != MIXER_GETLINEINFOF_TARGETTYPE) {
917         const char* name;
918         lpMl->Target.dwDeviceID = 0xFFFFFFFF;
919         lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
920         lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
921         lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
922         if (!(name = mix->name)) name = WINE_MIXER_NAME;
923         MultiByteToWideChar(CP_UNIXCP, 0, name, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
924     }
925
926     return ret;
927 }
928
929 /******************************************************************
930  *              MIX_CheckControl
931  *
932  */
933 static BOOL     MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
934 {
935     TRACE("(%p, %08x)\n", mix, ctrlID);
936
937     return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
938 }
939
940 /**************************************************************************
941  *                              MIX_GetLineControls             [internal]
942  */
943 static  DWORD   MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc,
944                                     DWORD flags)
945 {
946     DWORD               dwRet = MMSYSERR_NOERROR;
947     struct mixer*       mix;
948
949     TRACE("(%04X, %p, %u);\n", wDevID, lpMlc, flags);
950
951     if (lpMlc == NULL) {
952         WARN("invalid parameter: lpMlc == NULL\n");
953         return MMSYSERR_INVALPARAM;
954     }
955
956     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
957         WARN("invalid parameter: lpMlc->cbStruct = %d\n",
958              lpMlc->cbStruct);
959         return MMSYSERR_INVALPARAM;
960     }
961
962     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
963         WARN("invalid parameter: lpMlc->cbmxctrl = %d\n",
964              lpMlc->cbmxctrl);
965         return MMSYSERR_INVALPARAM;
966     }
967
968     if ((mix = MIX_Get(wDevID)) == NULL) {
969         WARN("bad device ID: %04X\n", wDevID);
970         return MMSYSERR_BADDEVICEID;
971     }
972
973     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
974     {
975     case MIXER_GETLINECONTROLSF_ALL:
976         {
977             int         i, j;
978
979             TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n",
980                   lpMlc->dwLineID, lpMlc->cControls);
981
982             for (i = j = 0; i < mix->numCtrl; i++)
983             {
984                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
985                     j++;
986             }
987
988             if (!j || lpMlc->cControls != j) {
989                 WARN("invalid parameter\n");
990                 dwRet = MMSYSERR_INVALPARAM;
991             } else if (!MIX_CheckLine(lpMlc->dwLineID)) {
992                 WARN("invalid line\n");
993                 dwRet = MIXERR_INVALLINE;
994             } else {
995                 for (i = j = 0; i < mix->numCtrl; i++)
996                 {
997                     if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
998                     {
999                         TRACE("[%d] => [%2d]: typ=%08x\n", j, i + 1,
1000                               mix->ctrl[i].ctrl.dwControlType);
1001                         lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
1002                     }
1003                 }
1004             }
1005         }
1006         break;
1007     case MIXER_GETLINECONTROLSF_ONEBYID:
1008         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n",
1009               lpMlc->dwLineID, lpMlc->u.dwControlID);
1010
1011         if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
1012             mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID) {
1013             WARN("invalid parameter\n");
1014             dwRet = MMSYSERR_INVALPARAM;
1015         } else
1016             lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
1017         break;
1018     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
1019         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n",
1020               lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
1021         if (!MIX_CheckLine(lpMlc->dwLineID)) {
1022             WARN("invalid line\n");
1023             dwRet = MIXERR_INVALLINE;
1024         } else {
1025             int   i;
1026             DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1027             for (i = 0; i < mix->numCtrl; i++) {
1028                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
1029                     ct == (mix->ctrl[i].ctrl.dwControlType &
1030                     MIXERCONTROL_CT_CLASS_MASK)) {
1031                     lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
1032                     break;
1033                 }
1034             }
1035
1036             if (i == mix->numCtrl) {
1037                 WARN("invalid parameter: control not found\n");
1038                 dwRet = MMSYSERR_INVALPARAM;
1039             }
1040         }
1041         break;
1042     default:
1043         ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1044         dwRet = MMSYSERR_INVALPARAM;
1045     }
1046
1047     return dwRet;
1048 }
1049
1050 /**************************************************************************
1051  *                              MIX_GetControlDetails           [internal]
1052  */
1053 static  DWORD   MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
1054                                       DWORD fdwDetails)
1055 {
1056     DWORD               ret = MMSYSERR_NOTSUPPORTED;
1057     DWORD               chnl;
1058     struct mixer*       mix;
1059
1060     TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails);
1061
1062     if (lpmcd == NULL) {
1063         WARN("invalid parameter: lpmcd == NULL\n");
1064         return MMSYSERR_INVALPARAM;
1065     }
1066
1067     if ((mix = MIX_Get(wDevID)) == NULL) {
1068         WARN("bad device ID: %04X\n", wDevID);
1069         return MMSYSERR_BADDEVICEID;
1070     }
1071
1072     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1073     {
1074     case MIXER_GETCONTROLDETAILSF_VALUE:
1075         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID);
1076         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1077         {
1078             DWORD c = lpmcd->dwControlID - 1;
1079             chnl = HIWORD(mix->ctrl[c].dwLineID);
1080             if (chnl == LINEID_DST)
1081                 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV :
1082                     SOUND_MIXER_VOLUME;
1083             switch (mix->ctrl[c].ctrl.dwControlType)
1084             {
1085             case MIXERCONTROL_CONTROLTYPE_VOLUME:
1086                 {
1087                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
1088                     int                                 val;
1089
1090                     if (lpmcd->cbDetails !=
1091                         sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1092                         WARN("invalid parameter: cbDetails = %d\n",
1093                              lpmcd->cbDetails);
1094                         return MMSYSERR_INVALPARAM;
1095                     }
1096
1097                     TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1098                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1099                           lpmcd->cChannels);
1100
1101                     mcdu = lpmcd->paDetails;
1102
1103                     /* return value is 00RL (4 bytes)... */
1104                     if ((val = mix->volume[chnl]) == -1 &&
1105                         !MIX_GetVal(mix, chnl, &val)) {
1106                         WARN("invalid parameter\n");
1107                         return MMSYSERR_INVALPARAM;
1108                     }
1109
1110                     switch (lpmcd->cChannels)
1111                     {
1112                     case 1:
1113                         /* mono... so R = L */
1114                         mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1115                         TRACE("Reading RL = %d\n", mcdu->dwValue);
1116                         break;
1117                     case 2:
1118                         /* stereo, left is paDetails[0] */
1119                         mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1120                         TRACE("Reading L = %d\n", mcdu->dwValue);
1121                         mcdu++;
1122                         mcdu->dwValue = ((HIBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1123                         TRACE("Reading R = %d\n", mcdu->dwValue);
1124                         break;
1125                     default:
1126                         WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
1127                         return MMSYSERR_INVALPARAM;
1128                     }
1129                     TRACE("=> %08x\n", mcdu->dwValue);
1130                 }
1131                 break;
1132             case MIXERCONTROL_CONTROLTYPE_MUTE:
1133             case MIXERCONTROL_CONTROLTYPE_ONOFF:
1134                 {
1135                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1136
1137                     if (lpmcd->cbDetails !=
1138                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1139                         WARN("invalid parameter: cbDetails = %d\n",
1140                              lpmcd->cbDetails);
1141                         return MMSYSERR_INVALPARAM;
1142                     }
1143
1144                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1145                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1146                           lpmcd->cChannels);
1147
1148                     /* we mute both channels at the same time */
1149                     mcdb = lpmcd->paDetails;
1150                     mcdb->fValue = (mix->volume[chnl] != -1);
1151                     TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
1152                 }
1153                 break;
1154             case MIXERCONTROL_CONTROLTYPE_MIXER:
1155             case MIXERCONTROL_CONTROLTYPE_MUX:
1156                 {
1157                     unsigned                            mask;
1158
1159                     if (lpmcd->cbDetails !=
1160                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1161                         WARN("invalid parameter: cbDetails = %d\n",
1162                              lpmcd->cbDetails);
1163                         return MMSYSERR_INVALPARAM;
1164                     }
1165
1166                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1167                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1168                           lpmcd->cChannels);
1169
1170                     if (!MIX_GetRecSrc(mix, &mask))
1171                     {
1172                         /* FIXME: ENXIO => no mixer installed */
1173                         WARN("mixer device not available !\n");
1174                         ret = MMSYSERR_ERROR;
1175                     }
1176                     else
1177                     {
1178                         LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
1179                         int                             i, j;
1180
1181                         /* we mute both channels at the same time */
1182                         mcdb = lpmcd->paDetails;
1183
1184                         for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1185                         {
1186                             if (WINE_CHN_SUPPORTS(mix->recMask, j))
1187                             {
1188                                 if (i >= lpmcd->u.cMultipleItems)
1189                                     return MMSYSERR_INVALPARAM;
1190                                 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
1191                             }
1192                         }
1193                     }
1194                 }
1195                 break;
1196             default:
1197                 WARN("%s Unsupported\n",
1198                      getControlType(mix->ctrl[c].ctrl.dwControlType));
1199             }
1200             ret = MMSYSERR_NOERROR;
1201         }
1202         else
1203         {
1204             WARN("invalid parameter\n");
1205             ret = MMSYSERR_INVALPARAM;
1206         }
1207         break;
1208     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1209         TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%08x)\n",
1210               lpmcd->dwControlID);
1211
1212         ret = MMSYSERR_INVALPARAM;
1213         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1214         {
1215             DWORD c = lpmcd->dwControlID - 1;
1216
1217             if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
1218                 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
1219             {
1220                 LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt;
1221                 int i, j;
1222
1223                 mcdlt = lpmcd->paDetails;
1224                 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1225                 {
1226                     if (WINE_CHN_SUPPORTS(mix->recMask, j))
1227                     {
1228                         mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
1229                         mcdlt[i].dwParam2 = 0;
1230                         MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[j], -1,
1231                                             mcdlt[i].szName, sizeof(mcdlt[i]) / sizeof(WCHAR));
1232                         i++;
1233                     }
1234                 }
1235                 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
1236                 ret = MMSYSERR_NOERROR;
1237             }
1238         }
1239         break;
1240     default:
1241         WARN("Unknown flag (%08lx)\n",
1242              fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1243     }
1244     return ret;
1245 }
1246
1247 /**************************************************************************
1248  *                              MIX_SetControlDetails           [internal]
1249  */
1250 static  DWORD   MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
1251                                       DWORD fdwDetails)
1252 {
1253     DWORD               ret = MMSYSERR_NOTSUPPORTED;
1254     DWORD               c, chnl;
1255     int                 val;
1256     struct mixer*       mix;
1257
1258     TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails);
1259
1260     if (lpmcd == NULL) {
1261         TRACE("invalid parameter: lpmcd == NULL\n");
1262         return MMSYSERR_INVALPARAM;
1263     }
1264
1265     if ((mix = MIX_Get(wDevID)) == NULL) {
1266         WARN("bad device ID: %04X\n", wDevID);
1267         return MMSYSERR_BADDEVICEID;
1268     }
1269
1270     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1271     {
1272     case MIXER_GETCONTROLDETAILSF_VALUE:
1273         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID);
1274         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1275         {
1276             c = lpmcd->dwControlID - 1;
1277
1278             TRACE("dwLineID=%08x\n",mix->ctrl[c].dwLineID);
1279
1280             chnl = HIWORD(mix->ctrl[c].dwLineID);
1281             if (chnl == LINEID_DST)
1282                 chnl = LOWORD(mix->ctrl[c].dwLineID) ?
1283                     SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
1284
1285             switch (mix->ctrl[c].ctrl.dwControlType)
1286             {
1287             case MIXERCONTROL_CONTROLTYPE_VOLUME:
1288                 {
1289                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
1290
1291                     if (lpmcd->cbDetails !=
1292                         sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1293                         WARN("invalid parameter: cbDetails = %d\n",
1294                              lpmcd->cbDetails);
1295                         return MMSYSERR_INVALPARAM;
1296                     }
1297
1298                     TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1299                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1300                           lpmcd->cChannels);
1301
1302                     mcdu = lpmcd->paDetails;
1303                     /* val should contain 00RL */
1304                     switch (lpmcd->cChannels)
1305                     {
1306                     case 1:
1307                         /* mono... so R = L */
1308                         TRACE("Setting RL to %d\n", mcdu->dwValue);
1309                         val = 0x101 * ((mcdu->dwValue * 100) >> 16);
1310                         break;
1311                     case 2:
1312                         /* stereo, left is paDetails[0] */
1313                         TRACE("Setting L to %d\n", mcdu->dwValue);
1314                         val = ((mcdu->dwValue * 100.0) / 65536.0) + 0.5;
1315                         mcdu++;
1316                         TRACE("Setting R to %d\n", mcdu->dwValue);
1317                         val += (int)(((mcdu->dwValue * 100) / 65536.0) + 0.5) << 8;
1318                         break;
1319                     default:
1320                         WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
1321                         return MMSYSERR_INVALPARAM;
1322                     }
1323
1324                     if (mix->volume[chnl] == -1)
1325                     {
1326                         if (!MIX_SetVal(mix, chnl, val)) {
1327                             WARN("invalid parameter\n");
1328                             return MMSYSERR_INVALPARAM;
1329                         }
1330                     }
1331                     else
1332                     {
1333                         mix->volume[chnl] = val;
1334                     }
1335                 }
1336                 ret = MMSYSERR_NOERROR;
1337                 break;
1338             case MIXERCONTROL_CONTROLTYPE_MUTE:
1339             case MIXERCONTROL_CONTROLTYPE_ONOFF:
1340                 {
1341                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1342
1343                     if (lpmcd->cbDetails !=
1344                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1345                         WARN("invalid parameter: cbDetails = %d\n",
1346                              lpmcd->cbDetails);
1347                         return MMSYSERR_INVALPARAM;
1348                     }
1349
1350                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1351                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1352                           lpmcd->cChannels);
1353
1354                     mcdb = lpmcd->paDetails;
1355                     if (mcdb->fValue)
1356                     {
1357                         /* save the volume and then set it to 0 */
1358                         if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) ||
1359                             !MIX_SetVal(mix, chnl, 0)) {
1360                             WARN("invalid parameter\n");
1361                             return MMSYSERR_INVALPARAM;
1362                         }
1363                     }
1364                     else
1365                     {
1366                         if (mix->volume[chnl] == -1)
1367                         {
1368                             ret = MMSYSERR_NOERROR;
1369                             break;
1370                         }
1371                         if (!MIX_SetVal(mix, chnl, mix->volume[chnl])) {
1372                             WARN("invalid parameter\n");
1373                             return MMSYSERR_INVALPARAM;
1374                         }
1375                         mix->volume[chnl] = -1;
1376                     }
1377                 }
1378                 ret = MMSYSERR_NOERROR;
1379                 break;
1380             case MIXERCONTROL_CONTROLTYPE_MIXER:
1381             case MIXERCONTROL_CONTROLTYPE_MUX:
1382                 {
1383                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1384                     unsigned                            mask;
1385                     int                                 i, j;
1386
1387                     if (lpmcd->cbDetails !=
1388                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1389                         WARN("invalid parameter: cbDetails = %d\n",
1390                              lpmcd->cbDetails);
1391                         return MMSYSERR_INVALPARAM;
1392                     }
1393
1394                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1395                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1396                           lpmcd->cChannels);
1397
1398                     /* we mute both channels at the same time */
1399                     mcdb = lpmcd->paDetails;
1400                     mask = 0;
1401                     for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1402                     {
1403                         if (WINE_CHN_SUPPORTS(mix->recMask, j) &&
1404                             mcdb[i++].fValue)
1405                         {
1406                             /* a mux can only select one line at a time... */
1407                             if (mix->singleRecChannel && mask != 0)
1408                             {
1409                                 FIXME("!!!\n");
1410                                 return MMSYSERR_INVALPARAM;
1411                             }
1412                             mask |= WINE_CHN_MASK(j);
1413                         }
1414                     }
1415                     if (i != lpmcd->u.cMultipleItems)
1416                         FIXME("bad count\n");
1417                     TRACE("writing %04x as rec src\n", mask);
1418                     if (!MIX_SetRecSrc(mix, mask))
1419                         ERR("Can't write new mixer settings\n");
1420                     else
1421                         ret = MMSYSERR_NOERROR;
1422                 }
1423                 break;
1424             }
1425         }
1426         break;
1427     default:
1428         WARN("Unknown SetControlDetails flag (%08lx)\n",
1429              fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1430     }
1431     return ret;
1432 }
1433
1434 /**************************************************************************
1435  *                              MIX_Init                        [internal]
1436  */
1437 static LRESULT OSS_MixerInit(void)
1438 {
1439     int i, mixer;
1440
1441     TRACE("()\n");
1442
1443     MIX_NumMixers = 0;
1444
1445     for (i = 0; i < MAX_MIXERDRV; i++) {
1446         char name[32];
1447
1448         if (i == 0)
1449             sprintf(name, "/dev/mixer");
1450         else
1451             sprintf(name, "/dev/mixer%d", i);
1452
1453         if ((mixer = open(name, O_RDWR)) >= 0) {
1454 #ifdef SOUND_MIXER_INFO
1455             mixer_info info;
1456             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
1457                 MIX_Mixers[MIX_NumMixers].name = HeapAlloc(GetProcessHeap(),0,strlen(info.name) + 1);
1458                 strcpy(MIX_Mixers[MIX_NumMixers].name, info.name);
1459             } else {
1460                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
1461                  * implement it properly, and there are probably similar issues
1462                  * on other platforms, so we warn but try to go ahead.
1463                  */
1464                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", name);
1465             }
1466 #endif
1467             close(mixer);
1468
1469             MIX_Mixers[MIX_NumMixers].dev_name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1470             strcpy(MIX_Mixers[MIX_NumMixers].dev_name, name);
1471             MIX_NumMixers++;
1472             MIX_Open(MIX_NumMixers - 1, NULL, 0); /* FIXME */
1473         } else {
1474             WARN("couldn't open %s\n", name);
1475         }
1476     }
1477
1478     if (MIX_NumMixers == 0) {
1479         WARN("no driver\n");
1480         return MMSYSERR_NODRIVER;
1481     }
1482
1483     return MMSYSERR_NOERROR;
1484 }
1485
1486 /**************************************************************************
1487  *                              MIX_Exit                        [internal]
1488  */
1489 static LRESULT OSS_MixerExit(void)
1490 {
1491     int i;
1492
1493     TRACE("()\n");
1494
1495     for (i = 0; i < MIX_NumMixers; i++) {
1496         HeapFree(GetProcessHeap(),0,MIX_Mixers[i].name);
1497         HeapFree(GetProcessHeap(),0,MIX_Mixers[i].dev_name);
1498     }
1499
1500     return MMSYSERR_NOERROR;
1501 }
1502
1503 /**************************************************************************
1504  *                              MIX_GetNumDevs                  [internal]
1505  */
1506 static  DWORD   MIX_GetNumDevs(void)
1507 {
1508     TRACE("()\n");
1509
1510     return MIX_NumMixers;
1511 }
1512
1513 /**************************************************************************
1514  *                              mxdMessage (WINEOSS.3)
1515  */
1516 DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1517                             DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1518 {
1519     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1520           dwUser, dwParam1, dwParam2);
1521
1522     switch (wMsg)
1523     {
1524     case DRVM_INIT:
1525         return OSS_MixerInit();
1526     case DRVM_EXIT:
1527         return OSS_MixerExit();
1528     case DRVM_ENABLE:
1529     case DRVM_DISABLE:
1530         /* FIXME: Pretend this is supported */
1531         return 0;
1532     case MXDM_GETDEVCAPS:
1533         return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1534     case MXDM_GETLINEINFO:
1535         return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1536     case MXDM_GETNUMDEVS:
1537         return MIX_GetNumDevs();
1538     case MXDM_OPEN:
1539         return MMSYSERR_NOERROR;
1540         /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1541     case MXDM_CLOSE:
1542         return MMSYSERR_NOERROR;
1543     case MXDM_GETLINECONTROLS:
1544         return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1545     case MXDM_GETCONTROLDETAILS:
1546         return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1547     case MXDM_SETCONTROLDETAILS:
1548         return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1549     default:
1550         WARN("unknown message %d!\n", wMsg);
1551         return MMSYSERR_NOTSUPPORTED;
1552     }
1553 }