|
|
/*
* @Doc DMusic16 * * @Module Device.c - Device management routines | * * This module manages handles and handle instances to the legacy MIDI device. * * Each open device is represented by a handle (which is * an <c OPENHANDLE> struct). This struct contains all of the information * concerning the state of the device, including a reference count of the * number of clients using the device. * * Each client use of one device is represented by a handle instance (which * is an <c OPENHANDLINSTANCE> struct). A near pointer to this struct is * the actual handle seen by the client. These handle instances are used * to hold any client-specific information, and to dereference client * handles to the proper <c OPENHANDLE> struct. * * Currently we support multiple clients on the same output device but * only one client per input device. * * @globalv NPLINKNODE | gOpenHandleInstanceList | The master list of all * open handle instances. * * @globalv NPLINKNODE | gOpenHandleList | The master list of all open * handles. * * @globalv UINT | gcOpenInputDevices | A reference count of open MIDI * in devices. * * @globalv UINT | gcOpenOutputDevices | A reference count of open MIDI * out devices. */
#include <windows.h>
#include <mmsystem.h>
#include "dmusic16.h"
#include "debug.h"
NPLINKNODE gOpenHandleInstanceList; NPLINKNODE gOpenHandleList; UINT gcOpenInputDevices; UINT gcOpenOutputDevices;
STATIC VOID PASCAL UpdateSegmentLocks(BOOL fIsOutput);
#pragma alloc_text(INIT_TEXT, DeviceOnLoad)
#pragma alloc_text(FIX_COMM_TEXT, IsValidHandle)
/* @func Called at DLL LibInit
* * @comm * * Initialize the handle lists to empty and clear the device reference counts. */ VOID PASCAL DeviceOnLoad(VOID) { gOpenHandleInstanceList = NULL; gOpenHandleList = NULL;
gcOpenInputDevices = 0; gcOpenOutputDevices = 0; }
/* @func Open a device
* * @comm * * This function is thunked to the 32-bit peer. * * This function allocates an <c OPENHANDLEINSTANCE> struct on behalf of the caller. * If the requested device is already open and is an output device, the device's * reference count will be incremented an no other action is taken. If the requested * device is already open and is an input device, then the open will fail. * * If a non-DirectMusic application has the requested device open, then the * open will fail regardless of device type. * * If this open is the first input or output device opened, then it will * page lock the appropriate segments containing callback code and data. * * @rdesc Returns one of the following: * * @flag MMSYSERR_NOERROR | On success * @flag MMSYSERR_NOMEM | If there was insufficient memory to allocate * the tracking structure. * * @flag MMSYSERR_BADDEVICEID | If the given device ID was out of range. * @flag MMSYSERR_ALLOCATED | The specified device is already open. * */ MMRESULT WINAPI OpenLegacyDevice( UINT id, /* @parm MMSYSTEM id of device to open */ BOOL fIsOutput, /* @parm TRUE if this is an output device */ BOOL fShare, /* @parm TRUE if the device should be shareable */ LPHANDLE ph) /* @parm Pointer where handle will be returned */ /* on success. */ { NPOPENHANDLEINSTANCE pohi; NPLINKNODE pLink; NPOPENHANDLE poh; MMRESULT mmr;
DPF(2, "OpenLegacyDevice(%d,%s,%s)", (UINT)id, (LPSTR)(fIsOutput ? "Output" : "Input"), (LPSTR)(fShare ? "Shared" : "Exclusive")); *ph = (HANDLE)NULL;
/* Sharing capture device is not allowed.
*/ if ((!fIsOutput) && (fShare)) { return MMSYSERR_ALLOCATED; }
/* Make sure id is in the valid range of devices
*/ if (fIsOutput) { if (id != MIDI_MAPPER && id >= midiOutGetNumDevs()) { return MMSYSERR_BADDEVICEID; } } else { if (id >= midiInGetNumDevs()) { return MMSYSERR_BADDEVICEID; } }
/* Create an open handle instance. This will be returned to
* Win32 as the handle. */ pohi = (NPOPENHANDLEINSTANCE)LocalAlloc(LPTR, sizeof(OPENHANDLEINSTANCE)); if (NULL == pohi) { return MMSYSERR_NOMEM; }
/* Search through the handles we already have open and try
* to find the handle already open. */ mmr = MMSYSERR_NOERROR; for (pLink = gOpenHandleList; pLink; pLink = pLink->pNext) { poh = (NPOPENHANDLE)pLink;
if (poh->id != id) { continue; }
if ((fIsOutput && (!(poh->wFlags & OH_F_MIDIIN))) || ((!fIsOutput) && (poh->wFlags & OH_F_MIDIIN))) { break; } }
/* If we didn't find it, try to allocate it.
* */ if (NULL == pLink) { poh = (NPOPENHANDLE)LocalAlloc(LPTR, sizeof(OPENHANDLE)); if (NULL == poh) { LocalFree((HLOCAL)pohi); return MMSYSERR_NOMEM; }
poh->uReferenceCount = 1; poh->id = id; poh->wFlags = (fIsOutput ? 0 : OH_F_MIDIIN); if (fShare) { poh->wFlags |= OH_F_SHARED; } InitializeCriticalSection(&poh->wCritSect); } else { poh = (NPOPENHANDLE)pLink; /* Validate sharing modes match.
* If they want exclusive mode, fail. * If the device is already open in exclusive mode, fail. */ if (!fShare) { DPF(0, "Legacy open failed: non-shared open request, port already open."); LocalFree((HLOCAL)pohi); return MIDIERR_BADOPENMODE; }
if (!(poh->wFlags & OH_F_SHARED)) { DPF(0, "Legacy open failed: Port already open in exclusive mode."); LocalFree((HLOCAL)pohi); return MIDIERR_BADOPENMODE; }
++poh->uReferenceCount; }
pohi->pHandle = poh; pohi->fActive = FALSE; pohi->wTask = GetCurrentTask();
/* We lock segments here so we minimize the impacy of activation. However,
* actual device open is tied to activation. */ if (fIsOutput) { ++gcOpenOutputDevices; mmr = MidiOutOnOpen(pohi); if (mmr) { --gcOpenOutputDevices; } UpdateSegmentLocks(fIsOutput); } else { ++gcOpenInputDevices; mmr = MidiInOnOpen(pohi); if (mmr) { --gcOpenInputDevices; } UpdateSegmentLocks(fIsOutput); }
if (poh->uReferenceCount == 1) { ListInsert(&gOpenHandleList, &poh->link); }
ListInsert(&gOpenHandleInstanceList, &pohi->link); ListInsert(&poh->pInstanceList, &pohi->linkHandleList);
*ph = (HANDLE)(DWORD)(WORD)pohi;
return MMSYSERR_NOERROR; }
/* @func Close a legacy device
* * @comm * * This function is thunked to the 32-bit peer. * * It just validates the handle and calls the internal close device API. * * @rdesc Returns one of the following: * * @flag MMSYSERR_NOERROR | On success * * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized. * */ MMRESULT WINAPI CloseLegacyDevice( HANDLE h) /* @parm The handle to close. */ { NPOPENHANDLEINSTANCE pohi = (NPOPENHANDLEINSTANCE)(WORD)h;
DPF(2, "CloseLegacyDevice %04X\n", h);
if (!IsValidHandle(h, VA_F_EITHER, &pohi)) { DPF(0, "CloseLegacyDevice: Invalid handle\n"); return MMSYSERR_INVALHANDLE; }
return CloseLegacyDeviceI(pohi); }
/* @func Activate or deactivate a legacy device
* * @comm * * This function is thunked to the 32-bit peer. * * Validate parameters and pass the call to the internal activate. * * @rdesc Returns one of the following: * * @flag MMSYSERR_NOERROR | On success * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized. * Any other MMRESULT that a midiXxx call might return. * */ MMRESULT WINAPI ActivateLegacyDevice( HANDLE h, BOOL fActivate) { NPOPENHANDLEINSTANCE pohi;
if (!IsValidHandle(h, VA_F_EITHER, &pohi)) { DPF(0, "Activate: Invalid handle\n"); return MMSYSERR_INVALHANDLE; }
return ActivateLegacyDeviceI(pohi, fActivate); }
/* @func Close a legacy device (internal)
* * @comm * * This function deallocates the referenced <c OPENHANDLEINSTANCE> struct. * If it is the last reference to the device, then the device will be closed * as well. * * If this is the last input or output device being closed, then the * appropriate segments containing callback code and data will be * unlocked. * * @rdesc Returns one of the following: * * @flag MMSYSERR_NOERROR | On success * */ MMRESULT PASCAL CloseLegacyDeviceI( NPOPENHANDLEINSTANCE pohi) { NPOPENHANDLE poh;
/* Deactivate this device. This might result in the device being closed.
*/ ActivateLegacyDeviceI(pohi, FALSE);
poh = pohi->pHandle; ListRemove(&gOpenHandleInstanceList, &pohi->link); ListRemove(&poh->pInstanceList, &pohi->linkHandleList);
--poh->uReferenceCount; if (poh->wFlags & OH_F_MIDIIN) { --gcOpenInputDevices; MidiInOnClose(pohi); UpdateSegmentLocks(FALSE /*fIsOutput*/); } else { --gcOpenOutputDevices; MidiOutOnClose(pohi); UpdateSegmentLocks(TRUE /*fIsOutput*/); }
if (0 == poh->uReferenceCount) { ListRemove(&gOpenHandleList, &poh->link); LocalFree((HLOCAL)poh); }
LocalFree((HLOCAL)pohi);
return MMSYSERR_NOERROR; }
/* @func Activate or deactivate a legacy device (internal)
* * @comm * * This function is thunked to the 32-bit peer. * * Handle open and close of the device on first activate and last deactivate. * * @rdesc Returns one of the following: * * @flag MMSYSERR_NOERROR | On success * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized. * Any other MMRESULT that a midiXxx call might return. * */ MMRESULT PASCAL ActivateLegacyDeviceI( NPOPENHANDLEINSTANCE pohi, BOOL fActivate) { NPOPENHANDLE poh; MMRESULT mmr;
poh = pohi->pHandle;
if (fActivate) { if (pohi->fActive) { DPF(0, "Activate: Activating already active handle %04X", pohi); return MMSYSERR_NOERROR; }
poh->uActiveCount++; if (poh->wFlags & OH_F_MIDIIN) { mmr = MidiInOnActivate(pohi); } else { mmr = MidiOutOnActivate(pohi); }
if (mmr == MMSYSERR_NOERROR) { pohi->fActive = TRUE; } else { --poh->uActiveCount; } } else { if (!pohi->fActive) { DPF(0, "Activate: Deactivating already inactive handle %04X", pohi); return MMSYSERR_NOERROR; }
pohi->fActive = TRUE; poh->uActiveCount--;
if (poh->wFlags & OH_F_MIDIIN) { mmr = MidiInOnDeactivate(pohi); } else { mmr = MidiOutOnDeactivate(pohi); }
if (mmr == MMSYSERR_NOERROR) { pohi->fActive = FALSE; } else { --poh->uActiveCount; } }
return mmr; }
/* @func Validate the given handle
* * @comm * * Determine if the given handle is valid, and if so, return the open handle instance. * * The handle is merely a pointer to an <c OPENHANDLEINSTANCE> struct. This function, * in the debug build, will verify that the handle actually points to a struct allocated * by this DLL. In all builds, the handle type will be verified. * * @rdesc Returns one of the following: * @flag MMSYSERR_NOERROR | On success * @flag MMSYSERR_INVALHANDLE | If the given handle is invalid or of the wrong type. * */ BOOL PASCAL IsValidHandle( HANDLE h, /* @parm The handle to verify */ WORD wType, /* @parm The required type of handle. One of the following: */ /* @flag VA_F_INPUT | If the handle must specify an input device */ /* @flag VA_F_OUTPUT | If the handle must specify an output device */ /* @flag VA_F_EITHER | If either type of handle is acceptable */ NPOPENHANDLEINSTANCE FAR *lppohi) /* @parm Will contain the open handle instance on return */ { #ifdef DEBUG
NPLINKNODE pLink; #endif
NPOPENHANDLEINSTANCE pohi = (NPOPENHANDLEINSTANCE)(WORD)h;
#ifdef DEBUG
/* Find the handle instance in the global list
*/ for (pLink = gOpenHandleInstanceList; pLink; pLink = pLink->pNext) { DPF(2, "IsValidHandle: Theirs %04X mine %04X", (WORD)h, (WORD)pLink); if (pLink == (NPLINKNODE)(WORD)h) { break; } }
if (NULL == pLink) { return FALSE; } #endif
DPF(2, "IsValidHandle: Got handle, flags are %04X", pohi->pHandle->wFlags);
*lppohi = pohi; /* Verify the handle type
*/ if (pohi->pHandle->wFlags & OH_F_MIDIIN) { if (wType & VA_F_INPUT) { return TRUE; } } else { if (wType & VA_F_OUTPUT) { return TRUE; } }
*lppohi = NULL;
return FALSE; }
/* @func Lock or unlock segments as need be.
* * @comm * * This function calls the DLL's Lock and Unlock functions to bring the lock status * of the segments containing callback code and data into sync with the actual types * of devices currently open. This prevents having too much memory page locked when * it is not actually being used. * */ STATIC VOID PASCAL UpdateSegmentLocks( BOOL fIsOutput) /* @parm TRUE if the last device opened or closed was an output device */ { if (fIsOutput) { switch(gcOpenOutputDevices) { case 0: if (gcOpenInputDevices) { DPF(2, "Unlocking output"); UnlockCode(LOCK_F_OUTPUT); } else { DPF(2, "Unlocking output+common"); UnlockCode(LOCK_F_OUTPUT | LOCK_F_COMMON); } break;
case 1: if (gcOpenInputDevices) { DPF(2, "Locking output"); LockCode(LOCK_F_OUTPUT); } else { DPF(2, "Locking output+common"); LockCode(LOCK_F_OUTPUT | LOCK_F_COMMON); } break; } } else { switch(gcOpenInputDevices) { case 0: if (gcOpenOutputDevices) { DPF(2, "Unlocking input"); UnlockCode(LOCK_F_INPUT); } else { DPF(2, "Unlocking input+common"); UnlockCode(LOCK_F_INPUT | LOCK_F_COMMON); } break;
case 1: if (gcOpenOutputDevices) { DPF(2, "Locking input"); LockCode(LOCK_F_INPUT); } else { DPF(2, "Locking input+common"); LockCode(LOCK_F_INPUT | LOCK_F_COMMON); } break; } } }
/* @func Clean up all open handles held by a given task
* * @comm This function is called when a task terminates. It will clean up resources left * behind by a process which did not terminate cleanly, and therefore did not tell * this DLL to unload in its context. */ VOID PASCAL CloseDevicesForTask( WORD wTask) { NPLINKNODE pLink; NPOPENHANDLEINSTANCE pohi;
for (pLink = gOpenHandleInstanceList; pLink; pLink = pLink->pNext) { pohi = (NPOPENHANDLEINSTANCE)pLink;
if (pohi->wTask != wTask) { continue; }
DPF(0, "CloseDevicesForTask: Closing %04X", (WORD)pohi); /* NOTE: This will free pohi
*/ CloseLegacyDeviceI(pohi);
pLink = gOpenHandleInstanceList; } }
|