|
|
//@doc
/******************************************************
** ** @module DTRANS.CPP | DataTransmitter implementation file ** ** Description: ** ** History: ** Created 11/13/97 Matthew L. Coill (mlc) ** ** (c) 1986-1997 Microsoft Corporation. All Rights Reserved. ******************************************************/
#include "FFDevice.h"
#include "DTrans.h"
//#include <devioctl.h>
#ifdef _DEBUG
extern void DebugOut(LPCTSTR szDebug); #else !_DEBUG
#define DebugOut(x)
#endif _DEBUG
const char cCommPortNames[4][5] = { "COM1", "COM2", "COM3", "COM4" }; const unsigned short c1_16_BytesPerShot = 3; const DWORD c1_16_SerialSleepTime = 1;
#define UART_FILTER_NAME TEXT("\\\\.\\.\\PortClass0\\Uart")
const WORD c_LongMsgMax = 256;
inline BOOL IsHandleValid(HANDLE handleToCheck) { return ((handleToCheck != NULL) && (handleToCheck != INVALID_HANDLE_VALUE)); }
#define CHECK_RELEASE_AND_NULL(pIUnknown) \
if (pIUnknown != NULL) \ { \ pIUnknown->Release(); \ pIUnknown = NULL; \ }
/************************** SerialDataTransmitter Class ******************************/
/******************************************************
** ** SerialDataTransmitter::SerialDataTransmitter() ** ** @mfunc Constructor. ** ******************************************************/ SerialDataTransmitter::SerialDataTransmitter() : DataTransmitter(), m_SerialPort(INVALID_HANDLE_VALUE), m_SerialPortIDHack(0) { }
/******************************************************
** ** SerialDataTransmitter::~SerialDataTransmitter() ** ** @mfunc Destructor. ** ******************************************************/ SerialDataTransmitter::~SerialDataTransmitter() { if (m_SerialPort != INVALID_HANDLE_VALUE) { if (::CloseHandle(m_SerialPort) == FALSE) { // ASSUME_NOT_REACHED();
} m_SerialPort = INVALID_HANDLE_VALUE; } }
/******************************************************
** ** SerialDataTransmitter::Initialize() ** ** returns: TRUE if initialized FALSE if not able to initialize ** @mfunc Initialize. ** ******************************************************/ BOOL SerialDataTransmitter::Initialize() { // If already open, close for reinitialization
if (m_SerialPort != INVALID_HANDLE_VALUE) { if (CloseHandle(m_SerialPort) == FALSE) { // ASSUME_NOT_REACHED();
} m_SerialPort = INVALID_HANDLE_VALUE; }
for (int portNum = 0; portNum < 4; portNum++) { DebugOut(cCommPortNames[portNum]); DebugOut(":\r\n"); m_SerialPort = ::CreateFile(cCommPortNames[portNum], GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (m_SerialPort != INVALID_HANDLE_VALUE) { DCB CommDCB; if (::GetCommState(m_SerialPort, &CommDCB)) { #ifdef _DEBUG
char dbgout[255]; wsprintf(dbgout, "Baud Rate = 0x%08X (38400 = 0x%08X)\r\n", CommDCB.BaudRate, CBR_38400); ::OutputDebugString(dbgout); #endif _DEBUG
CommDCB.BaudRate = CBR_38400; CommDCB.StopBits = ONESTOPBIT; CommDCB.ByteSize = 8; CommDCB.Parity = NOPARITY; if (!::SetCommState(m_SerialPort, &CommDCB)) { DebugOut("Unabled to set baud rate\r\n"); } } ::GetCommState(m_SerialPort, &CommDCB);
if (g_ForceFeedbackDevice.DetectHardware()) { m_SerialPortIDHack = portNum + 1; // Write to shared file
DebugOut(" Opened and FFDev Detected\r\n"); break; // Exit from for loop
} // Not found
::CloseHandle(m_SerialPort); DebugOut(" Opened but FFDev NOT detected\r\n"); m_SerialPort = INVALID_HANDLE_VALUE; } else { DebugOut(" Not able to open\r\n"); } } return (m_SerialPort != INVALID_HANDLE_VALUE); }
/******************************************************
** ** SerialDataTransmitter::Send() ** ** returns: TRUE if all data was successfully sent ** @mfunc Send. ** ******************************************************/ BOOL SerialDataTransmitter::Send(BYTE* data, UINT numBytes) { // Do we have a valid serial port (hopefully with MS FF device connected)
if (m_SerialPort == NULL) { return FALSE; }
/* char dbgOut[255];
::OutputDebugString("(SerialDataTransmitter::Send) : "); for (UINT i = 0; i < numBytes; i++) { wsprintf(dbgOut, " 0x%02X", data[i]); ::OutputDebugString(dbgOut); } ::OutputDebugString("\r\n"); */ if ((g_ForceFeedbackDevice.GetFirmwareVersionMajor() == 1) && (g_ForceFeedbackDevice.GetFirmwareVersionMinor() == 16)) { DWORD subTotalWritten; DWORD totalWritten = 0; DWORD numLeft = numBytes; while (numLeft > c1_16_BytesPerShot) { if (::WriteFile(m_SerialPort, (data + totalWritten), c1_16_BytesPerShot, &subTotalWritten, NULL) == FALSE) { return FALSE; } totalWritten += subTotalWritten; numLeft -= subTotalWritten; Sleep(c1_16_SerialSleepTime); } if (numLeft > 0) { if (::WriteFile(m_SerialPort, (data + totalWritten), numLeft, &subTotalWritten, NULL) == FALSE) { return FALSE; } totalWritten += subTotalWritten; } return (totalWritten == numBytes); }
// Firmware other than 1.16
DWORD numWritten; if (::WriteFile(m_SerialPort, data, numBytes, &numWritten, NULL) == FALSE) { return FALSE; } return (numWritten == numBytes); }
/************************** DMusicTransmitter Class ******************************/
/****************************************
** ** DMusicTransmitter::DMusicTransmitter() ** ** @mfunc Constructor for DirectMusic Transmitter ** *****************************************/ DMusicTransmitter::DMusicTransmitter() : DataTransmitter(), m_pIDirectMusic(NULL), m_pIDirectMusicPort(NULL), m_pIDirectMusicBuffer(NULL) { }
/****************************************
** ** DMusicTransmitter::~DMusicTransmitter() ** ** @mfunc Destructor for DirectMusic Transmitter ** *****************************************/ DMusicTransmitter::~DMusicTransmitter() { CHECK_RELEASE_AND_NULL(m_pIDirectMusicBuffer); CHECK_RELEASE_AND_NULL(m_pIDirectMusicPort); CHECK_RELEASE_AND_NULL(m_pIDirectMusic); }
/****************************************
** ** BOOL DMusicTransmitter::Initialize() ** ** @mfunc Intialize the Direct Music Transmission path ** ** @rdesc TRUE if initialization was successful, FALSE otherwise ** *****************************************/ BOOL DMusicTransmitter::Initialize() { // Case they are reinitializing
CHECK_RELEASE_AND_NULL(m_pIDirectMusicBuffer); CHECK_RELEASE_AND_NULL(m_pIDirectMusicPort); CHECK_RELEASE_AND_NULL(m_pIDirectMusic);
// Create the global IDirectMusic Interface
HRESULT hr = ::CoCreateInstance(CLSID_DirectMusic, NULL, CLSCTX_INPROC, IID_IDirectMusic, (void**)&m_pIDirectMusic); if (FAILED(hr) || m_pIDirectMusic == NULL) { return FALSE; }
// Enumerate and create the port when valid one is found
DMUS_PORTCAPS portCaps; portCaps.dwSize = sizeof portCaps; DWORD dwPortIndex = 0; for (;;) { HRESULT hr = m_pIDirectMusic->EnumPort(dwPortIndex, &portCaps); if (FAILED(hr) || hr == S_FALSE) { // Either we have failed or run out of ports
return FALSE; } if (portCaps.dwClass == DMUS_PC_OUTPUTCLASS) { DMUS_PORTPARAMS portParams; portParams.dwSize = sizeof DMUS_PORTPARAMS; portParams.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS; portParams.dwChannelGroups = 1; // hr = m_pIDirectMusic->CreatePort(portCaps.guidPort, GUID_NULL, &portParams, &m_pIDirectMusicPort, NULL);
hr = m_pIDirectMusic->CreatePort(portCaps.guidPort, &portParams, &m_pIDirectMusicPort, NULL); break; } dwPortIndex++; }
// Create the buffer
DMUS_BUFFERDESC dmbd; dmbd.dwSize = sizeof DMUS_BUFFERDESC; dmbd.dwFlags = 0; // dmbd.guidBufferFormat = GUID_KSMusicFormat;
dmbd.guidBufferFormat = GUID_NULL; dmbd.cbBuffer = 256; hr = m_pIDirectMusic->CreateMusicBuffer(&dmbd, &m_pIDirectMusicBuffer, NULL); if (FAILED(hr) || m_pIDirectMusicBuffer == NULL) { return FALSE; }
return TRUE; }
/****************************************
** ** BOOL DMusicTransmitter::Send(BYTE* pData, UINT ulByteCount) ** ** @mfunc Sends bytes via DirectMusic to the stick ** ** @rdesc TRUE if send was successful, FALSE otherwise ** *****************************************/ BOOL DMusicTransmitter::Send ( BYTE* pData, //@parm Data buffer to send
UINT ulByteCount //@parm Number of bytes in buffer to send
) { // Do sanity checks
if ((pData == NULL) || (m_pIDirectMusicPort == NULL) || (m_pIDirectMusicBuffer == NULL) || (ulByteCount == 0)) { return FALSE; }
// Check if we need to pack sysex or channel message
if (pData[0] == 0xF0) { // Create system exclusive
/*
// Pack the sysex-message into the buffer
HRESULT hr = m_pIDirectMusicBuffer->PackSysEx(0, 1, ulByteCount, pData); if (FAILED(hr)) { // Unable to pack the buffer
return FALSE; } */ } else { // Channel Message (fix intel backwards byte order)
DWORD channelMessage = pData[0]; if (ulByteCount > 1) { channelMessage |= pData[1] << 8; if (ulByteCount > 2) { channelMessage |= pData[2] << 16; } }
// Pack the channel-message into the buffer
/* HRESULT hr = m_pIDirectMusicBuffer->PackChannelMsg(0, 1, channelMessage);
if (FAILED(hr)) { // Unable to pack the buffer
return FALSE; } */ }
// Send the buffer to the port
HRESULT hr = m_pIDirectMusicPort->PlayBuffer(m_pIDirectMusicBuffer); if (FAILED(hr)) { // Unable to send the data across the port
return FALSE; }
return TRUE; }
#if 0
/************************** PinTransmitter Class ******************************/
/******************************************************
** ** PinTransmitter::PinTransmitter() ** ** @mfunc Constructor. ** ******************************************************/ PinTransmitter::PinTransmitter() : DataTransmitter(), m_UartFilter(INVALID_HANDLE_VALUE), m_MidiPin(INVALID_HANDLE_VALUE), m_MidiOutEvent(INVALID_HANDLE_VALUE) { }
/******************************************************
** ** PinTransmitter::~PinTransmitter() ** ** @mfunc Destructor. ** ******************************************************/ PinTransmitter::~PinTransmitter() { // Close the send event
if (IsHandleValid(m_MidiOutEvent)) { ::CloseHandle(m_MidiOutEvent); m_MidiOutEvent = NULL; }
// Close the pin
if (IsHandleValid(m_MidiPin)) { ::CloseHandle(m_MidiPin); m_MidiPin = INVALID_HANDLE_VALUE; }
// Close the Uart
if (IsHandleValid(m_UartFilter)) { ::CloseHandle(m_UartFilter); m_UartFilter = INVALID_HANDLE_VALUE; } }
/******************************************************
** ** PinTransmitter::Initialize() ** ** returns: TRUE if initialized FALSE if not able to initialize ** @mfunc Initialize. ** ******************************************************/ BOOL PinTransmitter::Initialize() { // Load the ksUserLibrary and grab the create pin function
HINSTANCE ksUserLib = ::LoadLibrary(TEXT("KsUser.dll")); if (ksUserLib == NULL) { return FALSE; } KSCREATEPIN pfCreatePin = (KSCREATEPIN)::GetProcAddress(ksUserLib, TEXT("KsCreatePin")); if (pfCreatePin == NULL) { ::FreeLibrary(ksUserLib); return FALSE; }
// Open the Uart
m_UartFilter = ::CreateFile(UART_FILTER_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (m_UartFilter == INVALID_HANDLE_VALUE) { ::FreeLibrary(ksUserLib); return FALSE; }
// Create Overlapped event
OVERLAPPED overlapped; ::memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
// Get the number of pins
KSP_PIN ksPinProp; ::memset(&ksPinProp, 0, sizeof(ksPinProp)); ksPinProp.Property.Set = KSPROPSETID_Pin; ksPinProp.Property.Id = KSPROPERTY_PIN_CTYPES; ksPinProp.Property.Flags = KSPROPERTY_TYPE_GET; DWORD numPins = 0; OverLappedPinIOCTL(overlapped, ksPinProp, &numPins, sizeof(numPins));
// Check each pin for proper type, then try to create
BOOL wasCreated = FALSE; for (UINT pinNum = 0; (pinNum < numPins) && (wasCreated == FALSE); pinNum++) { ksPinProp.PinId = pinNum; ksPinProp.Property.Id = KSPROPERTY_PIN_DATAFLOW; KSPIN_DATAFLOW dataFlow = (KSPIN_DATAFLOW)0; if (OverLappedPinIOCTL(overlapped, ksPinProp, &dataFlow, sizeof(dataFlow)) == TRUE) { if (dataFlow == KSPIN_DATAFLOW_IN) { ksPinProp.Property.Id = KSPROPERTY_PIN_COMMUNICATION; KSPIN_COMMUNICATION communication = KSPIN_COMMUNICATION_NONE; if (OverLappedPinIOCTL(overlapped, ksPinProp, &communication, sizeof(communication)) == TRUE) { if ((communication == KSPIN_COMMUNICATION_SINK) || (communication == KSPIN_COMMUNICATION_BOTH)) { wasCreated = CreatePinInstance(pinNum, pfCreatePin); } } } } } ::FreeLibrary(ksUserLib); ::CloseHandle(overlapped.hEvent); if (wasCreated == FALSE) { ::CloseHandle(m_UartFilter); m_UartFilter = INVALID_HANDLE_VALUE; return FALSE; } return TRUE; }
/******************************************************
** ** PinTransmitter::OverLappedPinIOCTL() ** ** returns: TRUE if able to proform Pin Property IOCTL ** @mfunc OverLappedPinIOCTL. ******************************************************/ BOOL PinTransmitter::OverLappedPinIOCTL(OVERLAPPED overlapped, KSP_PIN ksPinProp, void* pData, DWORD dataSize) { // IOCTL the Property
if (::DeviceIoControl(m_UartFilter, IOCTL_KS_PROPERTY, &ksPinProp, sizeof(ksPinProp), pData, dataSize, NULL, &overlapped) == TRUE) { return TRUE; }
// Failed IOCTL check if more time is needed
if (::GetLastError() != ERROR_IO_PENDING) { return FALSE; }
// Do wait
if (::WaitForSingleObject(overlapped.hEvent, 3000) == WAIT_OBJECT_0) { return TRUE; // Waiting paid off
} return FALSE; // Grew tired of waiting
}
/******************************************************
** ** PinTransmitter::CreatePinInstance() ** ** returns: TRUE if able to create the requested pin instance ** @mfunc CreatePinInstance. ******************************************************/ BOOL PinTransmitter::CreatePinInstance(UINT pinNumber, KSCREATEPIN pfCreatePin) { // Set the pin format
KSDATAFORMAT ksDataFormat; ::memset(&ksDataFormat, 0, sizeof(ksDataFormat)); ksDataFormat.FormatSize = sizeof(ksDataFormat); ksDataFormat.MajorFormat = KSDATAFORMAT_TYPE_MUSIC; ksDataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_MIDI; ksDataFormat.Specifier = KSDATAFORMAT_SPECIFIER_NONE;
// Set the pin connection information
KSPIN_CONNECT* pConnectionInfo = (KSPIN_CONNECT*) new BYTE[sizeof(KSPIN_CONNECT) + sizeof(ksDataFormat)]; ::memset(pConnectionInfo, 0, sizeof(KSPIN_CONNECT)); pConnectionInfo->Interface.Set = KSINTERFACESETID_Standard; pConnectionInfo->Interface.Id = KSINTERFACE_STANDARD_STREAMING; pConnectionInfo->Medium.Set = KSMEDIUMSETID_Standard; pConnectionInfo->Medium.Id = KSMEDIUM_STANDARD_DEVIO; pConnectionInfo->PinId = pinNumber; pConnectionInfo->Priority.PriorityClass = KSPRIORITY_NORMAL; pConnectionInfo->Priority.PrioritySubClass = 1; ::memcpy(pConnectionInfo + 1, &ksDataFormat, sizeof(ksDataFormat));
DWORD status = pfCreatePin(m_UartFilter, pConnectionInfo, FILE_WRITE_ACCESS, &m_MidiPin); delete[] pConnectionInfo; if (status != NO_ERROR) { #ifdef _DEBUG
TCHAR buff[256]; wsprintf(buff, TEXT("Error Creating Pin: 0x%08X\r\n"), status); ::OutputDebugString(buff); #endif
return FALSE; }
SetPinState(KSSTATE_PAUSE);
return TRUE; }
/******************************************************
** ** PinTransmitter::Send() ** ** returns: TRUE if all data was successfully sent ** @mfunc Send. ** ******************************************************/ BOOL PinTransmitter::Send(BYTE* pData, UINT numBytes) { if (!IsHandleValid(m_MidiPin)) { return FALSE; }
BYTE musicData[c_LongMsgMax + sizeof(KSMUSICFORMAT)]; ::memset(musicData, 0, sizeof(musicData)); ((KSMUSICFORMAT*)musicData)->ByteCount = numBytes; ::memcpy(((KSMUSICFORMAT*)musicData) + 1, pData, numBytes);
KSSTREAM_HEADER ksStreamHeader; ::memset(&ksStreamHeader, 0, sizeof(ksStreamHeader)); ksStreamHeader.Size = sizeof(ksStreamHeader); ksStreamHeader.PresentationTime.Numerator = 1; ksStreamHeader.PresentationTime.Denominator = 1; ksStreamHeader.FrameExtent = sizeof(musicData); ksStreamHeader.DataUsed = sizeof KSMUSICFORMAT + numBytes; ksStreamHeader.Data = (void*)musicData;
if (!IsHandleValid(m_MidiOutEvent)) { m_MidiOutEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); } OVERLAPPED overlapped; ::memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = m_MidiOutEvent;
SetPinState(KSSTATE_RUN); if (!DeviceIoControl(m_MidiPin, IOCTL_KS_WRITE_STREAM, NULL, 0, &ksStreamHeader, sizeof(ksStreamHeader), NULL, &overlapped)) { if (GetLastError() == ERROR_IO_PENDING) { ::WaitForSingleObject(overlapped.hEvent, 3000); } } SetPinState(KSSTATE_PAUSE); return TRUE; }
/******************************************************
** ** PinTransmitter::SetPinState() ** ** returns: Nothing ** @mfunc SetPinState. ** ******************************************************/ void PinTransmitter::SetPinState(KSSTATE state) { if (!IsHandleValid(m_MidiPin)) { return; }
KSPROPERTY ksProperty; ::memset(&ksProperty, 0, sizeof(ksProperty)); ksProperty.Set = KSPROPSETID_Connection; ksProperty.Id = KSPROPERTY_CONNECTION_STATE; ksProperty.Flags = KSPROPERTY_TYPE_SET;
OVERLAPPED overlapped; ::memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (IsHandleValid(overlapped.hEvent)) { if( !DeviceIoControl(m_MidiPin, IOCTL_KS_PROPERTY, &ksProperty, sizeof ksProperty, &state, sizeof state, NULL, &overlapped )) { if (GetLastError() == ERROR_IO_PENDING) { WaitForSingleObject(overlapped.hEvent, 30000); } } ::CloseHandle(overlapped.hEvent); } }
#endif
|