winecoreaudio: Initial implementation of widGetDevCaps.
[wine] / dlls / msacm32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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, %08x, %08x)\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     PWINE_ACMLOCALDRIVER pLocalDrv = NULL;
105
106     TRACE("(%p, %p, %08lx, %08x, %08x)\n",
107           phadid, hinstModule, lParam, dwPriority, fdwAdd);
108
109     if (!phadid) {
110         WARN("invalid parameter\n");
111         return MMSYSERR_INVALPARAM;
112     }
113
114     /* Check if any unknown flags */
115     if (fdwAdd &
116         ~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND|
117           ACM_DRIVERADDF_GLOBAL)) {
118         WARN("invalid flag\n");
119         return MMSYSERR_INVALFLAG;
120     }
121  
122     /* Check if any incompatible flags */
123     if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) &&
124         (fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) {
125         WARN("invalid flag\n");
126         return MMSYSERR_INVALFLAG;
127     }
128
129     switch (fdwAdd & ACM_DRIVERADDF_TYPEMASK) {
130     case ACM_DRIVERADDF_NAME:
131         /*
132                 hInstModule     (unused)
133                 lParam          name of value in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32
134                 dwPriority      (unused, set to 0)
135          */
136         *phadid = (HACMDRIVERID) MSACM_RegisterDriverFromRegistry((LPCWSTR)lParam);        
137         if (!*phadid) {
138             ERR("Unable to register driver via ACM_DRIVERADDF_NAME\n");
139             return MMSYSERR_INVALPARAM;
140         }
141         break;
142     case ACM_DRIVERADDF_FUNCTION:
143         /*
144                 hInstModule     Handle of module which contains driver entry proc
145                 lParam          Driver function address
146                 dwPriority      (unused, set to 0)
147          */
148         fdwAdd &= ~ACM_DRIVERADDF_TYPEMASK;
149         /* FIXME: fdwAdd ignored */
150         /* Application-supplied acmDriverProc's are placed at the top of the priority unless
151            fdwAdd indicates ACM_DRIVERADDF_GLOBAL
152          */
153         pLocalDrv = MSACM_RegisterLocalDriver(hinstModule, (DRIVERPROC)lParam);
154         *phadid = pLocalDrv ? (HACMDRIVERID) MSACM_RegisterDriver(NULL, NULL, pLocalDrv) : NULL;
155         if (!*phadid) {
156             ERR("Unable to register driver via ACM_DRIVERADDF_FUNCTION\n");
157             return MMSYSERR_INVALPARAM;
158         }
159         break;
160     case ACM_DRIVERADDF_NOTIFYHWND:
161         /*
162                 hInstModule     (unused)
163                 lParam          Handle of notification window
164                 dwPriority      Window message to send for notification broadcasts
165          */
166         *phadid = (HACMDRIVERID) MSACM_RegisterNotificationWindow((HWND)lParam, dwPriority);
167         if (!*phadid) {
168             ERR("Unable to register driver via ACM_DRIVERADDF_NOTIFYHWND\n");
169             return MMSYSERR_INVALPARAM;
170         }
171         break;
172     default:
173         ERR("invalid flag value 0x%08lx for fdwAdd\n", fdwAdd & ACM_DRIVERADDF_TYPEMASK);
174         return MMSYSERR_INVALFLAG;
175     }
176
177     MSACM_BroadcastNotification();
178     return MMSYSERR_NOERROR;
179 }
180
181 /***********************************************************************
182  *           acmDriverClose (MSACM32.@)
183  */
184 MMRESULT WINAPI acmDriverClose(HACMDRIVER had, DWORD fdwClose)
185 {
186     PWINE_ACMDRIVER     pad;
187     PWINE_ACMDRIVERID   padid;
188     PWINE_ACMDRIVER*    tpad;
189
190     TRACE("(%p, %08x)\n", had, fdwClose);
191
192     if (fdwClose) {
193         WARN("invalid flag\n");
194         return MMSYSERR_INVALFLAG;
195     }
196
197     pad = MSACM_GetDriver(had);
198     if (!pad) {
199         WARN("invalid handle\n");
200         return MMSYSERR_INVALHANDLE;
201     }
202
203     padid = pad->obj.pACMDriverID;
204
205     /* remove driver from list */
206     for (tpad = &(padid->pACMDriverList); *tpad; tpad = &((*tpad)->pNextACMDriver)) {
207         if (*tpad == pad) {
208             *tpad = (*tpad)->pNextACMDriver;
209             break;
210         }
211     }
212
213     /* close driver if it has been opened */
214     if (pad->hDrvr && !pad->pLocalDrvrInst)
215         CloseDriver(pad->hDrvr, 0, 0);
216     else if (pad->pLocalDrvrInst)
217         MSACM_CloseLocalDriver(pad->pLocalDrvrInst);
218
219     HeapFree(MSACM_hHeap, 0, pad);
220
221     return MMSYSERR_NOERROR;
222 }
223
224 /***********************************************************************
225  *           acmDriverDetailsA (MSACM32.@)
226  */
227 MMRESULT WINAPI acmDriverDetailsA(HACMDRIVERID hadid, PACMDRIVERDETAILSA padd, DWORD fdwDetails)
228 {
229     MMRESULT mmr;
230     ACMDRIVERDETAILSW   addw;
231
232     TRACE("(%p, %p, %08x)\n", hadid, padd, fdwDetails);
233
234     if (!padd) {
235         WARN("invalid parameter\n");
236         return MMSYSERR_INVALPARAM;
237     }
238
239     if (padd->cbStruct < 4) {
240         WARN("invalid parameter\n");
241         return MMSYSERR_INVALPARAM;
242     }
243
244     addw.cbStruct = sizeof(addw);
245     mmr = acmDriverDetailsW(hadid, &addw, fdwDetails);
246     if (mmr == 0) {
247         ACMDRIVERDETAILSA padda;
248
249         padda.fccType = addw.fccType;
250         padda.fccComp = addw.fccComp;
251         padda.wMid = addw.wMid;
252         padda.wPid = addw.wPid;
253         padda.vdwACM = addw.vdwACM;
254         padda.vdwDriver = addw.vdwDriver;
255         padda.fdwSupport = addw.fdwSupport;
256         padda.cFormatTags = addw.cFormatTags;
257         padda.cFilterTags = addw.cFilterTags;
258         padda.hicon = addw.hicon;
259         WideCharToMultiByte( CP_ACP, 0, addw.szShortName, -1, padda.szShortName,
260                              sizeof(padda.szShortName), NULL, NULL );
261         WideCharToMultiByte( CP_ACP, 0, addw.szLongName, -1, padda.szLongName,
262                              sizeof(padda.szLongName), NULL, NULL );
263         WideCharToMultiByte( CP_ACP, 0, addw.szCopyright, -1, padda.szCopyright,
264                              sizeof(padda.szCopyright), NULL, NULL );
265         WideCharToMultiByte( CP_ACP, 0, addw.szLicensing, -1, padda.szLicensing,
266                              sizeof(padda.szLicensing), NULL, NULL );
267         WideCharToMultiByte( CP_ACP, 0, addw.szFeatures, -1, padda.szFeatures,
268                              sizeof(padda.szFeatures), NULL, NULL );
269         padda.cbStruct = min(padd->cbStruct, sizeof(*padd));
270         memcpy(padd, &padda, padda.cbStruct);
271     }
272     return mmr;
273 }
274
275 /***********************************************************************
276  *           acmDriverDetailsW (MSACM32.@)
277  */
278 MMRESULT WINAPI acmDriverDetailsW(HACMDRIVERID hadid, PACMDRIVERDETAILSW padd, DWORD fdwDetails)
279 {
280     HACMDRIVER acmDrvr;
281     MMRESULT mmr;
282
283     TRACE("(%p, %p, %08x)\n", hadid, padd, fdwDetails);
284
285     if (!padd) {
286         WARN("invalid parameter\n");
287         return MMSYSERR_INVALPARAM;
288     }
289
290     if (padd->cbStruct < 4) {
291         WARN("invalid parameter\n");
292         return MMSYSERR_INVALPARAM;
293     }
294
295     if (fdwDetails) {
296         WARN("invalid flag\n");
297         return MMSYSERR_INVALFLAG;
298     }
299
300     mmr = acmDriverOpen(&acmDrvr, hadid, 0);
301     if (mmr == MMSYSERR_NOERROR) {
302         ACMDRIVERDETAILSW paddw;
303         paddw.cbStruct = sizeof(paddw);
304         mmr = (MMRESULT)MSACM_Message(acmDrvr, ACMDM_DRIVER_DETAILS, (LPARAM)&paddw,  0);
305
306         acmDriverClose(acmDrvr, 0);
307         paddw.cbStruct = min(padd->cbStruct, sizeof(*padd));
308         memcpy(padd, &paddw, paddw.cbStruct);
309     }
310
311     return mmr;
312 }
313
314 /***********************************************************************
315  *           acmDriverEnum (MSACM32.@)
316  */
317 MMRESULT WINAPI acmDriverEnum(ACMDRIVERENUMCB fnCallback, DWORD dwInstance, DWORD fdwEnum)
318 {
319     PWINE_ACMDRIVERID   padid;
320     DWORD               fdwSupport;
321
322     TRACE("(%p, %08x, %08x)\n", fnCallback, dwInstance, fdwEnum);
323
324     if (!fnCallback) {
325         WARN("invalid parameter\n");
326         return MMSYSERR_INVALPARAM;
327     }
328
329     if (fdwEnum & ~(ACM_DRIVERENUMF_NOLOCAL|ACM_DRIVERENUMF_DISABLED)) {
330         WARN("invalid flag\n");
331         return MMSYSERR_INVALFLAG;
332     }
333
334     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
335         fdwSupport = padid->fdwSupport;
336
337         if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
338             if (fdwEnum & ACM_DRIVERENUMF_DISABLED)
339                 fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
340             else
341                 continue;
342         }
343         if (!(*fnCallback)((HACMDRIVERID)padid, dwInstance, fdwSupport))
344             break;
345     }
346
347     return MMSYSERR_NOERROR;
348 }
349
350 /***********************************************************************
351  *           acmDriverID (MSACM32.@)
352  */
353 MMRESULT WINAPI acmDriverID(HACMOBJ hao, PHACMDRIVERID phadid, DWORD fdwDriverID)
354 {
355     PWINE_ACMOBJ pao;
356
357     TRACE("(%p, %p, %08x)\n", hao, phadid, fdwDriverID);
358
359     if (fdwDriverID) {
360         WARN("invalid flag\n");
361         return MMSYSERR_INVALFLAG;
362     }
363
364     pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE);
365     if (!pao) {
366         WARN("invalid handle\n");
367         return MMSYSERR_INVALHANDLE;
368     }
369
370     if (!phadid) {
371         WARN("invalid parameter\n");
372         return MMSYSERR_INVALPARAM;
373     }
374
375     *phadid = (HACMDRIVERID) pao->pACMDriverID;
376
377     return MMSYSERR_NOERROR;
378 }
379
380 /***********************************************************************
381  *           acmDriverMessage (MSACM32.@)
382  *
383  * Note: MSDN documentation (July 2001) is incomplete. This function
384  * accepts sending messages to an HACMDRIVERID in addition to the
385  * documented HACMDRIVER. In fact, for DRV_QUERYCONFIGURE and DRV_CONFIGURE, 
386  * this might actually be the required mode of operation.
387  *
388  * Note: For DRV_CONFIGURE, msacm supplies its own DRVCONFIGINFO structure
389  * when the application fails to supply one. Some native drivers depend on
390  * this and refuse to display unless a valid DRVCONFIGINFO structure is
391  * built and supplied.
392  */
393 LRESULT WINAPI acmDriverMessage(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
394 {
395     TRACE("(%p, %04x, %08lx, %08lx\n", had, uMsg, lParam1, lParam2);
396
397     if ((uMsg >= ACMDM_USER && uMsg < ACMDM_RESERVED_LOW) ||
398         uMsg == ACMDM_DRIVER_ABOUT ||
399         uMsg == DRV_QUERYCONFIGURE ||
400         uMsg == DRV_CONFIGURE)
401     {
402         PWINE_ACMDRIVERID padid;
403         LRESULT lResult;
404         LPDRVCONFIGINFO pConfigInfo = NULL;
405         LPWSTR section_name = NULL;
406         LPWSTR alias_name = NULL;
407
408         /* Check whether handle is an HACMDRIVERID */
409         padid  = MSACM_GetDriverID((HACMDRIVERID)had);
410         
411         /* If the message is DRV_CONFIGURE, and the application provides no
412            DRVCONFIGINFO structure, msacm must supply its own.
413          */
414         if (uMsg == DRV_CONFIGURE && lParam2 == 0) {
415             LPWSTR pAlias;
416             
417             /* Get the alias from the HACMDRIVERID */
418             if (padid) {
419                 pAlias = padid->pszDriverAlias;
420                 if (pAlias == NULL) {
421                     WARN("DRV_CONFIGURE: no alias for this driver, cannot self-supply alias\n");
422                 }
423             } else {
424                 FIXME("DRV_CONFIGURE: reverse lookup HACMDRIVER -> HACMDRIVERID not implemented\n");
425                 pAlias = NULL;
426             }
427         
428             if (pAlias != NULL) {
429                 unsigned int iStructSize = 16;
430                 /* This verification is required because DRVCONFIGINFO is 12 bytes
431                    long, yet native msacm reports a 16-byte structure to codecs.
432                  */
433                 if (iStructSize < sizeof(DRVCONFIGINFO)) iStructSize = sizeof(DRVCONFIGINFO);
434                 pConfigInfo = HeapAlloc(MSACM_hHeap, 0, iStructSize);
435                 if (!pConfigInfo) {
436                     ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
437                 } else {
438                     static const WCHAR drivers32[] = {'D','r','i','v','e','r','s','3','2','\0'};
439
440                     pConfigInfo->dwDCISize = iStructSize;
441
442                     section_name = HeapAlloc(MSACM_hHeap, 0, (strlenW(drivers32) + 1) * sizeof(WCHAR));
443                     if (section_name) strcpyW(section_name, drivers32);
444                     pConfigInfo->lpszDCISectionName = section_name;
445                     alias_name = HeapAlloc(MSACM_hHeap, 0, (strlenW(pAlias) + 1) * sizeof(WCHAR));
446                     if (alias_name) strcpyW(alias_name, pAlias);
447                     pConfigInfo->lpszDCIAliasName = alias_name;
448
449                     if (pConfigInfo->lpszDCISectionName == NULL || pConfigInfo->lpszDCIAliasName == NULL) {
450                         HeapFree(MSACM_hHeap, 0, alias_name);
451                         HeapFree(MSACM_hHeap, 0, section_name);
452                         HeapFree(MSACM_hHeap, 0, pConfigInfo);
453                         pConfigInfo = NULL;
454                         ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
455                     }
456                 }
457             }
458             
459             lParam2 = (LPARAM)pConfigInfo;
460         }
461         
462         if (padid) {
463             /* Handle is really an HACMDRIVERID, must have an open session to get an HACMDRIVER */
464             if (padid->pACMDriverList != NULL) {
465                 lResult = MSACM_Message((HACMDRIVER)padid->pACMDriverList, uMsg, lParam1, lParam2);
466             } else {
467                 MMRESULT mmr = acmDriverOpen(&had, (HACMDRIVERID)padid, 0);
468                 if (mmr != MMSYSERR_NOERROR) {
469                     lResult = MMSYSERR_INVALPARAM;
470                 } else {
471                     lResult = acmDriverMessage(had, uMsg, lParam1, lParam2);
472                     acmDriverClose(had, 0);
473                 }
474             }
475         } else {
476             lResult = MSACM_Message(had, uMsg, lParam1, lParam2);
477         }
478         if (pConfigInfo) {
479             HeapFree(MSACM_hHeap, 0, alias_name);
480             HeapFree(MSACM_hHeap, 0, section_name);
481             HeapFree(MSACM_hHeap, 0, pConfigInfo);
482         }
483         return lResult;
484     }
485     WARN("invalid parameter\n");
486     return MMSYSERR_INVALPARAM;
487 }
488
489 /***********************************************************************
490  *           acmDriverOpen (MSACM32.@)
491  */
492 MMRESULT WINAPI acmDriverOpen(PHACMDRIVER phad, HACMDRIVERID hadid, DWORD fdwOpen)
493 {
494     PWINE_ACMDRIVERID   padid;
495     PWINE_ACMDRIVER     pad = NULL;
496     MMRESULT            ret;
497
498     TRACE("(%p, %p, %08u)\n", phad, hadid, fdwOpen);
499
500     if (!phad) {
501         WARN("invalid parameter\n");
502         return MMSYSERR_INVALPARAM;
503     }
504
505     if (fdwOpen) {
506         WARN("invalid flag\n");
507         return MMSYSERR_INVALFLAG;
508     }
509
510     padid = MSACM_GetDriverID(hadid);
511     if (!padid) {
512         WARN("invalid handle\n");
513         return MMSYSERR_INVALHANDLE;
514     }
515
516     pad = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVER));
517     if (!pad) {
518         WARN("no memory\n");
519         return MMSYSERR_NOMEM;
520     }
521
522     pad->obj.dwType = WINE_ACMOBJ_DRIVER;
523     pad->obj.pACMDriverID = padid;
524     pad->hDrvr = 0;
525     pad->pLocalDrvrInst = NULL;
526
527     if (padid->pLocalDriver == NULL)
528     {
529         ACMDRVOPENDESCW adod;
530         int             len;
531         LPWSTR          section_name;
532
533         /* this is not an externally added driver... need to actually load it */
534         if (!padid->pszDriverAlias)
535         {
536             ret = MMSYSERR_ERROR;
537             goto gotError;
538         }
539
540         adod.cbStruct = sizeof(adod);
541         adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
542         adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
543         adod.dwVersion = acmGetVersion();
544         adod.dwFlags = fdwOpen;
545         adod.dwError = 0;
546         len = strlen("Drivers32") + 1;
547         section_name = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
548         MultiByteToWideChar(CP_ACP, 0, "Drivers32", -1, section_name, len);
549         adod.pszSectionName = section_name;
550         adod.pszAliasName = padid->pszDriverAlias;
551         adod.dnDevNode = 0;
552
553         pad->hDrvr = OpenDriver(padid->pszDriverAlias, NULL, (DWORD)&adod);
554
555         HeapFree(MSACM_hHeap, 0, section_name);
556         if (!pad->hDrvr)
557         {
558             ret = adod.dwError;
559             goto gotError;
560         }
561     }
562     else
563     {
564         ACMDRVOPENDESCW adod;
565
566         pad->hDrvr = NULL;
567
568         adod.cbStruct = sizeof(adod);
569         adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
570         adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
571         adod.dwVersion = acmGetVersion();
572         adod.dwFlags = fdwOpen;
573         adod.dwError = 0;
574         adod.pszSectionName = NULL;
575         adod.pszAliasName = NULL;
576         adod.dnDevNode = 0;
577
578         pad->pLocalDrvrInst = MSACM_OpenLocalDriver(padid->pLocalDriver, (DWORD)&adod);
579         if (!pad->pLocalDrvrInst)
580         {
581             ret = adod.dwError;
582             goto gotError;
583         }
584     }
585
586     /* insert new pad at beg of list */
587     pad->pNextACMDriver = padid->pACMDriverList;
588     padid->pACMDriverList = pad;
589
590     /* FIXME: Create a WINE_ACMDRIVER32 */
591     *phad = (HACMDRIVER)pad;
592     TRACE("'%s' => %p\n", debugstr_w(padid->pszDriverAlias), pad);
593
594     return MMSYSERR_NOERROR;
595  gotError:
596     WARN("failed: ret = %08x\n", ret);
597     if (pad && !pad->hDrvr)
598         HeapFree(MSACM_hHeap, 0, pad);
599     return ret;
600 }
601
602 /***********************************************************************
603  *           acmDriverPriority (MSACM32.@)
604  */
605 MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority)
606 {
607
608     TRACE("(%p, %08x, %08x)\n", hadid, dwPriority, fdwPriority);
609
610     /* Check for unknown flags */
611     if (fdwPriority &
612         ~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE|
613           ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
614         WARN("invalid flag\n");
615         return MMSYSERR_INVALFLAG;
616     }
617
618     /* Check for incompatible flags */
619     if ((fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) &&
620         (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE)) {
621         WARN("invalid flag\n");
622         return MMSYSERR_INVALFLAG;
623     }
624
625     /* Check for incompatible flags */
626     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) &&
627         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
628         WARN("invalid flag\n");
629         return MMSYSERR_INVALFLAG;
630     }
631     
632     /* According to MSDN, ACM_DRIVERPRIORITYF_BEGIN and ACM_DRIVERPRIORITYF_END 
633        may only appear by themselves, and in addition, hadid and dwPriority must
634        both be zero */
635     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) ||
636         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
637         if (fdwPriority & ~(ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
638             WARN("ACM_DRIVERPRIORITYF_[BEGIN|END] cannot be used with any other flags\n");
639             return MMSYSERR_INVALPARAM;
640         }
641         if (dwPriority) {
642             WARN("priority invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
643             return MMSYSERR_INVALPARAM;
644         }
645         if (hadid) {
646             WARN("non-null hadid invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
647             return MMSYSERR_INVALPARAM;
648         }
649         /* FIXME: MSDN wording suggests that deferred notification should be 
650            implemented as a system-wide lock held by a calling task, and that 
651            re-enabling notifications should broadcast them across all processes.
652            This implementation uses a simple DWORD counter. One consequence of the
653            current implementation is that applications will never see 
654            MMSYSERR_ALLOCATED as a return error.
655          */
656         if (fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) {
657             MSACM_DisableNotifications();
658         } else if (fdwPriority & ACM_DRIVERPRIORITYF_END) {
659             MSACM_EnableNotifications();
660         }
661         return MMSYSERR_NOERROR;
662     } else {
663         PWINE_ACMDRIVERID padid;
664         PWINE_ACMNOTIFYWND panwnd;
665         BOOL bPerformBroadcast = FALSE;
666
667         /* Fetch driver ID */
668         padid = MSACM_GetDriverID(hadid);
669         panwnd = MSACM_GetNotifyWnd(hadid);
670         if (!padid && !panwnd) {
671             WARN("invalid handle\n");
672             return MMSYSERR_INVALHANDLE;
673         }
674         
675         if (padid) {
676             /* Check whether driver ID is appropriate for requested op */
677             if (dwPriority) {
678                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL) {
679                     return MMSYSERR_NOTSUPPORTED;
680                 }
681                 if (dwPriority != 1 && dwPriority != (DWORD)-1) {
682                     FIXME("unexpected priority %d, using sign only\n", dwPriority);
683                     if ((signed)dwPriority < 0) dwPriority = (DWORD)-1;
684                     if (dwPriority > 0) dwPriority = 1;
685                 }
686                 
687                 if (dwPriority == 1 && (padid->pPrevACMDriverID == NULL || 
688                     (padid->pPrevACMDriverID->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL))) {
689                     /* do nothing - driver is first of list, or first after last
690                        local driver */
691                 } else if (dwPriority == (DWORD)-1 && padid->pNextACMDriverID == NULL) {
692                     /* do nothing - driver is last of list */
693                 } else {
694                     MSACM_RePositionDriver(padid, dwPriority);
695                     bPerformBroadcast = TRUE;
696                 }
697             }
698
699             /* Check whether driver ID should be enabled or disabled */
700             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
701                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
702                     padid->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
703                     bPerformBroadcast = TRUE;
704                 }
705             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
706                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
707                     padid->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
708                     bPerformBroadcast = TRUE;
709                 }
710             }
711         }
712         
713         if (panwnd) {
714             if (dwPriority) {
715                 return MMSYSERR_NOTSUPPORTED;
716             }
717         
718             /* Check whether notify window should be enabled or disabled */
719             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
720                 if (!(panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
721                     panwnd->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
722                     bPerformBroadcast = TRUE;
723                 }
724             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
725                 if (panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
726                     panwnd->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
727                     bPerformBroadcast = TRUE;
728                 }
729             }
730         }
731         
732         /* Perform broadcast of changes */
733         if (bPerformBroadcast) {
734             MSACM_WriteCurrentPriorities();
735             MSACM_BroadcastNotification();
736         }
737         return MMSYSERR_NOERROR;
738     }
739 }
740
741 /***********************************************************************
742  *           acmDriverRemove (MSACM32.@)
743  */
744 MMRESULT WINAPI acmDriverRemove(HACMDRIVERID hadid, DWORD fdwRemove)
745 {
746     PWINE_ACMDRIVERID padid;
747     PWINE_ACMNOTIFYWND panwnd;
748
749     TRACE("(%p, %08x)\n", hadid, fdwRemove);
750
751     padid = MSACM_GetDriverID(hadid);
752     panwnd = MSACM_GetNotifyWnd(hadid);
753     if (!padid && !panwnd) {
754         WARN("invalid handle\n");
755         return MMSYSERR_INVALHANDLE;
756     }
757
758     if (fdwRemove) {
759         WARN("invalid flag\n");
760         return MMSYSERR_INVALFLAG;
761     }
762
763     if (padid) MSACM_UnregisterDriver(padid);
764     if (panwnd) MSACM_UnRegisterNotificationWindow(panwnd);
765     MSACM_BroadcastNotification();
766
767     return MMSYSERR_NOERROR;
768 }