Partial implementation of WDMAUD.DRV - device capability querying fails
authorAndrew Greenwood <silverblade@reactos.org>
Thu, 24 Nov 2005 14:36:47 +0000 (14:36 +0000)
committerAndrew Greenwood <silverblade@reactos.org>
Thu, 24 Nov 2005 14:36:47 +0000 (14:36 +0000)
svn path=/trunk/; revision=19530

12 files changed:
reactos/lib/wdmaud/TODO [new file with mode: 0644]
reactos/lib/wdmaud/control.c [new file with mode: 0644]
reactos/lib/wdmaud/devices.c [new file with mode: 0644]
reactos/lib/wdmaud/helper.c [new file with mode: 0755]
reactos/lib/wdmaud/kernel.c [new file with mode: 0644]
reactos/lib/wdmaud/threads.c [new file with mode: 0644]
reactos/lib/wdmaud/user.c [new file with mode: 0644]
reactos/lib/wdmaud/wavehdr.c [new file with mode: 0644]
reactos/lib/wdmaud/wdmaud.def [new file with mode: 0755]
reactos/lib/wdmaud/wdmaud.h [new file with mode: 0755]
reactos/lib/wdmaud/wdmaud.rc [new file with mode: 0755]
reactos/lib/wdmaud/wdmaud.xml [new file with mode: 0755]

diff --git a/reactos/lib/wdmaud/TODO b/reactos/lib/wdmaud/TODO
new file mode 100644 (file)
index 0000000..af247a7
--- /dev/null
@@ -0,0 +1,4 @@
+To-Do:
+- Globally store the heap handle
+- Ensure cleanups are... clean...
+- Clone device info in OPEN/close?
diff --git a/reactos/lib/wdmaud/control.c b/reactos/lib/wdmaud/control.c
new file mode 100644 (file)
index 0000000..3c9c101
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *
+ * COPYRIGHT:           See COPYING in the top level directory
+ * PROJECT:             ReactOS Multimedia
+ * FILE:                lib/wdmaud/wavehdr.c
+ * PURPOSE:             WDM Audio Support - Device Control (Play/Stop etc.)
+ * PROGRAMMER:          Andrew Greenwood
+ * UPDATE HISTORY:
+ *                      Nov 23, 2005: Created
+ */
+
+#include <windows.h>
+#include "wdmaud.h"
+
+/*
+    TODO:
+    Make these work for the other device types!
+*/
+
+MMRESULT StartDevice(PWDMAUD_DEVICE_INFO device)
+{
+    MMRESULT result;
+    DWORD ioctl_code;
+
+    result = ValidateDeviceInfoAndState(device);
+
+    if ( result != MMSYSERR_NOERROR )
+        return result;
+
+    ioctl_code = device == WDMAUD_WAVE_IN ? IOCTL_WDMAUD_WAVE_IN_START :
+                 device == WDMAUD_WAVE_OUT ? IOCTL_WDMAUD_WAVE_OUT_START :
+                 0x0000;
+
+    ASSERT( ioctl_code );
+}
+
+MMRESULT StopDevice(PWDMAUD_DEVICE_INFO device)
+{
+}
+
+MMRESULT PauseDevice(PWDMAUD_DEVICE_INFO device)
+{
+}
+
+MMRESULT StopDeviceLooping(PWDMAUD_DEVICE_INFO device)
+{
+}
diff --git a/reactos/lib/wdmaud/devices.c b/reactos/lib/wdmaud/devices.c
new file mode 100644 (file)
index 0000000..26288ce
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/wdmaud/devices.c
+ * PURPOSE:              WDM Audio Support - Device Management
+ * PROGRAMMER:           Andrew Greenwood
+ * UPDATE HISTORY:
+ *                       Nov 18, 2005: Created
+ * 
+ * WARNING! SOME OF THESE FUNCTIONS OUGHT TO COPY THE DEVICE INFO STRUCTURE
+ * THAT HAS BEEN FED TO THEM!
+*/
+
+#include <windows.h>
+#include "wdmaud.h"
+
+const char WDMAUD_DEVICE_INFO_SIG[4] = "WADI";
+const char WDMAUD_DEVICE_STATE_SIG[4] = "WADS";
+
+
+BOOL IsValidDevicePath(WCHAR* path)
+{
+    if (IsBadReadPtr(path, 1))  /* TODO: Replace with flags */
+    {
+        DPRINT1("Bad interface\n");
+        return FALSE;
+    }
+
+    /* Original driver seems to check for strlenW < 0x1000 */
+
+    return TRUE;
+}
+
+MMRESULT ValidateDeviceInfo(PWDMAUD_DEVICE_INFO device_info)
+{
+    if ( IsBadWritePtr(device_info, sizeof(WDMAUD_DEVICE_INFO)) )
+        return MMSYSERR_INVALPARAM;
+
+    if ( *device_info->signature != *WDMAUD_DEVICE_INFO_SIG )
+        return MMSYSERR_INVALPARAM;
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state)
+{
+    if ( IsBadWritePtr(state, sizeof(WDMAUD_DEVICE_INFO)) )
+        return MMSYSERR_INVALPARAM;
+
+    if ( *state->signature != *WDMAUD_DEVICE_STATE_SIG )
+        return MMSYSERR_INVALPARAM;
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    ValidateDeviceStateEvents should be used in conjunction with the standard
+    state validation routine.
+*/
+
+MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state)
+{
+    if ( ( (DWORD) state->exit_thread_event == 0x00000000 ) &&
+         ( (DWORD) state->exit_thread_event != 0x48484848 ) )
+    {
+        DPRINT1("Bad exit thread event\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if ( ( (DWORD) state->queue_event == 0x00000000 ) &&
+         ( (DWORD) state->queue_event != 0x42424242 ) &&
+         ( (DWORD) state->queue_event != 0x43434343 ) )
+    {
+        DPRINT1("Bad queue event\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info)
+{
+    MMRESULT result;
+
+    result = ValidateDeviceInfo(device_info);
+
+    if ( result != MMSYSERR_NOERROR )
+        return result;
+
+    result = ValidateDeviceState(device_info->state);
+
+    if ( result != MMSYSERR_NOERROR )
+        return result;
+
+    return MMSYSERR_NOERROR;
+}
+
+PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path)
+{
+    HANDLE heap = 0;
+    PWDMAUD_DEVICE_INFO device_data = 0;
+    int path_size = 0;
+
+    if ( ! IsValidDevicePath(device_path) )
+    {
+        DPRINT1("No valid device interface given!\n");
+        goto cleanup;
+    }
+
+    /* Take into account this is a unicode string... */
+    path_size = (lstrlen(device_path) + 1) * sizeof(WCHAR);
+    DPRINT("Size of path is %d\n", (int) path_size);
+
+    heap = GetProcessHeap();
+
+    if ( ! heap )
+    {
+        DPRINT1("Couldn't get the process heap (error %d)\n",
+                (int) GetLastError());
+        goto cleanup;
+    }
+
+    DPRINT("Allocating %d bytes\n",
+           path_size + sizeof(WDMAUD_DEVICE_INFO));
+
+    device_data = (PWDMAUD_DEVICE_INFO) HeapAlloc(heap,
+                                                  HEAP_ZERO_MEMORY,
+                                                  path_size + sizeof(WDMAUD_DEVICE_INFO));
+
+    if ( ! device_data )
+    {
+        DPRINT1("Unable to allocate memory for device data (error %d)\n",
+                (int) GetLastError());
+        goto cleanup;
+    }
+
+    DPRINT("Copying signature\n");
+    memcpy(device_data->signature, WDMAUD_DEVICE_INFO_SIG, 4);
+
+    DPRINT("Copying path (0x%x)\n", (int)device_path);
+    lstrcpy(device_data->path, device_path);
+
+    device_data->type = device_type;
+
+    cleanup :
+    {
+        /* No cleanup needed (no failures possible after allocation.) */
+        DPRINT("Performing cleanup\n");
+
+        return device_data;
+    }
+}
+
+PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original)
+{
+    PWDMAUD_DEVICE_INFO clone = NULL;
+
+    if ( ValidateDeviceInfo(original) != MMSYSERR_NOERROR)
+    {
+        DPRINT1("Original device data was invalid\n");
+        return NULL;
+    }
+
+    /* This will set the type and path, so we can forget about those */
+    clone = CreateDeviceData(original->type, original->path);
+
+    if ( ! clone )
+    {
+        DPRINT1("Clone creation failed\n");
+        return NULL;
+    }
+
+    clone->id = original->id;
+    clone->wave_handle = original->wave_handle; /* ok? */
+
+    /* TODO: Maybe we should copy some more? */
+
+    return clone;
+}
+
+void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data)
+{
+    ASSERT( device_data );
+    /* TODO */
+}
+
+MMRESULT ModifyDevicePresence(
+    CHAR device_type,
+    WCHAR* device_path,
+    BOOL adding)
+{
+    DWORD ioctl = 0;
+    PWDMAUD_DEVICE_INFO device_data = 0;
+    MMRESULT result = MMSYSERR_ERROR;
+    MMRESULT kernel_result = MMSYSERR_ERROR;
+
+    DPRINT("ModifyDevicePresence - %s a device\n",
+           adding ? "adding" : "removing");
+
+    DPRINT("Topology path %S\n", device_path);
+
+    /* FIXME: DeviceType! */
+    /* TODO: Assert on device type? */
+
+    ASSERT( IsValidDeviceType(device_type) );
+    ASSERT( device_path );
+
+    device_data = CreateDeviceData(device_type, device_path);
+
+    if ( ! device_data )
+    {
+        DPRINT1("Couldn't allocate memory for device data\n");
+        result = MMSYSERR_NOMEM;
+        goto cleanup;
+    }
+
+/*    device_data->type = device_type; */
+
+    ioctl = adding ? IOCTL_WDMAUD_ADD_DEVICE : IOCTL_WDMAUD_REMOVE_DEVICE;
+
+    kernel_result = CallKernelDevice(device_data,
+                                     ioctl,
+                                     0,
+                                     0);
+
+    if ( kernel_result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("WdmAudioIoControl FAILED with error %d\n", (int) kernel_result);
+
+        switch ( kernel_result )
+        {
+            /* TODO: Translate into a real error code */
+            default :
+                result = MMSYSERR_ERROR;
+        }
+
+        goto cleanup;
+    }
+
+    DPRINT("ModifyDevicePresence succeeded\n");
+
+    result = MMSYSERR_NOERROR;
+
+    cleanup :
+    {
+        if ( device_data )
+            DeleteDeviceData(device_data);
+
+        return result;
+    }
+}
+
+DWORD GetDeviceCount(CHAR device_type, WCHAR* topology_path)
+{
+    PWDMAUD_DEVICE_INFO device_data;
+    int device_count = 0;
+
+    DPRINT("Topology path %S\n", topology_path);
+
+    device_data = CreateDeviceData(device_type, topology_path);
+
+    if (! device_data)
+    {
+        DPRINT1("Couldn't allocate device data\n");
+        goto cleanup;
+    }
+
+    DPRINT("Getting num devs\n");
+
+    /*device_data->type = device_type;*/
+    device_data->with_critical_section = FALSE;
+
+    if ( CallKernelDevice(device_data,
+                          IOCTL_WDMAUD_GET_DEVICE_COUNT,
+                          0,
+                          0) != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Failed\n");
+        goto cleanup;
+    }
+
+    device_count = device_data->id;
+
+    DPRINT("There are %d devs\n", device_count);
+
+    cleanup :
+    {
+        if ( device_data )
+            DeleteDeviceData(device_data);
+
+        return device_count;
+    }
+}
+
+/*
+    This is a bit messed up
+*/
+
+MMRESULT GetDeviceCapabilities(
+    CHAR device_type,
+    DWORD device_id,
+    WCHAR* device_path,
+    LPCOMMONCAPS caps
+)
+{
+    PWDMAUD_DEVICE_INFO device = NULL;
+    MMRESULT result = MMSYSERR_ERROR;
+
+    DPRINT("Device path %S\n", device_path);
+
+    /* Hmm - caps->wMid seems to be 0x54 (84) from XP's winmm.dll */
+    if (caps->wMid == 0)
+    {
+        return MMSYSERR_NOERROR;
+
+        DPRINT("caps->wMid == 0\n");
+
+        DPRINT("Manufacturer: 0x%x (%d)\n", caps->wMid, caps->wMid);
+        DPRINT("Product: 0x%x (%d)\n", caps->wPid, caps->wPid);
+        DPRINT("Device is: %S\n", caps->szPname);
+
+        if ( IsWaveOutDeviceType(device_type) )
+        {
+        LPWAVEOUTCAPS woc = (LPWAVEOUTCAPS) caps;
+        DPRINT("Formats: %d\n", (int) woc->dwFormats);
+        DPRINT("Channels: %d\n", woc->wChannels);
+        DPRINT("Reserved: %d\n", woc->wReserved1);
+        DPRINT("Support: %d\n", (int) woc->dwSupport);
+        }
+
+        return MMSYSERR_NOERROR;
+    }
+
+#if 0
+    int i;
+    for (i = 0; i < 64; i ++)
+    {
+        DPRINT("0x%x\n", *(((UCHAR*)caps) + i));
+    }
+
+    return MMSYSERR_NOERROR;
+#endif
+
+    DPRINT("Going to have to query the kernel-mode part\n");
+
+    device = CreateDeviceData(device_type, device_path);
+
+    if ( ! device )
+    {
+        DPRINT("Unable to allocate device data memory\n");
+        result = MMSYSERR_NOMEM;
+        goto cleanup;
+    }
+
+    device->id = device_id;
+    device->with_critical_section = FALSE;
+
+    /* ? */
+    DPRINT("Caps wMid is 0x%x\n", (int) caps->wMid);
+    DPRINT("Driver version is 0x%x\n", (int) caps->vDriverVersion);
+
+    DPRINT("%S\n", (WCHAR*) caps->vDriverVersion);
+
+    LPWORD theword;
+    theword = (LPWORD) caps->vDriverVersion;
+    *theword = 0x43;
+    //caps->vDriverVersion=0x4300;
+
+
+    DPRINT("Calling kernel device\n");
+    result = CallKernelDevice(device,
+                              IOCTL_WDMAUD_GET_CAPABILITIES,
+                              (DWORD)caps->wMid,
+                              (DWORD)caps->vDriverVersion);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT("IoControl failed\n");
+        goto cleanup;
+    }
+
+    
+
+    /* What do we return? */
+    /*return MMSYSERR_NOERROR; */ /* already set by now */
+
+    cleanup :
+    {
+        if ( device )
+            DeleteDeviceData(device);
+
+        return result;
+    }
+}
+
+MMRESULT TryOpenDevice(
+    PWDMAUD_DEVICE_INFO device,
+    LPWAVEFORMATEX format
+)
+{
+    if ( device->id > 0x64 )    /* FIXME */
+    {
+        DPRINT1("device->id > 0x64 ! ???\n");
+        return MMSYSERR_BADDEVICEID; /* OK? */
+    }
+
+    /* We'll only have a format set for wave devices */
+    if ( format )
+    {
+        if ( format->wFormatTag == 1 )
+        {
+            DWORD sample_size;
+
+            DPRINT("Standard (PCM) format\n");
+            sample_size = format->nChannels * format->wBitsPerSample;
+            device->state->sample_size = sample_size;
+
+            if ( CallKernelDevice(device,
+                                  IOCTL_WDMAUD_OPEN_DEVICE,
+                                  0x10,
+                                  (DWORD)format)
+                            != MMSYSERR_NOERROR )
+            {
+                DPRINT("Call failed\n");
+                /* FIXME */
+                return MMSYSERR_NOTSUPPORTED;   /* WAVERR_BADFORMAT? */
+            }
+        }
+        else
+        {
+            /* FIXME */
+            DPRINT("Non-PCM format\n");
+            return MMSYSERR_NOTSUPPORTED;
+        }
+    }
+
+    /* If we got this far without error, the format is supported! */
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT OpenWaveDevice(
+    CHAR device_type,
+    DWORD device_id,
+    LPWAVEOPENDESC open_details,
+    DWORD flags,
+    DWORD user_data
+)
+{
+    HANDLE heap = 0;
+    PWDMAUD_DEVICE_INFO device = NULL;
+    WCHAR* device_path = NULL;
+    MMRESULT result = MMSYSERR_ERROR;
+
+    /* ASSERT(open_details); */
+
+    heap = GetProcessHeap();
+
+    if ( ! heap )
+    {
+        DPRINT1("Couldn't get the process heap (error %d)\n",
+                (int) GetLastError());
+        result = MMSYSERR_ERROR;
+        goto cleanup;
+    }
+
+    DPRINT("OpenDevice called\n");
+
+    device_path = (WCHAR*) open_details->dnDevNode;
+    device = CreateDeviceData(device_type, device_path);
+
+    if ( ! device )
+    {
+        DPRINT1("Couldn't create device data\n");
+        result = MMSYSERR_NOMEM;
+        goto cleanup;
+    }
+
+    DPRINT("Allocated device data, allocating device state\n");
+
+    device->state = HeapAlloc(heap,
+                              HEAP_ZERO_MEMORY,
+                              sizeof(WDMAUD_DEVICE_STATE));
+
+    if ( ! device->state )
+    {
+        DPRINT1("Couldn't allocate memory for device state (error %d)\n",
+                (int) GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto cleanup;
+    }
+
+    /* FIXME: ok here ? */
+    device->type = device_type;
+    device->id = device_id;
+    device->flags = flags;
+
+    if ( flags & WAVE_FORMAT_QUERY )
+    {
+        DPRINT("Do I support this format? Hmm...\n");
+
+        result = TryOpenDevice(device, open_details->lpFormat);
+
+        if ( result != MMSYSERR_NOERROR )
+        {
+            DPRINT("Format not supported\n");
+            goto cleanup;
+        }
+
+        DPRINT("Yes, I do support this format!\n");
+    }
+    else
+    {
+
+        DPRINT("You actually want me to open the device, huh?\n");
+
+        /* Allocate memory for the "queue" critical section */
+        
+        device->state->queue_critical_section =
+            HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(CRITICAL_SECTION));
+
+        if ( ! device->state->queue_critical_section )
+        {
+            DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
+                    (int) GetLastError());
+            result = MMSYSERR_NOMEM;
+            goto cleanup;
+        }
+        
+        /* Initialize the critical section */
+        InitializeCriticalSection(device->state->queue_critical_section);
+
+        /* We need these so we can contact the client later */
+        device->client_instance = open_details->dwInstance;
+        device->client_callback = open_details->dwCallback;
+
+        /* Reset state */
+        device->state->open_descriptor = NULL;
+        device->state->unknown_24 = 0;
+        
+        device->state->is_running = FALSE;
+        device->state->is_paused =
+            device->type == WDMAUD_WAVE_IN ? TRUE : FALSE;
+
+        memcpy(device->state->signature, WDMAUD_DEVICE_STATE_SIG, 4);
+
+        DPRINT("All systems are go...\n");
+
+        result = TryOpenDevice(device, open_details->lpFormat);
+        
+        if ( result != MMSYSERR_NOERROR )
+        {
+            DPRINT1("Format not supported?\n");
+            goto cleanup; /* no need to set result - already done */
+        }
+
+        /* Enter the critical section while updating the device list */
+        EnterCriticalSection(device->state->queue_critical_section);
+        /* ... */
+        LeaveCriticalSection(device->state->queue_critical_section);
+
+        /* The wave device handle is actually our structure. Neat, eh? */
+        open_details->hWave = (HWAVE) device;
+
+        /* We also need to set our "user data" for winmm */
+        LPVOID* ud = (LPVOID*) user_data;   /* FIXME */
+        *ud = device;
+
+        if (device->client_callback)
+        {
+            DWORD message;
+
+            message = (device->type == WDMAUD_WAVE_IN ? WIM_OPEN :
+                                       WDMAUD_WAVE_OUT ? WOM_OPEN : -1);
+
+            DPRINT("About to call the client callback\n");
+
+            /* Call the callback */
+            NotifyClient(device, message, 0, 0);
+
+            DPRINT("...it is done!\n");
+        }
+
+        result = MMSYSERR_NOERROR;
+    }
+
+    /*
+        This cleanup may need checking for memory leakage :/
+    */
+
+    cleanup :
+    {
+        if ( ( result != MMSYSERR_NOERROR ) && ( heap ) )
+        {
+            if ( device )
+            {
+                if ( device->state )
+                {
+                    if ( device->state->queue_critical_section )
+                    {
+                        DeleteCriticalSection(device->state->queue_critical_section);
+                        HeapFree(heap, 0, device->state->queue_critical_section);
+                    }
+                    
+                    HeapFree(heap, 0, device->state);
+                }
+
+                DeleteDeviceData(device);
+            }
+        }
+
+        DPRINT("Returning %d\n", (int) result);
+
+        return result;
+    }
+}
+
+MMRESULT CloseDevice(
+    PWDMAUD_DEVICE_INFO device
+)
+{
+    MMRESULT result = MMSYSERR_ERROR;
+
+    DPRINT("CloseDevice()\n");
+
+    DUMP_WDMAUD_DEVICE_INFO(device);
+
+    if ( ValidateDeviceInfo(device) != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Invalid device info passed to CloseDevice\n");
+        result = MMSYSERR_INVALHANDLE;
+        goto cleanup;
+    }
+
+    /* TODO: Check state! */
+
+    if ( device->id > 0x64 ) /* FIXME ? */
+    {
+        DPRINT1("??\n");
+        goto cleanup;
+    }
+
+    switch(device->type)
+    {
+        case WDMAUD_WAVE_OUT :
+        {
+            if ( device->state->open_descriptor )
+            {
+                DPRINT1("Device is still playing!\n");
+                result = WAVERR_STILLPLAYING;
+                goto cleanup;
+            }
+
+            /* TODO: Destroy completion thread */
+            
+            break;
+        }
+        
+        default :
+        {
+            DPRINT1("Sorry, device type %d not supported yet!\n", (int) device->type);
+            goto cleanup;
+        }
+    }
+
+    result = CallKernelDevice(device, IOCTL_WDMAUD_CLOSE_DEVICE, 0, 0);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Couldn't close the device!\n");
+        goto cleanup;
+    }
+
+    if (device->client_callback)
+    {
+        DWORD message;
+
+        message = (device->type == WDMAUD_WAVE_IN ? WIM_CLOSE :
+                                   WDMAUD_WAVE_OUT ? WOM_CLOSE :
+                                   WDMAUD_MIDI_IN ? MIM_CLOSE :
+                                   WDMAUD_MIDI_OUT ? MOM_CLOSE : -1);
+
+        DPRINT("About to call the client callback\n");
+
+        /* Call the callback */
+        NotifyClient(device, message, 0, 0);
+
+        DPRINT("...it is done!\n");
+    }
+
+    /* Result was set earlier by CallKernelDevice */
+
+    cleanup :
+    {
+        return result;
+    }
+}
diff --git a/reactos/lib/wdmaud/helper.c b/reactos/lib/wdmaud/helper.c
new file mode 100755 (executable)
index 0000000..814721d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *
+ * COPYRIGHT:           See COPYING in the top level directory
+ * PROJECT:             ReactOS Multimedia
+ * FILE:                lib/wdmaud/helper.c
+ * PURPOSE:             Multimedia User Mode Driver - Helper Funcs
+ * PROGRAMMER:          Andrew Greenwood
+ * UPDATE HISTORY:
+ *                      Nov 13, 2005: Created
+ */
+
+#include <windows.h>
+#include <mmsystem.h>
+
+/*
+       TranslateWinError converts Win32 error codes (returned by
+       GetLastError, typically) into MMSYSERR codes.
+*/
+
+MMRESULT TranslateWinError(DWORD error)
+{
+       switch(error)
+       {
+               case NO_ERROR :
+               case ERROR_IO_PENDING :
+                       return MMSYSERR_NOERROR;
+
+               case ERROR_BUSY :
+                       return MMSYSERR_ALLOCATED;
+
+               case ERROR_NOT_SUPPORTED :
+               case ERROR_INVALID_FUNCTION :
+                       return MMSYSERR_NOTSUPPORTED;
+
+               case ERROR_NOT_ENOUGH_MEMORY :
+                       return MMSYSERR_NOMEM;
+
+               case ERROR_ACCESS_DENIED :
+                       return MMSYSERR_BADDEVICEID;
+
+               case ERROR_INSUFFICIENT_BUFFER :
+                       return MMSYSERR_INVALPARAM;
+       }
+
+       return MMSYSERR_ERROR;
+}
diff --git a/reactos/lib/wdmaud/kernel.c b/reactos/lib/wdmaud/kernel.c
new file mode 100644 (file)
index 0000000..e1ae043
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/wdmaud/kernel.c
+ * PURPOSE:              WDM Audio Support - Kernel Mode Interface
+ * PROGRAMMER:           Andrew Greenwood
+ * UPDATE HISTORY:
+ *                       Nov 18, 2005: Created
+ */
+
+#define INITGUID    /* FIXME */
+
+#include <windows.h>
+#include <setupapi.h>
+#include "wdmaud.h"
+
+/* HACK ALERT - This goes in ksmedia.h */
+DEFINE_GUID(KSCATEGORY_WDMAUD,
+    0x3e227e76L, 0x690d, 0x11d2, 0x81, 0x61, 0x00, 0x00, 0xf8, 0x77, 0x5b, 0xf1);
+
+/* This stores the handle of the kernel device */
+static HANDLE kernel_device_handle = NULL;
+
+//static WCHAR* 
+
+
+/*
+    TODO: There's a variant of this that uses critical sections...
+*/
+
+MMRESULT CallKernelDevice(
+    PWDMAUD_DEVICE_INFO device,
+    DWORD ioctl_code,
+    DWORD param1,
+    DWORD param2)
+{
+    OVERLAPPED overlap;
+    MMRESULT result = MMSYSERR_ERROR;
+    DWORD name_len = 0;
+    DWORD bytes_returned = 0;
+    BOOL using_critical_section = FALSE;
+
+    ASSERT(kernel_device_handle);
+    ASSERT(device);
+
+    DPRINT("Creating event\n");
+    overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    if ( ! overlap.hEvent )
+    {
+        DPRINT1("CreateEvent failed - error %d\n", (int)GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto cleanup;
+    }
+
+    DPRINT("Sizeof wchar == %d\n", (int) sizeof(WCHAR));
+    name_len = lstrlen(device->path) * sizeof(WCHAR);   /* ok ? */
+
+    /* These seem to carry optional structures */
+    device->ioctl_param1 = param1;
+    device->ioctl_param2 = param2;
+
+    /* Enter critical section if wave/midi device, and if required */
+    if ( ( ! IsMixerDeviceType(device->type) ) &&
+         ( ! IsAuxDeviceType(device->type) ) &&
+         ( device->with_critical_section ) )
+    {
+        /* this seems to crash under some conditions */
+        ASSERT(device->state);
+        using_critical_section = TRUE;
+        EnterCriticalSection(device->state->queue_critical_section);
+    }
+
+    DPRINT("Calling DeviceIoControl with IOCTL %x\n", (int) ioctl_code);
+    
+    if ( ! DeviceIoControl(kernel_device_handle,
+                           ioctl_code,
+                           device,
+                           name_len + sizeof(WDMAUD_DEVICE_INFO),
+                           device,
+                           sizeof(WDMAUD_DEVICE_INFO),
+                           &bytes_returned,
+                           &overlap) )
+    {
+        DWORD error = GetLastError();
+
+        if (error != ERROR_IO_PENDING)
+        {
+            DPRINT1("FAILED in CallKernelDevice (error %d)\n", (int) error);
+
+            DUMP_WDMAUD_DEVICE_INFO(device);
+
+            result = TranslateWinError(error);
+            goto cleanup;
+        }
+
+        DPRINT("Waiting for overlap I/O event\n");
+
+        /* Wait for the IO to be complete */
+        WaitForSingleObject(overlap.hEvent, INFINITE);
+    }
+
+    result = MMSYSERR_NOERROR;
+    DPRINT("CallKernelDevice succeeded :)\n");
+
+    DUMP_WDMAUD_DEVICE_INFO(device);
+
+    cleanup :
+    {
+        /* Leave the critical section */
+        if ( using_critical_section )
+            LeaveCriticalSection(device->state->queue_critical_section);
+
+        if ( overlap.hEvent )
+            CloseHandle(overlap.hEvent);
+
+        return result;
+    }
+}
+
+
+static BOOL ChangeKernelDeviceState(BOOL enable)
+{
+    PWDMAUD_DEVICE_INFO device = NULL;
+    DWORD ioctl_code;
+    MMRESULT call_result;
+
+    ioctl_code = enable ? IOCTL_WDMAUD_HELLO : IOCTL_WDMAUD_GOODBYE;
+
+    device = CreateDeviceData(WDMAUD_AUX, L"");
+
+    if ( ! device )
+    {
+        DPRINT1("Couldn't create a new device instance structure\n");
+        return FALSE;
+    }
+
+    DPRINT("Setting device type and disabling critical section\n");
+
+    device->type = WDMAUD_AUX;
+    device->with_critical_section = FALSE;
+
+    DPRINT("Calling kernel device\n");
+
+    call_result = CallKernelDevice(device, ioctl_code, 0, 0);
+
+    DeleteDeviceData(device);
+
+    if ( call_result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Kernel device doesn't like us! (error %d)\n", (int) GetLastError());
+        return FALSE;
+    }
+    else
+    {
+        return TRUE;
+    }
+}
+
+
+BOOL EnableKernelInterface()
+{
+    /* SetupAPI variables/structures for querying device data */
+    SP_DEVICE_INTERFACE_DATA interface_data;
+    PSP_DEVICE_INTERFACE_DETAIL_DATA detail = NULL;
+    DWORD detail_size = 0;
+    HANDLE heap = NULL;
+    HDEVINFO dev_info;
+
+    /* Set to TRUE right at the end to define cleanup behaviour */
+    BOOL success = FALSE;
+
+    /* Don't want to be called more than once */
+    ASSERT(kernel_device_handle == NULL);
+
+    dev_info = SetupDiGetClassDevs(&KSCATEGORY_WDMAUD,
+                                   NULL,
+                                   NULL,
+                                   DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+
+    if ( ( ! dev_info ) || ( dev_info == INVALID_HANDLE_VALUE ) )
+    {
+        DPRINT1("SetupDiGetClassDevs failed\n");
+        goto cleanup;
+    }
+
+    interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+    if ( ! SetupDiEnumDeviceInterfaces(dev_info,
+                                       NULL,
+                                       &KSCATEGORY_WDMAUD,
+                                       0,
+                                       &interface_data) )
+    {
+        DPRINT1("SetupDiEnumDeviceInterfaces failed\n");
+        goto cleanup;
+    }
+
+    /*
+        We need to find out the size of the interface detail, before we can
+        actually retrieve the detail. This is a bit backwards, as the function
+        will return a status of success if the interface is invalid, but we
+        need it to fail with ERROR_INSUFFICIENT_BUFFER so we can be told how
+        much memory we need to allocate.
+    */
+
+    if ( SetupDiGetDeviceInterfaceDetail(dev_info,
+                                         &interface_data,
+                                         NULL,
+                                         0,
+                                         &detail_size,
+                                         NULL) )
+    {
+        DPRINT1("SetupDiGetDeviceInterfaceDetail shouldn't succeed!\n");
+        goto cleanup;
+    }
+
+    /*
+        Now we make sure the error was the one we expected. If not, bail out.
+    */
+
+    if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
+    {
+        DPRINT1("SetupDiGetDeviceInterfaceDetail returned wrong error code\n");
+        goto cleanup;
+    }
+
+    heap = GetProcessHeap();
+
+    if ( ! heap )
+    {
+        DPRINT1("Unable to get the process heap (error %d)\n",
+                (int)GetLastError());
+        goto cleanup;
+    }
+
+    detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) HeapAlloc(heap,
+                                                          HEAP_ZERO_MEMORY,
+                                                          detail_size);
+
+    if ( ! detail )
+    {
+        DPRINT1("Unable to allocate memory for the detail buffer (error %d)\n",
+                (int)GetLastError());
+        goto cleanup;
+    }
+
+    detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+    if ( ! SetupDiGetDeviceInterfaceDetail(dev_info,
+                                           &interface_data,
+                                           detail,
+                                           detail_size,
+                                           0,
+                                           NULL) )
+    {
+        DPRINT1("SetupDiGetDeviceInterfaceDetail failed\n");
+        goto cleanup;
+    }
+
+    DPRINT("Device path: %S\n", detail->DevicePath);
+
+    /* FIXME - params! */
+    kernel_device_handle = CreateFile(detail->DevicePath,
+                                      0xC0000000,
+                                      0,
+                                      0,
+                                      3,
+                                      0x40000080,
+                                      0);
+
+    DPRINT("kernel_device_handle == 0x%x\n", (int) kernel_device_handle);
+
+    if ( ! kernel_device_handle )
+    {
+        DPRINT1("Unable to open kernel device (error %d)\n",
+                (int) GetLastError());
+        goto cleanup;
+    }
+
+    /* Now we say hello to wdmaud.sys */
+    if ( ! ChangeKernelDeviceState(TRUE) )
+    {
+        DPRINT1("Couldn't enable the kernel device\n");
+        goto cleanup;
+    }
+
+    success = TRUE;
+
+    cleanup :
+    {
+        DPRINT("Cleanup - success == %d\n", (int) success);
+
+        if ( ! success )
+        {
+            DPRINT("Failing\n");
+
+            if ( kernel_device_handle )
+                CloseHandle(kernel_device_handle);
+        }
+
+        if ( heap )
+        {
+            if ( detail )
+                HeapFree(heap, 0, detail);
+        }
+    }
+
+    return success;
+}
+
+/*
+    Nothing here should fail, but if it does, we just give up and ASSERT(). If
+    we don't, we could be left in a limbo-state (eg: device open but disabled.)
+*/
+
+BOOL DisableKernelInterface()
+{
+    return ChangeKernelDeviceState(FALSE);
+
+#if 0 /* OLD CODE */
+    PWDMAUD_DEVICE_INFO device = NULL;
+    
+    ASSERT(kernel_device_handle);
+
+    /* Say goodbyte to wdmaud.sys */
+    device = CreateDeviceData(WDMAUD_AUX, L"");
+
+    ASSERT(device);
+
+    DPRINT("Setting device type and disabling critical section\n");
+
+    device->type = WDMAUD_AUX;
+    device->with_critical_section = FALSE;
+
+    DPRINT("Calling kernel device\n");
+
+    ASSERT( CallKernelDevice(device, IOCTL_WDMAUD_GOODBYE, 0, 0) == MMSYSERR_NOERROR );
+    ASSERT( CloseHandle(kernel_device_handle) );
+
+    DPRINT("Kernel interface now disabled\n");
+
+    kernel_device_handle = NULL;
+
+    DeleteDeviceData(device);
+#endif
+}
+
+
+/*
+    The use of this should be avoided...
+*/
+
+HANDLE GetKernelInterface()
+{
+    return kernel_device_handle;
+}
diff --git a/reactos/lib/wdmaud/threads.c b/reactos/lib/wdmaud/threads.c
new file mode 100644 (file)
index 0000000..1a949ed
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ *
+ * COPYRIGHT:           See COPYING in the top level directory
+ * PROJECT:             ReactOS Multimedia
+ * FILE:                lib/wdmaud/threads.c
+ * PURPOSE:             WDM Audio Support - Completion Threads
+ * PROGRAMMER:          Andrew Greenwood
+ * UPDATE HISTORY:
+ *                      Nov 18, 2005: Created
+ */
+
+#include "wdmaud.h"
+
+DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
+{
+    PWDMAUD_DEVICE_INFO device = (PWDMAUD_DEVICE_INFO) data;
+    MMRESULT result = MMSYSERR_ERROR;
+    PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
+    HANDLE overlap_event = NULL;
+    BOOL quit_loop = FALSE;
+
+    DPRINT("WaveCompletionThread started\n");
+
+    EnterCriticalSection(device->state->queue_critical_section);
+
+    while ( ! quit_loop )
+    {
+    result = ValidateDeviceInfoAndState(device);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Invalid device data or state structure!\n");
+        break;
+    }
+
+    result = ValidateDeviceStateEvents(device->state);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Invalid device state events\n");
+        break;
+    }
+
+    if ( device->state->open_descriptor )
+    {
+        DPRINT("No open descriptor found - running? %d\n", (int) device->state->is_running);
+
+        if ( ! device->state->is_running )
+        {
+            LeaveCriticalSection(device->state->queue_critical_section);
+
+            DPRINT("Waiting for queue_event\n");
+            WaitForSingleObject(device->state->queue_event, INFINITE);
+
+            DPRINT("field 24 == %d\n", (int) device->state->unknown_24);
+
+            /* What is the importance of this field */
+
+            if ( ! device->state->unknown_24 )
+            {
+                /* ?!?! Presumably this dequeues */
+                continue;
+            }
+
+            DPRINT("We broke out the loop! Yay!\n");
+
+            /* TODO! */
+
+            return TRUE; /* bleh */
+        }
+        else
+        {
+            /* TODO: STOP */
+            DPRINT("TODO: Stop the device\n");
+        }
+    }
+    else
+    {
+        PWAVEHDR wave_header = device->state->wave_header;
+
+        DPRINT("An open descriptor or wave header was found\n");
+
+        result = ValidateWaveHeader(wave_header);
+
+        if ( result == MMSYSERR_NOERROR )
+        {
+            prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) wave_header->reserved;
+
+            result = ValidateWavePreparationData(prep_data);
+        }
+
+        /* If both checks passed, the playback is complete */
+
+        if ( result != MMSYSERR_NOERROR )
+        {
+            result = MMSYSERR_NOERROR;
+
+            DPRINT("Activating the next header\n");
+
+            /* Activate the next header */
+            device->state->wave_header = wave_header->lpNext;
+
+            /* Reset this just in case */
+            prep_data = NULL;
+            /* continue; */
+        }
+        else
+        {
+            /* Should have valid prep data... */
+            overlap_event = prep_data->overlapped->hEvent;
+
+            /* Setting this will cause the loop to exit now */
+            quit_loop = TRUE;
+        }
+    }
+
+    }
+
+    /* We do this here in case there's an error - deadlock = bad! */
+    LeaveCriticalSection(device->state->queue_critical_section);
+
+    if ( result != MMSYSERR_NOERROR)
+        goto cleanup;
+
+    DPRINT("Waiting for object: %d\n", (int) overlap_event);
+    WaitForSingleObject(overlap_event, INFINITE);
+
+    cleanup :
+    {
+        DPRINT("Performing thread cleanup\n");
+
+        /* Yeah, like what? */
+
+        return result;
+    }
+}
+
+DWORD WINAPI MidiCompletionThreadStart(LPVOID data)
+{
+    DPRINT("MidiCompletionThread started\n");
+    return 0;
+}
+
+BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device)
+{
+    LPTHREAD_START_ROUTINE thread_start = NULL;
+
+    if ( IsWaveDeviceType(device->type) )
+        thread_start = WaveCompletionThreadStart;
+    else if ( IsMidiDeviceType(device->type) )
+        thread_start = MidiCompletionThreadStart;
+    else
+        return FALSE;   /* What did you just give me?! */
+
+    if ( device->state->unknown_30 != 0 )
+    {
+        DPRINT1("unknown_30 wasn't zero (it was %d)\n",
+                (int) device->state->unknown_30);
+    }
+
+    if ( device->state->thread )
+    {
+        DPRINT("Thread isn't null\n");
+    }
+    else
+    {
+        DPRINT("Thread is null\n");
+        
+        if ( ( (DWORD) device->state->queue_event != 0 ) &&
+             ( (DWORD) device->state->queue_event != MAGIC_42) &&
+             ( (DWORD) device->state->queue_event != MAGIC_43) )
+        {
+            /* Not fatal... */
+            DPRINT("Queue event is being overwritten!\n");
+            /* return FALSE; */
+        }
+
+        device->state->queue_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+        if ( ! device->state->queue_event )
+        {
+            /* TODO - hmm original doesn't seem to care what happens */
+        }
+
+        if ( ( (DWORD) device->state->exit_thread_event != 0x00000000 ) &&
+             ( (DWORD) device->state->exit_thread_event != 0x48484848 ) )
+        {
+            /* Not fatal... */
+            DPRINT("Exit Thread event is being overwritten!\n");
+            /* return FALSE; */
+        }
+
+        device->state->exit_thread_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+        if ( ! device->state->exit_thread_event )
+        {
+            /* TODO - hmm original doesn't seem to care what happens */
+        }
+
+        device->state->thread = NULL;
+
+        /* Should this be unknown_04? aka THREAD? */
+
+        device->state->thread = CreateThread(NULL, 0, thread_start, device, 0,
+                                             &device->state->thread_id);
+
+        if ( ! device->state->thread )
+        {
+            DPRINT1("Thread creation failed (error %d)\n",
+                    (int) GetLastError());
+
+            if ( device->state->queue_event )
+            {
+                CloseHandle(device->state->queue_event);
+                device->state->queue_event = NULL;
+            }
+
+            if ( device->state->exit_thread_event )
+            {
+                CloseHandle(device->state->exit_thread_event);
+                device->state->exit_thread_event = NULL;
+            }
+
+            return FALSE;
+        }
+
+        SetThreadPriority(device->state->thread, 0xf);
+
+        DPRINT("Thread created! - %d\n", (int) device->state->thread);
+
+        /* TODO: Set priority */
+}
+
+    return TRUE; /* TODO / FIXME */
+}
diff --git a/reactos/lib/wdmaud/user.c b/reactos/lib/wdmaud/user.c
new file mode 100644 (file)
index 0000000..d7d3b5e
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ *
+ * COPYRIGHT:           See COPYING in the top level directory
+ * PROJECT:             ReactOS Multimedia
+ * FILE:                lib/wdmaud/user.c
+ * PURPOSE:             WDM Audio Support - User Mode Interface
+ * PROGRAMMER:                 Andrew Greenwood
+ * UPDATE HISTORY:
+ *                                             Nov 18, 2005: Created
+ */
+
+/*
+ *  The GETDEVCAPS message parameters have different meaning in our case.
+ *  The second parameter usually indicates the struct size. But this has
+ *  been replaced by a pointer to the device path.
+ */
+
+
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include "wdmaud.h"
+
+
+APIENTRY LONG DriverProc(
+    DWORD DriverID,
+    HDRVR DriverHandle,
+    UINT Message,
+    LONG Param1,
+    LONG Param2)
+{
+       /*
+               Only DRV_ENABLE and DRV_DISABLE need special handling - everything else
+               is just implemented to aid in debugging.
+       */
+
+       DPRINT("DriverProc %d %d %d %d %d\n", (INT) DriverID, (int) DriverHandle, (int) Message, (int) Param1, (int) Param2);
+
+    switch(Message)
+    {
+               /*
+                       DRV_LOAD is the first message we receive, to say we've been loaded.
+                       DriverHandle is documented as being unused, but appears to be the
+                       number 3 (on my system, at least.)
+               */
+        case DRV_LOAD :
+            DPRINT("DRV_LOAD\n");
+            /* We should initialize the device list */
+            return DRV_OK; // dont need to do any more
+
+        case DRV_FREE :
+            /* We should stop all wave and MIDI playback */
+            DPRINT("DRV_FREE\n");
+            return DRV_OK;
+
+               /*
+                       DRV_OPEN is sent when WINMM wishes to open the driver. Param1
+                       can specify configuration information, but we don't need any.
+               */
+        case DRV_OPEN :
+            return DRV_OK;
+
+        case DRV_CLOSE :
+            DPRINT("DRV_CLOSE\n");
+            return DRV_OK;
+
+               /*
+            Enabling this driver causes the kernel-mode portion of WDMAUD to
+            be opened. We send a message to the kernel-mode driver to say that
+            we want to make use of it.
+
+            And, of course, when we are being disabled, we tell the kernel-mode
+            portion that we don't require its services any more, and close
+            the handle to it.
+               */
+
+        case DRV_ENABLE :
+        {
+            DPRINT("DRV_ENABLE\n");
+            return EnableKernelInterface();
+        }
+
+        case DRV_DISABLE :
+            DPRINT("DRV_DISABLE\n");
+            DisableKernelInterface();
+            return DRV_OK;
+
+        /*
+            We don't actually support configuration or installation, so these
+            could probably be safely pruned.
+        */
+
+        case DRV_QUERYCONFIGURE :
+            DPRINT("DRV_QUERYCONFIGURE\n");
+            return FALSE;
+
+        case DRV_CONFIGURE :
+            DPRINT("DRV_CONFIGURE\n");
+            return FALSE;
+
+        case DRV_INSTALL :
+            DPRINT("DRV_INSTALL\n");
+            return FALSE;      /* ok? */
+
+               /* DRV_REMOVE */
+
+        default :
+            DPRINT("?\n");
+            return DefDriverProc(DriverID, DriverHandle, Message, Param1, Param2);
+    };
+}
+
+void NotifyClient(
+    PWDMAUD_DEVICE_INFO device,
+    DWORD message,
+    DWORD p1,
+    DWORD p2
+)
+{
+    DPRINT("Calling client\n");
+
+    DriverCallback(device->client_callback,
+                   HIWORD(device->flags),
+                   (HDRVR) device->wave_handle,
+                   message,
+                   device->client_instance,
+                   0,
+                   0);
+}
+
+APIENTRY DWORD widMessage(
+    DWORD id,
+    DWORD message,
+    DWORD user,
+    DWORD p1,
+    DWORD p2
+)
+{
+       DPRINT("widMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+       switch(message)
+       {
+               case DRVM_INIT :
+                       DPRINT("WIDM_INIT\n");
+            return AddWaveInDevice((WCHAR*) p2);
+
+               case DRVM_EXIT :
+            DPRINT("WIDM_EXIT\n");
+            return RemoveWaveInDevice((WCHAR*) p2); /* FIXME */
+
+               case WIDM_GETNUMDEVS :
+                       DPRINT("WIDM_GETNUMDEVS\n");
+            return GetWaveInCount((WCHAR*) p1);
+       
+        case WIDM_GETDEVCAPS :
+            DPRINT("WIDM_GETDEVCAPS\n");
+            return GetWaveInCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+   };
+
+       return MMSYSERR_NOERROR;
+       return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD wodMessage(
+    DWORD id,
+    DWORD message,
+    DWORD user,
+    DWORD p1,
+    DWORD p2)
+{
+       DPRINT("wodMessage %d %d %d %d %d\n",
+           (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+       switch(message)
+       {
+        /*
+         *  DRVM_INIT
+         *    Parameter 1 : Not used
+         *    Parameter 2 : Topology path
+         */
+               case DRVM_INIT :
+                       DPRINT("DRVM_INIT\n");
+                       return AddWaveOutDevice((WCHAR*) p2);
+
+               case DRVM_EXIT :
+            DPRINT("DRVM_EXIT\n");
+            return RemoveWaveInDevice((WCHAR*) p2); /* FIXME? */
+
+        /*
+         *  WODM_GETNUMDEVS
+         *    Parameter 1 : Topology device path
+         *    Parameter 2 : Not used
+         */
+               case WODM_GETNUMDEVS :
+                       DPRINT("WODM_GETNUMDEVS\n");
+                       return GetWaveOutCount((WCHAR*) p1);
+               
+        /*
+         *  WODM_GETDEVCAPS
+         *    Parameter 1 : Pointer to a WAVEOUTCAPS struct
+         *    Parameter 2 : "Wave" device path? FIXME (NEW!)
+         */
+        case WODM_GETDEVCAPS :
+                       DPRINT("WODM_GETDEVCAPS\n");
+                       return GetWaveOutCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+
+        /*
+         *  WODM_OPEN
+         *    Parameter 1 : Pointer to a WAVEOPENDESC struct (the dnDevNode
+         *                  member holds a device path.)
+         *    Parameter 2 : Flags
+         */
+               case WODM_OPEN :
+                       DPRINT("WODM_OPEN\n");
+            return OpenWaveOut(id, (LPWAVEOPENDESC) p1, p2, user);
+               
+        case WODM_CLOSE :
+                       DPRINT("WODM_CLOSE\n");
+                       return CloseDevice((PWDMAUD_DEVICE_INFO) user);  /* ugh! */
+
+        case WODM_PREPARE :
+            DPRINT("WODM_PREPARE\n");
+            return PrepareWaveHeader((PWDMAUD_DEVICE_INFO) user,
+                                     (PWAVEHDR) p1);
+
+        case WODM_UNPREPARE :
+            DPRINT("WODM_UNPREPARE\n");
+            return UnprepareWaveHeader((PWAVEHDR) p1);
+
+        case WODM_WRITE :
+            DPRINT("WODM_WRITE\n");
+            return WriteWaveData((PWDMAUD_DEVICE_INFO) user,
+                                 (PWAVEHDR) p1);
+       }
+
+       DPRINT("* NOT IMPLEMENTED *\n");
+       return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD midMessage(
+    DWORD id,
+    DWORD message,
+    DWORD user,
+    DWORD p1,
+    DWORD p2
+)
+{
+       DPRINT("midMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+       switch(message)
+       {
+        case DRVM_INIT :
+            DPRINT("MIDM_INIT\n");
+            return AddMidiInDevice((WCHAR*) p2);
+
+        case DRVM_EXIT :
+            DPRINT("MIDM_EXIT\n");
+            return RemoveMidiInDevice((WCHAR*) p2); /* FIXME */
+
+        case MIDM_GETNUMDEVS :
+            DPRINT("MIDM_GETNUMDEVS\n");
+            return GetMidiInCount((WCHAR*) p1);
+    
+        case MIDM_GETDEVCAPS :
+            DPRINT("MIDM_GETDEVCAPS\n");
+            return GetMidiInCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+       };
+
+    DPRINT("* NOT IMPLEMENTED *\n");
+       return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD modMessage(
+    DWORD id,
+    DWORD message,
+    DWORD user,
+    DWORD p1,
+    DWORD p2
+)
+{
+       DPRINT("modMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+       switch(message)
+       {
+        case DRVM_INIT :
+            DPRINT("MODM_INIT\n");
+            return AddMidiOutDevice((WCHAR*) p2);
+
+        case DRVM_EXIT :
+            DPRINT("MODM_EXIT\n");
+            return RemoveMidiOutDevice((WCHAR*) p2); /* FIXME */
+
+        case MODM_GETNUMDEVS :
+            DPRINT("MODM_GETNUMDEVS\n");
+            return GetMidiOutCount((WCHAR*) p1);
+    
+        case MODM_GETDEVCAPS :
+            DPRINT("MODM_GETDEVCAPS\n");
+            return GetMidiOutCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+       };
+
+    DPRINT("* NOT IMPLEMENTED *\n");
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD mxdMessage(
+    DWORD id,
+    DWORD message,
+    DWORD user,
+    DWORD p1,
+    DWORD p2
+)
+{
+    DPRINT("mxdMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+       switch(message)
+       {
+        case DRVM_INIT :
+            DPRINT("MXDM_INIT\n");
+            return AddMixerDevice((WCHAR*) p2);
+            
+        case DRVM_EXIT :
+            DPRINT("MXDM_EXIT\n");
+            return RemoveMixerDevice((WCHAR*) p2); /* FIXME */
+
+        case MXDM_GETNUMDEVS :
+            DPRINT("MXDM_GETNUMDEVS\n");
+            return GetMixerCount((WCHAR*) p1);
+
+        case MXDM_GETDEVCAPS :
+            DPRINT("MXDM_GETDEVCAPS\n");
+            return GetMixerCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+
+        /* ... */
+       };
+
+    DPRINT("* NOT IMPLEMENTED *\n");
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD auxMessage(DWORD id, DWORD message, DWORD user, DWORD p1, DWORD p2)
+{
+       DPRINT("auxMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
+
+    switch(message)
+    {
+        case DRVM_INIT :
+            DPRINT("AUXDM_INIT\n");
+            return AddAuxDevice((WCHAR*) p2);
+            
+        case DRVM_EXIT :
+            DPRINT("AUXDM_EXIT\n");
+            return RemoveAuxDevice((WCHAR*) p2); /* FIXME */
+
+        case AUXDM_GETNUMDEVS :
+            DPRINT("AUXDM_GETNUMDEVS\n");
+            return GetAuxCount((WCHAR*) p1);
+
+        case AUXDM_GETDEVCAPS :
+            DPRINT("AUXDM_GETDEVCAPS\n");
+            return GetAuxCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1);
+
+        /* ... */
+    };
+
+    DPRINT("* NOT IMPLEMENTED *\n");
+       return MMSYSERR_NOTSUPPORTED;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
+{
+    DPRINT("DllMain called!\n");
+
+    if (Reason == DLL_PROCESS_ATTACH)
+    {
+               DisableThreadLibraryCalls(hInstance);
+    }
+
+    else if (Reason == DLL_PROCESS_DETACH)
+    {
+    }
+
+    return TRUE;
+}
+
+/* EOF */
diff --git a/reactos/lib/wdmaud/wavehdr.c b/reactos/lib/wdmaud/wavehdr.c
new file mode 100644 (file)
index 0000000..bce45cb
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ *
+ * COPYRIGHT:           See COPYING in the top level directory
+ * PROJECT:             ReactOS Multimedia
+ * FILE:                lib/wdmaud/wavehdr.c
+ * PURPOSE:             WDM Audio Support - Wave Header Manipulation
+ * PROGRAMMER:          Andrew Greenwood
+ * UPDATE HISTORY:
+ *                      Nov 18, 2005: Created
+ */
+
+#include <windows.h>
+#include "wdmaud.h"
+
+const char WAVE_PREPARE_DATA_SIG[4] = "WPPD";
+
+/*
+    ValidateWaveHeaderPreparation Overview :
+
+    First, check to see if we can write to the buffer given to us. Fail
+    if we can't (invalid parameter?)
+
+    Make sure the signature matches "WPPD". Fail if not (invalid param?)
+
+    Finally, validate the "overlapped" member, by checking to see if
+    that buffer is writable, and ensuring hEvent is non-NULL.
+*/
+
+MMRESULT ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data)
+{
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    ValidateWaveHeader Overview :
+
+    Check that the pointer is valid to write to. If not, signal invalid
+    parameter.
+
+    Perform a bitwise AND on the flags & 0xFFFFFFE0. If there are no bits
+    set, that's good. Otherwise give error and leave.
+
+    Check that the "reserved" member contains a valid WAVEPREPAREDATA
+    structure.
+
+    Return MMSYSERR_NOERROR if all's well!
+*/
+
+MMRESULT ValidateWaveHeader(PWAVEHDR header)
+{
+    DWORD flag_check;
+
+    if ( IsBadWritePtr(header, sizeof(WAVEHDR)) )
+    {
+        DPRINT1("Bad write pointer\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    flag_check = header->dwFlags & 0xffffffe0;  /* FIXME: Use flag names */
+
+    if ( flag_check )
+    {
+        DPRINT1("Unknown flags present\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    return ValidateWavePreparationData(
+                            (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved);
+}
+
+
+/*
+    PrepareWaveHeader Overview :
+
+    Validate the parameters.
+
+    Allocate and lock 12 bytes of global memory (fixed, zeroed) for the
+    WAVEPREPAREDATA structure.
+
+    If that fails, return immediately.
+
+    Otherwise, allocate 20 bytes of memory on the process heap (with no
+    special flags.) This is for the overlapped member of the WAVEPREPAREDATA
+    structure.
+
+    If the HeapAlloc failed, free the global memory and return.
+
+    Create an event with all parameters false, NULL or zero. This is to be
+    stored as the hEvent member of the OVERLAPPED structure.
+
+    If the event creation failed, free all memory used and return.
+
+    If it succeeded, set the WAVEPREPAREDATA signature to "WPPD", and set
+    the reserved member of "header" to the pointer of the WAVEPREPAREDATA
+    instance.
+
+    Return MMSYSERR_NOTSUPPORTED so that winmm does further processing.
+*/
+
+MMRESULT PrepareWaveHeader(
+    PWDMAUD_DEVICE_INFO device,
+    PWAVEHDR header
+)
+{
+    MMRESULT result = MMSYSERR_ERROR;
+    PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
+    HANDLE heap = NULL;
+
+    DPRINT("PrepareWaveHeader called\n");
+
+    result = ValidateDeviceInfoAndState(device);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Bad device info or device state\n");
+        return result;
+    }
+
+    /* Make sure we were actually given a header to process */
+    DPRINT("Checking that a header was supplied\n");
+
+    if ( ! header )
+    {
+        DPRINT1("Bad header\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    DPRINT("Checking flags\n");
+
+    /* I don't think Winmm would let this happen, but ya never know */
+    /*
+    ASSERT( ! header->dwFlags & WHDR_PREPARED );
+    ASSERT( header->dwFlags & WHDR_INQUEUE );
+    */
+
+    header->lpNext = NULL;
+    header->reserved = 0;
+
+    heap = GetProcessHeap();
+
+    if ( ! heap )
+    {
+        DPRINT1("Couldn't obtain the process heap (error %d)\n",
+                (int)GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto fail;
+    }
+
+    DPRINT("Allocating preparation data\n");
+
+    prep_data =
+        (PWDMAUD_WAVE_PREPARATION_DATA)
+            HeapAlloc(heap,
+                      HEAP_ZERO_MEMORY,
+                      sizeof(WDMAUD_WAVE_PREPARATION_DATA));
+
+    if ( ! prep_data )
+    {
+        DPRINT1("Couldn't lock global memory for preparation data (error %d)\n",
+                (int)GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto fail;
+    }
+
+    DPRINT("Allocating overlapped data\n");
+
+    prep_data->overlapped = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(OVERLAPPED));
+
+    if ( ! prep_data->overlapped )
+    {
+        DPRINT1("Couldn't allocate heap memory for overlapped structure (error %d)\n",
+                (int)GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto fail;
+    }
+
+    DPRINT("Creating overlapped event\n");
+
+    prep_data->overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+    if ( ! prep_data->overlapped->hEvent )
+    {
+        DPRINT1("Creation of overlapped event failed (error %d)\n",
+                (int)GetLastError());
+        result = MMSYSERR_NOMEM;
+        goto fail;
+    }
+
+    /* Copy the signature over and tie the prepare structure to the wave header */
+    DPRINT("Copying signature\n");
+    *prep_data->signature = *WAVE_PREPARE_DATA_SIG;
+    header->reserved = (DWORD) prep_data;
+
+    result = MMSYSERR_NOTSUPPORTED;
+    return result;
+
+    fail :
+    {
+        if ( heap )
+        {
+            if ( prep_data )
+            {
+                if ( prep_data->overlapped )
+                {
+                    if ( prep_data->overlapped->hEvent )
+                        CloseHandle(prep_data->overlapped->hEvent); /* ok? */
+    
+                    HeapFree(heap, 0, prep_data->overlapped);
+                }
+
+                HeapFree(heap, 0, prep_data);
+            }
+        }
+
+        return result;
+    }
+}
+
+/*
+    UnprepareWaveHeader
+
+    Winmm is intelligent enough to not call this function with a header that
+    is currently queued for playing!
+*/
+
+MMRESULT UnprepareWaveHeader(PWAVEHDR header)
+{
+    HANDLE heap = NULL;
+    MMRESULT result = MMSYSERR_ERROR;
+    PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
+
+    DPRINT("UnprepareHeader called\n");
+
+    /* Make sure we were actually given a header to process */
+
+    if ( ! header )
+    {
+        DPRINT1("Bad header supplied\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved;
+    result = ValidateWavePreparationData(prep_data);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Bad wave header preparation structure pointer\n");
+        return result;
+    }
+
+    /*
+        Overview :
+
+        Set "reserved" to NULL.
+
+        Close the event handle (overlapped->hEvent) and set that
+        structure member to NULL (not strictly necessary.)
+
+        Obtain the heap pointer and free the heap-allocated and global
+        memory allocated in PrepareWaveHeader.
+    */
+
+    /* We're about to free the preparation structure, so this needs to go */
+    header->reserved = 0;
+
+    CloseHandle(prep_data->overlapped->hEvent);
+
+    heap = GetProcessHeap();
+
+    if ( ! heap )
+    {
+        /* Not quite sure how we handle this */
+        DPRINT1("Couldn't obtain the process heap (error %d)\n",
+                (int)GetLastError());
+        result = MMSYSERR_ERROR;
+        goto cleanup;
+    }
+
+    if ( ! HeapFree(heap, 0, prep_data->overlapped) )
+    {
+        DPRINT1("Unable to free the OVERLAPPED memory (error %d)\n",
+                (int)GetLastError());
+        /* This shouldn't happen! */
+        ASSERT(FALSE);
+    }
+
+    /* Overwrite the signature (structure will be invalid from now on) */
+    ZeroMemory(prep_data->signature, 4);
+
+    if ( ! HeapFree(heap, 0, prep_data) )
+    {
+        DPRINT1("Unable to free the preparation structure memory (error %d)\n",
+                (int)GetLastError());
+        /* This shouldn't happen! */
+        ASSERT(FALSE);
+    }
+
+    /* Always return like this so winmm thinks we didn't do anything */
+
+    DPRINT("Header now unprepared.\n");
+    result = MMSYSERR_NOTSUPPORTED;
+
+    cleanup :
+        return result;
+}
+
+/* Not sure about this */
+MMRESULT CompleteWaveHeader(PWAVEHDR header)
+{
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+
+/*
+    SubmitWaveHeader Overview :
+
+    This may span 2 functions (this one and and another "SubmitHeader")
+
+    First, validate the device info, then the state.
+
+    Validate the header, followed by the reserved member.
+
+    Fail if INQUEUE flag is set in header, or if PREPARED is not set in
+    header.
+
+    AND the flags with PREPARED, BEGINLOOP, ENDLOOP and INQUEUE. OR the
+    result with INQUEUE.
+
+    Enter the csQueue critical section.
+
+    Check if the device state's "open descriptor" member is NULL or not.
+    If we're adding an extra buffer, it will already have been allocated.
+
+    If the open descriptor is NULL:
+
+        If it's NULL, set "opendesc" to point to the wave header (?!)
+
+        If the state structure's "hevtQueue" member isn't NULL, compare it's
+        value to 0x43434343h and 0x42424242h. If it's not NULL or one of
+        those values, set the event.
+
+    If the open descriptor is NOT NULL:
+
+        Check the header's lpNext member. If it's not NULL, check that
+        structure's lpNext member, and so on, until a NULL entry is
+        found.
+
+        Set the NULL entry to point to our header.
+
+    Leave the csQueue critical section.
+
+    ** SUBMIT THE HEADER ** TODO **
+
+    If submission failed:
+
+        AND the flags with 0xFFFFFFEFh. If csQueue is set in the target
+        (the header who's lpNext was NULL), set it to NULL. Set the open
+        descriptor of state to NULL, too. And fail, of course.
+
+    If the device state is PAUSED or RUNNING, we must fail.
+
+    Otherwise, reset the device and set it as RUNNING. This may be done by
+    our caller (wodMessage, etc.)
+    0x1d8104 is used for wave in
+    0x1d8148 is used for wave out?
+
+    SetDeviceState should now be called with the above IOCTL code and the
+    device info structure.
+*/
+
+static MMRESULT ValidateWriteWaveDataParams(
+    PWDMAUD_DEVICE_INFO device,
+    PWAVEHDR header
+)
+{
+    MMRESULT result;
+
+    result = ValidateWaveHeader(header);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Bad wave header supplied\n");
+        return result;
+    }
+
+    /*
+        We don't want to queue something already queued, and we don't want
+        to queue something that hasn't been prepared. Who knows what garbage
+        might be sent to us?!
+    */
+
+    if ( header->dwFlags & WHDR_INQUEUE )
+    {
+        DPRINT1("This header is already queued!\n");
+        return MMSYSERR_INVALFLAG;
+    }
+
+    if ( ! header->dwFlags & WHDR_PREPARED )
+    {
+        DPRINT1("This header isn't prepared!\n");
+        return WAVERR_UNPREPARED;
+    }
+
+    result = ValidateDeviceInfoAndState(device);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Bad device info or device state supplied\n");
+        return result;
+    }
+
+    return result;
+}
+
+MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
+{
+    MMRESULT result = MMSYSERR_ERROR;
+    DWORD io_result = 0;
+    PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
+    PWDMAUD_DEVICE_INFO clone;
+
+    /* For the DeviceIoControl later */
+    DWORD ioctl_code;
+    DWORD bytes_returned;
+
+    DPRINT("SubmitWaveHeader called\n");
+
+    result = ValidateWriteWaveDataParams(device, header);
+
+    if ( result != MMSYSERR_NOERROR )
+        return result;
+
+    /*
+        TODO: Check to see if we actually get called with bad flags!
+    */
+
+    /* Retrieve our precious data from the reserved member */
+    prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved;
+
+    result = ValidateWavePreparationData(prep_data);
+
+    if ( result != MMSYSERR_NOERROR )
+    {
+        DPRINT1("Bad wave preparation structure supplied\n");
+        return result;
+    }
+
+    DPRINT("Flags == 0x%x\n", (int) header->dwFlags);
+
+    /* Mask the "done" flag */
+    CLEAR_WAVEHDR_FLAG(header, WHDR_DONE);
+    /* header->dwFlags &= ~WHDR_DONE; */
+    /* ...and set the queue flag! */
+    SET_WAVEHDR_FLAG(header, WHDR_INQUEUE);
+    /* header->dwFlags |= WHDR_INQUEUE; */
+
+    DPRINT("Flags == 0x%x\n", (int) header->dwFlags);
+
+    EnterCriticalSection(device->state->queue_critical_section);
+
+    if ( ! device->state->wave_header )
+    {
+        DPRINT("Device state wave_header is NULL\n");
+
+        device->state->wave_header = header;
+
+        /* My, what pretty symmetry you have... */
+
+        DPRINT("Queue event == %d\n", (int) device->state->queue_event);
+
+        if ( ( (DWORD) device->state->queue_event != 0 ) &&
+             ( (DWORD) device->state->queue_event != MAGIC_42 ) &&
+             ( (DWORD) device->state->queue_event != MAGIC_43 ) )
+        {
+            DPRINT("Setting queue event\n");
+            SetEvent(device->state->queue_event);
+        }
+    }
+    else
+    {
+        DPRINT("Device state open_descriptor is NOT NULL\n");
+        /* TODO */
+        ASSERT(FALSE);
+    }
+
+    LeaveCriticalSection(device->state->queue_critical_section);
+
+    /* CODE FROM SUBMITWAVEHEADER */
+
+    DPRINT("Now we actually perform header submission\n");
+
+    clone = CloneDeviceData(device);
+
+    if ( ! clone )
+    {
+        /* Safe to do this? */
+        DPRINT1("Clone creation failed\n");
+        return MMSYSERR_NOMEM;
+    }
+
+    if ( ! IsHeaderPrepared(header) )
+    {
+        DPRINT1("Unprepared header!\n");
+        DeleteDeviceData(clone);
+        return MMSYSERR_INVALPARAM;
+    }
+
+    /* Not sure what this is for */
+    prep_data->offspring = clone;
+
+    /* The modern version of WODM_WRITE, I guess ;) */
+    clone->ioctl_param1 = sizeof(WAVEHDR);
+    clone->ioctl_param2 = (DWORD) header;
+
+    ioctl_code = clone->type == WDMAUD_WAVE_IN
+                                ? 0x1d8150   /* FIXME */
+                                : IOCTL_WDMAUD_SUBMIT_WAVE_HDR;
+
+    DPRINT("Overlapped == 0x%x\n", (int) prep_data->overlapped);
+
+    /*
+        FIXME:
+        For wave input to work, we may need to pass different parameters.
+    */
+
+    /* We now send the header to the driver */
+
+    io_result =
+        DeviceIoControl(GetKernelInterface(),
+                        ioctl_code,
+                        clone,
+                        sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(clone->path) * 2),
+                        clone,
+                        sizeof(WDMAUD_DEVICE_INFO),
+                        &bytes_returned,    /* ... */
+                        prep_data->overlapped);
+
+    DPRINT("Wave header submission result : %d\n", (int) io_result);
+
+    if ( io_result != STATUS_SUCCESS )
+    {
+        DPRINT1("Wave header submission FAILED! (error %d)\n", (int) io_result);
+
+        CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
+        device->state->queue_critical_section = NULL;
+        device->state->wave_header = NULL;
+
+        return TranslateWinError(io_result);
+    }
+
+    /* CallKernelDevice(clone, ioctl_code, 0x20, (DWORD) header); */
+
+    DPRINT("Creating completion thread\n");
+    if ( ! CreateCompletionThread(device) )
+    {
+        DPRINT1("Couldn't create completion thread\n");
+
+        CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
+        device->state->queue_critical_section = NULL;
+        device->state->wave_header = NULL;
+
+        return MMSYSERR_ERROR; /* Care to be more specific? */
+    }
+
+
+    /* TODO */
+
+    DPRINT("applying hacks\n");
+
+    DPRINT("Running %d paused %d\n", (int)device->state->is_running, (int)device->state->is_paused);
+#if 1
+    /* HACK */
+    DPRINT("%d\n", (int)
+        DeviceIoControl(GetKernelInterface(),
+                        0x1d8104,
+                        device,
+                        sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
+                        device,
+                        sizeof(WDMAUD_DEVICE_INFO),
+                        &bytes_returned,    /* ... */
+                        prep_data->overlapped) );
+
+    DPRINT("Running %d paused %d\n", (int)device->state->is_running, (int)device->state->is_paused);
+
+#if 0 /* on error */
+    DPRINT("%d\n", (int)
+        DeviceIoControl(GetKernelInterface(),
+                        0x1d8148,
+                        device,
+                        sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
+                        device,
+                        sizeof(WDMAUD_DEVICE_INFO),
+                        &bytes_returned,    /* ... */
+                        prep_data->overlapped) );
+#endif
+#endif
+
+    result = MMSYSERR_NOERROR;
+
+    return result;
+}
diff --git a/reactos/lib/wdmaud/wdmaud.def b/reactos/lib/wdmaud/wdmaud.def
new file mode 100755 (executable)
index 0000000..8596325
--- /dev/null
@@ -0,0 +1,15 @@
+; $Id: wdmaud.def 12852 2005-01-06 13:58:04Z mf $
+;
+; wdmaud.def
+;
+; ReactOS Operating System
+;
+LIBRARY wdmaud.drv
+EXPORTS
+DriverProc@20
+;widMessage@20
+wodMessage@20
+;midMessage@20
+;modMessage@20
+;mxdMessage@20
+;auxMessage@20
diff --git a/reactos/lib/wdmaud/wdmaud.h b/reactos/lib/wdmaud/wdmaud.h
new file mode 100755 (executable)
index 0000000..589acd9
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/wdmaud/wdmaud.h
+ * PURPOSE:              WDM Audio Support - Common header
+ * PROGRAMMER:           Andrew Greenwood
+ * UPDATE HISTORY:
+ *                       Nov 12, 2005: Declarations for debugging + interface
+ */
+
+#ifndef __WDMAUD_PRIVATE_H__
+#define __WDMAUD_PRIVATE_H__
+
+/* Debugging */
+
+
+/*
+       Some of this stuff belongs in ksmedia.h or other such global includes.
+*/
+
+#include <stdio.h>
+
+#include <debug.h>
+#include <ddk/ntddk.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+/* HACK! */
+#define DbgPrint printf
+
+/* TODO: Put elsewhere */
+#if 0
+typedef struct tagWAVEOUTCAPS2A {
+    WORD    wMid;                  /* manufacturer ID */
+    WORD    wPid;                  /* product ID */
+    MMVERSION vDriverVersion;      /* version of the driver */
+    CHAR    szPname[MAXPNAMELEN];  /* product name (NULL terminated string) */
+    DWORD   dwFormats;             /* formats supported */
+    WORD    wChannels;             /* number of sources supported */
+    WORD    wReserved1;            /* packing */
+    DWORD   dwSupport;             /* functionality supported by driver */
+    GUID    ManufacturerGuid;      /* for extensible MID mapping */
+    GUID    ProductGuid;           /* for extensible PID mapping */
+    GUID    NameGuid;              /* for name lookup in registry */
+} WAVEOUTCAPS2A, *PWAVEOUTCAPS2A, *NPWAVEOUTCAPS2A, *LPWAVEOUTCAPS2A;
+typedef struct tagWAVEOUTCAPS2W {
+    WORD    wMid;                  /* manufacturer ID */
+    WORD    wPid;                  /* product ID */
+    MMVERSION vDriverVersion;      /* version of the driver */
+    WCHAR   szPname[MAXPNAMELEN];  /* product name (NULL terminated string) */
+    DWORD   dwFormats;             /* formats supported */
+    WORD    wChannels;             /* number of sources supported */
+    WORD    wReserved1;            /* packing */
+    DWORD   dwSupport;             /* functionality supported by driver */
+    GUID    ManufacturerGuid;      /* for extensible MID mapping */
+    GUID    ProductGuid;           /* for extensible PID mapping */
+    GUID    NameGuid;              /* for name lookup in registry */
+} WAVEOUTCAPS2W, *PWAVEOUTCAPS2W, *NPWAVEOUTCAPS2W, *LPWAVEOUTCAPS2W;
+#ifdef UNICODE
+typedef WAVEOUTCAPS2W WAVEOUTCAPS2;
+typedef PWAVEOUTCAPS2W PWAVEOUTCAPS2;
+typedef NPWAVEOUTCAPS2W NPWAVEOUTCAPS2;
+typedef LPWAVEOUTCAPS2W LPWAVEOUTCAPS2;
+#else
+typedef WAVEOUTCAPS2A WAVEOUTCAPS2;
+typedef PWAVEOUTCAPS2A PWAVEOUTCAPS2;
+typedef NPWAVEOUTCAPS2A NPWAVEOUTCAPS2;
+typedef LPWAVEOUTCAPS2A LPWAVEOUTCAPS2;
+#endif // UNICODE
+#endif
+
+
+
+/*
+    Handy macros
+*/
+
+#define REPORT_MM_RESULT(message, success) \
+    DPRINT("%s %s\n", message, success == MMSYSERR_NOERROR ? "succeeded" : "failed")
+
+
+/*
+       Private IOCTLs shared between wdmaud.sys and wdmaud.drv
+    
+    TO ADD/MODIFY:
+        IOCTL_WDMAUD_OPEN_PIN
+        IOCTL_WDMAUD_WAVE_OUT_WRITE_PIN
+        IOCTL_WDMAUD_WAVE_IN_READ_PIN
+        IOCTL_WDMAUD_MIXER_CLOSE
+        IOCTL_WDMAUD_MIXER_OPEN
+        IOCTL_WDMAUD_MIDI_IN_READ_PIN
+        IOCTL_WDMAUD_MIXER_GETLINEINFO
+        IOCTL_WDMAUD_MIXER_GETHARDWAREEVENTDATA
+        IOCTL_WDMAUD_MIXER_SETCONTROLDETAILS
+        IOCTL_WDMAUD_MIXER_GETCONTROLDETAILS
+        IOCTL_WDMAUD_MIXER_GETLINECONTROLS
+*/
+
+/* 0x1d8000 */
+#define IOCTL_WDMAUD_HELLO \
+       CTL_CODE(FILE_DEVICE_SOUND, 0x0000, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+#define IOCTL_WDMAUD_ADD_DEVICE                        0x1d8004
+#define IOCTL_WDMAUD_REMOVE_DEVICE             0x1d8008
+#define IOCTL_WDMAUD_GET_CAPABILITIES  0x1d800c
+#define IOCTL_WDMAUD_GET_DEVICE_COUNT  0x1d8010
+#define IOCTL_WDMAUD_OPEN_DEVICE               0x1d8014
+#define IOCTL_WDMAUD_CLOSE_DEVICE              0x1d8018
+#define IOCTL_WDMAUD_AUX_GET_VOLUME            0x1d801c
+#define IOCTL_WDMAUD_AUX_SET_VOLUME            0x1d8020
+
+/* 0x1d8024 */
+#define IOCTL_WDMAUD_GOODBYE \
+       CTL_CODE(FILE_DEVICE_SOUND, 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+#define IOCTL_WDMAUD_SET_PREFERRED             0x1d8028
+
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN1        0x1d8100
+#define IOCTL_WDMAUD_WAVE_OUT_START     0x1d8104    /* Reset wave in? */
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN3        0x1d8108    /* Start wave in? */
+#define IOCTL_WDMAUD_BREAK_LOOP         0x1d810c
+
+#define IOCTL_WDMAUD_GET_WAVE_OUT_POS  0x1d8110        /* Does something funky */
+#define IOCTL_WDMAUD_SET_VOLUME                        0x1d8114        /* Hasn't this already been covered? */
+#define IOCTL_WDMAUD_UNKNOWN1                  0x1d8018        /* Not used by wdmaud.drv */
+#define IOCTL_WDMAUD_SUBMIT_WAVE_HDR   0x1d811c
+
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN4        0x1d8140
+#define IOCTL_WDMAUD_WAVE_IN_START      0x1d8144    /* Reset wave out? */
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN6        0x1d8148    /* Start wave out? */
+
+#define IOCTL_WDMAUD_MIDI_OUT_SHORT_MESSAGE \
+                                        0x1d8204       /* Wrong description? */
+
+#define IOCTL_WDMAUD_UNKNOWN2                  0x1d8208
+
+#define IOCTL_WDMAUD_MIDI_OUT_LONG_MESSAGE \
+                                        0x1d820c
+
+#define IOCTL_WDMAUD_SUBMIT_MIDI_HDR   0x1d8210
+
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN7        0x1d8240
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN8        0x1d8244
+#define IOCTL_WDMAUD_SET_STATE_UNKNOWN9        0x1d8248
+
+#define IOCTL_WDMAUD_READ_MIDI_DATA            0x1d824c
+#define IOCTL_WDMAUD_MIDI_MESSAGE              0x1d8300        /* Wrong description? */
+
+#define IOCTL_WDMAUD_MIXER_UNKNOWN1            0x1d8310
+#define IOCTL_WDMAUD_MIXER_UNKNOWN2            0x1d8314
+#define IOCTL_WDMAUD_MIXER_UNKNOWN3            0x1d8318
+
+
+/*
+       Device Types
+*/
+
+enum
+{
+    WDMAUD_WAVE_IN = 0,
+       WDMAUD_WAVE_OUT,
+
+       WDMAUD_MIDI_IN,
+       WDMAUD_MIDI_OUT,
+
+    WDMAUD_MIXER,
+
+       WDMAUD_AUX,
+
+    /* For range checking */
+    WDMAUD_MIN_DEVICE_TYPE = WDMAUD_WAVE_IN,
+    WDMAUD_MAX_DEVICE_TYPE = WDMAUD_AUX
+};
+
+/*
+    Some macros for device type matching and checking
+*/
+
+#define IsWaveInDeviceType(device_type)     (device_type == WDMAUD_WAVE_IN)
+#define IsWaveOutDeviceType(device_type)    (device_type == WDMAUD_WAVE_OUT)
+#define IsMidiInDeviceType(device_type)     (device_type == WDMAUD_MIDI_IN)
+#define IsMidiOutDeviceType(device_type)    (device_type == WDMAUD_MIDI_OUT)
+#define IsMixerDeviceType(device_type)      (device_type == WDMAUD_MIXER)
+#define IsAuxDeviceType(device_type)        (device_type == WDMAUD_AUX)
+
+#define IsWaveDeviceType(device_type) \
+    (IsWaveInDeviceType(device_type) || IsWaveOutDeviceType(device_type))
+#define IsMidiDeviceType(device_type) \
+    (IsMidiInDeviceType(device_type) || IsMidiOutDeviceType(device_type))
+    
+#define IsValidDeviceType(device_type) \
+    (device_type >= WDMAUD_MIN_DEVICE_TYPE && \
+     device_type <= WDMAUD_MAX_DEVICE_TYPE)
+
+/*
+    The various "caps" (capabilities) structures begin with the same members,
+    so a generic structure is defined here which can be accessed independently
+    of a device type.
+*/
+
+typedef struct
+{
+    WORD wMid;
+    WORD wPid;
+    MMVERSION vDriverVersion;
+    WCHAR szPname[MAXPNAMELEN];
+} COMMONCAPSW, *LPCOMMONCAPSW;
+
+/* Unicode, anyone? */
+typedef COMMONCAPSW COMMONCAPS;
+typedef LPCOMMONCAPSW LPCOMMONCAPS;
+
+/* More abstraction */
+typedef LPVOID PWDMAUD_HEADER;
+
+/*
+    There are also various "opendesc" structures, but these don't have any
+    common members. Regardless, this typedef simply serves as a placeholder
+    to indicate that to access the members, it should be cast accordingly.
+*/
+typedef struct OPENDESC *LPOPENDESC;
+
+typedef struct
+{
+    DWORD sample_size;
+    HANDLE thread;
+    DWORD thread_id;
+    union
+    {
+        LPWAVEOPENDESC open_descriptor;
+        LPWAVEHDR wave_header;
+    };
+    DWORD unknown_10;   /* pointer to something */
+    DWORD unknown_14;
+    LPCRITICAL_SECTION queue_critical_section;
+    HANDLE queue_event;
+    HANDLE exit_thread_event;
+    DWORD unknown_24;
+    DWORD is_paused;
+    DWORD is_running;
+    DWORD unknown_30;
+    DWORD unknown_34;
+    DWORD unknown_38;
+    char signature[4];
+} WDMAUD_DEVICE_STATE, *PWDMAUD_DEVICE_STATE;
+
+typedef struct
+{
+    DWORD unknown_00;
+    DWORD id;
+    DWORD type;
+    HWAVE wave_handle;
+    DWORD client_instance;
+    DWORD client_callback;
+    DWORD unknown_18;
+    DWORD flags;
+    DWORD ioctl_param2;
+    DWORD ioctl_param1;
+    DWORD with_critical_section;
+    DWORD string_2c;
+    DWORD unknown_30;
+    DWORD playing_notes;
+    DWORD unknown_38;
+    DWORD unknown_3c;
+    DWORD unknown_40;
+    DWORD unknown_44;
+    DWORD unknown_48;
+    DWORD unknown_4C;
+    DWORD unknown_50;
+    DWORD beef;
+    PWDMAUD_DEVICE_STATE state;
+    char signature[4];
+    WCHAR path[1];
+} WDMAUD_DEVICE_INFO, *PWDMAUD_DEVICE_INFO;
+
+typedef struct
+{
+    PWDMAUD_DEVICE_INFO offspring;  /* not sure about this */
+    LPOVERLAPPED overlapped;
+    char signature[4];
+} WDMAUD_WAVE_PREPARATION_DATA, *PWDMAUD_WAVE_PREPARATION_DATA;
+
+/* Ugh... */
+
+typedef struct
+{
+    DWORD cbSize;   /* Maybe? */
+    
+} WDMAUD_CAPS, *PWDMAUD_CAPS;
+
+/*
+    Not quite sure what these are/do yet
+*/
+
+#define MAGIC_42    0x42424242  /* Queue critical section */
+#define MAGIC_43    0x43434343  /* Queue critical section */
+#define MAGIC_48    0x48484848  /* Exit-thread event */
+
+#define IsQueueMagic(test_value) \
+    ( ( (DWORD)test_value == MAGIC_42 ) || ( (DWORD)test_value == MAGIC_43) )
+
+
+/*
+    This should eventually be removed, but is used so we can be nosey and see
+    what the kernel-mode wdmaud.sys is doing with our structures!
+*/
+
+#ifdef DUMP_WDMAUD_STRUCTURES
+
+#define DUMP_MEMBER(struct, member) \
+    DPRINT("%s : %d [0x%x]\n", #member, (int) struct->member, (int) struct->member);
+
+#define DUMP_WDMAUD_DEVICE_INFO(info) \
+    { \
+        DPRINT("-- %s --\n", #info); \
+        DUMP_MEMBER(info, unknown_00); \
+        DUMP_MEMBER(info, id); \
+        DUMP_MEMBER(info, type); \
+        DUMP_MEMBER(info, wave_handle); \
+        DUMP_MEMBER(info, client_instance); \
+        DUMP_MEMBER(info, client_callback); \
+        DUMP_MEMBER(info, unknown_18); \
+        DUMP_MEMBER(info, flags); \
+        DUMP_MEMBER(info, ioctl_param2); \
+        DUMP_MEMBER(info, ioctl_param1); \
+        DUMP_MEMBER(info, with_critical_section); \
+        DUMP_MEMBER(info, string_2c); \
+        DUMP_MEMBER(info, unknown_30); \
+        DUMP_MEMBER(info, playing_notes); \
+        DUMP_MEMBER(info, unknown_38); \
+        DUMP_MEMBER(info, unknown_3c); \
+        DUMP_MEMBER(info, unknown_40); \
+        DUMP_MEMBER(info, unknown_44); \
+        DUMP_MEMBER(info, unknown_48); \
+        DUMP_MEMBER(info, unknown_4C); \
+        DUMP_MEMBER(info, unknown_50); \
+        DUMP_MEMBER(info, beef); \
+        DUMP_MEMBER(info, state); \
+        DUMP_MEMBER(info, signature); \
+    }
+
+#else
+
+#define DUMP_MEMBER(struct, member)
+#define DUMP_WDMAUD_DEVICE_INFO(info)
+
+#endif
+
+
+/* Helper (helper.c) funcs */
+
+MMRESULT TranslateWinError(DWORD error);
+#define GetLastMmError()        TranslateWinError(GetLastError());
+
+
+/* user.c */
+void NotifyClient(
+    PWDMAUD_DEVICE_INFO device,
+    DWORD message,
+    DWORD p1,
+    DWORD p2);
+
+/* #define NotifyClient(device, message, p1, p2) ? */
+
+
+/* kernel.c */
+
+BOOL EnableKernelInterface();
+BOOL DisableKernelInterface();
+HANDLE GetKernelInterface();
+
+MMRESULT CallKernelDevice(
+    PWDMAUD_DEVICE_INFO device,
+    DWORD ioctl_code,
+    DWORD param1,
+    DWORD param2);
+
+/* devices.c */
+BOOL IsValidDevicePath(WCHAR* path);
+MMRESULT ValidateDeviceInfo(PWDMAUD_DEVICE_INFO info);
+MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state);
+MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state);
+MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info);
+
+PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path);
+PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original);
+void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data);
+/* mixer ... */
+
+MMRESULT ModifyDevicePresence(
+    CHAR device_type,
+    WCHAR* device_path,
+    BOOL adding);
+
+#define AddDevice(device_type, device_path) \
+       ModifyDevicePresence(device_type, device_path, TRUE)
+
+#define AddWaveInDevice(device_path) \
+        AddDevice(WDMAUD_WAVE_IN, device_path)
+#define AddWaveOutDevice(device_path) \
+        AddDevice(WDMAUD_WAVE_OUT, device_path)
+#define AddMidiInDevice(device_path) \
+        AddDevice(WDMAUD_MIDI_IN,  device_path)
+#define AddMidiOutDevice(device_path) \
+        AddDevice(WDMAUD_MIDI_OUT, device_path)
+#define AddMixerDevice(device_path) \
+        AddDevice(WDMAUD_MIXER, device_path)
+#define AddAuxDevice(device_path) \
+        AddDevice(WDMAUD_AUX, device_path)
+
+#define RemoveDevice(device_type, device_path) \
+        ModifyDevicePresence(device_type, device_path, FALSE)
+
+#define RemoveWaveInDevice(device_path) \
+        RemoveDevice(WDMAUD_WAVE_IN, device_path)
+#define RemoveWaveOutDevice(device_path) \
+        RemoveDevice(WDMAUD_WAVE_OUT, device_path)
+#define RemoveMidiInDevice(device_path) \
+        RemoveDevice(WDMAUD_MIDI_IN, device_path)
+#define RemoveMidiOutDevice(device_path) \
+        RemoveDevice(WDMAUD_MIDI_OUT, device_path)
+#define RemoveMixerDevice(device_path) \
+        RemoveDevice(WDMAUD_MIXER, device_path)
+#define RemoveAuxDevice(device_path) \
+        RemoveDevice(WDMAUD_AUX, device_path)
+
+
+DWORD GetDeviceCount(CHAR device_type, WCHAR* device_path);
+#define GetWaveInCount(device_path)  GetDeviceCount(WDMAUD_WAVE_IN,  device_path)
+#define GetWaveOutCount(device_path) GetDeviceCount(WDMAUD_WAVE_OUT, device_path)
+#define GetMidiInCount(device_path)  GetDeviceCount(WDMAUD_MIDI_IN,  device_path)
+#define GetMidiOutCount(device_path) GetDeviceCount(WDMAUD_MIDI_OUT, device_path)
+#define GetMixerCount(device_path)   GetDeviceCount(WDMAUD_MIXER,    device_path)
+#define GetAuxCount(device_path)     GetDeviceCount(WDMAUD_AUX,      device_path)
+
+MMRESULT GetDeviceCapabilities(
+    CHAR device_type,
+    DWORD device_id,
+    WCHAR* device_path,
+    LPCOMMONCAPS caps);
+
+#define GetWaveInCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_WAVE_IN, device_path, caps);
+#define GetWaveOutCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_WAVE_OUT, device_path, caps);
+#define GetMidiInCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_MIDI_IN, device_path, caps);
+#define GetMidiOutCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_MIDI_OUT, device_path, caps);
+#define GetMixerCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_MIXER, device_path, caps);
+#define GetAuxCapabilities(id, device_path, caps) \
+        GetDeviceCapabilities(id, WDMAUD_AUX, device_path, caps);
+
+MMRESULT OpenWaveDevice(
+    CHAR device_type,
+    DWORD device_id,
+    LPWAVEOPENDESC open_details,
+    DWORD flags,
+    DWORD user_data);
+
+#define OpenWaveOut(id, open_details, flags, user_data) \
+        OpenWaveDevice(WDMAUD_WAVE_OUT, id, open_details, flags, user_data);
+
+MMRESULT CloseDevice(
+    PWDMAUD_DEVICE_INFO device
+);
+
+
+/* wavehdr.h */
+
+#define SET_WAVEHDR_FLAG(header, flag) \
+    header->dwFlags |= flag
+
+#define CLEAR_WAVEHDR_FLAG(header, flag) \
+    header->dwFlags &= ~flag
+
+MMRESULT ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data);
+
+MMRESULT ValidateWaveHeader(PWAVEHDR header);
+
+MMRESULT PrepareWaveHeader(
+    PWDMAUD_DEVICE_INFO device,
+    PWAVEHDR header
+);
+
+MMRESULT UnprepareWaveHeader(PWAVEHDR header);
+
+#define IsHeaderPrepared(header) \
+    ( header->reserved != 0 )
+
+MMRESULT CompleteWaveHeader(PWAVEHDR header);
+
+MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header);
+
+
+/* threads.c */
+
+BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device);
+
+
+/* MORE... */
+
+#endif
diff --git a/reactos/lib/wdmaud/wdmaud.rc b/reactos/lib/wdmaud/wdmaud.rc
new file mode 100755 (executable)
index 0000000..06336a7
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "WDM Audio System (Legacy Support)\0"
+#define REACTOS_STR_INTERNAL_NAME      "wdmaud\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "wdmaud.drv\0"
+#include <reactos/version.rc>
diff --git a/reactos/lib/wdmaud/wdmaud.xml b/reactos/lib/wdmaud/wdmaud.xml
new file mode 100755 (executable)
index 0000000..95eea53
--- /dev/null
@@ -0,0 +1,20 @@
+<module name="wdmaud" type="win32dll" extension=".drv" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv">
+       <importlibrary definition="wdmaud.def" />
+       <include base="wdmaud">.</include>
+       <define name="__USE_W32API" />
+       <define name="_DISABLE_TIDENTS" />
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <define name="__REACTOS__" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>winmm</library>
+       <library>setupapi</library>
+       <file>user.c</file>
+       <file>kernel.c</file>
+       <file>devices.c</file>
+       <file>wavehdr.c</file>
+       <file>threads.c</file>
+       <file>helper.c</file>
+       <file>wdmaud.rc</file>
+</module>