Update the address of the Free Software Foundation.
[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, %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     PWINE_ACMLOCALDRIVER pLocalDrv = NULL;
105
106     TRACE("(%p, %p, %08lx, %08lx, %08lx)\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, %08lx)\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, %08lx)\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, %08lx)\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, %08lx, %08lx)\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, %08lx)\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
406         /* Check whether handle is an HACMDRIVERID */
407         padid  = MSACM_GetDriverID((HACMDRIVERID)had);
408         
409         /* If the message is DRV_CONFIGURE, and the application provides no
410            DRVCONFIGINFO structure, msacm must supply its own.
411          */
412         if (uMsg == DRV_CONFIGURE && lParam2 == 0) {
413             LPWSTR pAlias;
414             
415             /* Get the alias from the HACMDRIVERID */
416             if (padid) {
417                 pAlias = padid->pszDriverAlias;
418                 if (pAlias == NULL) {
419                     WARN("DRV_CONFIGURE: no alias for this driver, cannot self-supply alias\n");
420                 }
421             } else {
422                 FIXME("DRV_CONFIGURE: reverse lookup HACMDRIVER -> HACMDRIVERID not implemented\n");
423                 pAlias = NULL;
424             }
425         
426             if (pAlias != NULL) {
427                 unsigned int iStructSize = 16;
428                 /* This verification is required because DRVCONFIGINFO is 12 bytes
429                    long, yet native msacm reports a 16-byte structure to codecs.
430                  */
431                 if (iStructSize < sizeof(DRVCONFIGINFO)) iStructSize = sizeof(DRVCONFIGINFO);
432                 pConfigInfo = HeapAlloc(MSACM_hHeap, 0, iStructSize);
433                 if (!pConfigInfo) {
434                     ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
435                 } else {
436                     static const WCHAR drivers32[] = {'D','r','i','v','e','r','s','3','2','\0'};
437                     pConfigInfo->dwDCISize = iStructSize;
438                 
439                     pConfigInfo->lpszDCISectionName = HeapAlloc(MSACM_hHeap, 0, (strlenW(drivers32) + 1) * sizeof(WCHAR));
440                     if (pConfigInfo->lpszDCISectionName) strcpyW((WCHAR *)pConfigInfo->lpszDCISectionName, drivers32);
441                     pConfigInfo->lpszDCIAliasName = HeapAlloc(MSACM_hHeap, 0, (strlenW(pAlias) + 1) * sizeof(WCHAR));
442                     if (pConfigInfo->lpszDCIAliasName) strcpyW((WCHAR *)pConfigInfo->lpszDCIAliasName, pAlias);
443                     
444                     if (pConfigInfo->lpszDCISectionName == NULL || pConfigInfo->lpszDCIAliasName == NULL) {
445                         HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
446                         HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
447                         HeapFree(MSACM_hHeap, 0, pConfigInfo);
448                         pConfigInfo = NULL;
449                         ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
450                     }
451                 }
452             }
453             
454             lParam2 = (LPARAM)pConfigInfo;
455         }
456         
457         if (padid) {
458             /* Handle is really an HACMDRIVERID, must have an open session to get an HACMDRIVER */
459             if (padid->pACMDriverList != NULL) {
460                 lResult = MSACM_Message((HACMDRIVER)padid->pACMDriverList, uMsg, lParam1, lParam2);
461             } else {
462                 MMRESULT mmr = acmDriverOpen(&had, (HACMDRIVERID)padid, 0);
463                 if (mmr != MMSYSERR_NOERROR) {
464                     lResult = MMSYSERR_INVALPARAM;
465                 } else {
466                     lResult = acmDriverMessage(had, uMsg, lParam1, lParam2);
467                     acmDriverClose(had, 0);
468                 }
469             }
470         } else {
471             lResult = MSACM_Message(had, uMsg, lParam1, lParam2);
472         }
473         if (pConfigInfo) {
474             HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
475             HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
476             HeapFree(MSACM_hHeap, 0, pConfigInfo);
477         }
478         return lResult;
479     }
480     WARN("invalid parameter\n");
481     return MMSYSERR_INVALPARAM;
482 }
483
484 /***********************************************************************
485  *           acmDriverOpen (MSACM32.@)
486  */
487 MMRESULT WINAPI acmDriverOpen(PHACMDRIVER phad, HACMDRIVERID hadid, DWORD fdwOpen)
488 {
489     PWINE_ACMDRIVERID   padid;
490     PWINE_ACMDRIVER     pad = NULL;
491     MMRESULT            ret;
492
493     TRACE("(%p, %p, %08lu)\n", phad, hadid, fdwOpen);
494
495     if (!phad) {
496         WARN("invalid parameter\n");
497         return MMSYSERR_INVALPARAM;
498     }
499
500     if (fdwOpen) {
501         WARN("invalid flag\n");
502         return MMSYSERR_INVALFLAG;
503     }
504
505     padid = MSACM_GetDriverID(hadid);
506     if (!padid) {
507         WARN("invalid handle\n");
508         return MMSYSERR_INVALHANDLE;
509     }
510
511     pad = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVER));
512     if (!pad) {
513         WARN("no memory\n");
514         return MMSYSERR_NOMEM;
515     }
516
517     pad->obj.dwType = WINE_ACMOBJ_DRIVER;
518     pad->obj.pACMDriverID = padid;
519     pad->hDrvr = 0;
520     pad->pLocalDrvrInst = NULL;
521
522     if (padid->pLocalDriver == NULL)
523     {
524         ACMDRVOPENDESCW adod;
525         int             len;
526
527         /* this is not an externally added driver... need to actually load it */
528         if (!padid->pszDriverAlias)
529         {
530             ret = MMSYSERR_ERROR;
531             goto gotError;
532         }
533
534         adod.cbStruct = sizeof(adod);
535         adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
536         adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
537         adod.dwVersion = acmGetVersion();
538         adod.dwFlags = fdwOpen;
539         adod.dwError = 0;
540         len = strlen("Drivers32") + 1;
541         adod.pszSectionName = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
542         MultiByteToWideChar(CP_ACP, 0, "Drivers32", -1, (LPWSTR)adod.pszSectionName, len);
543         adod.pszAliasName = padid->pszDriverAlias;
544         adod.dnDevNode = 0;
545
546         pad->hDrvr = OpenDriver(padid->pszDriverAlias, NULL, (DWORD)&adod);
547
548         HeapFree(MSACM_hHeap, 0, (LPWSTR)adod.pszSectionName);
549         if (!pad->hDrvr)
550         {
551             ret = adod.dwError;
552             goto gotError;
553         }
554     }
555     else
556     {
557         ACMDRVOPENDESCW adod;
558
559         pad->hDrvr = NULL;
560
561         adod.cbStruct = sizeof(adod);
562         adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
563         adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
564         adod.dwVersion = acmGetVersion();
565         adod.dwFlags = fdwOpen;
566         adod.dwError = 0;
567         adod.pszSectionName = NULL;
568         adod.pszAliasName = NULL;
569         adod.dnDevNode = 0;
570
571         pad->pLocalDrvrInst = MSACM_OpenLocalDriver(padid->pLocalDriver, (DWORD)&adod);
572         if (!pad->pLocalDrvrInst)
573         {
574             ret = adod.dwError;
575             goto gotError;
576         }
577     }
578
579     /* insert new pad at beg of list */
580     pad->pNextACMDriver = padid->pACMDriverList;
581     padid->pACMDriverList = pad;
582
583     /* FIXME: Create a WINE_ACMDRIVER32 */
584     *phad = (HACMDRIVER)pad;
585     TRACE("'%s' => %p\n", debugstr_w(padid->pszDriverAlias), pad);
586
587     return MMSYSERR_NOERROR;
588  gotError:
589     WARN("failed: ret = %08x\n", ret);
590     if (pad && !pad->hDrvr)
591         HeapFree(MSACM_hHeap, 0, pad);
592     return ret;
593 }
594
595 /***********************************************************************
596  *           acmDriverPriority (MSACM32.@)
597  */
598 MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority)
599 {
600
601     TRACE("(%p, %08lx, %08lx)\n", hadid, dwPriority, fdwPriority);
602
603     /* Check for unknown flags */
604     if (fdwPriority &
605         ~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE|
606           ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
607         WARN("invalid flag\n");
608         return MMSYSERR_INVALFLAG;
609     }
610
611     /* Check for incompatible flags */
612     if ((fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) &&
613         (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE)) {
614         WARN("invalid flag\n");
615         return MMSYSERR_INVALFLAG;
616     }
617
618     /* Check for incompatible flags */
619     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) &&
620         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
621         WARN("invalid flag\n");
622         return MMSYSERR_INVALFLAG;
623     }
624     
625     /* According to MSDN, ACM_DRIVERPRIORITYF_BEGIN and ACM_DRIVERPRIORITYF_END 
626        may only appear by themselves, and in addition, hadid and dwPriority must
627        both be zero */
628     if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) ||
629         (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
630         if (fdwPriority & ~(ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
631             WARN("ACM_DRIVERPRIORITYF_[BEGIN|END] cannot be used with any other flags\n");
632             return MMSYSERR_INVALPARAM;
633         }
634         if (dwPriority) {
635             WARN("priority invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
636             return MMSYSERR_INVALPARAM;
637         }
638         if (hadid) {
639             WARN("non-null hadid invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
640             return MMSYSERR_INVALPARAM;
641         }
642         /* FIXME: MSDN wording suggests that deferred notification should be 
643            implemented as a system-wide lock held by a calling task, and that 
644            re-enabling notifications should broadcast them across all processes.
645            This implementation uses a simple DWORD counter. One consequence of the
646            current implementation is that applications will never see 
647            MMSYSERR_ALLOCATED as a return error.
648          */
649         if (fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) {
650             MSACM_DisableNotifications();
651         } else if (fdwPriority & ACM_DRIVERPRIORITYF_END) {
652             MSACM_EnableNotifications();
653         }
654         return MMSYSERR_NOERROR;
655     } else {
656         PWINE_ACMDRIVERID padid;
657         PWINE_ACMNOTIFYWND panwnd;
658         BOOL bPerformBroadcast = FALSE;
659
660         /* Fetch driver ID */
661         padid = MSACM_GetDriverID(hadid);
662         panwnd = MSACM_GetNotifyWnd(hadid);
663         if (!padid && !panwnd) {
664             WARN("invalid handle\n");
665             return MMSYSERR_INVALHANDLE;
666         }
667         
668         if (padid) {
669             /* Check whether driver ID is appropriate for requested op */
670             if (dwPriority) {
671                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL) {
672                     return MMSYSERR_NOTSUPPORTED;
673                 }
674                 if (dwPriority != 1 && dwPriority != -1) {
675                     FIXME("unexpected priority %ld, using sign only\n", dwPriority);
676                     if (dwPriority < 0) dwPriority = -1;
677                     if (dwPriority > 0) dwPriority = 1;
678                 }
679                 
680                 if (dwPriority == 1 && (padid->pPrevACMDriverID == NULL || 
681                     (padid->pPrevACMDriverID->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL))) {
682                     /* do nothing - driver is first of list, or first after last
683                        local driver */
684                 } else if (dwPriority == -1 && padid->pNextACMDriverID == NULL) {
685                     /* do nothing - driver is last of list */
686                 } else {
687                     MSACM_RePositionDriver(padid, dwPriority);
688                     bPerformBroadcast = TRUE;
689                 }
690             }
691
692             /* Check whether driver ID should be enabled or disabled */
693             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
694                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
695                     padid->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
696                     bPerformBroadcast = TRUE;
697                 }
698             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
699                 if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
700                     padid->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
701                     bPerformBroadcast = TRUE;
702                 }
703             }
704         }
705         
706         if (panwnd) {
707             if (dwPriority) {
708                 return MMSYSERR_NOTSUPPORTED;
709             }
710         
711             /* Check whether notify window should be enabled or disabled */
712             if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
713                 if (!(panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
714                     panwnd->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
715                     bPerformBroadcast = TRUE;
716                 }
717             } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
718                 if (panwnd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
719                     panwnd->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
720                     bPerformBroadcast = TRUE;
721                 }
722             }
723         }
724         
725         /* Perform broadcast of changes */
726         if (bPerformBroadcast) {
727             MSACM_WriteCurrentPriorities();
728             MSACM_BroadcastNotification();
729         }
730         return MMSYSERR_NOERROR;
731     }
732 }
733
734 /***********************************************************************
735  *           acmDriverRemove (MSACM32.@)
736  */
737 MMRESULT WINAPI acmDriverRemove(HACMDRIVERID hadid, DWORD fdwRemove)
738 {
739     PWINE_ACMDRIVERID padid;
740     PWINE_ACMNOTIFYWND panwnd;
741
742     TRACE("(%p, %08lx)\n", hadid, fdwRemove);
743
744     padid = MSACM_GetDriverID(hadid);
745     panwnd = MSACM_GetNotifyWnd(hadid);
746     if (!padid && !panwnd) {
747         WARN("invalid handle\n");
748         return MMSYSERR_INVALHANDLE;
749     }
750
751     if (fdwRemove) {
752         WARN("invalid flag\n");
753         return MMSYSERR_INVALFLAG;
754     }
755
756     if (padid) MSACM_UnregisterDriver(padid);
757     if (panwnd) MSACM_UnRegisterNotificationWindow(panwnd);
758     MSACM_BroadcastNotification();
759
760     return MMSYSERR_NOERROR;
761 }