Author: silverblade Date: Wed Jul 4 14:17:48 2007 New Revision: 27384
URL: http://svn.reactos.org/svn/reactos?rev=27384&view=rev Log: Removing WDMAUD until I figure out how it can be implemented properly (this will not have any adverse effects as it doesn't actually work yet.) Also replacing MMDRV with a rewritten version as it appears to contain big chunks copied directly from NT4 DDK examples!
Added: trunk/reactos/dll/win32/mmdrv/TODO trunk/reactos/dll/win32/mmdrv/common.c trunk/reactos/dll/win32/mmdrv/kernel.c trunk/reactos/dll/win32/mmdrv/mme.c trunk/reactos/dll/win32/mmdrv/mmioctl.h trunk/reactos/dll/win32/mmdrv/session.c trunk/reactos/dll/win32/mmdrv/wave_io.c Removed: trunk/reactos/dll/win32/wdmaud/ Modified: trunk/reactos/dll/win32/mmdrv/entry.c trunk/reactos/dll/win32/mmdrv/mmdrv.def trunk/reactos/dll/win32/mmdrv/mmdrv.h trunk/reactos/dll/win32/mmdrv/mmdrv.rbuild trunk/reactos/dll/win32/mmdrv/wave.c
Added: trunk/reactos/dll/win32/mmdrv/TODO URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/TODO?rev=27... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/TODO (added) +++ trunk/reactos/dll/win32/mmdrv/TODO Wed Jul 4 14:17:48 2007 @@ -1,0 +1,4 @@ +Jan 2007 TODO list + +* Set WHDR_COMPLETE when WriteFileEx APC is called +* Check for WHDR_COMPLETE flag when completing buffers outside APC
Added: trunk/reactos/dll/win32/mmdrv/common.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/common.c?re... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/common.c (added) +++ trunk/reactos/dll/win32/mmdrv/common.c Wed Jul 4 14:17:48 2007 @@ -1,0 +1,258 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: dll/win32/mmdrv/common.c + * PURPOSE: Multimedia User Mode Driver (Common functions) + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 14, 2007: Created + */ + +#include <mmdrv.h> + +/* + Translates errors to MMRESULT codes. +*/ + +MMRESULT +ErrorToMmResult(UINT error_code) +{ + switch ( error_code ) + { + 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; + }; + + /* If all else fails, it's just a plain old error */ + + return MMSYSERR_ERROR; +} + + +/* + Obtains a device count for a specific kind of device. +*/ + +DWORD +GetDeviceCount(DeviceType device_type) +{ + UINT index = 0; + HANDLE handle; + + /* Cycle through devices until an error occurs */ + + while ( OpenKernelDevice(device_type, index, GENERIC_READ, &handle) == MMSYSERR_NOERROR ) + { + CloseHandle(handle); + index ++; + } + + DPRINT("Found %d devices of type %d\n", index, device_type); + + return index; +} + + +/* + Obtains device capabilities. This could either be done as individual + functions for wave, MIDI and aux, or like this. I chose this method as + it centralizes everything. +*/ + +DWORD +GetDeviceCapabilities( + DeviceType device_type, + DWORD device_id, + PVOID capabilities, + DWORD capabilities_size) +{ + MMRESULT result; + DWORD ioctl; + HANDLE handle; + DWORD bytes_returned; + BOOL device_io_result; + + ASSERT(capabilities); + + /* Choose the right IOCTL for the job */ + + if ( IsWaveDevice(device_type) ) + ioctl = IOCTL_WAVE_GET_CAPABILITIES; + else if ( IsMidiDevice(device_type) ) + ioctl = IOCTL_MIDI_GET_CAPABILITIES; + else if ( IsAuxDevice(device_type) ) + return MMSYSERR_NOTSUPPORTED; /* TODO */ + else + return MMSYSERR_NOTSUPPORTED; + + result = OpenKernelDevice(device_type, + device_id, + GENERIC_READ, + &handle); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT("Failed to open kernel device\n"); + return result; + } + + device_io_result = DeviceIoControl(handle, + ioctl, + NULL, + 0, + (LPVOID) capabilities, + capabilities_size, + &bytes_returned, + NULL); + + /* Translate result */ + + if ( device_io_result ) + result = MMSYSERR_NOERROR; + else + result = ErrorToMmResult(GetLastError()); + + /* Clean up and return */ + + CloseKernelDevice(handle); + + return result; +} + + +/* + A wrapper around OpenKernelDevice that creates a session, + opens the kernel device, initializes session data and notifies + the client (application) that the device has been opened. Again, + this supports any device type and the only real difference is + the open descriptor. +*/ + +DWORD +OpenDevice( + DeviceType device_type, + DWORD device_id, + PVOID open_descriptor, + DWORD flags, + DWORD private_handle) +{ + SessionInfo* session_info; + MMRESULT result; + DWORD message; + + /* This will automatically check for duplicate sessions */ + result = CreateSession(device_type, device_id, &session_info); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT("Couldn't allocate session info\n"); + return result; + } + + result = OpenKernelDevice(device_type, + device_id, + GENERIC_READ, + &session_info->kernel_device_handle); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT("Failed to open kernel device\n"); + DestroySession(session_info); + return result; + } + + /* Set common session data */ + + session_info->flags = flags; + + /* Set wave/MIDI specific data */ + + if ( IsWaveDevice(device_type) ) + { + LPWAVEOPENDESC wave_open_desc = (LPWAVEOPENDESC) open_descriptor; + session_info->callback = wave_open_desc->dwCallback; + session_info->mme_wave_handle = wave_open_desc->hWave; + session_info->app_user_data = wave_open_desc->dwInstance; + } + else + { + DPRINT("Only wave devices are supported at present!\n"); + DestroySession(session_info); + return MMSYSERR_NOTSUPPORTED; + } + + /* Start the processing thread */ + + result = StartSessionThread(session_info); + + if ( result != MMSYSERR_NOERROR ) + { + DestroySession(session_info); + return result; + } + + /* Store the session info */ + + *((SessionInfo**)private_handle) = session_info; + + /* Send the right message */ + + message = (device_type == WaveOutDevice) ? WOM_OPEN : + (device_type == WaveInDevice) ? WIM_OPEN : + (device_type == MidiOutDevice) ? MOM_OPEN : + (device_type == MidiInDevice) ? MIM_OPEN : 0xFFFFFFFF; + + NotifyClient(session_info, message, 0, 0); + + return MMSYSERR_NOERROR; +} + + +/* + Attempts to close a device. This can fail if playback/recording has + not been stopped. We need to make sure it's safe to destroy the + session as well (mainly by killing the session thread.) +*/ + +DWORD +CloseDevice( + DWORD private_handle) +{ + MMRESULT result; + SessionInfo* session_info = (SessionInfo*) private_handle; + /* TODO: Maybe this is best off inside the playback thread? */ + + ASSERT(session_info); + + result = CallSessionThread(session_info, WODM_CLOSE, 0); + + if ( result == MMSYSERR_NOERROR ) + { + /* TODO: Wait for it to be safe to terminate */ + + CloseKernelDevice(session_info->kernel_device_handle); + + DestroySession(session_info); + } + + return result; +} +
Modified: trunk/reactos/dll/win32/mmdrv/entry.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/entry.c?rev... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/entry.c (original) +++ trunk/reactos/dll/win32/mmdrv/entry.c Wed Jul 4 14:17:48 2007 @@ -2,68 +2,42 @@ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Multimedia - * FILE: lib/mmdrv/entry.c - * PURPOSE: Multimedia User Mode Driver + * FILE: dll/win32/mmdrv/entry.c + * PURPOSE: Multimedia User Mode Driver (DriverProc) * PROGRAMMER: Andrew Greenwood - * Aleksey Bragin * UPDATE HISTORY: - * Jan 30, 2004: Imported into ReactOS tree (Greenwood) - * Mar 16, 2004: Cleaned up a bit (Bragin) + * Jan 14, 2007: Created */
+#include <mmdrv.h>
-#include "mmdrv.h"
-#define NDEBUG -#include <debug.h> +/* + Nothing particularly special happens here.
-#define EXPORT __declspec(dllexport) + Back in the days of Windows 3.1, we would do something more useful here, + as this is effectively the old-style equivalent of NT's "DriverEntry", + though far more primitive.
-CRITICAL_SECTION DriverSection; + In summary, we just implement to satisfy the MME API (winmm) requirements. +*/
-APIENTRY LONG DriverProc(DWORD DriverID, HANDLE DriverHandle, UINT Message, - LONG Param1, LONG Param2) +LONG +DriverProc( + DWORD driver_id, + HANDLE driver_handle, + UINT message, + LONG parameter1, + LONG parameter2) { - DPRINT("DriverProc\n"); - -// HINSTANCE Module; - - switch(Message) + switch ( message ) { case DRV_LOAD : DPRINT("DRV_LOAD\n"); - return TRUE; // dont need to do any more -/* - Module = GetDriverModuleHandle(DriverHandle); - - // Create our process heap - Heap = GetProcessHeap(); - if (Heap == NULL) - return FALSE; - - DisableThreadLibraryCalls(Module); - InitializeCriticalSection(&CS); - - // - // Load our device list - // - -// if (sndFindDevices() != MMSYSERR_NOERROR) { -// DeleteCriticalSection(&mmDrvCritSec); -// return FALSE; -// } - - return TRUE; -*/ -// return 1L; + return 1L;
case DRV_FREE : DPRINT("DRV_FREE\n"); - -// TerminateMidi(); -// TerminateWave(); - -// DeleteCriticalSection(&CS); return 1L;
case DRV_OPEN : @@ -82,6 +56,11 @@ DPRINT("DRV_DISABLE\n"); return 1L;
+ /* + We don't provide configuration capabilities. This used to be + for things like I/O port, IRQ, DMA settings, etc. + */ + case DRV_QUERYCONFIGURE : DPRINT("DRV_QUERYCONFIGURE\n"); return 0L; @@ -93,44 +72,11 @@ case DRV_INSTALL : DPRINT("DRV_INSTALL\n"); return DRVCNF_RESTART; + };
- default : - DPRINT("?\n"); - return DefDriverProc(DriverID, DriverHandle, Message, Param1, Param2); - }; + return DefDriverProc(driver_id, + driver_handle, + message, + parameter1, + parameter2); } - - -BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved) -{ - DPRINT("DllMain called!\n"); - - if (Reason == DLL_PROCESS_ATTACH) - { - DisableThreadLibraryCalls(hInstance); - - // Create our heap - Heap = HeapCreate(0, 800, 0); - if (Heap == NULL) - return FALSE; - - InitializeCriticalSection(&CS); - - // OK to do this now?? - FindDevices(); - - } - else if (Reason == DLL_PROCESS_DETACH) - { - // We need to do cleanup here... -// TerminateMidi(); -// TerminateWave(); - - DeleteCriticalSection(&CS); - HeapDestroy(Heap); - } - - return TRUE; -} - -/* EOF */
Added: trunk/reactos/dll/win32/mmdrv/kernel.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/kernel.c?re... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/kernel.c (added) +++ trunk/reactos/dll/win32/mmdrv/kernel.c Wed Jul 4 14:17:48 2007 @@ -1,0 +1,206 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: dll/win32/mmdrv/kernel.c + * PURPOSE: Multimedia User Mode Driver (kernel interface) + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 14, 2007: Created + */ + +#include <mmdrv.h> + +/* + Devices that we provide access to follow a standard naming convention. + The first wave output, for example, appears as \Device\WaveOut0 + + I'm not entirely certain how drivers find a free name to use, or why + we need to strip the leading \Device from it when opening, but hey... +*/ + +MMRESULT +CobbleDeviceName( + DeviceType device_type, + DWORD device_id, + PWCHAR out_device_name) +{ + WCHAR base_device_name[MAX_DEVICE_NAME_LENGTH]; + + /* Work out the base name from the device type */ + + switch ( device_type ) + { + case WaveOutDevice : + wsprintf(base_device_name, L"%ls", WAVE_OUT_DEVICE_NAME); + break; + + case WaveInDevice : + wsprintf(base_device_name, L"%ls", WAVE_IN_DEVICE_NAME); + break; + + case MidiOutDevice : + wsprintf(base_device_name, L"%ls", MIDI_OUT_DEVICE_NAME); + break; + + case MidiInDevice : + wsprintf(base_device_name, L"%ls", MIDI_IN_DEVICE_NAME); + break; + + case AuxDevice : + wsprintf(base_device_name, L"%ls", AUX_DEVICE_NAME); + break; + + default : + return MMSYSERR_BADDEVICEID; + }; + + /* Now append the device number, removing the leading \Device */ + + wsprintf(out_device_name, + L"\\.%ls%d", + base_device_name + strlen("\Device"), + device_id); + + return MMSYSERR_NOERROR; +} + + +/* + Takes a device type (eg: WaveOutDevice), a device ID, desired access and + a pointer to a location that will store the handle of the opened "file" if + the function succeeds. + + The device type and ID are converted into a device name using the above + function. +*/ + +MMRESULT +OpenKernelDevice( + DeviceType device_type, + DWORD device_id, + DWORD access, + HANDLE* handle) +{ + MMRESULT result; + WCHAR device_name[MAX_DEVICE_NAME_LENGTH]; + DWORD open_flags = 0; + + ASSERT(handle); + + /* Glue the base device name and the ID together */ + + result = CobbleDeviceName(device_type, device_id, device_name); + + DPRINT("Opening kernel device %ls\n", device_name); + + if ( result != MMSYSERR_NOERROR ) + return result; + + /* We want overlapped I/O when writing */ + + if ( access != GENERIC_READ ) + open_flags = FILE_FLAG_OVERLAPPED; + + /* Now try opening... */ + + *handle = CreateFile(device_name, + access, + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + open_flags, + NULL); + + if ( *handle == INVALID_HANDLE_VALUE ) + return ErrorToMmResult(GetLastError()); + + return MMSYSERR_NOERROR; +} + + +/* + Just an alias for the benefit of having a pair of functions ;) +*/ + +void +CloseKernelDevice(HANDLE device_handle) +{ + CloseHandle(device_handle); +} + + +MMRESULT +SetDeviceData( + HANDLE device_handle, + DWORD ioctl, + PBYTE input_buffer, + DWORD buffer_size) +{ + DPRINT("SetDeviceData\n"); + /* TODO */ + return 0; +} + + +MMRESULT +GetDeviceData( + HANDLE device_handle, + DWORD ioctl, + PBYTE output_buffer, + DWORD buffer_size) +{ + OVERLAPPED overlap; + DWORD bytes_returned; + BOOL success; + DWORD transfer; + + DPRINT("GetDeviceData\n"); + + memset(&overlap, 0, sizeof(overlap)); + + overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ( ! overlap.hEvent ) + return MMSYSERR_NOMEM; + + success = DeviceIoControl(device_handle, + ioctl, + NULL, + 0, + output_buffer, + buffer_size, + &bytes_returned, + &overlap); + + if ( ! success ) + { + if ( GetLastError() == ERROR_IO_PENDING ) + { + if ( ! GetOverlappedResult(device_handle, &overlap, &transfer, TRUE) ) + { + CloseHandle(overlap.hEvent); + return ErrorToMmResult(GetLastError()); + } + } + else + { + CloseHandle(overlap.hEvent); + return ErrorToMmResult(GetLastError()); + } + } + + while ( TRUE ) + { + SetEvent(overlap.hEvent); + + if ( WaitForSingleObjectEx(overlap.hEvent, 0, TRUE) != WAIT_IO_COMPLETION ) + { + break; + } + } + + CloseHandle(overlap.hEvent); + + return MMSYSERR_NOERROR; +}
Modified: trunk/reactos/dll/win32/mmdrv/mmdrv.def URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/mmdrv.def?r... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/mmdrv.def (original) +++ trunk/reactos/dll/win32/mmdrv/mmdrv.def Wed Jul 4 14:17:48 2007 @@ -7,8 +7,8 @@ LIBRARY mmdrv.dll EXPORTS DriverProc@20 -widMessage@20 +;widMessage@20 wodMessage@20 -midMessage@20 -modMessage@20 -auxMessage@20 +;midMessage@20 +;modMessage@20 +;auxMessage@20
Modified: trunk/reactos/dll/win32/mmdrv/mmdrv.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/mmdrv.h?rev... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/mmdrv.h (original) +++ trunk/reactos/dll/win32/mmdrv/mmdrv.h Wed Jul 4 14:17:48 2007 @@ -2,117 +2,337 @@ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Multimedia - * FILE: lib/mmdrv/mmdrv.h + * FILE: dll/win32/mmdrv/mmdrv.h * PURPOSE: Multimedia User Mode Driver (header) * PROGRAMMER: Andrew Greenwood * Aleksey Bragin * UPDATE HISTORY: * Jan 30, 2004: Imported into ReactOS tree + * Jan 10, 2007: Rewritten and tidied up */
-#ifndef __INCLUDES_MMDRV_H__ -#define __INCLUDES_MMDRV_H__ - -//#define UNICODE - -#define EXPORT __declspec(dllexport) - +#ifndef MMDRV_H +#define MMDRV_H + +#include <mmioctl.h> +#include <mmddk.h>
#include <stdio.h> -#include <windows.h> -#include <mmsystem.h> -#include <mmddk.h> - -// This needs to be done to get winioctl.h to work: -//typedef unsigned __int64 DWORD64, *PDWORD64; - -#include <winioctl.h> -//#include "mmddk.h" - -#include "mmdef.h" - -ULONG DbgPrint(PCCH Format, ...); - -/* -#define SOUND_MAX_DEVICE_NAME 1024 // GUESSWORK -#define SOUND_MAX_DEVICES 256 // GUESSWORK -*/ - -// If the root is \Device and the Device type is -// WaveIn and the device number is 2, the full name is \Device\WaveIn2 - -#define WAVE_IN_DEVICE_NAME "\Device\WaveIn" -#define WAVE_IN_DEVICE_NAME_U L"\Device\WaveIn" -#define WAVE_OUT_DEVICE_NAME "\Device\WaveOut" -#define WAVE_OUT_DEVICE_NAME_U L"\Device\WaveOut" - -#define MIDI_IN_DEVICE_NAME "\Device\MidiIn" -#define MIDI_IN_DEVICE_NAME_U L"\Device\MidiIn" -#define MIDI_OUT_DEVICE_NAME "\Device\MidiOut" -#define MIDI_OUT_DEVICE_NAME_U L"\Device\MidiOut" - -#define AUX_DEVICE_NAME "\Device\MMAux" -#define AUX_DEVICE_NAME_U L"\Device\MMAux" - -/* -#define IOCTL_SOUND_BASE FILE_DEVICE_SOUND -#define IOCTL_WAVE_BASE 0x0000 -#define IOCTL_MIDI_BASE 0x0080 - -// Wave device driver IOCTLs - -#define IOCTL_WAVE_QUERY_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_WAVE_SET_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_WAVE_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_GET_POSITION CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_WAVE_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_WAVE_SET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_GET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_SET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_GET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_BREAK_LOOP CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_WAVE_SET_LOW_PRIORITY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS) - -// MIDI device driver IOCTLs - -#define IOCTL_MIDI_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_MIDI_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_MIDI_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_MIDI_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_MIDI_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) -#define IOCTL_MIDI_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_MIDI_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_MIDI_CACHE_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS) -#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) -*/ - - -CRITICAL_SECTION CS; // Serialize access to device lists - -HANDLE Heap; - - enum { - InvalidDevice, - WaveInDevice, - WaveOutDevice, - MidiInDevice, - MidiOutDevice, - AuxDevice -}; - -MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle, - DWORD Access); - -MMRESULT FindDevices(); - -DWORD GetDeviceCount(UINT DeviceType); - -DWORD TranslateStatus(void); +#define DPRINT printf + + +/* Need to check these */ +#define MAX_DEVICES 256 +#define MAX_DEVICE_NAME_LENGTH 256 +#define MAX_BUFFER_SIZE 1048576 +#define MAX_WAVE_BYTES 1048576 + +/* Custom flag set when overlapped I/O is done */ +#define WHDR_COMPLETE 0x80000000 + + +/* + The kinds of devices which MMSYSTEM/WINMM may request from us. +*/ + +typedef enum +{ + WaveOutDevice, + WaveInDevice, + MidiOutDevice, + MidiInDevice, + AuxDevice +} DeviceType; + +#define IsWaveDevice(devicetype) \ + ( ( devicetype == WaveOutDevice ) || ( devicetype == WaveInDevice ) ) + +#define IsMidiDevice(devicetype) \ + ( ( devicetype == MidiOutDevice ) || ( devicetype == MidiInDevice ) ) + +#define IsAuxDevice(devicetype) \ + ( devicetype == AuxDevice ) + + +/* + We use these structures to store information regarding open devices. Since + the main structure gets destroyed when a device is closed, I call this a + "session". +*/ + +typedef struct +{ + OVERLAPPED overlap; + LPWAVEHDR header; +} WaveOverlapInfo; + +/* +typedef enum +{ + WaveAddBuffer, + WaveClose, + WaveReset, + WaveRestart, + SessionThreadTerminate, + InvalidFunction +} ThreadFunction; +*/ + +/* Our own values, used with the session threads */ +typedef DWORD ThreadFunction; +#define DRVM_TERMINATE 0xFFFFFFFE +#define DRVM_INVALID 0xFFFFFFFF + +typedef enum +{ + WavePlaying, + WaveStopped, + WaveReset, + WaveRestart +} WaveState; + +typedef union +{ + PWAVEHDR wave_header; + PMIDIHDR midi_header; +} MediaHeader; + +/* +typedef union +{ + MediaHeader header; +} ThreadParameter; +*/ + +typedef struct _ThreadInfo +{ + HANDLE handle; + HANDLE ready_event; + HANDLE go_event; + + /*ThreadFunction function;*/ + DWORD function; + PVOID parameter; + + MMRESULT result; +} ThreadInfo; + +typedef struct _LoopInfo +{ + PWAVEHDR head; + DWORD iterations; +} LoopInfo; + +typedef struct _SessionInfo +{ + struct _SessionInfo* next; + + DeviceType device_type; + DWORD device_id; + + HANDLE kernel_device_handle; + + /* These are all the same */ + union + { + HDRVR mme_handle; + HWAVE mme_wave_handle; + HMIDI mme_midi_handle; + }; + + /* If playback is paused or not */ + BOOL is_paused; + + /* Stuff passed to us from winmm */ + DWORD app_user_data; + DWORD callback; + + DWORD flags; + + /* Can only be one or the other */ + union + { + PWAVEHDR wave_queue; + PMIDIHDR midi_queue; + }; + + /* Current playback point */ + //PWAVEHDR next_buffer; + + /* Where in the current buffer we are */ + DWORD buffer_position; + +// DWORD remaining_bytes; + + LoopInfo loop; + + ThreadInfo thread; +} SessionInfo; + +#undef ASSERT +#define ASSERT(condition) \ + if ( ! (condition) ) \ + DPRINT("ASSERT FAILED: %s\n", #condition); + +/* + MME interface +*/ + +BOOL +NotifyClient( + SessionInfo* session_info, + DWORD message, + DWORD parameter1, + DWORD parameter2); + + +/* + Helpers +*/ + +MMRESULT +ErrorToMmResult(UINT error_code); + + +/* Kernel interface */ + +MMRESULT +CobbleDeviceName( + DeviceType device_type, + DWORD device_id, + PWCHAR out_device_name); + +MMRESULT +OpenKernelDevice( + DeviceType device_type, + DWORD device_id, + DWORD access, + HANDLE* handle); + +VOID +CloseKernelDevice(HANDLE device_handle); + +MMRESULT +SetDeviceData( + HANDLE device_handle, + DWORD ioctl, + PBYTE input_buffer, + DWORD buffer_size); + +MMRESULT +GetDeviceData( + HANDLE device_handle, + DWORD ioctl, + PBYTE output_buffer, + DWORD buffer_size); + + +/* Session management */ + +MMRESULT +CreateSession( + DeviceType device_type, + DWORD device_id, + SessionInfo** session_info); + +VOID +DestroySession(SessionInfo* session); + +SessionInfo* +GetSession( + DeviceType device_type, + DWORD device_id); + +MMRESULT +StartSessionThread(SessionInfo* session_info); + +MMRESULT +CallSessionThread( + SessionInfo* session_info, + ThreadFunction function, + PVOID thread_parameter); + +DWORD +HandleBySessionThread( + DWORD private_handle, + DWORD message, + DWORD parameter); + + +/* General */ + +DWORD +GetDeviceCount(DeviceType device_type); + +DWORD +GetDeviceCapabilities( + DeviceType device_type, + DWORD device_id, + PVOID capabilities, + DWORD capabilities_size); + +DWORD +OpenDevice( + DeviceType device_type, + DWORD device_id, + PVOID open_descriptor, + DWORD flags, + DWORD private_handle); + +DWORD +CloseDevice( + DWORD private_handle); + +DWORD +PauseDevice( + DWORD private_handle); + +DWORD +RestartDevice( + DWORD private_handle); + +DWORD +ResetDevice( + DWORD private_handle); + +DWORD +GetPosition( + DWORD private_handle, + PMMTIME time, + DWORD time_size); + +DWORD +BreakLoop(DWORD private_handle); + +DWORD +QueryWaveFormat( + DeviceType device_type, + PVOID lpFormat); + +DWORD +WriteWaveBuffer( + DWORD private_handle, + PWAVEHDR wave_header, + DWORD wave_header_size); + + + + + +/* wave thread */ + +DWORD +WaveThread(LPVOID parameter); + + +/* Wave I/O */ + +VOID +PerformWaveIO(SessionInfo* session_info); + + +CRITICAL_SECTION critical_section; +
#endif
Modified: trunk/reactos/dll/win32/mmdrv/mmdrv.rbuild URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/mmdrv.rbuil... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/mmdrv.rbuild (original) +++ trunk/reactos/dll/win32/mmdrv/mmdrv.rbuild Wed Jul 4 14:17:48 2007 @@ -8,9 +8,11 @@ <library>kernel32</library> <library>user32</library> <library>winmm</library> - <file>auxil.c</file> <file>entry.c</file> - <file>midi.c</file> - <file>utils.c</file> + <file>mme.c</file> + <file>kernel.c</file> + <file>session.c</file> + <file>common.c</file> <file>wave.c</file> + <file>wave_io.c</file> </module>
Added: trunk/reactos/dll/win32/mmdrv/mme.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/mme.c?rev=2... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/mme.c (added) +++ trunk/reactos/dll/win32/mmdrv/mme.c Wed Jul 4 14:17:48 2007 @@ -1,0 +1,143 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: dll/win32/mmdrv/mme.c + * PURPOSE: Multimedia User Mode Driver (MME Interface) + * PROGRAMMER: Andrew Greenwood + * Aleksey Bragin + * UPDATE HISTORY: + * Jan 14, 2007: Rewritten and tidied up + */ + +#include <mmdrv.h> + +/* + Sends a message to the client (application), such as WOM_DONE. This + is just a wrapper around DriverCallback which translates the + parameters appropriately. +*/ + +BOOL +NotifyClient( + SessionInfo* session_info, + DWORD message, + DWORD parameter1, + DWORD parameter2) +{ + return DriverCallback(session_info->callback, + HIWORD(session_info->flags), + session_info->mme_handle, + message, + session_info->app_user_data, + parameter1, + parameter2); +} + + + +/* + MME Driver Entrypoint + Wave Output +*/ + +APIENTRY DWORD +wodMessage( + DWORD device_id, + DWORD message, + DWORD private_handle, + DWORD parameter1, + DWORD parameter2) +{ + switch ( message ) + { + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p80.htm */ + case WODM_GETNUMDEVS : + DPRINT("WODM_GETNUMDEVS\n"); + return GetDeviceCount(WaveOutDevice); + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p6h.htm */ + case WODM_GETDEVCAPS : + DPRINT("WODM_GETDEVCAPS\n"); + return GetDeviceCapabilities(WaveOutDevice, + device_id, + (PVOID) parameter1, + parameter2); + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p85.htm */ + case WODM_OPEN : + { + WAVEOPENDESC* open_desc = (WAVEOPENDESC*) parameter1; + DPRINT("WODM_OPEN\n"); + + if ( parameter2 && WAVE_FORMAT_QUERY ) + return QueryWaveFormat(WaveOutDevice, open_desc->lpFormat); + else + return OpenDevice(WaveOutDevice, + device_id, + open_desc, + parameter2, + private_handle); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p6g.htm */ + case WODM_CLOSE : + { + DPRINT("WODM_CLOSE\n"); + return CloseDevice(private_handle); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p9w.htm */ + case WODM_WRITE : + { + DPRINT("WODM_WRITE\n"); + return WriteWaveBuffer(private_handle, + (PWAVEHDR) parameter1, + parameter2); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p86.htm */ + case WODM_PAUSE : + { + DPRINT("WODM_PAUSE\n"); + return HandleBySessionThread(private_handle, message, 0); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p89.htm */ + case WODM_RESTART : + { + DPRINT("WODM_RESTART\n"); + return HandleBySessionThread(private_handle, message, 0); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p88.htm */ + case WODM_RESET : + { + DPRINT("WODM_RESET\n"); + return HandleBySessionThread(private_handle, message, 0); + } + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p83.htm */ +#if 0 + case WODM_GETPOS : + { + DPRINT("WODM_GETPOS\n"); + return GetPosition(private_handle, + (PMMTIME) parameter1, + parameter2); + } +#endif + + /* http://www.osronline.com/ddkx/w98ddk/mmedia_4p6f.htm */ + case WODM_BREAKLOOP : + { + DPRINT("WODM_BREAKLOOP\n"); + return HandleBySessionThread(private_handle, message, 0); + } + + /* TODO: Others */ + } + + DPRINT("Unsupported message\n"); + return MMSYSERR_NOTSUPPORTED; +}
Added: trunk/reactos/dll/win32/mmdrv/mmioctl.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/mmioctl.h?r... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/mmioctl.h (added) +++ trunk/reactos/dll/win32/mmdrv/mmioctl.h Wed Jul 4 14:17:48 2007 @@ -1,0 +1,147 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: dll/win32/mmdrv/mmioctl.h + * PURPOSE: Multimedia system NT4 compatibility + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 13, 2007: Split from mmdrv.h + */ + +#ifndef MMDRV_IOCTLS +#define MMDRV_IOCTLS + + +#include <windows.h> +#include <mmsystem.h> +#include <mmddk.h> +#include <winioctl.h> + + +/* + Base names of the supported devices, as provided by drivers running in + kernel mode. + + \Device\WaveIn0 etc. +*/ + +#define WAVE_OUT_DEVICE_NAME L"\Device\WaveOut" +#define WAVE_IN_DEVICE_NAME L"\Device\WaveIn" +#define MIDI_OUT_DEVICE_NAME L"\Device\MidiOut" +#define MIDI_IN_DEVICE_NAME L"\Device\MidiIn" +#define AUX_DEVICE_NAME L"\Device\MMAux" + + +/* + Base IOCTL codes +*/ + +#define IOCTL_SOUND_BASE FILE_DEVICE_SOUND +#define IOCTL_WAVE_BASE 0x0000 +#define IOCTL_MIDI_BASE 0x0080 +#define IOCTL_AUX_BASE 0x0100 + + +/* + Wave IOCTLs +*/ + +#define IOCTL_WAVE_QUERY_FORMAT \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_WAVE_SET_FORMAT \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_GET_CAPABILITIES \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_WAVE_SET_STATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_GET_STATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_GET_POSITION \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_SET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_WAVE_GET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_WAVE_SET_PITCH \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_GET_PITCH \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_SET_PLAYBACK_RATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_GET_PLAYBACK_RATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_PLAY \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_RECORD \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_BREAK_LOOP \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_WAVE_SET_LOW_PRIORITY \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS) + + +/* + MIDI IOCTLs +*/ + +#define IOCTL_MIDI_GET_CAPABILITIES \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_MIDI_SET_STATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MIDI_GET_STATE \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MIDI_SET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_MIDI_GET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_MIDI_PLAY \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MIDI_RECORD \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MIDI_CACHE_PATCHES \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MIDI_CACHE_DRUM_PATCHES \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) + + +/* + Aux IOCTLs +*/ + +#define IOCTL_AUX_GET_CAPABILITIES \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_AUX_SET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_AUX_GET_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_SOUND_GET_CHANGED_VOLUME \ + CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) + +#endif
Added: trunk/reactos/dll/win32/mmdrv/session.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/session.c?r... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/session.c (added) +++ trunk/reactos/dll/win32/mmdrv/session.c Wed Jul 4 14:17:48 2007 @@ -1,0 +1,245 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: dll/win32/mmdrv/session.c + * PURPOSE: Multimedia User Mode Driver (session management) + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 14, 2007: Created + */ + +#include <mmdrv.h> + +/* Each session is tracked, but the list must be locked when in use */ + +SessionInfo* session_list = NULL; +CRITICAL_SECTION session_lock; + + +/* + Obtains a pointer to the session associated with a device type and ID. + If no session exists, returns NULL. This is mainly used to see if a + session already exists prior to creating a new one. +*/ + +SessionInfo* +GetSession( + DeviceType device_type, + DWORD device_id) +{ + SessionInfo* session_info; + + EnterCriticalSection(&session_lock); + session_info = session_list; + + while ( session_info ) + { + if ( ( session_info->device_type == device_type ) && + ( session_info->device_id == device_id ) ) + { + LeaveCriticalSection(&session_lock); + return session_info; + } + + session_info = session_info->next; + } + + LeaveCriticalSection(&session_lock); + return NULL; +} + + +/* + Creates a new session, associated with the specified device type and ID. + Whilst the session list is locked, this also checks to see if an existing + session is associated with the device. +*/ + +MMRESULT +CreateSession( + DeviceType device_type, + DWORD device_id, + SessionInfo** session_info) +{ + HANDLE heap = GetProcessHeap(); + + ASSERT(session_info); + + EnterCriticalSection(&session_lock); + + /* Ensure we're not creating a duplicate session */ + + if ( GetSession(device_type, device_id) ) + { + DPRINT("Already allocated session\n"); + LeaveCriticalSection(&session_lock); + return MMSYSERR_ALLOCATED; + } + + *session_info = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(SessionInfo)); + + if ( ! *session_info ) + { + DPRINT("Failed to allocate mem for session info\n"); + LeaveCriticalSection(&session_lock); + return MMSYSERR_NOMEM; + } + + (*session_info)->device_type = device_type; + (*session_info)->device_id = device_id; + + /* Add to the list */ + + (*session_info)->next = session_list; + session_list = *session_info; + + LeaveCriticalSection(&session_lock); + + return MMSYSERR_NOERROR; +} + + +/* + Removes a session from the list and destroys it. This function does NOT + perform any additional cleanup. Think of it as a slightly more advanced + free() +*/ + +VOID +DestroySession(SessionInfo* session) +{ + HANDLE heap = GetProcessHeap(); + SessionInfo* session_node; + SessionInfo* session_prev; + + /* TODO: More cleanup stuff */ + + /* Remove from the list */ + + EnterCriticalSection(&session_lock); + + session_node = session_list; + session_prev = NULL; + + while ( session_node ) + { + if ( session_node == session ) + { + /* Bridge the gap for when we go */ + session_prev->next = session->next; + break; + } + + /* Save the previous node, fetch the next */ + session_prev = session_node; + session_node = session_node->next; + } + + LeaveCriticalSection(&session_lock); + + HeapFree(heap, 0, session); +} + + +/* + Allocates events and other resources for the session thread, starts it, + and waits for it to announce that it is ready to work for us. +*/ + +MMRESULT +StartSessionThread(SessionInfo* session_info) +{ + LPTASKCALLBACK task; + MMRESULT result; + + ASSERT(session_info); + + /* This is our "ready" event, sent when the thread is idle */ + + session_info->thread.ready_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ( ! session_info->thread.ready_event ) + { + DPRINT("Couldn't create thread_ready event\n"); + return MMSYSERR_NOMEM; + } + + /* This is our "go" event, sent when we want the thread to do something */ + + session_info->thread.go_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ( ! session_info->thread.go_event ) + { + DPRINT("Couldn't create thread_go event\n"); + CloseHandle(session_info->thread.ready_event); + return MMSYSERR_NOMEM; + } + + /* TODO - other kinds of devices need attention, too */ + task = ( session_info->device_type == WaveOutDevice ) + ? (LPTASKCALLBACK) WaveThread : NULL; + + ASSERT(task); + + /* Effectively, this is a beefed-up CreateThread */ + + result = mmTaskCreate(task, + &session_info->thread.handle, + (DWORD) session_info); + + if ( result != MMSYSERR_NOERROR ) + { + DPRINT("Task creation failed\n"); + CloseHandle(session_info->thread.ready_event); + CloseHandle(session_info->thread.go_event); + return result; + } + + /* Wait for the thread to be ready before completing */ + + WaitForSingleObject(session_info->thread.ready_event, INFINITE); + + return MMSYSERR_NOERROR; +} + + +/* + The session thread is pretty simple. Upon creation, it announces that it + is ready to do stuff for us. When we want it to perform an action, we use + CallSessionThread with an appropriate function and parameter, then tell + the thread we want it to do something. When it's finished, it announces + that it is ready once again. +*/ + +MMRESULT +CallSessionThread( + SessionInfo* session_info, + ThreadFunction function, + PVOID thread_parameter) +{ + ASSERT(session_info); + + session_info->thread.function = function; + session_info->thread.parameter = thread_parameter; + + DPRINT("Calling session thread\n"); + SetEvent(session_info->thread.go_event); + + DPRINT("Waiting for thread response\n"); + WaitForSingleObject(session_info->thread.ready_event, INFINITE); + + return session_info->thread.result; +} + + +DWORD +HandleBySessionThread( + DWORD private_handle, + DWORD message, + DWORD parameter) +{ + return CallSessionThread((SessionInfo*) private_handle, + message, + (PVOID) parameter); +}
Modified: trunk/reactos/dll/win32/mmdrv/wave.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/wave.c?rev=... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/wave.c (original) +++ trunk/reactos/dll/win32/mmdrv/wave.c Wed Jul 4 14:17:48 2007 @@ -2,1047 +2,380 @@ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Multimedia - * FILE: lib/mmdrv/wave.c - * PURPOSE: Multimedia User Mode Driver + * FILE: dll/win32/mmdrv/wave.c + * PURPOSE: Multimedia User Mode Driver (Wave Audio) * PROGRAMMER: Andrew Greenwood - * Aleksey Bragin (aleksey at studiocerebral.com) * UPDATE HISTORY: - * Jan 30, 2004: Imported into ReactOS tree (Greenwood) - * Mar 16, 2004: Implemented some funcs (Bragin) + * Jan 30, 2004: Imported into ReactOS tree + * Jan 14, 2007: Rewritten and tidied up */
-#include "mmdrv.h" -#include "wave.h" - -#define NDEBUG -#include <debug.h> - -#define WHDR_COMPLETE 0x80000000 -#define MAX_BUFFER_SIZE 8192 -#define MAX_WAVE_BYTES 5*MAX_BUFFER_SIZE - -PWAVEALLOC WaveLists; - -static MMRESULT waveReadWrite(PWAVEALLOC pClient); -void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); -void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); -void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); -void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); -static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State); - -/* ============================ - * INTERNAL - * functions start here - * ============================ - */ - -MMRESULT GetDeviceCapabilities(DWORD ID, UINT DeviceType, - LPBYTE pCaps, DWORD Size) -{ - HANDLE DeviceHandle = NULL; - MMRESULT Result = MMSYSERR_NOERROR; - DWORD BytesReturned = 0; - - // Open the wave device - - Result = OpenDevice(DeviceType, ID, &DeviceHandle, GENERIC_READ); - if (Result != MMSYSERR_NOERROR) - return Result; - - if ((DeviceType == WaveOutDevice) || (DeviceType == WaveInDevice)) - { - Result = DeviceIoControl(DeviceHandle, IOCTL_WAVE_GET_CAPABILITIES, - NULL, 0, (LPVOID)pCaps, Size, - &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); - } - - else if ((DeviceType == MidiInDevice) || (DeviceType == MidiOutDevice)) - { - Result = DeviceIoControl(DeviceHandle, IOCTL_MIDI_GET_CAPABILITIES, - NULL, 0, (LPVOID)pCaps, Size, - &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); - } - - else if (DeviceType == AuxDevice) - { - Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_GET_CAPABILITIES, - NULL, 0, (LPVOID)pCaps, Size, - &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); - } - - // Close the handle and return the result code - CloseHandle(DeviceHandle); - - return Result; -} - -static DWORD waveThread(LPVOID lpParameter) -{ - - PWAVEALLOC pClient = (PWAVEALLOC)lpParameter; - BOOL Terminate = FALSE; - +#include <mmdrv.h> + + +#define MAX_WAVE_BUFFER_SIZE 65536 + + +MMRESULT +QueueWaveBuffer( + SessionInfo* session_info, + LPWAVEHDR wave_header) +{ + PWAVEHDR queue_node, previous_node; + DPRINT("Queueing wave buffer\n"); + + if ( ! wave_header ) + { + return MMSYSERR_INVALPARAM; + } + + if ( ! wave_header->lpData ) + { + return MMSYSERR_INVALPARAM; + } + + /* Headers must be prepared first */ + if ( ! ( wave_header->dwFlags & WHDR_PREPARED ) ) + { + DPRINT("I was given a header which hasn't been prepared yet!\n"); + return WAVERR_UNPREPARED; + } + + /* ...and they must not already be in the playing queue! */ + if ( wave_header->dwFlags & WHDR_INQUEUE ) + { + DPRINT("I was given a header for a buffer which is already playing\n"); + return WAVERR_STILLPLAYING; + } + + /* Initialize */ + wave_header->dwBytesRecorded = 0; + + /* Clear the DONE bit, and mark the buffer as queued */ + wave_header->dwFlags &= ~WHDR_DONE; + wave_header->dwFlags |= WHDR_INQUEUE; + + /* Save our handle in the header */ + wave_header->reserved = (DWORD) session_info; + + /* Locate the end of the queue */ + previous_node = NULL; + queue_node = session_info->wave_queue; + + while ( queue_node ) + { + previous_node = queue_node; + queue_node = queue_node->lpNext; + } + + /* Go back a step to obtain the previous node (non-NULL) */ + queue_node = previous_node; + + /* Append our buffer here, and terminate the queue */ + queue_node->lpNext = wave_header; + wave_header->lpNext = NULL; + + /* When no buffers are playing there's no play queue so we start one */ +#if 0 + if ( ! session_info->next_buffer ) + { + session_info->buffer_position = 0; + session_info->next_buffer = wave_header; + } +#endif + + /* Pass to the driver - happens automatically during playback */ +// return PerformWaveIO(session_info); + return MMSYSERR_NOERROR; +} + +VOID +ReturnCompletedBuffers(SessionInfo* session_info) +{ + PWAVEHDR header = NULL; + + /* Set the current header and test to ensure it's not NULL */ + while ( ( header = session_info->wave_queue ) ) + { + if ( header->dwFlags & WHDR_DONE ) + { + DWORD message; + + /* Mark as done, and unqueued */ + header->dwFlags &= ~WHDR_INQUEUE; + header->dwFlags |= WHDR_DONE; + + /* Trim it from the start of the queue */ + session_info->wave_queue = header->lpNext; + + /* Choose appropriate notification */ + message = (session_info->device_type == WaveOutDevice) ? WOM_DONE : + WIM_DATA; + + DPRINT("Notifying client that buffer 0x%x is done\n", (int) header); + + /* Notify the client */ + NotifyClient(session_info, message, (DWORD) header, 0); + } + } + + /* TODO: Perform I/O as a new buffer may have arrived */ +} + + +/* + Each thread function/request is packed into the SessionInfo structure + using a function ID and a parameter (in some cases.) When the function + completes, the function code is set to an "invalid" value. This is, + effectively, a hub for operations where sound driver I/O is concerned. + It handles MME message codes so is a form of deferred wodMessage(). +*/ + +DWORD +ProcessSessionThreadRequest(SessionInfo* session_info) +{ + MMRESULT result = MMSYSERR_NOERROR; + + switch ( session_info->thread.function ) + { + case WODM_WRITE : + { + result = QueueWaveBuffer(session_info, + (LPWAVEHDR) session_info->thread.parameter); + break; + } + + case WODM_RESET : + { + /* TODO */ + break; + } + + case WODM_PAUSE : + { + /* TODO */ + break; + } + + case WODM_RESTART : + { + /* TODO */ + break; + } + + case WODM_GETPOS : + { + /* TODO */ + break; + } + + case WODM_SETPITCH : + { + result = SetDeviceData(session_info->kernel_device_handle, + IOCTL_WAVE_SET_PITCH, + (PBYTE) session_info->thread.parameter, + sizeof(DWORD)); + break; + } + + case WODM_GETPITCH : + { + result = GetDeviceData(session_info->kernel_device_handle, + IOCTL_WAVE_GET_PITCH, + (PBYTE) session_info->thread.parameter, + sizeof(DWORD)); + break; + } + + case WODM_SETVOLUME : + { + break; + } + + case WODM_GETVOLUME : + { +#if 0 + result = GetDeviceData(session_info->kernel_device_handle, + IOCTL_WAVE_GET_VOLUME, + (PBYTE) session_info->thread.parameter,); +#endif + break; + } + + case WODM_SETPLAYBACKRATE : + { + result = SetDeviceData(session_info->kernel_device_handle, + IOCTL_WAVE_SET_PLAYBACK_RATE, + (PBYTE) session_info->thread.parameter, + sizeof(DWORD)); + break; + } + + case WODM_GETPLAYBACKRATE : + { + result = GetDeviceData(session_info->kernel_device_handle, + IOCTL_WAVE_GET_PLAYBACK_RATE, + (PBYTE) session_info->thread.parameter, + sizeof(DWORD)); + break; + } + + case WODM_CLOSE : + { + DPRINT("Thread was asked if OK to close device\n"); + + if ( session_info->wave_queue != NULL ) + result = WAVERR_STILLPLAYING; + else + result = MMSYSERR_NOERROR; + + break; + } + + case DRVM_TERMINATE : + { + DPRINT("Terminating thread...\n"); + result = MMSYSERR_NOERROR; + break; + } + + default : + { + DPRINT("INVALID FUNCTION\n"); + result = MMSYSERR_ERROR; + break; + } + } + + /* We're done with the function now */ + + return result; +} + + +/* + The wave "session". This starts, sets itself as high priority, then waits + for the "go" event. When this occurs, it processes the requested function, + tidies up any buffers that have finished playing, sends new buffers to the + sound driver, then continues handing finished buffers back to the calling + application until it's asked to do something else. +*/ + +DWORD +WaveThread(LPVOID parameter) +{ + MMRESULT result = MMSYSERR_ERROR; + SessionInfo* session_info = (SessionInfo*) parameter; + BOOL terminate = FALSE; + + /* All your CPU time are belong to us */ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - SetEvent(pClient->AuxEvent2); - WaitForSingleObject(pClient->AuxEvent1, INFINITE); - - for (;;) - { - switch (pClient->AuxFunction) - { - case WaveThreadAddBuffer: - { - LPWAVEHDR *pHdrSearching; - - if (pClient->DeviceType == WaveInDevice) - pClient->AuxParam.pHdr->dwBytesRecorded = 0; - - pHdrSearching = &pClient->DeviceQueue; - pClient->AuxParam.pHdr->lpNext = NULL; - - while (*pHdrSearching) - { - pHdrSearching = &(*pHdrSearching)->lpNext; - } - - if (pClient->NextBuffer == NULL) - { - pClient->BufferPosition = 0; - pClient->NextBuffer = pClient->AuxParam.pHdr; - - } - - *pHdrSearching = pClient->AuxParam.pHdr; - - pClient->AuxReturnCode = waveReadWrite(pClient); - } - break; - - case WaveThreadSetState: - pClient->AuxReturnCode = waveSetState(pClient, pClient->AuxParam.State); - - if (pClient->AuxParam.State == WAVE_DD_RESET) - { - PWAVEHDR pHdr; - - pClient->LoopHead = NULL; - pClient->AuxReturnCode = MMSYSERR_NOERROR; - for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) - { - pHdr->dwFlags |= WHDR_COMPLETE; - } - - pClient->BufferPosition = 0; - pClient->NextBuffer = NULL; - } - else - { - if (pClient->DeviceType == WaveInDevice && pClient->AuxReturnCode == MMSYSERR_NOERROR) - { - if (pClient->AuxParam.State == WAVE_DD_STOP) - { - if (pClient->DeviceQueue) - { - while (!(pClient->DeviceQueue->dwFlags & WHDR_COMPLETE) && - pClient->BytesOutstanding != 0) - { - waveSetState(pClient, WAVE_DD_RECORD); - pClient->AuxReturnCode = waveSetState(pClient, WAVE_DD_STOP); - if (pClient->AuxReturnCode != MMSYSERR_NOERROR) - break; - - } - if (pClient->AuxReturnCode == MMSYSERR_NOERROR) - { - pClient->DeviceQueue->dwFlags |= WHDR_COMPLETE; - if (pClient->NextBuffer == pClient->DeviceQueue) - { - pClient->NextBuffer = pClient->DeviceQueue->lpNext; - pClient->BufferPosition = 0; - } - } - } - } - else - { - if (pClient->AuxParam.State == WAVE_DD_RECORD) - pClient->AuxReturnCode = waveReadWrite(pClient); - } - } - } - - break; - - case WaveThreadGetData: - { - OVERLAPPED Overlap; - DWORD BytesReturned; - - // FIXME - // Assert(hDev != NULL); - - memset(&Overlap, 0, sizeof(Overlap)); - - Overlap.hEvent = pClient->Event; - - if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, NULL, 0, - pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen, - &BytesReturned, &Overlap)) - { - DWORD cbTransfer; - - if (GetLastError() != ERROR_IO_PENDING) - pClient->AuxReturnCode = TranslateStatus(); - else - { - - if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) - pClient->AuxReturnCode = TranslateStatus(); - } - } - else - { - while (SetEvent(pClient->Event) && WaitForSingleObjectEx(pClient->Event, 0, TRUE) == - WAIT_IO_COMPLETION) {} - - pClient->AuxReturnCode = MMSYSERR_NOERROR; - } - } - break; - - case WaveThreadSetData: - { - OVERLAPPED Overlap; - DWORD BytesReturned; - memset((PVOID)&Overlap, 0, sizeof(Overlap)); - Overlap.hEvent = pClient->Event; - - if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, - pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen, - NULL, 0, &BytesReturned, &Overlap)) - { - DWORD cbTransfer; - if (GetLastError() == ERROR_IO_PENDING) - { - if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) - pClient->AuxReturnCode = TranslateStatus(); - } - else - pClient->AuxReturnCode = TranslateStatus(); - - } - else - { - while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {} - - pClient->AuxReturnCode = MMSYSERR_NOERROR; - } - } - break; - - case WaveThreadBreakLoop: - pClient->AuxReturnCode = MMSYSERR_NOERROR; - if (pClient->LoopHead) - pClient->LoopCount = 0; - break; - - case WaveThreadClose: - if (pClient->DeviceQueue != NULL) - pClient->AuxReturnCode = WAVERR_STILLPLAYING; - else - pClient->AuxReturnCode = MMSYSERR_NOERROR; - break; - - case WaveThreadTerminate: - Terminate = TRUE; - break; - - default: - DPRINT("WaveThread Error"); - break; - - } - - pClient->AuxFunction = WaveThreadInvalid; - - while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) - { - PWAVEHDR pHdr; - PWAVEALLOC pWav; - - pHdr = pClient->DeviceQueue; - pClient->DeviceQueue = pHdr->lpNext; - - pHdr->dwFlags &= ~WHDR_COMPLETE; - pHdr->dwFlags &= ~WHDR_INQUEUE; - pHdr->lpNext = NULL; - pHdr->dwFlags |= WHDR_DONE; - - pWav = (PWAVEALLOC)pHdr->reserved; - - if (pWav->dwCallback) - { - DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave, - pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, - pWav->dwInstance, (DWORD)pHdr, 0L); - } - } - - waveReadWrite(pClient); - - if (Terminate) return 1; - SetEvent(pClient->AuxEvent2); - while (WaitForSingleObjectEx(pClient->AuxEvent1, INFINITE, TRUE) == WAIT_IO_COMPLETION) - { - while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) - { - PWAVEHDR pHdr; - PWAVEALLOC pWav; - - pHdr = pClient->DeviceQueue; - pClient->DeviceQueue = pHdr->lpNext; - - pHdr->dwFlags &= ~WHDR_COMPLETE; - pHdr->dwFlags &= ~WHDR_INQUEUE; - pHdr->lpNext = NULL; - pHdr->dwFlags |= WHDR_DONE; - - pWav = (PWAVEALLOC)pHdr->reserved; - - if (pWav->dwCallback) - { - DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave, - pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, - pWav->dwInstance, (DWORD)pHdr, 0L); - } - } - - waveReadWrite(pClient); - } - } - - - return MMSYSERR_NOERROR; -} - - -static MMRESULT waveReadWrite(PWAVEALLOC pClient) -{ - DWORD dwSize; - BOOL Result = FALSE; - - - while (pClient->NextBuffer) - { - PWAVEHDR pHdr; - - pHdr = pClient->NextBuffer; - - //FIXME - //assert(!(pHdr->dwFlags & (WHDR_DONE | WHDR_COMPLETE))); - //assert(pClient->DeviceQueue != NULL); - - - dwSize = pHdr->dwBufferLength - pClient->BufferPosition; - if (dwSize > MAX_BUFFER_SIZE) - dwSize = MAX_BUFFER_SIZE; - - - if (dwSize + pClient->BytesOutstanding <= MAX_WAVE_BYTES) - { - PWAVEOVL pWaveOvl; - - if (pClient->BufferPosition == 0) - { - if (pClient->NextBuffer && (pClient->NextBuffer->dwFlags & WHDR_BEGINLOOP) && - pClient->NextBuffer != pClient->LoopHead) - { - pClient->LoopCount = pClient->NextBuffer->dwLoops; - pClient->LoopHead = pClient->NextBuffer; - if (pClient->LoopCount > 0) - pClient->LoopCount--; - } - - if (pClient->LoopCount == 0) - pClient->LoopHead = NULL; - } - - pWaveOvl = (PWAVEOVL)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(*pWaveOvl)); - - if (pWaveOvl == NULL) - return MMSYSERR_NOMEM; - - pWaveOvl->WaveHdr = pHdr; - - if (pClient->DeviceType == WaveOutDevice) - { - Result = WriteFileEx(pClient->hDev, - (PBYTE)pHdr->lpData + pClient->BufferPosition, - dwSize, - (LPOVERLAPPED)pWaveOvl, - (LPOVERLAPPED_COMPLETION_ROUTINE) - (pHdr->dwBufferLength != - pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ? - waveLoopOvl : waveOvl)); - } - else if (pClient->DeviceType == WaveInDevice) - { - Result = ReadFileEx(pClient->hDev, (PBYTE)pHdr->lpData + pClient->BufferPosition, - dwSize, (LPOVERLAPPED)pWaveOvl, - (LPOVERLAPPED_COMPLETION_ROUTINE) - (pHdr->dwBufferLength != - pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ? - waveLoopOvl : waveOvl)); - } - - - if (!Result && GetLastError() != ERROR_IO_PENDING) - { - HeapFree(Heap, 0, (LPSTR)pWaveOvl); - - if (pClient->BytesOutstanding == 0) - { - PWAVEHDR pHdr; - for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) - { - pHdr->dwFlags |= WHDR_COMPLETE; - } - - pClient->NextBuffer = NULL; - pClient->BufferPosition = 0; - - } - return TranslateStatus(); - - } - else - { - pClient->BufferPosition += dwSize; - pClient->BytesOutstanding += dwSize; - if (pClient->BufferPosition == pHdr->dwBufferLength) - { - - if (!pClient->LoopHead || !(pHdr->dwFlags & WHDR_ENDLOOP)) - pClient->NextBuffer = pHdr->lpNext; - else - { - if (pClient->LoopCount != 0) - { - pClient->NextBuffer = pClient->LoopHead; - pClient->LoopCount--; - } - else - { - pClient->DummyWaveOvl.WaveHdr = pClient->LoopHead; - - Result = WriteFileEx(pClient->hDev, (PVOID)pHdr->lpData, 0, - &pClient->DummyWaveOvl.Ovl, - (LPOVERLAPPED_COMPLETION_ROUTINE)waveBreakOvl); - - if (Result || GetLastError() == ERROR_IO_PENDING) - { - pClient->NextBuffer = pHdr->lpNext; - pClient->LoopHead = NULL; - - } - } - } - pClient->BufferPosition = 0; - } - } - - - } - else - break; - } - return MMSYSERR_NOERROR; -} -static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State) -{ - OVERLAPPED Overlap; - DWORD BytesReturned; - - memset((PVOID)&Overlap, 0, sizeof(Overlap)); - - Overlap.hEvent = pClient->Event; - - if (!DeviceIoControl(pClient->hDev, IOCTL_WAVE_SET_STATE, - &State, sizeof(State), NULL, 0, &BytesReturned, &Overlap)) - { - DWORD cbTransfer; - if (GetLastError() == ERROR_IO_PENDING) - { - if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) - return TranslateStatus(); - } - else - return TranslateStatus(); - - } - - while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {} - return MMSYSERR_NOERROR; -} - -void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) -{ - LPWAVEHDR pHdr; - PWAVEALLOC pClient; - - pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; - pClient = (PWAVEALLOC)pHdr->reserved; - - - /* FIXME - Assert(pHdr->dwFlags & WHDR_INQUEUE); - Assert(!(pHdr->dwFlags & WHDR_COMPLETE)); - */ - - pClient->BytesOutstanding -= MAX_BUFFER_SIZE; - - if (pClient->DeviceType == WaveInDevice) - pHdr->dwBytesRecorded += BytesTransferred; - HeapFree(Heap, 0, (LPSTR)pOverlapped); -} - -void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) -{ - ((PWAVEOVL)pOverlapped)->WaveHdr->dwFlags |= WHDR_COMPLETE; -} - -void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) -{ - DWORD dwFlags; - PWAVEHDR pHdr; - - pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; - dwFlags = pHdr->dwFlags; - waveOvl(dwErrorCode, BytesTransferred, pOverlapped); - pHdr->dwFlags = dwFlags; -} - -void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) -{ - PWAVEHDR pHdr; - PWAVEALLOC pClient; - - pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; - pClient = (PWAVEALLOC)pHdr->reserved; - - /* FIXME - Assert(pHdr->dwFlags & WHDR_INQUEUE); - Assert(!(pHdr->dwFlags & WHDR_COMPLETE)); - */ - - pHdr->dwFlags |= WHDR_COMPLETE; - - if (pHdr->dwFlags & WHDR_BEGINLOOP) - { - PWAVEHDR pHdrSearch; - for (pHdrSearch = pClient->DeviceQueue ; pHdrSearch != pHdr ; pHdrSearch = pHdrSearch->lpNext) - { - //Assert(pHdrSearch != NULL); - pHdrSearch->dwFlags |= WHDR_COMPLETE; - } - } - - if (pHdr->dwBufferLength) - pClient->BytesOutstanding -= (pHdr->dwBufferLength - 1) % MAX_BUFFER_SIZE + 1; - - if (pClient->DeviceType == WaveInDevice) - pHdr->dwBytesRecorded += BytesTransferred; - - HeapFree(Heap, 0, (LPSTR)pOverlapped); - -} - - - - - -static MMRESULT OpenWaveDevice(UINT DeviceType, - DWORD id, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) -{ - // TODO: Implement - PWAVEALLOC pClient = (PWAVEALLOC)dwUser; - MMRESULT mResult; - BOOL Result; - DWORD BytesReturned; - LPWAVEFORMATEX pFormats; - PWAVEALLOC *pUserHandle = NULL; - HANDLE hDevice; - - pFormats = (LPWAVEFORMATEX)((LPWAVEOPENDESC)dwParam1)->lpFormat; - - if (dwParam2 & WAVE_FORMAT_QUERY) - { - mResult = OpenDevice(DeviceType, id, &hDevice, GENERIC_READ); - if (mResult != MMSYSERR_NOERROR) - return mResult; - - Result = DeviceIoControl(hDevice, IOCTL_WAVE_QUERY_FORMAT, (PVOID)pFormats, - pFormats->wFormatTag == WAVE_FORMAT_PCM ? - sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize, - NULL, 0, &BytesReturned, NULL); - CloseHandle(hDevice); - return Result ? MMSYSERR_NOERROR : GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus(); - } - - EnterCriticalSection(&CS); - - for (pClient = WaveLists; pClient != NULL; pClient = pClient->Next) - { - if (pClient->DeviceNumber == id && pClient->DeviceType == DeviceType) - { - if (pClient->hDev != INVALID_HANDLE_VALUE) - { - LeaveCriticalSection(&CS); - return MMSYSERR_ALLOCATED; - } - break; - } - } - - if (pClient == NULL) - { - pClient = (PWAVEALLOC)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(WAVEALLOC)); - if (pClient == NULL) - { - LeaveCriticalSection(&CS); - return MMSYSERR_NOMEM; - } - - pClient->DeviceNumber = id; - pClient->Next = WaveLists; - pClient->DeviceType = DeviceType; - - WaveLists = pClient; - } - - pClient->hWave = ((LPWAVEOPENDESC)dwParam1)->hWave; - pClient->dwInstance = ((LPWAVEOPENDESC)dwParam1)->dwInstance; - pClient->dwFlags = dwParam2; - pClient->dwCallback = ((LPWAVEOPENDESC)dwParam1)->dwCallback; - pClient->hDev = INVALID_HANDLE_VALUE; - pClient->NextBuffer = NULL; - pClient->DeviceQueue = NULL; - pClient->LoopHead = NULL; - pClient->LoopCount = 0; - pClient->BytesOutstanding = 0; - pClient->BufferPosition = 0; - - - - - mResult = OpenDevice(DeviceType, id, &pClient->hDev, (GENERIC_READ | GENERIC_WRITE)); - - if (mResult != MMSYSERR_NOERROR) - { - LeaveCriticalSection(&CS); - return mResult; - } - - if (!DeviceIoControl(pClient->hDev,IOCTL_WAVE_SET_FORMAT, (PVOID)pFormats, - pFormats->wFormatTag == WAVE_FORMAT_PCM ? - sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize, - NULL, 0, &BytesReturned, NULL)) - { - CloseHandle(pClient->hDev); - pClient->hDev = INVALID_HANDLE_VALUE; - LeaveCriticalSection(&CS); - return GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus(); - } - - LeaveCriticalSection(&CS); - - if (!pClient->Event) - { - pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (pClient->Event == NULL) - { - // Cleanup - return MMSYSERR_NOMEM; - } - - pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!pClient->AuxEvent1) - { - // Cleanup - return MMSYSERR_NOMEM; - } - - pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!pClient->AuxEvent2) - { - // Cleanup - return MMSYSERR_NOMEM; - } - - - mResult = mmTaskCreate((LPTASKCALLBACK)waveThread, &pClient->ThreadHandle, (DWORD)pClient); - if ( mResult != MMSYSERR_NOERROR) - { - // Cleanup - return MMSYSERR_NOMEM; - } - - WaitForSingleObject(pClient->AuxEvent2, INFINITE); - } - - *pUserHandle = pClient; - pUserHandle = (PWAVEALLOC *)dwUser; - - - if (pClient->dwCallback) - { - DriverCallback(pClient->dwCallback, HIWORD(pClient->dwFlags), - (HDRVR)pClient->hWave, DeviceType == WaveOutDevice ? WOM_OPEN : WIM_OPEN, - pClient->dwInstance, 0L, 0L); - } - - return MMSYSERR_NOERROR; -} - - -//FIXME: MS-specific code, except for name of the func! -MMRESULT GetPositionWaveDevice(PWAVEALLOC pClient, LPMMTIME lpmmt, DWORD dwSize) -{ - /* - WAVE_DD_POSITION PositionData; - MMRESULT mErr; - - if (dwSize < sizeof(MMTIME)) - return MMSYSERR_ERROR; - - // - // Get the current position from the driver - // - mErr = sndGetHandleData(pClient->hDev, - sizeof(PositionData), - &PositionData, - IOCTL_WAVE_GET_POSITION, - pClient->Event); - - if (mErr == MMSYSERR_NOERROR) { - if (lpmmt->wType == TIME_BYTES) { - lpmmt->u.cb = PositionData.ByteCount; - } - - // default is samples. - else { - lpmmt->wType = TIME_SAMPLES; - lpmmt->u.sample = PositionData.SampleCount; - } - } - - return mErr;*/ return MMSYSERR_NOERROR; -} - - - -MMRESULT soundSetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, - ULONG Ioctl) -{ - HANDLE hDevcie; - MMRESULT Result; - DWORD BytesReturned; - - Result = OpenDevice(DeviceType, DeviceId, &hDevcie, GENERIC_READ); - if (Result != MMSYSERR_NOERROR) - return Result; - - Result = DeviceIoControl(hDevcie, Ioctl, Data, Length, NULL, 0, &BytesReturned, NULL) ? - MMSYSERR_NOERROR : TranslateStatus(); - CloseHandle(hDevcie); - - return Result; -} - -MMRESULT soundGetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, - ULONG Ioctl) -{ - HANDLE hDevice; - MMRESULT Result; - DWORD BytesReturned; - - Result = OpenDevice(DeviceType, DeviceId, &hDevice, GENERIC_READ); - if (Result != MMSYSERR_NOERROR) - return Result; - - Result = DeviceIoControl(hDevice, Ioctl, NULL, 0, (LPVOID)Data, Length, - &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); - CloseHandle(hDevice); - return Result; -} - - -/* ============================ - * EXPORT - * functions start here - * ============================ - */ - -/* - * @implemented - */ -APIENTRY DWORD wodMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) -{ - PWAVEALLOC pTask = (PWAVEALLOC)dwUser; - - switch (dwMessage) { - case WODM_GETNUMDEVS: - DPRINT("WODM_GETNUMDEVS"); - return GetDeviceCount(WaveOutDevice); - - case WODM_GETDEVCAPS: - DPRINT("WODM_GETDEVCAPS"); - return GetDeviceCapabilities(dwId, WaveOutDevice, (LPBYTE)dwParam1, - (DWORD)dwParam2); - - case WODM_OPEN: - DPRINT("WODM_OPEN"); - return OpenWaveDevice(WaveOutDevice, dwId, dwUser, dwParam1, dwParam2); - - case WODM_CLOSE: - { - DPRINT("WODM_CLOSE"); - - // 1. Check if the task is ready to complete - pTask->AuxFunction = WaveThreadClose; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - - if ( pTask->AuxReturnCode != MMSYSERR_NOERROR) - { - return pTask->AuxReturnCode; - } - else - - { - if (pTask->dwCallback) - { - DriverCallback(pTask->dwCallback, HIWORD(pTask->dwFlags), (HDRVR)pTask->hWave, - WOM_CLOSE, pTask->dwInstance, 0L, 0L); - } - } - - - // 2. Close the device - if (pTask->hDev != INVALID_HANDLE_VALUE) { - CloseHandle(pTask->hDev); - - EnterCriticalSection(&CS); - pTask->hDev = INVALID_HANDLE_VALUE; - LeaveCriticalSection(&CS); - } - - return MMSYSERR_NOERROR; - }; - - case WODM_WRITE: - { - LPWAVEHDR pWaveHdr = (LPWAVEHDR)dwParam1; - - DPRINT("WODM_WRITE"); - - if (dwParam1 != 0) - return MMSYSERR_INVALPARAM; - - if ((pWaveHdr->dwFlags & ~(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP))) - return MMSYSERR_INVALPARAM; - - pWaveHdr->dwFlags &= (WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP); - - if ((pWaveHdr->dwFlags & WHDR_PREPARED) == 0) - return MMSYSERR_INVALPARAM; - - // Check, if the wave header is already prepared - if (!(pWaveHdr->dwFlags & WHDR_PREPARED)) - return WAVERR_UNPREPARED; - - // If it's already located in the queue, this op is impossible - if (pWaveHdr->dwFlags & WHDR_INQUEUE ) - return ( WAVERR_STILLPLAYING ); - - // save WAVEALLOC pointer in the WaveHeader - pWaveHdr->reserved = dwUser; - - - pWaveHdr->dwFlags |= WHDR_INQUEUE; - pWaveHdr->dwFlags &= ~WHDR_DONE; - pTask->AuxParam.pHdr = pWaveHdr; - - pTask->AuxFunction = WaveThreadAddBuffer; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - } - - case WODM_PAUSE: - DPRINT("WODM_PAUSE"); - pTask->AuxParam.State = WAVE_DD_STOP; - - pTask->AuxFunction = WaveThreadSetState; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_RESTART: - DPRINT("WODM_RESTART"); - pTask->AuxParam.State = WAVE_DD_PLAY; - - pTask->AuxFunction = WaveThreadSetState; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_RESET: - DPRINT("WODM_RESET"); - pTask->AuxParam.State = WAVE_DD_RESET; - - pTask->AuxFunction = WaveThreadSetState; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_BREAKLOOP: - DPRINT("WODM_BREAKLOOP"); - - pTask->AuxFunction = WaveThreadBreakLoop; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_GETPOS: - DPRINT("WODM_GETPOS"); - return GetPositionWaveDevice(pTask, (LPMMTIME)dwParam1, dwParam2); - - case WODM_SETPITCH: - DPRINT("WODM_SETPITCH"); - pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1; - pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); - pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PITCH; - - pTask->AuxFunction = WaveThreadSetData; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_SETVOLUME: - DPRINT("WODM_SETVOLUME"); - { - WAVE_DD_VOLUME Vol; - Vol.Left = LOWORD(dwParam1) << 16; - Vol.Right = HIWORD(dwParam1) << 16; - - return soundSetData(WaveOutDevice, dwId, sizeof(Vol), - (PBYTE)&Vol, IOCTL_WAVE_SET_VOLUME); - } - - case WODM_SETPLAYBACKRATE: - DPRINT("WODM_SETPLAYBACKRATE"); - pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1; - pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); - pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PLAYBACK_RATE; - - pTask->AuxFunction = WaveThreadSetData; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - - case WODM_GETPITCH: - DPRINT("WODM_GETPITCH"); - pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1; - pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); - pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PITCH; - - pTask->AuxFunction = WaveThreadGetData; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - case WODM_GETVOLUME: - DPRINT("WODM_GETVOLUME"); - { - WAVE_DD_VOLUME Vol = {}; - DWORD res; - - res = soundGetData(WaveOutDevice, dwId, sizeof(Vol), - (PBYTE)&Vol, IOCTL_WAVE_GET_VOLUME); - - if (res == MMSYSERR_NOERROR) - *(LPDWORD)dwParam1 = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right)); - - return res; - } - - case WODM_GETPLAYBACKRATE: - DPRINT("WODM_GETPLAYBACKRATE"); - pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1; - pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); - pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PLAYBACK_RATE; - - pTask->AuxFunction = WaveThreadGetData; - SetEvent(pTask->AuxEvent1); - WaitForSingleObject(pTask->AuxEvent2, INFINITE); - return pTask->AuxReturnCode; - - default: - return MMSYSERR_NOTSUPPORTED; - } - - // This point of execution should never be reached - return MMSYSERR_NOTSUPPORTED; -} - - -/* - * @implemented - */ -APIENTRY DWORD widMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) -{ - DPRINT("widMessage\n"); - - switch (dwMessage) - { - case WIDM_GETNUMDEVS: - DPRINT("WIDM_GETNUMDEVS"); - return GetDeviceCount(WaveInDevice); - - case WIDM_GETDEVCAPS: - DPRINT("WODM_GETDEVCAPS"); - return GetDeviceCapabilities(dwId, WaveInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2); - - case WIDM_OPEN: - DPRINT("WIDM_OPEN"); - return OpenWaveDevice(WaveInDevice, dwId, dwUser, dwParam1, dwParam2); - - case WIDM_CLOSE: - return MMSYSERR_NOERROR; - - case WIDM_ADDBUFFER: - return MMSYSERR_NOERROR; - - case WIDM_STOP: - return MMSYSERR_NOERROR; - - case WIDM_START: - return MMSYSERR_NOERROR; - - case WIDM_RESET: - return MMSYSERR_NOERROR; - - case WIDM_GETPOS: - return MMSYSERR_NOERROR; - - - default : - return MMSYSERR_NOTSUPPORTED; - } -} - + + DPRINT("Wave processing thread setting ready state\n"); + + SetEvent(session_info->thread.ready_event); + + while ( ! terminate ) + { + /* Wait for GO event, or IO completion notification */ + while ( WaitForSingleObjectEx(session_info->thread.go_event, + INFINITE, + TRUE) == WAIT_IO_COMPLETION ) + { + /* A buffer has been finished with - pass back to the client */ + ReturnCompletedBuffers(session_info); + } + + DPRINT("Wave processing thread woken up\n"); + + /* Set the terminate flag if that's what the caller wants */ + terminate = (session_info->thread.function == DRVM_TERMINATE); + + /* Process the request */ + DPRINT("Processing thread request\n"); + result = ProcessSessionThreadRequest(session_info); + + /* Store the result code */ + session_info->thread.result = result; + + /* Submit new buffers and continue existing ones */ + DPRINT("Performing wave I/O\n"); + PerformWaveIO(session_info); + + /* Now we're ready for more action */ + DPRINT("Wave processing thread sleeping\n"); + SetEvent(session_info->thread.ready_event); + } + + return 0; +} + + +/* + Convenience function for calculating the size of the WAVEFORMATEX struct. +*/ + +DWORD +GetWaveFormatExSize(PWAVEFORMATEX format) +{ + if ( format->wFormatTag == WAVE_FORMAT_PCM ) + return sizeof(PCMWAVEFORMAT); + else + return sizeof(WAVEFORMATEX) + format->cbSize; +} + + +/* + Query if the driver/device is capable of handling a format. This is called + if the device is a wave device, and the QUERYFORMAT flag is set. +*/ + +DWORD +QueryWaveFormat( + DeviceType device_type, + PVOID lpFormat) +{ + /* TODO */ + return WAVERR_BADFORMAT; +} + + +/* + Set the format to be used. +*/ + +BOOL +SetWaveFormat( + HANDLE device_handle, + PWAVEFORMATEX format) +{ + DWORD bytes_returned; + DWORD size; + + size = GetWaveFormatExSize(format); + + DPRINT("SetWaveFormat\n"); + + return DeviceIoControl(device_handle, + IOCTL_WAVE_SET_FORMAT, + (PVOID) format, + size, + NULL, + 0, + &bytes_returned, + NULL); +} + + +DWORD +WriteWaveBuffer( + DWORD private_handle, + PWAVEHDR wave_header, + DWORD wave_header_size) +{ + SessionInfo* session_info = (SessionInfo*) private_handle; + ASSERT(session_info); + + /* Let the processing thread know that it has work to do */ + return CallSessionThread(session_info, WODM_WRITE, wave_header); +}
Added: trunk/reactos/dll/win32/mmdrv/wave_io.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/mmdrv/wave_io.c?r... ============================================================================== --- trunk/reactos/dll/win32/mmdrv/wave_io.c (added) +++ trunk/reactos/dll/win32/mmdrv/wave_io.c Wed Jul 4 14:17:48 2007 @@ -1,0 +1,40 @@ +/* + Don't use this. +*/ + +#include <mmdrv.h> + +/* + Complete a partial wave buffer transaction +*/ + +void +CompleteWaveOverlap( + DWORD error_code, + DWORD bytes_transferred, + LPOVERLAPPED overlapped) +{ + DPRINT("Complete partial wave overlap\n"); +} + +/* + Helper function to set up loops +*/ + +VOID +UpdateWaveLoop(SessionInfo* session_info) +{ +} + + +/* + The hub of all wave I/O. This ensures a constant stream of buffers are + passed between the land of usermode and kernelmode. +*/ + +VOID +PerformWaveIO( + SessionInfo* session_info) +{ + +}