|
1 | 1 | /*
|
2 | 2 | gub211.c
|
3 |
| -Copyright (c) 2013 RyanC <[email protected]> |
| 3 | +Copyright © 2013-2022 Ryan Castellucci |
4 | 4 |
|
5 | 5 | Redistribution and use in source and binary forms, with or without
|
6 | 6 | modification, are permitted provided that the following conditions are met:
|
| 7 | +
|
7 | 8 | * Redistributions of source code must retain the above copyright
|
8 | 9 | notice, this list of conditions and the following disclaimer.
|
9 | 10 | * 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.
|
36 | 37 | */
|
37 | 38 |
|
38 | 39 | #include <stdio.h>
|
| 40 | +#include <stdarg.h> |
| 41 | +#include <stdlib.h> |
39 | 42 | #include <string.h>
|
| 43 | +#include <stdint.h> |
| 44 | +#include <stdbool.h> |
| 45 | + |
40 | 46 | #include <libusb-1.0/libusb.h>
|
41 | 47 |
|
42 | 48 | #define GUB211_VENDORID 0x2101
|
43 | 49 | #define GUB211_PRODUCTID 0x0231
|
| 50 | +#define MAX_PORT_DEPTH 7 |
| 51 | +#define MAX_PATH_STRLEN (5+MAX_PORT_DEPTH*4) |
44 | 52 |
|
45 | 53 | /* compile with gcc -Wall -O2 gub211.c -lusb-1.0 -o gub211 */
|
46 | 54 |
|
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) { |
54 | 61 | 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 | + } |
59 | 74 | }
|
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; |
66 | 79 | }
|
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; |
77 | 85 | }
|
78 | 86 | }
|
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; |
83 | 127 | }
|
84 | 128 | }
|
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; |
88 | 139 | }
|
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 | + } |
92 | 158 | }
|
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)); |
96 | 173 | return 1;
|
97 | 174 | }
|
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; |
101 | 181 | }
|
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; |
105 | 246 | }
|
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); } |
107 | 256 | libusb_exit(ctx);
|
108 |
| - return 0; |
| 257 | + return r; |
109 | 258 | }
|
0 commit comments