-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Yet another of my potentially useless projects.
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
0 parents
commit a9d858a
Showing
6 changed files
with
1,210 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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']) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.