Sync to trunk (r44371)
[reactos.git] / reactos / dll / win32 / wdmaud.drv / wdmaud.c
index 2a3d61d..2a89dc1 100644 (file)
 /*
+ * PROJECT:     ReactOS Sound System
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        dll/win32/wdmaud.drv/wdmaud.c
  *
- * PROJECT:         ReactOS WDM Audio driver mapper
- * FILE:            dll/win32/wdmaud.drv/wdmaud.c
- * PURPOSE:         wdmaud.drv
- * PROGRAMMER:      Dmitry Chapyshev (dmitry@reactos.org)
+ * PURPOSE:     WDM Audio Driver (User-mode part)
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+ *
+ * NOTES:       Looking for wodMessage & co? You won't find them here. Try
+ *              the MME Buddy library, which is where these routines are
+ *              actually implemented.
  *
- * UPDATE HISTORY:
- *      25/05/2008  Created
  */
 
-#include <stdarg.h>
-
 #include <windows.h>
-#include <mmsystem.h>
+#include <ntddsnd.h>
+#include <sndtypes.h>
 #include <mmddk.h>
-#include <mmreg.h>
-#include <debug.h>
+#include <mmebuddy.h>
+
+#include <ks.h>
+#include <ksmedia.h>
+#include "interface.h"
+
+#define KERNEL_DEVICE_NAME      L"\\\\.\\wdmaud"
+
+PWSTR UnknownWaveIn = L"Wave Input";
+PWSTR UnknownWaveOut = L"Wave Output";
+PWSTR UnknownMidiIn = L"Midi Input";
+PWSTR UnknownMidiOut = L"Midi Output";
+
+HANDLE KernelHandle = INVALID_HANDLE_VALUE;
+DWORD OpenCount = 0;
+
+MMRESULT
+WriteFileEx_Remixer(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID OffsetPtr,
+    IN  DWORD Length,
+    IN  PSOUND_OVERLAPPED Overlap,
+    IN  LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine);
+
 
-DWORD APIENTRY
-mxdMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+
+MMRESULT
+GetNumWdmDevs(
+    IN  HANDLE Handle,
+    IN  MMDEVICE_TYPE DeviceType,
+    OUT DWORD* DeviceCount)
 {
-    DPRINT1("mxdMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    MMRESULT Result;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+
+    VALIDATE_MMSYS_PARAMETER( Handle != INVALID_HANDLE_VALUE );
+    VALIDATE_MMSYS_PARAMETER( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
+    VALIDATE_MMSYS_PARAMETER( DeviceCount );
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.DeviceType = DeviceType;
+
+    Result = SyncOverlappedDeviceIoControl(Handle,
+                                           IOCTL_GETNUMDEVS_TYPE,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
 
-    switch (uMsg)
+    if ( ! MMSUCCESS( Result ) )
     {
-        case MXDM_INIT:
-        break;
+        SND_ERR(L"Call to IOCTL_GETNUMDEVS_TYPE failed\n");
+        *DeviceCount = 0;
+        return TranslateInternalMmResult(Result);
+    }
 
-        case MXDM_GETNUMDEVS:
-        break;
+    *DeviceCount = DeviceInfo.DeviceCount;
 
-        case MXDM_GETDEVCAPS:
-        break;
+    return MMSYSERR_NOERROR;
+}
 
-        case MXDM_OPEN:
-        break;
+MMRESULT
+GetWdmDeviceCapabilities(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  DWORD DeviceId,
+    OUT PVOID Capabilities,
+    IN  DWORD CapabilitiesSize)
+{
+    /* NOTE - At this time, WDMAUD does not support this properly */
 
-        case MXDM_CLOSE:
-        break;
+    MMRESULT Result;
+    MMDEVICE_TYPE DeviceType;
+    WDMAUD_DEVICE_INFO DeviceInfo;
 
-        case MXDM_GETLINEINFO:
-        break;
+    SND_ASSERT( SoundDevice );
+    SND_ASSERT( Capabilities );
 
-        case MXDM_GETLINECONTROLS:
-        break;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) )
+        return Result;
+
+    SND_TRACE(L"WDMAUD - GetWdmDeviceCapabilities DeviceType %u DeviceId %u\n", DeviceType, DeviceId);
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.DeviceType = DeviceType;
+    DeviceInfo.DeviceIndex = DeviceId;
 
-        case MXDM_GETCONTROLDETAILS:
-        break;
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_GETCAPABILITIES,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
 
-        case MXDM_SETCONTROLDETAILS:
-        break;
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
     }
 
-    return MMSYSERR_NOTSUPPORTED;
+    /* This is pretty much a big hack right now */
+    switch ( DeviceType )
+    {
+        case MIXER_DEVICE_TYPE:
+        {
+            LPMIXERCAPS MixerCaps = (LPMIXERCAPS) Capabilities;
+
+            DeviceInfo.u.MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
+            CopyWideString(MixerCaps->szPname, DeviceInfo.u.MixCaps.szPname);
+
+            MixerCaps->cDestinations = DeviceInfo.u.MixCaps.cDestinations;
+            MixerCaps->fdwSupport = DeviceInfo.u.MixCaps.fdwSupport;
+            MixerCaps->vDriverVersion = DeviceInfo.u.MixCaps.vDriverVersion;
+            MixerCaps->wMid = DeviceInfo.u.MixCaps.wMid;
+            MixerCaps->wPid = DeviceInfo.u.MixCaps.wPid;
+            break;
+        }
+        case WAVE_OUT_DEVICE_TYPE :
+        {
+            LPWAVEOUTCAPS WaveOutCaps = (LPWAVEOUTCAPS) Capabilities;
+
+            DeviceInfo.u.WaveOutCaps.szPname[MAXPNAMELEN-1] = L'\0';
+            WaveOutCaps->wMid = DeviceInfo.u.WaveOutCaps.wMid;
+            WaveOutCaps->wPid = DeviceInfo.u.WaveOutCaps.wPid;
+
+            WaveOutCaps->vDriverVersion = 0x0001;
+            CopyWideString(WaveOutCaps->szPname, DeviceInfo.u.WaveOutCaps.szPname);
+
+            WaveOutCaps->dwFormats = DeviceInfo.u.WaveOutCaps.dwFormats;
+            WaveOutCaps->wChannels = DeviceInfo.u.WaveOutCaps.wChannels;
+            WaveOutCaps->dwSupport = DeviceInfo.u.WaveOutCaps.dwSupport;
+            break;
+        }
+        case WAVE_IN_DEVICE_TYPE :
+        {
+            LPWAVEINCAPSW WaveInCaps = (LPWAVEINCAPSW) Capabilities;
+
+            DeviceInfo.u.WaveInCaps.szPname[MAXPNAMELEN-1] = L'\0';
+
+            WaveInCaps->wMid = DeviceInfo.u.WaveInCaps.wMid;
+            WaveInCaps->wPid = DeviceInfo.u.WaveInCaps.wPid;
+
+            WaveInCaps->vDriverVersion = 0x0001;
+            CopyWideString(WaveInCaps->szPname, DeviceInfo.u.WaveInCaps.szPname);
+
+            WaveInCaps->dwFormats = DeviceInfo.u.WaveInCaps.dwFormats;
+            WaveInCaps->wChannels = DeviceInfo.u.WaveInCaps.wChannels;
+            WaveInCaps->wReserved1 = 0;
+            break;
+        }
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+
+MMRESULT
+OpenWdmSoundDevice(
+    IN  struct _SOUND_DEVICE* SoundDevice,  /* NOT USED */
+    OUT PVOID* Handle)
+{
+    /* Only open this if it's not already open */
+    if ( KernelHandle == INVALID_HANDLE_VALUE )
+    {
+        SND_TRACE(L"Opening wdmaud device\n");
+        KernelHandle = CreateFileW(KERNEL_DEVICE_NAME,
+                                  GENERIC_READ | GENERIC_WRITE,
+                                  0,
+                                  NULL,
+                                  OPEN_EXISTING,
+                                  FILE_FLAG_OVERLAPPED,
+                                  NULL);
+    }
+
+    if ( KernelHandle == INVALID_HANDLE_VALUE )
+        return MMSYSERR_ERROR;
+
+    SND_ASSERT( Handle );
+
+    *Handle = KernelHandle;
+    ++ OpenCount;
+
+    return MMSYSERR_NOERROR;
 }
 
-DWORD APIENTRY
-auxMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+MMRESULT
+CloseWdmSoundDevice(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN  PVOID Handle)
 {
-    DPRINT1("auxMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    MMRESULT Result;
+    MMDEVICE_TYPE DeviceType;
+    PSOUND_DEVICE SoundDevice;
 
-    switch (uMsg)
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+
+    if ( ! MMSUCCESS(Result) )
     {
-        case AUXDM_GETDEVCAPS:
+        return TranslateInternalMmResult(Result);
+    }
 
-        break;
+    if ( OpenCount == 0 )
+    {
+        return MMSYSERR_NOERROR;
+    }
 
-        case AUXDM_GETNUMDEVS:
+    SND_ASSERT( KernelHandle != INVALID_HANDLE_VALUE );
 
-        break;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
 
-        case AUXDM_GETVOLUME:
+    if (SoundDeviceInstance->Handle != (PVOID)KernelHandle)
+    {
+        ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
 
-        break;
+        DeviceInfo.DeviceType = DeviceType;
+        DeviceInfo.hDevice = SoundDeviceInstance->Handle;
 
-        case AUXDM_SETVOLUME:
+         /* First stop the stream */
+         if (DeviceType != MIXER_DEVICE_TYPE)
+         {
+             DeviceInfo.u.State = KSSTATE_STOP;
+             SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_SETDEVICE_STATE,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                            sizeof(WDMAUD_DEVICE_INFO),
+                                            NULL);
+        }
 
-        break;
+        SyncOverlappedDeviceIoControl(KernelHandle,
+                                      IOCTL_CLOSE_WDMAUD,
+                                      (LPVOID) &DeviceInfo,
+                                      sizeof(WDMAUD_DEVICE_INFO),
+                                      (LPVOID) &DeviceInfo,
+                                      sizeof(WDMAUD_DEVICE_INFO),
+                                      NULL);
+    }
 
-        default:
-            return MMSYSERR_NOTSUPPORTED;
+    if (DeviceType == MIXER_DEVICE_TYPE)
+    {
+        SetEvent(SoundDeviceInstance->hStopEvent);
+        CloseHandle(SoundDeviceInstance->hStopEvent);
+        CloseHandle(SoundDeviceInstance->hNotifyEvent);
     }
 
-    return MMSYSERR_NOTSUPPORTED;
+    --OpenCount;
+
+    if ( OpenCount < 1 )
+    {
+        CloseHandle(KernelHandle);
+        KernelHandle = INVALID_HANDLE_VALUE;
+    }
+
+    return MMSYSERR_NOERROR;
 }
 
-DWORD APIENTRY
-wodMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+
+MMRESULT
+QueryWdmWaveDeviceFormatSupport(
+    IN  PSOUND_DEVICE Device,
+    IN  PWAVEFORMATEX WaveFormat,
+    IN  DWORD WaveFormatSize)
 {
-    DPRINT1("wodMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    /* Whatever... */
+    return MMSYSERR_NOERROR;
+}
+
 
-    switch (uMsg)
+DWORD
+WINAPI
+MixerEventThreadRoutine(
+    LPVOID Parameter)
+{
+    HANDLE WaitObjects[2];
+    DWORD dwResult;
+    MMRESULT Result;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    PSOUND_DEVICE_INSTANCE Instance = (PSOUND_DEVICE_INSTANCE)Parameter;
+
+    /* setup wait objects */
+    WaitObjects[0] = Instance->hNotifyEvent;
+    WaitObjects[1] = Instance->hStopEvent;
+
+    /* zero device info */
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+
+    DeviceInfo.hDevice = Instance->Handle;
+    DeviceInfo.DeviceType = MIXER_DEVICE_TYPE;
+
+    do
+    {
+        dwResult = WaitForMultipleObjects(2, WaitObjects, FALSE, INFINITE);
+
+        if (dwResult == WAIT_OBJECT_0 + 1)
+        {
+            /* stop event was signalled */
+            break;
+        }
+
+        do
+        {
+            Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                                   IOCTL_GET_MIXER_EVENT,
+                                                   (LPVOID) &DeviceInfo,
+                                                   sizeof(WDMAUD_DEVICE_INFO),
+                                                   (LPVOID) &DeviceInfo,
+                                                   sizeof(WDMAUD_DEVICE_INFO),
+                                                   NULL);
+
+            if (Result == MMSYSERR_NOERROR)
+            {
+                DriverCallback(Instance->WinMM.ClientCallback,
+                               HIWORD(Instance->WinMM.Flags),
+                               Instance->WinMM.Handle,
+                               DeviceInfo.u.MixerEvent.NotificationType,
+                               Instance->WinMM.ClientCallbackInstanceData,
+                               (DWORD_PTR)DeviceInfo.u.MixerEvent.Value,
+                               0);
+            }
+        }while(Result == MMSYSERR_NOERROR);
+    }while(TRUE);
+
+    /* done */
+    return 0;
+}
+
+
+MMRESULT
+SetWdmMixerDeviceFormat(
+    IN  PSOUND_DEVICE_INSTANCE Instance,
+    IN  DWORD DeviceId,
+    IN  PWAVEFORMATEX WaveFormat,
+    IN  DWORD WaveFormatSize)
+{
+    MMRESULT Result;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    HANDLE hThread;
+
+    if (Instance->Handle != KernelHandle)
     {
-        case WODM_GETNUMDEVS:
-        break;
+        /* device is already open */
+        return MMSYSERR_NOERROR;
+    }
+
+    Instance->hNotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if ( ! Instance->hNotifyEvent )
+        return MMSYSERR_NOMEM;
+
+    Instance->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if ( ! Instance->hStopEvent )
+        return MMSYSERR_NOMEM;
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.DeviceType = MIXER_DEVICE_TYPE;
+    DeviceInfo.DeviceIndex = DeviceId;
+    DeviceInfo.u.hNotifyEvent = Instance->hNotifyEvent;
+
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_OPEN_WDMAUD,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        CloseHandle(Instance->hNotifyEvent);
+        CloseHandle(Instance->hStopEvent);
+        return TranslateInternalMmResult(Result);
+    }
 
-        case WODM_GETDEVCAPS:
-        break;
+    hThread = CreateThread(NULL, 0, MixerEventThreadRoutine, (LPVOID)Instance, 0, NULL);
+    if (  hThread )
+    {
+        CloseHandle(hThread);
+    }
+
+    /* Store sound device handle instance handle */
+    Instance->Handle = (PVOID)DeviceInfo.hDevice;
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+SetWdmWaveDeviceFormat(
+    IN  PSOUND_DEVICE_INSTANCE Instance,
+    IN  DWORD DeviceId,
+    IN  PWAVEFORMATEX WaveFormat,
+    IN  DWORD WaveFormatSize)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PVOID Identifier;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    MMDEVICE_TYPE DeviceType;
 
-        case WODM_OPEN:
-        break;
+    Result = GetSoundDeviceFromInstance(Instance, &SoundDevice);
 
-        case WODM_CLOSE:
-        break;
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
 
-        case WODM_WRITE:
-        break;
+    Result = GetSoundDeviceIdentifier(SoundDevice, &Identifier);
 
-        case WODM_PAUSE:
-        break;
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
 
-        case WODM_RESTART:
-        break;
+    if (Instance->Handle != KernelHandle)
+    {
+        /* device is already open */
+        return MMSYSERR_NOERROR;
+    }
 
-        case WODM_RESET:
-        break;
 
-        case WODM_BREAKLOOP:
-        break;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
 
-        case WODM_GETPOS:
-        break;
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.DeviceType = DeviceType;
+    DeviceInfo.DeviceIndex = DeviceId;
+    DeviceInfo.u.WaveFormatEx.cbSize = sizeof(WAVEFORMATEX); //WaveFormat->cbSize;
+    DeviceInfo.u.WaveFormatEx.wFormatTag = WaveFormat->wFormatTag;
+#ifdef USERMODE_MIXER
+    DeviceInfo.u.WaveFormatEx.nChannels = 2;
+    DeviceInfo.u.WaveFormatEx.nSamplesPerSec = 44100;
+    DeviceInfo.u.WaveFormatEx.nBlockAlign = 4;
+    DeviceInfo.u.WaveFormatEx.nAvgBytesPerSec = 176400;
+    DeviceInfo.u.WaveFormatEx.wBitsPerSample = 16;
+#else
+    DeviceInfo.u.WaveFormatEx.nChannels = WaveFormat->nChannels;
+    DeviceInfo.u.WaveFormatEx.nSamplesPerSec = WaveFormat->nSamplesPerSec;
+    DeviceInfo.u.WaveFormatEx.nBlockAlign = WaveFormat->nBlockAlign;
+    DeviceInfo.u.WaveFormatEx.nAvgBytesPerSec = WaveFormat->nAvgBytesPerSec;
+    DeviceInfo.u.WaveFormatEx.wBitsPerSample = WaveFormat->wBitsPerSample;
+#endif
 
-        case WODM_SETPITCH:
-        break;
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_OPEN_WDMAUD,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
 
-        case WODM_SETVOLUME:
-        break;
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
 
-        case WODM_SETPLAYBACKRATE:
-        break;
+    /* Store format */
+    Instance->WaveFormatEx.cbSize = WaveFormat->cbSize;
+    Instance->WaveFormatEx.wFormatTag = WaveFormat->wFormatTag;
+    Instance->WaveFormatEx.nChannels = WaveFormat->nChannels;
+    Instance->WaveFormatEx.nSamplesPerSec = WaveFormat->nSamplesPerSec;
+    Instance->WaveFormatEx.nBlockAlign = WaveFormat->nBlockAlign;
+    Instance->WaveFormatEx.nAvgBytesPerSec = WaveFormat->nAvgBytesPerSec;
+    Instance->WaveFormatEx.wBitsPerSample = WaveFormat->wBitsPerSample;
 
-        case WODM_GETPITCH:
-        break;
+    /* Store sound device handle instance handle */
+    Instance->Handle = (PVOID)DeviceInfo.hDevice;
 
-        case WODM_GETVOLUME:
-        break;
+    /* Now determine framing requirements */
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_GETFRAMESIZE,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
 
-        case WODM_GETPLAYBACKRATE:
-        break;
+    if ( MMSUCCESS(Result) )
+    {
+        if (DeviceInfo.u.FrameSize)
+        {
+            Instance->FrameSize = DeviceInfo.u.FrameSize * 2;
+            Instance->BufferCount = WaveFormat->nAvgBytesPerSec / Instance->FrameSize;
+            SND_TRACE(L"FrameSize %u BufferCount %u\n", Instance->FrameSize, Instance->BufferCount);
+        }
+    }
+    else
+    {
+        // use a default of 100 buffers
+        Instance->BufferCount = 100;
+    }
 
-        default:
-            return MMSYSERR_NOTSUPPORTED;
+    if (DeviceType == WAVE_OUT_DEVICE_TYPE)
+    {
+        /* Now start the stream */
+        DeviceInfo.u.State = KSSTATE_RUN;
+        SyncOverlappedDeviceIoControl(KernelHandle,
+                                      IOCTL_SETDEVICE_STATE,
+                                      (LPVOID) &DeviceInfo,
+                                      sizeof(WDMAUD_DEVICE_INFO),
+                                      (LPVOID) &DeviceInfo,
+                                      sizeof(WDMAUD_DEVICE_INFO),
+                                      NULL);
     }
 
-    return MMSYSERR_NOTSUPPORTED;
+    return MMSYSERR_NOERROR;
 }
 
-DWORD APIENTRY
-widMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+MMRESULT
+WriteFileEx_Committer2(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID OffsetPtr,
+    IN  DWORD Length,
+    IN  PSOUND_OVERLAPPED Overlap,
+    IN  LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
 {
-    DPRINT1("widMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    HANDLE Handle;
+    MMRESULT Result;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    PSOUND_DEVICE SoundDevice;
+    MMDEVICE_TYPE DeviceType;
+    BOOL Ret;
+
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+    VALIDATE_MMSYS_PARAMETER( OffsetPtr );
+    VALIDATE_MMSYS_PARAMETER( Overlap );
+    VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
+
+    GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
 
-    switch (uMsg)
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+
+    if ( ! MMSUCCESS(Result) )
     {
-        case WIDM_GETNUMDEVS:
-        break;
+        return TranslateInternalMmResult(Result);
+    }
 
-        case WIDM_GETDEVCAPS:
-        break;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
 
-        case WIDM_OPEN:
-        break;
+    SND_ASSERT(Handle);
 
-        case WIDM_CLOSE:
-        break;
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
 
-        case WIDM_ADDBUFFER:
-        break;
+    DeviceInfo.Header.FrameExtent = Length;
+    if (DeviceType == WAVE_OUT_DEVICE_TYPE)
+    {
+        DeviceInfo.Header.DataUsed = Length;
+    }
+    DeviceInfo.Header.Data = OffsetPtr;
+    DeviceInfo.Header.Size = sizeof(WDMAUD_DEVICE_INFO);
+    DeviceInfo.Header.PresentationTime.Numerator = 1;
+    DeviceInfo.Header.PresentationTime.Denominator = 1;
+    DeviceInfo.hDevice = Handle;
+    DeviceInfo.DeviceType = DeviceType;
 
-        case WIDM_STOP:
-        break;
+    Overlap->Standard.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
 
-        case WIDM_START:
-        break;
+    if (DeviceType == WAVE_OUT_DEVICE_TYPE)
+    {
+        Ret = WriteFileEx(KernelHandle, &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPOVERLAPPED)Overlap, CompletionRoutine);
+        if (Ret)
+            WaitForSingleObjectEx (KernelHandle, INFINITE, TRUE);
+    }
+    else if (DeviceType == WAVE_IN_DEVICE_TYPE)
+    {
+        Ret = ReadFileEx(KernelHandle, &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPOVERLAPPED)Overlap, CompletionRoutine);
+        //if (Ret)
+        //    WaitForSingleObjectEx (KernelHandle, INFINITE, TRUE);
+    }
+
+    return MMSYSERR_NOERROR;
+}
 
-        case WIDM_RESET:
-        break;
+MMRESULT
+SetWdmWaveState(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN BOOL bStart)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    MMDEVICE_TYPE DeviceType;
+    HANDLE Handle;
 
-        case WIDM_GETPOS:
-        break;
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
 
-        default:
-            return MMSYSERR_NOTSUPPORTED;
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
     }
 
-    return MMSYSERR_NOTSUPPORTED;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.hDevice = Handle;
+    DeviceInfo.DeviceType = DeviceType;
+
+    if (bStart)
+        DeviceInfo.u.State = KSSTATE_RUN;
+    else
+        DeviceInfo.u.State = KSSTATE_PAUSE;
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_SETDEVICE_STATE,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+    return Result;
+}
+
+MMRESULT
+GetDeviceInterfaceString(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  DWORD DeviceId,
+    IN  LPWSTR Interface,
+    IN  DWORD  InterfaceLength,
+    OUT  DWORD * InterfaceSize)
+{
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    MMRESULT Result;
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.DeviceType = DeviceType;
+    DeviceInfo.DeviceIndex = DeviceId;
+
+
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_QUERYDEVICEINTERFACESTRING,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
+
+
+    if (!Interface)
+    {
+        SND_ASSERT(InterfaceSize);
+
+        *InterfaceSize = DeviceInfo.u.Interface.DeviceInterfaceStringSize;
+        return MMSYSERR_NOERROR;
+    }
+
+    if (InterfaceLength < DeviceInfo.u.Interface.DeviceInterfaceStringSize)
+    {
+        /* buffer is too small */
+        return MMSYSERR_MOREDATA;
+    }
+
+    DeviceInfo.u.Interface.DeviceInterfaceStringSize = InterfaceLength;
+    DeviceInfo.u.Interface.DeviceInterfaceString = Interface;
+
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_QUERYDEVICEINTERFACESTRING,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+    if (  MMSUCCESS(Result) && InterfaceLength > 2)
+    {
+        Interface[1] = L'\\';
+        Interface[InterfaceLength-1] = L'\0';
+    }
+
+    return Result;
 }
 
-DWORD APIENTRY
-modMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+MMRESULT
+GetWdmPosition(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN  MMTIME* Time)
 {
-    DPRINT1("modMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    MMDEVICE_TYPE DeviceType;
+    HANDLE Handle;
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
 
-    return MMSYSERR_NOTSUPPORTED;
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.hDevice = Handle;
+    DeviceInfo.DeviceType = DeviceType;
+
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_OPEN_WDMAUD,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        return TranslateInternalMmResult(Result);
+    }
+
+    Time->wType = TIME_BYTES;
+    Time->u.cb = (DWORD)DeviceInfo.u.Position;
+
+    return MMSYSERR_NOERROR;
 }
 
-LRESULT APIENTRY
-DriverProc(DWORD dwDriverID,
-           HDRVR hDriver,
-           UINT uiMessage,
-           LPARAM lParam1,
-           LPARAM lParam2)
+MMRESULT
+ResetStream(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  BOOLEAN bStartReset)
 {
-    return DefDriverProc(dwDriverID, hDriver, uiMessage, lParam1, lParam2);
+    MMRESULT Result;
+    HANDLE Handle;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.hDevice = Handle;
+    DeviceInfo.DeviceType = DeviceType;
+    DeviceInfo.u.ResetStream = (bStartReset ? KSRESET_BEGIN : KSRESET_END);
+
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IOCTL_RESET_STREAM,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+    return Result;
 }
 
-BOOL WINAPI
-DllMain(IN HINSTANCE hinstDLL,
-        IN DWORD dwReason,
-        IN LPVOID lpvReserved)
+
+MMRESULT
+QueryMixerInfo(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN UINT uMsg,
+    IN LPVOID Parameter,
+    IN DWORD Flags)
 {
-    switch (dwReason)
+    MMRESULT Result;
+    WDMAUD_DEVICE_INFO DeviceInfo;
+    HANDLE Handle;
+    DWORD IoControlCode;
+    LPMIXERLINEW MixLine;
+    LPMIXERLINECONTROLSW MixControls;
+    LPMIXERCONTROLDETAILS MixDetails;
+
+    SND_TRACE(L"uMsg %x Flags %x\n", uMsg, Flags);
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
+    DeviceInfo.hDevice = Handle;
+    DeviceInfo.DeviceType = MIXER_DEVICE_TYPE;
+    DeviceInfo.Flags = Flags;
+
+    MixLine = (LPMIXERLINEW)Parameter;
+    MixControls = (LPMIXERLINECONTROLSW)Parameter;
+    MixDetails = (LPMIXERCONTROLDETAILS)Parameter;
+
+    switch(uMsg)
     {
-        case DLL_PROCESS_ATTACH:
-            DisableThreadLibraryCalls(hinstDLL);
+        case MXDM_GETLINEINFO:
+            RtlCopyMemory(&DeviceInfo.u.MixLine, MixLine, sizeof(MIXERLINEW));
+            IoControlCode = IOCTL_GETLINEINFO;
+            break;
+        case MXDM_GETLINECONTROLS:
+            RtlCopyMemory(&DeviceInfo.u.MixControls, MixControls, sizeof(MIXERLINECONTROLSW));
+            IoControlCode = IOCTL_GETLINECONTROLS;
+            break;
+       case MXDM_SETCONTROLDETAILS:
+            RtlCopyMemory(&DeviceInfo.u.MixDetails, MixDetails, sizeof(MIXERCONTROLDETAILS));
+            IoControlCode = IOCTL_SETCONTROLDETAILS;
             break;
+       case MXDM_GETCONTROLDETAILS:
+            RtlCopyMemory(&DeviceInfo.u.MixDetails, MixDetails, sizeof(MIXERCONTROLDETAILS));
+            IoControlCode = IOCTL_GETCONTROLDETAILS;
+            break;
+       default:
+           SND_ASSERT(0);
+           return MMSYSERR_NOTSUPPORTED;
     }
 
-    return TRUE;
+    Result = SyncOverlappedDeviceIoControl(KernelHandle,
+                                           IoControlCode,
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           (LPVOID) &DeviceInfo,
+                                           sizeof(WDMAUD_DEVICE_INFO),
+                                           NULL);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        return Result;
+    }
+
+    switch(uMsg)
+    {
+        case MXDM_GETLINEINFO:
+        {
+            RtlCopyMemory(MixLine, &DeviceInfo.u.MixLine, sizeof(MIXERLINEW));
+            break;
+        }
+    }
+
+    return Result;
+}
+
+
+MMRESULT
+PopulateWdmDeviceList(
+    HANDLE Handle,
+    MMDEVICE_TYPE DeviceType)
+{
+    MMRESULT Result;
+    DWORD DeviceCount = 0;
+    PSOUND_DEVICE SoundDevice = NULL;
+    MMFUNCTION_TABLE FuncTable;
+    DWORD i;
+
+    VALIDATE_MMSYS_PARAMETER( Handle != INVALID_HANDLE_VALUE );
+    VALIDATE_MMSYS_PARAMETER( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
+
+    Result = GetNumWdmDevs(Handle, DeviceType, &DeviceCount);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Error %d while obtaining number of devices\n", Result);
+        return TranslateInternalMmResult(Result);
+    }
+
+    SND_TRACE(L"%d devices of type %d found\n", DeviceCount, DeviceType);
+
+
+    for ( i = 0; i < DeviceCount; ++ i )
+    {
+        Result = ListSoundDevice(DeviceType, UlongToPtr(i), &SoundDevice);
+
+        if ( ! MMSUCCESS(Result) )
+        {
+            SND_ERR(L"Failed to list sound device - error %d\n", Result);
+            return TranslateInternalMmResult(Result);
+        }
+
+        /* Set up our function table */
+        ZeroMemory(&FuncTable, sizeof(MMFUNCTION_TABLE));
+        FuncTable.GetCapabilities = GetWdmDeviceCapabilities;
+        FuncTable.QueryWaveFormatSupport = QueryWdmWaveDeviceFormatSupport;
+        if (DeviceType == MIXER_DEVICE_TYPE)
+        {
+            FuncTable.SetWaveFormat = SetWdmMixerDeviceFormat;
+            FuncTable.QueryMixerInfo = QueryMixerInfo;
+        }
+        else
+        {
+            FuncTable.SetWaveFormat = SetWdmWaveDeviceFormat;
+        }
+
+        if (DeviceType == WAVE_IN_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
+        {
+            FuncTable.SetState = SetWdmWaveState;
+            FuncTable.ResetStream = ResetStream;
+        }
+
+        FuncTable.Open = OpenWdmSoundDevice;
+        FuncTable.Close = CloseWdmSoundDevice;
+        FuncTable.GetDeviceInterfaceString = GetDeviceInterfaceString;
+#ifndef USERMODE_MIXER
+        FuncTable.CommitWaveBuffer = WriteFileEx_Committer2;
+#else
+        FuncTable.CommitWaveBuffer = WriteFileEx_Remixer;
+#endif
+        FuncTable.GetPos = GetWdmPosition;
+
+        SetSoundDeviceFunctionTable(SoundDevice, &FuncTable);
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+
+
+LONG
+APIENTRY
+DriverProc(
+    DWORD DriverId,
+    HANDLE DriverHandle,
+    UINT Message,
+    LONG Parameter1,
+    LONG Parameter2)
+{
+    MMRESULT Result;
+
+    switch ( Message )
+    {
+        case DRV_LOAD :
+        {
+            HANDLE Handle;
+            SND_TRACE(L"DRV_LOAD\n");
+
+            Result = InitEntrypointMutexes();
+
+            if ( ! MMSUCCESS(Result) )
+                return 0L;
+
+            OpenWdmSoundDevice(NULL, &Handle);
+
+            if ( Handle == INVALID_HANDLE_VALUE )
+            {
+                SND_ERR(L"Failed to open %s\n", KERNEL_DEVICE_NAME);
+                CleanupEntrypointMutexes();
+
+                //UnlistAllSoundDevices();
+
+                return 0L;
+            }
+
+            /* Populate the device lists */
+            SND_TRACE(L"Populating device lists\n");
+            PopulateWdmDeviceList(KernelHandle, WAVE_OUT_DEVICE_TYPE);
+            PopulateWdmDeviceList(KernelHandle, WAVE_IN_DEVICE_TYPE);
+            PopulateWdmDeviceList(KernelHandle, MIDI_OUT_DEVICE_TYPE);
+            PopulateWdmDeviceList(KernelHandle, MIDI_IN_DEVICE_TYPE);
+            PopulateWdmDeviceList(KernelHandle, AUX_DEVICE_TYPE);
+            PopulateWdmDeviceList(KernelHandle, MIXER_DEVICE_TYPE);
+
+            SND_TRACE(L"Initialisation complete\n");
+
+            return 1L;
+        }
+
+        case DRV_FREE :
+        {
+            SND_TRACE(L"DRV_FREE\n");
+
+            if ( KernelHandle != INVALID_HANDLE_VALUE )
+            {
+                CloseHandle(KernelHandle);
+                KernelHandle = INVALID_HANDLE_VALUE;
+            }
+
+            /* TODO: Clean up the path names! */
+            UnlistAllSoundDevices();
+
+            CleanupEntrypointMutexes();
+
+            SND_TRACE(L"Unfreed memory blocks: %d\n",
+                      GetMemoryAllocationCount());
+
+            return 1L;
+        }
+
+        case DRV_ENABLE :
+        case DRV_DISABLE :
+        {
+            SND_TRACE(L"DRV_ENABLE / DRV_DISABLE\n");
+            return 1L;
+        }
+
+        case DRV_OPEN :
+        case DRV_CLOSE :
+        {
+            SND_TRACE(L"DRV_OPEN / DRV_CLOSE\n");
+            return 1L;
+        }
+
+        case DRV_QUERYCONFIGURE :
+        {
+            SND_TRACE(L"DRV_QUERYCONFIGURE\n");
+            return 0L;
+        }
+        case DRV_CONFIGURE :
+            return DRVCNF_OK;
+
+        default :
+            SND_TRACE(L"Unhandled message %d\n", Message);
+            return DefDriverProc(DriverId,
+                                 DriverHandle,
+                                 Message,
+                                 Parameter1,
+                                 Parameter2);
+    }
 }
 
+
+BOOL WINAPI DllMain(
+    HINSTANCE hinstDLL,
+    DWORD fdwReason,
+    LPVOID lpvReserved)
+{
+    switch ( fdwReason )
+    {
+        case DLL_PROCESS_ATTACH :
+            SND_TRACE(L"WDMAUD.DRV - Process attached\n");
+            break;
+        case DLL_PROCESS_DETACH :
+            SND_TRACE(L"WDMAUD.DRV - Process detached\n");
+            break;
+        case DLL_THREAD_ATTACH :
+            SND_TRACE(L"WDMAUD.DRV - Thread attached\n");
+            break;
+        case DLL_THREAD_DETACH :
+            SND_TRACE(L"WDMAUD.DRV - Thread detached\n");
+            break;
+    }
+
+    return TRUE;
+}