mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-04 15:50:42 -07:00
Initial commit for GitHub migration based on spice2x-25-03-03
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
#include "piuio.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// it's 2019 and I'm writing code to avoid issues with Windows XP.
|
||||
static HINSTANCE WINUSB_INSTANCE = nullptr;
|
||||
static std::string WINUSB_NAME = "winusb.dll";
|
||||
|
||||
static std::string WinUsb_InitializeStr = "WinUsb_Initialize";
|
||||
typedef BOOL(__stdcall * WinUsb_Initialize_t)(
|
||||
_In_ HANDLE DeviceHandle,
|
||||
_Out_ PWINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
|
||||
static std::string WinUsb_ControlTransferStr = "WinUsb_ControlTransfer";
|
||||
typedef BOOL(__stdcall * WinUsb_ControlTransfer_t)(
|
||||
_In_ WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
_In_ WINUSB_SETUP_PACKET SetupPacket,
|
||||
_Out_writes_bytes_to_opt_(BufferLength, *LengthTransferred) PUCHAR Buffer,
|
||||
_In_ ULONG BufferLength,
|
||||
_Out_opt_ PULONG LengthTransferred,
|
||||
_In_opt_ LPOVERLAPPED Overlapped
|
||||
);
|
||||
|
||||
static WinUsb_ControlTransfer_t pWinUsb_ControlTransfer = nullptr;
|
||||
static WinUsb_Initialize_t pWinUsb_Initialize = nullptr;
|
||||
|
||||
const string rawinput::PIUIO::LIGHT_NAMES[rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P2 Up",
|
||||
"P2 Down",
|
||||
"P2 Left",
|
||||
"P2 Right",
|
||||
"P2 Center",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Neon",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P1 Up",
|
||||
"P1 Down",
|
||||
"P1 Left",
|
||||
"P1 Right",
|
||||
"P1 Center",
|
||||
"Marquee Upper Left",
|
||||
"Marquee Lower Right",
|
||||
"Marquee Lower Left",
|
||||
"Marquee Upper Right",
|
||||
"USB Enable",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
rawinput::PIUIO::PIUIO(Device *device) {
|
||||
this->device = device;
|
||||
button_state = 0;
|
||||
light_data = 0;
|
||||
current_sensor = 0;
|
||||
prev_timeout = 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::LoadWinUSBFunctions() {
|
||||
static bool functions_loaded = false;
|
||||
|
||||
if (functions_loaded)
|
||||
return true;
|
||||
|
||||
// see if we even have winusb available.
|
||||
WINUSB_INSTANCE = libutils::try_library(WINUSB_NAME);
|
||||
|
||||
// if windows didn't load it just now, then it's not on the system.
|
||||
if (WINUSB_INSTANCE != nullptr) {
|
||||
|
||||
// load winusb functions
|
||||
pWinUsb_Initialize = (WinUsb_Initialize_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_InitializeStr.c_str());
|
||||
pWinUsb_ControlTransfer = (WinUsb_ControlTransfer_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_ControlTransferStr.c_str());
|
||||
|
||||
// make sure they actually loaded.
|
||||
if(pWinUsb_Initialize != nullptr && pWinUsb_ControlTransfer!= nullptr)
|
||||
functions_loaded = true;
|
||||
} else {
|
||||
log_warning("piuio", "Could not load \'{}\', skipping...", WINUSB_NAME);
|
||||
}
|
||||
|
||||
return functions_loaded;
|
||||
}
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA rawinput::PIUIO::GetDevicePath() {
|
||||
|
||||
// modified from https://forum.vellemanprojects.eu/t/k8090-how-to-identify-which-com-port-it-is-connected-to/3810
|
||||
// https://www.velleman.eu/images/tmp/usbfind.c
|
||||
|
||||
HDEVINFO hDevInfo;
|
||||
SP_DEVICE_INTERFACE_DATA DevIntfData;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
|
||||
SP_DEVINFO_DATA DevData;
|
||||
|
||||
DWORD dwSize, dwMemberIdx;
|
||||
|
||||
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
|
||||
if (hDevInfo != INVALID_HANDLE_VALUE) {
|
||||
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
dwMemberIdx = 0;
|
||||
|
||||
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE,
|
||||
dwMemberIdx, &DevIntfData);
|
||||
|
||||
while (GetLastError() != ERROR_NO_MORE_ITEMS) {
|
||||
DevData.cbSize = sizeof(DevData);
|
||||
|
||||
SetupDiGetDeviceInterfaceDetail(
|
||||
hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
|
||||
|
||||
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
|
||||
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
|
||||
DevIntfDetailData, dwSize, &dwSize, &DevData)) {
|
||||
if (strstr(DevIntfDetailData->DevicePath, VENDOR_PROD_ID) != 0) {
|
||||
return DevIntfDetailData;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
|
||||
|
||||
// continue looping
|
||||
SetupDiEnumDeviceInterfaces(
|
||||
hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::IsPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS) {
|
||||
|
||||
// logic high is pressed, logic low is released.
|
||||
return button_state & (1 << iKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::WasPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS)
|
||||
return prev_button_state & (1 << iKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetUSBPortState(bool bState) {
|
||||
SetLight(USB_ENABLE_BIT, bState);
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetLight(int iLight, bool bState) {
|
||||
if (iLight < PIUIO_MAX_NUM_OF_LIGHTS) {
|
||||
|
||||
// turn on a light by setting the bit, turn it off by clearing it.
|
||||
if (bState)
|
||||
light_data |= 1 << iLight;
|
||||
else
|
||||
light_data &= ~(1 << iLight);
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Open() {
|
||||
|
||||
// ensure the libs are loaded
|
||||
// if we can't load them, then we shouldn't exist.
|
||||
if (!LoadWinUSBFunctions())
|
||||
return false;
|
||||
|
||||
bool bResult = false;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail;
|
||||
|
||||
dev_detail = GetDevicePath();
|
||||
|
||||
if (dev_detail == 0) {
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
return false;
|
||||
}
|
||||
|
||||
device_handle = CreateFile(dev_detail->DevicePath,
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL
|
||||
);
|
||||
|
||||
// no longer need dev_detail
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
|
||||
if (device_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
if (pWinUsb_Initialize != nullptr)
|
||||
bResult = pWinUsb_Initialize(device_handle, &win_usb_handle);
|
||||
|
||||
if (!bResult) {
|
||||
CloseHandle(device_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Read() {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40 | 0x80; // EndPoint In, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
unsigned char buffer[PACKET_SIZE_BYTES] {};
|
||||
if (pWinUsb_ControlTransfer != nullptr)
|
||||
bResult = pWinUsb_ControlTransfer(win_usb_handle, SetupPacket, buffer, PACKET_SIZE_BYTES, &cbSent, 0);
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "read error");
|
||||
|
||||
return Deserialize(buffer);
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Write(uint32_t p_Data) {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40; //EndPoint Out, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
if (pWinUsb_ControlTransfer != nullptr) {
|
||||
bResult = pWinUsb_ControlTransfer(
|
||||
win_usb_handle,
|
||||
SetupPacket,
|
||||
Serialize(light_data),
|
||||
PACKET_SIZE_BYTES,
|
||||
&cbSent,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "write error");
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
unsigned char * rawinput::PIUIO::Serialize(uint32_t p_Data) {
|
||||
static unsigned char buffer[PACKET_SIZE_BYTES];
|
||||
|
||||
buffer[0] = p_Data;
|
||||
buffer[1] = p_Data >> 8;
|
||||
buffer[2] = p_Data >> 16;
|
||||
buffer[3] = p_Data >> 24;
|
||||
|
||||
// these bits don't matter for the piuio
|
||||
buffer[4] = 0xFF;
|
||||
buffer[5] = 0xFF;
|
||||
buffer[6] = 0xFF;
|
||||
buffer[7] = 0xFF;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Deserialize(unsigned char * p_Data) {
|
||||
uint32_t result;
|
||||
|
||||
result = p_Data[0];
|
||||
result |= p_Data[1] << 8;
|
||||
result |= p_Data[2] << 16;
|
||||
result |= p_Data[3] << 24;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::IOThread() {
|
||||
while (is_connected)
|
||||
UpdateSingleButtonState();
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::UpdateSingleButtonState() {
|
||||
|
||||
// write a set of sensors to request
|
||||
light_data &= 0xFFFCFFFC;
|
||||
light_data |= (current_sensor | (current_sensor << 16));
|
||||
|
||||
// request this set of sensors
|
||||
// this also updates the light status
|
||||
this->Write(light_data);
|
||||
|
||||
// read from this set of sensors
|
||||
input_data[current_sensor] = this->Read();
|
||||
|
||||
// piuio opens high; invert the input
|
||||
input_data[current_sensor] = ~input_data[current_sensor];
|
||||
|
||||
// update high values ASAP, that's when the button is pressed.
|
||||
// see: https://github.com/djpohly/piuio#usb-protocol-and-multiplexer
|
||||
auto button_state_new = button_state | input_data[current_sensor];
|
||||
if (button_state != button_state_new) {
|
||||
this->device->mutex->lock();
|
||||
button_state |= button_state_new;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
|
||||
if (current_sensor >= SENSOR_NUM - 1) {
|
||||
uint32_t temp_btn_state = 0;
|
||||
|
||||
// we have done a full sensor arrangement, let's see if the panel has been pulled away from.
|
||||
// combine all the input.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
temp_btn_state |= input_data[i];
|
||||
|
||||
// only store every 255th previous value since we are polling very quickly.
|
||||
// only used by spicecfg to see if a user pressed a button, so not very important.
|
||||
// unsigned ints rollover
|
||||
prev_timeout++;
|
||||
if (prev_timeout == 0)
|
||||
prev_button_state = button_state;
|
||||
|
||||
if (button_state != temp_btn_state) {
|
||||
this->device->mutex->lock();
|
||||
button_state = temp_btn_state;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
current_sensor = 0;
|
||||
} else {
|
||||
current_sensor++;
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Init() {
|
||||
is_connected = Open();
|
||||
|
||||
if (is_connected) {
|
||||
|
||||
// force the USB lines to be always on.
|
||||
SetUSBPortState(true);
|
||||
|
||||
IOThreadObject = new std::thread(&rawinput::PIUIO::IOThread, this);
|
||||
}
|
||||
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Stop() {
|
||||
is_connected = false;
|
||||
|
||||
if (IOThreadObject != NULL) {
|
||||
IOThreadObject->join();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rawinput::PIUIO::~PIUIO() {
|
||||
Stop();
|
||||
}
|
||||
Reference in New Issue
Block a user