mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 22:16:04 +00:00
Replace .def file by a .spec file
svn path=/trunk/; revision=35209
This commit is contained in:
parent
ce5c619db0
commit
389c99370d
10 changed files with 1048 additions and 1055 deletions
|
@ -1,258 +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;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
In summary, we just implement to satisfy the MME API (winmm) requirements.
|
||||
*/
|
||||
|
||||
LONG
|
||||
LONG WINAPI
|
||||
DriverProc(
|
||||
DWORD driver_id,
|
||||
HANDLE driver_handle,
|
||||
|
|
|
@ -1,206 +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;
|
||||
}
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
; $Id$
|
||||
;
|
||||
; mmdrv.def
|
||||
;
|
||||
; ReactOS Operating System
|
||||
;
|
||||
LIBRARY mmdrv.dll
|
||||
EXPORTS
|
||||
DriverProc@20
|
||||
;widMessage@20
|
||||
wodMessage@20
|
||||
;midMessage@20
|
||||
;modMessage@20
|
||||
;auxMessage@20
|
|
@ -1,5 +1,5 @@
|
|||
<module name="mmdrv" type="win32dll" baseaddress="${BASEADDRESS_MMDRV}" installbase="system32" installname="mmdrv.dll" unicode="yes">
|
||||
<importlibrary definition="mmdrv.def" />
|
||||
<importlibrary definition="mmdrv.spec.def" />
|
||||
<include base="mmdrv">.</include>
|
||||
<define name="NDEBUG" />
|
||||
<library>ntdll</library>
|
||||
|
@ -13,4 +13,5 @@
|
|||
<file>common.c</file>
|
||||
<file>wave.c</file>
|
||||
<file>wave_io.c</file>
|
||||
<file>mmdrv.spec</file>
|
||||
</module>
|
||||
|
|
6
reactos/dll/win32/mmdrv/mmdrv.spec
Normal file
6
reactos/dll/win32/mmdrv/mmdrv.spec
Normal file
|
@ -0,0 +1,6 @@
|
|||
@ stdcall DriverProc(long ptr long long long)
|
||||
;@ stdcall widMessage(long long long long long)
|
||||
@ stdcall wodMessage(long long long long long)
|
||||
;@ stdcall midMessage(long long long long long)
|
||||
;@ stdcall modMessage(long long long long long)
|
||||
;@ stdcall auxMessage(long long long long long)
|
|
@ -1,143 +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;
|
||||
}
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -1,147 +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
|
||||
/*
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,245 +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);
|
||||
}
|
||||
/*
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -1,40 +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)
|
||||
{
|
||||
|
||||
}
|
||||
/*
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue