wip: implement portalIO
TODO : Fix the write IO timing issue
This commit is contained in:
parent
ea75a1d07e
commit
2360897978
3 changed files with 212 additions and 16 deletions
|
@ -21,6 +21,5 @@
|
|||
"LoadingPhase": "PostDefault"
|
||||
}
|
||||
],
|
||||
"IsExperimentalVersion": false,
|
||||
"Sealed": true
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
// A lot of this code is made because of the work of https://github.com/capull0/SkyDumper
|
||||
// A lot of this code is made because of the work of https://github.com/capull0/SkyDumper and https://github.com/silicontrip/SkyReader
|
||||
|
||||
#include "SkyPortalSubsystem.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogHIDApi);
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogSkyportalIO);
|
||||
|
||||
void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
|
||||
Super::Initialize(Collection);
|
||||
// Custom initialization logic
|
||||
UE_LOG(LogTemp, Warning, TEXT("SkyPortalSubsystem Initialized"));
|
||||
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
|
||||
|
||||
// Initialize HIDAPI
|
||||
int res = hid_init();
|
||||
|
@ -38,7 +38,7 @@ void USkyPortalSubsystem::Deinitialize()
|
|||
hid_exit();
|
||||
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("SkyPortalSubsystem Deinitialized"));
|
||||
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Deinitialized"));
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ bool USkyPortalSubsystem::ConnectPortal() {
|
|||
|
||||
// Free the device list
|
||||
hid_free_enumeration(list);
|
||||
bPortalConnected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -108,13 +109,179 @@ bool USkyPortalSubsystem::ConnectPortal() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void USkyPortalSubsystem::ChangePortalColor(const FLinearColor& Color)
|
||||
{
|
||||
|
||||
unsigned char r = Color.R;
|
||||
unsigned char g = Color.G;
|
||||
unsigned char b = Color.B;
|
||||
|
||||
void USkyPortalSubsystem::ReadPortal() {
|
||||
if (PortalDevice) {
|
||||
hid_read(PortalDevice, PortalRawData, 16);
|
||||
//RawDataArray.Append(PortalRawData, 16);
|
||||
}
|
||||
RWBlock req;
|
||||
|
||||
memset(req.buf, 0, rw_buf_size);
|
||||
req.buf[1] = 'C';
|
||||
req.buf[2] = r; // R
|
||||
req.buf[3] = g; // G
|
||||
req.buf[4] = b; // B
|
||||
|
||||
// no response for this one.
|
||||
Write(&req);
|
||||
}
|
||||
|
||||
void USkyPortalSubsystem::Write(RWBlock* pb) {
|
||||
// TODO: Need to make this function async
|
||||
if (!ensure(PortalDevice)) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("No Portal found"));
|
||||
return;
|
||||
}
|
||||
|
||||
pb->buf[0] = 0; // Use report 0
|
||||
|
||||
/*
|
||||
for (int attempt = 0; attempt < 10; attempt++) {
|
||||
if (hid_write(PortalDevice, pb->buf, 0x21) == -1) {
|
||||
Sleep(100);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
ensureMsgf(hid_write(PortalDevice, pb->buf, 0x21) != -1, TEXT("Unable to write to Portal, %s"), hid_error(PortalDevice));
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Writed"));
|
||||
}
|
||||
|
||||
|
||||
void USkyPortalSubsystem::Sleep(int sleepMs) {
|
||||
FPlatformProcess::Sleep(sleepMs * 0.0001);
|
||||
}
|
||||
|
||||
|
||||
bool USkyPortalSubsystem::CheckResponse(RWBlock* res, char expect)
|
||||
{
|
||||
if (!PortalDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int b = hid_read_timeout(PortalDevice, res->buf, rw_buf_size, TIMEOUT);
|
||||
|
||||
if (b < 0) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("Unable to read Skylander from Portal.\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
res->BytesTransferred = b;
|
||||
|
||||
/* this is here to debug the different responses from the portal.
|
||||
#if DEBUG
|
||||
SkylanderIO* skio;
|
||||
skio = new SkylanderIO();
|
||||
printf("<<<\n");
|
||||
skio->fprinthex(stdout, res->buf, 0x21);
|
||||
delete skio;
|
||||
#endif
|
||||
*/
|
||||
|
||||
// found wireless USB but portal is not connected
|
||||
if (res->buf[0] == 'Z')
|
||||
{
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("found wireless USB but portal is not connected"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status says no skylander on portal
|
||||
if (res->buf[0] == 'Q' && res->buf[1] == 0) {
|
||||
UE_LOG(LogSkyportalIO, Warning, TEXT("Status says no skylander on portal"));
|
||||
}
|
||||
|
||||
return (res->buf[0] != expect);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool USkyPortalSubsystem::WriteBlock(unsigned int block, unsigned char data[0x10], int skylander) {
|
||||
RWBlock req, res; //request and response buffer
|
||||
unsigned char verify[0x10]; // A 16-byte array used to verify the data that was written to the portal.
|
||||
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("Trying to write the current block :%X\n"), block);
|
||||
|
||||
// Trying to write 3 times
|
||||
for (int retries = 0; retries < 3; retries++) {
|
||||
// Write request
|
||||
// W 57 10 <block number> <0x10 bytes of data>
|
||||
memset(req.buf, 0, rw_buf_size);//Reset request buffer
|
||||
req.buf[1] = 'W';
|
||||
req.buf[2] = 0x10 + skylander;
|
||||
req.buf[3] = (unsigned char)block;
|
||||
memcpy(req.buf + 4, data, 0x10);
|
||||
|
||||
do { Write(&req); } while (CheckResponse(&res, 'W'));
|
||||
|
||||
Sleep(100); //Wait 0.1 seconds for write to take effect
|
||||
|
||||
memset(verify, 0xCD, sizeof(verify)); // 0xCD is a placeholder value
|
||||
ReadBlock(block, verify, skylander);
|
||||
|
||||
if (memcmp(data, verify, sizeof(verify))) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("verification of the written block failed"));
|
||||
continue; //retry
|
||||
}
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("block successfully written"));
|
||||
return true;
|
||||
}
|
||||
UE_LOG(LogSkyportalIO, Fatal, TEXT("failed to write block"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool USkyPortalSubsystem::ReadBlock(unsigned int block, unsigned char data[0x10], int skylander) {
|
||||
RWBlock req, res; //request and response buffers
|
||||
unsigned char followup;
|
||||
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("PortalIO:ReadBlock :%X"), block);
|
||||
|
||||
|
||||
// Checking if the block is not out of range
|
||||
if (!ensure(block < 0x40)) {
|
||||
UE_LOG(LogSkyportalIO, Error, TEXT("PortalIO:ReadBlock failed, block out of range"));
|
||||
return false; // Early return instead of throwing an exception
|
||||
}
|
||||
|
||||
// Send query request
|
||||
|
||||
// Trying to read data 15x
|
||||
for (int attempt = 0; attempt < 15; attempt++)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
memset(req.buf, 0, rw_buf_size); // Clear the request buffer (initialize all bytes to zero)
|
||||
req.buf[1] = 'Q';
|
||||
followup = 0x10 + skylander;
|
||||
req.buf[2] = followup;
|
||||
if (block == 0) {
|
||||
req.buf[2] = followup + 0x10; // may not be needed
|
||||
}
|
||||
req.buf[3] = (unsigned char)block;
|
||||
|
||||
memset(&(res.buf), 0, rw_buf_size); // Clear the response buffer to prepare for incoming data
|
||||
|
||||
|
||||
do { Write(&req); } while (CheckResponse(&res, 'Q'));
|
||||
|
||||
if (res.buf[0] == 'Q' && res.buf[2] == (unsigned char)block) {
|
||||
// Got our query back
|
||||
if (res.buf[1] == followup) {
|
||||
// got the query back with no error
|
||||
memcpy(data, res.buf + 3, 0x10);
|
||||
UE_LOG(LogSkyportalIO, Verbose, TEXT("PortalIO:ReadBlock success"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // retries
|
||||
|
||||
UE_LOG(LogSkyportalIO, Fatal, TEXT("PortalIO:ReadBlock failed after retries"));
|
||||
ensureMsgf(false, TEXT("PortalIO: Failed to read block after multiple retries"));
|
||||
return false;
|
||||
}
|
|
@ -9,8 +9,12 @@
|
|||
|
||||
|
||||
/* Macro Definitions */
|
||||
#define rw_buf_size 0x21
|
||||
#define TIMEOUT 30000
|
||||
#define DEBUG true
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
|
||||
|
||||
/* Subsystem */
|
||||
UCLASS()
|
||||
|
@ -24,19 +28,43 @@ public:
|
|||
virtual void Deinitialize() override;
|
||||
|
||||
// Connect to Portal, return false if portal is not found
|
||||
UFUNCTION(BlueprintCallable, CallInEditor)
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal"))
|
||||
bool ConnectPortal();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
bool bPortalConnected = false;
|
||||
|
||||
FString HidError;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "Color", Category = "SkyPortal"))
|
||||
void ChangePortalColor(const FLinearColor& Color = FLinearColor::Green);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
//Portal ref used in the subsystem
|
||||
hid_device* PortalDevice;
|
||||
|
||||
unsigned char* PortalRawData;
|
||||
typedef struct {
|
||||
unsigned char buf[rw_buf_size]; int BytesTransferred;
|
||||
} RWBlock;
|
||||
|
||||
|
||||
static void Sleep(int sleepMs);
|
||||
|
||||
|
||||
// Block/byte related data write/read functions
|
||||
bool ReadBlock(unsigned int block, unsigned char data[0x10], int skylander);
|
||||
bool WriteBlock(unsigned int, unsigned char[0x10], int);
|
||||
bool CheckResponse(RWBlock* res, char expect);
|
||||
void Write(RWBlock* pb);
|
||||
|
||||
FString RawData();
|
||||
void ReadPortal();
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -44,8 +72,10 @@ protected:
|
|||
const int VendorIds[4] = { 0x12ba, 0x54c, 0x1430, 0x1430 };
|
||||
const int ProductIds[4] = { 0x150, 0x967, 0x1f17 };
|
||||
|
||||
|
||||
|
||||
/////Defaults values, should not be used
|
||||
const int VendorId = 5168;
|
||||
const int ProductId = 336;
|
||||
//const int VendorId = 5168;
|
||||
//const int ProductId = 336;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue