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