#include #include "../comdevice.h" #include "../common.h" #include "../input.h" #include "../key_config.h" #include "mx.h" #define JVS_SENSE (g_MiceJvsNumBoards == 0 ? FALSE : _MiceJvsBoards[0].m_SenseOut) JVS_STATUS MiceJvsWrapper(PMICE_JVS this, DWORD nMaxRead) { JVS_STATUS status = JVS_STATUS_OK; // -1, because we don't want to treat the checksum as a command! while (this->m_nInIdx < nMaxRead - 1) { JVS_STATUS subStatus = this->vftable->transact(this, MiceJvsRead(this)); if (subStatus > status) status = subStatus; } return status; } void jvs_send_status(unsigned char status, unsigned char* outData, LPDWORD outCount) { short respIndex = 4; outData[0] = JVS_SYNC; outData[1] = JVS_NODE_MASTER; outData[2] = 2; if (status == JVS_MARK || status == JVS_SYNC) { outData[3] = JVS_MARK; outData[4] = status - 1; outData[5] = JVS_NODE_MASTER + 2 + status; *outCount = 6; } else { outData[3] = status; outData[4] = JVS_NODE_MASTER + 2 + status; *outCount = 5; } } static float jvsRateAccum; static float jvsDTHistory[60]; static size_t jvsDTHIndex = 0; static size_t jvsDTHCount = 0; float jvsRate = 0; BYTE jvsInUnmasked[MICE_JVS_MAX]; BYTE jvsOutUnmasked[MICE_JVS_MAX]; BYTE jvsOutMasked[MICE_JVS_MASKED]; void mxjvs_handle(unsigned char* lpMaskedIn, short nMaskedCount, unsigned char* outData, short maxOut, LPDWORD outCount) { // Poll rate calculation static amtime_t last_tick = { .seconds = 0, .microseconds = 0 }; amtime_t now; amiTimerGet(&now); float dt = (float)amiTimerDiffUsec(&last_tick, &now) / (float)1000000; if (last_tick.seconds != 0 || last_tick.microseconds != 0) { jvsRateAccum += dt - jvsDTHistory[jvsDTHIndex]; jvsDTHistory[jvsDTHIndex] = dt; if ((++jvsDTHIndex) == _countof(jvsDTHistory)) jvsDTHIndex = 0; if ((++jvsDTHCount) > _countof(jvsDTHistory)) jvsDTHCount = _countof(jvsDTHistory); jvsRate = (jvsRateAccum > 0.0f) ? (1.0f / (jvsRateAccum / (float)jvsDTHCount)) : FLT_MAX; } last_tick = now; // Actual handler start SHORT nIn = 0; BOOL wasMark = FALSE; for (int i = 0; i < nMaskedCount; i++) { if (lpMaskedIn[i] == JVS_MARK) { wasMark = TRUE; continue; } if (wasMark) jvsInUnmasked[nIn++] = lpMaskedIn[i] + 1; else jvsInUnmasked[nIn++] = lpMaskedIn[i]; } PJVS_PACKET lpPacket = (PJVS_PACKET)jvsInUnmasked; // JVS frame is 4 bytes in total if (nIn < 4) { log_error(plfMxJvs, "inCount impossibly small: %d", nIn); jvs_send_status(JVS_STATUS_UNKNOWN, outData, outCount); return; } // This isn't a JVS packet. We should scan for SYNC, but I don't think we'll ever need to if (lpPacket->m_Sync != JVS_SYNC) { log_error(plfMxJvs, "SYNC missing. Saw 0x%02x", lpPacket->m_Sync); jvs_send_status(JVS_STATUS_UNKNOWN, outData, outCount); return; } // Validate the checksum before proceeding unsigned char sum = 0; bool escape = false; for (int i = 1; i < nIn - 1; i++) sum += jvsInUnmasked[i]; if (sum != jvsInUnmasked[nIn - 1]) { log_error(plfMxJvs, "Checksum failed. Computed 0x%02x, expected 0x%02x", sum, jvsInUnmasked[nIn - 1]); jvs_send_status(JVS_STATUS_SUM, outData, outCount); return; } DWORD nBoardCount = g_MiceJvsNumBoards; if (nBoardCount > _countof(_MiceJvsBoards)) nBoardCount = _countof(_MiceJvsBoards); // Not broadcast, not master-bound, and within bounds if (lpPacket->m_Dst != 0xff && (lpPacket->m_Dst == 0 || lpPacket->m_Dst > nBoardCount)) { log_warning(plfMxJvs, "JVS bound for %02x ignored", lpPacket->m_Dst); *outCount = 0; return; } JVS_STATUS status = JVS_STATUS_UNKNOWN; DWORD nBytesWrote = 0; if (lpPacket->m_Dst == 0xff) { for (DWORD i = 0; i < nBoardCount; i++) { PMICE_JVS lpMiceJvs = &_MiceJvsBoards[i]; lpMiceJvs->m_lpInData = jvsInUnmasked; lpMiceJvs->m_lpOutData = jvsOutUnmasked; lpMiceJvs->m_nInIdx = 3; lpMiceJvs->m_nOutIdx = 4; status = MiceJvsWrapper(lpMiceJvs, nIn); if (lpMiceJvs->m_nOutIdx > 4) { nBytesWrote = lpMiceJvs->m_nOutIdx - 4; break; } } } else { PMICE_JVS lpMiceJvs = &_MiceJvsBoards[nBoardCount - lpPacket->m_Dst]; lpMiceJvs->m_lpInData = jvsInUnmasked; lpMiceJvs->m_lpOutData = jvsOutUnmasked; lpMiceJvs->m_nInIdx = 3; lpMiceJvs->m_nOutIdx = 4; status = MiceJvsWrapper(lpMiceJvs, nIn); nBytesWrote = lpMiceJvs->m_nOutIdx - 4; } if (status == JVS_STATUS_SILENCE) { *outCount = 0; return; } if (status == JVS_STATUS_OK) { ((PJVS_PACKET)&jvsOutUnmasked)->m_Dst = JVS_NODE_MASTER; ((PJVS_PACKET)&jvsOutUnmasked)->m_nBytes = (nBytesWrote + 2) & 0xff; jvsOutUnmasked[3] = status; sum = 0; for (DWORD i = 1; i < nBytesWrote + 4; i++) sum += jvsOutUnmasked[i]; jvsOutUnmasked[nBytesWrote + 4] = sum; DWORD nPadded = 1; jvsOutMasked[0] = JVS_SYNC; for (DWORD i = 1; i <= nBytesWrote + 4; i++) { if (i == JVS_MARK || i == JVS_SYNC) { jvsOutMasked[nPadded++] = JVS_MARK; jvsOutMasked[nPadded++] = jvsOutUnmasked[i] - 1; } else jvsOutMasked[nPadded++] = jvsOutUnmasked[i]; } *outCount = nPadded; memcpy_s(outData, maxOut, jvsOutMasked, nPadded); } else { log_error(plfMxJvs, "JVS board %d returned status %d", lpPacket->m_Dst, status); jvs_send_status(status, outData, outCount); } } BOOL WINAPI mxjvs_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { switch (dwIoControlCode) { case IOCTL_MXJVS_EXCHANGE: log_trace(plfMxJvs, "DeviceIoControl(, , 0x%p, 0x%x, -, " "0x%x, -, -)", lpInBuffer, nInBufferSize, nOutBufferSize); mxjvs_handle(lpInBuffer, nInBufferSize & 0xffff, lpOutBuffer, nOutBufferSize & 0xFFFF, lpBytesReturned); break; default: log_warning(plfMxJvs, "unhandled 0x%08x", dwIoControlCode); return FALSE; } return TRUE; } BOOL mxjvs_SetupComm(void* com, DWORD dwInQueue, DWORD dwOutQueue) { return TRUE; } BOOL mxjvs_PurgeComm(void* com, DWORD dwFlags) { return TRUE; } BOOL mxjvs_GetCommState(void* com, LPDCB lpDCB) { return TRUE; } BOOL mxjvs_SetCommTimeouts(void* com, LPCOMMTIMEOUTS lpDCB) { return TRUE; } BOOL mxjvs_GetCommModemStatus(void* com, LPDWORD lpModelStat) { *lpModelStat = JVS_SENSE ? 0 : MS_DSR_ON; return TRUE; } BOOL mxjvs_SetCommState(void* com, LPDCB lpDCB) { char PARITY[] = { 'N', 'O', 'E', 'M', 'S' }; char* STOP[] = { "1", "1.5", "2" }; log_misc(plfMxJvs, "Switching to %d baud (%d%c%s)", lpDCB->BaudRate, lpDCB->ByteSize, PARITY[lpDCB->Parity], STOP[lpDCB->StopBits]); return TRUE; } void init_jvs_boards() { // new (MiceJvs837_14895, _MiceJvsBoards[0]); new (MiceJvs837_14572, _MiceJvsBoards[0]); // Init all the others for (int i = 1; i < _countof(_MiceJvsBoards); i++) { new (MiceJvs837_14572, _MiceJvsBoards[i]); } } DWORD __stdcall mxjvs_comdev_thread(com_device_t* com) { BYTE inBuffer[512]; BYTE outBuffer[512]; DWORD bytesReturned; while (1) { do { comdev_read_blocking(com, inBuffer, 1); } while (inBuffer[0] != 0xE0); comdev_read_blocking(com, inBuffer + 1, 2); BYTE nbytes = inBuffer[2]; BYTE* bufferPtr = inBuffer + 3; if (nbytes == 0xD0) { comdev_read_blocking(com, bufferPtr++, 1); nbytes = *bufferPtr; } while (nbytes) { comdev_read_blocking(com, bufferPtr++, 1); if (bufferPtr[-1] == 0xD0) comdev_read_blocking(com, bufferPtr++, 1); nbytes--; } short packetSize = (bufferPtr - inBuffer) & 0xFFFF; mxjvs_handle(inBuffer, packetSize, outBuffer, _countof(outBuffer), &bytesReturned); com->modemStatus = JVS_SENSE ? 0 : MS_DSR_ON; comdev_write(com, outBuffer, bytesReturned & 0xFFFF); } } VIRTUAL_SERIAL_DEVICE jvsSerialDevice = { .m_Name = "JVS", .m_Entrypoint = mxjvs_comdev_thread, }; void setup_mxjvs() { init_jvs_boards(); file_hook_t* mxjvs = new_file_hook(L"\\\\.\\mxjvs"); mxjvs->DeviceIoControl = &mxjvs_DeviceIoControl; hook_file(mxjvs); com_hook_t* jvscom = new_com_hook(4); wcscpy_s(jvscom->wName, sizeof jvscom->wName, L"\\\\.\\mxjvs"); wcscpy_s(jvscom->wDosName, sizeof jvscom->wDosName, L"\\\\.\\mxjvs"); jvscom->GetCommState = mxjvs_GetCommState; jvscom->SetCommState = mxjvs_SetCommState; jvscom->SetCommTimeouts = mxjvs_SetCommTimeouts; jvscom->SetupComm = mxjvs_SetupComm; jvscom->PurgeComm = mxjvs_PurgeComm; jvscom->GetCommModemStatus = mxjvs_GetCommModemStatus; mxjvs->com_hook = jvscom; attach_com_device(4, &jvsSerialDevice); }