Skip to content

Commit 321ac5b

Browse files
committed
c: support multiple devices
1 parent 162e3ec commit 321ac5b

File tree

1 file changed

+198
-49
lines changed

1 file changed

+198
-49
lines changed

gub211.c

+198-49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/*
22
gub211.c
3-
Copyright (c) 2013 RyanC <[email protected]>
3+
Copyright © 2013-2022 Ryan Castellucci
44
55
Redistribution and use in source and binary forms, with or without
66
modification, are permitted provided that the following conditions are met:
7+
78
* Redistributions of source code must retain the above copyright
89
notice, this list of conditions and the following disclaimer.
910
* Redistributions in binary form must reproduce the above copyright
@@ -36,74 +37,222 @@ set the permissions to allow access to the user you're going to run it as.
3637
*/
3738

3839
#include <stdio.h>
40+
#include <stdarg.h>
41+
#include <stdlib.h>
3942
#include <string.h>
43+
#include <stdint.h>
44+
#include <stdbool.h>
45+
4046
#include <libusb-1.0/libusb.h>
4147

4248
#define GUB211_VENDORID 0x2101
4349
#define GUB211_PRODUCTID 0x0231
50+
#define MAX_PORT_DEPTH 7
51+
#define MAX_PATH_STRLEN (5+MAX_PORT_DEPTH*4)
4452

4553
/* compile with gcc -Wall -O2 gub211.c -lusb-1.0 -o gub211 */
4654

47-
int main() {
48-
#ifdef GUB211_DOLIST
49-
libusb_device **devs;
50-
ssize_t cnt;
51-
#endif
52-
libusb_context *ctx = NULL;
53-
libusb_device_handle *handle;
55+
int ctrl_transfer(
56+
libusb_device_handle *handle,
57+
uint8_t bmRequestType, uint8_t bRequest,
58+
uint16_t wValue, uint16_t wIndex,
59+
unsigned char *data, uint16_t wLength,
60+
unsigned int timeout) {
5461
int r;
55-
r = libusb_init(&ctx);
56-
if (r < 0) {
57-
fprintf(stderr, "Error initializing libusb: %d\n", r);
58-
return 1;
62+
for (int i = 0; i < 2; ++i) {
63+
if (libusb_kernel_driver_active(handle, i)) {
64+
if ((r = libusb_detach_kernel_driver(handle, i)) != LIBUSB_SUCCESS) {
65+
fprintf(stderr, "Detaching kernel driver from interface %d failed: %s\n", i, libusb_error_name(r));
66+
return r;
67+
}
68+
}
69+
70+
if ((r = libusb_claim_interface(handle, i)) != LIBUSB_SUCCESS) {
71+
fprintf(stderr, "Claiming interface %d failed: %s\n", i, libusb_error_name(r));
72+
return r;
73+
}
5974
}
60-
libusb_set_debug(ctx, 3);
61-
#ifdef GUB211_DOLIST
62-
cnt = libusb_get_device_list(ctx, &devs);
63-
if (cnt < 0) {
64-
fprintf(stderr, "Error getting device list: %zd\n", cnt);
65-
return 1;
75+
76+
if ((r = libusb_control_transfer(handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout)) != wLength) {
77+
fprintf(stderr, "Control transfer failed: %s\n", libusb_error_name(r));
78+
return r >= 0 ? LIBUSB_ERROR_OTHER : r;
6679
}
67-
#endif
68-
handle = libusb_open_device_with_vid_pid(ctx, GUB211_VENDORID, GUB211_PRODUCTID);
69-
70-
#ifdef GUB211_DOLIST
71-
libusb_free_device_list(devs, cnt);
72-
#endif
73-
if (libusb_kernel_driver_active(handle, 0) == 1) {
74-
if ((r = libusb_detach_kernel_driver(handle, 0)) < 0) {
75-
fprintf(stderr, "Detaching kernel driver from interface 0 failed: %d\n", r);
76-
return 1;
80+
81+
for (int i = 0; i < 2; ++i) {
82+
if ((r = libusb_release_interface(handle, i)) != LIBUSB_SUCCESS) {
83+
fprintf(stderr, "Releasing interface %d failed: %s\n", i, libusb_error_name(r));
84+
return r;
7785
}
7886
}
79-
if (libusb_kernel_driver_active(handle, 1) == 1) {
80-
if ((r = libusb_detach_kernel_driver(handle, 1)) < 0) {
81-
fprintf(stderr, "Detaching kernel driver from interface 1 failed: %d\n", r);
82-
return 1;
87+
88+
return LIBUSB_SUCCESS;
89+
}
90+
91+
int switch_to(libusb_device_handle *handle) {
92+
return ctrl_transfer(handle, 0x21, 0x09, 0x0203, 0x0001, (unsigned char *)"\x03\x5d\x42\x00\x00", 5, 100);
93+
}
94+
95+
ssize_t bnprintf(char **d, size_t *n, const char *format, ...) {
96+
va_list ap;
97+
va_start(ap, format);
98+
// length excluding null byte
99+
ssize_t len = vsnprintf(*d, *n, format, ap);
100+
va_end(ap);
101+
if (len < 0 || (size_t)len >= *n) return -1;
102+
*n -= len;
103+
*d += len;
104+
return len;
105+
}
106+
107+
int path_str(char *str, size_t len, libusb_device *dev) {
108+
int r;
109+
char *ptr = str;
110+
uint8_t bus = libusb_get_bus_number(dev);
111+
uint8_t port_numbers[MAX_PORT_DEPTH+1];
112+
113+
uint8_t n_ports;
114+
if ((r = libusb_get_port_numbers(dev, port_numbers, MAX_PORT_DEPTH)) < 0) {
115+
fprintf(stderr, "Error getting port numbers: %s\n", libusb_error_name(r));
116+
return r;
117+
}
118+
n_ports = (uint8_t)r;
119+
120+
if (bnprintf(&ptr, &len, "%u:", bus) < 0) {
121+
return LIBUSB_ERROR_OTHER;
122+
}
123+
124+
for (int j = 0; j < n_ports; ++j) {
125+
if (bnprintf(&ptr, &len, "%s%u", j ? "/" : "", port_numbers[j]) < 0) {
126+
return LIBUSB_ERROR_OTHER;
83127
}
84128
}
85-
if ((r = libusb_claim_interface(handle, 0)) < 0) {
86-
fprintf(stderr, "Claiming interface 0 failed: %d\n", r);
87-
return 1;
129+
130+
return LIBUSB_SUCCESS;
131+
}
132+
133+
int matches_vid_pid(libusb_device *dev, uint16_t vid, uint16_t pid) {
134+
int r;
135+
struct libusb_device_descriptor desc[1] = {0};
136+
if ((r = libusb_get_device_descriptor(dev, desc)) != LIBUSB_SUCCESS) {
137+
fprintf(stderr, "Error getting device descriptor: %s\n", libusb_error_name(r));
138+
return r;
88139
}
89-
if ((r = libusb_claim_interface(handle, 1)) < 0) {
90-
fprintf(stderr, "Claiming interface 1 failed: %d\n", r);
91-
return 1;
140+
141+
return desc->idVendor == vid && desc->idProduct == pid ? 1 : 0;
142+
}
143+
144+
int print_list(ssize_t cnt, libusb_device **devs) {
145+
int r;
146+
char path[MAX_PATH_STRLEN];
147+
148+
for (ssize_t i = 0; i < cnt; ++i) {
149+
if ((r = matches_vid_pid(devs[i], GUB211_VENDORID, GUB211_PRODUCTID)) == 1) {
150+
if ((r = path_str(path, MAX_PATH_STRLEN, devs[i])) != LIBUSB_SUCCESS) {
151+
fprintf(stderr, "Failed to get path: %d\n", r);
152+
return r;
153+
}
154+
printf("device at: %s\n", path);
155+
} else if (r < 0) {
156+
return r;
157+
}
92158
}
93-
/* magic numbers obtained by sniffing the usb bus */
94-
if ((r = libusb_control_transfer(handle, 0x21, 0x09, 0x0203, 0x0001, (unsigned char *)"\x03\x5d\x42\x00\x00", 5, 100)) != 5) {
95-
fprintf(stderr, "Control transfer failed: %d\n", r);
159+
160+
return LIBUSB_SUCCESS;
161+
}
162+
163+
int main(int argc, char *argv[]) {
164+
int r;
165+
ssize_t cnt;
166+
167+
libusb_device **devs = NULL;
168+
libusb_context *ctx = NULL;
169+
libusb_device_handle *handle = NULL;
170+
171+
if ((r = libusb_init(&ctx)) != LIBUSB_SUCCESS) {
172+
fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(r));
96173
return 1;
97174
}
98-
if ((r = libusb_release_interface(handle, 0)) < 0) {
99-
fprintf(stderr, "Releasing interface 0 failed: %d\n", r);
100-
return 2;
175+
176+
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 3);
177+
178+
if ((cnt = libusb_get_device_list(ctx, &devs)) < 0) {
179+
fprintf(stderr, "Error getting device list: %s\n", libusb_error_name(cnt));
180+
return 1;
101181
}
102-
if ((r = libusb_release_interface(handle, 1)) < 0) {
103-
fprintf(stderr, "Releasing interface 1 failed: %d\n", r);
104-
return 2;
182+
183+
if (argc == 2 && strcmp(argv[1], "list") == 0) {
184+
if ((r = print_list(cnt, devs)) != LIBUSB_SUCCESS) {
185+
goto cleanup_failure;
186+
}
187+
} else if (argc == 1 || (argc == 2 && strcmp(argv[1], "first") == 0)) {
188+
for (ssize_t i = 0; i < cnt; ++i) {
189+
if ((r = matches_vid_pid(devs[i], GUB211_VENDORID, GUB211_PRODUCTID)) == 1) {
190+
if ((r = libusb_open(devs[i], &handle)) != LIBUSB_SUCCESS) {
191+
fprintf(stderr, "Could not open device: %s\n", libusb_error_name(r));
192+
goto cleanup_failure;
193+
}
194+
if ((r = switch_to(handle)) != LIBUSB_SUCCESS) { goto cleanup_failure; }
195+
goto cleanup_success;
196+
} else if (r < 0) {
197+
goto cleanup_failure;
198+
}
199+
}
200+
fprintf(stderr, "no devices found\n");
201+
goto cleanup_failure;
202+
} else if (argc == 2 && strcmp(argv[1], "only") == 0) {
203+
ssize_t only = -1;
204+
for (ssize_t i = 0; i < cnt; ++i) {
205+
if ((r = matches_vid_pid(devs[i], GUB211_VENDORID, GUB211_PRODUCTID)) == 1) {
206+
if (only == -1) {
207+
only = i;
208+
} else {
209+
fprintf(stderr, "multiple devices found\n");
210+
goto cleanup_failure;
211+
}
212+
} else if (r < 0) {
213+
goto cleanup_failure;
214+
}
215+
}
216+
if (only == -1) {
217+
fprintf(stderr, "no devices found\n");
218+
goto cleanup_failure;
219+
}
220+
if ((r = libusb_open(devs[only], &handle)) != LIBUSB_SUCCESS) {
221+
fprintf(stderr, "Could not open device: %s\n", libusb_error_name(r));
222+
goto cleanup_failure;
223+
}
224+
if ((r = switch_to(handle)) != LIBUSB_SUCCESS) { goto cleanup_failure; }
225+
goto cleanup_success;
226+
} else if (argc == 2) {
227+
char path[MAX_PATH_STRLEN];
228+
for (ssize_t i = 0; i < cnt; ++i) {
229+
if ((r = matches_vid_pid(devs[i], GUB211_VENDORID, GUB211_PRODUCTID)) == 1) {
230+
if ((r = path_str(path, MAX_PATH_STRLEN, devs[i])) != LIBUSB_SUCCESS) {
231+
fprintf(stderr, "Failed to get path: %d\n", r);
232+
} else if (strcmp(path, argv[1]) == 0) {
233+
if ((r = libusb_open(devs[i], &handle)) != LIBUSB_SUCCESS) {
234+
fprintf(stderr, "Could not open device: %s\n", libusb_error_name(r));
235+
goto cleanup_failure;
236+
}
237+
if ((r = switch_to(handle)) != LIBUSB_SUCCESS) { goto cleanup_failure; }
238+
goto cleanup_success;
239+
}
240+
} else if (r < 0) {
241+
goto cleanup_failure;
242+
}
243+
}
244+
fprintf(stderr, "no matching device\n");
245+
goto cleanup_failure;
105246
}
106-
libusb_close(handle);
247+
248+
cleanup_success:
249+
r = EXIT_SUCCESS;
250+
goto cleanup_done;
251+
cleanup_failure:
252+
r = EXIT_FAILURE;
253+
cleanup_done:
254+
if (devs != NULL && cnt > 0) { libusb_free_device_list(devs, cnt); }
255+
if (handle != NULL) { libusb_close(handle); }
107256
libusb_exit(ctx);
108-
return 0;
257+
return r;
109258
}

0 commit comments

Comments
 (0)