WII_IPC_HLE/57e_305: Fake CommandReadBufferSize replies

Apparently, Nintendo's Bluetooth stack expects the ACL packet buffer
size to be limited to 10.

Reporting anything higher than that could cause memory corruption,
which can result in warning messages in the logs ("event mismatch"),
and more annoyingly, random disconnects.

Thanks to shuffle2 for the fix!
This commit is contained in:
Léo Lam 2016-09-11 12:34:14 +02:00
parent 4b47997cf8
commit 419a9c55e4
5 changed files with 52 additions and 9 deletions

View file

@ -28,6 +28,11 @@ public:
virtual void TriggerSyncButtonPressedEvent() {}
virtual void TriggerSyncButtonHeldEvent() {}
protected:
static constexpr int ACL_PKT_SIZE = 339;
static constexpr int ACL_PKT_NUM = 10;
static constexpr int SCO_PKT_SIZE = 64;
static constexpr int SCO_PKT_NUM = 0;
enum USBIOCtl
{
USBV0_IOCTL_CTRLMSG = 0,

View file

@ -521,7 +521,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305_emu::ACLPool::Store(const u8* data, con
return;
}
_dbg_assert_msg_(WII_IPC_WIIMOTE, size < m_acl_pkt_size, "ACL packet too large for pool");
_dbg_assert_msg_(WII_IPC_WIIMOTE, size < ACL_PKT_SIZE, "ACL packet too large for pool");
m_queue.push_back(Packet());
auto& packet = m_queue.back();
@ -1824,13 +1824,13 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305_emu::CommandReadBufferSize(const u8* in
{
hci_read_buffer_size_rp reply;
reply.status = 0x00;
reply.max_acl_size = m_acl_pkt_size;
reply.max_acl_size = ACL_PKT_SIZE;
// Due to how the widcomm stack which Nintendo uses is coded, we must never
// let the stack think the controller is buffering more than 10 data packets
// - it will cause a u8 underflow and royally screw things up.
reply.num_acl_pkts = m_acl_pkts_num;
reply.max_sco_size = 64;
reply.num_sco_pkts = 0;
reply.num_acl_pkts = ACL_PKT_NUM;
reply.max_sco_size = SCO_PKT_SIZE;
reply.num_sco_pkts = SCO_PKT_NUM;
INFO_LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_BUFFER_SIZE:");
DEBUG_LOG(WII_IPC_WIIMOTE, "return:");

View file

@ -95,14 +95,11 @@ private:
u32 m_ACLSetup;
CtrlBuffer m_ACLEndpoint;
static const int m_acl_pkt_size = 339;
static const int m_acl_pkts_num = 10;
class ACLPool
{
struct Packet
{
u8 data[m_acl_pkt_size];
u8 data[ACL_PKT_SIZE];
u16 size;
u16 conn_handle;
};

View file

@ -140,6 +140,12 @@ IPCCommandResult CWII_IPC_HLE_Device_usb_oh1_57e_305_real::IOCtlV(u32 command_ad
case USBV0_IOCTL_CTRLMSG:
{
auto cmd = std::make_unique<CtrlMessage>(cmd_buffer);
const u16 opcode = *reinterpret_cast<u16*>(Memory::GetPointer(cmd->payload_addr));
if (opcode == HCI_CMD_READ_BUFFER_SIZE)
{
m_fake_read_buffer_size_reply.Set();
return GetNoReply();
}
auto buffer = std::vector<u8>(cmd->length + LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(buffer.data(), cmd->request_type, cmd->request, cmd->value,
cmd->index, cmd->length);
@ -156,6 +162,11 @@ IPCCommandResult CWII_IPC_HLE_Device_usb_oh1_57e_305_real::IOCtlV(u32 command_ad
case USBV0_IOCTL_INTRMSG:
{
auto buffer = std::make_unique<CtrlBuffer>(cmd_buffer, command_address);
if (cmd_buffer.Parameter == USBV0_IOCTL_INTRMSG && m_fake_read_buffer_size_reply.TestAndClear())
{
FakeReadBufferSizeReply(*buffer);
return GetNoReply();
}
if (cmd_buffer.Parameter == USBV0_IOCTL_INTRMSG &&
m_sync_button_state == SyncButtonState::Pressed)
{
@ -241,6 +252,32 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305_real::SendHCIResetCommand()
INFO_LOG(WII_IPC_WIIMOTE, "Sent a reset command to adapter");
}
// Due to how the widcomm stack which Nintendo uses is coded, we must never
// let the stack think the controller is buffering more than 10 data packets
// - it will cause a u8 underflow and royally screw things up.
// Therefore, the reply to this command has to be faked to avoid random, weird issues
// (including Wiimote disconnects and "event mismatch" warning messages).
void CWII_IPC_HLE_Device_usb_oh1_57e_305_real::FakeReadBufferSizeReply(const CtrlBuffer& ctrl)
{
u8* packet = Memory::GetPointer(ctrl.m_payload_addr);
auto* hci_event = reinterpret_cast<SHCIEventCommand*>(packet);
hci_event->EventType = HCI_EVENT_COMMAND_COMPL;
hci_event->PayloadLength = sizeof(SHCIEventCommand) - 2 + sizeof(hci_read_buffer_size_rp);
hci_event->PacketIndicator = 0x01;
hci_event->Opcode = HCI_CMD_READ_BUFFER_SIZE;
hci_read_buffer_size_rp reply;
reply.status = 0x00;
reply.max_acl_size = ACL_PKT_SIZE;
reply.num_acl_pkts = ACL_PKT_NUM;
reply.max_sco_size = SCO_PKT_SIZE;
reply.num_sco_pkts = SCO_PKT_NUM;
memcpy(packet + sizeof(SHCIEventCommand), &reply, sizeof(hci_read_buffer_size_rp));
ctrl.SetRetVal(sizeof(SHCIEventCommand) + sizeof(hci_read_buffer_size_rp));
EnqueueReply(ctrl.m_cmd_address);
}
void CWII_IPC_HLE_Device_usb_oh1_57e_305_real::FakeSyncButtonEvent(const CtrlBuffer& ctrl,
const u8* payload, const u8 size)
{

View file

@ -64,7 +64,11 @@ private:
Common::Flag m_thread_running;
std::thread m_thread;
// Set when we received a command to read the buffer size, and we need to fake a reply
Common::Flag m_fake_read_buffer_size_reply;
void SendHCIResetCommand();
void FakeReadBufferSizeReply(const CtrlBuffer& ctrl);
void FakeSyncButtonEvent(const CtrlBuffer& ctrl, const u8* payload, u8 size);
void FakeSyncButtonPressedEvent(const CtrlBuffer& ctrl);
void FakeSyncButtonHeldEvent(const CtrlBuffer& ctrl);