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