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