From fc3b932fc8a8125c04aa22e513b72ae890fc2fa6 Mon Sep 17 00:00:00 2001 From: Andrew Greenwood Date: Thu, 24 Nov 2005 14:36:47 +0000 Subject: [PATCH] Partial implementation of WDMAUD.DRV - device capability querying fails svn path=/trunk/; revision=19530 --- reactos/lib/wdmaud/TODO | 4 + reactos/lib/wdmaud/control.c | 47 +++ reactos/lib/wdmaud/devices.c | 697 ++++++++++++++++++++++++++++++++++ reactos/lib/wdmaud/helper.c | 46 +++ reactos/lib/wdmaud/kernel.c | 358 +++++++++++++++++ reactos/lib/wdmaud/threads.c | 235 ++++++++++++ reactos/lib/wdmaud/user.c | 387 +++++++++++++++++++ reactos/lib/wdmaud/wavehdr.c | 601 +++++++++++++++++++++++++++++ reactos/lib/wdmaud/wdmaud.def | 15 + reactos/lib/wdmaud/wdmaud.h | 507 +++++++++++++++++++++++++ reactos/lib/wdmaud/wdmaud.rc | 5 + reactos/lib/wdmaud/wdmaud.xml | 20 + 12 files changed, 2922 insertions(+) create mode 100644 reactos/lib/wdmaud/TODO create mode 100644 reactos/lib/wdmaud/control.c create mode 100644 reactos/lib/wdmaud/devices.c create mode 100755 reactos/lib/wdmaud/helper.c create mode 100644 reactos/lib/wdmaud/kernel.c create mode 100644 reactos/lib/wdmaud/threads.c create mode 100644 reactos/lib/wdmaud/user.c create mode 100644 reactos/lib/wdmaud/wavehdr.c create mode 100755 reactos/lib/wdmaud/wdmaud.def create mode 100755 reactos/lib/wdmaud/wdmaud.h create mode 100755 reactos/lib/wdmaud/wdmaud.rc create mode 100755 reactos/lib/wdmaud/wdmaud.xml diff --git a/reactos/lib/wdmaud/TODO b/reactos/lib/wdmaud/TODO new file mode 100644 index 00000000000..af247a7ebc4 --- /dev/null +++ b/reactos/lib/wdmaud/TODO @@ -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 index 00000000000..3c9c1018528 --- /dev/null +++ b/reactos/lib/wdmaud/control.c @@ -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 +#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 index 00000000000..26288cec0d1 --- /dev/null +++ b/reactos/lib/wdmaud/devices.c @@ -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 +#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 index 00000000000..814721d6535 --- /dev/null +++ b/reactos/lib/wdmaud/helper.c @@ -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 +#include + +/* + 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 index 00000000000..e1ae043f42e --- /dev/null +++ b/reactos/lib/wdmaud/kernel.c @@ -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 +#include +#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 index 00000000000..1a949edc8b4 --- /dev/null +++ b/reactos/lib/wdmaud/threads.c @@ -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 index 00000000000..d7d3b5e2ed2 --- /dev/null +++ b/reactos/lib/wdmaud/user.c @@ -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 +#include +#include +#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 index 00000000000..bce45cba7f0 --- /dev/null +++ b/reactos/lib/wdmaud/wavehdr.c @@ -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 +#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 index 00000000000..85963254fec --- /dev/null +++ b/reactos/lib/wdmaud/wdmaud.def @@ -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 index 00000000000..589acd9b9a7 --- /dev/null +++ b/reactos/lib/wdmaud/wdmaud.h @@ -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 + +#include +#include + +#include +#include +#include + +/* 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 index 00000000000..06336a7bd29 --- /dev/null +++ b/reactos/lib/wdmaud/wdmaud.rc @@ -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 diff --git a/reactos/lib/wdmaud/wdmaud.xml b/reactos/lib/wdmaud/wdmaud.xml new file mode 100755 index 00000000000..95eea5330d4 --- /dev/null +++ b/reactos/lib/wdmaud/wdmaud.xml @@ -0,0 +1,20 @@ + + + . + + + + + + ntdll + kernel32 + winmm + setupapi + user.c + kernel.c + devices.c + wavehdr.c + threads.c + helper.c + wdmaud.rc +