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