msacm: reroute all SendDriverMessage() calls to MSACM_Message.
[wine] / dlls / msacm / driver.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  *      MSACM32 library
5  *
6  *      Copyright 1998  Patrik Stridvall
7  *                1999  Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28 #include <stdio.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "mmsystem.h"
38 #include "mmreg.h"
39 #include "msacm.h"
40 #include "msacmdrv.h"
41 #include "wineacm.h"
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
46
47 /***********************************************************************
48  *           acmDriverAddA (MSACM32.@)
49  */
50 MMRESULT WINAPI acmDriverAddA(PHACMDRIVERID phadid, HINSTANCE hinstModule,
51                               LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
52 {
53     MMRESULT resultW;
54     WCHAR * driverW = NULL;
55     LPARAM lParamW = lParam;
56
57     TRACE("(%p, %p, %08lx, %08lx, %08lx)\n",
58           phadid, hinstModule, lParam, dwPriority, fdwAdd);
59
60     if (!phadid) {
61         WARN("invalid parameter\n");
62         return MMSYSERR_INVALPARAM;
63     }
64
65     /* Check if any unknown flags */
66     if (fdwAdd &
67         ~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND|
68           ACM_DRIVERADDF_GLOBAL)) {
69         WARN("invalid flag\n");
70         return MMSYSERR_INVALFLAG;
71     }
72
73     /* Check if any incompatible flags */
74     if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) &&
75         (fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) {
76         WARN("invalid flag\n");
77         return MMSYSERR_INVALFLAG;
78     }
79
80     /* A->W translation of name */
81     if ((fdwAdd & ACM_DRIVERADDF_TYPEMASK) == ACM_DRIVERADDF_NAME) {
82         unsigned long len;
83         
84         if (lParam == 0) return MMSYSERR_INVALPARAM;
85         len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0);
86         driverW = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
87         if (!driverW) return MMSYSERR_NOMEM;
88         MultiByteToWideChar(CP_ACP, 0, (LPSTR)lParam, -1, driverW, len);
89         lParamW = (LPARAM)driverW;
90     }
91
92     resultW = acmDriverAddW(phadid, hinstModule, lParamW, dwPriority, fdwAdd);
93     HeapFree(MSACM_hHeap, 0, driverW);
94     return resultW;
95 }
96
97 /***********************************************************************
98  *           acmDriverAddW (MSACM32.@)
99  *
100  */
101 MMRESULT WINAPI acmDriverAddW(PHACMDRIVERID phadid, HINSTANCE hinstModule,
102                               LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
103 {
104     TRACE("(%p, %p, %08lx, %08lx, %08lx)\n",
105           phadid, hinstModule, lParam, dwPriority, fdwAdd);
106
107     if (!phadid) {
108         WARN("invalid parameter\n");
109         return MMSYSERR_INVALPARAM;
110     }
111
112     /* Check if any unknown flags */
113     if (fdwAdd &
114         ~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND|
115           ACM_DRIVERADDF_GLOBAL)) {
116         WARN("invalid flag\n");
117         return MMSYSERR_INVALFLAG;
118     }
119  
120     /* Check if any incompatible flags */
121     if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) &&
122         (fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) {
123         WARN("invalid flag\n");
124         return MMSYSERR_INVALFLAG;
125     }
126
127     switch (fdwAdd & ACM_DRIVERADDF_TYPEMASK) {
128     case ACM_DRIVERADDF_NAME:
129         /*
130                 hInstModule     (unused)
131                 lParam          name of value in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32
132                 dwPriority      (unused, set to 0)
133          */
134         *phadid = (HACMDRIVERID) MSACM_RegisterDriverFromRegistry((LPCWSTR)lParam);        
135         if (!*phadid) {
136             ERR("Unable to register driver via ACM_DRIVERADDF_NAME\n");
137             return MMSYSERR_INVALPARAM;
138         }
139         break;
140     case ACM_DRIVERADDF_FUNCTION:
141         /*
142                 hInstModule     Handle of module which contains driver entry proc
143                 lParam          Driver function address
144                 dwPriority      (unused, set to 0)
145          */
146         fdwAdd &= ~ACM_DRIVERADDF_TYPEMASK;
147
148         *phadid = 0;
149         FIXME("(%p, %p, %ld, %ld, %ld): ACM_DRIVERADDF_FUNCTION: stub\n", phadid, hinstModule, lParam, dwPriority, fdwAdd);
150         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
151         return MMSYSERR_ERROR;
152     case ACM_DRIVERADDF_NOTIFYHWND:
153         /*
154                 hInstModule     (unused)
155                 lParam          Handle of notification window
156                 dwPriority      Window message to send for notification broadcasts
157          */
158         *phadid = (HACMDRIVERID) MSACM_RegisterNotificationWindow((HWND)lParam, dwPriority);
159         if (!*phadid) {
160             ERR("Unable to register driver via ACM_DRIVERADDF_NOTIFYHWND\n");
161             return MMSYSERR_INVALPARAM;
162         }
163         break;
164     default:
165         ERR("invalid flag value 0x%08lx for fdwAdd\n", fdwAdd & ACM_DRIVERADDF_TYPEMASK);
166         return MMSYSERR_INVALFLAG;
167     }
168
169     MSACM_BroadcastNotification();
170     return MMSYSERR_NOERROR;
171 }
172
173 /***********************************************************************
174  *           acmDriverClose (MSACM32.@)
175  */
176 MMRESULT WINAPI acmDriverClose(HACMDRIVER had, DWORD fdwClose)
177 {
178     PWINE_ACMDRIVER     pad;
179     PWINE_ACMDRIVERID   padid;
180     PWINE_ACMDRIVER*    tpad;
181
182     TRACE("(%p, %08lx)\n", had, fdwClose);
183
184     if (fdwClose) {
185         WARN("invalid flag\n");
186         return MMSYSERR_INVALFLAG;
187     }
188
189     pad = MSACM_GetDriver(had);
190     if (!pad) {
191         WARN("invalid handle\n");
192         return MMSYSERR_INVALHANDLE;
193     }
194
195     padid = pad->obj.pACMDriverID;
196
197     /* remove driver from list */
198     for (tpad = &(padid->pACMDriverList); *tpad; tpad = &((*tpad)->pNextACMDriver)) {
199         if (*tpad == pad) {
200             *tpad = (*tpad)->pNextACMDriver;
201             break;
202         }
203     }
204
205     /* close driver if it has been opened */
206     if (pad->hDrvr && !padid->hInstModule)
207         CloseDriver(pad->hDrvr, 0, 0);
208
209     HeapFree(MSACM_hHeap, 0, pad);
210
211     return MMSYSERR_NOERROR;
212 }
213
214 /***********************************************************************
215  *           acmDriverDetailsA (MSACM32.@)
216  */
217 MMRESULT WINAPI acmDriverDetailsA(HACMDRIVERID hadid, PACMDRIVERDETAILSA padd, DWORD fdwDetails)
218 {
219     MMRESULT mmr;
220     ACMDRIVERDETAILSW   addw;
221
222     TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails);
223
224     if (!padd) {
225         WARN("invalid parameter\n");
226         return MMSYSERR_INVALPARAM;
227     }
228
229     if (padd->cbStruct < 4) {
230         WARN("invalid parameter\n");
231         return MMSYSERR_INVALPARAM;
232     }
233
234     addw.cbStruct = sizeof(addw);
235     mmr = acmDriverDetailsW(hadid, &addw, fdwDetails);
236     if (mmr == 0) {
237         ACMDRIVERDETAILSA padda;
238
239         padda.fccType = addw.fccType;
240         padda.fccComp = addw.fccComp;
241         padda.wMid = addw.wMid;
242         padda.wPid = addw.wPid;
243         padda.vdwACM = addw.vdwACM;
244         padda.vdwDriver = addw.vdwDriver;
245         padda.fdwSupport = addw.fdwSupport;
246         padda.cFormatTags = addw.cFormatTags;
247         padda.cFilterTags = addw.cFilterTags;
248         padda.hicon = addw.hicon;
249         WideCharToMultiByte( CP_ACP, 0, addw.szShortName, -1, padda.szShortName,
250                              sizeof(padda.szShortName), NULL, NULL );
251         WideCharToMultiByte( CP_ACP, 0, addw.szLongName, -1, padda.szLongName,
252                              sizeof(padda.szLongName), NULL, NULL );
253         WideCharToMultiByte( CP_ACP, 0, addw.szCopyright, -1, padda.szCopyright,
254                              sizeof(padda.szCopyright), NULL, NULL );
255         WideCharToMultiByte( CP_ACP, 0, addw.szLicensing, -1, padda.szLicensing,
256                              sizeof(padda.szLicensing), NULL, NULL );
257         WideCharToMultiByte( CP_ACP, 0, addw.szFeatures, -1, padda.szFeatures,
258                              sizeof(padda.szFeatures), NULL, NULL );
259         padda.cbStruct = min(padd->cbStruct, sizeof(*padd));
260         memcpy(padd, &padda, padda.cbStruct);
261     }
262     return mmr;
263 }
264
265 /***********************************************************************
266  *           acmDriverDetailsW (MSACM32.@)
267  */
268 MMRESULT WINAPI acmDriverDetailsW(HACMDRIVERID hadid, PACMDRIVERDETAILSW padd, DWORD fdwDetails)
269 {
270     HACMDRIVER acmDrvr;
271     MMRESULT mmr;
272
273     TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails);
274
275     if (!padd) {
276         WARN("invalid parameter\n");
277         return MMSYSERR_INVALPARAM;
278     }
279
280     if (padd->cbStruct < 4) {
281         WARN("invalid parameter\n");
282         return MMSYSERR_INVALPARAM;
283     }
284
285     if (fdwDetails) {
286         WARN("invalid flag\n");
287         return MMSYSERR_INVALFLAG;
288     }
289
290     mmr = acmDriverOpen(&acmDrvr, hadid, 0);
291     if (mmr == MMSYSERR_NOERROR) {
292         ACMDRIVERDETAILSW paddw;
293         paddw.cbStruct = sizeof(paddw);
294         mmr = (MMRESULT)MSACM_Message(acmDrvr, ACMDM_DRIVER_DETAILS, (LPARAM)&paddw,  0);
295
296         acmDriverClose(acmDrvr, 0);
297         paddw.cbStruct = min(padd->cbStruct, sizeof(*padd));
298         memcpy(padd, &paddw, paddw.cbStruct);
299     }
300
301     return mmr;
302 }
303
304 /***********************************************************************
305  *           acmDriverEnum (MSACM32.@)
306  */
307 MMRESULT WINAPI acmDriverEnum(ACMDRIVERENUMCB fnCallback, DWORD dwInstance, DWORD fdwEnum)
308 {
309     PWINE_ACMDRIVERID   padid;
310     DWORD               fdwSupport;
311
312     TRACE("(%p, %08lx, %08lx)\n", fnCallback, dwInstance, fdwEnum);
313
314     if (!fnCallback) {
315         WARN("invalid parameter\n");
316         return MMSYSERR_INVALPARAM;
317     }
318
319     if (fdwEnum & ~(ACM_DRIVERENUMF_NOLOCAL|ACM_DRIVERENUMF_DISABLED)) {
320         WARN("invalid flag\n");
321         return MMSYSERR_INVALFLAG;
322     }
323
324     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
325         fdwSupport = padid->fdwSupport;
326
327         if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
328             if (fdwEnum & ACM_DRIVERENUMF_DISABLED)
329                 fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
330             else
331                 continue;
332         }
333         if (!(*fnCallback)((HACMDRIVERID)padid, dwInstance, fdwSupport))
334             break;
335     }
336
337     return MMSYSERR_NOERROR;
338 }
339
340 /***********************************************************************
341  *           acmDriverID (MSACM32.@)
342  */
343 MMRESULT WINAPI acmDriverID(HACMOBJ hao, PHACMDRIVERID phadid, DWORD fdwDriverID)
344 {
345     PWINE_ACMOBJ pao;
346
347     TRACE("(%p, %p, %08lx)\n", hao, phadid, fdwDriverID);
348
349     if (fdwDriverID) {
350         WARN("invalid flag\n");
351         return MMSYSERR_INVALFLAG;
352     }
353
354     pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE);
355     if (!pao) {
356         WARN("invalid handle\n");
357         return MMSYSERR_INVALHANDLE;
358     }
359
360     if (!phadid) {
361         WARN("invalid parameter\n");
362         return MMSYSERR_INVALPARAM;
363     }
364
365     *phadid = (HACMDRIVERID) pao->pACMDriverID;
366
367     return MMSYSERR_NOERROR;
368 }
369
370 /***********************************************************************
371  *           acmDriverMessage (MSACM32.@)
372  *
373  * Note: MSDN documentation (July 2001) is incomplete. This function
374  * accepts sending messages to an HACMDRIVERID in addition to the
375  * documented HACMDRIVER. In fact, for DRV_QUERYCONFIGURE and DRV_CONFIGURE, 
376  * this might actually be the required mode of operation.
377  *
378  * Note: For DRV_CONFIGURE, msacm supplies its own DRVCONFIGINFO structure
379  * when the application fails to supply one. Some native drivers depend on
380  * this and refuse to display unless a valid DRVCONFIGINFO structure is
381  * built and supplied.
382  */
383 LRESULT WINAPI acmDriverMessage(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
384 {
385     TRACE("(%p, %04x, %08lx, %08lx\n", had, uMsg, lParam1, lParam2);
386
387     if ((uMsg >= ACMDM_USER && uMsg < ACMDM_RESERVED_LOW) ||
388         uMsg == ACMDM_DRIVER_ABOUT ||
389         uMsg == DRV_QUERYCONFIGURE ||
390         uMsg == DRV_CONFIGURE)
391     {
392         PWINE_ACMDRIVERID padid;
393         LRESULT lResult;
394         LPDRVCONFIGINFO pConfigInfo = NULL;
395
396         /* Check whether handle is an HACMDRIVERID */
397         padid  = MSACM_GetDriverID((HACMDRIVERID)had);
398         
399         /* If the message is DRV_CONFIGURE, and the application provides no
400            DRVCONFIGINFO structure, msacm must supply its own.
401          */
402         if (uMsg == DRV_CONFIGURE && lParam2 == 0) {
403             LPWSTR pAlias;
404             
405             /* Get the alias from the HACMDRIVERID */
406             if (padid) {
407                 pAlias = padid->pszDriverAlias;
408                 if (pAlias == NULL) {
409                     WARN("DRV_CONFIGURE: no alias for this driver, cannot self-supply alias\n");
410                 }
411             } else {
412                 FIXME("DRV_CONFIGURE: reverse lookup HACMDRIVER -> HACMDRIVERID not implemented\n");
413                 pAlias = NULL;
414             }
415         
416             if (pAlias != NULL) {
417                 unsigned int iStructSize = 16;
418                 /* This verification is required because DRVCONFIGINFO is 12 bytes
419                    long, yet native msacm reports a 16-byte structure to codecs.
420                  */
421                 if (iStructSize < sizeof(DRVCONFIGINFO)) iStructSize = sizeof(DRVCONFIGINFO);
422                 pConfigInfo = HeapAlloc(MSACM_hHeap, 0, iStructSize);
423                 if (!pConfigInfo) {
424                     ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
425                 } else {
426                     static const WCHAR drivers32[] = {'D','r','i','v','e','r','s','3','2','\0'};
427                     pConfigInfo->dwDCISize = iStructSize;
428                 
429                     pConfigInfo->lpszDCISectionName = HeapAlloc(MSACM_hHeap, 0, (strlenW(drivers32) + 1) * sizeof(WCHAR));
430                     if (pConfigInfo->lpszDCISectionName) strcpyW((WCHAR *)pConfigInfo->lpszDCISectionName, drivers32);
431                     pConfigInfo->lpszDCIAliasName = HeapAlloc(MSACM_hHeap, 0, (strlenW(pAlias) + 1) * sizeof(WCHAR));
432                     if (pConfigInfo->lpszDCIAliasName) strcpyW((WCHAR *)pConfigInfo->lpszDCIAliasName, pAlias);
433                     
434                     if (pConfigInfo->lpszDCISectionName == NULL || pConfigInfo->lpszDCIAliasName == NULL) {
435                         HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
436                         HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
437                         HeapFree(MSACM_hHeap, 0, pConfigInfo);
438                         pConfigInfo = NULL;
439                         ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
440                     }
441                 }
442             }
443             
444             lParam2 = (LPARAM)pConfigInfo;
445         }
446         
447         if (padid) {
448             /* Handle is really an HACMDRIVERID, must have an open session to get an HACMDRIVER */
449             if (padid->pACMDriverList != NULL) {
450                 lResult = MSACM_Message((HACMDRIVER)padid->pACMDriverList, uMsg, lParam1, lParam2);
451             } else {
452                 MMRESULT mmr = acmDriverOpen(&had, (HACMDRIVERID)padid, 0);
453                 if (mmr != MMSYSERR_NOERROR) {
454                     lResult = MMSYSERR_INVALPARAM;
455                 } else {
456                     lResult = acmDriverMessage(had, uMsg, lParam1, lParam2);
457                     acmDriverClose(had, 0);
458                 }
459             }
460         } else {
461             lResult = MSACM_Message(had, uMsg, lParam1, lParam2);
462         }
463         if (pConfigInfo) {
464             HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
465             HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
466             HeapFree(MSACM_hHeap, 0, pConfigInfo);
467         }
468         return lResult;
469     }
470     WARN("invalid parameter\n");
471     return MMSYSERR_INVALPARAM;
472 }
473
474 /***********************************************************************
475  *           acmDriverOpen (MSACM32.@)
476  */
477 MMRESULT WINAPI acmDriverOpen(PHACMDRIVER phad, HACMDRIVERID hadid, DWORD fdwOpen)
478 {
479     PWINE_ACMDRIVERID   padid;
480     PWINE_ACMDRIVER     pad = NULL;
481     MMRESULT            ret;
482
483     TRACE("(%p, %p, %08lu)\n", phad, hadid, fdwOpen);
484
485     if (!phad) {
486         WARN("invalid parameter\n");
487         return MMSYSERR_INVALPARAM;
488     }
489
490     if (fdwOpen) {
491         WARN("invalid flag\n");
492         return MMSYSERR_INVALFLAG;
493     }
494
495     padid = MSACM_GetDriverID(hadid);
496     if (!padid) {
497         WARN("invalid handle\n");
498         return MMSYSERR_INVALHANDLE;
499     }
500
501     pad = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVER));
502     if (!pad) {
503         WARN("no memory\n");
504         return MMSYSERR_NOMEM;
505     }
506
507     pad->obj.dwType = WINE_ACMOBJ_DRIVER;
508     pad->obj.pACMDriverID = padid;
509
510     if (!(pad->hDrvr = (HDRVR)padid->hInstModule))
511     {
512         ACMDRVOPENDESCW adod;
513         int             len;
514
515         /* this is not an externally added driver... need to actually load it */
516         if (!padid->pszDriverAlias)
517         {
518             ret = MMSYSERR_ERROR;
519             goto gotError;
520         }
521
522         adod.cbStruct = sizeof(adod);
523         adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
524         adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
525         adod.dwVersion = acmGetVersion();
526         adod.dwFlags = fdwOpen;
527         adod.dwError = 0;
528         len = strlen("Drivers32") + 1;
529         adod.pszSectionName = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
530         MultiByteToWideChar(CP_ACP, 0, "Drivers32", -1, (LPWSTR)adod.pszSectionName, len);
531         adod.pszAliasName = padid->pszDriverAlias;
532         adod.dnDevNode = 0;
533
534         pad->hDrvr = OpenDriver(padid->pszDriverAlias, NULL, (DWORD)&adod);
535
536         HeapFree(MSACM_hHeap, 0, (LPWSTR)adod.pszSectionName);
537         if (!pad->hDrvr)
538         {
539             ret = adod.dwError;
540             goto gotError;
541         }
542     }
543
544     /* insert new pad at beg of list */
545     pad->pNextACMDriver = padid->pACMDriverList;
546     padid->pACMDriverList = pad;
547
548     /* FIXME: Create a WINE_ACMDRIVER32 */
549     *phad = (HACMDRIVER)pad;
550     TRACE("'%s' => %p\n", debugstr_w(padid->pszDriverAlias), pad);
551
552     return MMSYSERR_NOERROR;
553  gotError:
554     WARN("failed: ret = %08x\n", ret);
555     if (pad && !pad->hDrvr)
556         HeapFree(MSACM_hHeap, 0, pad);
557     return ret;
558 }
559
560 /***********************************************************************
561  *           acmDriverPriority (MSACM32.@)
562  */
563 MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority)
564 {
565
566     TRACE("(%p, %08lx, %08lx)\n", hadid, dwPriority, fdwPriority);
567
568     /* Check for unknown flags */
569     if (fdwPriority &
570         ~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE|
571           ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
572         WARN("invalid flag\n");
573         return MMSYSERR_INVALFLAG;
574     }
575
576     /* Check for incompatible flags */
577     if ((fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) &&
578         (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE)) {
579         WARN("invalid flag\n");
580         return MMSYSERR_INVALFLAG;
581     }
582
583     /* Check for incompatible flags */
584     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) &&
585         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
586         WARN("invalid flag\n");
587         return MMSYSERR_INVALFLAG;
588     }
589     
590     /* According to MSDN, ACM_DRIVERPRIORITYF_BEGIN and ACM_DRIVERPRIORITYF_END 
591        may only appear by themselves, and in addition, hadid and dwPriority must
592        both be zero */
593     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) ||
594         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
595         if (fdwPriority & ~(ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
596             WARN("ACM_DRIVERPRIORITYF_[BEGIN|END] cannot be used with any other flags\n");
597             return MMSYSERR_INVALPARAM;
598         }
599         if (dwPriority) {
600             WARN("priority invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
601             return MMSYSERR_INVALPARAM;
602         }
603         if (hadid) {
604             WARN("non-null hadid invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
605             return MMSYSERR_INVALPARAM;
606         }
607         /* FIXME: MSDN wording suggests that deferred notification should be 
608            implemented as a system-wide lock held by a calling task, and that 
609            re-enabling notifications should broadcast them across all processes.
610            This implementation uses a simple DWORD counter. One consequence of the
611            current implementation is that applications will never see 
612            MMSYSERR_ALLOCATED as a return error.
613          */
614         if (fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) {
615             MSACM_DisableNotifications();
616         } else if (fdwPriority & ACM_DRIVERPRIORITYF_END) {
617             MSACM_EnableNotifications();
618         }
619         return MMSYSERR_NOERROR;
620     } else {
621         PWINE_ACMDRIVERID padid;
622         PWINE_ACMNOTIFYWND panwnd;
623         BOOL bPerformBroadcast = FALSE;
624
625         /* Fetch driver ID */
626         padid = MSACM_GetDriverID(hadid);
627         panwnd = MSACM_GetNotifyWnd(hadid);
628         if (!padid && !panwnd) {
629             WARN("invalid handle\n");
630             return MMSYSERR_INVALHANDLE;
631         }
632         
633         if (padid) {
634             /* Check whether driver ID is appropriate for requested op */
635             if (dwPriority) {
636                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL) {
637                     return MMSYSERR_NOTSUPPORTED;
638                 }
639                 if (dwPriority != 1 && dwPriority != -1) {
640                     FIXME("unexpected priority %ld, using sign only\n", dwPriority);
641                     if (dwPriority < 0) dwPriority = -1;
642                     if (dwPriority > 0) dwPriority = 1;
643                 }
644                 
645                 if (dwPriority == 1 && (padid->pPrevACMDriverID == NULL || 
646                     (padid->pPrevACMDriverID->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL))) {
647                     /* do nothing - driver is first of list, or first after last
648                        local driver */
649                 } else if (dwPriority == -1 && padid->pNextACMDriverID == NULL) {
650                     /* do nothing - driver is last of list */
651                 } else {
652                     MSACM_RePositionDriver(padid, dwPriority);
653                     bPerformBroadcast = TRUE;
654                 }
655             }
656
657             /* Check whether driver ID should be enabled or disabled */
658             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
659                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
660                     padid->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
661                     bPerformBroadcast = TRUE;
662                 }
663             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
664                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
665                     padid->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
666                     bPerformBroadcast = TRUE;
667                 }
668             }
669         }
670         
671         if (panwnd) {
672             if (dwPriority) {
673                 return MMSYSERR_NOTSUPPORTED;
674             }
675         
676             /* Check whether notify window should be enabled or disabled */
677             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
678                 if (!(panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
679                     panwnd->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
680                     bPerformBroadcast = TRUE;
681                 }
682             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
683                 if (panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
684                     panwnd->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
685                     bPerformBroadcast = TRUE;
686                 }
687             }
688         }
689         
690         /* Perform broadcast of changes */
691         if (bPerformBroadcast) {
692             MSACM_WriteCurrentPriorities();
693             MSACM_BroadcastNotification();
694         }
695         return MMSYSERR_NOERROR;
696     }
697 }
698
699 /***********************************************************************
700  *           acmDriverRemove (MSACM32.@)
701  */
702 MMRESULT WINAPI acmDriverRemove(HACMDRIVERID hadid, DWORD fdwRemove)
703 {
704     PWINE_ACMDRIVERID padid;
705     PWINE_ACMNOTIFYWND panwnd;
706
707     TRACE("(%p, %08lx)\n", hadid, fdwRemove);
708
709     padid = MSACM_GetDriverID(hadid);
710     panwnd = MSACM_GetNotifyWnd(hadid);
711     if (!padid && !panwnd) {
712         WARN("invalid handle\n");
713         return MMSYSERR_INVALHANDLE;
714     }
715
716     if (fdwRemove) {
717         WARN("invalid flag\n");
718         return MMSYSERR_INVALFLAG;
719     }
720
721     if (padid) MSACM_UnregisterDriver(padid);
722     if (panwnd) MSACM_UnRegisterNotificationWindow(panwnd);
723     MSACM_BroadcastNotification();
724
725     return MMSYSERR_NOERROR;
726 }