IOS/USB: Add "Connect USB Keyboard" support for HID
This commit is contained in:
parent
074add14ac
commit
49fc7e78cc
|
@ -69,13 +69,16 @@ enum
|
||||||
}
|
}
|
||||||
using HIDPressedKeys = std::array<u8, 6>;
|
using HIDPressedKeys = std::array<u8, 6>;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
struct HIDPressedState
|
struct HIDPressedState
|
||||||
{
|
{
|
||||||
u8 modifiers = 0;
|
u8 modifiers = 0;
|
||||||
|
u8 oem = 0;
|
||||||
HIDPressedKeys pressed_keys{};
|
HIDPressedKeys pressed_keys{};
|
||||||
|
|
||||||
auto operator<=>(const HIDPressedState&) const = default;
|
auto operator<=>(const HIDPressedState&) const = default;
|
||||||
};
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
class KeyboardContext
|
class KeyboardContext
|
||||||
{
|
{
|
||||||
|
|
|
@ -427,6 +427,8 @@ add_library(core
|
||||||
IOS/USB/Bluetooth/WiimoteHIDAttr.h
|
IOS/USB/Bluetooth/WiimoteHIDAttr.h
|
||||||
IOS/USB/Common.cpp
|
IOS/USB/Common.cpp
|
||||||
IOS/USB/Common.h
|
IOS/USB/Common.h
|
||||||
|
IOS/USB/Emulated/HIDKeyboard.cpp
|
||||||
|
IOS/USB/Emulated/HIDKeyboard.h
|
||||||
IOS/USB/Emulated/Infinity.cpp
|
IOS/USB/Emulated/Infinity.cpp
|
||||||
IOS/USB/Emulated/Infinity.h
|
IOS/USB/Emulated/Infinity.h
|
||||||
IOS/USB/Emulated/Microphone.cpp
|
IOS/USB/Emulated/Microphone.cpp
|
||||||
|
|
|
@ -24,11 +24,34 @@ enum StandardDeviceRequestCodes
|
||||||
REQUEST_SET_INTERFACE = 11,
|
REQUEST_SET_INTERFACE = 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// See USB HID specification under "Class-Specific Requests":
|
||||||
|
// - https://www.usb.org/sites/default/files/documents/hid1_11.pdf
|
||||||
|
namespace HIDRequestCodes
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GET_REPORT = 1,
|
||||||
|
GET_IDLE = 2,
|
||||||
|
GET_PROTOCOL = 3,
|
||||||
|
// 0x04~0x08 - Reserved
|
||||||
|
SET_REPORT = 9,
|
||||||
|
SET_IDLE = 10,
|
||||||
|
SET_PROTOCOL = 11,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class HIDProtocol : u16
|
||||||
|
{
|
||||||
|
Boot = 0,
|
||||||
|
Report = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum ControlRequestTypes
|
enum ControlRequestTypes
|
||||||
{
|
{
|
||||||
DIR_HOST2DEVICE = 0,
|
DIR_HOST2DEVICE = 0,
|
||||||
DIR_DEVICE2HOST = 1,
|
DIR_DEVICE2HOST = 1,
|
||||||
TYPE_STANDARD = 0,
|
TYPE_STANDARD = 0,
|
||||||
|
TYPE_CLASS = 1,
|
||||||
TYPE_VENDOR = 2,
|
TYPE_VENDOR = 2,
|
||||||
REC_DEVICE = 0,
|
REC_DEVICE = 0,
|
||||||
REC_INTERFACE = 1,
|
REC_INTERFACE = 1,
|
||||||
|
|
|
@ -0,0 +1,316 @@
|
||||||
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "Core/IOS/USB/Emulated/HIDKeyboard.h"
|
||||||
|
|
||||||
|
#include "Core/Config/MainSettings.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
#include "InputCommon/ControlReference/ControlReference.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE::USB
|
||||||
|
{
|
||||||
|
HIDKeyboard::HIDKeyboard()
|
||||||
|
{
|
||||||
|
m_id = u64(m_vid) << 32 | u64(m_pid) << 16 | u64(8) << 8 | u64(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HIDKeyboard::~HIDKeyboard()
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return;
|
||||||
|
CancelPendingTransfers();
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceDescriptor HIDKeyboard::GetDeviceDescriptor() const
|
||||||
|
{
|
||||||
|
return m_device_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ConfigDescriptor> HIDKeyboard::GetConfigurations() const
|
||||||
|
{
|
||||||
|
return m_config_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<InterfaceDescriptor> HIDKeyboard::GetInterfaces(u8 config) const
|
||||||
|
{
|
||||||
|
return m_interface_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EndpointDescriptor> HIDKeyboard::GetEndpoints(u8 config, u8 interface, u8 alt) const
|
||||||
|
{
|
||||||
|
if (const auto it{m_endpoint_descriptor.find(interface)}; it != m_endpoint_descriptor.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::Attach()
|
||||||
|
{
|
||||||
|
if (m_device_attached)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
INFO_LOG_FMT(IOS_USB, "[{:04x}:{:04x}] Opening emulated keyboard", m_vid, m_pid);
|
||||||
|
m_keyboard_context = Common::KeyboardContext::GetInstance();
|
||||||
|
m_worker.Reset("HID Keyboard", [this](auto transfer) { HandlePendingTransfer(transfer); });
|
||||||
|
m_device_attached = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::AttachAndChangeInterface(const u8 interface)
|
||||||
|
{
|
||||||
|
if (!Attach())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (interface != m_active_interface)
|
||||||
|
return ChangeInterface(interface) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::CancelTransfer(const u8 endpoint)
|
||||||
|
{
|
||||||
|
if (endpoint != KEYBOARD_ENDPOINT)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(
|
||||||
|
IOS_USB,
|
||||||
|
"[{:04x}:{:04x} {}] Cancelling transfers for invalid endpoint {:#x} (expected: {:#x})",
|
||||||
|
m_vid, m_pid, m_active_interface, endpoint, KEYBOARD_ENDPOINT);
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Cancelling transfers (endpoint {:#x})", m_vid, m_pid,
|
||||||
|
m_active_interface, endpoint);
|
||||||
|
CancelPendingTransfers();
|
||||||
|
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::ChangeInterface(const u8 interface)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Changing interface to {}", m_vid, m_pid,
|
||||||
|
m_active_interface, interface);
|
||||||
|
m_active_interface = interface;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::GetNumberOfAltSettings(u8 interface)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::SetAltSetting(u8 alt_setting)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(IOS_USB,
|
||||||
|
"[{:04x}:{:04x} {}] Control: bRequestType={:02x} bRequest={:02x} wValue={:04x}"
|
||||||
|
" wIndex={:04x} wLength={:04x}",
|
||||||
|
m_vid, m_pid, m_active_interface, cmd->request_type, cmd->request, cmd->value,
|
||||||
|
cmd->index, cmd->length);
|
||||||
|
|
||||||
|
auto& ios = cmd->GetEmulationKernel();
|
||||||
|
|
||||||
|
switch (cmd->request_type << 8 | cmd->request)
|
||||||
|
{
|
||||||
|
case USBHDR(DIR_DEVICE2HOST, TYPE_STANDARD, REC_INTERFACE, REQUEST_GET_INTERFACE):
|
||||||
|
{
|
||||||
|
constexpr u8 data{1};
|
||||||
|
cmd->FillBuffer(&data, sizeof(data));
|
||||||
|
cmd->ScheduleTransferCompletion(1, 100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_INTERFACE, REQUEST_SET_INTERFACE):
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] REQUEST_SET_INTERFACE index={:04x} value={:04x}",
|
||||||
|
m_vid, m_pid, m_active_interface, cmd->index, cmd->value);
|
||||||
|
if (static_cast<u8>(cmd->index) != m_active_interface)
|
||||||
|
{
|
||||||
|
const int ret = ChangeInterface(static_cast<u8>(cmd->index));
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Failed to change interface to {}", m_vid, m_pid,
|
||||||
|
m_active_interface, cmd->index);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const int ret = SetAltSetting(static_cast<u8>(cmd->value));
|
||||||
|
if (ret == 0)
|
||||||
|
ios.EnqueueIPCReply(cmd->ios_request, cmd->length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_CLASS, REC_INTERFACE, HIDRequestCodes::SET_REPORT):
|
||||||
|
{
|
||||||
|
// According to the HID specification:
|
||||||
|
// - A device might choose to ignore input Set_Report requests as meaningless.
|
||||||
|
// - Alternatively these reports could be used to reset the origin of a control
|
||||||
|
// (that is, current position should report zero).
|
||||||
|
// - The effect of sent reports will also depend on whether the recipient controls
|
||||||
|
// are absolute or relative.
|
||||||
|
const u8 report_type = cmd->value >> 8;
|
||||||
|
const u8 report_id = cmd->value & 0xFF;
|
||||||
|
auto& memory = ios.GetSystem().GetMemory();
|
||||||
|
|
||||||
|
// The data seems to report LED status for keys such as:
|
||||||
|
// - NUM LOCK, CAPS LOCK
|
||||||
|
const u8* data = memory.GetPointerForRange(cmd->data_address, cmd->length);
|
||||||
|
INFO_LOG_FMT(IOS_USB, "SET_REPORT ignored (report_type={:02x}, report_id={:02x}, index={})\n{}",
|
||||||
|
report_type, report_id, cmd->index, HexDump(data, cmd->length));
|
||||||
|
ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_CLASS, REC_INTERFACE, HIDRequestCodes::SET_IDLE):
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(IOS_USB, "SET_IDLE not implemented (value={:04x}, index={})", cmd->value,
|
||||||
|
cmd->index);
|
||||||
|
// TODO: Handle idle duration and implement NAK
|
||||||
|
ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_CLASS, REC_INTERFACE, HIDRequestCodes::SET_PROTOCOL):
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(IOS_USB, "SET_PROTOCOL: value={}, index={}", cmd->value, cmd->index);
|
||||||
|
const HIDProtocol protocol = static_cast<HIDProtocol>(cmd->value);
|
||||||
|
switch (protocol)
|
||||||
|
{
|
||||||
|
case HIDProtocol::Boot:
|
||||||
|
case HIDProtocol::Report:
|
||||||
|
m_current_protocol = protocol;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(IOS_USB, "SET_PROTOCOL: Unknown protocol {} for interface {}", cmd->value,
|
||||||
|
cmd->index);
|
||||||
|
}
|
||||||
|
ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(IOS_USB, "Unknown command, req={:02x}, type={:02x}", cmd->request,
|
||||||
|
cmd->request_type);
|
||||||
|
ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::SubmitTransfer(std::unique_ptr<BulkMessage> cmd)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Bulk: length={:04x} endpoint={:02x}", m_vid, m_pid,
|
||||||
|
m_active_interface, cmd->length, cmd->endpoint);
|
||||||
|
cmd->GetEmulationKernel().EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::SubmitTransfer(std::unique_ptr<IntrMessage> cmd)
|
||||||
|
{
|
||||||
|
static auto start_time = std::chrono::steady_clock::now();
|
||||||
|
const auto current_time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
const bool should_poll =
|
||||||
|
(current_time - start_time) >= POLLING_RATE && ControlReference::GetInputGate();
|
||||||
|
const Common::HIDPressedState state =
|
||||||
|
should_poll ? m_keyboard_context->GetPressedState() : m_last_state;
|
||||||
|
|
||||||
|
// We can't use cmd->ScheduleTransferCompletion here as it might provoke
|
||||||
|
// invalid memory access with scheduled transfers when CancelTransfer is called.
|
||||||
|
EnqueueTransfer(std::move(cmd), state);
|
||||||
|
|
||||||
|
if (should_poll)
|
||||||
|
{
|
||||||
|
m_last_state = std::move(state);
|
||||||
|
start_time = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HIDKeyboard::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(IOS_USB,
|
||||||
|
"[{:04x}:{:04x} {}] Isochronous: length={:04x} endpoint={:02x} num_packets={:02x}",
|
||||||
|
m_vid, m_pid, m_active_interface, cmd->length, cmd->endpoint, cmd->num_packets);
|
||||||
|
cmd->ScheduleTransferCompletion(IPC_SUCCESS, 2000);
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIDKeyboard::EnqueueTransfer(std::unique_ptr<IntrMessage> msg,
|
||||||
|
const Common::HIDPressedState& state)
|
||||||
|
{
|
||||||
|
msg->FillBuffer(reinterpret_cast<const u8*>(&state), sizeof(state));
|
||||||
|
auto transfer = std::make_shared<PendingTransfer>(std::move(msg));
|
||||||
|
m_worker.EmplaceItem(transfer);
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_pending_lock);
|
||||||
|
m_pending_tranfers.insert(transfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIDKeyboard::HandlePendingTransfer(std::shared_ptr<PendingTransfer> transfer)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_pending_lock);
|
||||||
|
if (transfer->IsCanceled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (!transfer->IsReady())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
std::this_thread::sleep_for(POLLING_RATE / 2);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (transfer->IsCanceled())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer->Do();
|
||||||
|
m_pending_tranfers.erase(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIDKeyboard::CancelPendingTransfers()
|
||||||
|
{
|
||||||
|
m_worker.Cancel();
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_pending_lock);
|
||||||
|
for (auto& transfer : m_pending_tranfers)
|
||||||
|
transfer->Cancel();
|
||||||
|
m_pending_tranfers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HIDKeyboard::PendingTransfer::PendingTransfer(std::unique_ptr<IntrMessage> msg)
|
||||||
|
{
|
||||||
|
m_time = std::chrono::steady_clock::now();
|
||||||
|
m_msg = std::move(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
HIDKeyboard::PendingTransfer::~PendingTransfer()
|
||||||
|
{
|
||||||
|
if (!m_pending)
|
||||||
|
return;
|
||||||
|
// Value based on LibusbDevice's HandleTransfer implementation
|
||||||
|
m_msg->ScheduleTransferCompletion(-5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::PendingTransfer::IsReady() const
|
||||||
|
{
|
||||||
|
return (std::chrono::steady_clock::now() - m_time) >= POLLING_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HIDKeyboard::PendingTransfer::IsCanceled() const
|
||||||
|
{
|
||||||
|
return m_is_canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIDKeyboard::PendingTransfer::Do()
|
||||||
|
{
|
||||||
|
m_msg->ScheduleTransferCompletion(IPC_SUCCESS, 0);
|
||||||
|
m_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIDKeyboard::PendingTransfer::Cancel()
|
||||||
|
{
|
||||||
|
m_is_canceled = true;
|
||||||
|
}
|
||||||
|
} // namespace IOS::HLE::USB
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Keyboard.h"
|
||||||
|
#include "Common/WorkQueueThread.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE::USB
|
||||||
|
{
|
||||||
|
class HIDKeyboard final : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HIDKeyboard();
|
||||||
|
~HIDKeyboard() override;
|
||||||
|
|
||||||
|
DeviceDescriptor GetDeviceDescriptor() const override;
|
||||||
|
std::vector<ConfigDescriptor> GetConfigurations() const override;
|
||||||
|
std::vector<InterfaceDescriptor> GetInterfaces(u8 config) const override;
|
||||||
|
std::vector<EndpointDescriptor> GetEndpoints(u8 config, u8 interface, u8 alt) const override;
|
||||||
|
bool Attach() override;
|
||||||
|
bool AttachAndChangeInterface(u8 interface) override;
|
||||||
|
int CancelTransfer(u8 endpoint) override;
|
||||||
|
int ChangeInterface(u8 interface) override;
|
||||||
|
int GetNumberOfAltSettings(u8 interface) override;
|
||||||
|
int SetAltSetting(u8 alt_setting) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<CtrlMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<BulkMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<IntrMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<IsoMessage> message) override;
|
||||||
|
|
||||||
|
static constexpr auto POLLING_RATE = std::chrono::milliseconds(8); // 125 Hz
|
||||||
|
|
||||||
|
private:
|
||||||
|
class PendingTransfer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PendingTransfer(std::unique_ptr<IntrMessage> msg);
|
||||||
|
~PendingTransfer();
|
||||||
|
|
||||||
|
bool IsReady() const;
|
||||||
|
bool IsCanceled() const;
|
||||||
|
void Do();
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<IntrMessage> m_msg;
|
||||||
|
std::chrono::steady_clock::time_point m_time;
|
||||||
|
bool m_is_canceled = false;
|
||||||
|
bool m_pending = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
void EnqueueTransfer(std::unique_ptr<IntrMessage> msg, const Common::HIDPressedState& state);
|
||||||
|
void HandlePendingTransfer(std::shared_ptr<PendingTransfer> transfer);
|
||||||
|
void CancelPendingTransfers();
|
||||||
|
|
||||||
|
Common::WorkQueueThreadSP<std::shared_ptr<PendingTransfer>> m_worker;
|
||||||
|
std::mutex m_pending_lock;
|
||||||
|
std::set<std::shared_ptr<PendingTransfer>> m_pending_tranfers;
|
||||||
|
|
||||||
|
HIDProtocol m_current_protocol = HIDProtocol::Report;
|
||||||
|
Common::HIDPressedState m_last_state;
|
||||||
|
std::shared_ptr<Common::KeyboardContext> m_keyboard_context;
|
||||||
|
|
||||||
|
// Apple Extended Keyboard [Mitsumi]
|
||||||
|
// - Model A1058 / USB 1.1
|
||||||
|
const u16 m_vid = 0x05ac;
|
||||||
|
const u16 m_pid = 0x020c;
|
||||||
|
u8 m_active_interface = 0;
|
||||||
|
bool m_device_attached = false;
|
||||||
|
const DeviceDescriptor m_device_descriptor{0x12, 0x01, 0x0110, 0x00, 0x00, 0x00, 0x08,
|
||||||
|
0x05AC, 0x020C, 0x0395, 0x01, 0x03, 0x00, 0x01};
|
||||||
|
const std::vector<ConfigDescriptor> m_config_descriptor{
|
||||||
|
{0x09, 0x02, 0x003B, 0x02, 0x01, 0x00, 0xA0, 0x19}};
|
||||||
|
static constexpr u8 INTERFACE_0 = 0;
|
||||||
|
static constexpr u8 INTERFACE_1 = 1;
|
||||||
|
static constexpr u8 KEYBOARD_ENDPOINT = 0x81;
|
||||||
|
static constexpr u8 HUB_ENDPOINT = 0x82;
|
||||||
|
const std::vector<InterfaceDescriptor> m_interface_descriptor{
|
||||||
|
{0x09, 0x04, INTERFACE_0, 0x00, 0x01, 0x03, 0x01, 0x01, 0x00},
|
||||||
|
{0x09, 0x04, INTERFACE_1, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00}};
|
||||||
|
const std::map<u8, std::vector<EndpointDescriptor>> m_endpoint_descriptor{
|
||||||
|
{INTERFACE_0, {{0x07, 0x05, KEYBOARD_ENDPOINT, 0x03, 0x0008, 0x0A}}},
|
||||||
|
{INTERFACE_1, {{0x07, 0x05, HUB_ENDPOINT, 0x03, 0x0004, 0x0A}}},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} // namespace IOS::HLE::USB
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/IOS/USB/Common.h"
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
#include "Core/IOS/USB/Emulated/HIDKeyboard.h"
|
||||||
#include "Core/IOS/USB/Emulated/Infinity.h"
|
#include "Core/IOS/USB/Emulated/Infinity.h"
|
||||||
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
|
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
|
||||||
#include "Core/IOS/USB/Emulated/WiiSpeak.h"
|
#include "Core/IOS/USB/Emulated/WiiSpeak.h"
|
||||||
|
@ -187,6 +188,11 @@ void USBScanner::AddEmulatedDevices(DeviceMap* new_devices)
|
||||||
auto wii_speak = std::make_unique<USB::WiiSpeak>();
|
auto wii_speak = std::make_unique<USB::WiiSpeak>();
|
||||||
AddDevice(std::move(wii_speak), new_devices);
|
AddDevice(std::move(wii_speak), new_devices);
|
||||||
}
|
}
|
||||||
|
if (Config::Get(Config::MAIN_WII_KEYBOARD) && !NetPlay::IsNetPlayRunning())
|
||||||
|
{
|
||||||
|
auto keyboard = std::make_unique<USB::HIDKeyboard>();
|
||||||
|
AddDevice(std::move(keyboard), new_devices);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void USBScanner::WakeupSantrollerDevice(libusb_device* device)
|
void USBScanner::WakeupSantrollerDevice(libusb_device* device)
|
||||||
|
|
|
@ -407,6 +407,7 @@
|
||||||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteDevice.h" />
|
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteDevice.h" />
|
||||||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.h" />
|
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.h" />
|
||||||
<ClInclude Include="Core\IOS\USB\Common.h" />
|
<ClInclude Include="Core\IOS\USB\Common.h" />
|
||||||
|
<ClInclude Include="Core\IOS\USB\Emulated\HIDKeyboard.h" />
|
||||||
<ClInclude Include="Core\IOS\USB\Emulated\Infinity.h" />
|
<ClInclude Include="Core\IOS\USB\Emulated\Infinity.h" />
|
||||||
<ClInclude Include="Core\IOS\USB\Emulated\Microphone.h" />
|
<ClInclude Include="Core\IOS\USB\Emulated\Microphone.h" />
|
||||||
<ClInclude Include="Core\IOS\USB\Emulated\Skylanders\Skylander.h" />
|
<ClInclude Include="Core\IOS\USB\Emulated\Skylanders\Skylander.h" />
|
||||||
|
@ -1078,6 +1079,7 @@
|
||||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteDevice.cpp" />
|
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteDevice.cpp" />
|
||||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
||||||
<ClCompile Include="Core\IOS\USB\Common.cpp" />
|
<ClCompile Include="Core\IOS\USB\Common.cpp" />
|
||||||
|
<ClCompile Include="Core\IOS\USB\Emulated\HIDKeyboard.cpp" />
|
||||||
<ClCompile Include="Core\IOS\USB\Emulated\Infinity.cpp" />
|
<ClCompile Include="Core\IOS\USB\Emulated\Infinity.cpp" />
|
||||||
<ClCompile Include="Core\IOS\USB\Emulated\Microphone.cpp" />
|
<ClCompile Include="Core\IOS\USB\Emulated\Microphone.cpp" />
|
||||||
<ClCompile Include="Core\IOS\USB\Emulated\Skylanders\Skylander.cpp" />
|
<ClCompile Include="Core\IOS\USB\Emulated\Skylanders\Skylander.cpp" />
|
||||||
|
|
Loading…
Reference in New Issue