SI: Clarify length fields for manual commands

Fix up the calculation of the length fields and check that the returned
response is the expected length. This touches many files because it
converts a parameter name from the SI_Device interface from 'length' to
'request_length'. Prior, this field seemed to be used as request length
sometimes, as response length sometimes, and usually just totally ignored.
This commit is contained in:
booto 2019-07-11 01:22:22 -04:00
parent d01220e69d
commit 62a2611925
17 changed files with 82 additions and 67 deletions

View File

@ -8,7 +8,9 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
#include <iomanip>
#include <memory> #include <memory>
#include <sstream>
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -280,31 +282,51 @@ static void GenerateSIInterrupt(SIInterruptType type)
UpdateInterrupts(); UpdateInterrupts();
} }
constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;
// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]
constexpr u32 ConvertSILengthField(u32 field)
{
return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;
}
static void RunSIBuffer(u64 user_data, s64 cycles_late) static void RunSIBuffer(u64 user_data, s64 cycles_late)
{ {
if (s_com_csr.TSTART) if (s_com_csr.TSTART)
{ {
// Math in_length u32 request_length = ConvertSILengthField(s_com_csr.OUTLNGTH);
int in_length = s_com_csr.INLNGTH; u32 expected_response_length = ConvertSILengthField(s_com_csr.INLNGTH);
if (in_length == 0) std::vector<u8> request_copy(s_si_buffer.data(), s_si_buffer.data() + request_length);
in_length = 128;
else
in_length++;
// Math out_length
int out_length = s_com_csr.OUTLNGTH;
if (out_length == 0)
out_length = 128;
else
out_length++;
std::unique_ptr<ISIDevice>& device = s_channel[s_com_csr.CHANNEL].device; std::unique_ptr<ISIDevice>& device = s_channel[s_com_csr.CHANNEL].device;
int numOutput = device->RunBuffer(s_si_buffer.data(), in_length); u32 actual_response_length = device->RunBuffer(s_si_buffer.data(), request_length);
DEBUG_LOG(SERIALINTERFACE, "RunSIBuffer chan: %d inLen: %i outLen: %i processed: %i", DEBUG_LOG(SERIALINTERFACE,
s_com_csr.CHANNEL, in_length, out_length, numOutput); "RunSIBuffer chan: %d request_length: %u expected_response_length: %u "
"actual_response_length: %u",
s_com_csr.CHANNEL, request_length, expected_response_length, actual_response_length);
if (expected_response_length != actual_response_length)
{
std::stringstream ss;
for (u8 b : request_copy)
{
ss << std::hex << std::setw(2) << std::setfill('0') << (int)b << ' ';
}
ERROR_LOG(
SERIALINTERFACE,
"RunSIBuffer: expected_response_length(%u) != actual_response_length(%u): request: %s",
expected_response_length, actual_response_length, ss.str().c_str());
}
if (numOutput != 0) // TODO:
// 1) Wait a reasonable amount of time for the result to be available:
// request is N bytes, ends with a stop bit
// response in M bytes, ends with a stop bit
// processing controller-side takes K us (investigate?)
// each bit takes 4us ([3us low/1us high] for a 0, [1us low/3us high] for a 1)
// time until response is available is at least: K + ((N*8 + 1) + (M*8 + 1)) * 4 us
// 2) Investigate the timeout period for NOREP0
if (actual_response_length != 0)
{ {
s_com_csr.TSTART = 0; s_com_csr.TSTART = 0;
GenerateSIInterrupt(INT_TCINT); GenerateSIInterrupt(INT_TCINT);

View File

@ -37,15 +37,16 @@ SIDevices ISIDevice::GetDeviceType() const
return m_device_type; return m_device_type;
} }
int ISIDevice::RunBuffer(u8* buffer, int length) int ISIDevice::RunBuffer(u8* buffer, int request_length)
{ {
#ifdef _DEBUG #ifdef _DEBUG
DEBUG_LOG(SERIALINTERFACE, "Send Data Device(%i) - Length(%i) ", m_device_number, length); DEBUG_LOG(SERIALINTERFACE, "Send Data Device(%i) - Length(%i) ", m_device_number,
request_length);
std::string temp; std::string temp;
int num = 0; int num = 0;
while (num < length) while (num < request_length)
{ {
temp += StringFromFormat("0x%02x ", buffer[num]); temp += StringFromFormat("0x%02x ", buffer[num]);
num++; num++;

View File

@ -73,7 +73,7 @@ public:
SIDevices GetDeviceType() const; SIDevices GetDeviceType() const;
// Run the SI Buffer // Run the SI Buffer
virtual int RunBuffer(u8* buffer, int length); virtual int RunBuffer(u8* buffer, int request_length);
virtual int TransferInterval(); virtual int TransferInterval();
// Return true on new data // Return true on new data

View File

@ -17,24 +17,19 @@ CSIDevice_DanceMat::CSIDevice_DanceMat(SIDevices device, int device_number)
{ {
} }
int CSIDevice_DanceMat::RunBuffer(u8* buffer, int length) int CSIDevice_DanceMat::RunBuffer(u8* buffer, int request_length)
{ {
// Read the command // Read the command
EBufferCommands command = static_cast<EBufferCommands>(buffer[0]); EBufferCommands command = static_cast<EBufferCommands>(buffer[0]);
if (command == CMD_RESET) if (command == CMD_RESET)
{ {
ISIDevice::RunBuffer(buffer, length); ISIDevice::RunBuffer(buffer, request_length);
u32 id = Common::swap32(SI_DANCEMAT); u32 id = Common::swap32(SI_DANCEMAT);
std::memcpy(buffer, &id, sizeof(id)); std::memcpy(buffer, &id, sizeof(id));
return sizeof(id);
} }
else return CSIDevice_GCController::RunBuffer(buffer, request_length);
{
return CSIDevice_GCController::RunBuffer(buffer, length);
}
return length;
} }
u32 CSIDevice_DanceMat::MapPadStatus(const GCPadStatus& pad_status) u32 CSIDevice_DanceMat::MapPadStatus(const GCPadStatus& pad_status)

View File

@ -16,7 +16,7 @@ class CSIDevice_DanceMat : public CSIDevice_GCController
public: public:
CSIDevice_DanceMat(SIDevices device, int device_number); CSIDevice_DanceMat(SIDevices device, int device_number);
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
u32 MapPadStatus(const GCPadStatus& pad_status) override; u32 MapPadStatus(const GCPadStatus& pad_status) override;
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;
}; };

View File

@ -290,7 +290,7 @@ CSIDevice_GBA::CSIDevice_GBA(SIDevices device, int device_number) : ISIDevice(de
{ {
} }
int CSIDevice_GBA::RunBuffer(u8* buffer, int length) int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
{ {
switch (m_next_action) switch (m_next_action)
{ {

View File

@ -45,7 +45,7 @@ class CSIDevice_GBA : public ISIDevice
public: public:
CSIDevice_GBA(SIDevices device, int device_number); CSIDevice_GBA(SIDevices device, int device_number);
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
int TransferInterval() override; int TransferInterval() override;
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;
void SendCommand(u32 command, u8 poll) override; void SendCommand(u32 command, u8 poll) override;

View File

@ -49,7 +49,7 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus()
return pad_status; return pad_status;
} }
int CSIDevice_GCAdapter::RunBuffer(u8* buffer, int length) int CSIDevice_GCAdapter::RunBuffer(u8* buffer, int request_length)
{ {
if (!Core::WantsDeterminism()) if (!Core::WantsDeterminism())
{ {
@ -66,7 +66,7 @@ int CSIDevice_GCAdapter::RunBuffer(u8* buffer, int length)
return 4; return 4;
} }
} }
return CSIDevice_GCController::RunBuffer(buffer, length); return CSIDevice_GCController::RunBuffer(buffer, request_length);
} }
bool CSIDevice_GCAdapter::GetData(u32& hi, u32& low) bool CSIDevice_GCAdapter::GetData(u32& hi, u32& low)

View File

@ -16,7 +16,7 @@ public:
CSIDevice_GCAdapter(SIDevices device, int device_number); CSIDevice_GCAdapter(SIDevices device, int device_number);
GCPadStatus GetPadStatus() override; GCPadStatus GetPadStatus() override;
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;

View File

@ -36,17 +36,17 @@ CSIDevice_GCController::CSIDevice_GCController(SIDevices device, int device_numb
m_origin.substick_y = GCPadStatus::C_STICK_CENTER_Y; m_origin.substick_y = GCPadStatus::C_STICK_CENTER_Y;
} }
int CSIDevice_GCController::RunBuffer(u8* buffer, int length) int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
{ {
// For debug logging only // For debug logging only
ISIDevice::RunBuffer(buffer, length); ISIDevice::RunBuffer(buffer, request_length);
GCPadStatus pad_status = GetPadStatus(); GCPadStatus pad_status = GetPadStatus();
if (!pad_status.isConnected) if (!pad_status.isConnected)
{ {
u32 reply = Common::swap32(SI_ERROR_NO_RESPONSE); u32 reply = Common::swap32(SI_ERROR_NO_RESPONSE);
std::memcpy(buffer, &reply, sizeof(reply)); std::memcpy(buffer, &reply, sizeof(reply));
return 4; return sizeof(reply);
} }
// Read the command // Read the command
@ -60,21 +60,21 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int length)
{ {
u32 id = Common::swap32(SI_GC_CONTROLLER); u32 id = Common::swap32(SI_GC_CONTROLLER);
std::memcpy(buffer, &id, sizeof(id)); std::memcpy(buffer, &id, sizeof(id));
break; return sizeof(id);
} }
case CMD_DIRECT: case CMD_DIRECT:
{ {
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", length); INFO_LOG(SERIALINTERFACE, "PAD - Direct (Request length: %d)", request_length);
u32 high, low; u32 high, low;
GetData(high, low); GetData(high, low);
for (int i = 0; i < (length - 1) / 2; i++) for (int i = 0; i < 4; i++)
{ {
buffer[i + 0] = (high >> (24 - (i * 8))) & 0xff; buffer[i + 0] = (high >> (24 - (i * 8))) & 0xff;
buffer[i + 4] = (low >> (24 - (i * 8))) & 0xff; buffer[i + 4] = (low >> (24 - (i * 8))) & 0xff;
} }
return sizeof(high) + sizeof(low);
} }
break;
case CMD_ORIGIN: case CMD_ORIGIN:
{ {
@ -85,8 +85,8 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int length)
{ {
buffer[i] = *calibration++; buffer[i] = *calibration++;
} }
return sizeof(SOrigin);
} }
break;
// Recalibrate (FiRES: i am not 100 percent sure about this) // Recalibrate (FiRES: i am not 100 percent sure about this)
case CMD_RECALIBRATE: case CMD_RECALIBRATE:
@ -98,8 +98,8 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int length)
{ {
buffer[i] = *calibration++; buffer[i] = *calibration++;
} }
return sizeof(SOrigin);
} }
break;
// DEFAULT // DEFAULT
default: default:
@ -110,7 +110,7 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int length)
break; break;
} }
return length; return 0;
} }
void CSIDevice_GCController::HandleMoviePadStatus(GCPadStatus* pad_status) void CSIDevice_GCController::HandleMoviePadStatus(GCPadStatus* pad_status)

View File

@ -84,7 +84,7 @@ public:
CSIDevice_GCController(SIDevices device, int device_number); CSIDevice_GCController(SIDevices device, int device_number);
// Run the SI Buffer // Run the SI Buffer
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
// Return true on new data // Return true on new data
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;

View File

@ -20,10 +20,10 @@ CSIDevice_GCSteeringWheel::CSIDevice_GCSteeringWheel(SIDevices device, int devic
{ {
} }
int CSIDevice_GCSteeringWheel::RunBuffer(u8* buffer, int length) int CSIDevice_GCSteeringWheel::RunBuffer(u8* buffer, int request_length)
{ {
// For debug logging only // For debug logging only
ISIDevice::RunBuffer(buffer, length); ISIDevice::RunBuffer(buffer, request_length);
// Read the command // Read the command
EBufferCommands command = static_cast<EBufferCommands>(buffer[0]); EBufferCommands command = static_cast<EBufferCommands>(buffer[0]);
@ -36,14 +36,11 @@ int CSIDevice_GCSteeringWheel::RunBuffer(u8* buffer, int length)
{ {
u32 id = Common::swap32(SI_GC_STEERING); u32 id = Common::swap32(SI_GC_STEERING);
std::memcpy(buffer, &id, sizeof(id)); std::memcpy(buffer, &id, sizeof(id));
break; return sizeof(id);
}
} }
default: return CSIDevice_GCController::RunBuffer(buffer, request_length);
return CSIDevice_GCController::RunBuffer(buffer, length);
}
return length;
} }
bool CSIDevice_GCSteeringWheel::GetData(u32& hi, u32& low) bool CSIDevice_GCSteeringWheel::GetData(u32& hi, u32& low)

View File

@ -13,7 +13,7 @@ class CSIDevice_GCSteeringWheel : public CSIDevice_GCController
public: public:
CSIDevice_GCSteeringWheel(SIDevices device, int device_number); CSIDevice_GCSteeringWheel(SIDevices device, int device_number);
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;
void SendCommand(u32 command, u8 poll) override; void SendCommand(u32 command, u8 poll) override;

View File

@ -21,10 +21,10 @@ CSIDevice_Keyboard::CSIDevice_Keyboard(SIDevices device, int device_number)
{ {
} }
int CSIDevice_Keyboard::RunBuffer(u8* buffer, int length) int CSIDevice_Keyboard::RunBuffer(u8* buffer, int request_length)
{ {
// For debug logging only // For debug logging only
ISIDevice::RunBuffer(buffer, length); ISIDevice::RunBuffer(buffer, request_length);
// Read the command // Read the command
EBufferCommands command = static_cast<EBufferCommands>(buffer[0]); EBufferCommands command = static_cast<EBufferCommands>(buffer[0]);
@ -37,21 +37,21 @@ int CSIDevice_Keyboard::RunBuffer(u8* buffer, int length)
{ {
u32 id = Common::swap32(SI_GC_KEYBOARD); u32 id = Common::swap32(SI_GC_KEYBOARD);
std::memcpy(buffer, &id, sizeof(id)); std::memcpy(buffer, &id, sizeof(id));
break; return sizeof(id);
} }
case CMD_DIRECT: case CMD_DIRECT:
{ {
INFO_LOG(SERIALINTERFACE, "Keyboard - Direct (Length: %d)", length); INFO_LOG(SERIALINTERFACE, "Keyboard - Direct (Request Length: %d)", request_length);
u32 high, low; u32 high, low;
GetData(high, low); GetData(high, low);
for (int i = 0; i < (length - 1) / 2; i++) for (int i = 0; i < 4; i++)
{ {
buffer[i + 0] = (high >> (24 - (i * 8))) & 0xff; buffer[i + 0] = (high >> (24 - (i * 8))) & 0xff;
buffer[i + 4] = (low >> (24 - (i * 8))) & 0xff; buffer[i + 4] = (low >> (24 - (i * 8))) & 0xff;
} }
return sizeof(high) + sizeof(low);
} }
break;
default: default:
{ {
@ -60,7 +60,7 @@ int CSIDevice_Keyboard::RunBuffer(u8* buffer, int length)
break; break;
} }
return length; return 0;
} }
KeyboardStatus CSIDevice_Keyboard::GetKeyboardStatus() const KeyboardStatus CSIDevice_Keyboard::GetKeyboardStatus() const

View File

@ -18,7 +18,7 @@ public:
CSIDevice_Keyboard(SIDevices device, int device_number); CSIDevice_Keyboard(SIDevices device, int device_number);
// Run the SI Buffer // Run the SI Buffer
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
// Return true on new data // Return true on new data
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;

View File

@ -14,11 +14,11 @@ CSIDevice_Null::CSIDevice_Null(SIDevices device, int device_number)
{ {
} }
int CSIDevice_Null::RunBuffer(u8* buffer, int length) int CSIDevice_Null::RunBuffer(u8* buffer, int request_length)
{ {
u32 reply = Common::swap32(SI_ERROR_NO_RESPONSE); u32 reply = Common::swap32(SI_ERROR_NO_RESPONSE);
std::memcpy(buffer, &reply, sizeof(reply)); std::memcpy(buffer, &reply, sizeof(reply));
return 4; return sizeof(reply);
} }
bool CSIDevice_Null::GetData(u32& hi, u32& low) bool CSIDevice_Null::GetData(u32& hi, u32& low)

View File

@ -15,7 +15,7 @@ class CSIDevice_Null final : public ISIDevice
public: public:
CSIDevice_Null(SIDevices device, int device_number); CSIDevice_Null(SIDevices device, int device_number);
int RunBuffer(u8* buffer, int length) override; int RunBuffer(u8* buffer, int request_length) override;
bool GetData(u32& hi, u32& low) override; bool GetData(u32& hi, u32& low) override;
void SendCommand(u32 command, u8 poll) override; void SendCommand(u32 command, u8 poll) override;
}; };