Skip to content

Instantly share code, notes, and snippets.

@veryjos
Created November 9, 2017 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save veryjos/7625dc0aa5ba6aaee6cd59ea017ccc21 to your computer and use it in GitHub Desktop.
Save veryjos/7625dc0aa5ba6aaee6cd59ea017ccc21 to your computer and use it in GitHub Desktop.
#include "Project_Combo.h"
#include "GCAdapter.h"
#define ADAPTER_VENDOR_ID 0x057e
#define ADAPTER_PRODUCT_ID 0x0337
GCAdapter::GCAdapter() {
// Clear controller plugged states
for (int i = 0; i < 4; i++) {
controllers[i].connected = GCPadState::STATE_NONE;
controllers[i].portNum = i;
}
handle = nullptr;
// Init libusb
int ret = libusb_init(&libusb_context);
if (ret)
{
UE_LOG(LogGCAdapter, Error, TEXT("Failed to init libusb"));
libusb_context = nullptr;
return;
}
else
{
// Enumerate USB devices
libusb_device** deviceList;
ssize_t cnt = libusb_get_device_list(libusb_context, &deviceList);
// Find GC adapters
for (int i = 0; i < cnt; i++)
{
libusb_device* device = deviceList[i];
libusb_device_descriptor desc;
ret = libusb_get_device_descriptor(device, &desc);
// Skip over if the device can't be read
if (ret)
continue;
if (desc.idVendor == ADAPTER_VENDOR_ID &&
desc.idProduct == ADAPTER_PRODUCT_ID)
{
UE_LOG(LogGCAdapter, Log, TEXT("Found GC Adapter"));
uint8 bus = libusb_get_bus_number(device);
uint8 port = libusb_get_device_address(device);
ret = libusb_open(device, &handle);
if (ret)
{
UE_LOG(LogGCAdapter, Warning, TEXT("Can't access GCAdapter device: Bus %03d Device %03d"), bus, port);
continue;
}
// Detach the kernel driver
if ((ret = libusb_kernel_driver_active(handle, 0)) == 1)
{
if ((ret = libusb_detach_kernel_driver(handle, 0)) != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
{
UE_LOG(LogGCAdapter, Error, TEXT("libusb_detach_kernel_driver failed, error: %d"), ret);
}
}
if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
{
continue;
}
else if ((ret = libusb_claim_interface(handle, 0)) != 0)
{
UE_LOG(LogGCAdapter, Error, TEXT("libusb_claim_interface failed, error: %d"), ret);
}
else
{
UE_LOG(LogGCAdapter, Log, TEXT("Successfully opened GC device"));
LinkGCAdapter(device);
break;
}
}
}
libusb_free_device_list(deviceList, cnt);
}
}
GCAdapter::~GCAdapter() {
if (libusb_context) {
UE_LOG(LogGCAdapter, Log, TEXT("Shutting down libusb.."));
// Close the libusb device
if (handle) {
libusb_release_interface(handle, 0);
libusb_close(handle);
}
// Exit the libusb context
libusb_exit(libusb_context);
}
}
void GCAdapter::LinkGCAdapter(libusb_device* device)
{
libusb_config_descriptor* config = nullptr;
libusb_get_config_descriptor(device, 0, &config);
for (uint8 ic = 0; ic < config->bNumInterfaces; ic++)
{
const libusb_interface* interfaceContainer = &config->interface[ic];
for (int i = 0; i < interfaceContainer->num_altsetting; i++)
{
const libusb_interface_descriptor *interface = &interfaceContainer->altsetting[i];
for (uint8 e = 0; e < interface->bNumEndpoints; e++)
{
const libusb_endpoint_descriptor *endpoint = &interface->endpoint[e];
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN)
endpoint_in = endpoint->bEndpointAddress;
else
endpoint_out = endpoint->bEndpointAddress;
}
}
}
int tmp = 0;
// This packet starts comms with the GC controllers
unsigned char payload = 0x13;
libusb_interrupt_transfer(handle, endpoint_out, &payload, sizeof(payload), &tmp, 16);
UE_LOG(LogGCAdapter, Log, TEXT("Linking GCAdapter.."));
libusb_free_config_descriptor(config);
}
void GCAdapter::Read()
{
if (!handle)
return;
int payloadSize;
libusb_interrupt_transfer(handle, endpoint_in, payload_in, sizeof(payload_in), &payloadSize, 16);
if (payloadSize != PAYLOAD_IN_SIZE)
{
UE_LOG(LogGCAdapter, Warning, TEXT("Error reading payload from GCAdapter"));
}
else
{
// Read all four controller states
for (int padNum = 0; padNum < 4; padNum++)
{
GCPad& pad = GetPad(padNum);
uint8 type = payload_in[1 + (9 * padNum)] >> 4;
if (type != GCPadState::STATE_NONE && !pad.connected)
{
UE_LOG(LogGCAdapter, Log, TEXT("New GC controller connected to port %d"), padNum);
m_justConnectedPads.push_back(&pad);
}
else if (type == GCPadState::STATE_NONE && pad.connected)
{
UE_LOG(LogGCAdapter, Log, TEXT("GC controller disconnected from port %d"), padNum);
m_justDisconnectedPads.push_back(&pad);
}
pad.connected = (type != GCPadState::STATE_NONE);
if (pad.connected)
{
// Clear pad button states
memset(&pad.buttons, 0, sizeof(pad.buttons));
uint8 b1 = payload_in[1 + (9 * padNum) + 1];
uint8 b2 = payload_in[1 + (9 * padNum) + 2];
if (b1 & (1 << 0)) pad.buttons |= GCPAD_BUTTON_A;
if (b1 & (1 << 1)) pad.buttons |= GCPAD_BUTTON_B;
if (b1 & (1 << 2)) pad.buttons |= GCPAD_BUTTON_X;
if (b1 & (1 << 3)) pad.buttons |= GCPAD_BUTTON_Y;
if (b1 & (1 << 4)) pad.buttons |= GCPAD_BUTTON_LEFT;
if (b1 & (1 << 5)) pad.buttons |= GCPAD_BUTTON_RIGHT;
if (b1 & (1 << 6)) pad.buttons |= GCPAD_BUTTON_DOWN;
if (b1 & (1 << 7)) pad.buttons |= GCPAD_BUTTON_UP;
if (b2 & (1 << 0)) pad.buttons |= GCPAD_BUTTON_START;
if (b2 & (1 << 1)) pad.buttons |= GCPAD_BUTTON_Z;
if (b2 & (1 << 2)) pad.buttons |= GCPAD_BUTTON_DIGITAL_R;
if (b2 & (1 << 3)) pad.buttons |= GCPAD_BUTTON_DIGITAL_L;
pad.stickX = payload_in[1 + (9 * padNum) + 3];
pad.stickY = payload_in[1 + (9 * padNum) + 4];
pad.cStickX = payload_in[1 + (9 * padNum) + 5];
pad.cStickY = payload_in[1 + (9 * padNum) + 6];
pad.lTrigger = payload_in[1 + (9 * padNum) + 7];
pad.rTrigger = payload_in[1 + (9 * padNum) + 8];
// Calculate new button presses
pad.pressed = (pad.buttons ^ pad.oldButtons) & pad.buttons;
// Calculate new button releases
pad.released = (pad.buttons ^ pad.oldButtons) & pad.oldButtons;
pad.oldButtons = pad.buttons;
}
}
}
}
GCPad& GCAdapter::GetPad(uint8 pad)
{
return controllers[pad];
}
std::vector<GCPad*> GCAdapter::GetJustConnectedPads()
{
return m_justConnectedPads;
}
std::vector<GCPad*> GCAdapter::GetJustDisconnectedPads()
{
return m_justDisconnectedPads;
}
#pragma once
#include <cstdint>
#include "AllowWindowsPlatformTypes.h"
#include "libusb-1.0/libusb.h"
#include "HideWindowsPlatformTypes.h"
#include <vector>
#define PAYLOAD_IN_SIZE 37
static const uint32_t GCPAD_BUTTON_A = (1 << 0);
static const uint32_t GCPAD_BUTTON_B = (1 << 1);
static const uint32_t GCPAD_BUTTON_X = (1 << 2);
static const uint32_t GCPAD_BUTTON_Y = (1 << 3);
static const uint32_t GCPAD_BUTTON_LEFT = (1 << 4);
static const uint32_t GCPAD_BUTTON_RIGHT = (1 << 5);
static const uint32_t GCPAD_BUTTON_DOWN = (1 << 6);
static const uint32_t GCPAD_BUTTON_UP = (1 << 7);
static const uint32_t GCPAD_BUTTON_START = (1 << 8);
static const uint32_t GCPAD_BUTTON_Z = (1 << 9);
static const uint32_t GCPAD_BUTTON_DIGITAL_L = (1 << 10);
static const uint32_t GCPAD_BUTTON_DIGITAL_R = (1 << 11);
enum GCPadState
{
STATE_NONE,
STATE_WIRED,
STATE_WIRELESS
};
struct GCPad {
bool connected;
uint8_t portNum;
uint32_t buttons;
uint32_t newButtons;
uint32_t oldButtons;
uint32_t pressed;
uint32_t released;
uint8_t stickX;
uint8_t stickY;
uint8_t cStickX;
uint8_t cStickY;
uint8_t lTrigger;
uint8_t rTrigger;
};
class GCAdapter {
public:
GCAdapter();
~GCAdapter();
void Read();
GCPad& GetPad(uint8_t padNum);
std::vector<GCPad*> GetJustConnectedPads();
std::vector<GCPad*> GetJustDisconnectedPads();
private:
void LinkGCAdapter(libusb_device* device);
GCPad controllers[4];
uint8_t endpoint_in;
uint8_t endpoint_out;
uint8_t payload_in[PAYLOAD_IN_SIZE];
libusb_device_handle* handle;
libusb_context* libusb_context;
std::vector<GCPad*> m_justConnectedPads;
std::vector<GCPad*> m_justDisconnectedPads;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment