[HID] Added support for Xbox 360 Skylanders Portal
- This requires Zadig for installation of WinUSB driver for portal
This commit is contained in:
committed by
Radosław Gliński
parent
7667958556
commit
7298536d46
23
src/xenia/hid/skylander/premake5.lua
Normal file
23
src/xenia/hid/skylander/premake5.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
project_root = "../../../.."
|
||||
include(project_root.."/tools/build")
|
||||
|
||||
group("src")
|
||||
project("xenia-hid-skylander")
|
||||
uuid("ddc114da-a279-4868-8b20-53108599bd78")
|
||||
kind("StaticLib")
|
||||
language("C++")
|
||||
files({
|
||||
"skylander_portal.h",
|
||||
"skylander_portal.cc",
|
||||
"skylander_emulated.h",
|
||||
"skylander_emulated.cc"
|
||||
})
|
||||
|
||||
filter({"platforms:Windows"})
|
||||
links({
|
||||
"libusb",
|
||||
})
|
||||
files({
|
||||
"skylander_hardware.h",
|
||||
"skylander_hardware.cc"
|
||||
})
|
||||
32
src/xenia/hid/skylander/skylander_emulated.cc
Normal file
32
src/xenia/hid/skylander/skylander_emulated.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/hid/skylander/skylander_emulated.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
SkylanderPortalEmulated::SkylanderPortalEmulated() : SkylanderPortal() {}
|
||||
|
||||
SkylanderPortalEmulated::~SkylanderPortalEmulated() {}
|
||||
|
||||
bool SkylanderPortalEmulated::init_device() { return false; }
|
||||
|
||||
void SkylanderPortalEmulated::destroy_device() {}
|
||||
|
||||
X_STATUS SkylanderPortalEmulated::read(std::vector<uint8_t>& data) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
X_STATUS SkylanderPortalEmulated::write(std::vector<uint8_t>& data) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
34
src/xenia/hid/skylander/skylander_emulated.h
Normal file
34
src/xenia/hid/skylander/skylander_emulated.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_EMULATED_H_
|
||||
#define XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_EMULATED_H_
|
||||
|
||||
#include "xenia/hid/skylander/skylander_portal.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
class SkylanderPortalEmulated final : public SkylanderPortal {
|
||||
public:
|
||||
SkylanderPortalEmulated();
|
||||
~SkylanderPortalEmulated() override;
|
||||
|
||||
X_STATUS read(std::vector<uint8_t>& data) override;
|
||||
X_STATUS write(std::vector<uint8_t>& data) override;
|
||||
|
||||
private:
|
||||
bool init_device() override;
|
||||
void destroy_device() override;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
121
src/xenia/hid/skylander/skylander_hardware.cc
Normal file
121
src/xenia/hid/skylander/skylander_hardware.cc
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/hid/skylander/skylander_hardware.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
SkylanderPortalLibusb::SkylanderPortalLibusb() : SkylanderPortal() {
|
||||
libusb_init(&context_);
|
||||
}
|
||||
|
||||
SkylanderPortalLibusb::~SkylanderPortalLibusb() {
|
||||
if (handle_) {
|
||||
destroy_device();
|
||||
}
|
||||
|
||||
libusb_exit(context_);
|
||||
}
|
||||
|
||||
bool SkylanderPortalLibusb::init_device() {
|
||||
if (!context_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_device** devs;
|
||||
ssize_t cnt = libusb_get_device_list(context_, &devs);
|
||||
if (cnt < 0) {
|
||||
// No device available... It might appear later.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_found = false;
|
||||
for (ssize_t i = 0; i < cnt; ++i) {
|
||||
libusb_device* dev = devs[i];
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(dev, &desc) == 0) {
|
||||
// Check if this device matches the target Vendor ID and Product ID
|
||||
if (desc.idVendor == portal_vendor_product_id.first &&
|
||||
desc.idProduct == portal_vendor_product_id.second) {
|
||||
libusb_device_handle* handle;
|
||||
if (libusb_open(dev, &handle) == 0) {
|
||||
libusb_claim_interface(handle, 0);
|
||||
handle_ = reinterpret_cast<uintptr_t*>(handle);
|
||||
device_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devs, 0);
|
||||
|
||||
return device_found;
|
||||
}
|
||||
|
||||
void SkylanderPortalLibusb::destroy_device() {
|
||||
libusb_release_interface(reinterpret_cast<libusb_device_handle*>(handle_), 0);
|
||||
libusb_close(reinterpret_cast<libusb_device_handle*>(handle_));
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
X_STATUS SkylanderPortalLibusb::read(std::vector<uint8_t>& data) {
|
||||
if (!is_initialized()) {
|
||||
if (!init_device()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.size() > skylander_buffer_size) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const int result = libusb_interrupt_transfer(
|
||||
reinterpret_cast<libusb_device_handle*>(handle_), read_endpoint,
|
||||
data.data(), static_cast<int>(data.size()), nullptr, timeout);
|
||||
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
destroy_device();
|
||||
}
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS SkylanderPortalLibusb::write(std::vector<uint8_t>& data) {
|
||||
if (!is_initialized()) {
|
||||
if (!init_device()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.size() > skylander_buffer_size) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const int result = libusb_interrupt_transfer(
|
||||
reinterpret_cast<libusb_device_handle*>(handle_), write_endpoint,
|
||||
data.data(), static_cast<int>(data.size()), nullptr, timeout);
|
||||
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
destroy_device();
|
||||
}
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
47
src/xenia/hid/skylander/skylander_hardware.h
Normal file
47
src/xenia/hid/skylander/skylander_hardware.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_LIBUSB_H_
|
||||
#define XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_LIBUSB_H_
|
||||
|
||||
// Include order is important here. Including skylander_portal.h after libusb
|
||||
// will cause conflicts.
|
||||
#include "xenia/hid/skylander/skylander_portal.h"
|
||||
|
||||
#include "third_party/libusb/libusb/libusb.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
class SkylanderPortalLibusb final : public SkylanderPortal {
|
||||
public:
|
||||
SkylanderPortalLibusb();
|
||||
~SkylanderPortalLibusb() override;
|
||||
|
||||
X_STATUS read(std::vector<uint8_t>& data) override;
|
||||
X_STATUS write(std::vector<uint8_t>& data) override;
|
||||
|
||||
private:
|
||||
bool init_device() override;
|
||||
void destroy_device() override;
|
||||
|
||||
bool is_initialized() const { return handle_ != nullptr; }
|
||||
|
||||
const uint8_t read_endpoint = 0x81;
|
||||
const uint8_t write_endpoint = 0x02;
|
||||
const uint16_t timeout = 300;
|
||||
|
||||
libusb_context* context_ = nullptr;
|
||||
uintptr_t* handle_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
19
src/xenia/hid/skylander/skylander_portal.cc
Normal file
19
src/xenia/hid/skylander/skylander_portal.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/hid/skylander/skylander_portal.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
SkylanderPortal::SkylanderPortal() {}
|
||||
SkylanderPortal::~SkylanderPortal() {}
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
40
src/xenia/hid/skylander/skylander_portal.h
Normal file
40
src/xenia/hid/skylander/skylander_portal.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_H_
|
||||
#define XENIA_HID_SKYLANDER_SKYLANDER_PORTAL_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
constexpr std::pair<uint16_t, uint16_t> portal_vendor_product_id = {0x1430,
|
||||
0x1F17};
|
||||
constexpr uint8_t skylander_buffer_size = 0x20;
|
||||
|
||||
class SkylanderPortal {
|
||||
public:
|
||||
SkylanderPortal();
|
||||
virtual ~SkylanderPortal();
|
||||
|
||||
virtual X_STATUS read(std::vector<uint8_t>& data) = 0;
|
||||
virtual X_STATUS write(std::vector<uint8_t>& data) = 0;
|
||||
|
||||
private:
|
||||
virtual bool init_device() = 0;
|
||||
virtual void destroy_device() = 0;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user