|
|
|
@ -14,6 +14,7 @@
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
|
|
#include <libudev.h>
|
|
|
|
|
#include <libusb.h>
|
|
|
|
@ -83,21 +84,20 @@ struct ports
|
|
|
|
|
|
|
|
|
|
struct adapter
|
|
|
|
|
{
|
|
|
|
|
volatile bool quitting;
|
|
|
|
|
struct libusb_device *device;
|
|
|
|
|
struct libusb_device_handle *handle;
|
|
|
|
|
pthread_t thread;
|
|
|
|
|
unsigned char rumble[5];
|
|
|
|
|
struct ports controllers[4];
|
|
|
|
|
struct adapter *next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool raw_mode;
|
|
|
|
|
|
|
|
|
|
static volatile int quitting;
|
|
|
|
|
|
|
|
|
|
static pthread_mutex_t adapter_mutex;
|
|
|
|
|
|
|
|
|
|
static struct adapter *adapters;
|
|
|
|
|
static volatile int adapters_size;
|
|
|
|
|
static struct adapter adapters;
|
|
|
|
|
|
|
|
|
|
static const char *uinput_path;
|
|
|
|
|
|
|
|
|
@ -173,10 +173,17 @@ static bool uinput_create(int i, struct ports *port, unsigned char type)
|
|
|
|
|
snprintf(uinput_dev.name, sizeof(uinput_dev.name), "Wii U GameCube Adapter Port %d", i+1);
|
|
|
|
|
uinput_dev.name[sizeof(uinput_dev.name)-1] = 0;
|
|
|
|
|
uinput_dev.id.bustype = BUS_USB;
|
|
|
|
|
write(port->uinput, &uinput_dev, sizeof(uinput_dev));
|
|
|
|
|
if (write(port->uinput, &uinput_dev, sizeof(uinput_dev)) != sizeof(uinput_dev))
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "error writing uinput device settings");
|
|
|
|
|
close(port->uinput);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ioctl(port->uinput, UI_DEV_CREATE) != 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "error creating uinput device");
|
|
|
|
|
close(port->uinput);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
port->type = type;
|
|
|
|
@ -192,35 +199,6 @@ static void uinput_destroy(int i, struct ports *port)
|
|
|
|
|
port->connected = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: only call the following functions when the adapter mutex is owned, they are not thread safe otherwise
|
|
|
|
|
static void realloc_adapters(void)
|
|
|
|
|
{
|
|
|
|
|
if (adapters_size > 0)
|
|
|
|
|
{
|
|
|
|
|
adapters = realloc(adapters, sizeof(struct adapter) * adapters_size);
|
|
|
|
|
|
|
|
|
|
if (adapters == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "FATAL: realloc() failed");
|
|
|
|
|
exit(-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int find_adapter(struct libusb_device *device, int guess)
|
|
|
|
|
{
|
|
|
|
|
if (guess >= 0 && guess < adapters_size && adapters[guess].device == device)
|
|
|
|
|
return guess;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < adapters_size; i++)
|
|
|
|
|
{
|
|
|
|
|
if (adapters[i].device == device)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct timespec ts_add(struct timespec *start, int milliseconds)
|
|
|
|
|
{
|
|
|
|
|
struct timespec ret = *start;
|
|
|
|
@ -371,7 +349,8 @@ static void handle_payload(int i, struct ports *port, unsigned char *payload, st
|
|
|
|
|
events[e_count].type = EV_SYN;
|
|
|
|
|
events[e_count].code = SYN_REPORT;
|
|
|
|
|
e_count++;
|
|
|
|
|
write(port->uinput, events, sizeof(events[0]) * e_count);
|
|
|
|
|
if (write(port->uinput, events, sizeof(events[0]) * e_count) != (int)sizeof(events[0]) * e_count)
|
|
|
|
|
fprintf(stderr, "Warning: writing input events failed\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check for rumble events
|
|
|
|
@ -426,26 +405,13 @@ static void handle_payload(int i, struct ports *port, unsigned char *payload, st
|
|
|
|
|
|
|
|
|
|
static void *adapter_thread(void *data)
|
|
|
|
|
{
|
|
|
|
|
struct libusb_device *device = (struct libusb_device *)data;
|
|
|
|
|
int i = -1;
|
|
|
|
|
struct adapter *a = (struct adapter *)data;
|
|
|
|
|
|
|
|
|
|
while (!quitting)
|
|
|
|
|
while (!a->quitting)
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_lock(&adapter_mutex);
|
|
|
|
|
i = find_adapter(device, i);
|
|
|
|
|
// we were removed, abort
|
|
|
|
|
if (i < 0)
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct adapter a = adapters[i];
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
unsigned char payload[37];
|
|
|
|
|
int size = 0;
|
|
|
|
|
libusb_interrupt_transfer(a.handle, EP_IN, payload, sizeof(payload), &size, 0);
|
|
|
|
|
libusb_interrupt_transfer(a->handle, EP_IN, payload, sizeof(payload), &size, 0);
|
|
|
|
|
if (size != 37 || payload[0] != 0x21)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
@ -456,13 +422,13 @@ static void *adapter_thread(void *data)
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
|
|
|
|
for (int i = 0; i < 4; i++, controller += 9)
|
|
|
|
|
{
|
|
|
|
|
handle_payload(i, &a.controllers[i], controller, ¤t_time);
|
|
|
|
|
handle_payload(i, &a->controllers[i], controller, ¤t_time);
|
|
|
|
|
rumble[i+1] = 0;
|
|
|
|
|
if (a.controllers[i].extra_power && a.controllers[i].type == STATE_NORMAL)
|
|
|
|
|
if (a->controllers[i].extra_power && a->controllers[i].type == STATE_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
for (int j = 0; j < MAX_FF_EVENTS; j++)
|
|
|
|
|
{
|
|
|
|
|
struct ff_event *e = &a.controllers[i].ff_events[j];
|
|
|
|
|
struct ff_event *e = &a->controllers[i].ff_events[j];
|
|
|
|
|
if (e->in_use)
|
|
|
|
|
{
|
|
|
|
|
if (ts_lessthan(&e->start_time, ¤t_time) && ts_greaterthan(&e->end_time, ¤t_time))
|
|
|
|
@ -474,93 +440,107 @@ static void *adapter_thread(void *data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memcmp(rumble, a.rumble, sizeof(rumble)) != 0)
|
|
|
|
|
if (memcmp(rumble, a->rumble, sizeof(rumble)) != 0)
|
|
|
|
|
{
|
|
|
|
|
memcpy(a.rumble, rumble, sizeof(rumble));
|
|
|
|
|
libusb_interrupt_transfer(a.handle, EP_OUT, a.rumble, sizeof(a.rumble), &size, 0);
|
|
|
|
|
memcpy(a->rumble, rumble, sizeof(rumble));
|
|
|
|
|
libusb_interrupt_transfer(a->handle, EP_OUT, a->rumble, sizeof(a->rumble), &size, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&adapter_mutex);
|
|
|
|
|
i = find_adapter(device, i);
|
|
|
|
|
// we were removed, abort
|
|
|
|
|
if (i < 0)
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
adapters[i] = a;
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
if (a->controllers[i].connected)
|
|
|
|
|
uinput_destroy(i, &a->controllers[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data)
|
|
|
|
|
static void add_adapter(struct libusb_device *dev)
|
|
|
|
|
{
|
|
|
|
|
(void)ctx;
|
|
|
|
|
(void)user_data;
|
|
|
|
|
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
|
|
|
|
struct adapter *a = (struct adapter *)calloc(1, sizeof(struct adapter));
|
|
|
|
|
if (a == NULL)
|
|
|
|
|
{
|
|
|
|
|
struct adapter a = { 0 };
|
|
|
|
|
a.device = dev;
|
|
|
|
|
fprintf(stderr, "FATAL: calloc() failed");
|
|
|
|
|
exit(-1);
|
|
|
|
|
}
|
|
|
|
|
a->device = dev;
|
|
|
|
|
|
|
|
|
|
if (libusb_open(a.device, &a.handle) != 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Error opening device 0x%p\n", a.device);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (libusb_open(a->device, &a->handle) != 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Error opening device 0x%p\n", a->device);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (libusb_kernel_driver_active(a->handle, 0) == 1 && libusb_detach_kernel_driver(a->handle, 0))
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Error detaching handle 0x%p from kernel\n", a->handle);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tmp;
|
|
|
|
|
unsigned char payload[1] = { 0x13 };
|
|
|
|
|
libusb_interrupt_transfer(a->handle, EP_OUT, payload, sizeof(payload), &tmp, 0);
|
|
|
|
|
|
|
|
|
|
struct adapter *old_head = adapters.next;
|
|
|
|
|
adapters.next = a;
|
|
|
|
|
a->next = old_head;
|
|
|
|
|
|
|
|
|
|
pthread_create(&a->thread, NULL, adapter_thread, a);
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "adapter 0x%p connected\n", a->device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (libusb_kernel_driver_active(a.handle, 0) == 1 && libusb_detach_kernel_driver(a.handle, 0))
|
|
|
|
|
static void remove_adapter(struct libusb_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct adapter *a = &adapters;
|
|
|
|
|
while (a->next != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (a->next->device == dev)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Error detaching handle 0x%p from kernel\n", a.handle);
|
|
|
|
|
return 0;
|
|
|
|
|
a->next->quitting = true;
|
|
|
|
|
pthread_join(a->next->thread, NULL);
|
|
|
|
|
fprintf(stderr, "adapter 0x%p disconnected\n", a->next->device);
|
|
|
|
|
libusb_close(a->next->handle);
|
|
|
|
|
struct adapter *new_next = a->next->next;
|
|
|
|
|
free(a->next);
|
|
|
|
|
a->next = new_next;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tmp;
|
|
|
|
|
unsigned char payload[1] = { 0x13 };
|
|
|
|
|
libusb_interrupt_transfer(a.handle, EP_OUT, payload, sizeof(payload), &tmp, 0);
|
|
|
|
|
pthread_mutex_lock(&adapter_mutex);
|
|
|
|
|
adapters_size++;
|
|
|
|
|
realloc_adapters();
|
|
|
|
|
adapters[adapters_size-1] = a;
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
|
|
|
|
|
pthread_create(&a.thread, NULL, adapter_thread, a.device);
|
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "adapter 0x%p connected\n", a.device);
|
|
|
|
|
static int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
(void)ctx;
|
|
|
|
|
(void)user_data;
|
|
|
|
|
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
|
|
|
|
{
|
|
|
|
|
add_adapter(dev);
|
|
|
|
|
}
|
|
|
|
|
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_lock(&adapter_mutex);
|
|
|
|
|
int i = find_adapter(dev, -1);
|
|
|
|
|
struct adapter a;
|
|
|
|
|
if (i >= 0)
|
|
|
|
|
{
|
|
|
|
|
a = adapters[i];
|
|
|
|
|
memmove(&adapters[i], &adapters[i+1], (adapters_size-i-1)*sizeof(struct adapter));
|
|
|
|
|
adapters_size--;
|
|
|
|
|
realloc_adapters();
|
|
|
|
|
}
|
|
|
|
|
pthread_mutex_unlock(&adapter_mutex);
|
|
|
|
|
if (i >= 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "adapter 0x%p disconnected\n", a.device);
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
if (a.controllers[i].connected)
|
|
|
|
|
uinput_destroy(i, &a.controllers[i]);
|
|
|
|
|
}
|
|
|
|
|
libusb_close(a.handle);
|
|
|
|
|
}
|
|
|
|
|
remove_adapter(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void quitting_signal(int sig)
|
|
|
|
|
{
|
|
|
|
|
(void)sig;
|
|
|
|
|
quitting = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct udev *udev;
|
|
|
|
|
struct udev_device *uinput;
|
|
|
|
|
struct sigaction sa;
|
|
|
|
|
|
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
|
|
|
|
|
|
if (argc > 1 && (strcmp(argv[1], "-r") == 0 || strcmp(argv[1], "--raw") == 0))
|
|
|
|
|
{
|
|
|
|
@ -568,11 +548,12 @@ int main(int argc, char *argv[])
|
|
|
|
|
raw_mode = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pthread_mutex_init(&adapter_mutex, NULL) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "mutex/cond init errors\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
sa.sa_handler = quitting_signal;
|
|
|
|
|
sa.sa_flags = SA_RESTART | SA_RESETHAND;
|
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
|
|
|
|
|
|
sigaction(SIGINT, &sa, NULL);
|
|
|
|
|
sigaction(SIGTERM, &sa, NULL);
|
|
|
|
|
|
|
|
|
|
udev = udev_new();
|
|
|
|
|
if (udev == NULL) {
|
|
|
|
@ -605,7 +586,7 @@ int main(int argc, char *argv[])
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
libusb_get_device_descriptor(devices[i], &desc);
|
|
|
|
|
if (desc.idVendor == 0x057e && desc.idProduct == 0x0337)
|
|
|
|
|
hotplug_callback(NULL, devices[i], LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, NULL);
|
|
|
|
|
add_adapter(devices[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
@ -618,9 +599,12 @@ int main(int argc, char *argv[])
|
|
|
|
|
fprintf(stderr, "cannot register hotplug callback, hotplugging not enabled\n");
|
|
|
|
|
|
|
|
|
|
// pump events until shutdown & all helper threads finish cleaning up
|
|
|
|
|
while (!quitting || adapters_size > 0)
|
|
|
|
|
while (!quitting)
|
|
|
|
|
libusb_handle_events_completed(NULL, (int *)&quitting);
|
|
|
|
|
|
|
|
|
|
while (adapters.next)
|
|
|
|
|
remove_adapter(adapters.next->device);
|
|
|
|
|
|
|
|
|
|
if (hotplug_ret == 0)
|
|
|
|
|
libusb_hotplug_deregister_callback(NULL, callback);
|
|
|
|
|
|
|
|
|
|