mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-03 12:23:25 +00:00
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! svn path=/trunk/; revision=27384
This commit is contained in:
@@ -0,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
|
||||
@@ -0,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;
|
||||
}
|
||||
|
||||
@@ -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 @@ APIENTRY LONG DriverProc(DWORD DriverID, HANDLE DriverHandle, UINT Message,
|
||||
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 @@ APIENTRY LONG DriverProc(DWORD DriverID, HANDLE DriverHandle, UINT Message,
|
||||
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 */
|
||||
|
||||
@@ -0,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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+300
-80
@@ -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__
|
||||
#ifndef MMDRV_H
|
||||
#define MMDRV_H
|
||||
|
||||
//#define UNICODE
|
||||
|
||||
#define EXPORT __declspec(dllexport)
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmioctl.h>
|
||||
#include <mmddk.h>
|
||||
|
||||
// This needs to be done to get winioctl.h to work:
|
||||
//typedef unsigned __int64 DWORD64, *PDWORD64;
|
||||
#include <stdio.h>
|
||||
#define DPRINT printf
|
||||
|
||||
#include <winioctl.h>
|
||||
//#include "mmddk.h"
|
||||
|
||||
#include "mmdef.h"
|
||||
/* 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
|
||||
|
||||
ULONG DbgPrint(PCCH Format, ...);
|
||||
|
||||
/*
|
||||
#define SOUND_MAX_DEVICE_NAME 1024 // GUESSWORK
|
||||
#define SOUND_MAX_DEVICES 256 // GUESSWORK
|
||||
The kinds of devices which MMSYSTEM/WINMM may request from us.
|
||||
*/
|
||||
|
||||
// If the root is \Device and the Device type is
|
||||
// WaveIn and the device number is 2, the full name is \Device\WaveIn2
|
||||
typedef enum
|
||||
{
|
||||
WaveOutDevice,
|
||||
WaveInDevice,
|
||||
MidiOutDevice,
|
||||
MidiInDevice,
|
||||
AuxDevice
|
||||
} DeviceType;
|
||||
|
||||
#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 IsWaveDevice(devicetype) \
|
||||
( ( devicetype == WaveOutDevice ) || ( devicetype == WaveInDevice ) )
|
||||
|
||||
#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 IsMidiDevice(devicetype) \
|
||||
( ( devicetype == MidiOutDevice ) || ( devicetype == MidiInDevice ) )
|
||||
|
||||
#define IsAuxDevice(devicetype) \
|
||||
( devicetype == AuxDevice )
|
||||
|
||||
#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)
|
||||
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;
|
||||
|
||||
CRITICAL_SECTION CS; // Serialize access to device lists
|
||||
/*
|
||||
typedef enum
|
||||
{
|
||||
WaveAddBuffer,
|
||||
WaveClose,
|
||||
WaveReset,
|
||||
WaveRestart,
|
||||
SessionThreadTerminate,
|
||||
InvalidFunction
|
||||
} ThreadFunction;
|
||||
*/
|
||||
|
||||
HANDLE Heap;
|
||||
/* Our own values, used with the session threads */
|
||||
typedef DWORD ThreadFunction;
|
||||
#define DRVM_TERMINATE 0xFFFFFFFE
|
||||
#define DRVM_INVALID 0xFFFFFFFF
|
||||
|
||||
enum {
|
||||
InvalidDevice,
|
||||
WaveInDevice,
|
||||
WaveOutDevice,
|
||||
MidiInDevice,
|
||||
MidiOutDevice,
|
||||
AuxDevice
|
||||
};
|
||||
typedef enum
|
||||
{
|
||||
WavePlaying,
|
||||
WaveStopped,
|
||||
WaveReset,
|
||||
WaveRestart
|
||||
} WaveState;
|
||||
|
||||
MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle,
|
||||
DWORD Access);
|
||||
typedef union
|
||||
{
|
||||
PWAVEHDR wave_header;
|
||||
PMIDIHDR midi_header;
|
||||
} MediaHeader;
|
||||
|
||||
MMRESULT FindDevices();
|
||||
/*
|
||||
typedef union
|
||||
{
|
||||
MediaHeader header;
|
||||
} ThreadParameter;
|
||||
*/
|
||||
|
||||
DWORD GetDeviceCount(UINT DeviceType);
|
||||
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;
|
||||
|
||||
DWORD TranslateStatus(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -0,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;
|
||||
}
|
||||
@@ -0,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
|
||||
@@ -0,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);
|
||||
}
|
||||
+323
-990
File diff suppressed because it is too large
Load Diff
@@ -0,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)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
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
|
||||
@@ -1,4 +0,0 @@
|
||||
To-Do:
|
||||
- Globally store the heap handle
|
||||
- Ensure cleanups are... clean...
|
||||
- Clone device info in OPEN/close?
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/wdmaud.h
|
||||
* PURPOSE: WDM Audio Support - Callbacks
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 24, 2005: Started
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
|
||||
/*
|
||||
CheckCallbacks
|
||||
|
||||
This appears to just be used by the mixer stuff.
|
||||
|
||||
If the global callback file mapping handle isn't set, return FALSE.
|
||||
|
||||
Check the first parameter. If the value is below that of the first
|
||||
DWORD in the mapped view, return FALSE.
|
||||
|
||||
TODO: Finish analysis
|
||||
*/
|
||||
|
||||
BOOL CheckCallbacks()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
CreateWdmaudCallbacks
|
||||
|
||||
The original appears to use a security descriptor... We won't bother
|
||||
with that for now.
|
||||
|
||||
Create a file mapping with the name "Global\WDMAUD_Callbacks".
|
||||
The current process is used as the file handle for the file mapping. The
|
||||
file mapping attributes should be set to a length of 12 bytes, the
|
||||
security descriptor we ignore and the handle doesn't need to be
|
||||
inheritable.
|
||||
|
||||
The maximum size should be set to 1028 bytes, and the file view
|
||||
protection should be set to PAGE_READWRITE.
|
||||
|
||||
The result of the file mapping creation is stored in the global callback
|
||||
handle.
|
||||
|
||||
If the creation succeeded, try and map a view of the file, from offset 0
|
||||
for 1028 bytes, with READ and WRITE access.
|
||||
|
||||
If this succeeds, close the created file mapping handle, and set the
|
||||
global handle to the one returned by the file mapping function.
|
||||
*/
|
||||
|
||||
BOOL CreateWdmaudCallbacks()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
GetWdmaudCallbacks
|
||||
|
||||
If the global callback handle is already set, do nothing.
|
||||
|
||||
Open the file mapping to "Global\WDMAUD_Callbacks" with READ and WRITE
|
||||
access. The handle doesn't need to be inherited.
|
||||
|
||||
The handle is stored as the global callback handle.
|
||||
|
||||
If the file was opened successfully, map the view of 1028 bytes from
|
||||
offset 0 with READ and WRITE access.
|
||||
|
||||
If this fails, close the global callback handle and set it to NULL.
|
||||
|
||||
...and then return
|
||||
*/
|
||||
|
||||
BOOL GetWdmaudCallbacks()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/wavehdr.c
|
||||
* PURPOSE: WDM Audio Support - Device Control (Play/Stop etc.)
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 23, 2005: Created
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
/*
|
||||
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)
|
||||
{
|
||||
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_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 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)
|
||||
{
|
||||
}
|
||||
@@ -1,693 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/devices.c
|
||||
* PURPOSE: WDM Audio Support - Device Management
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*
|
||||
* WARNING! SOME OF THESE FUNCTIONS OUGHT TO COPY THE DEVICE INFO STRUCTURE
|
||||
* THAT HAS BEEN FED TO THEM!
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
const char WDMAUD_DEVICE_INFO_SIG[4] = "WADI";
|
||||
const char WDMAUD_DEVICE_STATE_SIG[4] = "WADS";
|
||||
|
||||
|
||||
/*
|
||||
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 */
|
||||
{
|
||||
DPRINT1("Bad interface\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Original driver seems to check for strlenW < 0x1000 */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
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, sizeof(WDMAUD_DEVICE_INFO)) )
|
||||
{
|
||||
DPRINT1("Device data structure not writable\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
if ( strncmp(device->signature, WDMAUD_DEVICE_INFO_SIG, 4) != 0 )
|
||||
{
|
||||
DPRINT1("Device signature is invalid\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
if ( ! IsValidDeviceType(device->type) )
|
||||
{
|
||||
DPRINT1("Invalid device type\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
if ( device->id > 100 )
|
||||
{
|
||||
DPRINT1("Device ID is out of range\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
/* 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 (NOT on its own!)
|
||||
|
||||
FIXME: The tests are wrong
|
||||
*/
|
||||
/*
|
||||
MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state)
|
||||
{
|
||||
if ( ( (DWORD) state->exit_thread_event != 0x00000000 ) &&
|
||||
( (DWORD) state->exit_thread_event != 0x48484848 ) )
|
||||
{
|
||||
DPRINT1("Bad exit thread event\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
if ( ( (DWORD) state->queue_event != 0x00000000 ) &&
|
||||
( (DWORD) state->queue_event != 0x42424242 ) &&
|
||||
( (DWORD) state->queue_event != 0x43434343 ) )
|
||||
{
|
||||
DPRINT1("Bad queue event\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
CreateDeviceData
|
||||
|
||||
This is a glorified memory allocation routine, which acts as a primitive
|
||||
constructor for a device data structure.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
PWDMAUD_DEVICE_INFO
|
||||
CreateDeviceData(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
WCHAR* device_path,
|
||||
BOOL with_state
|
||||
)
|
||||
{
|
||||
BOOL success = FALSE;
|
||||
PWDMAUD_DEVICE_INFO device = 0;
|
||||
int path_size = 0;
|
||||
|
||||
DPRINT("Creating device data for device type %d\n", (int) device_type);
|
||||
|
||||
if ( ! IsValidDevicePath(device_path) )
|
||||
{
|
||||
DPRINT1("No valid device interface given!\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Take into account this is a unicode string... */
|
||||
path_size = (lstrlen(device_path) + 1) * sizeof(WCHAR);
|
||||
/* DPRINT("Size of path is %d\n", (int) path_size); */
|
||||
|
||||
DPRINT("Allocating %d bytes for device data\n",
|
||||
path_size + sizeof(WDMAUD_DEVICE_INFO));
|
||||
|
||||
device = (PWDMAUD_DEVICE_INFO)
|
||||
AllocMem(path_size + sizeof(WDMAUD_DEVICE_INFO));
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT1("Unable to allocate memory for device data (error %d)\n",
|
||||
(int) GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Copy the signature and device path */
|
||||
memcpy(device->signature, WDMAUD_DEVICE_INFO_SIG, 4);
|
||||
lstrcpy(device->path, device_path);
|
||||
|
||||
/* Initialize these common members */
|
||||
device->id = device_id;
|
||||
device->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 :
|
||||
{
|
||||
if ( ! success )
|
||||
{
|
||||
if ( device )
|
||||
{
|
||||
if ( device->state )
|
||||
{
|
||||
ZeroMemory(device->state->signature, 4);
|
||||
FreeMem(device->state);
|
||||
}
|
||||
|
||||
ZeroMemory(device->signature, 4);
|
||||
FreeMem(device);
|
||||
}
|
||||
}
|
||||
|
||||
return (success ? device : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
DeleteDeviceData
|
||||
|
||||
Blanks out the device and device state structures, and frees the memory
|
||||
associated with the structures.
|
||||
|
||||
TODO: Free critical sections / events if set?
|
||||
*/
|
||||
|
||||
void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data)
|
||||
{
|
||||
DPRINT("Deleting device data\n");
|
||||
|
||||
ASSERT( device_data );
|
||||
|
||||
/* We don't really care if the structure is valid or not */
|
||||
if ( ! device_data )
|
||||
return;
|
||||
|
||||
if ( device_data->state )
|
||||
{
|
||||
/* 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 );
|
||||
|
||||
/* Insert a cow (not sure if this is right or not) */
|
||||
device_data->state->sample_size = 0xDEADBEEF;
|
||||
|
||||
/* Overwrite the structure with zeroes and free it */
|
||||
ZeroMemory(device_data->state, sizeof(WDMAUD_DEVICE_STATE));
|
||||
FreeMem(device_data->state);
|
||||
}
|
||||
|
||||
/* Overwrite the structure with zeroes and free it */
|
||||
ZeroMemory(device_data, sizeof(WDMAUD_DEVICE_INFO));
|
||||
FreeMem(device_data);
|
||||
}
|
||||
|
||||
/*
|
||||
ModifyDevicePresence
|
||||
|
||||
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.
|
||||
|
||||
"device_type" is WDMAUD_WAVE_IN, WDMAUD_WAVE_OUT, etc...
|
||||
|
||||
"device_path" specifies the NT object path of the device.
|
||||
|
||||
(I'm not sure what happens to devices that are added but never removed.)
|
||||
*/
|
||||
|
||||
MMRESULT ModifyDevicePresence(
|
||||
CHAR device_type,
|
||||
WCHAR* device_path,
|
||||
BOOL adding)
|
||||
{
|
||||
DWORD ioctl = 0;
|
||||
PWDMAUD_DEVICE_INFO device_data = 0;
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
MMRESULT kernel_result = MMSYSERR_ERROR;
|
||||
|
||||
DPRINT("ModifyDevicePresence - %s a device\n",
|
||||
adding ? "adding" : "removing");
|
||||
|
||||
/* DPRINT("Topology path %S\n", device_path); */
|
||||
DPRINT("Devtype %d\n", (int) device_type);
|
||||
|
||||
ASSERT( IsValidDeviceType(device_type) );
|
||||
ASSERT( device_path );
|
||||
|
||||
device_data = CreateDeviceData(device_type, 0, device_path, FALSE);
|
||||
|
||||
if ( ! device_data )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for device data\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ioctl = adding ? IOCTL_WDMAUD_ADD_DEVICE : IOCTL_WDMAUD_REMOVE_DEVICE;
|
||||
|
||||
kernel_result = CallKernelDevice(device_data,
|
||||
ioctl,
|
||||
0,
|
||||
0);
|
||||
|
||||
if ( kernel_result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("WdmAudioIoControl FAILED with error %d\n", (int) kernel_result);
|
||||
|
||||
switch ( kernel_result )
|
||||
{
|
||||
/* TODO: Translate into a real error code */
|
||||
default :
|
||||
result = MMSYSERR_ERROR;
|
||||
}
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DPRINT("ModifyDevicePresence succeeded\n");
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
cleanup :
|
||||
{
|
||||
if ( device_data )
|
||||
DeleteDeviceData(device_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
int device_count = 0;
|
||||
|
||||
DPRINT("Topology path %S\n", topology_path);
|
||||
|
||||
device_data = CreateDeviceData(device_type, 0, topology_path, FALSE);
|
||||
|
||||
if (! device_data)
|
||||
{
|
||||
DPRINT1("Couldn't allocate device data\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DPRINT("Getting num devs\n");
|
||||
|
||||
device_data->with_critical_section = FALSE;
|
||||
|
||||
if ( CallKernelDevice(device_data,
|
||||
IOCTL_WDMAUD_GET_DEVICE_COUNT,
|
||||
0,
|
||||
0) != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
device_count = device_data->id;
|
||||
|
||||
DPRINT("There are %d devs\n", device_count);
|
||||
|
||||
cleanup :
|
||||
{
|
||||
if ( device_data )
|
||||
DeleteDeviceData(device_data);
|
||||
|
||||
return device_count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GetDeviceCapabilities
|
||||
|
||||
This uses a different structure to the traditional documentation, because
|
||||
we handle plug and play devices.
|
||||
|
||||
Much sleep was lost over implementing this. I got the ID and type
|
||||
parameters the wrong way round!
|
||||
*/
|
||||
|
||||
MMRESULT GetDeviceCapabilities(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
WCHAR* device_path,
|
||||
LPMDEVICECAPSEX caps
|
||||
)
|
||||
{
|
||||
PWDMAUD_DEVICE_INFO device = NULL;
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
|
||||
DPRINT("Device path %S\n", device_path);
|
||||
|
||||
/* Is this right? */
|
||||
if (caps->cbSize == 0)
|
||||
{
|
||||
DPRINT1("We appear to have been given an invalid parameter\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
DPRINT("Going to have to query the kernel-mode part\n");
|
||||
|
||||
device = CreateDeviceData(device_type, device_id, device_path, FALSE);
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT("Unable to allocate device data memory\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* These are not needed as they're already initialized */
|
||||
ASSERT( device_id == device->id );
|
||||
ASSERT( ! device->with_critical_section );
|
||||
|
||||
*(LPWORD)caps->pCaps = (WORD) 0x43;
|
||||
|
||||
DPRINT("Calling kernel device\n");
|
||||
result = CallKernelDevice(device,
|
||||
IOCTL_WDMAUD_GET_CAPABILITIES,
|
||||
(DWORD)caps->cbSize,
|
||||
(DWORD)caps->pCaps);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT("IoControl failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Return code will already be MMSYSERR_NOERROR by now */
|
||||
|
||||
cleanup :
|
||||
{
|
||||
if ( device )
|
||||
DeleteDeviceData(device);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
)
|
||||
{
|
||||
DWORD format_struct_len = 0;
|
||||
|
||||
DPRINT("Opening device via kernel\n");
|
||||
|
||||
if ( format->wFormatTag == 1 ) /* FIXME */
|
||||
{
|
||||
/* Standard PCM format */
|
||||
DWORD sample_size;
|
||||
|
||||
DPRINT("Standard (PCM) format\n");
|
||||
|
||||
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 */
|
||||
}
|
||||
|
||||
return CallKernelDevice(device,
|
||||
IOCTL_WDMAUD_OPEN_DEVICE,
|
||||
format_struct_len,
|
||||
(DWORD)format);
|
||||
}
|
||||
|
||||
|
||||
/* 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,
|
||||
LPVOID open_descriptor,
|
||||
DWORD flags,
|
||||
PWDMAUD_DEVICE_INFO* user_data
|
||||
)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
WCHAR* device_path;
|
||||
PWDMAUD_DEVICE_INFO device;
|
||||
LPWAVEFORMATEX format;
|
||||
|
||||
/* As we support both types */
|
||||
LPWAVEOPENDESC wave_opendesc = (LPWAVEOPENDESC) open_descriptor;
|
||||
LPMIDIOPENDESC midi_opendesc = (LPMIDIOPENDESC) open_descriptor;
|
||||
|
||||
/* FIXME: Does this just apply to wave, or MIDI also? */
|
||||
if ( device_id > 100 )
|
||||
return MMSYSERR_BADDEVICEID;
|
||||
|
||||
/* 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;
|
||||
|
||||
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for device data\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
device->flags = flags;
|
||||
|
||||
if ( ( IsWaveDeviceType(device->type) ) &&
|
||||
( device->flags & WAVE_FORMAT_QUERY ) )
|
||||
{
|
||||
result = OpenDeviceViaKernel(device, wave_opendesc->lpFormat);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Format not supported (mmsys error %d)\n", (int) result);
|
||||
result = WAVERR_BADFORMAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT("Format supported\n");
|
||||
result = MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
device->state->device_queue_guard = CreateCriticalSection();
|
||||
|
||||
if ( ! device->state->device_queue_guard )
|
||||
{
|
||||
DPRINT1("Couldn't create queue cs\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Set up the callbacks */
|
||||
device->client_instance = IsWaveDeviceType(device->type)
|
||||
? wave_opendesc->dwInstance
|
||||
: midi_opendesc->dwInstance;
|
||||
|
||||
device->client_callback = IsWaveDeviceType(device->type)
|
||||
? wave_opendesc->dwCallback
|
||||
: midi_opendesc->dwCallback;
|
||||
|
||||
/*
|
||||
The device state will be stopped and unpaused already, but in some
|
||||
cases this isn't the desired behaviour.
|
||||
*/
|
||||
|
||||
/* FIXME: What do our friends MIDI in and out need? */
|
||||
device->state->is_paused = IsWaveOutDeviceType(device->type) ? TRUE : FALSE;
|
||||
|
||||
if ( IsMidiOutDeviceType(device->type) )
|
||||
{
|
||||
device->state->midi_buffer = AllocMem(2048);
|
||||
|
||||
if ( ! device->state->midi_buffer )
|
||||
{
|
||||
DPRINT1("Couldn't allocate MIDI buffer\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Format is only for wave devices */
|
||||
format = IsWaveDeviceType(device->type) ? wave_opendesc->lpFormat : NULL;
|
||||
|
||||
result = OpenDeviceViaKernel(device, format);
|
||||
|
||||
if ( MM_FAILURE(result) )
|
||||
{
|
||||
DPRINT1("FAILED to open device - mm error %d\n", (int) result);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
/* TODO */
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
if ( IsWaveDeviceType(device->type) )
|
||||
wave_opendesc->hWave = (HWAVE) device;
|
||||
else
|
||||
midi_opendesc->hMidi = (HMIDI) device;
|
||||
|
||||
/* Our "user data" is actually the device information */
|
||||
*user_data = device;
|
||||
|
||||
if ( device->client_callback )
|
||||
{
|
||||
DWORD message = IsWaveInDeviceType(device->type) ? WIM_OPEN :
|
||||
IsWaveOutDeviceType(device->type) ? WOM_OPEN :
|
||||
IsMidiInDeviceType(device->type) ? MIM_OPEN :
|
||||
MOM_OPEN;
|
||||
|
||||
DPRINT("Calling client with message %d\n", (int) message);
|
||||
NotifyClient(device, message, 0, 0);
|
||||
}
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
cleanup :
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/helper.c
|
||||
* PURPOSE: Multimedia User Mode Driver - Helper Funcs
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 13, 2005: Created
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
/*
|
||||
TranslateWinError converts Win32 error codes (returned by
|
||||
GetLastError, typically) into MMSYSERR codes.
|
||||
*/
|
||||
|
||||
MMRESULT TranslateWinError(DWORD error)
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
case NO_ERROR :
|
||||
case ERROR_IO_PENDING :
|
||||
return MMSYSERR_NOERROR;
|
||||
|
||||
case ERROR_BUSY :
|
||||
return MMSYSERR_ALLOCATED;
|
||||
|
||||
case ERROR_NOT_SUPPORTED :
|
||||
case ERROR_INVALID_FUNCTION :
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
case ERROR_NOT_ENOUGH_MEMORY :
|
||||
return MMSYSERR_NOMEM;
|
||||
|
||||
case ERROR_ACCESS_DENIED :
|
||||
return MMSYSERR_BADDEVICEID;
|
||||
|
||||
case ERROR_INSUFFICIENT_BUFFER :
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
return MMSYSERR_ERROR;
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/kernel.c
|
||||
* PURPOSE: WDM Audio Support - Kernel Mode Interface
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*/
|
||||
|
||||
#define INITGUID /* FIXME */
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
/* HACK ALERT - This goes in ksmedia.h */
|
||||
DEFINE_GUID(KSCATEGORY_WDMAUD,
|
||||
0x3e227e76L, 0x690d, 0x11d2, 0x81, 0x61, 0x00, 0x00, 0xf8, 0x77, 0x5b, 0xf1);
|
||||
|
||||
/* This stores the handle of the kernel device */
|
||||
static HANDLE kernel_device_handle = NULL;
|
||||
|
||||
//static WCHAR*
|
||||
|
||||
|
||||
/*
|
||||
TODO: There's a variant of this that uses critical sections...
|
||||
*/
|
||||
|
||||
MMRESULT CallKernelDevice(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD ioctl_code,
|
||||
DWORD param1,
|
||||
DWORD param2)
|
||||
{
|
||||
OVERLAPPED overlap;
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
DWORD name_len = 0;
|
||||
DWORD bytes_returned = 0;
|
||||
BOOL using_critical_section = FALSE;
|
||||
|
||||
ASSERT(kernel_device_handle);
|
||||
ASSERT(device);
|
||||
|
||||
DPRINT("Creating event\n");
|
||||
overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if ( ! overlap.hEvent )
|
||||
{
|
||||
DPRINT1("CreateEvent failed - error %d\n", (int)GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DPRINT("Sizeof wchar == %d\n", (int) sizeof(WCHAR));
|
||||
name_len = lstrlen(device->path) * sizeof(WCHAR); /* ok ? */
|
||||
|
||||
/* These seem to carry optional structures */
|
||||
device->ioctl_param1 = param1;
|
||||
device->ioctl_param2 = param2;
|
||||
|
||||
/* Enter critical section if wave/midi device, and if required */
|
||||
if ( ( ! IsMixerDeviceType(device->type) ) &&
|
||||
( ! IsAuxDeviceType(device->type) ) &&
|
||||
( device->with_critical_section ) )
|
||||
{
|
||||
ASSERT(device->state);
|
||||
using_critical_section = TRUE;
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
}
|
||||
|
||||
DPRINT("Calling DeviceIoControl with IOCTL %x\n", (int) ioctl_code);
|
||||
|
||||
if ( ! DeviceIoControl(kernel_device_handle,
|
||||
ioctl_code,
|
||||
device,
|
||||
name_len + sizeof(WDMAUD_DEVICE_INFO),
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO),
|
||||
&bytes_returned,
|
||||
&overlap) )
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
|
||||
if (error != ERROR_IO_PENDING)
|
||||
{
|
||||
DPRINT1("FAILED in CallKernelDevice (error %d)\n", (int) error);
|
||||
|
||||
DUMP_WDMAUD_DEVICE_INFO(device);
|
||||
|
||||
result = TranslateWinError(error);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DPRINT("Waiting for overlap I/O event\n");
|
||||
|
||||
/* Wait for the IO to be complete */
|
||||
WaitForSingleObject(overlap.hEvent, INFINITE);
|
||||
}
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
DPRINT("CallKernelDevice succeeded :)\n");
|
||||
|
||||
DUMP_WDMAUD_DEVICE_INFO(device);
|
||||
|
||||
cleanup :
|
||||
{
|
||||
/* Leave the critical section */
|
||||
if ( using_critical_section )
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
if ( overlap.hEvent )
|
||||
CloseHandle(overlap.hEvent);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static BOOL ChangeKernelDeviceState(BOOL enable)
|
||||
{
|
||||
PWDMAUD_DEVICE_INFO device = NULL;
|
||||
DWORD ioctl_code;
|
||||
MMRESULT call_result;
|
||||
|
||||
ioctl_code = enable ? IOCTL_WDMAUD_HELLO : IOCTL_WDMAUD_GOODBYE;
|
||||
|
||||
device = CreateDeviceData(WDMAUD_AUX, 0, L"", FALSE);
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT1("Couldn't create a new device instance structure\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
device->with_critical_section = FALSE;
|
||||
|
||||
DPRINT("Calling kernel device\n");
|
||||
|
||||
call_result = CallKernelDevice(device, ioctl_code, 0, 0);
|
||||
|
||||
DeleteDeviceData(device);
|
||||
|
||||
if ( call_result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Kernel device doesn't like us! (error %d)\n", (int) GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL EnableKernelInterface()
|
||||
{
|
||||
/* SetupAPI variables/structures for querying device data */
|
||||
SP_DEVICE_INTERFACE_DATA interface_data;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail = NULL;
|
||||
DWORD detail_size = 0;
|
||||
HANDLE heap = NULL;
|
||||
HDEVINFO dev_info;
|
||||
|
||||
/* Set to TRUE right at the end to define cleanup behaviour */
|
||||
BOOL success = FALSE;
|
||||
|
||||
/* Don't want to be called more than once */
|
||||
ASSERT(kernel_device_handle == NULL);
|
||||
|
||||
dev_info = SetupDiGetClassDevs(&KSCATEGORY_WDMAUD,
|
||||
NULL,
|
||||
NULL,
|
||||
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
|
||||
if ( ( ! dev_info ) || ( dev_info == INVALID_HANDLE_VALUE ) )
|
||||
{
|
||||
DPRINT1("SetupDiGetClassDevs failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
if ( ! SetupDiEnumDeviceInterfaces(dev_info,
|
||||
NULL,
|
||||
&KSCATEGORY_WDMAUD,
|
||||
0,
|
||||
&interface_data) )
|
||||
{
|
||||
DPRINT1("SetupDiEnumDeviceInterfaces failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
We need to find out the size of the interface detail, before we can
|
||||
actually retrieve the detail. This is a bit backwards, as the function
|
||||
will return a status of success if the interface is invalid, but we
|
||||
need it to fail with ERROR_INSUFFICIENT_BUFFER so we can be told how
|
||||
much memory we need to allocate.
|
||||
*/
|
||||
|
||||
if ( SetupDiGetDeviceInterfaceDetail(dev_info,
|
||||
&interface_data,
|
||||
NULL,
|
||||
0,
|
||||
&detail_size,
|
||||
NULL) )
|
||||
{
|
||||
DPRINT1("SetupDiGetDeviceInterfaceDetail shouldn't succeed!\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
Now we make sure the error was the one we expected. If not, bail out.
|
||||
*/
|
||||
|
||||
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
||||
{
|
||||
DPRINT1("SetupDiGetDeviceInterfaceDetail returned wrong error code\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
heap = GetProcessHeap();
|
||||
|
||||
if ( ! heap )
|
||||
{
|
||||
DPRINT1("Unable to get the process heap (error %d)\n",
|
||||
(int)GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) HeapAlloc(heap,
|
||||
HEAP_ZERO_MEMORY,
|
||||
detail_size);
|
||||
|
||||
if ( ! detail )
|
||||
{
|
||||
DPRINT1("Unable to allocate memory for the detail buffer (error %d)\n",
|
||||
(int)GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
if ( ! SetupDiGetDeviceInterfaceDetail(dev_info,
|
||||
&interface_data,
|
||||
detail,
|
||||
detail_size,
|
||||
0,
|
||||
NULL) )
|
||||
{
|
||||
DPRINT1("SetupDiGetDeviceInterfaceDetail failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DPRINT("Device path: %S\n", detail->DevicePath);
|
||||
|
||||
/* FIXME - params! */
|
||||
kernel_device_handle = CreateFile(detail->DevicePath,
|
||||
0xC0000000,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
0x40000080,
|
||||
0);
|
||||
|
||||
DPRINT("kernel_device_handle == 0x%x\n", (int) kernel_device_handle);
|
||||
|
||||
if ( ! kernel_device_handle )
|
||||
{
|
||||
DPRINT1("Unable to open kernel device (error %d)\n",
|
||||
(int) GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Now we say hello to wdmaud.sys */
|
||||
if ( ! ChangeKernelDeviceState(TRUE) )
|
||||
{
|
||||
DPRINT1("Couldn't enable the kernel device\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
|
||||
cleanup :
|
||||
{
|
||||
DPRINT("Cleanup - success == %d\n", (int) success);
|
||||
|
||||
if ( ! success )
|
||||
{
|
||||
DPRINT("Failing\n");
|
||||
|
||||
if ( kernel_device_handle )
|
||||
CloseHandle(kernel_device_handle);
|
||||
}
|
||||
|
||||
if ( heap )
|
||||
{
|
||||
if ( detail )
|
||||
HeapFree(heap, 0, detail);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
Nothing here should fail, but if it does, we just give up and ASSERT(). If
|
||||
we don't, we could be left in a limbo-state (eg: device open but disabled.)
|
||||
*/
|
||||
|
||||
BOOL DisableKernelInterface()
|
||||
{
|
||||
return ChangeKernelDeviceState(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The use of this should be avoided...
|
||||
*/
|
||||
|
||||
HANDLE GetKernelInterface()
|
||||
{
|
||||
return kernel_device_handle;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/memtrack.c
|
||||
* PURPOSE: WDM Audio Support - Memory Tracking
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wdmaud.h"
|
||||
|
||||
static int alloc_count = 0;
|
||||
|
||||
LPVOID AllocMem(DWORD size)
|
||||
{
|
||||
HANDLE heap;
|
||||
LPVOID pointer;
|
||||
|
||||
heap = GetProcessHeap();
|
||||
|
||||
if ( ! heap )
|
||||
{
|
||||
DPRINT1("SEVERE ERROR! Couldn't get process heap! (error %d)\n",
|
||||
(int) GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pointer = HeapAlloc(heap, HEAP_ZERO_MEMORY, size);
|
||||
|
||||
if ( pointer )
|
||||
alloc_count ++;
|
||||
|
||||
ReportMem();
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
VOID FreeMem(LPVOID pointer)
|
||||
{
|
||||
HANDLE heap;
|
||||
|
||||
if ( ! pointer )
|
||||
{
|
||||
DPRINT1("Trying to free a NULL pointer!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
heap = GetProcessHeap();
|
||||
|
||||
if ( ! heap )
|
||||
{
|
||||
DPRINT1("SEVERE ERROR! Couldn't get process heap! (error %d)\n",
|
||||
(int) GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! HeapFree(heap, 0, pointer) )
|
||||
{
|
||||
DPRINT("Unable to free memory (error %d)\n", (int)GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
alloc_count --;
|
||||
|
||||
ReportMem();
|
||||
}
|
||||
|
||||
VOID ReportMem()
|
||||
{
|
||||
DPRINT("Memory blocks allocated: %d\n", (int) alloc_count);
|
||||
|
||||
if ( alloc_count < 0 )
|
||||
DPRINT1("FREEMEM HAS BEEN CALLED TOO MANY TIMES!\n");
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/midi.c
|
||||
* PURPOSE: WDM Audio Support - MIDI Device / Header Manipulation
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 29, 2005: Created
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
/*
|
||||
OpenMidiDevice
|
||||
|
||||
OBSOLETE CODE - REFERENCE ONLY
|
||||
*/
|
||||
|
||||
#if 0
|
||||
MMRESULT
|
||||
OpenMidiDevice(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
LPMIDIOPENDESC open_details,
|
||||
DWORD flags,
|
||||
DWORD user_data
|
||||
)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
WCHAR* device_path;
|
||||
PWDMAUD_DEVICE_INFO device;
|
||||
|
||||
ASSERT( open_details );
|
||||
|
||||
/* FIXME? Is this true for MIDI devs too then? */
|
||||
if ( device_id > 100 )
|
||||
return MMSYSERR_BADDEVICEID; /* Not sure about this */
|
||||
|
||||
/* TODO: Case statement for wave/midi selection? */
|
||||
device_path = (WCHAR*) open_details->dnDevNode;
|
||||
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT1("Couldn't create device data\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
device->type = device_type; /* not necessary */
|
||||
device->id = device_id;
|
||||
device->flags = flags;
|
||||
|
||||
/* Wave devices look for format query flag here... */
|
||||
/* ... Validate Flags ? */
|
||||
|
||||
device->state->device_queue_guard = AllocMem(sizeof(CRITICAL_SECTION));
|
||||
|
||||
if ( ! device->state->device_queue_guard )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
|
||||
(int) GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initialize the critical section */
|
||||
InitializeCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
/* We need these so we can contact the client later */
|
||||
device->client_instance = open_details->dwInstance;
|
||||
device->client_callback = open_details->dwCallback;
|
||||
|
||||
/* Reset state */
|
||||
device->state->current_midi_header = NULL;
|
||||
device->state->unknown_24 = 0;
|
||||
|
||||
device->state->is_running = FALSE;
|
||||
device->state->is_paused = FALSE;
|
||||
|
||||
/* MIDI ONLY */
|
||||
device->state->midi_buffer = 0;
|
||||
device->state->running_status = 0x00;
|
||||
|
||||
/* For wave devices, we call the kernel NOW (but we're not handling wave) */
|
||||
/* MIDI devices are a little more complicated... Code follows... */
|
||||
|
||||
/* MIDI OUT */
|
||||
if ( device->type == WDMAUD_MIDI_OUT )
|
||||
{
|
||||
device->state->midi_buffer = AllocMem(2048);
|
||||
|
||||
if ( ! device->state->midi_buffer )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for MIDI output buffer\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fairly generic code */
|
||||
|
||||
result = OpenDeviceViaKernel(device, NULL);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Couldn't open device (mmsys error %d)\n", (int) result);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Enter the critical section while updating the device list */
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
/* ... update MIDI list ... */
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
/* The MIDI device handle is actually our structure. Neat, eh? */
|
||||
open_details->hMidi = (HMIDI) device;
|
||||
|
||||
/* We also need to set our "user data" for winmm */
|
||||
LPVOID* ud = (LPVOID*) user_data; /* FIXME */
|
||||
*ud = device;
|
||||
|
||||
/* MIDI specific code follows */
|
||||
if ( device->type == WDMAUD_MIDI_IN )
|
||||
{
|
||||
/* TODO: Read MIDI data until none left? */
|
||||
}
|
||||
|
||||
if ( device->client_callback )
|
||||
{
|
||||
DWORD message;
|
||||
|
||||
message = (device->type == WDMAUD_MIDI_IN ? MIM_OPEN : MOM_OPEN);
|
||||
|
||||
DPRINT("About to call the client callback\n");
|
||||
|
||||
/* Call the callback */
|
||||
NotifyClient(device, message, 0, 0);
|
||||
|
||||
DPRINT("...it is done!\n");
|
||||
}
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
cleanup :
|
||||
{
|
||||
/* TODO!!!! */
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MMRESULT
|
||||
CloseMidiDevice(PWDMAUD_DEVICE_INFO device)
|
||||
{
|
||||
DPRINT("CloseMidiDevice\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
WriteMidiShort(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD message
|
||||
)
|
||||
{
|
||||
DPRINT("WriteMidiShort\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
WriteMidiBuffer(PWDMAUD_DEVICE_INFO device)
|
||||
{
|
||||
DPRINT("WriteMidiBuffer\n");
|
||||
/* TODO - fix params too! */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
ResetMidiDevice(PWDMAUD_DEVICE_INFO device)
|
||||
{
|
||||
DPRINT("ResetMidiDevice\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:
|
||||
SetVolume
|
||||
GetVolume
|
||||
SetPreferred
|
||||
*/
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/threads.c
|
||||
* PURPOSE: WDM Audio Support - Completion Threads
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*/
|
||||
|
||||
#include "wdmaud.h"
|
||||
|
||||
DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
|
||||
{
|
||||
PWDMAUD_DEVICE_INFO device = (PWDMAUD_DEVICE_INFO) data;
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
|
||||
HANDLE overlap_event = NULL;
|
||||
BOOL quit_loop = FALSE;
|
||||
|
||||
DPRINT("WaveCompletionThread started\n");
|
||||
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
while ( ! quit_loop )
|
||||
{
|
||||
result = ValidateDeviceData(device, TRUE);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Invalid device data or state structure!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: REIMPLEMENT */
|
||||
/* result = ValidateDeviceStateEvents(device->state); */
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Invalid device state events\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if ( device->state->current_wave_header )
|
||||
{
|
||||
DPRINT("No current header - running? %d\n", (int) device->state->is_running);
|
||||
|
||||
if ( ! device->state->is_running )
|
||||
{
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
DPRINT("Waiting for queue_event\n");
|
||||
WaitForSingleObject(device->state->queue_event, INFINITE);
|
||||
|
||||
DPRINT("field 24 == %d\n", (int) device->state->unknown_24);
|
||||
|
||||
/* What is the importance of this field */
|
||||
|
||||
if ( ! device->state->unknown_24 )
|
||||
{
|
||||
/* ?!?! Presumably this dequeues */
|
||||
continue;
|
||||
}
|
||||
|
||||
DPRINT("We broke out the loop! Yay!\n");
|
||||
|
||||
/* TODO! */
|
||||
|
||||
return TRUE; /* bleh */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: STOP */
|
||||
DPRINT("TODO: Stop the device\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PWAVEHDR wave_header = device->state->current_wave_header;
|
||||
|
||||
DPRINT("An open descriptor or wave header was found\n");
|
||||
|
||||
result = ValidateWaveHeader(wave_header);
|
||||
|
||||
if ( result == MMSYSERR_NOERROR )
|
||||
{
|
||||
prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) wave_header->reserved;
|
||||
|
||||
result = ValidateWavePreparationData(prep_data);
|
||||
}
|
||||
|
||||
/* If both checks passed, the playback is complete */
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
DPRINT("Activating the next header\n");
|
||||
|
||||
/* Activate the next header */
|
||||
device->state->current_wave_header = wave_header->lpNext;
|
||||
|
||||
/* Reset this just in case */
|
||||
prep_data = NULL;
|
||||
/* continue; */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Should have valid prep data... */
|
||||
overlap_event = prep_data->overlapped->hEvent;
|
||||
|
||||
/* Setting this will cause the loop to exit now */
|
||||
quit_loop = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* We do this here in case there's an error - deadlock = bad! */
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR)
|
||||
goto cleanup;
|
||||
|
||||
DPRINT("Waiting for object: %d\n", (int) overlap_event);
|
||||
WaitForSingleObject(overlap_event, INFINITE);
|
||||
|
||||
cleanup :
|
||||
{
|
||||
DPRINT("Performing thread cleanup\n");
|
||||
|
||||
/* Yeah, like what? */
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI MidiCompletionThreadStart(LPVOID data)
|
||||
{
|
||||
DPRINT("MidiCompletionThread started\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device)
|
||||
{
|
||||
LPTHREAD_START_ROUTINE thread_start = NULL;
|
||||
|
||||
if ( IsWaveDeviceType(device->type) )
|
||||
thread_start = WaveCompletionThreadStart;
|
||||
else if ( IsMidiDeviceType(device->type) )
|
||||
thread_start = MidiCompletionThreadStart;
|
||||
else
|
||||
return FALSE; /* What did you just give me?! */
|
||||
|
||||
if ( device->state->unknown_30 != 0 )
|
||||
{
|
||||
DPRINT1("unknown_30 wasn't zero (it was %d)\n",
|
||||
(int) device->state->unknown_30);
|
||||
}
|
||||
|
||||
if ( device->state->thread )
|
||||
{
|
||||
DPRINT("Thread isn't null\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT("Thread is null\n");
|
||||
|
||||
if ( ( (DWORD) device->state->queue_event != 0 ) &&
|
||||
( (DWORD) device->state->queue_event != MAGIC_42) &&
|
||||
( (DWORD) device->state->queue_event != MAGIC_43) )
|
||||
{
|
||||
/* Not fatal... */
|
||||
DPRINT("Queue event is being overwritten!\n");
|
||||
/* return FALSE; */
|
||||
}
|
||||
|
||||
device->state->queue_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if ( ! device->state->queue_event )
|
||||
{
|
||||
/* TODO - hmm original doesn't seem to care what happens */
|
||||
}
|
||||
|
||||
if ( ( (DWORD) device->state->exit_thread_event != 0x00000000 ) &&
|
||||
( (DWORD) device->state->exit_thread_event != 0x48484848 ) )
|
||||
{
|
||||
/* Not fatal... */
|
||||
DPRINT("Exit Thread event is being overwritten!\n");
|
||||
/* return FALSE; */
|
||||
}
|
||||
|
||||
device->state->exit_thread_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if ( ! device->state->exit_thread_event )
|
||||
{
|
||||
/* TODO - hmm original doesn't seem to care what happens */
|
||||
}
|
||||
|
||||
device->state->thread = NULL;
|
||||
|
||||
/* Should this be unknown_04? aka THREAD? */
|
||||
|
||||
device->state->thread = CreateThread(NULL, 0, thread_start, device, 0,
|
||||
&device->state->thread_id);
|
||||
|
||||
if ( ! device->state->thread )
|
||||
{
|
||||
DPRINT1("Thread creation failed (error %d)\n",
|
||||
(int) GetLastError());
|
||||
|
||||
if ( device->state->queue_event )
|
||||
{
|
||||
CloseHandle(device->state->queue_event);
|
||||
device->state->queue_event = NULL;
|
||||
}
|
||||
|
||||
if ( device->state->exit_thread_event )
|
||||
{
|
||||
CloseHandle(device->state->exit_thread_event);
|
||||
device->state->exit_thread_event = NULL;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SetThreadPriority(device->state->thread, 0xf);
|
||||
|
||||
DPRINT("Thread created! - %d\n", (int) device->state->thread);
|
||||
|
||||
/* TODO: Set priority */
|
||||
}
|
||||
|
||||
return TRUE; /* TODO / FIXME */
|
||||
}
|
||||
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/user.c
|
||||
* PURPOSE: WDM Audio Support - User Mode Interface
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*/
|
||||
|
||||
/*
|
||||
* The GETDEVCAPS message parameters have different meaning in our case.
|
||||
* The second parameter usually indicates the struct size. But this has
|
||||
* been replaced by a pointer to the device path.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmddk.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
|
||||
APIENTRY LRESULT DriverProc(
|
||||
DWORD DriverID,
|
||||
HDRVR DriverHandle,
|
||||
UINT Message,
|
||||
LONG Param1,
|
||||
LONG Param2)
|
||||
{
|
||||
/*
|
||||
Only DRV_ENABLE and DRV_DISABLE need special handling - everything else
|
||||
is just implemented to aid in debugging.
|
||||
*/
|
||||
|
||||
DPRINT("DriverProc %d %d %d %d %d\n", (INT) DriverID, (int) DriverHandle, (int) Message, (int) Param1, (int) Param2);
|
||||
|
||||
switch(Message)
|
||||
{
|
||||
/*
|
||||
DRV_LOAD is the first message we receive, to say we've been loaded.
|
||||
DriverHandle is documented as being unused, but appears to be the
|
||||
number 3 (on my system, at least.)
|
||||
*/
|
||||
case DRV_LOAD :
|
||||
DPRINT("DRV_LOAD\n");
|
||||
/* We should initialize the device list */
|
||||
return TRUE; // dont need to do any more
|
||||
|
||||
case DRV_FREE :
|
||||
/* We should stop all wave and MIDI playback */
|
||||
DPRINT("DRV_FREE\n");
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
DRV_OPEN is sent when WINMM wishes to open the driver. Param1
|
||||
can specify configuration information, but we don't need any.
|
||||
*/
|
||||
case DRV_OPEN :
|
||||
DPRINT("DRV_OPEN\n");
|
||||
return TRUE;
|
||||
|
||||
case DRV_CLOSE :
|
||||
DPRINT("DRV_CLOSE\n");
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
Enabling this driver causes the kernel-mode portion of WDMAUD to
|
||||
be opened. We send a message to the kernel-mode driver to say that
|
||||
we want to make use of it.
|
||||
|
||||
And, of course, when we are being disabled, we tell the kernel-mode
|
||||
portion that we don't require its services any more, and close
|
||||
the handle to it.
|
||||
*/
|
||||
|
||||
case DRV_ENABLE :
|
||||
{
|
||||
DPRINT("DRV_ENABLE\n");
|
||||
return EnableKernelInterface();
|
||||
}
|
||||
|
||||
case DRV_DISABLE :
|
||||
DPRINT("DRV_DISABLE\n");
|
||||
DisableKernelInterface();
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
We don't actually support configuration or installation, so these
|
||||
could probably be safely pruned.
|
||||
*/
|
||||
|
||||
case DRV_QUERYCONFIGURE :
|
||||
DPRINT("DRV_QUERYCONFIGURE\n");
|
||||
return FALSE;
|
||||
|
||||
case DRV_CONFIGURE :
|
||||
DPRINT("DRV_CONFIGURE\n");
|
||||
return FALSE;
|
||||
|
||||
case DRV_INSTALL :
|
||||
DPRINT("DRV_INSTALL\n");
|
||||
return TRUE; /* ok? */
|
||||
|
||||
case DRV_REMOVE :
|
||||
DPRINT("DRV_REMOVE\n");
|
||||
return TRUE;
|
||||
|
||||
default :
|
||||
DPRINT("?\n");
|
||||
return DefDriverProc(DriverID, DriverHandle, Message, Param1, Param2);
|
||||
};
|
||||
}
|
||||
|
||||
void NotifyClient(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD message,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
)
|
||||
{
|
||||
DPRINT("Calling client\n");
|
||||
|
||||
DriverCallback(device->client_callback,
|
||||
HIWORD(device->flags),
|
||||
(HDRVR) device->handle,
|
||||
message,
|
||||
device->client_instance,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
APIENTRY DWORD widMessage(
|
||||
DWORD id,
|
||||
DWORD message,
|
||||
DWORD user,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
)
|
||||
{
|
||||
DPRINT("widMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
case DRVM_INIT :
|
||||
DPRINT("WIDM_INIT\n");
|
||||
return AddWaveInDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("WIDM_EXIT\n");
|
||||
return RemoveWaveInDevice((WCHAR*) p2); /* FIXME */
|
||||
|
||||
case WIDM_GETNUMDEVS :
|
||||
DPRINT("WIDM_GETNUMDEVS\n");
|
||||
return GetWaveInCount((WCHAR*) p1);
|
||||
|
||||
case WIDM_GETDEVCAPS :
|
||||
DPRINT("WIDM_GETDEVCAPS\n");
|
||||
return GetWaveInCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
};
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
APIENTRY DWORD wodMessage(
|
||||
DWORD id,
|
||||
DWORD message,
|
||||
DWORD user,
|
||||
DWORD p1,
|
||||
DWORD p2)
|
||||
{
|
||||
DPRINT("wodMessage %d %d %d %d %d\n",
|
||||
(int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
/*
|
||||
* DRVM_INIT
|
||||
* Parameter 1 : Not used
|
||||
* Parameter 2 : Topology path
|
||||
*/
|
||||
case DRVM_INIT :
|
||||
DPRINT("DRVM_INIT\n");
|
||||
return AddWaveOutDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("DRVM_EXIT\n");
|
||||
return RemoveWaveOutDevice((WCHAR*) p2); /* FIXME? */
|
||||
|
||||
/*
|
||||
* WODM_GETNUMDEVS
|
||||
* Parameter 1 : Topology device path
|
||||
* Parameter 2 : Not used
|
||||
*/
|
||||
case WODM_GETNUMDEVS :
|
||||
DPRINT("WODM_GETNUMDEVS\n");
|
||||
return GetWaveOutCount((WCHAR*) p1);
|
||||
|
||||
/*
|
||||
* WODM_GETDEVCAPS
|
||||
* Parameter 1 : Pointer to a MDEVICECAPS struct
|
||||
* Parameter 2 : Device path
|
||||
*/
|
||||
case WODM_GETDEVCAPS :
|
||||
DPRINT("WODM_GETDEVCAPS\n");
|
||||
return GetWaveOutCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
|
||||
/*
|
||||
* WODM_OPEN
|
||||
* Parameter 1 : Pointer to a WAVEOPENDESC struct (the dnDevNode
|
||||
* member holds a device path.)
|
||||
* Parameter 2 : Flags
|
||||
*/
|
||||
case WODM_OPEN :
|
||||
DPRINT("WODM_OPEN\n");
|
||||
return OpenWaveOutDevice(id,
|
||||
(LPWAVEOPENDESC) p1,
|
||||
p2,
|
||||
(PWDMAUD_DEVICE_INFO*) user);
|
||||
|
||||
case WODM_CLOSE :
|
||||
DPRINT("WODM_CLOSE\n");
|
||||
return CloseWaveDevice((PWDMAUD_DEVICE_INFO) user);
|
||||
|
||||
case WODM_PREPARE :
|
||||
DPRINT("WODM_PREPARE\n");
|
||||
return PrepareWaveHeader((PWDMAUD_DEVICE_INFO) user,
|
||||
(PWAVEHDR) p1);
|
||||
|
||||
case WODM_UNPREPARE :
|
||||
DPRINT("WODM_UNPREPARE\n");
|
||||
return UnprepareWaveHeader((PWAVEHDR) p1);
|
||||
|
||||
case WODM_WRITE :
|
||||
DPRINT("WODM_WRITE\n");
|
||||
return WriteWaveData((PWDMAUD_DEVICE_INFO) user,
|
||||
(PWAVEHDR) p1);
|
||||
}
|
||||
|
||||
DPRINT("* NOT IMPLEMENTED *\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
APIENTRY DWORD midMessage(
|
||||
DWORD id,
|
||||
DWORD message,
|
||||
DWORD user,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
)
|
||||
{
|
||||
DPRINT("midMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
case DRVM_INIT :
|
||||
DPRINT("MIDM_INIT\n");
|
||||
return AddMidiInDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("MIDM_EXIT\n");
|
||||
return RemoveMidiInDevice((WCHAR*) p2); /* FIXME */
|
||||
|
||||
case MIDM_GETNUMDEVS :
|
||||
DPRINT("MIDM_GETNUMDEVS\n");
|
||||
return GetMidiInCount((WCHAR*) p1);
|
||||
|
||||
case MIDM_GETDEVCAPS :
|
||||
DPRINT("MIDM_GETDEVCAPS\n");
|
||||
return GetMidiInCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
};
|
||||
|
||||
DPRINT("* NOT IMPLEMENTED *\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
APIENTRY DWORD modMessage(
|
||||
DWORD id,
|
||||
DWORD message,
|
||||
DWORD user,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
)
|
||||
{
|
||||
DPRINT("modMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
case DRVM_INIT :
|
||||
DPRINT("MODM_INIT\n");
|
||||
return AddMidiOutDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("MODM_EXIT\n");
|
||||
return RemoveMidiOutDevice((WCHAR*) p2); /* FIXME */
|
||||
|
||||
case MODM_GETNUMDEVS :
|
||||
DPRINT("MODM_GETNUMDEVS\n");
|
||||
return GetMidiOutCount((WCHAR*) p1);
|
||||
|
||||
case MODM_GETDEVCAPS :
|
||||
DPRINT("MODM_GETDEVCAPS\n");
|
||||
return GetMidiOutCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
|
||||
case MODM_OPEN :
|
||||
DPRINT("MODM_OPEN\n");
|
||||
return OpenMidiOutDevice(id,
|
||||
(LPMIDIOPENDESC) p1,
|
||||
p2,
|
||||
(PWDMAUD_DEVICE_INFO*) user);
|
||||
|
||||
case MODM_CLOSE :
|
||||
DPRINT("MODM_CLOSE\n");
|
||||
return CloseMidiDevice((PWDMAUD_DEVICE_INFO) user);
|
||||
|
||||
case MODM_DATA :
|
||||
DPRINT("MODM_DATA\n");
|
||||
return WriteMidiShort((PWDMAUD_DEVICE_INFO) user, p1);
|
||||
|
||||
case MODM_LONGDATA :
|
||||
DPRINT("MODM_LONGDATA\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
case MODM_RESET :
|
||||
DPRINT("MODM_RESET\n");
|
||||
return ResetMidiDevice((PWDMAUD_DEVICE_INFO) user);
|
||||
|
||||
case MODM_SETVOLUME :
|
||||
DPRINT("MODM_SETVOLUME\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
case MODM_GETVOLUME :
|
||||
DPRINT("MODM_GETVOLUME\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
/* TODO: WINE's mmddk.h needs MODM_PREFERRED to be defined (value is ??) */
|
||||
/*
|
||||
case MODM_PREFERRED :
|
||||
DPRINT("MODM_PREFERRED\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
DPRINT("* NOT IMPLEMENTED *\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
APIENTRY DWORD mxdMessage(
|
||||
DWORD id,
|
||||
DWORD message,
|
||||
DWORD user,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
)
|
||||
{
|
||||
DPRINT("mxdMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
case DRVM_INIT :
|
||||
DPRINT("MXDM_INIT\n");
|
||||
return AddMixerDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("MXDM_EXIT\n");
|
||||
return RemoveMixerDevice((WCHAR*) p2); /* FIXME */
|
||||
|
||||
case MXDM_GETNUMDEVS :
|
||||
DPRINT("MXDM_GETNUMDEVS\n");
|
||||
return GetMixerCount((WCHAR*) p1);
|
||||
|
||||
case MXDM_GETDEVCAPS :
|
||||
DPRINT("MXDM_GETDEVCAPS\n");
|
||||
return GetMixerCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
|
||||
/* ... */
|
||||
};
|
||||
|
||||
DPRINT("* NOT IMPLEMENTED *\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
APIENTRY DWORD auxMessage(DWORD id, DWORD message, DWORD user, DWORD p1, DWORD p2)
|
||||
{
|
||||
DPRINT("auxMessage %d %d %d %d %d\n", (int)id, (int)message, (int)user, (int)p1, (int)p2);
|
||||
|
||||
switch(message)
|
||||
{
|
||||
case DRVM_INIT :
|
||||
DPRINT("AUXDM_INIT\n");
|
||||
return AddAuxDevice((WCHAR*) p2);
|
||||
|
||||
case DRVM_EXIT :
|
||||
DPRINT("AUXDM_EXIT\n");
|
||||
return RemoveAuxDevice((WCHAR*) p2); /* FIXME */
|
||||
|
||||
case AUXDM_GETNUMDEVS :
|
||||
DPRINT("AUXDM_GETNUMDEVS\n");
|
||||
return GetAuxCount((WCHAR*) p1);
|
||||
|
||||
case AUXDM_GETDEVCAPS :
|
||||
DPRINT("AUXDM_GETDEVCAPS\n");
|
||||
return GetAuxCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
|
||||
|
||||
/* ... */
|
||||
};
|
||||
|
||||
DPRINT("* NOT IMPLEMENTED *\n");
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
|
||||
{
|
||||
DPRINT("DllMain called!\n");
|
||||
|
||||
if (Reason == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
DisableThreadLibraryCalls(hInstance);
|
||||
}
|
||||
|
||||
else if (Reason == DLL_PROCESS_DETACH)
|
||||
{
|
||||
DPRINT("*** wdmaud.drv is being closed ***\n");
|
||||
ReportMem();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -1,723 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/wave.c
|
||||
* PURPOSE: WDM Audio Support - Wave Device / Header Manipulation
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 18, 2005: Created
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "wdmaud.h"
|
||||
|
||||
const char WAVE_PREPARE_DATA_SIG[4] = "WPPD";
|
||||
|
||||
/*
|
||||
OBSOLETE CODE - FOR REFERENCE ONLY
|
||||
*/
|
||||
#if 0
|
||||
MMRESULT OpenWaveDevice(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
LPWAVEOPENDESC open_details,
|
||||
DWORD flags,
|
||||
DWORD user_data
|
||||
)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
WCHAR* device_path;
|
||||
PWDMAUD_DEVICE_INFO device;
|
||||
|
||||
ASSERT( open_details );
|
||||
ASSERT( open_details->lpFormat );
|
||||
|
||||
if ( device_id > 100 )
|
||||
return MMSYSERR_BADDEVICEID; /* Not sure about this */
|
||||
|
||||
device_path = (WCHAR*) open_details->dnDevNode;
|
||||
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
|
||||
|
||||
if ( ! device )
|
||||
{
|
||||
DPRINT1("Couldn't create device data\n");
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
device->type = device_type;
|
||||
device->id = device_id;
|
||||
device->flags = flags;
|
||||
|
||||
/* We don't deal with this here */
|
||||
if ( flags & WAVE_FORMAT_QUERY )
|
||||
{
|
||||
result = QueryWaveFormatSupport(device, open_details);
|
||||
DeleteDeviceData( device );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
device->state->device_queue_guard = AllocMem(sizeof(CRITICAL_SECTION));
|
||||
|
||||
if ( ! device->state->device_queue_guard )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
|
||||
(int) GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initialize the critical section */
|
||||
InitializeCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
/* We need these so we can contact the client later */
|
||||
device->client_instance = open_details->dwInstance;
|
||||
device->client_callback = open_details->dwCallback;
|
||||
|
||||
/* Reset state */
|
||||
device->state->current_wave_header = NULL;
|
||||
device->state->unknown_24 = 0;
|
||||
|
||||
device->state->is_running = FALSE;
|
||||
device->state->is_paused =
|
||||
device->type == WDMAUD_WAVE_IN ? TRUE : FALSE;
|
||||
|
||||
DPRINT("Opening the device\n");
|
||||
|
||||
result = OpenDeviceViaKernel(device, open_details->lpFormat);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Couldn't open! mmsys error %d\n", (int) result);
|
||||
/* TODO: WAVERR_BADFORMAT translation ? */
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Enter the critical section while updating the device list */
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
/* ... */
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
/* The wave device handle is actually our structure. Neat, eh? */
|
||||
open_details->hWave = (HWAVE) device;
|
||||
|
||||
/* We also need to set our "user data" for winmm */
|
||||
LPVOID* ud = (LPVOID*) user_data; /* FIXME */
|
||||
*ud = device;
|
||||
|
||||
if ( device->client_callback )
|
||||
{
|
||||
DWORD message;
|
||||
|
||||
message = (device->type == WDMAUD_WAVE_IN ? WIM_OPEN : WOM_OPEN);
|
||||
|
||||
DPRINT("About to call the client callback\n");
|
||||
|
||||
/* Call the callback */
|
||||
NotifyClient(device, message, 0, 0);
|
||||
|
||||
DPRINT("...it is done!\n");
|
||||
}
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
cleanup :
|
||||
{
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
if ( device )
|
||||
DeleteDeviceData(device);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MMRESULT CloseWaveDevice(
|
||||
PWDMAUD_DEVICE_INFO device
|
||||
)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
|
||||
result = ValidateDeviceData(device, TRUE);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Device data invalid\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
if ( ! IsWaveDeviceType(device->type) )
|
||||
{
|
||||
DPRINT1("Invalid device type (expected a WAVE device)\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
/* TODO: Perform actual close */
|
||||
if ( device->state->current_wave_header )
|
||||
{
|
||||
DPRINT1("Can't close! Device is still playing\n");
|
||||
return WAVERR_STILLPLAYING;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
/* DestroyCompletionThread(device); - check result */
|
||||
|
||||
result = CallKernelDevice(device, IOCTL_WDMAUD_CLOSE_DEVICE, 0, 0);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Close failed! mmsyserr %d\n", (int) result);
|
||||
return result; /* TODO: convert? */
|
||||
}
|
||||
|
||||
if ( device->client_callback )
|
||||
{
|
||||
DWORD message;
|
||||
|
||||
message = (device->type == WDMAUD_WAVE_IN ? WIM_CLOSE : WOM_CLOSE);
|
||||
|
||||
DPRINT("About to call the client callback\n");
|
||||
|
||||
/* Call the callback */
|
||||
NotifyClient(device, message, 0, 0);
|
||||
|
||||
DPRINT("...it is done!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:
|
||||
Enter critical section
|
||||
Loop through device list until we reach the end or until we find a
|
||||
pointer matching "device".
|
||||
Leave critical section
|
||||
Delete critical section
|
||||
...
|
||||
*/
|
||||
|
||||
DeleteDeviceData(device);
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
ValidateWaveHeaderPreparation Overview :
|
||||
|
||||
First, check to see if we can write to the buffer given to us. Fail
|
||||
if we can't (invalid parameter?)
|
||||
|
||||
Make sure the signature matches "WPPD". Fail if not (invalid param?)
|
||||
|
||||
Finally, validate the "overlapped" member, by checking to see if
|
||||
that buffer is writable, and ensuring hEvent is non-NULL.
|
||||
*/
|
||||
|
||||
MMRESULT ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data)
|
||||
{
|
||||
/* UNIMPLEMENTED */
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
ValidateWaveHeader
|
||||
|
||||
Checks that the header memory can be written to, that the flags are
|
||||
valid (using the mask 0xFFFFFFE0), and that the wave preparation data
|
||||
is valid.
|
||||
|
||||
Returns MMSYSERR_NOERROR if all's well, or MMSYSERR_INVALPARAM if not.
|
||||
*/
|
||||
|
||||
MMRESULT ValidateWaveHeader(PWAVEHDR header)
|
||||
{
|
||||
DWORD flag_check;
|
||||
|
||||
if ( IsBadWritePtr(header, sizeof(WAVEHDR)) )
|
||||
{
|
||||
DPRINT1("Bad write pointer\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
flag_check = header->dwFlags & 0xffffffe0; /* FIXME: Use flag names */
|
||||
|
||||
if ( flag_check )
|
||||
{
|
||||
DPRINT1("Unknown flags present\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
return ValidateWavePreparationData(
|
||||
(PWDMAUD_WAVE_PREPARATION_DATA) header->reserved);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
PrepareWaveHeader
|
||||
|
||||
Checks the parameters are sane, allocates memory for a WAVEPREPAREDATA
|
||||
structure and also memory for an OVERLAPPED structure.
|
||||
|
||||
After this, an un-named event is created (as hEvent of the OVERLAPPED)
|
||||
structure, and the WAVEPREPAREDATA structure has its signature set
|
||||
accordingly.
|
||||
|
||||
Returns MMSYSERR_NOTSUPPORTED so that winmm does further processing.
|
||||
*/
|
||||
|
||||
MMRESULT PrepareWaveHeader(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
PWAVEHDR header
|
||||
)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
|
||||
|
||||
DPRINT("PrepareWaveHeader called\n");
|
||||
|
||||
/* Check the device data is valid */
|
||||
result = ValidateDeviceData(device, TRUE);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Bad device info or device state\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Make sure we were actually given a header to process */
|
||||
if ( ! header )
|
||||
{
|
||||
DPRINT1("Bad header\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
/* NOTE: At this point, what happens if not prepared or already queued? */
|
||||
|
||||
header->lpNext = NULL;
|
||||
header->reserved = 0;
|
||||
|
||||
/* Allocate memory for the wave preparation data */
|
||||
prep_data =
|
||||
(PWDMAUD_WAVE_PREPARATION_DATA)
|
||||
AllocMem(sizeof(WDMAUD_WAVE_PREPARATION_DATA));
|
||||
|
||||
if ( ! prep_data )
|
||||
{
|
||||
DPRINT1("Couldn't lock global memory for preparation data (error %d)\n",
|
||||
(int)GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create an event */
|
||||
|
||||
prep_data->overlapped = AllocMem(sizeof(OVERLAPPED));
|
||||
|
||||
if ( ! prep_data->overlapped )
|
||||
{
|
||||
DPRINT1("Couldn't allocate memory for overlapped structure (error %d)\n",
|
||||
(int)GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
prep_data->overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if ( ! prep_data->overlapped->hEvent )
|
||||
{
|
||||
DPRINT1("Creation of overlapped event failed (error %d)\n",
|
||||
(int)GetLastError());
|
||||
result = MMSYSERR_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy the signature over and tie the prepare structure to the wave header */
|
||||
memcpy(prep_data->signature, WAVE_PREPARE_DATA_SIG, 4);
|
||||
header->reserved = (DWORD) prep_data;
|
||||
|
||||
/* We return this so WINMM can do further processing */
|
||||
result = MMSYSERR_NOTSUPPORTED;
|
||||
return result;
|
||||
|
||||
fail :
|
||||
{
|
||||
if ( prep_data )
|
||||
{
|
||||
if ( prep_data->overlapped )
|
||||
{
|
||||
if ( prep_data->overlapped->hEvent )
|
||||
CloseHandle(prep_data->overlapped->hEvent); /* ok? */
|
||||
|
||||
FreeMem(prep_data->overlapped);
|
||||
}
|
||||
|
||||
FreeMem(prep_data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
UnprepareWaveHeader
|
||||
|
||||
Cleans up after a header has been used, by killing the event we set up
|
||||
above, and freeing the preparation data.
|
||||
|
||||
Winmm is intelligent enough to not call this function with a header that
|
||||
is currently queued for playing!
|
||||
*/
|
||||
|
||||
MMRESULT UnprepareWaveHeader(PWAVEHDR header)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
|
||||
|
||||
DPRINT("UnprepareHeader called\n");
|
||||
|
||||
/* Make sure we were actually given a header to process */
|
||||
|
||||
if ( ! header )
|
||||
{
|
||||
DPRINT1("Bad header supplied\n");
|
||||
return MMSYSERR_INVALPARAM;
|
||||
}
|
||||
|
||||
prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved;
|
||||
result = ValidateWavePreparationData(prep_data);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Bad wave header preparation structure pointer\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/* We're about to free the preparation structure, so this needs to go */
|
||||
header->reserved = 0;
|
||||
|
||||
/* Kill the event */
|
||||
CloseHandle(prep_data->overlapped->hEvent);
|
||||
FreeMem(prep_data->overlapped);
|
||||
|
||||
/* Overwrite the signature (structure will be invalid from now on) */
|
||||
ZeroMemory(prep_data->signature, 4);
|
||||
FreeMem(prep_data);
|
||||
|
||||
/* Always return like this so winmm thinks we didn't do anything */
|
||||
|
||||
DPRINT("Header now unprepared.\n");
|
||||
result = MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Not sure about this */
|
||||
MMRESULT CompleteWaveHeader(PWAVEHDR header)
|
||||
{
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SubmitWaveHeader Overview :
|
||||
|
||||
This may span 2 functions (this one and and another "SubmitHeader")
|
||||
|
||||
First, validate the device info, then the state.
|
||||
|
||||
Validate the header, followed by the reserved member.
|
||||
|
||||
Fail if INQUEUE flag is set in header, or if PREPARED is not set in
|
||||
header.
|
||||
|
||||
AND the flags with PREPARED, BEGINLOOP, ENDLOOP and INQUEUE. OR the
|
||||
result with INQUEUE.
|
||||
|
||||
Enter the csQueue critical section.
|
||||
|
||||
Check if the device state's "open descriptor" member is NULL or not.
|
||||
If we're adding an extra buffer, it will already have been allocated.
|
||||
|
||||
If the open descriptor is NULL:
|
||||
|
||||
If it's NULL, set "opendesc" to point to the wave header (?!)
|
||||
|
||||
If the state structure's "hevtQueue" member isn't NULL, compare it's
|
||||
value to 0x43434343h and 0x42424242h. If it's not NULL or one of
|
||||
those values, set the event.
|
||||
|
||||
If the open descriptor is NOT NULL:
|
||||
|
||||
Check the header's lpNext member. If it's not NULL, check that
|
||||
structure's lpNext member, and so on, until a NULL entry is
|
||||
found.
|
||||
|
||||
Set the NULL entry to point to our header.
|
||||
|
||||
Leave the csQueue critical section.
|
||||
|
||||
** SUBMIT THE HEADER ** TODO **
|
||||
|
||||
If submission failed:
|
||||
|
||||
AND the flags with 0xFFFFFFEFh. If csQueue is set in the target
|
||||
(the header who's lpNext was NULL), set it to NULL. Set the open
|
||||
descriptor of state to NULL, too. And fail, of course.
|
||||
|
||||
If the device state is PAUSED or RUNNING, we must fail.
|
||||
|
||||
Otherwise, reset the device and set it as RUNNING. This may be done by
|
||||
our caller (wodMessage, etc.)
|
||||
0x1d8104 is used for wave in
|
||||
0x1d8148 is used for wave out?
|
||||
|
||||
SetDeviceState should now be called with the above IOCTL code and the
|
||||
device info structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
ValidateWriteWaveDataParams
|
||||
|
||||
This is just a helper function that shrinks WriteWaveData a little
|
||||
bit.
|
||||
*/
|
||||
|
||||
static MMRESULT ValidateWriteWaveDataParams(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
PWAVEHDR header
|
||||
)
|
||||
{
|
||||
MMRESULT result;
|
||||
|
||||
result = ValidateWaveHeader(header);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Bad wave header supplied\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
We don't want to queue something already queued, and we don't want
|
||||
to queue something that hasn't been prepared. Who knows what garbage
|
||||
might be sent to us?!
|
||||
*/
|
||||
|
||||
if ( header->dwFlags & WHDR_INQUEUE )
|
||||
{
|
||||
DPRINT1("This header is already queued!\n");
|
||||
return MMSYSERR_INVALFLAG;
|
||||
}
|
||||
|
||||
if ( ! header->dwFlags & WHDR_PREPARED )
|
||||
{
|
||||
DPRINT1("This header isn't prepared!\n");
|
||||
return WAVERR_UNPREPARED;
|
||||
}
|
||||
|
||||
result = ValidateDeviceData(device, TRUE);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Bad device info or device state supplied\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
WriteWaveData
|
||||
|
||||
This is the exciting (?!) bit where playback actually begins. Various
|
||||
validation takes place, before the header is queued for playback. Playback
|
||||
can then begin. This entails telling the kernel-mode device about the
|
||||
header, then telling the device to start playback.
|
||||
|
||||
It all seems pretty straightforward, but it's not all that easy...
|
||||
*/
|
||||
|
||||
MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
|
||||
{
|
||||
MMRESULT result = MMSYSERR_ERROR;
|
||||
DWORD io_result = 0;
|
||||
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
|
||||
/* PWDMAUD_DEVICE_INFO clone; */
|
||||
|
||||
/* For the DeviceIoControl later */
|
||||
DWORD ioctl_code;
|
||||
DWORD bytes_returned;
|
||||
|
||||
DPRINT("WriteWaveHeader called\n");
|
||||
|
||||
result = ValidateWriteWaveDataParams(device, header);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
return result;
|
||||
|
||||
/* Check to see if we actually get called with bad flags! */
|
||||
if ( ! IS_WAVEHDR_FLAG_SET(header, WHDR_PREPARED) )
|
||||
{
|
||||
DPRINT1("Not prepared!\n");
|
||||
return WAVERR_UNPREPARED;
|
||||
}
|
||||
|
||||
/* Retrieve our precious data from the reserved member */
|
||||
prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved;
|
||||
|
||||
result = ValidateWavePreparationData(prep_data);
|
||||
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
DPRINT1("Bad wave preparation structure supplied\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
DPRINT("Flags == 0x%x\n", (int) header->dwFlags);
|
||||
|
||||
/* Mask the "done" flag */
|
||||
CLEAR_WAVEHDR_FLAG(header, WHDR_DONE);
|
||||
/* header->dwFlags &= ~WHDR_DONE; */
|
||||
/* ...and set the queue flag! */
|
||||
SET_WAVEHDR_FLAG(header, WHDR_INQUEUE);
|
||||
/* header->dwFlags |= WHDR_INQUEUE; */
|
||||
|
||||
DPRINT("Flags == 0x%x\n", (int) header->dwFlags);
|
||||
|
||||
EnterCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
if ( ! device->state->current_wave_header )
|
||||
{
|
||||
DPRINT("Device state wave_header is NULL\n");
|
||||
|
||||
device->state->current_wave_header = header;
|
||||
|
||||
/* My, what pretty symmetry you have... */
|
||||
|
||||
DPRINT("Queue event == 0x%x\n", (int) device->state->queue_event);
|
||||
|
||||
if ( ( (DWORD) device->state->queue_event != 0 ) &&
|
||||
( (DWORD) device->state->queue_event != MAGIC_42 ) &&
|
||||
( (DWORD) device->state->queue_event != MAGIC_43 ) )
|
||||
{
|
||||
DPRINT("Setting queue event\n");
|
||||
SetEvent(device->state->queue_event);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT("Device state open_descriptor is NOT NULL\n");
|
||||
/* TODO */
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(device->state->device_queue_guard);
|
||||
|
||||
/* Now we send the header to the kernel device */
|
||||
|
||||
if ( ! IsHeaderPrepared(header) )
|
||||
{
|
||||
DPRINT1("Unprepared header!\n");
|
||||
result = MMSYSERR_INVALPARAM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
Not sure what this is for. I *think* it's used for tracking which
|
||||
device a preparation belongs to.
|
||||
*/
|
||||
prep_data->offspring = device;
|
||||
|
||||
/* The modern version of WODM_WRITE, I guess ;) */
|
||||
device->ioctl_param1 = sizeof(WAVEHDR);
|
||||
device->ioctl_param2 = (DWORD) header;
|
||||
|
||||
ioctl_code = device->type == WDMAUD_WAVE_IN
|
||||
? IOCTL_WDMAUD_SUBMIT_WAVE_IN_HDR /* FIXME */
|
||||
: IOCTL_WDMAUD_SUBMIT_WAVE_OUT_HDR;
|
||||
|
||||
/*
|
||||
FIXME:
|
||||
For wave input to work, we may need to pass different parameters.
|
||||
*/
|
||||
|
||||
/* We now send the header to the driver */
|
||||
|
||||
io_result =
|
||||
DeviceIoControl(GetKernelInterface(),
|
||||
ioctl_code,
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO),
|
||||
&bytes_returned, /* ... */
|
||||
prep_data->overlapped);
|
||||
|
||||
DPRINT("Wave header submission result : %d\n", (int) io_result);
|
||||
|
||||
if ( io_result != STATUS_SUCCESS )
|
||||
{
|
||||
DPRINT1("Wave header submission FAILED! (error %d)\n", (int) io_result);
|
||||
|
||||
CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
|
||||
device->state->device_queue_guard = NULL;
|
||||
device->state->current_wave_header = NULL;
|
||||
|
||||
return TranslateWinError(io_result);
|
||||
}
|
||||
|
||||
/* CallKernelDevice(clone, ioctl_code, 0x20, (DWORD) header); */
|
||||
|
||||
if ( ! CreateCompletionThread(device) )
|
||||
{
|
||||
DPRINT1("Couldn't create completion thread\n");
|
||||
|
||||
CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
|
||||
device->state->device_queue_guard = NULL;
|
||||
device->state->current_wave_header = NULL;
|
||||
|
||||
return MMSYSERR_ERROR; /* Care to be more specific? */
|
||||
}
|
||||
|
||||
|
||||
/* ***** FIXME ****** THIS IS NASTY HACKERY ****** */
|
||||
|
||||
DPRINT("applying hacks\n");
|
||||
|
||||
DPRINT("Running %d paused %d\n", (int)device->state->is_running, (int)device->state->is_paused);
|
||||
#if 1
|
||||
/* HACK */
|
||||
DPRINT("%d\n", (int)
|
||||
DeviceIoControl(GetKernelInterface(),
|
||||
IOCTL_WDMAUD_WAVE_OUT_START,
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO),
|
||||
&bytes_returned, /* ... */
|
||||
prep_data->overlapped) );
|
||||
|
||||
DPRINT("Running %d paused %d\n", (int)device->state->is_running, (int)device->state->is_paused);
|
||||
|
||||
#if 0 /* on error */
|
||||
DPRINT("%d\n", (int)
|
||||
DeviceIoControl(GetKernelInterface(),
|
||||
0x1d8148,
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
|
||||
device,
|
||||
sizeof(WDMAUD_DEVICE_INFO),
|
||||
&bytes_returned, /* ... */
|
||||
prep_data->overlapped) );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
result = MMSYSERR_NOERROR;
|
||||
|
||||
cleanup :
|
||||
{
|
||||
/* TODO */
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
; $Id: wdmaud.def 12852 2005-01-06 13:58:04Z mf $
|
||||
;
|
||||
; wdmaud.def
|
||||
;
|
||||
; ReactOS Operating System
|
||||
;
|
||||
; Each of the "message" functions can be commented out to stop the
|
||||
; corresponding device type from being supported. This is mainly so we can
|
||||
; focus on debugging a particular device type.
|
||||
;
|
||||
LIBRARY wdmaud.drv
|
||||
EXPORTS
|
||||
; DriverProc is needed so the driver can startup and shutdown
|
||||
DriverProc@20
|
||||
; Wave input support
|
||||
;widMessage@20
|
||||
; Wave output support
|
||||
wodMessage@20
|
||||
; Midi input support
|
||||
;midMessage@20
|
||||
; Midi output support
|
||||
;modMessage@20
|
||||
; Mixer support
|
||||
;mxdMessage@20
|
||||
; Auxiliary device support
|
||||
;auxMessage@20
|
||||
@@ -1,602 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Multimedia
|
||||
* FILE: lib/wdmaud/wdmaud.h
|
||||
* PURPOSE: WDM Audio Support - Common header
|
||||
* PROGRAMMER: Andrew Greenwood
|
||||
* UPDATE HISTORY:
|
||||
* Nov 12, 2005: Declarations for debugging + interface
|
||||
*/
|
||||
|
||||
#ifndef __WDMAUD_PRIVATE_H__
|
||||
#define __WDMAUD_PRIVATE_H__
|
||||
|
||||
/* Debugging */
|
||||
|
||||
|
||||
/*
|
||||
Some of this stuff belongs in ksmedia.h or other such global includes.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <ntddk.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmddk.h>
|
||||
|
||||
/* HACK! */
|
||||
#define DbgPrint printf
|
||||
|
||||
|
||||
/*
|
||||
Handy macros
|
||||
*/
|
||||
|
||||
#define REPORT_MM_RESULT(message, success) \
|
||||
DPRINT("%s %s\n", message, success == MMSYSERR_NOERROR ? "succeeded" : "failed")
|
||||
|
||||
#define MM_SUCCESS(value) \
|
||||
( value == MMSYSERR_NOERROR )
|
||||
|
||||
#define MM_FAILURE(value) \
|
||||
( value != MMSYSERR_NOERROR )
|
||||
|
||||
|
||||
#define GOBAL_CALLBACKS_PATH L"Global\\WDMAUD_Callbacks"
|
||||
|
||||
|
||||
/*
|
||||
Private IOCTLs shared between wdmaud.sys and wdmaud.drv
|
||||
|
||||
TO ADD/MODIFY:
|
||||
IOCTL_WDMAUD_OPEN_PIN
|
||||
IOCTL_WDMAUD_WAVE_OUT_WRITE_PIN
|
||||
IOCTL_WDMAUD_WAVE_IN_READ_PIN
|
||||
IOCTL_WDMAUD_MIXER_CLOSE
|
||||
IOCTL_WDMAUD_MIXER_OPEN
|
||||
IOCTL_WDMAUD_MIDI_IN_READ_PIN
|
||||
IOCTL_WDMAUD_MIXER_GETLINEINFO
|
||||
IOCTL_WDMAUD_MIXER_GETHARDWAREEVENTDATA
|
||||
IOCTL_WDMAUD_MIXER_SETCONTROLDETAILS
|
||||
IOCTL_WDMAUD_MIXER_GETCONTROLDETAILS
|
||||
IOCTL_WDMAUD_MIXER_GETLINECONTROLS
|
||||
*/
|
||||
|
||||
/* 0x1d8000 */
|
||||
#define IOCTL_WDMAUD_HELLO \
|
||||
CTL_CODE(FILE_DEVICE_SOUND, 0x0000, METHOD_BUFFERED, FILE_WRITE_ACCESS)
|
||||
|
||||
#define IOCTL_WDMAUD_ADD_DEVICE 0x1d8004
|
||||
#define IOCTL_WDMAUD_REMOVE_DEVICE 0x1d8008
|
||||
#define IOCTL_WDMAUD_GET_CAPABILITIES 0x1d800c
|
||||
#define IOCTL_WDMAUD_GET_DEVICE_COUNT 0x1d8010
|
||||
#define IOCTL_WDMAUD_OPEN_DEVICE 0x1d8014
|
||||
#define IOCTL_WDMAUD_CLOSE_DEVICE 0x1d8018
|
||||
#define IOCTL_WDMAUD_AUX_GET_VOLUME 0x1d801c
|
||||
#define IOCTL_WDMAUD_AUX_SET_VOLUME 0x1d8020
|
||||
|
||||
/* 0x1d8024 */
|
||||
#define IOCTL_WDMAUD_GOODBYE \
|
||||
CTL_CODE(FILE_DEVICE_SOUND, 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
|
||||
|
||||
#define IOCTL_WDMAUD_SET_PREFERRED 0x1d8028
|
||||
|
||||
#define IOCTL_WDMAUD_WAVE_OUT_STOP 0x1d8100
|
||||
#define IOCTL_WDMAUD_WAVE_OUT_START 0x1d8104
|
||||
#define IOCTL_WDMAUD_WAVE_OUT_RESET 0x1d8108
|
||||
#define IOCTL_WDMAUD_BREAK_LOOP 0x1d810c
|
||||
|
||||
#define IOCTL_WDMAUD_GET_WAVE_OUT_POS 0x1d8110 /* Does something funky */
|
||||
#define IOCTL_WDMAUD_SET_VOLUME 0x1d8114 /* Already been covered? */
|
||||
#define IOCTL_WDMAUD_UNKNOWN1 0x1d8118 /* Not used by wdmaud.drv */
|
||||
#define IOCTL_WDMAUD_SUBMIT_WAVE_OUT_HDR 0x1d811c
|
||||
|
||||
#define IOCTL_WDMAUD_WAVE_IN_STOP 0x1d8140
|
||||
#define IOCTL_WDMAUD_WAVE_IN_START 0x1d8144
|
||||
#define IOCTL_WDMAUD_WAVE_IN_RESET 0x1d8148
|
||||
|
||||
#define IOCTL_WDMAUD_SUBMIT_WAVE_IN_HDR 0x1d8150 /* FIXME: Unsure about this */
|
||||
|
||||
#define IOCTL_WDMAUD_MIDI_OUT_SHORT_MESSAGE \
|
||||
0x1d8204 /* Wrong description? */
|
||||
|
||||
#define IOCTL_WDMAUD_UNKNOWN2 0x1d8208
|
||||
|
||||
#define IOCTL_WDMAUD_MIDI_OUT_LONG_MESSAGE \
|
||||
0x1d820c
|
||||
|
||||
#define IOCTL_WDMAUD_SUBMIT_MIDI_HDR 0x1d8210
|
||||
|
||||
#define IOCTL_WDMAUD_MIDI_IN_STOP 0x1d8240
|
||||
#define IOCTL_WDMAUD_MIDI_IN_START 0x1d8244
|
||||
#define IOCTL_WDMAUD_MIDI_IN_RESET 0x1d8248
|
||||
|
||||
#define IOCTL_WDMAUD_READ_MIDI_DATA 0x1d824c
|
||||
#define IOCTL_WDMAUD_MIDI_MESSAGE 0x1d8300 /* Wrong description? */
|
||||
|
||||
#define IOCTL_WDMAUD_MIXER_UNKNOWN1 0x1d8310
|
||||
#define IOCTL_WDMAUD_MIXER_UNKNOWN2 0x1d8314
|
||||
#define IOCTL_WDMAUD_MIXER_UNKNOWN3 0x1d8318
|
||||
|
||||
|
||||
/*
|
||||
Device Types
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
WDMAUD_WAVE_IN = 0,
|
||||
WDMAUD_WAVE_OUT,
|
||||
|
||||
WDMAUD_MIDI_IN,
|
||||
WDMAUD_MIDI_OUT,
|
||||
|
||||
WDMAUD_MIXER,
|
||||
|
||||
WDMAUD_AUX,
|
||||
|
||||
/* For range checking */
|
||||
WDMAUD_MIN_DEVICE_TYPE = WDMAUD_WAVE_IN,
|
||||
WDMAUD_MAX_DEVICE_TYPE = WDMAUD_AUX
|
||||
};
|
||||
|
||||
/*
|
||||
Some macros for device type matching and checking
|
||||
*/
|
||||
|
||||
#define IsWaveInDeviceType(device_type) (device_type == WDMAUD_WAVE_IN)
|
||||
#define IsWaveOutDeviceType(device_type) (device_type == WDMAUD_WAVE_OUT)
|
||||
#define IsMidiInDeviceType(device_type) (device_type == WDMAUD_MIDI_IN)
|
||||
#define IsMidiOutDeviceType(device_type) (device_type == WDMAUD_MIDI_OUT)
|
||||
#define IsMixerDeviceType(device_type) (device_type == WDMAUD_MIXER)
|
||||
#define IsAuxDeviceType(device_type) (device_type == WDMAUD_AUX)
|
||||
|
||||
#define IsWaveDeviceType(device_type) \
|
||||
(IsWaveInDeviceType(device_type) || IsWaveOutDeviceType(device_type))
|
||||
#define IsMidiDeviceType(device_type) \
|
||||
(IsMidiInDeviceType(device_type) || IsMidiOutDeviceType(device_type))
|
||||
|
||||
#define IsValidDeviceType(device_type) \
|
||||
(device_type >= WDMAUD_MIN_DEVICE_TYPE && \
|
||||
device_type <= WDMAUD_MAX_DEVICE_TYPE)
|
||||
|
||||
/*
|
||||
The various "caps" (capabilities) structures begin with the same members,
|
||||
so a generic structure is defined here which can be accessed independently
|
||||
of a device type.
|
||||
*/
|
||||
|
||||
/*
|
||||
This is used as a general-purpose structure to retrieve capabilities
|
||||
from the driver.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DWORD cbSize;
|
||||
LPVOID pCaps;
|
||||
} MDEVICECAPSEX, *LPMDEVICECAPSEX;
|
||||
|
||||
/* Abstraction */
|
||||
typedef LPVOID PWDMAUD_HEADER;
|
||||
|
||||
/*
|
||||
There are also various "opendesc" structures, but these don't have any
|
||||
common members. Regardless, this typedef simply serves as a placeholder
|
||||
to indicate that to access the members, it should be cast accordingly.
|
||||
|
||||
TODO: Maybe have a generic OPEN routine that uses this?
|
||||
*/
|
||||
typedef struct OPENDESC *LPOPENDESC;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD sample_size;
|
||||
HANDLE thread;
|
||||
DWORD thread_id;
|
||||
|
||||
union
|
||||
{
|
||||
LPWAVEHDR current_wave_header;
|
||||
LPMIDIHDR current_midi_header;
|
||||
};
|
||||
|
||||
DWORD unknown_10; /* pointer to something */
|
||||
DWORD unknown_14;
|
||||
|
||||
LPCRITICAL_SECTION device_queue_guard;
|
||||
HANDLE queue_event;
|
||||
HANDLE exit_thread_event;
|
||||
|
||||
DWORD unknown_24;
|
||||
|
||||
DWORD is_paused;
|
||||
DWORD is_running;
|
||||
|
||||
DWORD unknown_30;
|
||||
LPVOID midi_buffer; /* for output ? */
|
||||
DWORD running_status;
|
||||
|
||||
char signature[4];
|
||||
} WDMAUD_DEVICE_STATE, *PWDMAUD_DEVICE_STATE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD next_device;
|
||||
|
||||
DWORD id;
|
||||
DWORD type;
|
||||
|
||||
HWAVE handle;
|
||||
|
||||
DWORD client_instance;
|
||||
DWORD client_callback;
|
||||
|
||||
DWORD unknown_18;
|
||||
|
||||
DWORD flags;
|
||||
DWORD ioctl_param2;
|
||||
DWORD ioctl_param1;
|
||||
DWORD with_critical_section;
|
||||
DWORD string_2c;
|
||||
|
||||
DWORD unknown_30;
|
||||
|
||||
DWORD playing_notes;
|
||||
|
||||
DWORD unknown_38;
|
||||
DWORD unknown_3c;
|
||||
DWORD unknown_40;
|
||||
DWORD unknown_44;
|
||||
DWORD unknown_48;
|
||||
DWORD unknown_4C;
|
||||
DWORD unknown_50;
|
||||
|
||||
DWORD beef;
|
||||
PWDMAUD_DEVICE_STATE state;
|
||||
char signature[4];
|
||||
WCHAR path[1];
|
||||
} WDMAUD_DEVICE_INFO, *PWDMAUD_DEVICE_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PWDMAUD_DEVICE_INFO offspring; /* not sure about this */
|
||||
LPOVERLAPPED overlapped;
|
||||
char signature[4];
|
||||
} WDMAUD_WAVE_PREPARATION_DATA, *PWDMAUD_WAVE_PREPARATION_DATA;
|
||||
|
||||
/* Ugh... */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD cbSize; /* Maybe? */
|
||||
|
||||
} WDMAUD_CAPS, *PWDMAUD_CAPS;
|
||||
|
||||
/*
|
||||
Not quite sure what these are/do yet
|
||||
*/
|
||||
|
||||
#define MAGIC_42 0x42424242 /* Queue critical section */
|
||||
#define MAGIC_43 0x43434343 /* Queue critical section */
|
||||
#define MAGIC_48 0x48484848 /* Exit-thread event */
|
||||
|
||||
#define IsQueueMagic(test_value) \
|
||||
( ( (DWORD)test_value == MAGIC_42 ) || ( (DWORD)test_value == MAGIC_43) )
|
||||
|
||||
|
||||
/*
|
||||
This should eventually be removed, but is used so we can be nosey and see
|
||||
what the kernel-mode wdmaud.sys is doing with our structures!
|
||||
*/
|
||||
|
||||
#ifdef DUMP_WDMAUD_STRUCTURES
|
||||
|
||||
#define DUMP_MEMBER(struct, member) \
|
||||
DPRINT("%s : %d [0x%x]\n", #member, (int) struct->member, (int) struct->member);
|
||||
|
||||
#define DUMP_WDMAUD_DEVICE_INFO(info) \
|
||||
{ \
|
||||
DPRINT("-- %s --\n", #info); \
|
||||
DUMP_MEMBER(info, unknown_00); \
|
||||
DUMP_MEMBER(info, id); \
|
||||
DUMP_MEMBER(info, type); \
|
||||
DUMP_MEMBER(info, wave_handle); \
|
||||
DUMP_MEMBER(info, client_instance); \
|
||||
DUMP_MEMBER(info, client_callback); \
|
||||
DUMP_MEMBER(info, unknown_18); \
|
||||
DUMP_MEMBER(info, flags); \
|
||||
DUMP_MEMBER(info, ioctl_param2); \
|
||||
DUMP_MEMBER(info, ioctl_param1); \
|
||||
DUMP_MEMBER(info, with_critical_section); \
|
||||
DUMP_MEMBER(info, string_2c); \
|
||||
DUMP_MEMBER(info, unknown_30); \
|
||||
DUMP_MEMBER(info, playing_notes); \
|
||||
DUMP_MEMBER(info, unknown_38); \
|
||||
DUMP_MEMBER(info, unknown_3c); \
|
||||
DUMP_MEMBER(info, unknown_40); \
|
||||
DUMP_MEMBER(info, unknown_44); \
|
||||
DUMP_MEMBER(info, unknown_48); \
|
||||
DUMP_MEMBER(info, unknown_4C); \
|
||||
DUMP_MEMBER(info, unknown_50); \
|
||||
DUMP_MEMBER(info, beef); \
|
||||
DUMP_MEMBER(info, state); \
|
||||
DUMP_MEMBER(info, signature); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define DUMP_MEMBER(struct, member)
|
||||
#define DUMP_WDMAUD_DEVICE_INFO(info)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Helper (helper.c) funcs */
|
||||
|
||||
MMRESULT TranslateWinError(DWORD error);
|
||||
#define GetLastMmError() TranslateWinError(GetLastError());
|
||||
|
||||
|
||||
/* user.c */
|
||||
|
||||
void
|
||||
NotifyClient(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD message,
|
||||
DWORD p1,
|
||||
DWORD p2
|
||||
);
|
||||
|
||||
|
||||
/* kernel.c */
|
||||
|
||||
BOOL
|
||||
EnableKernelInterface();
|
||||
|
||||
BOOL
|
||||
DisableKernelInterface();
|
||||
|
||||
HANDLE
|
||||
GetKernelInterface();
|
||||
|
||||
MMRESULT
|
||||
CallKernelDevice(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD ioctl_code,
|
||||
DWORD param1,
|
||||
DWORD param2);
|
||||
|
||||
/* devices.c */
|
||||
|
||||
BOOL
|
||||
IsValidDevicePath(WCHAR* path);
|
||||
|
||||
MMRESULT
|
||||
ValidateDeviceData(
|
||||
PWDMAUD_DEVICE_INFO device_data,
|
||||
BOOL require_state
|
||||
);
|
||||
/*
|
||||
MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state);
|
||||
MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state);
|
||||
MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info);
|
||||
*/
|
||||
|
||||
/* TODO: Add ID parameter */
|
||||
|
||||
PWDMAUD_DEVICE_INFO
|
||||
CreateDeviceData(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
WCHAR* device_path,
|
||||
BOOL with_state
|
||||
);
|
||||
|
||||
void
|
||||
DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data);
|
||||
|
||||
/* mixer ... */
|
||||
|
||||
MMRESULT ModifyDevicePresence(
|
||||
CHAR device_type,
|
||||
WCHAR* device_path,
|
||||
BOOL adding);
|
||||
|
||||
#define AddDevice(device_type, device_path) \
|
||||
ModifyDevicePresence(device_type, device_path, TRUE)
|
||||
|
||||
#define AddWaveInDevice(device_path) \
|
||||
AddDevice(WDMAUD_WAVE_IN, device_path)
|
||||
#define AddWaveOutDevice(device_path) \
|
||||
AddDevice(WDMAUD_WAVE_OUT, device_path)
|
||||
#define AddMidiInDevice(device_path) \
|
||||
AddDevice(WDMAUD_MIDI_IN, device_path)
|
||||
#define AddMidiOutDevice(device_path) \
|
||||
AddDevice(WDMAUD_MIDI_OUT, device_path)
|
||||
#define AddMixerDevice(device_path) \
|
||||
AddDevice(WDMAUD_MIXER, device_path)
|
||||
#define AddAuxDevice(device_path) \
|
||||
AddDevice(WDMAUD_AUX, device_path)
|
||||
|
||||
#define RemoveDevice(device_type, device_path) \
|
||||
ModifyDevicePresence(device_type, device_path, FALSE)
|
||||
|
||||
#define RemoveWaveInDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_WAVE_IN, device_path)
|
||||
#define RemoveWaveOutDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_WAVE_OUT, device_path)
|
||||
#define RemoveMidiInDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_MIDI_IN, device_path)
|
||||
#define RemoveMidiOutDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_MIDI_OUT, device_path)
|
||||
#define RemoveMixerDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_MIXER, device_path)
|
||||
#define RemoveAuxDevice(device_path) \
|
||||
RemoveDevice(WDMAUD_AUX, device_path)
|
||||
|
||||
|
||||
DWORD
|
||||
GetDeviceCount(CHAR device_type, WCHAR* device_path);
|
||||
|
||||
#define GetWaveInCount(device_path) GetDeviceCount(WDMAUD_WAVE_IN, device_path)
|
||||
#define GetWaveOutCount(device_path) GetDeviceCount(WDMAUD_WAVE_OUT, device_path)
|
||||
#define GetMidiInCount(device_path) GetDeviceCount(WDMAUD_MIDI_IN, device_path)
|
||||
#define GetMidiOutCount(device_path) GetDeviceCount(WDMAUD_MIDI_OUT, device_path)
|
||||
#define GetMixerCount(device_path) GetDeviceCount(WDMAUD_MIXER, device_path)
|
||||
#define GetAuxCount(device_path) GetDeviceCount(WDMAUD_AUX, device_path)
|
||||
|
||||
MMRESULT
|
||||
GetDeviceCapabilities(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
WCHAR* device_path,
|
||||
LPMDEVICECAPSEX caps
|
||||
);
|
||||
|
||||
#define GetWaveInCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_WAVE_IN, id, device_path, caps);
|
||||
#define GetWaveOutCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_WAVE_OUT, id, device_path, caps);
|
||||
#define GetMidiInCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_MIDI_IN, id, device_path, caps);
|
||||
#define GetMidiOutCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_MIDI_OUT, id, device_path, caps);
|
||||
#define GetMixerCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_MIXER, id, device_path, caps);
|
||||
#define GetAuxCapabilities(id, device_path, caps) \
|
||||
GetDeviceCapabilities(WDMAUD_AUX, id, device_path, caps);
|
||||
|
||||
MMRESULT
|
||||
OpenDeviceViaKernel(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
LPWAVEFORMATEX format
|
||||
);
|
||||
|
||||
MMRESULT
|
||||
OpenDevice(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
LPVOID open_descriptor,
|
||||
DWORD flags,
|
||||
PWDMAUD_DEVICE_INFO* user_data
|
||||
);
|
||||
|
||||
|
||||
/* wave.c */
|
||||
#if 0
|
||||
MMRESULT
|
||||
OpenWaveDevice(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
LPWAVEOPENDESC open_details,
|
||||
DWORD flags,
|
||||
DWORD user_data
|
||||
);
|
||||
#endif
|
||||
|
||||
#define OpenWaveInDevice(id, open_details, flags, user_data) \
|
||||
OpenDevice(WDMAUD_WAVE_IN, id, open_details, flags, user_data);
|
||||
#define OpenWaveOutDevice(id, open_details, flags, user_data) \
|
||||
OpenDevice(WDMAUD_WAVE_OUT, id, open_details, flags, user_data);
|
||||
#define OpenMidiInDevice(id, open_details, flags, user_data) \
|
||||
OpenDevice(WDMAUD_MIDI_IN, id, open_details, flags, user_data);
|
||||
#define OpenMidiOutDevice(id, open_details, flags, user_data) \
|
||||
OpenDevice(WDMAUD_MIDI_OUT, id, open_details, flags, user_data);
|
||||
|
||||
|
||||
MMRESULT
|
||||
CloseWaveDevice(
|
||||
PWDMAUD_DEVICE_INFO device
|
||||
);
|
||||
|
||||
#define SET_WAVEHDR_FLAG(header, flag) \
|
||||
header->dwFlags |= flag
|
||||
|
||||
#define CLEAR_WAVEHDR_FLAG(header, flag) \
|
||||
header->dwFlags &= ~flag
|
||||
|
||||
#define IS_WAVEHDR_FLAG_SET(header, flag) \
|
||||
( header->dwFlags & flag )
|
||||
|
||||
MMRESULT
|
||||
ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data);
|
||||
|
||||
MMRESULT
|
||||
ValidateWaveHeader(PWAVEHDR header);
|
||||
|
||||
MMRESULT
|
||||
PrepareWaveHeader(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
PWAVEHDR header
|
||||
);
|
||||
|
||||
MMRESULT
|
||||
UnprepareWaveHeader(PWAVEHDR header);
|
||||
|
||||
#define IsHeaderPrepared(header) \
|
||||
( header->reserved != 0 )
|
||||
|
||||
MMRESULT
|
||||
CompleteWaveHeader(PWAVEHDR header);
|
||||
|
||||
MMRESULT
|
||||
WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header);
|
||||
|
||||
|
||||
/* midi.c */
|
||||
|
||||
#if 0
|
||||
MMRESULT
|
||||
OpenMidiDevice(
|
||||
CHAR device_type,
|
||||
DWORD device_id,
|
||||
LPMIDIOPENDESC open_details,
|
||||
DWORD flags,
|
||||
DWORD user_data
|
||||
);
|
||||
#endif
|
||||
|
||||
MMRESULT
|
||||
CloseMidiDevice(
|
||||
PWDMAUD_DEVICE_INFO device
|
||||
);
|
||||
|
||||
MMRESULT
|
||||
WriteMidiShort(
|
||||
PWDMAUD_DEVICE_INFO device,
|
||||
DWORD message
|
||||
);
|
||||
|
||||
/* FIXME: Bad params */
|
||||
MMRESULT
|
||||
WriteMidiBuffer(PWDMAUD_DEVICE_INFO device);
|
||||
|
||||
MMRESULT
|
||||
ResetMidiDevice(PWDMAUD_DEVICE_INFO device);
|
||||
|
||||
|
||||
|
||||
|
||||
/* threads.c */
|
||||
|
||||
BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device);
|
||||
|
||||
|
||||
/* MORE... */
|
||||
|
||||
/*
|
||||
DEBUGGING + RESOURCE TRACKING
|
||||
*/
|
||||
|
||||
LPVOID AllocMem(DWORD size);
|
||||
VOID FreeMem(LPVOID pointer);
|
||||
VOID ReportMem();
|
||||
|
||||
#define AutoAlloc(type) \
|
||||
(type*) MemAlloc(sizeof(type));
|
||||
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
<module name="wdmaud" type="win32dll" extension=".drv" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv">
|
||||
<importlibrary definition="wdmaud.def" />
|
||||
<include base="wdmaud">.</include>
|
||||
<define name="__USE_W32API" />
|
||||
<define name="_DISABLE_TIDENTS" />
|
||||
<define name="UNICODE" />
|
||||
<define name="_UNICODE" />
|
||||
<define name="__REACTOS__" />
|
||||
<library>ntdll</library>
|
||||
<library>kernel32</library>
|
||||
<library>winmm</library>
|
||||
<library>setupapi</library>
|
||||
<file>user.c</file>
|
||||
<file>kernel.c</file>
|
||||
<file>devices.c</file>
|
||||
<file>midi.c</file>
|
||||
<file>wave.c</file>
|
||||
<file>threads.c</file>
|
||||
<file>helper.c</file>
|
||||
<file>memtrack.c</file>
|
||||
<file>wdmaud.rc</file>
|
||||
</module>
|
||||
@@ -1,5 +0,0 @@
|
||||
#define REACTOS_VERSION_DLL
|
||||
#define REACTOS_STR_FILE_DESCRIPTION "WDM Audio System (Legacy Support)\0"
|
||||
#define REACTOS_STR_INTERNAL_NAME "wdmaud\0"
|
||||
#define REACTOS_STR_ORIGINAL_FILENAME "wdmaud.drv\0"
|
||||
#include <reactos/version.rc>
|
||||
Reference in New Issue
Block a user