kernel32: Remove superfluous heap reallocation calls in FormatMessageA/W.
[wine] / dlls / winmm / lolvldrv.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * MMSYSTEM low level drivers handling functions
5  *
6  * Copyright 1999 Eric Pouech
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winemm.h"
34 #include "wine/debug.h"
35 #include "wine/exception.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
38
39 /* Default set of drivers to be loaded */
40 #define WINE_DEFAULT_WINMM_DRIVER "alsa,oss,coreaudio,esd"
41
42 /* each known type of driver has an instance of this structure */
43 typedef struct tagWINE_LLTYPE {
44     /* those attributes depend on the specification of the type */
45     LPCSTR              typestr;        /* name (for debugging) */
46     /* those attributes reflect the loaded/current situation for the type */
47     UINT                wMaxId;         /* number of loaded devices (sum across all loaded drivers) */
48     LPWINE_MLD          lpMlds;         /* "static" mlds to access the part though device IDs */
49     int                 nMapper;        /* index to mapper */
50 } WINE_LLTYPE;
51
52 static WINE_LLTYPE llTypes[MMDRV_MAX] = {
53     { "Aux", 0, 0, -1 },
54     { "Mixer", 0, 0, -1 },
55     { "MidiIn", 0, 0, -1 },
56     { "MidiOut", 0, 0, -1 },
57     { "WaveIn", 0, 0, -1 },
58     { "WaveOut", 0, 0, -1 }
59 };
60
61 static int drivers_loaded, MMDrvsHi;
62 static WINE_MM_DRIVER   MMDrvs[8];
63 static LPWINE_MLD       MM_MLDrvs[40];
64 #define MAX_MM_MLDRVS   (sizeof(MM_MLDrvs) / sizeof(MM_MLDrvs[0]))
65
66 static void MMDRV_Init(void);
67
68 static void MMDRV_InitSingleType(UINT type) {
69     if (!drivers_loaded) {
70         drivers_loaded = 1;
71         MMDRV_Init();
72     }
73 }
74
75 /**************************************************************************
76  *                      MMDRV_GetNum                            [internal]
77  */
78 UINT    MMDRV_GetNum(UINT type)
79 {
80     TRACE("(%04x)\n", type);
81     assert(type < MMDRV_MAX);
82     MMDRV_InitSingleType(type);
83     return llTypes[type].wMaxId;
84 }
85
86 /**************************************************************************
87  *                              MMDRV_Message                   [internal]
88  */
89 DWORD  MMDRV_Message(LPWINE_MLD mld, UINT wMsg, DWORD_PTR dwParam1,
90                      DWORD_PTR dwParam2)
91 {
92     LPWINE_MM_DRIVER            lpDrv;
93     DWORD                       ret;
94     WINE_MM_DRIVER_PART*        part;
95     WINE_LLTYPE*                llType = &llTypes[mld->type];
96     int                         devID;
97
98     TRACE("(%s %u %u 0x%08lx 0x%08lx 0x%08lx)\n",
99           llTypes[mld->type].typestr, mld->uDeviceID, wMsg,
100           mld->dwDriverInstance, dwParam1, dwParam2);
101
102     if (mld->uDeviceID == (UINT16)-1) {
103         if (llType->nMapper == -1) {
104             WARN("uDev=-1 requested on non-mapped ll type %s\n",
105                  llTypes[mld->type].typestr);
106             return MMSYSERR_BADDEVICEID;
107         }
108         devID = -1;
109     } else {
110         if (mld->uDeviceID >= llType->wMaxId) {
111             WARN("uDev(%u) requested >= max (%d)\n", mld->uDeviceID, llType->wMaxId);
112             return MMSYSERR_BADDEVICEID;
113         }
114         devID = mld->uDeviceID;
115     }
116
117     lpDrv = &MMDrvs[mld->mmdIndex];
118     part = &lpDrv->parts[mld->type];
119
120     assert(part->fnMessage32);
121
122     TRACE("Calling message(dev=%u msg=%u usr=0x%08lx p1=0x%08lx p2=0x%08lx)\n",
123           mld->uDeviceID, wMsg, mld->dwDriverInstance, dwParam1, dwParam2);
124     ret = part->fnMessage32(mld->uDeviceID, wMsg, mld->dwDriverInstance, dwParam1, dwParam2);
125     TRACE("=> %s\n", WINMM_ErrorToString(ret));
126
127     return ret;
128 }
129
130 /**************************************************************************
131  *                              MMDRV_Alloc                     [internal]
132  */
133 LPWINE_MLD      MMDRV_Alloc(UINT size, UINT type, LPHANDLE hndl, DWORD* dwFlags,
134                             DWORD_PTR* dwCallback, DWORD_PTR* dwInstance)
135 {
136     LPWINE_MLD  mld;
137     UINT_PTR i;
138     TRACE("(%d, %04x, %p, %p, %p, %p)\n",
139           size, type, hndl, dwFlags, dwCallback, dwInstance);
140
141     mld = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
142     if (!mld)   return NULL;
143
144     /* find an empty slot in MM_MLDrvs table */
145     for (i = 0; i < MAX_MM_MLDRVS; i++) if (!MM_MLDrvs[i]) break;
146
147     if (i == MAX_MM_MLDRVS) {
148         /* the MM_MLDrvs table could be made growable in the future if needed */
149         ERR("Too many open drivers\n");
150         HeapFree(GetProcessHeap(), 0, mld);
151         return NULL;
152     }
153     MM_MLDrvs[i] = mld;
154     *hndl = (HANDLE)(i | 0x8000);
155
156     mld->type = type;
157     if ((UINT_PTR)*hndl < MMDRV_GetNum(type) || ((UINT_PTR)*hndl >> 16)) {
158         /* FIXME: those conditions must be fulfilled so that:
159          * - we can distinguish between device IDs and handles
160          * - we can use handles as 16 or 32 bit entities
161          */
162         ERR("Shouldn't happen. Bad allocation scheme\n");
163     }
164
165     mld->dwFlags = HIWORD(*dwFlags);
166     mld->dwCallback = *dwCallback;
167     mld->dwClientInstance = *dwInstance;
168
169     return mld;
170 }
171
172 /**************************************************************************
173  *                              MMDRV_Free                      [internal]
174  */
175 void    MMDRV_Free(HANDLE hndl, LPWINE_MLD mld)
176 {
177     TRACE("(%p, %p)\n", hndl, mld);
178
179     if ((UINT_PTR)hndl & 0x8000) {
180         UINT_PTR idx = (UINT_PTR)hndl & ~0x8000;
181         if (idx < sizeof(MM_MLDrvs) / sizeof(MM_MLDrvs[0])) {
182             MM_MLDrvs[idx] = NULL;
183             HeapFree(GetProcessHeap(), 0, mld);
184             return;
185         }
186     }
187     ERR("Bad Handle %p at %p (not freed)\n", hndl, mld);
188 }
189
190 /**************************************************************************
191  *                              MMDRV_Open                      [internal]
192  */
193 DWORD MMDRV_Open(LPWINE_MLD mld, UINT wMsg, DWORD_PTR dwParam1, DWORD dwFlags)
194 {
195     DWORD               dwRet = MMSYSERR_BADDEVICEID;
196     DWORD_PTR           dwInstance;
197     WINE_LLTYPE*        llType = &llTypes[mld->type];
198     TRACE("(%p, %04x, 0x%08lx, 0x%08x)\n", mld, wMsg, dwParam1, dwFlags);
199
200     mld->dwDriverInstance = (DWORD_PTR)&dwInstance;
201
202     if (mld->uDeviceID == (UINT)-1 || mld->uDeviceID == (UINT16)-1) {
203         TRACE("MAPPER mode requested !\n");
204         if (llType->nMapper == -1) {
205             WARN("Mapper not supported for type %s\n", llTypes[mld->type].typestr);
206             return MMSYSERR_BADDEVICEID;
207         }
208         mld->uDeviceID = (UINT16)-1;
209         mld->mmdIndex = llType->lpMlds[-1].mmdIndex;
210         TRACE("Setting mmdIndex to %u\n", mld->mmdIndex);
211         dwRet = MMDRV_Message(mld, wMsg, dwParam1, dwFlags);
212     } else {
213         if (mld->uDeviceID < llType->wMaxId) {
214             mld->mmdIndex = llType->lpMlds[mld->uDeviceID].mmdIndex;
215             TRACE("Setting mmdIndex to %u\n", mld->mmdIndex);
216             dwRet = MMDRV_Message(mld, wMsg, dwParam1, dwFlags);
217         }
218     }
219     if (dwRet == MMSYSERR_NOERROR)
220         mld->dwDriverInstance = dwInstance;
221     return dwRet;
222 }
223
224 /**************************************************************************
225  *                              MMDRV_Close                     [internal]
226  */
227 DWORD   MMDRV_Close(LPWINE_MLD mld, UINT wMsg)
228 {
229     TRACE("(%p, %04x)\n", mld, wMsg);
230     return MMDRV_Message(mld, wMsg, 0L, 0L);
231 }
232
233 /**************************************************************************
234  *                              MMDRV_GetByID                   [internal]
235  */
236 static LPWINE_MLD MMDRV_GetByID(UINT uDevID, UINT type)
237 {
238     TRACE("(%04x, %04x)\n", uDevID, type);
239     if (uDevID < llTypes[type].wMaxId)
240         return &llTypes[type].lpMlds[uDevID];
241     if ((uDevID == (UINT16)-1 || uDevID == (UINT)-1) && llTypes[type].nMapper != -1)
242         return &llTypes[type].lpMlds[-1];
243     return NULL;
244 }
245
246 /**************************************************************************
247  *                              MMDRV_Get                       [internal]
248  */
249 LPWINE_MLD      MMDRV_Get(HANDLE _hndl, UINT type, BOOL bCanBeID)
250 {
251     LPWINE_MLD  mld = NULL;
252     UINT_PTR    hndl = (UINT_PTR)_hndl;
253     TRACE("(%p, %04x, %c)\n", _hndl, type, bCanBeID ? 'Y' : 'N');
254
255     assert(type < MMDRV_MAX);
256     MMDRV_InitSingleType(type);
257
258     if (hndl >= llTypes[type].wMaxId &&
259         hndl != (UINT16)-1 && hndl != (UINT)-1) {
260         if (hndl & 0x8000) {
261             UINT idx = hndl & ~0x8000;
262             if (idx < sizeof(MM_MLDrvs) / sizeof(MM_MLDrvs[0])) {
263                 __TRY
264                 {
265                     mld = MM_MLDrvs[idx];
266                     if (mld && mld->type != type) mld = NULL;
267                 }
268                 __EXCEPT_PAGE_FAULT
269                 {
270                     mld = NULL;
271                 }
272                 __ENDTRY;
273             }
274         }
275     }
276     if (mld == NULL && bCanBeID) {
277         mld = MMDRV_GetByID(hndl, type);
278     }
279     return mld;
280 }
281
282 /**************************************************************************
283  *                              MMDRV_GetRelated                [internal]
284  */
285 LPWINE_MLD      MMDRV_GetRelated(HANDLE hndl, UINT srcType,
286                                  BOOL bSrcCanBeID, UINT dstType)
287 {
288     LPWINE_MLD          mld;
289     TRACE("(%p, %04x, %c, %04x)\n",
290           hndl, srcType, bSrcCanBeID ? 'Y' : 'N', dstType);
291
292     if ((mld = MMDRV_Get(hndl, srcType, bSrcCanBeID)) != NULL) {
293         WINE_MM_DRIVER_PART*    part = &MMDrvs[mld->mmdIndex].parts[dstType];
294         if (part->nIDMin < part->nIDMax)
295             return MMDRV_GetByID(part->nIDMin, dstType);
296     }
297     return NULL;
298 }
299
300 /**************************************************************************
301  *                              MMDRV_PhysicalFeatures          [internal]
302  */
303 UINT    MMDRV_PhysicalFeatures(LPWINE_MLD mld, UINT uMsg,
304                                DWORD_PTR dwParam1, DWORD_PTR dwParam2)
305 {
306     WINE_MM_DRIVER*     lpDrv = &MMDrvs[mld->mmdIndex];
307
308     TRACE("(%p, %04x, %08lx, %08lx)\n", mld, uMsg, dwParam1, dwParam2);
309
310     /* all those function calls are undocumented */
311     switch (uMsg) {
312     case DRV_QUERYDRVENTRY:
313         lstrcpynA((LPSTR)dwParam1, lpDrv->drvname, LOWORD(dwParam2));
314         break;
315     case DRV_QUERYDEVNODE:
316         *(LPDWORD)dwParam1 = 0L; /* should be DevNode */
317         break;
318     case DRV_QUERYNAME:
319         WARN("NIY QueryName\n");
320         break;
321     case DRV_QUERYDRIVERIDS:
322         WARN("NIY call VxD\n");
323         /* should call VxD MMDEVLDR with (DevNode, dwParam1 and dwParam2) as pmts
324          * dwParam1 is buffer and dwParam2 is sizeof(buffer)
325          * I don't know where the result is stored though
326          */
327         break;
328     case DRV_QUERYMAPPABLE:
329         return (lpDrv->bIsMapper) ? 2 : 0;
330
331     case DRVM_MAPPER_PREFERRED_GET:
332         /* FIXME: get from registry someday */
333         *((LPDWORD)dwParam1) = -1;      /* No preferred device */
334         *((LPDWORD)dwParam2) = 0;
335         break;
336
337     case DRV_QUERYDEVICEINTERFACE:
338     case DRV_QUERYDEVICEINTERFACESIZE:
339         return MMDRV_Message(mld, uMsg, dwParam1, dwParam2);
340
341     case DRV_QUERYDSOUNDIFACE: /* Wine-specific: Retrieve DirectSound interface */
342     case DRV_QUERYDSOUNDDESC: /* Wine-specific: Retrieve DirectSound driver description*/
343         return MMDRV_Message(mld, uMsg, dwParam1, dwParam2);
344
345     default:
346         WARN("Unknown call %04x\n", uMsg);
347         return MMSYSERR_INVALPARAM;
348     }
349     return 0L;
350 }
351
352 /**************************************************************************
353  *                              MMDRV_InitPerType               [internal]
354  */
355 static  BOOL    MMDRV_InitPerType(LPWINE_MM_DRIVER lpDrv, UINT type, UINT wMsg)
356 {
357     WINE_MM_DRIVER_PART*        part = &lpDrv->parts[type];
358     DWORD                       ret;
359     UINT                        count = 0;
360     int                         i, k;
361     TRACE("(%p, %04x, %04x)\n", lpDrv, type, wMsg);
362
363     part->nIDMin = part->nIDMax = 0;
364
365     /* for DRVM_INIT and DRVM_ENABLE, dwParam2 should be PnP node */
366     /* the DRVM_ENABLE is only required when the PnP node is non zero */
367     if (part->fnMessage32) {
368         ret = part->fnMessage32(0, DRVM_INIT, 0L, 0L, 0L);
369         TRACE("DRVM_INIT => %s\n", WINMM_ErrorToString(ret));
370 #if 0
371         ret = part->fnMessage32(0, DRVM_ENABLE, 0L, 0L, 0L);
372         TRACE("DRVM_ENABLE => %08lx\n", ret);
373 #endif
374         count = part->fnMessage32(0, wMsg, 0L, 0L, 0L);
375     }
376     else return FALSE;
377
378     TRACE("Got %u dev for (%s:%s)\n", count, lpDrv->drvname, llTypes[type].typestr);
379     
380     if (HIWORD(count))
381         return FALSE;
382
383     /* got some drivers */
384     if (lpDrv->bIsMapper) {
385         llTypes[type].nMapper = MMDrvsHi;
386     } else {
387         if (count == 0)
388             return FALSE;
389         part->nIDMin = llTypes[type].wMaxId;
390         llTypes[type].wMaxId += count;
391         part->nIDMax = llTypes[type].wMaxId;
392     }
393     TRACE("Setting min=%d max=%d (ttop=%d) for (%s:%s)\n",
394           part->nIDMin, part->nIDMax, llTypes[type].wMaxId,
395           lpDrv->drvname, llTypes[type].typestr);
396     /* realloc translation table */
397     if (llTypes[type].lpMlds)
398         llTypes[type].lpMlds = (LPWINE_MLD)
399         HeapReAlloc(GetProcessHeap(), 0, llTypes[type].lpMlds - 1,
400                     sizeof(WINE_MLD) * (llTypes[type].wMaxId + 1)) + 1;
401     else
402         llTypes[type].lpMlds = (LPWINE_MLD)
403         HeapAlloc(GetProcessHeap(), 0,
404                     sizeof(WINE_MLD) * (llTypes[type].wMaxId + 1)) + 1;
405
406     /* re-build the translation table */
407     if (lpDrv->bIsMapper) {
408         TRACE("%s:Trans[%d] -> %s\n", llTypes[type].typestr, -1, MMDrvs[llTypes[type].nMapper].drvname);
409         llTypes[type].lpMlds[-1].uDeviceID = (UINT16)-1;
410         llTypes[type].lpMlds[-1].type = type;
411         llTypes[type].lpMlds[-1].mmdIndex = llTypes[type].nMapper;
412         llTypes[type].lpMlds[-1].dwDriverInstance = 0;
413     }
414     for (i = k = 0; i <= MMDrvsHi; i++) {
415         while (MMDrvs[i].parts[type].nIDMin <= k && k < MMDrvs[i].parts[type].nIDMax) {
416             TRACE("%s:Trans[%d] -> %s\n", llTypes[type].typestr, k, MMDrvs[i].drvname);
417             llTypes[type].lpMlds[k].uDeviceID = k;
418             llTypes[type].lpMlds[k].type = type;
419             llTypes[type].lpMlds[k].mmdIndex = i;
420             llTypes[type].lpMlds[k].dwDriverInstance = 0;
421             k++;
422         }
423     }
424     return TRUE;
425 }
426
427 /**************************************************************************
428  *                              MMDRV_Install                   [internal]
429  */
430 static  BOOL    MMDRV_Install(LPCSTR drvRegName, LPCSTR drvFileName, BOOL bIsMapper)
431 {
432     int                 i, count = 0;
433     LPWINE_MM_DRIVER    lpDrv = &MMDrvs[MMDrvsHi];
434     LPWINE_DRIVER       d;
435     WINEMM_msgFunc32    func;
436
437     TRACE("('%s', '%s', mapper=%c);\n", drvRegName, drvFileName, bIsMapper ? 'Y' : 'N');
438
439     for (i = 0; i < MMDrvsHi; i++) {
440         if (!strcmp(drvRegName, MMDrvs[i].drvname)) return FALSE;
441     }
442
443     /* Be sure that size of MMDrvs matches the max number of loadable
444      * drivers !!
445      * If not just increase size of MMDrvs
446      */
447     assert(MMDrvsHi <= sizeof(MMDrvs)/sizeof(MMDrvs[0]));
448
449     memset(lpDrv, 0, sizeof(*lpDrv));
450
451     if (!(lpDrv->hDriver = OpenDriverA(drvFileName, 0, 0))) {
452         WARN("Couldn't open driver '%s'\n", drvFileName);
453         return FALSE;
454     }
455
456     d = DRIVER_FindFromHDrvr(lpDrv->hDriver);
457
458     /* Then look for xxxMessage functions */
459 #define AA(_h,_w,_x,_y,_z)                                      \
460     func = (WINEMM_msgFunc##_y) _z ((_h), #_x);                 \
461     if (func != NULL)                                           \
462         { lpDrv->parts[_w].fnMessage##_y = func; count++;       \
463           TRACE("Got %d bit func '%s'\n", _y, #_x);         }
464
465     if (d->hModule) {
466 #define A(_x,_y)        AA(d->hModule,_x,_y,32,GetProcAddress)
467             A(MMDRV_AUX,        auxMessage);
468             A(MMDRV_MIXER,      mxdMessage);
469             A(MMDRV_MIDIIN,     midMessage);
470             A(MMDRV_MIDIOUT,    modMessage);
471             A(MMDRV_WAVEIN,     widMessage);
472             A(MMDRV_WAVEOUT,    wodMessage);
473 #undef A
474     }
475 #undef AA
476
477     if (!count) {
478         CloseDriver(lpDrv->hDriver, 0, 0);
479         WARN("No message functions found\n");
480         return FALSE;
481     }
482
483     /* FIXME: being a mapper or not should be known by another way */
484     /* it's known for NE drvs (the description is of the form '*mapper: *'
485      * I don't have any clue for PE drvs
486      */
487     lpDrv->bIsMapper = bIsMapper;
488     lpDrv->drvname = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(drvRegName) + 1), drvRegName);
489
490     /* Finish init and get the count of the devices */
491     i = 0;
492     if (MMDRV_InitPerType(lpDrv, MMDRV_AUX,     AUXDM_GETNUMDEVS))      i = 1;
493     if (MMDRV_InitPerType(lpDrv, MMDRV_MIXER,   MXDM_GETNUMDEVS))       i = 1;
494     if (MMDRV_InitPerType(lpDrv, MMDRV_MIDIIN,  MIDM_GETNUMDEVS))       i = 1;
495     if (MMDRV_InitPerType(lpDrv, MMDRV_MIDIOUT, MODM_GETNUMDEVS))       i = 1;
496     if (MMDRV_InitPerType(lpDrv, MMDRV_WAVEIN,  WIDM_GETNUMDEVS))       i = 1;
497     if (MMDRV_InitPerType(lpDrv, MMDRV_WAVEOUT, WODM_GETNUMDEVS))       i = 1;
498     /* if all those func calls return FALSE, then the driver must be unloaded */
499     if (!i) {
500         CloseDriver(lpDrv->hDriver, 0, 0);
501         HeapFree(GetProcessHeap(), 0, lpDrv->drvname);
502         WARN("Driver initialization failed\n");
503         return FALSE;
504     }
505
506     MMDrvsHi++;
507
508     return TRUE;
509 }
510
511 /**************************************************************************
512  *                              MMDRV_Init
513  */
514 static void MMDRV_Init(void)
515 {
516     HKEY        hKey;
517     char        driver_buffer[256];
518     char *p, *next;
519     TRACE("()\n");
520
521     strcpy(driver_buffer, WINE_DEFAULT_WINMM_DRIVER);
522
523     /* @@ Wine registry key: HKCU\Software\Wine\Drivers */
524     if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Drivers", &hKey))
525     {
526         DWORD size = sizeof(driver_buffer);
527         if (RegQueryValueExA(hKey, "Audio", 0, NULL, (BYTE*)driver_buffer, &size))
528             strcpy(driver_buffer, WINE_DEFAULT_WINMM_DRIVER);
529     }
530
531     for (p = driver_buffer; p; p = next)
532     {
533         char filename[sizeof(driver_buffer)+10];
534         next = strchr(p, ',');
535         if (next) *next++ = 0;
536         sprintf( filename, "wine%s.drv", p );
537         if (MMDRV_Install(filename, filename, FALSE))
538             break;
539         p = next;
540     }
541
542     MMDRV_Install("wavemapper", "msacm32.drv", TRUE);
543     MMDRV_Install("midimapper", "midimap.dll", TRUE);
544 }
545
546 /******************************************************************
547  *              ExitPerType
548  *
549  *
550  */
551 static  BOOL    MMDRV_ExitPerType(LPWINE_MM_DRIVER lpDrv, UINT type)
552 {
553     WINE_MM_DRIVER_PART*        part = &lpDrv->parts[type];
554     DWORD                       ret;
555     TRACE("(%p, %04x)\n", lpDrv, type);
556
557     if (part->fnMessage32) {
558 #if 0
559         ret = part->fnMessage32(0, DRVM_DISABLE, 0L, 0L, 0L);
560         TRACE("DRVM_DISABLE => %08lx\n", ret);
561 #endif
562         ret = part->fnMessage32(0, DRVM_EXIT, 0L, 0L, 0L);
563         TRACE("DRVM_EXIT => %s\n", WINMM_ErrorToString(ret));
564     }
565
566     return TRUE;
567 }
568
569 /******************************************************************
570  *              Exit
571  *
572  *
573  */
574 void MMDRV_Exit(void)
575 {
576     unsigned int i;
577     TRACE("()\n");
578
579     for (i = 0; i < sizeof(MM_MLDrvs) / sizeof(MM_MLDrvs[0]); i++)
580     {
581         if (MM_MLDrvs[i] != NULL)
582         {
583             FIXME("Closing while ll-driver open\n");
584 #if 0
585             /* FIXME: should generate a message depending on type */
586             MMDRV_Free((HANDLE)(i | 0x8000), MM_MLDrvs[i]);
587 #endif
588         }
589     }
590
591     /* unload driver, in reverse order of loading */
592     i = sizeof(MMDrvs) / sizeof(MMDrvs[0]);
593     while (i-- > 0)
594     {
595         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_AUX);
596         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_MIXER);
597         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_MIDIIN);
598         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_MIDIOUT);
599         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_WAVEIN);
600         MMDRV_ExitPerType(&MMDrvs[i], MMDRV_WAVEOUT);
601         CloseDriver(MMDrvs[i].hDriver, 0, 0);
602     }
603     if (llTypes[MMDRV_AUX].lpMlds)
604         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_AUX].lpMlds - 1);
605     if (llTypes[MMDRV_MIXER].lpMlds)
606         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_MIXER].lpMlds - 1);
607     if (llTypes[MMDRV_MIDIIN].lpMlds)
608         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_MIDIIN].lpMlds - 1);
609     if (llTypes[MMDRV_MIDIOUT].lpMlds)
610         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_MIDIOUT].lpMlds - 1);
611     if (llTypes[MMDRV_WAVEIN].lpMlds)
612         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_WAVEIN].lpMlds - 1);
613     if (llTypes[MMDRV_WAVEOUT].lpMlds)
614         HeapFree(GetProcessHeap(), 0, llTypes[MMDRV_WAVEOUT].lpMlds - 1);
615 }