Moved midimap to the top-level dlls directory.
[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_HEADPHONES:
864         case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
865             ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_SPEAKER);
866             break;
867         case MIXERLINE_COMPONENTTYPE_DST_LINE:
868         case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
869         case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
870             ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_RECORD);
871             break;
872         case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
873             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
874             break;
875         case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
876             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
877             break;
878         case MIXERLINE_COMPONENTTYPE_SRC_LINE:
879             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
880             break;
881         case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
882             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
883             break;
884         case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
885             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
886             break;
887         case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
888             ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
889             break;
890         default:
891             FIXME("Unhandled component type (%s)\n",
892                   getComponentType(lpMl->dwComponentType));
893             return MMSYSERR_INVALPARAM;
894         }
895         break;
896     case MIXER_GETLINEINFOF_TARGETTYPE:
897         FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n");
898         TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n",
899               getTargetType(lpMl->Target.dwType));
900         switch (lpMl->Target.dwType) {
901         case MIXERLINE_TARGETTYPE_UNDEFINED:
902         case MIXERLINE_TARGETTYPE_WAVEOUT:
903         case MIXERLINE_TARGETTYPE_WAVEIN:
904         case MIXERLINE_TARGETTYPE_MIDIOUT:
905         case MIXERLINE_TARGETTYPE_MIDIIN:
906         case MIXERLINE_TARGETTYPE_AUX:
907         default:
908             FIXME("Unhandled target type (%s)\n",
909                   getTargetType(lpMl->Target.dwType));
910             return MMSYSERR_INVALPARAM;
911         }
912         break;
913     default:
914         WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
915         break;
916     }
917
918     if ((fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) != MIXER_GETLINEINFOF_TARGETTYPE) {
919         const char* name;
920         lpMl->Target.dwDeviceID = 0xFFFFFFFF;
921         lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
922         lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
923         lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
924         if (!(name = mix->name)) name = WINE_MIXER_NAME;
925         MultiByteToWideChar(CP_ACP, 0, name, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
926     }
927
928     return ret;
929 }
930
931 /******************************************************************
932  *              MIX_CheckControl
933  *
934  */
935 static BOOL     MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
936 {
937     TRACE("(%p, %08lx)\n", mix, ctrlID);
938
939     return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
940 }
941
942 /**************************************************************************
943  *                              MIX_GetLineControls             [internal]
944  */
945 static  DWORD   MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc,
946                                     DWORD flags)
947 {
948     DWORD               dwRet = MMSYSERR_NOERROR;
949     struct mixer*       mix;
950
951     TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
952
953     if (lpMlc == NULL) {
954         WARN("invalid parameter: lpMlc == NULL\n");
955         return MMSYSERR_INVALPARAM;
956     }
957
958     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
959         WARN("invalid parameter: lpMlc->cbStruct = %ld < %d\n",
960              lpMlc->cbStruct, sizeof(*lpMlc));
961         return MMSYSERR_INVALPARAM;
962     }
963
964     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
965         WARN("invalid parameter: lpMlc->cbmxctrl = %ld < %d\n",
966              lpMlc->cbmxctrl, sizeof(MIXERCONTROLW));
967         return MMSYSERR_INVALPARAM;
968     }
969
970     if ((mix = MIX_Get(wDevID)) == NULL) {
971         WARN("bad device id\n");
972         return MMSYSERR_BADDEVICEID;
973     }
974
975     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
976     {
977     case MIXER_GETLINECONTROLSF_ALL:
978         {
979             int         i, j;
980
981             TRACE("line=%08lx MIXER_GETLINECONTROLSF_ALL (%ld)\n",
982                   lpMlc->dwLineID, lpMlc->cControls);
983
984             for (i = j = 0; i < mix->numCtrl; i++)
985             {
986                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
987                     j++;
988             }
989
990             if (!j || lpMlc->cControls != j) {
991                 WARN("invalid parameter\n");
992                 dwRet = MMSYSERR_INVALPARAM;
993             } else if (!MIX_CheckLine(lpMlc->dwLineID)) {
994                 WARN("invalid line\n");
995                 dwRet = MIXERR_INVALLINE;
996             } else {
997                 for (i = j = 0; i < mix->numCtrl; i++)
998                 {
999                     if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
1000                     {
1001                         TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1,
1002                               mix->ctrl[i].ctrl.dwControlType);
1003                         lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
1004                     }
1005                 }
1006             }
1007         }
1008         break;
1009     case MIXER_GETLINECONTROLSF_ONEBYID:
1010         TRACE("line=%08lx MIXER_GETLINECONTROLSF_ONEBYID (%lx)\n",
1011               lpMlc->dwLineID, lpMlc->u.dwControlID);
1012
1013         if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
1014             mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID) {
1015             WARN("invalid parameter\n");
1016             dwRet = MMSYSERR_INVALPARAM;
1017         } else
1018             lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
1019         break;
1020     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
1021         TRACE("line=%08lx MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n",
1022               lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
1023         if (!MIX_CheckLine(lpMlc->dwLineID)) {
1024             WARN("invalid line\n");
1025             dwRet = MIXERR_INVALLINE;
1026         } else {
1027             int   i;
1028             DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1029             for (i = 0; i < mix->numCtrl; i++) {
1030                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
1031                     ct == (mix->ctrl[i].ctrl.dwControlType &
1032                     MIXERCONTROL_CT_CLASS_MASK)) {
1033                     lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
1034                     break;
1035                 }
1036             }
1037
1038             if (i == mix->numCtrl) {
1039                 WARN("invalid parameter: control not found\n");
1040                 dwRet = MMSYSERR_INVALPARAM;
1041             }
1042         }
1043         break;
1044     default:
1045         ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1046         dwRet = MMSYSERR_INVALPARAM;
1047     }
1048
1049     return dwRet;
1050 }
1051
1052 /**************************************************************************
1053  *                              MIX_GetControlDetails           [internal]
1054  */
1055 static  DWORD   MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
1056                                       DWORD fdwDetails)
1057 {
1058     DWORD               ret = MMSYSERR_NOTSUPPORTED;
1059     DWORD               c, chnl;
1060     struct mixer*       mix;
1061
1062     TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
1063
1064     if (lpmcd == NULL) {
1065         WARN("invalid parameter: lpmcd == NULL\n");
1066         return MMSYSERR_INVALPARAM;
1067     }
1068
1069     if ((mix = MIX_Get(wDevID)) == NULL) {
1070         WARN("bad device ID\n");
1071         return MMSYSERR_BADDEVICEID;
1072     }
1073
1074     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1075     {
1076     case MIXER_GETCONTROLDETAILSF_VALUE:
1077         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08lx)\n", lpmcd->dwControlID);
1078         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1079         {
1080             c = lpmcd->dwControlID - 1;
1081             chnl = HIWORD(mix->ctrl[c].dwLineID);
1082             if (chnl == LINEID_DST)
1083                 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV :
1084                     SOUND_MIXER_VOLUME;
1085             switch (mix->ctrl[c].ctrl.dwControlType)
1086             {
1087             case MIXERCONTROL_CONTROLTYPE_VOLUME:
1088                 {
1089                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
1090                     int                                 val;
1091
1092                     if (lpmcd->cbDetails !=
1093                         sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1094                         WARN("invalid parameter: cbDetails != %d\n",
1095                              sizeof(MIXERCONTROLDETAILS_UNSIGNED));
1096                         return MMSYSERR_INVALPARAM;
1097                     }
1098
1099                     TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%lu]\n",
1100                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1101                           lpmcd->cChannels);
1102
1103                     mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
1104
1105                     /* return value is 00RL (4 bytes)... */
1106                     if ((val = mix->volume[chnl]) == -1 &&
1107                         !MIX_GetVal(mix, chnl, &val)) {
1108                         WARN("invalid parameter\n");
1109                         return MMSYSERR_INVALPARAM;
1110                     }
1111
1112                     switch (lpmcd->cChannels)
1113                     {
1114                     case 1:
1115                         /* mono... so R = L */
1116                         mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1117                         TRACE("Reading RL = %ld\n", mcdu->dwValue);
1118                         break;
1119                     case 2:
1120                         /* stereo, left is paDetails[0] */
1121                         mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1122                         TRACE("Reading L = %ld\n", mcdu->dwValue);
1123                         mcdu++;
1124                         mcdu->dwValue = ((HIBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1125                         TRACE("Reading R = %ld\n", mcdu->dwValue);
1126                         break;
1127                     default:
1128                         WARN("Unsupported cChannels (%ld)\n", lpmcd->cChannels);
1129                         return MMSYSERR_INVALPARAM;
1130                     }
1131                     TRACE("=> %08lx\n", mcdu->dwValue);
1132                 }
1133                 break;
1134             case MIXERCONTROL_CONTROLTYPE_MUTE:
1135             case MIXERCONTROL_CONTROLTYPE_ONOFF:
1136                 {
1137                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1138
1139                     if (lpmcd->cbDetails !=
1140                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1141                         WARN("invalid parameter: cbDetails != %d\n",
1142                              sizeof(MIXERCONTROLDETAILS_BOOLEAN));
1143                         return MMSYSERR_INVALPARAM;
1144                     }
1145
1146                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%lu]\n",
1147                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1148                           lpmcd->cChannels);
1149
1150                     /* we mute both channels at the same time */
1151                     mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
1152                     mcdb->fValue = (mix->volume[chnl] != -1);
1153                     TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
1154                 }
1155                 break;
1156             case MIXERCONTROL_CONTROLTYPE_MIXER:
1157             case MIXERCONTROL_CONTROLTYPE_MUX:
1158                 {
1159                     unsigned                            mask;
1160
1161                     if (lpmcd->cbDetails !=
1162                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1163                         WARN("invalid parameter: cbDetails != %d\n",
1164                              sizeof(MIXERCONTROLDETAILS_BOOLEAN));
1165                         return MMSYSERR_INVALPARAM;
1166                     }
1167
1168                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%lu]\n",
1169                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1170                           lpmcd->cChannels);
1171
1172                     if (!MIX_GetRecSrc(mix, &mask))
1173                     {
1174                         /* FIXME: ENXIO => no mixer installed */
1175                         WARN("mixer device not available !\n");
1176                         ret = MMSYSERR_ERROR;
1177                     }
1178                     else
1179                     {
1180                         LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
1181                         int                             i, j;
1182
1183                         /* we mute both channels at the same time */
1184                         mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
1185
1186                         for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1187                         {
1188                             if (WINE_CHN_SUPPORTS(mix->recMask, j))
1189                             {
1190                                 if (i >= lpmcd->u.cMultipleItems)
1191                                     return MMSYSERR_INVALPARAM;
1192                                 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
1193                             }
1194                         }
1195                     }
1196                 }
1197                 break;
1198             default:
1199                 WARN("%s Unsupported\n",
1200                      getControlType(mix->ctrl[c].ctrl.dwControlType));
1201             }
1202             ret = MMSYSERR_NOERROR;
1203         }
1204         else
1205         {
1206             WARN("invalid parameter\n");
1207             ret = MMSYSERR_INVALPARAM;
1208         }
1209         break;
1210     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1211         TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%08lx)\n",
1212               lpmcd->dwControlID);
1213
1214         ret = MMSYSERR_INVALPARAM;
1215         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1216         {
1217             int c = lpmcd->dwControlID - 1;
1218
1219             if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
1220                 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
1221             {
1222                 LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt;
1223                 int i, j;
1224
1225                 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTW)lpmcd->paDetails;
1226                 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1227                 {
1228                     if (WINE_CHN_SUPPORTS(mix->recMask, j))
1229                     {
1230                         mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
1231                         mcdlt[i].dwParam2 = 0;
1232                         MultiByteToWideChar(CP_ACP, 0, MIX_Names[j], -1, 
1233                                             mcdlt[i].szName, sizeof(mcdlt[i]) / sizeof(WCHAR));
1234                         i++;
1235                     }
1236                 }
1237                 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
1238                 ret = MMSYSERR_NOERROR;
1239             }
1240         }
1241         break;
1242     default:
1243         WARN("Unknown flag (%08lx)\n",
1244              fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1245     }
1246     return ret;
1247 }
1248
1249 /**************************************************************************
1250  *                              MIX_SetControlDetails           [internal]
1251  */
1252 static  DWORD   MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
1253                                       DWORD fdwDetails)
1254 {
1255     DWORD               ret = MMSYSERR_NOTSUPPORTED;
1256     DWORD               c, chnl;
1257     int                 val;
1258     struct mixer*       mix;
1259
1260     TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
1261
1262     if (lpmcd == NULL) {
1263         TRACE("invalid parameter: lpmcd == NULL\n");
1264         return MMSYSERR_INVALPARAM;
1265     }
1266
1267     if ((mix = MIX_Get(wDevID)) == NULL) {
1268         WARN("bad device ID\n");
1269         return MMSYSERR_BADDEVICEID;
1270     }
1271
1272     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1273     {
1274     case MIXER_GETCONTROLDETAILSF_VALUE:
1275         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08lx)\n", lpmcd->dwControlID);
1276         if (MIX_CheckControl(mix, lpmcd->dwControlID))
1277         {
1278             c = lpmcd->dwControlID - 1;
1279
1280             TRACE("dwLineID=%08lx\n",mix->ctrl[c].dwLineID);
1281
1282             chnl = HIWORD(mix->ctrl[c].dwLineID);
1283             if (chnl == LINEID_DST)
1284                 chnl = LOWORD(mix->ctrl[c].dwLineID) ?
1285                     SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
1286
1287             switch (mix->ctrl[c].ctrl.dwControlType)
1288             {
1289             case MIXERCONTROL_CONTROLTYPE_VOLUME:
1290                 {
1291                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
1292
1293                     if (lpmcd->cbDetails !=
1294                         sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1295                         WARN("invalid parameter: cbDetails != %d\n",
1296                              sizeof(MIXERCONTROLDETAILS_UNSIGNED));
1297                         return MMSYSERR_INVALPARAM;
1298                     }
1299
1300                     TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%lu]\n",
1301                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1302                           lpmcd->cChannels);
1303
1304                     mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
1305                     /* val should contain 00RL */
1306                     switch (lpmcd->cChannels)
1307                     {
1308                     case 1:
1309                         /* mono... so R = L */
1310                         TRACE("Setting RL to %ld\n", mcdu->dwValue);
1311                         val = 0x101 * ((mcdu->dwValue * 100) >> 16);
1312                         break;
1313                     case 2:
1314                         /* stereo, left is paDetails[0] */
1315                         TRACE("Setting L to %ld\n", mcdu->dwValue);
1316                         val = ((mcdu->dwValue * 100.0) / 65536.0) + 0.5;
1317                         mcdu++;
1318                         TRACE("Setting R to %ld\n", mcdu->dwValue);
1319                         val += (int)(((mcdu->dwValue * 100) / 65536.0) + 0.5) << 8;
1320                         break;
1321                     default:
1322                         WARN("Unsupported cChannels (%ld)\n", lpmcd->cChannels);
1323                         return MMSYSERR_INVALPARAM;
1324                     }
1325
1326                     if (mix->volume[chnl] == -1)
1327                     {
1328                         if (!MIX_SetVal(mix, chnl, val)) {
1329                             WARN("invalid parameter\n");
1330                             return MMSYSERR_INVALPARAM;
1331                         }
1332                     }
1333                     else
1334                     {
1335                         mix->volume[chnl] = val;
1336                     }
1337                 }
1338                 ret = MMSYSERR_NOERROR;
1339                 break;
1340             case MIXERCONTROL_CONTROLTYPE_MUTE:
1341             case MIXERCONTROL_CONTROLTYPE_ONOFF:
1342                 {
1343                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1344
1345                     if (lpmcd->cbDetails !=
1346                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1347                         WARN("invalid parameter: cbDetails != %d\n",
1348                              sizeof(MIXERCONTROLDETAILS_BOOLEAN));
1349                         return MMSYSERR_INVALPARAM;
1350                     }
1351
1352                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%lu]\n",
1353                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1354                           lpmcd->cChannels);
1355
1356                     mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
1357                     if (mcdb->fValue)
1358                     {
1359                         /* save the volume and then set it to 0 */
1360                         if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) ||
1361                             !MIX_SetVal(mix, chnl, 0)) {
1362                             WARN("invalid parameter\n");
1363                             return MMSYSERR_INVALPARAM;
1364                         }
1365                     }
1366                     else
1367                     {
1368                         if (mix->volume[chnl] == -1)
1369                         {
1370                             ret = MMSYSERR_NOERROR;
1371                             break;
1372                         }
1373                         if (!MIX_SetVal(mix, chnl, mix->volume[chnl])) {
1374                             WARN("invalid parameter\n");
1375                             return MMSYSERR_INVALPARAM;
1376                         }
1377                         mix->volume[chnl] = -1;
1378                     }
1379                 }
1380                 ret = MMSYSERR_NOERROR;
1381                 break;
1382             case MIXERCONTROL_CONTROLTYPE_MIXER:
1383             case MIXERCONTROL_CONTROLTYPE_MUX:
1384                 {
1385                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
1386                     unsigned                            mask;
1387                     int                                 i, j;
1388
1389                     if (lpmcd->cbDetails !=
1390                         sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1391                         WARN("invalid parameter: cbDetails != %d\n",
1392                              sizeof(MIXERCONTROLDETAILS_BOOLEAN));
1393                         return MMSYSERR_INVALPARAM;
1394                     }
1395
1396                     TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%lu]\n",
1397                           getControlType(mix->ctrl[c].ctrl.dwControlType),
1398                           lpmcd->cChannels);
1399
1400                     /* we mute both channels at the same time */
1401                     mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
1402                     mask = 0;
1403                     for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
1404                     {
1405                         if (WINE_CHN_SUPPORTS(mix->recMask, j) &&
1406                             mcdb[i++].fValue)
1407                         {
1408                             /* a mux can only select one line at a time... */
1409                             if (mix->singleRecChannel && mask != 0)
1410                             {
1411                                 FIXME("!!!\n");
1412                                 return MMSYSERR_INVALPARAM;
1413                             }
1414                             mask |= WINE_CHN_MASK(j);
1415                         }
1416                     }
1417                     if (i != lpmcd->u.cMultipleItems)
1418                         FIXME("bad count\n");
1419                     TRACE("writing %04x as rec src\n", mask);
1420                     if (!MIX_SetRecSrc(mix, mask))
1421                         ERR("Can't write new mixer settings\n");
1422                     else
1423                         ret = MMSYSERR_NOERROR;
1424                 }
1425                 break;
1426             }
1427         }
1428         break;
1429     default:
1430         WARN("Unknown SetControlDetails flag (%08lx)\n",
1431              fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1432     }
1433     return ret;
1434 }
1435
1436 /**************************************************************************
1437  *                              MIX_Init                        [internal]
1438  */
1439 static  DWORD   MIX_Init(void)
1440 {
1441     int i, mixer;
1442
1443     TRACE("()\n");
1444
1445     MIX_NumMixers = 0;
1446
1447     for (i = 0; i < MAX_MIXERDRV; i++) {
1448         char name[32];
1449
1450         if (i == 0)
1451             sprintf(name, "/dev/mixer");
1452         else
1453             sprintf(name, "/dev/mixer%d", i);
1454
1455         if ((mixer = open(name, O_RDWR)) >= 0) {
1456 #ifdef SOUND_MIXER_INFO
1457             mixer_info info;
1458             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
1459                 MIX_Mixers[i].name = HeapAlloc(GetProcessHeap(),0,strlen(info.name) + 1);
1460                 strcpy(MIX_Mixers[i].name, info.name);
1461             } else {
1462                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
1463                  * implement it properly, and there are probably similar issues
1464                  * on other platforms, so we warn but try to go ahead.
1465                  */
1466                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", name);
1467             }
1468 #endif
1469             close(mixer);
1470
1471             MIX_NumMixers++;
1472             MIX_Mixers[i].dev_name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
1473             strcpy(MIX_Mixers[i].dev_name, name);
1474             MIX_Open(i, NULL, 0); /* FIXME */
1475         } else {
1476             WARN("couldn't open %s\n", name);
1477         }
1478     }
1479
1480     if (MIX_NumMixers == 0) {
1481         WARN("no driver\n");
1482         return MMSYSERR_NODRIVER;
1483     }
1484
1485     return MMSYSERR_NOERROR;
1486 }
1487
1488 /**************************************************************************
1489  *                              MIX_Exit                        [internal]
1490  */
1491 static  DWORD   MIX_Exit(void)
1492 {
1493     int i;
1494
1495     TRACE("()\n");
1496
1497     for (i = 0; i < MIX_NumMixers; i++) {
1498         HeapFree(GetProcessHeap(),0,MIX_Mixers[i].name);
1499         HeapFree(GetProcessHeap(),0,MIX_Mixers[i].dev_name);
1500     }
1501
1502     return MMSYSERR_NOERROR;
1503 }
1504
1505 /**************************************************************************
1506  *                              MIX_GetNumDevs                  [internal]
1507  */
1508 static  DWORD   MIX_GetNumDevs(void)
1509 {
1510     TRACE("()\n");
1511
1512     return MIX_NumMixers;
1513 }
1514
1515 #endif /* HAVE_OSS */
1516
1517 /**************************************************************************
1518  *                              mxdMessage (WINEOSS.3)
1519  */
1520 DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1521                             DWORD dwParam1, DWORD dwParam2)
1522 {
1523 #ifdef HAVE_OSS
1524     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1525           dwUser, dwParam1, dwParam2);
1526
1527     switch (wMsg)
1528     {
1529     case DRVM_INIT:
1530         return MIX_Init();
1531     case DRVM_EXIT:
1532         return MIX_Exit();
1533     case DRVM_ENABLE:
1534     case DRVM_DISABLE:
1535         /* FIXME: Pretend this is supported */
1536         return 0;
1537     case MXDM_GETDEVCAPS:
1538         return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1539     case MXDM_GETLINEINFO:
1540         return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1541     case MXDM_GETNUMDEVS:
1542         return MIX_GetNumDevs();
1543     case MXDM_OPEN:
1544         return MMSYSERR_NOERROR;
1545         /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1546     case MXDM_CLOSE:
1547         return MMSYSERR_NOERROR;
1548     case MXDM_GETLINECONTROLS:
1549         return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1550     case MXDM_GETCONTROLDETAILS:
1551         return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1552     case MXDM_SETCONTROLDETAILS:
1553         return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1554     default:
1555         WARN("unknown message %d!\n", wMsg);
1556         return MMSYSERR_NOTSUPPORTED;
1557     }
1558 #else
1559     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg,
1560           dwUser, dwParam1, dwParam2);
1561
1562     return MMSYSERR_NOTENABLED;
1563 #endif
1564 }