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