Skip to content

Instantly share code, notes, and snippets.

@ToadKing
Created December 18, 2014 05:52
Show Gist options
  • Save ToadKing/a5edb237cb3e4433c3f3 to your computer and use it in GitHub Desktop.
Save ToadKing/a5edb237cb3e4433c3f3 to your computer and use it in GitHub Desktop.
Wii U adapter emulator
// Super messy and adapted almost exclusively from libusb-gadget loopback example
/*
* Copyright (C) 2009 Daiki Ueno <ueno@unixuser.org>
* This file is part of libusb-gadget.
*
* libusb-gadget is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libusb-gadget is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include <poll.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <getopt.h>
//#include "config.h"
#include <linux/usb/ch9.h>
#include <usb-gadget.h>
#include <SDL2/SDL.h>
/* /dev/gadget/ep* doesn't support poll, we have to use an alternative
approach. */
#include <pthread.h>
struct hid_class_descriptor {
__u8 bDescriptorType;
__le16 wDescriptorLength;
} __attribute__ ((packed));
struct hid_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdHID;
__u8 bCountryCode;
__u8 bNumDescriptors;
struct hid_class_descriptor desc[1];
} __attribute__ ((packed));
#define STRING_MANUFACTURER 25
#define STRING_PRODUCT 45
#define STRING_SERIAL 101
#define STRING_LOOPBACK 0
static struct usb_gadget_string strings[] = {
{STRING_MANUFACTURER, "Nintendo",},
{STRING_PRODUCT, "WUP-028",},
{STRING_SERIAL, "15/07/2014",}
};
static struct usb_gadget_strings loopback_strings = {
.language = 0x0409, /* en-us */
.strings = strings
};
#define CONFIG_LOOPBACK 1
static struct usb_device_descriptor loopback_device_descriptor = {
.bLength = sizeof(loopback_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = usb_gadget_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.iManufacturer = usb_gadget_cpu_to_le16(STRING_MANUFACTURER),
.iProduct = usb_gadget_cpu_to_le16(STRING_PRODUCT),
.iSerialNumber = usb_gadget_cpu_to_le16(STRING_SERIAL),
.bNumConfigurations = 1,
};
static struct usb_config_descriptor loopback_config_descriptor = {
.bLength = sizeof(loopback_config_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.bNumInterfaces = 1,
.bConfigurationValue = CONFIG_LOOPBACK,
.iConfiguration = STRING_LOOPBACK,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1, /* self-powered */
};
static const struct usb_interface_descriptor loopback_interface_descriptor = {
.bLength = sizeof(loopback_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = 3,
.iInterface = STRING_LOOPBACK,
};
static const struct hid_descriptor loopback_hid_descriptor = {
.bLength = sizeof(loopback_hid_descriptor),
.bDescriptorType = 33,
.bcdHID = usb_gadget_cpu_to_le16(0x110),
.bCountryCode = 0,
.bNumDescriptors = 1,
.desc = {
{
.bDescriptorType = 34,
.wDescriptorLength = 214
}
}
};
static struct usb_endpoint_descriptor loopback_ep_in_descriptor = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81, /* number is mandatory for gadgetfs */
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = usb_gadget_cpu_to_le16(0x25), /* mandatory for gadgetfs */
};
static struct usb_endpoint_descriptor loopback_ep_out_descriptor = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x02, /* number is mandatory for gadgetfs */
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = usb_gadget_cpu_to_le16(0x05), /* mandatory for gadgetfs */
};
static struct usb_endpoint_descriptor loopback_hs_ep_in_descriptor = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81, /* number is mandatory for gadgetfs */
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = usb_gadget_cpu_to_le16(0x25), /* mandatory for gadgetfs */
};
static struct usb_endpoint_descriptor loopback_hs_ep_out_descriptor = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x02, /* number is mandatory for gadgetfs */
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = usb_gadget_cpu_to_le16(0x05), /* mandatory for gadgetfs */
};
static struct usb_descriptor_header *loopback_config[] = {
(struct usb_descriptor_header *)&loopback_config_descriptor,
(struct usb_descriptor_header *)&loopback_interface_descriptor,
(struct usb_descriptor_header *)&loopback_hid_descriptor,
(struct usb_descriptor_header *)&loopback_ep_in_descriptor,
(struct usb_descriptor_header *)&loopback_ep_out_descriptor,
NULL,
};
static struct usb_descriptor_header *loopback_hs_config[] = {
(struct usb_descriptor_header *)&loopback_config_descriptor,
(struct usb_descriptor_header *)&loopback_interface_descriptor,
(struct usb_descriptor_header *)&loopback_hid_descriptor,
(struct usb_descriptor_header *)&loopback_hs_ep_in_descriptor,
(struct usb_descriptor_header *)&loopback_hs_ep_out_descriptor,
NULL,
};
static struct usb_gadget_endpoint *loopback_ep_in, *loopback_ep_out;
static pthread_t loopback_thread_read, loopback_thread_write;
static SDL_GameController *controller;
static void
loopback_stop_endpoints_read (void *data)
{
usb_gadget_endpoint_close (loopback_ep_out);
}
static void
loopback_stop_endpoints_write (void *data)
{
usb_gadget_endpoint_close (loopback_ep_in);
}
static void*
loopback_loop_read (void *data)
{
char buf[BUFSIZ];
int ret;
pthread_cleanup_push (loopback_stop_endpoints_read, NULL);
while (1)
{
pthread_testcancel ();
ret = usb_gadget_endpoint_read (loopback_ep_out, buf, 5, 0);
if (ret < 0)
{
//perror ("usb_gadget_endpoint_read");
//break;
}
}
pthread_cleanup_pop (1);
}
static void*
loopback_loop_write (void *data)
{
char buf[BUFSIZ];
int ret;
pthread_cleanup_push (loopback_stop_endpoints_write, NULL);
while (1)
{
pthread_testcancel ();
SDL_GameControllerUpdate();
Sint16 lx = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
Sint16 ly = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) ^ 0xFFFF;
Sint16 rx = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
Sint16 ry = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) ^ 0xFFFF;
Sint16 tl = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
Sint16 tr = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
Uint8 a = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A);
Uint8 b = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X);
Uint8 x = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_B);
Uint8 y = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_Y);
Uint8 start = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START);
Uint8 up = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP);
Uint8 down = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
Uint8 left = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
Uint8 right = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
Uint8 z = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
Uint8 tl_d = tl > 20000 ? 1 : 0;
Uint8 tr_d = tr > 20000 ? 1 : 0;
unsigned char payload[0x25] = {0x21, 0x10};
payload[4] = ((lx + 32768) & 0xFF00) >> 8;
payload[5] = ((ly + 32768) & 0xFF00) >> 8;
payload[6] = ((rx + 32768) & 0xFF00) >> 8;
payload[7] = ((ry + 32768) & 0xFF00) >> 8;
payload[8] = ((tl + 32768) & 0xFF00) >> 8;
payload[9] = ((tr + 32768) & 0xFF00) >> 8;
payload[2] |= a;
payload[2] |= x << 1;
payload[2] |= b << 2;
payload[2] |= y << 3;
payload[2] |= left << 4;
payload[2] |= right << 5;
payload[2] |= down << 6;
payload[2] |= up << 7;
payload[3] |= start;
payload[3] |= z << 1;
payload[3] |= tr_d << 2;
payload[3] |= tl_d << 3;
usb_gadget_endpoint_write (loopback_ep_in, payload, 0x25, 100);
}
pthread_cleanup_pop (1);
}
static void
loopback_event_cb (usb_gadget_dev_handle *handle, struct usb_gadget_event *event, void *arg)
{
switch (event->type)
{
case USG_EVENT_ENDPOINT_ENABLE:
fprintf(stderr, "enable\n");
if (event->u.number == (loopback_ep_in_descriptor.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK))
loopback_ep_in = usb_gadget_endpoint (handle, event->u.number);
else if (event->u.number == (loopback_ep_out_descriptor.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK))
loopback_ep_out = usb_gadget_endpoint (handle, event->u.number);
if (!loopback_ep_in || !loopback_ep_out)
return;
if (pthread_create (&loopback_thread_read, 0, loopback_loop_read, NULL) != 0)
perror ("pthread_create loopback_loop_read");
if (pthread_create (&loopback_thread_write, 0, loopback_loop_write, NULL) != 0)
perror ("pthread_create loopback_loop_write");
break;
case USG_EVENT_ENDPOINT_DISABLE:
fprintf(stderr, "disable\n");
if (event->u.number == (loopback_ep_in_descriptor.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK))
loopback_ep_in = NULL;
else if (event->u.number == (loopback_ep_out_descriptor.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK))
loopback_ep_out = NULL;
case USG_EVENT_DISCONNECT: /* FALLTHROUGH */
if (loopback_thread_read)
pthread_cancel (loopback_thread_read);
if (loopback_thread_write)
pthread_cancel (loopback_thread_write);
break;
}
}
static char *program_name;
static void
usage (FILE *out)
{
fprintf (out, "Usage: %s [OPTIONS]\n"
"Options are:\n"
"\t--debug=LEVEL, -d\tSpecify debug level\n"
"\t--help, -h\tShow this help\n",
program_name);
}
int main (int argc, char **argv)
{
int i;
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
for (i = 0; i < SDL_NumJoysticks(); ++i) {
if ( SDL_IsGameController(i) )
{
controller = SDL_GameControllerOpen(i);
if (controller)
break;
}
}
if (!controller)
{
fprintf(stderr, "could not open controller\n");
return 1;
}
struct usb_gadget_device device = {
.device = &loopback_device_descriptor,
.config = loopback_config,
.hs_config = loopback_hs_config,
.strings = &loopback_strings,
};
usb_gadget_dev_handle *handle;
struct usb_gadget_endpoint *ep0;
struct pollfd fds;
int vendor_id, product_id, c, debug_level = 0;
struct option long_options[] = {
{"debug", 1, 0, 'd'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
program_name = argv[0];
while (1)
{
int option_index = 0;
c = getopt_long (argc, argv, "hd:", long_options, &option_index);
if (c == -1)
break;
switch (c)
{
case 'd':
debug_level = atoi (optarg);
break;
case 'h':
usage (stdout);
exit (0);
default:
usage (stderr);
exit (1);
}
}
loopback_device_descriptor.idVendor = 0x057e;
loopback_device_descriptor.idProduct = 0x0337;
handle = usb_gadget_open (&device);
if (!handle)
{
fprintf (stderr, "Couldn't open device.\n");
exit (1);
}
usb_gadget_set_event_cb (handle, loopback_event_cb, NULL);
usb_gadget_set_debug_level (handle, debug_level);
ep0 = usb_gadget_endpoint (handle, 0);
fds.fd = usb_gadget_control_fd (handle);
fds.events = POLLIN;
while (1)
{
if (poll (&fds, 1, -1) < 0)
{
perror ("poll");
break;
}
if (fds.revents & POLLIN)
usb_gadget_handle_control_event (handle);
}
usb_gadget_close (handle);
return 0;
}
@TheGreatRambler
Copy link

Cool beans

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment