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