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