From 69a13a12e29827c92f299d981ce8d96eeecb0a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 11 Nov 2016 01:33:52 +0100 Subject: [PATCH 01/10] Add USB passthrough setting and USBUtils This adds a USB passthrough setting to ConfigManager and everything needed for the UI to show and manage the whitelist properly. --- Source/Core/Core/ConfigManager.cpp | 42 ++++++++++++++ Source/Core/Core/ConfigManager.h | 8 +++ Source/Core/UICommon/CMakeLists.txt | 6 +- Source/Core/UICommon/UICommon.cpp | 3 + Source/Core/UICommon/UICommon.vcxproj | 4 ++ Source/Core/UICommon/USBUtils.cpp | 79 +++++++++++++++++++++++++++ Source/Core/UICommon/USBUtils.h | 20 +++++++ 7 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 Source/Core/UICommon/USBUtils.cpp create mode 100644 Source/Core/UICommon/USBUtils.h diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index edb2386bd4..0a8294acea 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -75,6 +75,7 @@ void SConfig::SaveSettings() SaveAnalyticsSettings(ini); SaveNetworkSettings(ini); SaveBluetoothPassthroughSettings(ini); + SaveUSBPassthroughSettings(ini); SaveSysconfSettings(ini); ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX)); @@ -341,6 +342,20 @@ void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini) section->Set("LinkKeys", m_bt_passthrough_link_keys); } +void SConfig::SaveUSBPassthroughSettings(IniFile& ini) +{ + IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough"); + + std::ostringstream oss; + for (const auto& device : m_usb_passthrough_devices) + oss << StringFromFormat("%04x:%04x", device.first, device.second) << ','; + std::string devices_string = oss.str(); + if (!devices_string.empty()) + devices_string.pop_back(); + + section->Set("Devices", devices_string); +} + void SConfig::SaveSysconfSettings(IniFile& ini) { IniFile::Section* section = ini.GetOrCreateSection("Sysconf"); @@ -398,6 +413,7 @@ void SConfig::LoadSettings() LoadNetworkSettings(ini); LoadAnalyticsSettings(ini); LoadBluetoothPassthroughSettings(ini); + LoadUSBPassthroughSettings(ini); LoadSysconfSettings(ini); } @@ -674,6 +690,27 @@ void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini) section->Get("LinkKeys", &m_bt_passthrough_link_keys, ""); } +void SConfig::LoadUSBPassthroughSettings(IniFile& ini) +{ + IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough"); + m_usb_passthrough_devices.clear(); + std::string devices_string; + std::vector pairs; + section->Get("Devices", &devices_string, ""); + SplitString(devices_string, ',', pairs); + for (const auto& pair : pairs) + { + const auto index = pair.find(':'); + if (index == std::string::npos) + continue; + + const u16 vid = static_cast(strtol(pair.substr(0, index).c_str(), nullptr, 16)); + const u16 pid = static_cast(strtol(pair.substr(index + 1).c_str(), nullptr, 16)); + if (vid && pid) + m_usb_passthrough_devices.emplace(vid, pid); + } +} + void SConfig::LoadSysconfSettings(IniFile& ini) { IniFile::Section* section = ini.GetOrCreateSection("Sysconf"); @@ -764,6 +801,11 @@ void SConfig::LoadDefaults() m_revision = 0; } +bool SConfig::IsUSBDeviceWhitelisted(const std::pair vid_pid) const +{ + return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end(); +} + const char* SConfig::GetDirectoryForRegion(DiscIO::Region region) { switch (region) diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index e91bb9b1ff..8ac1bc2ee4 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -5,7 +5,9 @@ #pragma once #include +#include #include +#include #include #include "Common/IniFile.h" @@ -151,6 +153,10 @@ struct SConfig : NonCopyable int m_bt_passthrough_vid = -1; std::string m_bt_passthrough_link_keys; + // USB passthrough settings + std::set> m_usb_passthrough_devices; + bool IsUSBDeviceWhitelisted(std::pair vid_pid) const; + // SYSCONF settings int m_sensor_bar_position = 0x01; int m_sensor_bar_sensitivity = 0x03; @@ -350,6 +356,7 @@ private: void SaveNetworkSettings(IniFile& ini); void SaveAnalyticsSettings(IniFile& ini); void SaveBluetoothPassthroughSettings(IniFile& ini); + void SaveUSBPassthroughSettings(IniFile& ini); void SaveSysconfSettings(IniFile& ini); void LoadGeneralSettings(IniFile& ini); @@ -364,6 +371,7 @@ private: void LoadNetworkSettings(IniFile& ini); void LoadAnalyticsSettings(IniFile& ini); void LoadBluetoothPassthroughSettings(IniFile& ini); + void LoadUSBPassthroughSettings(IniFile& ini); void LoadSysconfSettings(IniFile& ini); bool SetRegion(DiscIO::Region region, std::string* directory_name); diff --git a/Source/Core/UICommon/CMakeLists.txt b/Source/Core/UICommon/CMakeLists.txt index 65630b6b8e..6bfc4be9bd 100644 --- a/Source/Core/UICommon/CMakeLists.txt +++ b/Source/Core/UICommon/CMakeLists.txt @@ -1,6 +1,10 @@ set(SRCS Disassembler.cpp - UICommon.cpp) + UICommon.cpp + USBUtils.cpp) set(LIBS common) +if(LIBUSB_FOUND) + set(LIBS ${LIBS} ${LIBUSB_LIBRARIES}) +endif() add_dolphin_library(uicommon "${SRCS}" "${LIBS}") diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index ece3d2d2ee..8de73bd151 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -17,6 +17,7 @@ #include "InputCommon/GCAdapter.h" #include "UICommon/UICommon.h" +#include "UICommon/USBUtils.h" #include "VideoCommon/VideoBackendBase.h" @@ -29,6 +30,7 @@ void Init() VideoBackendBase::PopulateList(); WiimoteReal::LoadSettings(); GCAdapter::Init(); + USBUtils::Init(); VideoBackendBase::ActivateBackend(SConfig::GetInstance().m_strVideoBackend); SetEnableAlert(SConfig::GetInstance().bUsePanicHandlers); @@ -40,6 +42,7 @@ void Shutdown() WiimoteReal::Shutdown(); VideoBackendBase::ClearList(); SConfig::Shutdown(); + USBUtils::Shutdown(); LogManager::Shutdown(); } diff --git a/Source/Core/UICommon/UICommon.vcxproj b/Source/Core/UICommon/UICommon.vcxproj index 24f4a1cfc5..567ba5f6ff 100644 --- a/Source/Core/UICommon/UICommon.vcxproj +++ b/Source/Core/UICommon/UICommon.vcxproj @@ -45,10 +45,14 @@ + + 4200;%(DisableSpecificWarnings) + + diff --git a/Source/Core/UICommon/USBUtils.cpp b/Source/Core/UICommon/USBUtils.cpp new file mode 100644 index 0000000000..8f0ba21d52 --- /dev/null +++ b/Source/Core/UICommon/USBUtils.cpp @@ -0,0 +1,79 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#ifdef __LIBUSB__ +#include +#endif + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "UICommon/USBUtils.h" + +#ifdef __LIBUSB__ +static libusb_context* s_libusb_context; +#endif + +// Because opening and getting the device name from devices is slow, especially on Windows +// with usbdk, we cannot do that for every single device. We should however still show +// device names for known Wii peripherals. +static const std::map, std::string> s_wii_peripherals = {{ + {{0x046d, 0x0a03}, "Logitech Microphone"}, + {{0x057e, 0x0308}, "Wii Speak"}, + {{0x057e, 0x0309}, "Nintendo USB Microphone"}, + {{0x057e, 0x030a}, "Ubisoft Motion Tracking Camera"}, + {{0x0e6f, 0x0129}, "Disney Infinity Reader (Portal Device)"}, + {{0x1430, 0x0100}, "Tony Hawk Ride Skateboard"}, + {{0x1430, 0x0150}, "Skylanders Portal"}, + {{0x1bad, 0x0004}, "Harmonix Guitar Controller"}, + {{0x1bad, 0x3110}, "Rock Band 3 Mustang Guitar Dongle"}, + {{0x1bad, 0x3430}, "Rock Band Drum Set"}, + {{0x21a4, 0xac40}, "EA Active NFL"}, +}}; + +namespace USBUtils +{ +void Init() +{ +#ifdef __LIBUSB__ + libusb_init(&s_libusb_context); +#endif +} + +void Shutdown() +{ +#ifdef __LIBUSB__ + libusb_exit(s_libusb_context); +#endif +} + +std::map, std::string> GetInsertedDevices() +{ + std::map, std::string> devices; + +#ifdef __LIBUSB__ + if (!s_libusb_context) + return devices; + + libusb_device** list; + const ssize_t cnt = libusb_get_device_list(s_libusb_context, &list); + for (ssize_t i = 0; i < cnt; ++i) + { + libusb_device_descriptor descr; + libusb_get_device_descriptor(list[i], &descr); + const std::pair vid_pid{descr.idVendor, descr.idProduct}; + devices[vid_pid] = GetDeviceName(vid_pid); + } + libusb_free_device_list(list, 1); +#endif + + return devices; +} + +std::string GetDeviceName(const std::pair vid_pid) +{ + const auto iter = s_wii_peripherals.find(vid_pid); + const std::string device_name = iter == s_wii_peripherals.cend() ? "Unknown" : iter->second; + return StringFromFormat("%04x:%04x - %s", vid_pid.first, vid_pid.second, device_name.c_str()); +} +} // namespace USBUtils diff --git a/Source/Core/UICommon/USBUtils.h b/Source/Core/UICommon/USBUtils.h new file mode 100644 index 0000000000..6ae778e8a4 --- /dev/null +++ b/Source/Core/UICommon/USBUtils.h @@ -0,0 +1,20 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "Common/CommonTypes.h" + +namespace USBUtils +{ +void Init(); +void Shutdown(); + +std::map, std::string> GetInsertedDevices(); +std::string GetDeviceName(std::pair vid_pid); +} // namespace USBUtils From b7cc25535f065651703010f208248fb022ceacac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 31 Oct 2016 21:51:35 +0100 Subject: [PATCH 02/10] DolphinWX: Add USB passthrough settings Allows adding/removing devices from USB passthrough. --- Source/Core/DolphinWX/CMakeLists.txt | 1 + .../DolphinWX/Config/AddUSBDeviceDiag.cpp | 164 ++++++++++++++++++ .../Core/DolphinWX/Config/AddUSBDeviceDiag.h | 56 ++++++ .../Core/DolphinWX/Config/WiiConfigPane.cpp | 74 +++++++- Source/Core/DolphinWX/Config/WiiConfigPane.h | 13 +- Source/Core/DolphinWX/DolphinWX.vcxproj | 2 + .../Core/DolphinWX/DolphinWX.vcxproj.filters | 6 + 7 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp create mode 100644 Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 1d4b958b04..38ccd561e7 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -7,6 +7,7 @@ set(GUI_SRCS Cheats/CheatsWindow.cpp Cheats/CreateCodeDialog.cpp Cheats/GeckoCodeDiag.cpp + Config/AddUSBDeviceDiag.cpp Config/AdvancedConfigPane.cpp Config/AudioConfigPane.cpp Config/ConfigMain.cpp diff --git a/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp new file mode 100644 index 0000000000..c6f1154af5 --- /dev/null +++ b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp @@ -0,0 +1,164 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/StringUtil.h" +#include "Core/ConfigManager.h" +#include "DolphinWX/Config/AddUSBDeviceDiag.h" +#include "DolphinWX/WxUtils.h" +#include "UICommon/USBUtils.h" + +AddUSBDeviceDiag::AddUSBDeviceDiag(wxWindow* const parent) + : wxDialog(parent, wxID_ANY, _("Add New USB Device")) +{ + InitControls(); + + RefreshDeviceList(); + Bind(wxEVT_TIMER, &AddUSBDeviceDiag::OnRefreshDevicesTimer, this, + m_refresh_devices_timer.GetId()); + m_refresh_devices_timer.Start(DEVICE_REFRESH_INTERVAL_MS, wxTIMER_CONTINUOUS); + + auto* const btn_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); + btn_sizer->GetAffirmativeButton()->SetLabel(_("Add")); + Bind(wxEVT_BUTTON, &AddUSBDeviceDiag::OnSave, this, wxID_OK); + + auto* const sizer = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); + sizer->AddSpacer(FromDIP(10)); + sizer->Add(new wxStaticText(this, wxID_ANY, _("Enter USB device ID"), wxDefaultPosition, + wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), + 0, wxEXPAND | wxBOTTOM, FromDIP(10)); + sizer->Add(CreateManualControlsSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer->Add(new wxStaticText(this, wxID_ANY, _("or select a device"), wxDefaultPosition, + wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), + 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10)); + auto* const device_list_sizer = CreateDeviceListSizer(); + sizer->Add(device_list_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, space5); + sizer->SetItemMinSize(device_list_sizer, FromDIP(350), FromDIP(150)); + sizer->Add(btn_sizer, 0, wxEXPAND); + sizer->AddSpacer(space5); + + SetSizerAndFit(sizer); + Center(); +} + +void AddUSBDeviceDiag::InitControls() +{ + m_new_device_vid_ctrl = new wxTextCtrl(this, wxID_ANY); + m_new_device_pid_ctrl = new wxTextCtrl(this, wxID_ANY); + // i18n: VID means Vendor ID (in the context of a USB device) + m_new_device_vid_ctrl->SetHint(_("Device VID (e.g., 057e)")); + // i18n: PID means Product ID (in the context of a USB device), not Process ID + m_new_device_pid_ctrl->SetHint(_("Device PID (e.g., 0305)")); + + m_inserted_devices_listbox = new wxListBox(this, wxID_ANY); + m_inserted_devices_listbox->Bind(wxEVT_LISTBOX, &AddUSBDeviceDiag::OnDeviceSelection, this); + m_inserted_devices_listbox->Bind(wxEVT_LISTBOX_DCLICK, &AddUSBDeviceDiag::OnSave, this); +} + +void AddUSBDeviceDiag::RefreshDeviceList() +{ + const auto& current_devices = USBUtils::GetInsertedDevices(); + if (current_devices == m_shown_devices) + return; + + m_inserted_devices_listbox->Freeze(); + const auto selection_string = m_inserted_devices_listbox->GetStringSelection(); + m_inserted_devices_listbox->Clear(); + for (const auto& device : current_devices) + { + if (SConfig::GetInstance().IsUSBDeviceWhitelisted(device.first)) + continue; + m_inserted_devices_listbox->Append(device.second, new USBPassthroughDeviceEntry(device.first)); + } + if (!selection_string.empty()) + m_inserted_devices_listbox->SetStringSelection(selection_string); + m_inserted_devices_listbox->Thaw(); + + m_shown_devices = current_devices; +} + +wxSizer* AddUSBDeviceDiag::CreateManualControlsSizer() +{ + const int space5 = FromDIP(5); + auto* const sizer = new wxBoxSizer(wxHORIZONTAL); + + sizer->Add(m_new_device_vid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer->Add(m_new_device_pid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + + return sizer; +} + +wxSizer* AddUSBDeviceDiag::CreateDeviceListSizer() +{ + const int space5 = FromDIP(5); + auto* const sizer = new wxBoxSizer(wxVERTICAL); + + sizer->Add(m_inserted_devices_listbox, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + + return sizer; +} + +static bool IsValidUSBIDString(const std::string& string) +{ + if (string.empty() || string.length() > 4) + return false; + return std::all_of(string.begin(), string.end(), + [](const auto character) { return std::isxdigit(character) != 0; }); +} + +void AddUSBDeviceDiag::OnRefreshDevicesTimer(wxTimerEvent&) +{ + RefreshDeviceList(); +} + +void AddUSBDeviceDiag::OnDeviceSelection(wxCommandEvent&) +{ + const int index = m_inserted_devices_listbox->GetSelection(); + if (index == wxNOT_FOUND) + return; + auto* const entry = static_cast( + m_inserted_devices_listbox->GetClientObject(index)); + m_new_device_vid_ctrl->SetValue(StringFromFormat("%04x", entry->m_vid)); + m_new_device_pid_ctrl->SetValue(StringFromFormat("%04x", entry->m_pid)); +} + +void AddUSBDeviceDiag::OnSave(wxCommandEvent&) +{ + const std::string vid_string = StripSpaces(WxStrToStr(m_new_device_vid_ctrl->GetValue())); + const std::string pid_string = StripSpaces(WxStrToStr(m_new_device_pid_ctrl->GetValue())); + if (!IsValidUSBIDString(vid_string)) + { + // i18n: Here, VID means Vendor ID (for a USB device). + WxUtils::ShowErrorDialog(_("The entered VID is invalid.")); + return; + } + if (!IsValidUSBIDString(pid_string)) + { + // i18n: Here, PID means Product ID (for a USB device). + WxUtils::ShowErrorDialog(_("The entered PID is invalid.")); + return; + } + + const u16 vid = static_cast(std::stoul(vid_string, nullptr, 16)); + const u16 pid = static_cast(std::stoul(pid_string, nullptr, 16)); + + if (SConfig::GetInstance().IsUSBDeviceWhitelisted({vid, pid})) + { + WxUtils::ShowErrorDialog(_("This USB device is already whitelisted.")); + return; + } + + SConfig::GetInstance().m_usb_passthrough_devices.emplace(vid, pid); + AcceptAndClose(); +} diff --git a/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h new file mode 100644 index 0000000000..38b65c34db --- /dev/null +++ b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h @@ -0,0 +1,56 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include +#include +#include + +#include "Common/CommonTypes.h" + +class wxListBox; +class wxSizer; +class wxTextCtrl; + +class USBPassthroughDeviceEntry final : public wxClientData +{ +public: + explicit USBPassthroughDeviceEntry(const std::pair pair) + : m_vid(pair.first), m_pid(pair.second) + { + } + const u16 m_vid; + const u16 m_pid; +}; + +// This dialog is used to add a new USB device to the USB passthrough whitelist, +// either by selecting a connected USB device or by entering the PID/VID manually. +class AddUSBDeviceDiag final : public wxDialog +{ +public: + explicit AddUSBDeviceDiag(wxWindow* parent); + +private: + static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100; + + void InitControls(); + void RefreshDeviceList(); + wxSizer* CreateManualControlsSizer(); + wxSizer* CreateDeviceListSizer(); + + void OnRefreshDevicesTimer(wxTimerEvent&); + void OnDeviceSelection(wxCommandEvent&); + void OnSave(wxCommandEvent&); + + std::map, std::string> m_shown_devices; + wxTimer m_refresh_devices_timer{this}; + + wxTextCtrl* m_new_device_vid_ctrl; + wxTextCtrl* m_new_device_pid_ctrl; + wxListBox* m_inserted_devices_listbox; +}; diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp index a98778eb2d..0d2ede097d 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "DolphinWX/Config/WiiConfigPane.h" - +#include #include #include #include +#include #include #include #include @@ -14,9 +14,12 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/IOS/IPC.h" +#include "DolphinWX/Config/AddUSBDeviceDiag.h" +#include "DolphinWX/Config/WiiConfigPane.h" #include "DolphinWX/DolphinSlider.h" #include "DolphinWX/WxEventUtils.h" #include "DolphinWX/WxUtils.h" +#include "UICommon/USBUtils.h" WiiConfigPane::WiiConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) { @@ -52,6 +55,10 @@ void WiiConfigPane::InitializeGUI() new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_system_language_strings); m_sd_card_checkbox = new wxCheckBox(this, wxID_ANY, _("Insert SD Card")); m_connect_keyboard_checkbox = new wxCheckBox(this, wxID_ANY, _("Connect USB Keyboard")); + m_usb_passthrough_devices_listbox = + new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100)); + m_usb_passthrough_add_device_btn = new wxButton(this, wxID_ANY, _("Add...")); + m_usb_passthrough_rem_device_btn = new wxButton(this, wxID_ANY, _("Remove")); m_bt_sensor_bar_pos = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bt_sensor_bar_pos_strings); m_bt_sensor_bar_sens = new DolphinSlider(this, wxID_ANY, 0, 0, 4); @@ -79,6 +86,12 @@ void WiiConfigPane::InitializeGUI() misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + auto* const usb_passthrough_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + usb_passthrough_btn_sizer->AddStretchSpacer(); + usb_passthrough_btn_sizer->Add(m_usb_passthrough_add_device_btn, 0, + wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, space5); + usb_passthrough_btn_sizer->Add(m_usb_passthrough_rem_device_btn, 0, wxALIGN_CENTER_VERTICAL); + auto* const bt_sensor_bar_pos_sizer = new wxBoxSizer(wxHORIZONTAL); bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0, wxALIGN_CENTER_VERTICAL); @@ -123,6 +136,15 @@ void WiiConfigPane::InitializeGUI() device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxLEFT | wxRIGHT, space5); device_settings_sizer->AddSpacer(space5); + auto* const usb_passthrough_sizer = + new wxStaticBoxSizer(wxVERTICAL, this, _("Whitelisted USB Passthrough Devices")); + usb_passthrough_sizer->AddSpacer(space5); + usb_passthrough_sizer->Add(m_usb_passthrough_devices_listbox, 0, wxEXPAND | wxLEFT | wxRIGHT, + space5); + usb_passthrough_sizer->AddSpacer(space5); + usb_passthrough_sizer->Add(usb_passthrough_btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + usb_passthrough_sizer->AddSpacer(space5); + auto* const bt_settings_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings")); bt_settings_static_sizer->AddSpacer(space5); @@ -135,6 +157,8 @@ void WiiConfigPane::InitializeGUI() main_sizer->AddSpacer(space5); main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); main_sizer->AddSpacer(space5); + main_sizer->Add(usb_passthrough_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); main_sizer->AddSpacer(space5); @@ -151,12 +175,26 @@ void WiiConfigPane::LoadGUIValues() m_sd_card_checkbox->SetValue(SConfig::GetInstance().m_WiiSDCard); m_connect_keyboard_checkbox->SetValue(SConfig::GetInstance().m_WiiKeyboard); + PopulateUSBPassthroughListbox(); + m_bt_sensor_bar_pos->SetSelection(SConfig::GetInstance().m_sensor_bar_position); m_bt_sensor_bar_sens->SetValue(SConfig::GetInstance().m_sensor_bar_sensitivity); m_bt_speaker_volume->SetValue(SConfig::GetInstance().m_speaker_volume); m_bt_wiimote_motor->SetValue(SConfig::GetInstance().m_wiimote_motor); } +void WiiConfigPane::PopulateUSBPassthroughListbox() +{ + m_usb_passthrough_devices_listbox->Freeze(); + m_usb_passthrough_devices_listbox->Clear(); + for (const auto& device : SConfig::GetInstance().m_usb_passthrough_devices) + { + m_usb_passthrough_devices_listbox->Append(USBUtils::GetDeviceName(device), + new USBPassthroughDeviceEntry(device)); + } + m_usb_passthrough_devices_listbox->Thaw(); +} + void WiiConfigPane::BindEvents() { m_screensaver_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnScreenSaverCheckBoxChanged, this); @@ -186,6 +224,38 @@ void WiiConfigPane::BindEvents() m_bt_wiimote_motor->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnWiimoteMotorChanged, this); m_bt_wiimote_motor->Bind(wxEVT_UPDATE_UI, &WxEventUtils::OnEnableIfCoreNotRunning); + + m_usb_passthrough_add_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistAddButton, + this); + + m_usb_passthrough_rem_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistRemoveButton, + this); + m_usb_passthrough_rem_device_btn->Bind(wxEVT_UPDATE_UI, + &WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate, this); +} + +void WiiConfigPane::OnUSBWhitelistAddButton(wxCommandEvent&) +{ + AddUSBDeviceDiag add_dialog{this}; + // Reload the USB device whitelist + if (add_dialog.ShowModal() == wxID_OK) + PopulateUSBPassthroughListbox(); +} + +void WiiConfigPane::OnUSBWhitelistRemoveButton(wxCommandEvent&) +{ + const int index = m_usb_passthrough_devices_listbox->GetSelection(); + if (index == wxNOT_FOUND) + return; + auto* const entry = static_cast( + m_usb_passthrough_devices_listbox->GetClientObject(index)); + SConfig::GetInstance().m_usb_passthrough_devices.erase({entry->m_vid, entry->m_pid}); + m_usb_passthrough_devices_listbox->Delete(index); +} + +void WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent& event) +{ + event.Enable(m_usb_passthrough_devices_listbox->GetSelection() != wxNOT_FOUND); } void WiiConfigPane::OnScreenSaverCheckBoxChanged(wxCommandEvent& event) diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.h b/Source/Core/DolphinWX/Config/WiiConfigPane.h index bb68f89e4e..557ab624f1 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.h +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.h @@ -6,11 +6,12 @@ #include #include -#include "Common/CommonTypes.h" class DolphinSlider; +class wxButton; class wxCheckBox; class wxChoice; +class wxListBox; class wxSlider; class WiiConfigPane final : public wxPanel @@ -23,6 +24,8 @@ private: void LoadGUIValues(); void BindEvents(); + void PopulateUSBPassthroughListbox(); + void OnScreenSaverCheckBoxChanged(wxCommandEvent&); void OnPAL60CheckBoxChanged(wxCommandEvent&); void OnSDCardCheckBoxChanged(wxCommandEvent&); @@ -30,6 +33,10 @@ private: void OnSystemLanguageChoiceChanged(wxCommandEvent&); void OnAspectRatioChoiceChanged(wxCommandEvent&); + void OnUSBWhitelistAddButton(wxCommandEvent&); + void OnUSBWhitelistRemoveButton(wxCommandEvent&); + void OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent&); + void OnSensorBarPosChanged(wxCommandEvent&); void OnSensorBarSensChanged(wxCommandEvent&); void OnSpeakerVolumeChanged(wxCommandEvent&); @@ -46,6 +53,10 @@ private: wxChoice* m_system_language_choice; wxChoice* m_aspect_ratio_choice; + wxListBox* m_usb_passthrough_devices_listbox; + wxButton* m_usb_passthrough_add_device_btn; + wxButton* m_usb_passthrough_rem_device_btn; + wxChoice* m_bt_sensor_bar_pos; DolphinSlider* m_bt_sensor_bar_sens; DolphinSlider* m_bt_speaker_volume; diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 81412afe65..bae3476422 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -62,6 +62,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index 5cc92cb65a..90842a6629 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -224,6 +224,9 @@ GUI\Config + + GUI\Config + GUI\Config @@ -443,6 +446,9 @@ GUI\Config + + GUI\Config + GUI\Config From e246afb0498b7149864e1bb352386188476d5fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 21 Jan 2017 19:08:17 +0100 Subject: [PATCH 03/10] IOS: Add UpdateWantDeterminism to devices This will be useful for the USB devices to disconnect and hide any real devices when Core::g_want_determinism is true. --- Source/Core/Core/Core.cpp | 4 ++-- Source/Core/Core/IOS/Device.h | 1 + Source/Core/Core/IOS/IPC.cpp | 8 ++++++++ Source/Core/Core/IOS/IPC.h | 2 ++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 06b76119ce..f408409359 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -54,7 +54,7 @@ #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" #include "Core/HW/Wiimote.h" -#include "Core/IOS/Network/Socket.h" +#include "Core/IOS/IPC.h" #include "Core/IOS/USB/Bluetooth/BTEmu.h" #include "Core/IOS/USB/Bluetooth/WiimoteDevice.h" #include "Core/Movie.h" @@ -975,7 +975,7 @@ void UpdateWantDeterminism(bool initial) bool was_unpaused = Core::PauseAndLock(true); g_want_determinism = new_want_determinism; - IOS::HLE::WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism); + IOS::HLE::UpdateWantDeterminism(new_want_determinism); Fifo::UpdateWantDeterminism(new_want_determinism); // We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use // of FMA. diff --git a/Source/Core/Core/IOS/Device.h b/Source/Core/Core/IOS/Device.h index 23aa9d1dc5..48ba68b10d 100644 --- a/Source/Core/Core/IOS/Device.h +++ b/Source/Core/Core/IOS/Device.h @@ -159,6 +159,7 @@ public: virtual IPCCommandResult IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); } virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); } virtual void Update() {} + virtual void UpdateWantDeterminism(bool new_want_determinism) {} virtual DeviceType GetDeviceType() const { return m_device_type; } virtual bool IsOpened() const { return m_is_active; } static IPCCommandResult GetDefaultReply(s32 return_value); diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 371942c9eb..85f30f2752 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -44,6 +44,7 @@ #include "Core/IOS/IPC.h" #include "Core/IOS/Network/Net.h" #include "Core/IOS/Network/SSL.h" +#include "Core/IOS/Network/Socket.h" #include "Core/IOS/SDIO/SDIOSlot0.h" #include "Core/IOS/STM/STM.h" #include "Core/IOS/USB/Bluetooth/BTEmu.h" @@ -851,5 +852,12 @@ void UpdateDevices() } } } + +void UpdateWantDeterminism(const bool new_want_determinism) +{ + WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism); + for (const auto& device : s_device_map) + device.second->UpdateWantDeterminism(new_want_determinism); +} } // namespace HLE } // namespace IOS diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h index 1c6146b793..79549b2e9e 100644 --- a/Source/Core/Core/IOS/IPC.h +++ b/Source/Core/Core/IOS/IPC.h @@ -77,6 +77,8 @@ void Update(); // Update Devices void UpdateDevices(); +void UpdateWantDeterminism(bool new_want_determinism); + void ExecuteCommand(u32 address); void EnqueueRequest(u32 address); From b8c651eac486c770f07e56a1703c44e55bcfd1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 28 Jan 2017 00:27:18 +0100 Subject: [PATCH 04/10] IOS: Store the active IOS title ID This allows us to get back the current active IOS version and expose only devices which exist/can be used in that version. --- Source/Core/Core/IOS/IPC.cpp | 12 ++++++++++++ Source/Core/Core/IOS/IPC.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 85f30f2752..6655b5f6da 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -86,6 +86,8 @@ static CoreTiming::EventType* s_event_sdio_notify; static u64 s_last_reply_time; +static u64 s_active_title_id; + static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL; static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL; @@ -412,6 +414,14 @@ static void SDIO_EventNotify_CPUThread(u64 userdata, s64 cycles_late) device->EventNotify(); } +// The title ID is a u64 where the first 32 bits are used for the title type. +// For IOS title IDs, the type will always be 00000001 (system), and the lower 32 bits +// are used for the IOS major version -- which is what we want here. +u32 GetVersion() +{ + return static_cast(s_active_title_id); +} + bool SetupMemory(u64 ios_title_id) { auto target_imv = std::find_if( @@ -424,6 +434,8 @@ bool SetupMemory(u64 ios_title_id) return false; } + s_active_title_id = ios_title_id; + Memory::Write_U32(target_imv->mem1_physical_size, ADDR_MEM1_SIZE); Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE); Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END); diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h index 79549b2e9e..7508be12f9 100644 --- a/Source/Core/Core/IOS/IPC.h +++ b/Source/Core/Core/IOS/IPC.h @@ -51,6 +51,8 @@ void Init(); // Needs to be called after Reset(true) to recreate the device tree void Reinit(); +u32 GetVersion(); + bool SetupMemory(u64 ios_title_id); // Shutdown From 73e55ccf4472db8c85255dd97ebb3c7596d5201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 13 Nov 2016 14:16:13 +0100 Subject: [PATCH 05/10] IOS: Add base Host and USB::Device classes The Host class will be used by the OH0, VEN and HID implementations as the base class for the IOS HLE device. It handles scanning devices, detecting device changes and everything that will be needed for OH0, VEN and HID to be implemented, while mostly abstracting libusb away. The Device class is for actual USB devices. This commit adds a LibusbDevice which interacts with a real USB device and enables USB passthrough. --- Source/Core/Core/CMakeLists.txt | 4 +- Source/Core/Core/Core.vcxproj | 8 + Source/Core/Core/Core.vcxproj.filters | 12 + Source/Core/Core/IOS/USB/Common.cpp | 122 +++++- Source/Core/Core/IOS/USB/Common.h | 68 ++++ Source/Core/Core/IOS/USB/Host.cpp | 251 +++++++++++++ Source/Core/Core/IOS/USB/Host.h | 85 +++++ Source/Core/Core/IOS/USB/LibusbDevice.cpp | 437 ++++++++++++++++++++++ Source/Core/Core/IOS/USB/LibusbDevice.h | 94 +++++ 9 files changed, 1079 insertions(+), 2 deletions(-) create mode 100644 Source/Core/Core/IOS/USB/Host.cpp create mode 100644 Source/Core/Core/IOS/USB/Host.h create mode 100644 Source/Core/Core/IOS/USB/LibusbDevice.cpp create mode 100644 Source/Core/Core/IOS/USB/LibusbDevice.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index ed0cecdcc5..5d95be79fc 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -154,6 +154,7 @@ set(SRCS ActionReplay.cpp IOS/SDIO/SDIOSlot0.cpp IOS/STM/STM.cpp IOS/USB/Common.cpp + IOS/USB/Host.cpp IOS/USB/USBV0.cpp IOS/USB/USB_KBD.cpp IOS/USB/USB_VEN.cpp @@ -264,7 +265,8 @@ set(LIBS if(LIBUSB_FOUND) # Using shared LibUSB set(LIBS ${LIBS} ${LIBUSB_LIBRARIES}) - set(SRCS ${SRCS} IOS/USB/USB_HIDv4.cpp + set(SRCS ${SRCS} IOS/USB/LibusbDevice.cpp + IOS/USB/USB_HIDv4.cpp IOS/USB/Bluetooth/BTReal.cpp) endif() diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 42496fe3e0..4cb3db2b4d 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -186,6 +186,12 @@ + + 4200;%(DisableSpecificWarnings) + + + 4200;%(DisableSpecificWarnings) + - 4200;%(DisableSpecificWarnings) - + @@ -431,8 +426,9 @@ + - + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 0c7ba536db..3b9ad3503a 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -785,10 +785,13 @@ IOS\USB + + IOS\USB + IOS\USB - + IOS\USB @@ -1377,10 +1380,13 @@ IOS\USB + + IOS\USB + IOS\USB - + IOS\USB diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 16443efa7e..0db81b2efc 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -51,6 +51,7 @@ #include "Core/IOS/USB/Bluetooth/BTReal.h" #include "Core/IOS/USB/OH0/OH0.h" #include "Core/IOS/USB/OH0/OH0Device.h" +#include "Core/IOS/USB/USB_HID/HIDv4.h" #include "Core/IOS/USB/USB_KBD.h" #include "Core/IOS/USB/USB_VEN.h" #include "Core/IOS/WFS/WFSI.h" @@ -61,10 +62,6 @@ namespace CoreTiming struct EventType; } // namespace CoreTiming -#if defined(__LIBUSB__) -#include "Core/IOS/USB/USB_HIDv4.h" -#endif - namespace IOS { namespace HLE @@ -513,11 +510,7 @@ void Reinit() AddDevice("/dev/usb/ven"); AddDevice("/dev/sdio/slot0"); AddDevice("/dev/sdio/slot1"); -#if defined(__LIBUSB__) AddDevice("/dev/usb/hid"); -#else - AddDevice("/dev/usb/hid"); -#endif AddDevice("/dev/usb/oh0"); AddDevice("/dev/usb/oh1"); AddDevice("/dev/usb/wfssrv"); diff --git a/Source/Core/Core/IOS/USB/USBV4.cpp b/Source/Core/Core/IOS/USB/USBV4.cpp new file mode 100644 index 0000000000..fae4e2011a --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV4.cpp @@ -0,0 +1,97 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/CommonFuncs.h" +#include "Common/CommonTypes.h" +#include "Core/HW/Memmap.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/USB/USBV4.h" + +namespace IOS +{ +namespace HLE +{ +namespace USB +{ +// Source: https://wiibrew.org/w/index.php?title=/dev/usb/hid&oldid=96809 +#pragma pack(push, 1) +struct HIDRequest +{ + u8 padding[16]; + s32 device_no; + union + { + struct + { + u8 bmRequestType; + u8 bmRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + } control; + struct + { + u32 endpoint; + u32 length; + } interrupt; + struct + { + u8 bIndex; + } string; + }; + u32 data_addr; +}; +#pragma pack(pop) + +V4CtrlMessage::V4CtrlMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1) +{ + HIDRequest hid_request; + Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request)); + request_type = hid_request.control.bmRequestType; + request = hid_request.control.bmRequest; + value = Common::swap16(hid_request.control.wValue); + index = Common::swap16(hid_request.control.wIndex); + length = Common::swap16(hid_request.control.wLength); + data_address = Common::swap32(hid_request.data_addr); +} + +// Since this is just a standard control request, but with additional requirements +// (US for the language and replacing non-ASCII characters with '?'), +// we can simply submit it as a usual control request. +V4GetUSStringMessage::V4GetUSStringMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1) +{ + HIDRequest hid_request; + Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request)); + request_type = 0x80; + request = REQUEST_GET_DESCRIPTOR; + value = (0x03 << 8) | hid_request.string.bIndex; + index = 0x0409; // language US + length = 255; + data_address = Common::swap32(hid_request.data_addr); +} + +void V4GetUSStringMessage::OnTransferComplete() const +{ + const std::locale& c_locale = std::locale::classic(); + std::string message = Memory::GetString(data_address); + std::replace_if(message.begin(), message.end(), + [&c_locale](char c) { return !std::isprint(c, c_locale); }, '?'); + Memory::CopyToEmu(data_address, message.c_str(), message.size()); +} + +V4IntrMessage::V4IntrMessage(const IOCtlRequest& ioctl) : IntrMessage(ioctl, -1) +{ + HIDRequest hid_request; + Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request)); + length = static_cast(Common::swap32(hid_request.interrupt.length)); + endpoint = static_cast(Common::swap32(hid_request.interrupt.endpoint)); + data_address = Common::swap32(hid_request.data_addr); +} +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USBV4.h b/Source/Core/Core/IOS/USB/USBV4.h new file mode 100644 index 0000000000..93ccd7ee21 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV4.h @@ -0,0 +1,50 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Core/IOS/USB/Common.h" + +// Used by an early version of /dev/usb/hid. + +namespace IOS +{ +namespace HLE +{ +struct IOCtlRequest; + +namespace USB +{ +enum V4Requests +{ + IOCTL_USBV4_GETDEVICECHANGE = 0, + IOCTL_USBV4_SET_SUSPEND = 1, + IOCTL_USBV4_CTRLMSG = 2, + IOCTL_USBV4_INTRMSG_IN = 3, + IOCTL_USBV4_INTRMSG_OUT = 4, + IOCTL_USBV4_GET_US_STRING = 5, + IOCTL_USBV4_GETVERSION = 6, + IOCTL_USBV4_SHUTDOWN = 7, + IOCTL_USBV4_CANCELINTERRUPT = 8, +}; + +struct V4CtrlMessage final : CtrlMessage +{ + explicit V4CtrlMessage(const IOCtlRequest& ioctl); +}; + +struct V4GetUSStringMessage final : CtrlMessage +{ + explicit V4GetUSStringMessage(const IOCtlRequest& ioctl); + void OnTransferComplete() const override; +}; + +struct V4IntrMessage final : IntrMessage +{ + explicit V4IntrMessage(const IOCtlRequest& ioctl); +}; +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp new file mode 100644 index 0000000000..4734490f1c --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp @@ -0,0 +1,230 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Align.h" +#include "Common/ChunkFile.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Core/CoreTiming.h" +#include "Core/HW/Memmap.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/USB/Common.h" +#include "Core/IOS/USB/USBV4.h" +#include "Core/IOS/USB/USB_HID/HIDv4.h" + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +USB_HIDv4::USB_HIDv4(u32 device_id, const std::string& device_name) + : USBHost(device_id, device_name) +{ +} + +USB_HIDv4::~USB_HIDv4() +{ + StopThreads(); +} + +IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request) +{ + request.Log(GetDeviceName(), LogTypes::IOS_USB); + switch (request.request) + { + case USB::IOCTL_USBV4_GETVERSION: + return GetDefaultReply(VERSION); + case USB::IOCTL_USBV4_GETDEVICECHANGE: + return GetDeviceChange(request); + case USB::IOCTL_USBV4_SHUTDOWN: + return Shutdown(request); + case USB::IOCTL_USBV4_SET_SUSPEND: + // Not implemented in IOS + return GetDefaultReply(IPC_SUCCESS); + case USB::IOCTL_USBV4_CANCELINTERRUPT: + return CancelInterrupt(request); + case USB::IOCTL_USBV4_GET_US_STRING: + case USB::IOCTL_USBV4_CTRLMSG: + case USB::IOCTL_USBV4_INTRMSG_IN: + case USB::IOCTL_USBV4_INTRMSG_OUT: + { + if (request.buffer_in == 0 || request.buffer_in_size != 32) + return GetDefaultReply(IPC_EINVAL); + const auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in + 16)); + if (!device->Attach(0)) + return GetDefaultReply(IPC_EINVAL); + return HandleTransfer(device, request.request, + [&, this]() { return SubmitTransfer(*device, request); }); + } + default: + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB); + return GetDefaultReply(IPC_SUCCESS); + } +} + +IPCCommandResult USB_HIDv4::CancelInterrupt(const IOCtlRequest& request) +{ + if (request.buffer_in == 0 || request.buffer_in_size != 8) + return GetDefaultReply(IPC_EINVAL); + + auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in)); + if (!device) + return GetDefaultReply(IPC_ENOENT); + device->CancelTransfer(Memory::Read_U8(request.buffer_in + 4)); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USB_HIDv4::GetDeviceChange(const IOCtlRequest& request) +{ + std::lock_guard lk{m_devicechange_hook_address_mutex}; + if (request.buffer_out == 0 || request.buffer_out_size != 0x600) + return GetDefaultReply(IPC_EINVAL); + + m_devicechange_hook_request = std::make_unique(request.address); + // On the first call, the reply is sent immediately (instead of on device insertion/removal) + if (m_devicechange_first_call) + { + TriggerDeviceChangeReply(); + m_devicechange_first_call = false; + } + return GetNoReply(); +} + +IPCCommandResult USB_HIDv4::Shutdown(const IOCtlRequest& request) +{ + std::lock_guard lk{m_devicechange_hook_address_mutex}; + if (m_devicechange_hook_request != 0) + { + Memory::Write_U32(0xffffffff, m_devicechange_hook_request->buffer_out); + EnqueueReply(*m_devicechange_hook_request, -1); + m_devicechange_hook_request.reset(); + } + return GetDefaultReply(IPC_SUCCESS); +} + +s32 USB_HIDv4::SubmitTransfer(USB::Device& device, const IOCtlRequest& request) +{ + switch (request.request) + { + case USB::IOCTL_USBV4_CTRLMSG: + return device.SubmitTransfer(std::make_unique(request)); + case USB::IOCTL_USBV4_GET_US_STRING: + return device.SubmitTransfer(std::make_unique(request)); + case USB::IOCTL_USBV4_INTRMSG_IN: + case USB::IOCTL_USBV4_INTRMSG_OUT: + return device.SubmitTransfer(std::make_unique(request)); + default: + return IPC_EINVAL; + } +} + +void USB_HIDv4::DoState(PointerWrap& p) +{ + p.Do(m_devicechange_first_call); + u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0; + p.Do(hook_address); + if (hook_address != 0) + m_devicechange_hook_request = std::make_unique(hook_address); + else + m_devicechange_hook_request.reset(); + + p.Do(m_ios_ids); + p.Do(m_device_ids); + + USBHost::DoState(p); +} + +std::shared_ptr USB_HIDv4::GetDeviceByIOSID(const s32 ios_id) const +{ + std::lock_guard lk{m_id_map_mutex}; + const auto iterator = m_ios_ids.find(ios_id); + if (iterator == m_ios_ids.cend()) + return nullptr; + return GetDeviceById(iterator->second); +} + +void USB_HIDv4::OnDeviceChange(ChangeEvent event, std::shared_ptr device) +{ + { + std::lock_guard id_map_lock{m_id_map_mutex}; + if (event == ChangeEvent::Inserted) + { + s32 new_id = 0; + while (m_ios_ids.find(new_id) != m_ios_ids.cend()) + ++new_id; + m_ios_ids[new_id] = device->GetId(); + m_device_ids[device->GetId()] = new_id; + } + else if (event == ChangeEvent::Removed && + m_device_ids.find(device->GetId()) != m_device_ids.cend()) + { + m_ios_ids.erase(m_device_ids.at(device->GetId())); + m_device_ids.erase(device->GetId()); + } + } + + { + std::lock_guard lk{m_devicechange_hook_address_mutex}; + TriggerDeviceChangeReply(); + } +} + +bool USB_HIDv4::ShouldAddDevice(const USB::Device& device) const +{ + return device.HasClass(HID_CLASS); +} + +void USB_HIDv4::TriggerDeviceChangeReply() +{ + if (!m_devicechange_hook_request) + return; + + { + std::lock_guard lk(m_devices_mutex); + const u32 dest = m_devicechange_hook_request->buffer_out; + u32 offset = 0; + for (const auto& device : m_devices) + { + const std::vector device_section = GetDeviceEntry(*device.second.get()); + if (offset + device_section.size() > m_devicechange_hook_request->buffer_out_size - 1) + { + WARN_LOG(IOS_USB, "Too many devices connected, skipping"); + break; + } + Memory::CopyToEmu(dest + offset, device_section.data(), device_section.size()); + offset += Common::AlignUp(static_cast(device_section.size()), 4); + } + // IOS writes 0xffffffff to the buffer when there are no more devices + Memory::Write_U32(0xffffffff, dest + offset); + } + + EnqueueReply(*m_devicechange_hook_request, IPC_SUCCESS, 0, CoreTiming::FromThread::ANY); + m_devicechange_hook_request.reset(); +} + +std::vector USB_HIDv4::GetDeviceEntry(const USB::Device& device) const +{ + std::lock_guard id_map_lock{m_id_map_mutex}; + + // The structure for a device section is as follows: + // 0-4 bytes: total size of the device data, including the size and the device ID + // 4-8 bytes: device ID + // the rest of the buffer is device descriptors data + std::vector entry(8); + const std::vector descriptors = device.GetDescriptorsUSBV4(); + const u32 entry_size = Common::swap32(static_cast(entry.size() + descriptors.size())); + const u32 ios_device_id = Common::swap32(m_device_ids.at(device.GetId())); + std::memcpy(entry.data(), &entry_size, sizeof(entry_size)); + std::memcpy(entry.data() + 4, &ios_device_id, sizeof(ios_device_id)); + entry.insert(entry.end(), descriptors.begin(), descriptors.end()); + + return entry; +} +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h new file mode 100644 index 0000000000..4048f3c339 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h @@ -0,0 +1,62 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/IOS/IPC.h" +#include "Core/IOS/USB/Host.h" + +class PointerWrap; + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +class USB_HIDv4 final : public USBHost +{ +public: + USB_HIDv4(u32 device_id, const std::string& device_name); + ~USB_HIDv4() override; + + IPCCommandResult IOCtl(const IOCtlRequest& request) override; + + void DoState(PointerWrap& p) override; + +private: + std::shared_ptr GetDeviceByIOSID(s32 ios_id) const; + + IPCCommandResult CancelInterrupt(const IOCtlRequest& request); + IPCCommandResult GetDeviceChange(const IOCtlRequest& request); + IPCCommandResult Shutdown(const IOCtlRequest& request); + s32 SubmitTransfer(USB::Device& device, const IOCtlRequest& request); + + void TriggerDeviceChangeReply(); + std::vector GetDeviceEntry(const USB::Device& device) const; + void OnDeviceChange(ChangeEvent, std::shared_ptr) override; + bool ShouldAddDevice(const USB::Device& device) const override; + + static constexpr u32 VERSION = 0x40001; + static constexpr u8 HID_CLASS = 0x03; + + bool m_devicechange_first_call = true; + std::mutex m_devicechange_hook_address_mutex; + std::unique_ptr m_devicechange_hook_request; + + mutable std::mutex m_id_map_mutex; + // IOS device IDs <=> USB device IDs + std::map m_ios_ids; + std::map m_device_ids; +}; +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HIDv4.cpp deleted file mode 100644 index f52fe5607a..0000000000 --- a/Source/Core/Core/IOS/USB/USB_HIDv4.cpp +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2012 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include - -#include - -#include "Common/Align.h" -#include "Common/CommonFuncs.h" -#include "Common/Logging/Log.h" -#include "Core/Core.h" -#include "Core/CoreTiming.h" -#include "Core/Debugger/Debugger_SymbolMap.h" -#include "Core/HW/Memmap.h" -#include "Core/IOS/IPC.h" -#include "Core/IOS/USB/USB_HIDv4.h" - -namespace IOS -{ -namespace HLE -{ -namespace Device -{ -constexpr int MAX_DEVICE_DEVNUM = 256; -static u64 hidDeviceAliases[MAX_DEVICE_DEVNUM]; - -// Regular thread -void USB_HIDv4::checkUsbUpdates(USB_HIDv4* hid) -{ - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 500; - while (hid->usb_thread_running) - { - static u16 timeToFill = 0; - if (timeToFill == 0) - { - std::lock_guard lk(hid->m_device_list_reply_mutex); - if (hid->deviceCommandAddress != 0) - { - IOCtlRequest request{hid->deviceCommandAddress}; - hid->FillOutDevices(request); - EnqueueReply(request, IPC_SUCCESS, 0, CoreTiming::FromThread::NON_CPU); - hid->deviceCommandAddress = 0; - } - } - timeToFill += 8; - libusb_handle_events_timeout(nullptr, &tv); - } - - return; -} - -void USB_HIDv4::handleUsbUpdates(struct libusb_transfer* transfer) -{ - s32 ret = IPC_EINVAL; - u32 replyAddress = (u32)(size_t)transfer->user_data; - if (transfer->status == LIBUSB_TRANSFER_COMPLETED) - { - ret = transfer->length; - } - - IOCtlRequest request{replyAddress}; - EnqueueReply(request, ret, 0, CoreTiming::FromThread::NON_CPU); -} - -USB_HIDv4::USB_HIDv4(u32 device_id, const std::string& device_name) : Device(device_id, device_name) -{ - deviceCommandAddress = 0; - memset(hidDeviceAliases, 0, sizeof(hidDeviceAliases)); - int ret = libusb_init(nullptr); - if (ret) - { - ERROR_LOG(IOS_HID, "libusb_init failed with error: %d", ret); - } - else - { - usb_thread_running = true; - usb_thread = std::thread(checkUsbUpdates, this); - } -} - -USB_HIDv4::~USB_HIDv4() -{ - bool deinit_libusb = false; - if (usb_thread_running) - { - usb_thread_running = false; - usb_thread.join(); - deinit_libusb = true; - } - - for (const auto& device : m_open_devices) - { - libusb_close(device.second); - } - m_open_devices.clear(); - - if (deinit_libusb) - libusb_exit(nullptr); -} - -IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request) -{ - if (Core::g_want_determinism) - { - return GetDefaultReply(IPC_EACCES); - } - - s32 return_value = IPC_SUCCESS; - switch (request.request) - { - case IOCTL_HID_GET_ATTACHED: - { - deviceCommandAddress = request.address; - return GetNoReply(); - } - case IOCTL_HID_OPEN: - { - // hid version, apparently - return_value = 0x40001; - break; - } - case IOCTL_HID_SET_SUSPEND: - { - // not actually implemented in IOS - return_value = IPC_SUCCESS; - break; - } - case IOCTL_HID_CANCEL_INTERRUPT: - { - return_value = IPC_SUCCESS; - break; - } - case IOCTL_HID_CONTROL: - { - /* - ERROR CODES: - -4 Can't find device specified - */ - - u32 dev_num = Memory::Read_U32(request.buffer_in + 0x10); - u8 bmRequestType = Memory::Read_U8(request.buffer_in + 0x14); - u8 bRequest = Memory::Read_U8(request.buffer_in + 0x15); - u16 wValue = Memory::Read_U16(request.buffer_in + 0x16); - u16 wIndex = Memory::Read_U16(request.buffer_in + 0x18); - u16 wLength = Memory::Read_U16(request.buffer_in + 0x1A); - u32 data = Memory::Read_U32(request.buffer_in + 0x1C); - - return_value = IPC_EINVAL; - - libusb_device_handle* dev_handle = GetDeviceByDevNum(dev_num); - - if (dev_handle == nullptr) - { - INFO_LOG(IOS_HID, "Could not find handle: %X", dev_num); - break; - } - struct libusb_transfer* transfer = libusb_alloc_transfer(0); - transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; - - u8* buffer = (u8*)malloc(wLength + LIBUSB_CONTROL_SETUP_SIZE); - libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, wLength); - Memory::CopyFromEmu(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); - libusb_fill_control_transfer(transfer, dev_handle, buffer, handleUsbUpdates, - (void*)(size_t)request.address, /* no timeout */ 0); - libusb_submit_transfer(transfer); - - // DEBUG_LOG(IOS_HID, "HID::IOCtl(Control)(%02X, %02X) (BufferIn: (%08x, %i), - // request.buffer_out: - // (%08x, %i)", - // bmRequestType, bRequest, BufferIn, request.buffer_in_size, request.buffer_out, - // request.buffer_out_size); - - // It's the async way! - return GetNoReply(); - } - case IOCTL_HID_INTERRUPT_OUT: - case IOCTL_HID_INTERRUPT_IN: - { - u32 dev_num = Memory::Read_U32(request.buffer_in + 0x10); - u32 endpoint = Memory::Read_U32(request.buffer_in + 0x14); - u32 length = Memory::Read_U32(request.buffer_in + 0x18); - - u32 data = Memory::Read_U32(request.buffer_in + 0x1C); - - return_value = IPC_EINVAL; - - libusb_device_handle* dev_handle = GetDeviceByDevNum(dev_num); - - if (dev_handle == nullptr) - { - INFO_LOG(IOS_HID, "Could not find handle: %X", dev_num); - break; - } - - struct libusb_transfer* transfer = libusb_alloc_transfer(0); - transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; - libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, Memory::GetPointer(data), length, - handleUsbUpdates, (void*)(size_t)request.address, 0); - libusb_submit_transfer(transfer); - - // It's the async way! - return GetNoReply(); - } - case IOCTL_HID_SHUTDOWN: - { - std::lock_guard lk(m_device_list_reply_mutex); - if (deviceCommandAddress != 0) - { - IOCtlRequest pending_request{deviceCommandAddress}; - Memory::Write_U32(0xFFFFFFFF, pending_request.buffer_out); - EnqueueReply(pending_request, -1); - deviceCommandAddress = 0; - } - INFO_LOG(IOS_HID, "HID::IOCtl(Shutdown) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", - request.buffer_in, request.buffer_in_size, request.buffer_out, - request.buffer_out_size); - break; - } - default: - request.Log(GetDeviceName(), LogTypes::IOS_HID); - } - - return GetDefaultReply(return_value); -} - -bool USB_HIDv4::ClaimDevice(libusb_device_handle* dev) -{ - int ret = 0; - if ((ret = libusb_kernel_driver_active(dev, 0)) == 1) - { - if ((ret = libusb_detach_kernel_driver(dev, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) - { - ERROR_LOG(IOS_HID, "libusb_detach_kernel_driver failed with error: %d", ret); - return false; - } - } - else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) - { - ERROR_LOG(IOS_HID, "libusb_kernel_driver_active error ret = %d", ret); - return false; - } - - if ((ret = libusb_claim_interface(dev, 0))) - { - ERROR_LOG(IOS_HID, "libusb_claim_interface failed with error: %d", ret); - return false; - } - - return true; -} - -IPCCommandResult USB_HIDv4::IOCtlV(const IOCtlVRequest& request) -{ - Dolphin_Debugger::PrintCallstack(LogTypes::IOS_HID, LogTypes::LWARNING); - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_HID); - return GetDefaultReply(IPC_SUCCESS); -} - -void USB_HIDv4::ConvertDeviceToWii(USB::DeviceDescriptor* dest, const libusb_device_descriptor* src) -{ - dest->bLength = src->bLength; - dest->bDescriptorType = src->bDescriptorType; - dest->bcdUSB = Common::swap16(src->bcdUSB); - dest->bDeviceClass = src->bDeviceClass; - dest->bDeviceSubClass = src->bDeviceSubClass; - dest->bDeviceProtocol = src->bDeviceProtocol; - dest->bMaxPacketSize0 = src->bMaxPacketSize0; - dest->idVendor = Common::swap16(src->idVendor); - dest->idProduct = Common::swap16(src->idProduct); - dest->bcdDevice = Common::swap16(src->bcdDevice); - dest->iManufacturer = src->iManufacturer; - dest->iProduct = src->iProduct; - dest->iSerialNumber = src->iSerialNumber; - dest->bNumConfigurations = src->bNumConfigurations; -} - -void USB_HIDv4::ConvertConfigToWii(USB::ConfigDescriptor* dest, const libusb_config_descriptor* src) -{ - memcpy(dest, src, sizeof(USB::ConfigDescriptor)); - dest->wTotalLength = Common::swap16(dest->wTotalLength); -} - -void USB_HIDv4::ConvertInterfaceToWii(USB::InterfaceDescriptor* dest, - const libusb_interface_descriptor* src) -{ - memcpy(dest, src, sizeof(USB::InterfaceDescriptor)); -} - -void USB_HIDv4::ConvertEndpointToWii(USB::EndpointDescriptor* dest, - const libusb_endpoint_descriptor* src) -{ - memcpy(dest, src, sizeof(USB::EndpointDescriptor)); - dest->wMaxPacketSize = Common::swap16(dest->wMaxPacketSize); -} - -void USB_HIDv4::FillOutDevices(const IOCtlRequest& request) -{ - static u16 check = 1; - int OffsetBuffer = request.buffer_out; - int OffsetStart = 0; - // int OffsetDevice = 0; - int d, c, ic, i, e; /* config, interface container, interface, endpoint */ - - libusb_device** list; - // libusb_device *found = nullptr; - ssize_t cnt = libusb_get_device_list(nullptr, &list); - INFO_LOG(IOS_HID, "Found %ld viable USB devices.", cnt); - for (d = 0; d < cnt; d++) - { - libusb_device* device = list[d]; - struct libusb_device_descriptor desc; - int dRet = libusb_get_device_descriptor(device, &desc); - if (dRet) - { - // could not aquire the descriptor, no point in trying to use it. - WARN_LOG(IOS_HID, "libusb_get_device_descriptor failed with error: %d", dRet); - continue; - } - OffsetStart = OffsetBuffer; - OffsetBuffer += 4; // skip length for now, fill at end - - OffsetBuffer += 4; // skip devNum for now - - USB::DeviceDescriptor wii_device; - ConvertDeviceToWii(&wii_device, &desc); - Memory::CopyToEmu(OffsetBuffer, &wii_device, wii_device.bLength); - OffsetBuffer += Common::AlignUp(wii_device.bLength, 4); - bool deviceValid = true; - bool isHID = false; - - for (c = 0; deviceValid && c < desc.bNumConfigurations; c++) - { - struct libusb_config_descriptor* config = nullptr; - int cRet = libusb_get_config_descriptor(device, c, &config); - // do not try to use usb devices with more than one interface, games can crash - if (cRet == 0 && config->bNumInterfaces <= MAX_HID_INTERFACES) - { - USB::ConfigDescriptor wii_config; - ConvertConfigToWii(&wii_config, config); - Memory::CopyToEmu(OffsetBuffer, &wii_config, wii_config.bLength); - OffsetBuffer += Common::AlignUp(wii_config.bLength, 4); - - for (ic = 0; ic < config->bNumInterfaces; ic++) - { - const struct libusb_interface* interfaceContainer = &config->interface[ic]; - - for (i = 0; i < interfaceContainer->num_altsetting; i++) - { - const struct libusb_interface_descriptor* interface = - &interfaceContainer->altsetting[i]; - - if (interface->bInterfaceClass == LIBUSB_CLASS_HID) - isHID = true; - - USB::InterfaceDescriptor wii_interface; - ConvertInterfaceToWii(&wii_interface, interface); - Memory::CopyToEmu(OffsetBuffer, &wii_interface, wii_interface.bLength); - OffsetBuffer += Common::AlignUp(wii_interface.bLength, 4); - - for (e = 0; e < interface->bNumEndpoints; e++) - { - const struct libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; - - USB::EndpointDescriptor wii_endpoint; - ConvertEndpointToWii(&wii_endpoint, endpoint); - Memory::CopyToEmu(OffsetBuffer, &wii_endpoint, wii_endpoint.bLength); - OffsetBuffer += Common::AlignUp(wii_endpoint.bLength, 4); - - } // endpoints - } // interfaces - } // interface containters - libusb_free_config_descriptor(config); - config = nullptr; - } - else - { - if (cRet) - WARN_LOG(IOS_HID, "libusb_get_config_descriptor failed with: %d", cRet); - deviceValid = false; - OffsetBuffer = OffsetStart; - } - } // configs - - if (!isHID) - { - deviceValid = false; - OffsetBuffer = OffsetStart; - } - - if (deviceValid) - { - Memory::Write_U32(OffsetBuffer - OffsetStart, OffsetStart); // fill in length - - int devNum = GetAvailableDevNum(desc.idVendor, desc.idProduct, libusb_get_bus_number(device), - libusb_get_device_address(device), check); - if (devNum < 0) - { - // too many devices to handle. - ERROR_LOG(IOS_HID, "Exhausted device list, there are way too many usb devices plugged in."); - OffsetBuffer = OffsetStart; - continue; - } - - INFO_LOG(IOS_HID, "Found device with Vendor: %X Product: %X Devnum: %d", desc.idVendor, - desc.idProduct, devNum); - - Memory::Write_U32(devNum, OffsetStart + 4); // write device num - } - } - - // Find devices that no longer exists and free them - for (i = 0; i < MAX_DEVICE_DEVNUM; i++) - { - u16 check_cur = (u16)(hidDeviceAliases[i] >> 48); - if (hidDeviceAliases[i] != 0 && check_cur != check) - { - INFO_LOG(IOS_HID, "Removing: device %d %hX %hX", i, check, check_cur); - std::lock_guard lk(m_open_devices_mutex); - if (m_open_devices.find(i) != m_open_devices.end()) - { - libusb_device_handle* handle = m_open_devices[i]; - libusb_close(handle); - m_open_devices.erase(i); - } - hidDeviceAliases[i] = 0; - } - } - check++; - - libusb_free_device_list(list, 1); - - Memory::Write_U32(0xFFFFFFFF, OffsetBuffer); // no more devices -} - -libusb_device_handle* USB_HIDv4::GetDeviceByDevNum(u32 devNum) -{ - libusb_device** list; - libusb_device_handle* handle = nullptr; - ssize_t cnt; - - if (devNum >= MAX_DEVICE_DEVNUM) - return nullptr; - - std::lock_guard lk(m_open_devices_mutex); - - if (m_open_devices.find(devNum) != m_open_devices.end()) - { - handle = m_open_devices[devNum]; - if (libusb_kernel_driver_active(handle, 0) != LIBUSB_ERROR_NO_DEVICE) - { - return handle; - } - else - { - libusb_close(handle); - m_open_devices.erase(devNum); - } - } - - cnt = libusb_get_device_list(nullptr, &list); - - if (cnt < 0) - return nullptr; - -#ifdef _WIN32 - static bool has_warned_about_drivers = false; -#endif - - for (ssize_t i = 0; i < cnt; i++) - { - libusb_device* device = list[i]; - struct libusb_device_descriptor desc; - int dRet = libusb_get_device_descriptor(device, &desc); - u8 bus = libusb_get_bus_number(device); - u8 port = libusb_get_device_address(device); - u64 unique_id = - ((u64)desc.idVendor << 32) | ((u64)desc.idProduct << 16) | ((u64)bus << 8) | (u64)port; - if ((hidDeviceAliases[devNum] & HID_ID_MASK) == unique_id) - { - int ret = libusb_open(device, &handle); - if (ret) - { - if (ret == LIBUSB_ERROR_ACCESS) - { - if (dRet) - { - ERROR_LOG(IOS_HID, "Dolphin does not have access to this device: Bus %03d Device " - "%03d: ID ????:???? (couldn't get id).", - bus, port); - } - else - { - ERROR_LOG( - IOS_HID, - "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", - bus, port, desc.idVendor, desc.idProduct); - } - } -#ifdef _WIN32 - else if (ret == LIBUSB_ERROR_NOT_SUPPORTED) - { - if (!has_warned_about_drivers) - { - // Max of one warning. - has_warned_about_drivers = true; - WARN_LOG(IOS_HID, "Please install the libusb drivers for the device %04X:%04X", - desc.idVendor, desc.idProduct); - } - } -#endif - else - { - ERROR_LOG(IOS_HID, "libusb_open failed to open device with error = %d", ret); - } - continue; - } - - if (!ClaimDevice(handle)) - { - ERROR_LOG(IOS_HID, "Could not claim the device for handle: %X", devNum); - libusb_close(handle); - continue; - } - - m_open_devices[devNum] = handle; - break; - } - else - { - handle = nullptr; - } - } - - libusb_free_device_list(list, 1); - - return handle; -} - -int USB_HIDv4::GetAvailableDevNum(u16 idVendor, u16 idProduct, u8 bus, u8 port, u16 check) -{ - int pos = -1; - u64 unique_id = ((u64)idVendor << 32) | ((u64)idProduct << 16) | ((u64)bus << 8) | (u64)port; - - for (int i = 0; i < MAX_DEVICE_DEVNUM; i++) - { - u64 id = hidDeviceAliases[i] & HID_ID_MASK; - if (id == 0 && pos == -1) - { - pos = i; - } - else if (id == unique_id) - { - hidDeviceAliases[i] = id | ((u64)check << 48); - return i; - } - } - - if (pos != -1) - { - hidDeviceAliases[pos] = unique_id | ((u64)check << 48); - return pos; - } - - return -1; -} -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HIDv4.h b/Source/Core/Core/IOS/USB/USB_HIDv4.h deleted file mode 100644 index 0732a91b44..0000000000 --- a/Source/Core/Core/IOS/USB/USB_HIDv4.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2012 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "Core/IOS/Device.h" -#include "Core/IOS/IPC.h" -#include "Core/IOS/USB/Common.h" - -// Forward declare things which we need from libusb header. -// This prevents users of this file from indirectly pulling in libusb. -#if defined(_WIN32) -#define LIBUSB_CALL WINAPI -#else -#define LIBUSB_CALL -#endif -struct libusb_config_descriptor; -struct libusb_device_descriptor; -struct libusb_device_handle; -struct libusb_endpoint_descriptor; -struct libusb_interface_descriptor; -struct libusb_transfer; - -namespace IOS -{ -namespace HLE -{ -#define HID_ID_MASK 0x0000FFFFFFFFFFFF -#define MAX_HID_INTERFACES 1 - -namespace Device -{ -class USB_HIDv4 : public Device -{ -public: - USB_HIDv4(u32 _DeviceID, const std::string& _rDeviceName); - - virtual ~USB_HIDv4(); - - IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - IPCCommandResult IOCtl(const IOCtlRequest& request) override; - -private: - enum - { - IOCTL_HID_GET_ATTACHED = 0x00, - IOCTL_HID_SET_SUSPEND = 0x01, - IOCTL_HID_CONTROL = 0x02, - IOCTL_HID_INTERRUPT_IN = 0x03, - IOCTL_HID_INTERRUPT_OUT = 0x04, - IOCTL_HID_GET_US_STRING = 0x05, - IOCTL_HID_OPEN = 0x06, - IOCTL_HID_SHUTDOWN = 0x07, - IOCTL_HID_CANCEL_INTERRUPT = 0x08, - }; - - u32 deviceCommandAddress; - void FillOutDevices(const IOCtlRequest& request); - int GetAvailableDevNum(u16 idVendor, u16 idProduct, u8 bus, u8 port, u16 check); - bool ClaimDevice(libusb_device_handle* dev); - - void ConvertDeviceToWii(USB::DeviceDescriptor* dest, const libusb_device_descriptor* src); - void ConvertConfigToWii(USB::ConfigDescriptor* dest, const libusb_config_descriptor* src); - void ConvertInterfaceToWii(USB::InterfaceDescriptor* dest, - const libusb_interface_descriptor* src); - void ConvertEndpointToWii(USB::EndpointDescriptor* dest, const libusb_endpoint_descriptor* src); - - static void checkUsbUpdates(USB_HIDv4* hid); - static void LIBUSB_CALL handleUsbUpdates(libusb_transfer* transfer); - - libusb_device_handle* GetDeviceByDevNum(u32 devNum); - std::map m_open_devices; - std::mutex m_open_devices_mutex; - std::mutex m_device_list_reply_mutex; - - std::thread usb_thread; - bool usb_thread_running; -}; -} // namespace Device -} // namespace HLE -} // namespace IOS From b63b6111b3dbbafc3248ff00e41e356d6a090cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 13 Nov 2016 14:23:29 +0100 Subject: [PATCH 09/10] IOS: Implement USB_VEN (/dev/usb/ven) --- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/Core.vcxproj | 6 +- Source/Core/Core/Core.vcxproj.filters | 14 +- Source/Core/Core/IOS/IPC.cpp | 4 +- Source/Core/Core/IOS/USB/USBV5.cpp | 55 ++++ Source/Core/Core/IOS/USB/USBV5.h | 58 ++++ Source/Core/Core/IOS/USB/USB_VEN.cpp | 83 ------ Source/Core/Core/IOS/USB/USB_VEN.h | 46 ---- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 333 +++++++++++++++++++++++ Source/Core/Core/IOS/USB/USB_VEN/VEN.h | 95 +++++++ 10 files changed, 559 insertions(+), 138 deletions(-) create mode 100644 Source/Core/Core/IOS/USB/USBV5.cpp create mode 100644 Source/Core/Core/IOS/USB/USBV5.h delete mode 100644 Source/Core/Core/IOS/USB/USB_VEN.cpp delete mode 100644 Source/Core/Core/IOS/USB/USB_VEN.h create mode 100644 Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp create mode 100644 Source/Core/Core/IOS/USB/USB_VEN/VEN.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 594608b377..166eb158bf 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -158,10 +158,11 @@ set(SRCS ActionReplay.cpp IOS/USB/OH0/OH0.cpp IOS/USB/OH0/OH0Device.cpp IOS/USB/USB_HID/HIDv4.cpp + IOS/USB/USB_VEN/VEN.cpp IOS/USB/USBV0.cpp IOS/USB/USBV4.cpp + IOS/USB/USBV5.cpp IOS/USB/USB_KBD.cpp - IOS/USB/USB_VEN.cpp IOS/USB/Bluetooth/BTBase.cpp IOS/USB/Bluetooth/BTEmu.cpp IOS/USB/Bluetooth/BTStub.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 7983948be3..dc6bcd4a36 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -195,10 +195,11 @@ + + - @@ -427,10 +428,11 @@ + + - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 3b9ad3503a..109608d2bb 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -788,16 +788,19 @@ IOS\USB + + IOS\USB + IOS\USB IOS\USB - + IOS\USB - + IOS\USB @@ -1383,16 +1386,19 @@ IOS\USB + + IOS\USB + IOS\USB IOS\USB - + IOS\USB - + IOS\USB diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 0db81b2efc..1580791a80 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -53,7 +53,7 @@ #include "Core/IOS/USB/OH0/OH0Device.h" #include "Core/IOS/USB/USB_HID/HIDv4.h" #include "Core/IOS/USB/USB_KBD.h" -#include "Core/IOS/USB/USB_VEN.h" +#include "Core/IOS/USB/USB_VEN/VEN.h" #include "Core/IOS/WFS/WFSI.h" #include "Core/IOS/WFS/WFSSRV.h" @@ -507,12 +507,12 @@ void Reinit() AddDevice("/dev/net/ip/top"); AddDevice("/dev/net/ssl"); AddDevice("/dev/usb/kbd"); - AddDevice("/dev/usb/ven"); AddDevice("/dev/sdio/slot0"); AddDevice("/dev/sdio/slot1"); AddDevice("/dev/usb/hid"); AddDevice("/dev/usb/oh0"); AddDevice("/dev/usb/oh1"); + AddDevice("/dev/usb/ven"); AddDevice("/dev/usb/wfssrv"); AddDevice("/dev/wfsi"); } diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp new file mode 100644 index 0000000000..bf4fd09399 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -0,0 +1,55 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/HW/Memmap.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/USB/USBV5.h" + +namespace IOS +{ +namespace HLE +{ +namespace USB +{ +V5CtrlMessage::V5CtrlMessage(const IOCtlVRequest& ioctlv) + : CtrlMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 16)) +{ + request_type = Memory::Read_U8(ioctlv.in_vectors[0].address + 8); + request = Memory::Read_U8(ioctlv.in_vectors[0].address + 9); + value = Memory::Read_U16(ioctlv.in_vectors[0].address + 10); + index = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + length = Memory::Read_U16(ioctlv.in_vectors[0].address + 14); +} + +V5BulkMessage::V5BulkMessage(const IOCtlVRequest& ioctlv) + : BulkMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 18); +} + +V5IntrMessage::V5IntrMessage(const IOCtlVRequest& ioctlv) + : IntrMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 14); +} + +V5IsoMessage::V5IsoMessage(const IOCtlVRequest& ioctlv) + : IsoMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + num_packets = Memory::Read_U8(ioctlv.in_vectors[0].address + 16); + endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 17); + packet_sizes_addr = Memory::Read_U32(ioctlv.in_vectors[0].address + 12); + for (size_t i = 0; i < num_packets; ++i) + packet_sizes.push_back(Memory::Read_U16(static_cast(packet_sizes_addr + i * sizeof(u16)))); + length = std::accumulate(packet_sizes.begin(), packet_sizes.end(), 0); +} +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USBV5.h b/Source/Core/Core/IOS/USB/USBV5.h new file mode 100644 index 0000000000..871d0e7856 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV5.h @@ -0,0 +1,58 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Core/IOS/USB/Common.h" + +// Used by late USB interfaces for /dev/usb/ven and /dev/usb/hid (since IOS57 which +// reorganised the USB modules in IOS). + +namespace IOS +{ +namespace HLE +{ +struct IOCtlRequest; + +namespace USB +{ +enum V5Requests +{ + IOCTL_USBV5_GETVERSION = 0, + IOCTL_USBV5_GETDEVICECHANGE = 1, + IOCTL_USBV5_SHUTDOWN = 2, + IOCTL_USBV5_GETDEVPARAMS = 3, + IOCTL_USBV5_ATTACHFINISH = 6, + IOCTL_USBV5_SETALTERNATE = 7, + IOCTL_USBV5_SUSPEND_RESUME = 16, + IOCTL_USBV5_CANCELENDPOINT = 17, + IOCTLV_USBV5_CTRLMSG = 18, + IOCTLV_USBV5_INTRMSG = 19, + IOCTLV_USBV5_ISOMSG = 20, + IOCTLV_USBV5_BULKMSG = 21 +}; + +struct V5CtrlMessage final : CtrlMessage +{ + explicit V5CtrlMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5BulkMessage final : BulkMessage +{ + explicit V5BulkMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5IntrMessage final : IntrMessage +{ + explicit V5IntrMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5IsoMessage final : IsoMessage +{ + explicit V5IsoMessage(const IOCtlVRequest& cmd_buffer); +}; +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN.cpp deleted file mode 100644 index 811ef83443..0000000000 --- a/Source/Core/Core/IOS/USB/USB_VEN.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Core/IOS/USB/USB_VEN.h" -#include "Common/Logging/Log.h" -#include "Core/HW/Memmap.h" - -namespace IOS -{ -namespace HLE -{ -namespace Device -{ -USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : Device(device_id, device_name) -{ -} - -IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) -{ - request.Dump(GetDeviceName()); - return GetNoReply(); -} - -IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) -{ - request.Log(GetDeviceName(), LogTypes::OSHLE); - - IPCCommandResult reply = GetDefaultReply(IPC_SUCCESS); - switch (request.request) - { - case USBV5_IOCTL_GETVERSION: - Memory::Write_U32(0x50001, request.buffer_out); - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_GETDEVICECHANGE: - { - // sent on change - static bool firstcall = true; - if (firstcall) - { - reply = GetDefaultReply(IPC_SUCCESS); - firstcall = false; - } - - // num devices - reply = GetDefaultReply(0); - return reply; - } - break; - - case USBV5_IOCTL_ATTACHFINISH: - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_SUSPEND_RESUME: - DEBUG_LOG(OSHLE, "Device: %i Resumed: %i", Memory::Read_U32(request.buffer_in), - Memory::Read_U32(request.buffer_in + 4)); - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_GETDEVPARAMS: - { - s32 device = Memory::Read_U32(request.buffer_in); - u32 unk = Memory::Read_U32(request.buffer_in + 4); - - DEBUG_LOG(OSHLE, "USBV5_IOCTL_GETDEVPARAMS device: %i unk: %i", device, unk); - - Memory::Write_U32(0, request.buffer_out); - - reply = GetDefaultReply(IPC_SUCCESS); - } - break; - - default: - request.Log(GetDeviceName(), LogTypes::OSHLE, LogTypes::LDEBUG); - } - return reply; -} -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN.h b/Source/Core/Core/IOS/USB/USB_VEN.h deleted file mode 100644 index c52b67cf32..0000000000 --- a/Source/Core/Core/IOS/USB/USB_VEN.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "Common/CommonTypes.h" -#include "Core/IOS/Device.h" -#include "Core/IOS/IPC.h" - -namespace IOS -{ -namespace HLE -{ -namespace Device -{ -class USB_VEN final : public Device -{ -public: - USB_VEN(u32 device_id, const std::string& device_name); - - IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - IPCCommandResult IOCtl(const IOCtlRequest& request) override; - -private: - enum USBIOCtl - { - USBV5_IOCTL_GETVERSION = 0, - USBV5_IOCTL_GETDEVICECHANGE = 1, - USBV5_IOCTL_SHUTDOWN = 2, - USBV5_IOCTL_GETDEVPARAMS = 3, - USBV5_IOCTL_ATTACHFINISH = 6, - USBV5_IOCTL_SETALTERNATE = 7, - USBV5_IOCTL_SUSPEND_RESUME = 16, - USBV5_IOCTL_CANCELENDPOINT = 17, - USBV5_IOCTL_CTRLMSG = 18, - USBV5_IOCTL_INTRMSG = 19, - USBV5_IOCTL_ISOMSG = 20, - USBV5_IOCTL_BULKMSG = 21 - }; -}; -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp new file mode 100644 index 0000000000..a8171fcedb --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -0,0 +1,333 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Core/CoreTiming.h" +#include "Core/HW/Memmap.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/USB/Common.h" +#include "Core/IOS/USB/USBV5.h" +#include "Core/IOS/USB/USB_VEN/VEN.h" + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : USBHost(device_id, device_name) +{ +} + +USB_VEN::~USB_VEN() +{ + StopThreads(); +} + +ReturnCode USB_VEN::Open(const OpenRequest& request) +{ + const u32 ios_major_version = GetVersion(); + if (ios_major_version != 57 && ios_major_version != 58 && ios_major_version != 59) + return IPC_ENOENT; + return USBHost::Open(request); +} + +IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) +{ + request.Log(GetDeviceName(), LogTypes::IOS_USB); + switch (request.request) + { + case USB::IOCTL_USBV5_GETVERSION: + Memory::Write_U32(VERSION, request.buffer_out); + return GetDefaultReply(IPC_SUCCESS); + case USB::IOCTL_USBV5_GETDEVICECHANGE: + return GetDeviceChange(request); + case USB::IOCTL_USBV5_SHUTDOWN: + return Shutdown(request); + case USB::IOCTL_USBV5_GETDEVPARAMS: + return HandleDeviceIOCtl(request, &USB_VEN::GetDeviceInfo); + case USB::IOCTL_USBV5_ATTACHFINISH: + return GetDefaultReply(IPC_SUCCESS); + case USB::IOCTL_USBV5_SETALTERNATE: + return HandleDeviceIOCtl(request, &USB_VEN::SetAlternateSetting); + case USB::IOCTL_USBV5_SUSPEND_RESUME: + return HandleDeviceIOCtl(request, &USB_VEN::SuspendResume); + case USB::IOCTL_USBV5_CANCELENDPOINT: + return HandleDeviceIOCtl(request, &USB_VEN::CancelEndpoint); + default: + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR); + return GetDefaultReply(IPC_SUCCESS); + } +} + +IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) +{ + static const std::map s_num_vectors = { + {USB::IOCTLV_USBV5_CTRLMSG, 2}, + {USB::IOCTLV_USBV5_INTRMSG, 2}, + {USB::IOCTLV_USBV5_BULKMSG, 2}, + {USB::IOCTLV_USBV5_ISOMSG, 4}, + }; + + switch (request.request) + { + case USB::IOCTLV_USBV5_CTRLMSG: + case USB::IOCTLV_USBV5_INTRMSG: + case USB::IOCTLV_USBV5_BULKMSG: + case USB::IOCTLV_USBV5_ISOMSG: + { + if (request.in_vectors.size() + request.io_vectors.size() != s_num_vectors.at(request.request)) + return GetDefaultReply(IPC_EINVAL); + + const s32 device_id = Memory::Read_U32(request.in_vectors[0].address); + auto device = GetDeviceByIOSID(device_id); + if (!device || !device->Attach(GetInterfaceNumber(device_id))) + return GetDefaultReply(IPC_ENOENT); + return HandleTransfer(device, request.request, + [&, this]() { return SubmitTransfer(*device, request); }); + } + default: + return GetDefaultReply(IPC_EINVAL); + } +} + +void USB_VEN::DoState(PointerWrap& p) +{ + p.Do(m_devicechange_first_call); + u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0; + p.Do(hook_address); + if (hook_address != 0) + m_devicechange_hook_request = std::make_unique(hook_address); + else + m_devicechange_hook_request.reset(); + + p.Do(m_device_number); + p.Do(m_ios_ids); + p.Do(m_device_ids); + USBHost::DoState(p); +} + +std::shared_ptr USB_VEN::GetDeviceByIOSID(const s32 ios_id) const +{ + std::lock_guard lk{m_id_map_mutex}; + const auto iter = m_ios_ids.find(ios_id); + if (iter == m_ios_ids.cend()) + return nullptr; + return GetDeviceById(iter->second); +} + +u8 USB_VEN::GetInterfaceNumber(const s32 ios_id) const +{ + const s32 id = Common::swap32(ios_id); + DeviceID device_id; + std::memcpy(&device_id, &id, sizeof(id)); + return device_id.interface_plus_1e - 0x1e; +} + +IPCCommandResult USB_VEN::CancelEndpoint(USB::Device& device, const IOCtlRequest& request) +{ + const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 2 * sizeof(s32))); + device.CancelTransfer(endpoint); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USB_VEN::GetDeviceChange(const IOCtlRequest& request) +{ + std::lock_guard lk{m_devicechange_hook_address_mutex}; + m_devicechange_hook_request = std::make_unique(request.address); + // On the first call, the reply is sent immediately (instead of on device insertion/removal) + if (m_devicechange_first_call) + { + TriggerDeviceChangeReply(); + m_devicechange_first_call = false; + } + return GetNoReply(); +} + +IPCCommandResult USB_VEN::GetDeviceInfo(USB::Device& device, const IOCtlRequest& request) +{ + const s32 device_id = Memory::Read_U32(request.buffer_in); + if (request.buffer_out == 0 || request.buffer_out_size != 0xc0) + return GetDefaultReply(IPC_EINVAL); + + const u8 alt_setting = Memory::Read_U8(request.buffer_in + 8); + auto descriptors = device.GetDescriptorsUSBV5(GetInterfaceNumber(device_id), alt_setting); + if (descriptors.empty()) + return GetDefaultReply(IPC_ENOENT); + + descriptors.resize(request.buffer_out_size - 20); + if (descriptors.size() > request.buffer_out_size - 20) + WARN_LOG(IOS_USB, "Buffer is too large. Only the first 172 bytes will be copied."); + + Memory::Memset(request.buffer_out, 0, request.buffer_out_size); + Memory::Write_U32(device_id, request.buffer_out); + Memory::Write_U32(1, request.buffer_out + 4); + Memory::CopyToEmu(request.buffer_out + 20, descriptors.data(), descriptors.size()); + + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USB_VEN::SetAlternateSetting(USB::Device& device, const IOCtlRequest& request) +{ + const s32 device_id = Memory::Read_U32(request.buffer_in); + if (!device.Attach(GetInterfaceNumber(device_id))) + return GetDefaultReply(-1); + + const u8 alt_setting = Memory::Read_U8(request.buffer_in + 2 * sizeof(s32)); + + const bool success = device.SetAltSetting(alt_setting) == 0; + return GetDefaultReply(success ? IPC_SUCCESS : IPC_EINVAL); +} + +IPCCommandResult USB_VEN::Shutdown(const IOCtlRequest& request) +{ + if (request.buffer_in != 0 || request.buffer_in_size != 0 || request.buffer_out != 0 || + request.buffer_out_size != 0) + { + return GetDefaultReply(IPC_EINVAL); + } + + std::lock_guard lk{m_devicechange_hook_address_mutex}; + if (m_devicechange_hook_request) + { + EnqueueReply(*m_devicechange_hook_request, IPC_SUCCESS); + m_devicechange_hook_request.reset(); + } + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USB_VEN::SuspendResume(USB::Device& device, const IOCtlRequest& request) +{ + const s32 device_id = Memory::Read_U32(request.buffer_in); + const s32 resumed = Memory::Read_U32(request.buffer_in + 2 * sizeof(s32)); + + // Note: this is unimplemented because there's no easy way to do this in a + // platform-independant way (libusb does not support power management). + INFO_LOG(IOS_USB, "[%04x:%04x %d] Received %s command", device.GetVid(), device.GetPid(), + GetInterfaceNumber(device_id), resumed == 0 ? "suspend" : "resume"); + return GetDefaultReply(IPC_SUCCESS); +} + +s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) +{ + switch (ioctlv.request) + { + case USB::IOCTLV_USBV5_CTRLMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_INTRMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_BULKMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_ISOMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + default: + return IPC_EINVAL; + } +} + +IPCCommandResult USB_VEN::HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler) +{ + if (request.buffer_in == 0 || request.buffer_in_size != 0x20) + return GetDefaultReply(IPC_EINVAL); + + const s32 device_id = Memory::Read_U32(request.buffer_in); + const auto device = GetDeviceByIOSID(device_id); + if (!device) + return GetDefaultReply(IPC_ENOENT); + return handler(this, *device, request); +} + +void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptr device) +{ + std::lock_guard id_map_lock{m_id_map_mutex}; + if (event == ChangeEvent::Inserted) + { + for (const auto& interface : device->GetInterfaces(0)) + { + if (interface.bAlternateSetting != 0) + continue; + + DeviceID id; + id.unknown = 0xe7; + id.interface_plus_1e = interface.bInterfaceNumber + 0x1e; + id.zero = 0x00; + id.counter = m_device_number; + + s32 ios_device_id = 0; + std::memcpy(&ios_device_id, &id, sizeof(id)); + ios_device_id = Common::swap32(ios_device_id); + m_ios_ids[ios_device_id] = device->GetId(); + m_device_ids[device->GetId()].insert(ios_device_id); + } + } + else if (event == ChangeEvent::Removed) + { + for (const s32 ios_id : m_device_ids[device->GetId()]) + m_ios_ids.erase(ios_id); + m_device_ids.erase(device->GetId()); + } +} + +void USB_VEN::OnDeviceChangeEnd() +{ + std::lock_guard lk{m_devicechange_hook_address_mutex}; + TriggerDeviceChangeReply(); + ++m_device_number; +} + +void USB_VEN::TriggerDeviceChangeReply() +{ + if (!m_devicechange_hook_request) + return; + + std::lock_guard id_map_lock{m_id_map_mutex}; + u8 num_devices = 0; + const size_t max_num = m_devicechange_hook_request->buffer_out_size / sizeof(DeviceEntry); + for (const auto& ios_device : m_ios_ids) + { + if (num_devices >= max_num) + { + WARN_LOG(IOS_USB, "Too many devices (%d ≥ %zu), skipping", num_devices, max_num); + break; + } + + const s32 ios_device_id = ios_device.first; + const auto device = GetDeviceById(m_ios_ids.at(ios_device_id)); + if (!device) + continue; + const u8 interface_number = GetInterfaceNumber(ios_device_id); + + // IOS's device list contains entries of the form: + // e7 XX 00 YY VV VV PP PP 00 YY DD AA + // ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^ ^^^^^ ^^ + // Device ID VID PID ?? See ID Number of alt settings + // + // XX is 1e (for a device plugged in to the left port) + DD (interface number). + // YY is a counter that starts at 21 and is incremented on every device change. + // DD is the interface number (since VEN exposes each interface as a separate device). + + DeviceEntry entry; + entry.device_id = Common::swap32(ios_device_id); + entry.vid = Common::swap16(device->GetVid()); + entry.pid = Common::swap16(device->GetPid()); + entry.unknown = 0x00; + entry.device_number = ios_device_id & 0xff; + entry.interface_number = interface_number; + entry.num_altsettings = device->GetNumberOfAltSettings(interface_number); + + Memory::CopyToEmu(m_devicechange_hook_request->buffer_out + sizeof(entry) * num_devices++, + &entry, sizeof(entry)); + } + + EnqueueReply(*m_devicechange_hook_request, num_devices, 0, CoreTiming::FromThread::ANY); + m_devicechange_hook_request.reset(); + INFO_LOG(IOS_USB, "%d device(s), including interfaces", num_devices); +} +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h new file mode 100644 index 0000000000..b35ea15384 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h @@ -0,0 +1,95 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/IPC.h" +#include "Core/IOS/USB/Host.h" + +class PointerWrap; + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +class USB_VEN final : public USBHost +{ +public: + USB_VEN(u32 device_id, const std::string& device_name); + ~USB_VEN() override; + + ReturnCode Open(const OpenRequest& request) override; + IPCCommandResult IOCtl(const IOCtlRequest& request) override; + IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; + + void DoState(PointerWrap& p) override; + +private: +#pragma pack(push, 1) + struct DeviceID + { + u8 unknown; + u8 interface_plus_1e; + u8 zero; + u8 counter; + }; + + struct DeviceEntry + { + s32 device_id; + u16 vid; + u16 pid; + u8 unknown; + u8 device_number; + u8 interface_number; + u8 num_altsettings; + }; +#pragma pack(pop) + + std::shared_ptr GetDeviceByIOSID(s32 ios_id) const; + u8 GetInterfaceNumber(s32 ios_id) const; + + IPCCommandResult CancelEndpoint(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult GetDeviceChange(const IOCtlRequest& request); + IPCCommandResult GetDeviceInfo(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult SetAlternateSetting(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult Shutdown(const IOCtlRequest& request); + IPCCommandResult SuspendResume(USB::Device& device, const IOCtlRequest& request); + s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request); + + using Handler = std::function; + IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler); + + void OnDeviceChange(ChangeEvent, std::shared_ptr) override; + void OnDeviceChangeEnd() override; + void TriggerDeviceChangeReply(); + + static constexpr u32 VERSION = 0x50001; + + bool m_devicechange_first_call = true; + std::mutex m_devicechange_hook_address_mutex; + std::unique_ptr m_devicechange_hook_request; + + mutable std::mutex m_id_map_mutex; + u8 m_device_number = 0x21; + // IOS device IDs => USB device IDs (one to one) + std::map m_ios_ids; + // USB device IDs => IOS device IDs (one to many, because VEN exposes one device per interface) + std::map> m_device_ids; +}; +} // namespace Device +} // namespace HLE +} // namespace IOS From 56fec3d72a602595580cad4c31c6abf5f9b929ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 24 Jan 2017 18:25:18 +0100 Subject: [PATCH 10/10] Switch to the usbdk backend for libusb on Windows The usbdk backend is the only libusb backend that has official support for isochronous transfers (which are required for Wii Speak, microphones and cameras). And it's actively developed and maintained. --- Externals/libusb/libusb_static_2013.vcxproj | 4 ++-- Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj | 2 +- Source/VSProps/Base.props | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Externals/libusb/libusb_static_2013.vcxproj b/Externals/libusb/libusb_static_2013.vcxproj index 2aac3e8db1..74397eb70d 100644 --- a/Externals/libusb/libusb_static_2013.vcxproj +++ b/Externals/libusb/libusb_static_2013.vcxproj @@ -52,7 +52,7 @@ - + @@ -65,7 +65,7 @@ - + diff --git a/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj b/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj index 67944bafb7..b02ad158f6 100644 --- a/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj +++ b/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj @@ -159,4 +159,4 @@ - \ No newline at end of file + diff --git a/Source/VSProps/Base.props b/Source/VSProps/Base.props index 0dbbdd59ff..768d03214b 100644 --- a/Source/VSProps/Base.props +++ b/Source/VSProps/Base.props @@ -51,7 +51,7 @@ $(ExternalsDir)xxhash;%(AdditionalIncludeDirectories) $(ExternalsDir)zlib;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) - USE_UPNP;__LIBUSB__;%(PreprocessorDefinitions) + USE_UPNP;USE_USBDK;__LIBUSB__;%(PreprocessorDefinitions) PSAPI_VERSION=1;_M_X86=1;%(PreprocessorDefinitions) SFML_STATIC;%(PreprocessorDefinitions) CURL_STATICLIB;%(PreprocessorDefinitions)