Modernization of the legacy vJoy-based RIO cockpit interface for Win10/11, removing the vJoy dependency in favor of a custom VHF/UMDF HID driver, rewritten in C#/.NET 8 as a background tray app with per-game profiles. - Reorganize: legacy C++ -> legacy/, cockpit art -> docs/reference/ - RioJoy.sln: src/RioJoy.Core (lib) + src/RioJoy.Tray (tray app), net8.0-windows x64 - driver/ placeholder for the RioGamepad WDK driver - docs/PLAN.md (7-phase plan; profiles + serial-yield model) - docs/PROTOCOL.md (RIO wire format + iRIO input-map reference) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
363 lines
16 KiB
C++
363 lines
16 KiB
C++
#include <cstdio>
|
|
#include <windows.h>
|
|
#include "c232comm.h"
|
|
#include "CRIO.h"
|
|
|
|
|
|
static int RIOLengths[] = {
|
|
0, // 0.CheckRequest
|
|
0, // 1.VersionRequest
|
|
0, // 2.AnalogRequest
|
|
1, // 3.ResetRequest
|
|
2, // 4.LampRequest
|
|
2, // 5.CheckReply
|
|
2, // 6.VersionReply
|
|
10, // 7.AnalogReply
|
|
1, // 8.ButtonPressed
|
|
1, // 9.ButtonReleased
|
|
2, // 10.KeyPressed
|
|
2, // 11.KeyReleased
|
|
1 // 12.TestModeChange
|
|
};
|
|
|
|
static const char* RIOPackets[] = {
|
|
"CheckRequest",
|
|
"VersionRequest",
|
|
"AnalogRequest",
|
|
"ResetRequest",
|
|
"LampRequest",
|
|
"CheckReply",
|
|
"VersionReply",
|
|
"AnalogReply",
|
|
"ButtonPressed",
|
|
"ButtonReleased",
|
|
"KeyPressed",
|
|
"KeyReleased",
|
|
"TestModeChange",
|
|
};
|
|
|
|
void CRIO::Init ()
|
|
{
|
|
m_pStart = &m_ba[0];
|
|
m_pEnd = &m_ba[sizeof (m_ba)];
|
|
m_nCount = 0;
|
|
m_dwFlags = 0;
|
|
m_nState = 0;
|
|
m_nRead = 0;
|
|
m_nLeft = 0;
|
|
m_bCheck = 0;
|
|
m_nReplies = 0;
|
|
TestModeActive = 0;
|
|
m_dwAnalogRequest = 0;
|
|
}
|
|
|
|
void CRIO::RIOBordState (BYTE ID)
|
|
{
|
|
switch (ID)
|
|
{
|
|
case BoardMissing:
|
|
break;
|
|
case BoardBad:
|
|
break;
|
|
case LampBad:
|
|
break;
|
|
case RestartCount:
|
|
break;
|
|
case AbandonCount:
|
|
break;
|
|
case FullBufferCount:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CRIO::DoEventLoop()
|
|
{
|
|
CString str, temp;
|
|
BYTE baBuffer[256];
|
|
int nRet = -1;
|
|
|
|
if (IsValid()) {
|
|
memset (baBuffer, 0, sizeof(baBuffer));
|
|
nRet = rxCom (&baBuffer[0], sizeof(baBuffer));
|
|
|
|
if (nRet > 0) {
|
|
EventLoop (baBuffer, nRet);
|
|
} else {
|
|
}
|
|
CheckAnalogRequest();
|
|
} else {
|
|
}
|
|
while(m_ListBox.GetCount() > 40) {
|
|
m_ListBox.DeleteString(0);
|
|
}
|
|
}
|
|
|
|
void CRIO::EventLoop(const BYTE *buf, int nLen)
|
|
{
|
|
int nNewLen;
|
|
BYTE *pIndex;
|
|
|
|
CString strText;
|
|
|
|
nNewLen = m_nCount + nLen;
|
|
pIndex = &m_pStart[nNewLen];
|
|
|
|
if (pIndex > m_pEnd) {
|
|
if (m_nCount > 0) {
|
|
memcpy (&m_ba[0], m_pStart, m_nCount);
|
|
}
|
|
m_pStart = &m_ba[0];
|
|
}
|
|
|
|
memcpy (&m_pStart[m_nCount], &buf[0], nLen);
|
|
m_nCount += nNewLen;
|
|
|
|
while (m_nCount > 0)
|
|
{
|
|
BYTE bCmd = m_pStart[0];
|
|
|
|
switch (bCmd)
|
|
{
|
|
case IDLE_CHAR:
|
|
m_ListBox.AddString("IDLE");
|
|
break;
|
|
|
|
case ACK_CHAR:
|
|
m_dwFlags |= RX_ACK_BIT;
|
|
m_ListBox.AddString("ACK");
|
|
break;
|
|
|
|
case NAK_CHAR:
|
|
m_dwFlags |= RX_NAK_BIT;
|
|
m_ListBox.AddString("NAK");
|
|
break;
|
|
|
|
default:
|
|
if (m_nState) {
|
|
if (bCmd & 0x80) {
|
|
m_nState = 0;
|
|
if (bCmd == RESTART_CHAR) {
|
|
// discard...
|
|
m_ListBox.AddString("DISCARD");
|
|
} else {
|
|
// error.
|
|
AddReply (NAK_CHAR);
|
|
}
|
|
} else {
|
|
if (m_nLeft > 0) {
|
|
m_baRead[m_nRead++] = bCmd;
|
|
m_bCheck += bCmd;
|
|
m_nLeft--;
|
|
} else {
|
|
MSBOFF (m_bCheck);
|
|
m_nState = 0;
|
|
if (bCmd == m_bCheck) {
|
|
// chechsum OK.
|
|
AddReply (ACK_CHAR);
|
|
#ifdef TEST_RIO
|
|
switch(m_baRead[0]) {
|
|
case rio_CheckReply:
|
|
strText.Format("CheckReply(%x)", m_baRead[1]);
|
|
RIOBordState (m_baRead[1]);
|
|
break;
|
|
case rio_VersionReply:
|
|
strText = "VersionReply";
|
|
break;
|
|
case rio_AnalogReply:
|
|
strText = "AnalogReply";
|
|
m_dwAnalogRequest = 0;
|
|
break;
|
|
case rio_ButtonPressed:
|
|
strText.Format("[%d] 버튼눌림", m_baRead[1]);
|
|
break;
|
|
case rio_ButtonReleased:
|
|
strText.Format("[%d] 버튼띰", m_baRead[1]);
|
|
break;
|
|
case rio_KeyPressed:
|
|
strText = "KeyPressed";
|
|
break;
|
|
case rio_TestModeChange:
|
|
TestModeActive = 1;
|
|
if (m_baRead[1] != 0) {
|
|
}
|
|
else {
|
|
TestModeActive = 0;
|
|
}
|
|
strText = "TestModeChange";
|
|
break;
|
|
default:
|
|
strText = "Default";
|
|
break;
|
|
}
|
|
m_ListBox.AddString(strText);
|
|
#else //
|
|
//extern void RioButtonManager (BYTE *byData);
|
|
//RioButtonManager(m_baRead);
|
|
#endif //
|
|
} else {
|
|
// error.
|
|
AddReply(NAK_CHAR);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (bCmd & 0x80) {
|
|
if (bCmd == RESTART_CHAR) {
|
|
// restart...
|
|
m_ListBox.AddString("Restart");
|
|
} else {
|
|
MSBOFF (bCmd);
|
|
if (MAXLENGTH) {
|
|
// valid command...
|
|
m_bCheck = bCmd;
|
|
m_baRead[0] = bCmd | 0x80;
|
|
m_nRead = 1;
|
|
m_nLeft = RIOLengths[bCmd];
|
|
m_nState = 1;
|
|
} else {
|
|
// unknown command...
|
|
}
|
|
}
|
|
} else {
|
|
// error...
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
m_pStart++;
|
|
m_nCount--;
|
|
}
|
|
CheckReplies();
|
|
}
|
|
|
|
void CRIO::AddReply (BYTE bReply)
|
|
{
|
|
// ASSERT ((bReply == ACK_CHAR) || (bReply == NAK_CHAR));
|
|
if (m_nReplies < (sizeof (m_baReplies) / sizeof (m_baReplies[0]))) {
|
|
m_baReplies[m_nReplies++] = bReply;
|
|
} else {
|
|
// reply buffer full...
|
|
}
|
|
}
|
|
|
|
void CRIO::CheckReplies ()
|
|
{
|
|
if (m_nReplies > 0) {
|
|
int nRet = txCom (&m_baReplies, m_nReplies);
|
|
if (nRet > 0) {
|
|
CString str;
|
|
str.Format(">>ACK or NAK - %d", nRet);
|
|
m_ListBox.AddString(str);
|
|
m_nReplies -= nRet;
|
|
if (m_nReplies > 0) {
|
|
memcpy (&m_baReplies[0], &m_baReplies[nRet], m_nReplies);
|
|
} else {
|
|
// ASSERT (m_nReplies == 0);
|
|
m_nReplies = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRIO::CheckAnalogRequest()
|
|
{
|
|
static BOOL s_b1st = TRUE;
|
|
if (s_b1st) {
|
|
s_b1st = FALSE;
|
|
} else {
|
|
if (m_dwAnalogRequest == 0) {
|
|
update:
|
|
RequestAnalogUpdate();
|
|
m_dwAnalogRequest = GetTickCount();
|
|
} else {
|
|
if ((m_dwAnalogRequest + 1000) < GetTickCount()) {
|
|
goto update;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRIO::SendCommand (const BYTE* pbPacket)
|
|
{
|
|
BYTE ba[256];
|
|
|
|
BYTE bCmd = *pbPacket++;
|
|
|
|
if (bCmd & 0x80) {
|
|
MSBOFF (bCmd);
|
|
if (bCmd < MAXLENGTH) {
|
|
CString str;
|
|
str.Format(">>%s", RIOPackets[bCmd]);
|
|
|
|
int nLeft = RIOLengths[bCmd];
|
|
BYTE* p = &ba[0];
|
|
BYTE bCheck = bCmd;
|
|
MSBON (bCmd);
|
|
*p++ = bCmd;
|
|
|
|
while (nLeft > 0) {
|
|
BYTE b = *pbPacket++;
|
|
MSBOFF (b);
|
|
bCheck += b;
|
|
*p++ = b;
|
|
nLeft--;
|
|
}
|
|
|
|
MSBOFF (bCheck);
|
|
*p++ = bCheck;
|
|
SendPacket (&ba[0], p - &ba[0]);
|
|
|
|
m_ListBox.AddString(str);
|
|
} else {
|
|
m_ListBox.AddString("unknown command");
|
|
}
|
|
} else {
|
|
m_ListBox.AddString("error");
|
|
}
|
|
}
|
|
|
|
void CRIO::SendPacket (const BYTE* pbPacket, int nLen)
|
|
{
|
|
CheckReplies();
|
|
txComLoop(pbPacket, nLen);
|
|
}
|
|
|
|
void CRIO::SetLamp(int lampNumber, int state)
|
|
{
|
|
static BYTE request_lamp_string[] = { rio_LampRequest, 0, 0 };
|
|
|
|
request_lamp_string[1] = (BYTE) (lampNumber & 0x7F);
|
|
request_lamp_string[2] = (BYTE) (state & 0x7F);
|
|
|
|
SendCommand (request_lamp_string);
|
|
}
|
|
|
|
void CRIO::GeneralReset()
|
|
{
|
|
static BYTE request_reset_string[] = { rio_ResetRequest, 0 };
|
|
SendCommand (request_reset_string);
|
|
}
|
|
|
|
void CRIO::ResetThrottle()
|
|
{
|
|
static BYTE request_throttle_string[] = { rio_ResetRequest, 1 };
|
|
SendCommand (request_throttle_string);
|
|
}
|
|
|
|
void CRIO::RequestCheck()
|
|
{
|
|
static BYTE request_check_string[] = { rio_CheckRequest };
|
|
SendCommand (request_check_string);
|
|
}
|
|
|
|
void CRIO::RequestVersion()
|
|
{
|
|
static BYTE request_version_string[] = { rio_VersionRequest };
|
|
SendCommand (request_version_string);
|
|
}
|
|
|
|
void CRIO::RequestAnalogUpdate()
|
|
{
|
|
static BYTE request_analog_string[] = { rio_AnalogRequest };
|
|
SendCommand (request_analog_string);
|
|
} |