Cleanup of existing code, basic implementation of play/stop/resume states. Added: trunk/reactos/lib/wdmaud/README.TXT Modified: trunk/reactos/lib/wdmaud/control.c Modified: trunk/reactos/lib/wdmaud/devices.c Modified: trunk/reactos/lib/wdmaud/kernel.c Modified: trunk/reactos/lib/wdmaud/memtrack.c Added: trunk/reactos/lib/wdmaud/midi.c Modified: trunk/reactos/lib/wdmaud/threads.c Modified: trunk/reactos/lib/wdmaud/user.c Added: trunk/reactos/lib/wdmaud/wave.c Deleted: trunk/reactos/lib/wdmaud/wavehdr.c Modified: trunk/reactos/lib/wdmaud/wdmaud.h Modified: trunk/reactos/lib/wdmaud/wdmaud.xml _____
Added: trunk/reactos/lib/wdmaud/README.TXT --- trunk/reactos/lib/wdmaud/README.TXT 2005-12-07 18:33:12 UTC (rev 19953) +++ trunk/reactos/lib/wdmaud/README.TXT 2005-12-07 18:37:49 UTC (rev 19954) @@ -0,0 +1,97 @@
+User-mode Multimedia Driver for WDM Audio +----------------------------------------- + +USAGE +----- +This is a "drop-in" replacement for the Windows XP wdmaud.drv component. To +make use of it, you'll need to disable system file protection somehow (easy +way is to rename all *.cab files in the sub-folders of C:\WINDOWS\DRIVER CACHE +to something else, then delete or rename WDMAUD.DRV in both C:\WINDOWS\SYSTEM32 +and C:\WINDOWS\SYSTEM32\DLLCACHE.) + +Now put the ReactOS wdmaud.drv in C:\WINDOWS\SYSTEM32. At some point, you'll +be asked to insert the Windows CD as some files are missing - cancel this and +choose "yes" when asked if you're sure. This is due to the system file +protection not being able to have its own way. + +That should be all there is to it. + + +IMPLEMENTATION/DEVELOPMENT NOTES +-------------------------------- +The style of driver used here dates back to the days of Windows 3.1. Not much +has changed - the NT 4 Sound Blaster driver exported the same functions and +supported the same message codes as the traditional Windows 3.1 user-mode +drivers did. + +But in XP, something strange happens with WDMAUD (which is why you can't just +put this next to the existing WDMAUD under a different name!) + +It appears that WINMM.DLL treats WDMAUD.DRV differently to other drivers. + +Here's a summary of how things work differently: + +1) It seems DRV_ENABLE and DRV_DISABLE are the only important messages + processed by DriverProc. These open and close the kernel-mode + driver. + +2) Each message handling function (aside from DriverProc) receives a + DRVM_INIT message after the driver has been opened. The second + parameter of this is a pointer to a string containing a device + path (\?... format.) Returning zero seems to be the accepted + thing to do, in any case. + + The purpose of this function (in our case) is to allow WDMAUD.DRV + to let WDMAUD.SYS know which device we want to play with. + + Presumably, this is called when new devices are added to the system, + as well. I don't know if this is called once per hardware device... + +3) xxxx_GETNUMDEVS now has extra data passed to it! The first + parameter is a device path string - which will have been passed + to DRVM_INIT previously. + +4) xxxx_GETDEVCAPS is a bit hazardous. The old set of parameters were: + 1 - Pointer to a capabilities structure (eg: WAVEOUTCAPS) + 2 - Size of the above structure + + But now, the parameters are: + 1 - Pointer to a MDEVCAPSEX struct (which points to a regular + capabilities structure) + 2 - Device path string + + So anything expecting the second parameter to be a size (for copying + memory maybe) is in for a bit of a surprise there! + +The reason for the above changes is Plug and Play. It seems that the extra +functionality was added in Windows 98 (possibly 95 as well) to make it +possible to hot-swap winmm-supported devices without requiring a restart of +whatever applications are using the devices. + +That's the theory, at least. + + +TODO +---- +Our WINMM.DLL will need hacking to make sure it can take into account the +preferential treatment given to WDMAUD.DRV + +I'm not sure if it'll work with it yet as there seems to be some accomodation +for the PnP DRVM_INIT and DRVM_EXIT messages, but whether or not winmm will +function correctly with this WDMAUD.DRV replacement is something that remains +to be seen. + + +THANKS +------ +Thanks to everyone who has encouraged me to continue developing the audio +system for ReactOS. + +In particular, I'd like to thank Alex Ionescu for the many hours of assistance +he has given me in figuring out how things are done. + + +- + +Andrew Greenwood +andrew.greenwood AT silverblade DOT co DOT uk Property changes on: trunk/reactos/lib/wdmaud/README.TXT ___________________________________________________________________ Name: svn:executable + * Name: eol-style + native Property changes on: trunk/reactos/lib/wdmaud/TODO ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/reactos/lib/wdmaud/callbacks.c ___________________________________________________________________ Name: eol-style + native _____
Modified: trunk/reactos/lib/wdmaud/control.c --- trunk/reactos/lib/wdmaud/control.c 2005-12-07 18:33:12 UTC (rev 19953) +++ trunk/reactos/lib/wdmaud/control.c 2005-12-07 18:37:49 UTC (rev 19954) @@ -13,8 +13,13 @@
#include "wdmaud.h"
/* - TODO: - Make these work for the other device types! + StartDevice + + Creates a completion thread for a device, sets the "is_running" member + of the device state to "true", and tells the kernel device to start + processing audio/MIDI data. + + Wave devices always start paused. */
MMRESULT StartDevice(PWDMAUD_DEVICE_INFO device) @@ -25,21 +30,165 @@ result = ValidateDeviceInfoAndState(device);
if ( result != MMSYSERR_NOERROR ) + { + DPRINT1("Device info/state not valid\n"); return result; + }
- ioctl_code = device == WDMAUD_WAVE_IN ? IOCTL_WDMAUD_WAVE_IN_START : - device == WDMAUD_WAVE_OUT ? IOCTL_WDMAUD_WAVE_OUT_START : - 0x0000; + ioctl_code = + IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_START : + IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_START : + IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_START : + 0x0000;
ASSERT( ioctl_code ); + + result = CreateCompletionThread(device); + + if ( MM_FAILURE( result ) ) + { + DPRINT1("Failed to create completion thread\n"); + return result; + } + + device->state->is_running = TRUE; + + result = CallKernelDevice(device, ioctl_code, 0, 0); + + if ( MM_FAILURE( result ) ) + { + DPRINT1("Audio could not be started\n"); + return result; + } + + if ( ! IsWaveDeviceType(device->type) ) + device->state->is_paused = FALSE; + + return result; }
MMRESULT StopDevice(PWDMAUD_DEVICE_INFO device) { + MMRESULT result; + DWORD ioctl_code; + + result = ValidateDeviceInfoAndState(device); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT1("Device info/state not valid\n"); + return result; + } + + ioctl_code = + IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_STOP : + IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_STOP : + IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_STOP : + 0x0000; + + ASSERT( ioctl_code ); + + if ( IsMidiInDeviceType(device->type) ) + { + EnterCriticalSection(device->state->device_queue_guard); + + if ( ! device->state->is_running ) + { + /* TODO: Free the MIDI data queue */ + } + else + { + device->state->is_running = FALSE; + } + + LeaveCriticalSection(device->state->device_queue_guard); + } + else /* wave device */ + { + device->state->is_paused = TRUE; + } + + result = CallKernelDevice(device, ioctl_code, 0, 0); + + if ( MM_FAILURE( result ) ) + { + DPRINT1("Audio could not be stopped\n"); + return result; + } + + if ( IsWaveDeviceType(device-type) ) + { + device->state->is_paused = TRUE; + } + else /* MIDI Device */ + { + /* TODO: Destroy completion thread etc. */ + } + + return result; }
-MMRESULT PauseDevice(PWDMAUD_DEVICE_INFO device) +MMRESULT ResetDevice(PWDMAUD_DEVICE_INFO device) { + MMRESULT result; + DWORD ioctl_code; + + result = ValidateDeviceInfoAndState(device); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT1("Device info/state not valid\n"); + return result; + } + + ioctl_code = + IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_RESET : + IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_RESET : + IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_RESET : + 0x0000; + + ASSERT( ioctl_code ); + + if ( IsMidiInDeviceType(device->type) ) + { + EnterCriticalSection(device->state->device_queue_guard); + + if ( ! device->state->is_running ) + { + /* TODO: Free the MIDI data queue */ + } + else + { + device->state->is_running = FALSE; + } + + LeaveCriticalSection(device->state->device_queue_guard); + } + + result = CallKernelDevice(device, ioctl_code, 0, 0); + + if ( MM_FAILURE( result ) ) + { + DPRINT1("Audio could not be reset\n"); + return result; + } + + if ( IsWaveDeviceType(device->type) ) + { + if ( IsWaveInDeviceType(device->type) ) + device->state->is_paused = TRUE; + else if ( IsWaveOutDeviceType(device->type) ) + device->state->is_paused = FALSE; + + /* TODO: Destroy completion thread + check ret val */ + } + else /* MIDI input device */ + { + /* TODO: Destroy completion thread + check ret val */ + /* TODO - more stuff */ + } + + return result; }
MMRESULT StopDeviceLooping(PWDMAUD_DEVICE_INFO device) Property changes on: trunk/reactos/lib/wdmaud/control.c ___________________________________________________________________ Name: eol-style + native _____
Modified: trunk/reactos/lib/wdmaud/devices.c --- trunk/reactos/lib/wdmaud/devices.c 2005-12-07 18:33:12 UTC (rev 19953) +++ trunk/reactos/lib/wdmaud/devices.c 2005-12-07 18:37:49 UTC (rev 19954) @@ -17,9 +17,15 @@
const char WDMAUD_DEVICE_INFO_SIG[4] = "WADI"; const char WDMAUD_DEVICE_STATE_SIG[4] = "WADS"; -const char WDMAUD_NULL_SIGNATURE[4] = {0,0,0,0};
+/* + IsValidDevicePath + + Just checks to see if the string containing the path to the device path + (object) is a valid, readable string. +*/ + BOOL IsValidDevicePath(WCHAR* path) { if (IsBadReadPtr(path, 1)) /* TODO: Replace with flags */ @@ -33,43 +39,93 @@ return TRUE; }
-MMRESULT ValidateDeviceInfo(PWDMAUD_DEVICE_INFO device_info) +/* + ValidateDeviceData + + Checks that the memory pointed at by the device data pointer is writable, + and that it has a valid signature. + + If the "state" member isn't NULL, the state structure is also validated + in the same way. If the "require_state" parameter is TRUE and the "state" + member is NULL, an error code is returned. Otherwise the "state" member + isn't validated and no error occurs. +*/ + +MMRESULT ValidateDeviceData( + PWDMAUD_DEVICE_INFO device, + BOOL require_state +) { - if ( IsBadWritePtr(device_info, sizeof(WDMAUD_DEVICE_INFO)) ) + if ( IsBadWritePtr(device, sizeof(WDMAUD_DEVICE_INFO)) ) + { + DPRINT1("Device data structure not writable\n"); return MMSYSERR_INVALPARAM; + }
- if ( *device_info->signature != *WDMAUD_DEVICE_INFO_SIG ) + if ( strncmp(device->signature, WDMAUD_DEVICE_INFO_SIG, 4) != 0 ) + { + DPRINT1("Device signature is invalid\n"); return MMSYSERR_INVALPARAM; + }
- return MMSYSERR_NOERROR; -} + if ( ! IsValidDeviceType(device->type) ) + { + DPRINT1("Invalid device type\n"); + return MMSYSERR_INVALPARAM; + }
-MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state) -{ - if ( IsBadWritePtr(state, sizeof(WDMAUD_DEVICE_INFO)) ) + if ( device->id > 100 ) + { + DPRINT1("Device ID is out of range\n"); return MMSYSERR_INVALPARAM; + }
- if ( *state->signature != *WDMAUD_DEVICE_STATE_SIG ) + /* Now we validate the device state (if present) */ + + if ( device->state ) + { + if ( IsBadWritePtr(device->state, sizeof(WDMAUD_DEVICE_INFO)) ) + { + DPRINT1("Device state structure not writable\n"); + return MMSYSERR_INVALPARAM; + } + + if ( strncmp(device->state->signature, + WDMAUD_DEVICE_STATE_SIG, + 4) != 0 ) + { + DPRINT1("Device state signature is invalid\n"); + return MMSYSERR_INVALPARAM; + } + + /* TODO: Validate state events */ + } + else if ( require_state ) + { return MMSYSERR_INVALPARAM; + }
return MMSYSERR_NOERROR; }
+ /* ValidateDeviceStateEvents should be used in conjunction with the standard - state validation routine. -*/ + state validation routine (NOT on its own!)
+ FIXME: The tests are wrong +*/ +/* MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state) { - if ( ( (DWORD) state->exit_thread_event == 0x00000000 ) && + 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 ) && + if ( ( (DWORD) state->queue_event != 0x00000000 ) && ( (DWORD) state->queue_event != 0x42424242 ) && ( (DWORD) state->queue_event != 0x43434343 ) ) { @@ -79,28 +135,32 @@
return MMSYSERR_NOERROR; } +*/
-MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info) -{ - MMRESULT result; +/* + CreateDeviceData
- result = ValidateDeviceInfo(device_info); + This is a glorified memory allocation routine, which acts as a primitive + constructor for a device data structure.
- if ( result != MMSYSERR_NOERROR ) - return result; + It validates the device path given, allocates memory for both the device + data and the device state data, copies the signatures over and sets the + device type accordingly.
- result = ValidateDeviceState(device_info->state); + In some cases, a state structure isn't required, so the creation of one can + be avoided by passing FALSE for the "with_state" parameter. +*/
- if ( result != MMSYSERR_NOERROR ) - return result; - - return MMSYSERR_NOERROR; -} - -PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path) +PWDMAUD_DEVICE_INFO +CreateDeviceData( + CHAR device_type, + DWORD device_id, + WCHAR* device_path, + BOOL with_state +) { - HANDLE heap = 0; - PWDMAUD_DEVICE_INFO device_data = 0; + BOOL success = FALSE; + PWDMAUD_DEVICE_INFO device = 0; int path_size = 0;
DPRINT("Creating device data for device type %d\n", (int) device_type); @@ -115,121 +175,119 @@ 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", + DPRINT("Allocating %d bytes for device data\n", path_size + sizeof(WDMAUD_DEVICE_INFO)); -/* - device_data = (PWDMAUD_DEVICE_INFO) HeapAlloc(heap, - HEAP_ZERO_MEMORY, - path_size + sizeof(WDMAUD_DEVICE_INFO)); -*/
- device_data = (PWDMAUD_DEVICE_INFO) + device = (PWDMAUD_DEVICE_INFO) AllocMem(path_size + sizeof(WDMAUD_DEVICE_INFO));
- if ( ! device_data ) + if ( ! device ) { 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); + /* Copy the signature and device path */ + memcpy(device->signature, WDMAUD_DEVICE_INFO_SIG, 4); + lstrcpy(device->path, device_path);
- DPRINT("Copying path (0x%x)\n", (int)device_path); - lstrcpy(device_data->path, device_path); + /* Initialize these common members */ + device->id = device_id; + device->type = device_type;
- device_data->type = device_type; + if ( with_state ) + { + /* Allocate device state structure */ + device->state = AllocMem(sizeof(WDMAUD_DEVICE_STATE));
+ if ( ! device->state ) + { + DPRINT1("Couldn't allocate memory for device state (error %d)\n", + (int) GetLastError()); + goto cleanup; + } + + /* Copy the signature */ + memcpy(device->state->signature, WDMAUD_DEVICE_STATE_SIG, 4); + } + + success = TRUE; + cleanup : { - /* No cleanup needed (no failures possible after allocation.) */ - DPRINT("Performing cleanup\n"); + if ( ! success ) + { + if ( device ) + { + if ( device->state ) + { + ZeroMemory(device->state->signature, 4); + FreeMem(device->state); + }
- return device_data; + ZeroMemory(device->signature, 4); + FreeMem(device); + } + } + + return (success ? device : NULL); } }
+ /* - CloneDeviceData + DeleteDeviceData
- This isn't all that great... Maybe some macros would be better: - BEGIN_CLONING_STRUCT(source, target) - CLONE_MEMBER(member) - END_CLONING_STRUCT() + Blanks out the device and device state structures, and frees the memory + associated with the structures.
- The main problem is that sometimes we'll want to copy more than - the data presented here. I guess we could blindly copy EVERYTHING - but that'd be excessive. + TODO: Free critical sections / events if set? */
-PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original) +void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data) { - PWDMAUD_DEVICE_INFO clone = NULL; + DPRINT("Deleting device data\n");
- if ( ValidateDeviceInfo(original) != MMSYSERR_NOERROR) - { - DPRINT1("Original device data was invalid\n"); - return NULL; - } + ASSERT( device_data );
- /* This will set the type and path, so we can forget about those */ - clone = CreateDeviceData(original->type, original->path); + /* We don't really care if the structure is valid or not */ + if ( ! device_data ) + return;
- if ( ! clone ) + if ( device_data->state ) { - DPRINT1("Clone creation failed\n"); - return NULL; - } + /* We DON'T want these to be set - should we clean up? */ + ASSERT ( ! device_data->state->device_queue_guard ); + ASSERT ( ! device_data->state->queue_event ); + ASSERT ( ! device_data->state->exit_thread_event );
- clone->id = original->id; - clone->wave_handle = original->wave_handle; /* ok? */ + /* Insert a cow (not sure if this is right or not) */ + device_data->state->sample_size = 0xDEADBEEF;
- /* TODO: Maybe we should copy some more? */ + /* Overwrite the structure with zeroes and free it */ + ZeroMemory(device_data->state, sizeof(WDMAUD_DEVICE_STATE)); + FreeMem(device_data->state); + }
- return clone; + /* Overwrite the structure with zeroes and free it */ + ZeroMemory(device_data, sizeof(WDMAUD_DEVICE_INFO)); + FreeMem(device_data); }
-void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data) -{ - HANDLE heap; +/* + ModifyDevicePresence
- ASSERT( device_data ); + Use this to add or remove devices in the kernel-mode driver. If the + "adding" parameter is TRUE, the device is added, otherwise it is removed.
- /* Erase the signature to prevent any possible future mishaps */ - *device_data->signature = *WDMAUD_NULL_SIGNATURE; + "device_type" is WDMAUD_WAVE_IN, WDMAUD_WAVE_OUT, etc...
- heap = GetProcessHeap(); + "device_path" specifies the NT object path of the device.
- if ( ! heap ) - { - DPRINT1("Couldn't get the process heap (error %d)\n", - (int) GetLastError()); - goto exit; - } + (I'm not sure what happens to devices that are added but never removed.) +*/
- FreeMem(device_data); - /* - { - DPRINT1("Couldn't free device data memory (error %d)\n", - (int) GetLastError()); - } - */ - - exit : - /* We just return */ - return; -} - MMRESULT ModifyDevicePresence( CHAR device_type, WCHAR* device_path, @@ -249,7 +307,7 @@ ASSERT( IsValidDeviceType(device_type) ); ASSERT( device_path );
- device_data = CreateDeviceData(device_type, device_path); + device_data = CreateDeviceData(device_type, 0, device_path, FALSE);
if ( ! device_data ) { @@ -292,6 +350,16 @@ } }
+/* + GetDeviceCount + + Pretty straightforward - pass the device type (WDMAUD_WAVE_IN, ...) and + a topology device (NT object path) to obtain the number of devices + present in that topology of that particular type. + + The topology path is supplied to us by winmm. +*/ + DWORD GetDeviceCount(CHAR device_type, WCHAR* topology_path) { PWDMAUD_DEVICE_INFO device_data; @@ -299,7 +367,7 @@
DPRINT("Topology path %S\n", topology_path);
- device_data = CreateDeviceData(device_type, topology_path); + device_data = CreateDeviceData(device_type, 0, topology_path, FALSE);
if (! device_data) { @@ -339,7 +407,8 @@ This uses a different structure to the traditional documentation, because we handle plug and play devices.
- Much sleep was lost over implementing this. + Much sleep was lost over implementing this. I got the ID and type + parameters the wrong way round! */
MMRESULT GetDeviceCapabilities( @@ -363,7 +432,7 @@
DPRINT("Going to have to query the kernel-mode part\n");
- device = CreateDeviceData(device_type, device_path); + device = CreateDeviceData(device_type, device_id, device_path, FALSE);
if ( ! device ) { @@ -372,8 +441,9 @@ goto cleanup; }
- device->id = device_id; - device->with_critical_section = FALSE; + /* These are not needed as they're already initialized */ + ASSERT( device_id == device->id ); + ASSERT( ! device->with_critical_section );
*(LPWORD)caps->pCaps = (WORD) 0x43;
@@ -400,305 +470,224 @@ } }
-MMRESULT TryOpenDevice( + +/* + OpenDeviceViaKernel + + Internal function to rub the kernel mode part of wdmaud the right way + so it opens a device on our behalf. +*/ + +MMRESULT +OpenDeviceViaKernel( PWDMAUD_DEVICE_INFO device, LPWAVEFORMATEX format ) { - if ( device->id > 0x64 ) /* FIXME */ - { - DPRINT1("device->id > 0x64 ! ???\n"); - return MMSYSERR_BADDEVICEID; /* OK? */ - } + DWORD format_struct_len = 0;
- /* We'll only have a format set for wave devices */ - if ( format ) + DPRINT("Opening device via kernel\n"); + + if ( format->wFormatTag == 1 ) /* FIXME */ { - if ( format->wFormatTag == 1 ) - { - DWORD sample_size; + /* Standard PCM format */ + DWORD sample_size;
- DPRINT("Standard (PCM) format\n"); - sample_size = format->nChannels * format->wBitsPerSample; - device->state->sample_size = sample_size; + DPRINT("Standard (PCM) format\n");
- 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; - } + sample_size = format->nChannels * format->wBitsPerSample; + + device->state->sample_size = sample_size; + + format_struct_len = 16; /* FIXME */ } + else + { + /* Non-standard format */ + return MMSYSERR_NOTSUPPORTED; /* TODO */ + }
- /* If we got this far without error, the format is supported! */ - return MMSYSERR_NOERROR; + return CallKernelDevice(device, + IOCTL_WDMAUD_OPEN_DEVICE, + format_struct_len, + (DWORD)format); }
-MMRESULT OpenWaveDevice( + +/* MOVEME */ +LPCRITICAL_SECTION CreateCriticalSection() +{ + LPCRITICAL_SECTION cs; + + cs = AllocMem(sizeof(CRITICAL_SECTION)); + + if ( ! cs ) + return NULL; + + InitializeCriticalSection(cs); + + return cs; +} + + +/* + OpenDevice + + A generic "open device" function, which makes use of the above function + once parameters have been checked. This is capable of handling both + MIDI and wave devices, which is an improvement over the previous + implementation (which had a lot of duplicate functionality.) +*/ + +MMRESULT +OpenDevice( CHAR device_type, DWORD device_id, - LPWAVEOPENDESC open_details, + LPVOID open_descriptor, DWORD flags, - DWORD user_data + PWDMAUD_DEVICE_INFO* user_data ) { - HANDLE heap = 0; - PWDMAUD_DEVICE_INFO device = NULL; - WCHAR* device_path = NULL; MMRESULT result = MMSYSERR_ERROR; + WCHAR* device_path; + PWDMAUD_DEVICE_INFO device; + LPWAVEFORMATEX format;
- /* ASSERT(open_details); */ + /* As we support both types */ + LPWAVEOPENDESC wave_opendesc = (LPWAVEOPENDESC) open_descriptor; + LPMIDIOPENDESC midi_opendesc = (LPMIDIOPENDESC) open_descriptor;
- heap = GetProcessHeap(); + /* FIXME: Does this just apply to wave, or MIDI also? */ + if ( device_id > 100 ) + return MMSYSERR_BADDEVICEID;
- if ( ! heap ) - { - DPRINT1("Couldn't get the process heap (error %d)\n", - (int) GetLastError()); - result = MMSYSERR_ERROR; - goto cleanup; - } + /* Copy the appropriate dnDevNode value */ + if ( IsWaveDeviceType(device_type) ) + device_path = (WCHAR*) wave_opendesc->dnDevNode; + else if ( IsMidiDeviceType(device_type) ) + device_path = (WCHAR*) midi_opendesc->dnDevNode; + else + return MMSYSERR_INVALPARAM;
- DPRINT("OpenDevice called\n"); + device = CreateDeviceData(device_type, device_id, device_path, TRUE);
- device_path = (WCHAR*) open_details->dnDevNode; - device = CreateDeviceData(device_type, device_path); - if ( ! device ) { - DPRINT1("Couldn't create device data\n"); + DPRINT1("Couldn't allocate memory for 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 ) + if ( ( IsWaveDeviceType(device->type) ) && + ( device->flags & WAVE_FORMAT_QUERY ) ) { - DPRINT("Do I support this format? Hmm...\n"); + result = OpenDeviceViaKernel(device, wave_opendesc->lpFormat);
- result = TryOpenDevice(device, open_details->lpFormat); - if ( result != MMSYSERR_NOERROR ) { - DPRINT("Format not supported\n"); - goto cleanup; + DPRINT1("Format not supported (mmsys error %d)\n", (int) result); + result = WAVERR_BADFORMAT; } - - 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 ) + else { - DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n", - (int) GetLastError()); - result = MMSYSERR_NOMEM; - goto cleanup; + DPRINT("Format supported\n"); + result = MMSYSERR_NOERROR; }
- /* Initialize the critical section */ - InitializeCriticalSection(device->state->queue_critical_section); + goto cleanup; + }
- /* We need these so we can contact the client later */ - device->client_instance = open_details->dwInstance; - device->client_callback = open_details->dwCallback; + device->state->device_queue_guard = CreateCriticalSection();
- /* Reset state */ - device->state->open_descriptor = NULL; - device->state->unknown_24 = 0; + if ( ! device->state->device_queue_guard ) + { + DPRINT1("Couldn't create queue cs\n"); + result = MMSYSERR_NOMEM; + goto cleanup; + }
- device->state->is_running = FALSE; - device->state->is_paused = - device->type == WDMAUD_WAVE_IN ? TRUE : FALSE; + /* Set up the callbacks */ + device->client_instance = IsWaveDeviceType(device->type) + ? wave_opendesc->dwInstance + : midi_opendesc->dwInstance; [truncated at 1000 lines; 2515 more skipped]