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