-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhotplug.c
More file actions
226 lines (201 loc) · 7.04 KB
/
hotplug.c
File metadata and controls
226 lines (201 loc) · 7.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <libusb-1.0/libusb.h>
#include "usbcommon.h"
enum {
EVENT_QUIT = 1,
EVENT_DEVICE_ARRIVED = 2,
EVENT_DEVICE_LEFT = 3,
EVENT_TRANSFER_COMPLETED = 4,
EVENT_BUTTON_A_PRESSED = 5,
EVENT_BUTTON_X_PRESSED = 6
};
static libusb_device_handle *devh = NULL;
static struct libusb_transfer *transfer = NULL;
static libusb_hotplug_callback_handle hotplug_callback_handle = 0;
static int completed = 0;
// http://libusb.sourceforge.net/api-1.0/libusb_hotplug.html
static int LIBUSB_CALL hotplug_callback(
__attribute__((unused)) struct libusb_context *ctx,
struct libusb_device *dev,
libusb_hotplug_event event,
void *user_data)
{
struct libusb_device_descriptor desc;
int rc;
int * const completed = user_data;
libusb_get_device_descriptor(dev, &desc);
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
// NOTE: Most functions that take a libusb_device_handle are
// not safe to call. Examples of such functions are any of the
// synchronous API functions or the blocking functions that
// retrieve various USB descriptors.
rc = libusb_open(dev, &devh);
if (rc != LIBUSB_SUCCESS) {
fputs("Could not open USB device\n", stderr);
}
*completed = EVENT_DEVICE_ARRIVED;
}
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
// NOTE: The only safe function is libusb_get_device_descriptor().
if (devh != NULL) {
libusb_close(devh);
devh = NULL;
}
*completed = EVENT_DEVICE_LEFT;
}
else {
fprintf(stderr, "Unhandled event %d\n", event);
}
return 0; // rearm this callback
}
// http://libusb.sourceforge.net/api-1.0/group__libusb__asyncio.html
static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer)
{
int * const completed = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
printf("Read successful! %d bytes: ", transfer->actual_length);
printhex(transfer->actual_length, transfer->buffer);
putchar('\n');
*completed = EVENT_TRANSFER_COMPLETED;
if (transfer->actual_length == 18 && transfer->buffer[0] == 0x20) {
// We received button data
struct gamepad_t gamepad;
data_to_gamepad(transfer->buffer, &gamepad);
if (gamepad.a) {
*completed = EVENT_BUTTON_A_PRESSED;
}
else if (gamepad.x) {
*completed = EVENT_BUTTON_X_PRESSED;
}
}
}
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
// This was triggered by the call to libusb_cancel_transfer()
// in signal_handler().
*completed = EVENT_QUIT;
}
else {
// User detached device. This is invoked before hotplug_callback().
fprintf(stderr, "Transfer failed with status = %s\n",
libusb_error_name(transfer->status));
}
}
static void signal_handler(int signum)
{
if (signum == SIGINT) {
// User pressed CTRL-C
if (devh != NULL) {
// A device is present
libusb_cancel_transfer(transfer);
}
else {
// No device is present
completed = EVENT_QUIT;
libusb_interrupt_event_handler(NULL);
}
}
}
static int register_signal_handler(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = signal_handler;
return sigaction(SIGINT, &sa, NULL);
}
// Error handling code has been omitted for clarity
int main(void)
{
uint8_t data[64]; // data buffer
int rc; // return code
if (register_signal_handler() == -1) {
perror("sigaction");
return EXIT_FAILURE;
}
print_libusb_version();
rc = libusb_init(NULL);
libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_WARNING);
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
fputs("Hotplug is not supported\n", stderr);
libusb_exit(NULL);
return EXIT_FAILURE;
}
rc = libusb_hotplug_register_callback(NULL,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
0, VENDOR_ID, PRODUCT_ID, LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback,
&completed, &hotplug_callback_handle);
if (rc != LIBUSB_SUCCESS) {
fputs("Error registering hotplug callback\n", stderr);
libusb_exit(NULL);
return EXIT_FAILURE;
}
// We use a single transfer structure throughout the whole program
transfer = libusb_alloc_transfer(0);
// Open device and submit an asynchronous transfer
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (devh != NULL) {
puts("Device opened");
print_port_path(devh);
rc = libusb_set_auto_detach_kernel_driver(devh, 1);
rc = libusb_claim_interface(devh, 0);
rc = init_device(devh);
libusb_fill_interrupt_transfer(transfer, devh,
(2 | LIBUSB_ENDPOINT_IN),
data, sizeof(data), transfer_callback, &completed, 0);
rc = libusb_submit_transfer(transfer);
}
else {
puts("Device not found; attach device to begin");
}
// Loop forever until user presses button X or CTRL-C
while (1) {
completed = 0;
// Block forever until an event is received
rc = libusb_handle_events_completed(NULL, &completed);
//nanosleep(&(struct timespec){0, 100000000UL}, NULL); // 100 ms
if (completed == EVENT_QUIT) {
putchar('\n');
break;
}
else if (completed == EVENT_DEVICE_ARRIVED) {
puts("Device arrived");
print_port_path(devh);
rc = libusb_set_auto_detach_kernel_driver(devh, 1);
rc = libusb_claim_interface(devh, 0);
rc = init_device(devh);
libusb_fill_interrupt_transfer(transfer, devh,
(2 | LIBUSB_ENDPOINT_IN),
data, sizeof(data), transfer_callback, &completed, 0);
rc = libusb_submit_transfer(transfer);
}
else if (completed == EVENT_DEVICE_LEFT) {
puts("Device left");
}
else if (completed == EVENT_TRANSFER_COMPLETED) {
rc = libusb_submit_transfer(transfer);
}
else if (completed == EVENT_BUTTON_A_PRESSED) {
rumble(devh, 0x20, 0x20);
rc = libusb_submit_transfer(transfer);
}
else if (completed == EVENT_BUTTON_X_PRESSED) {
break;
}
}
// Shutting down from here onwards
if (devh != NULL) {
libusb_release_interface(devh, 0); // release the claimed interface
libusb_close(devh); // close the device we opened
}
libusb_free_transfer(transfer);
libusb_hotplug_deregister_callback(NULL, hotplug_callback_handle);
libusb_exit(NULL); // deinitialize libusb
puts("bye");
return EXIT_SUCCESS;
}