Skip to content

Commit

Permalink
Yet another of my potentially useless projects.
Browse files Browse the repository at this point in the history
So, here we have the implementation of a userspace
driver that uses libusb from computerquip/libusb on github, and
uinput from the linux input subsystem for userspace.

Things that are left to do are as follows:
1) Any output which includes:
	a) LED packets
	b) Rumble packets

These are easy to do, but synchronizing them may be difficult.

I do eventually plan to support more than one xbox controller.
  • Loading branch information
computerquip committed Sep 1, 2014
0 parents commit a9d858a
Show file tree
Hide file tree
Showing 6 changed files with 1,210 additions and 0 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project('xpad2', 'c')

libusb = dependency('libusb-1.0')
pthread = find_library('pthread')

libs = [libusb, pthread]

src = ['xpad360_usb.c', 'xpad360_threadpool.c']

executable('xpad360', src, \
dependencies : libs, \
c_args : ['-std=gnu99'])
123 changes: 123 additions & 0 deletions xpad360_threadpool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct worker_data {
unsigned char data[32];
void *ctx;
pthread_t thread;
pthread_cond_t condition;
pthread_mutex_t mutex;
int ready;
};

static void(*process)(unsigned char*, void*);
static struct worker_data *workers;
static int num_workers;

static int parse_cpucount_sysfs()
{
const char online_filepath[] = "/sys/devices/system/cpu/online";
int prev = -1; /* Not 0 as 0 is a valid value. */
int num_cores = 0;

/* file_num_online contains the number of cores online (not all possible) across various CPUs. */
FILE* file = fopen(online_filepath, "r");

if (!file) {
printf("You got problems son. Most likely, you're missing an important scheduling module.\n");
return 0;
}

/* The following is similar to what's found in glibc for fetching number of online processors. */

for (;;) {
char sep;
int cpu;
int n = fscanf(file, "%u%c", &cpu, &sep);

if (n <= 0)
break;

if (n == 1) /* There was no seperator... meaning eol.*/
sep = '\n';

if (prev >= 0) { /* This is the second number in a range, for sure higher than the last. */
for (int i = prev; i <= cpu; ++i) {
++num_cores;
}

prev = -1;
} else if (sep == '-') {
prev = cpu;
} else {
++num_cores;
}

if (sep == '\n') break;
}

fclose(file);
return num_cores;
}

static void *worker_func(void *_data)
{
struct worker_data *data = _data;
pthread_mutex_lock(&data->mutex);

for (;;) {
while (!data->ready) {
pthread_cond_wait(&data->condition, &data->mutex);
}

process(data->data, data->ctx);
data->ready = 0;
}

return 0;
}

int pool_init(void(*proc)(unsigned char*, void*))
{
num_workers = parse_cpucount_sysfs();

if (!num_workers) return -1;
workers = calloc(num_workers, sizeof(struct worker_data));

if (!workers) return -2;

process = proc;

for (int i = 0; i < num_workers; ++i) {
pthread_cond_init(&workers[i].condition, NULL);
pthread_mutex_init(&workers[i].mutex, NULL);

pthread_create(
&workers[i].thread, NULL,
worker_func, &workers[i]);
}

return 0;
}

void pool_queue_work(unsigned char *data, void *ctx)
{
static int worker;

++worker;
worker = worker % num_workers;
memcpy(workers[worker].data, data, 32);
workers[worker].ctx = ctx;
workers[worker].ready = 1;
pthread_cond_signal(&workers[worker].condition);
}

void pool_destroy()
{
free(workers);
}



5 changes: 5 additions & 0 deletions xpad360_threadpool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

int pool_init(void (*)(unsigned char *, void*));
void pool_queue_work(unsigned char *, void*);
void pool_destroy();
43 changes: 43 additions & 0 deletions xpad360_uinput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

static inline __u16 le16_to_cpup(const __le16 *p)
{
return le16toh(*p);
}

inline static void uinput_set_abs_params(struct uinput_user_dev *dev, unsigned int axis,
int min, int max, int fuzz, int flat)
{
dev->absmin[axis] = min;
dev->absmax[axis] = max;
dev->absfuzz[axis] = fuzz;
dev->absflat[axis] = flat;
}

inline static void uinput_report_event(int uinput_fd, __u16 type, __u16 code, __s32 value)
{
struct input_event event = {
.time = { 0 },
.type = type,
.code = code,
.value = value
};

if (write(uinput_fd, &event, sizeof(event)) < 0)
printf("Failed to write to uinput device: %s\n", strerror(errno));
}

inline static void uinput_report_key(int uinput_fd, __u16 code, __s32 value)
{
uinput_report_event(uinput_fd, EV_KEY, code, !!value);
}

inline static void uinput_report_abs(int uinput_fd, __u16 code, __s32 value)
{
uinput_report_event(uinput_fd, EV_ABS, code, value);
}

inline static void uinput_sync(int uinput_fd)
{
uinput_report_event(uinput_fd, EV_SYN, SYN_REPORT, 0);
}
Loading

0 comments on commit a9d858a

Please sign in to comment.