-
Notifications
You must be signed in to change notification settings - Fork 10
/
usb.c
349 lines (312 loc) · 10.3 KB
/
usb.c
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include <libusb.h>
#include <stdio.h>
#include <poll.h>
#include <stdint.h>
#include <string.h>
#include <sys/select.h>
#include "usb.h"
#include "alsa.h"
#include "log.h"
#include "dump.h"
#define MAX_PFDS 20
struct device {
uint16_t product_id;
uint8_t interface_number;
uint8_t ep_in;
uint8_t ep_out;
uint8_t ep_in2;
uint8_t interface_number2;
};
const struct device devices[] = {
{ 0xb102, 1, 0x83, 0x04, 0x0, 0 }, // Steel
{ 0xb105, 1, 0x81, 0x03, 0x0, 0 }, // MP3e2
{ 0xb120, 1, 0x82, 0x03, 0x0, 0 }, // Hercules MP3 LE / Glow
{ 0xb107, 5, 0x83, 0x03, 0x0, 0 }, // Hercules Mk4
{ 0xb100, 1, 0x86, 0x06, 0x0, 0 }, // Hercules Mk2
{ 0xb109, 5, 0x83, 0x03, 0x84, 0}, // 4-Mx
{ 0xb10c, 5, 0x83, 0x03, 0x84, 0}, // Hercules DJ 4Set
{ 0xb10d, 5, 0x83, 0x03, 0x84, 0}, // Hercules DJ 4Set, second variant(?)
{ 0, 0, 0, 0 }
};
static const int MANUFACTURER_HERCULES = 0x6f8;
static libusb_context *context = NULL;
static struct libusb_device_handle *usb_dev = NULL;
static const struct device *dev_info;
const struct libusb_pollfd **usb_fds = NULL;
struct libusb_transfer *xfer_in;
struct libusb_transfer *xfer_in2;
static int writes_pending = 0;
static int reads_pending = 0;
void
usb_debug_msg(char *action, int ep, uint8_t data[], size_t datalen)
{
#ifdef DEBUG
fprintf(stderr, "%s on ep%02x:", action, ep);
for (int i = 0; i < datalen; i += 1) {
fprintf(stderr, " %02x", data[i]);
}
fprintf(stderr, "\n");
#endif
}
void usb_xfer_done(struct libusb_transfer *xfer);
void usb_xfer_done_additional(struct libusb_transfer *xfer);
void
usb_interrupting()
{
libusb_cancel_transfer(xfer_in);
if (dev_info->ep_in2 != 0x0) {
libusb_cancel_transfer(xfer_in2);
}
}
static void
usb_initiate_transfer()
{
static const int buffsize = 256;
unsigned char *buf;
buf = (unsigned char *)malloc(buffsize);
// Tell libusb we want to know about bulk transfers
xfer_in = libusb_alloc_transfer(0);
//timeout if in 1000 milliseconds it hasn't been sent
xfer_in->timeout=1000;
xfer_in->flags |=LIBUSB_TRANSFER_ADD_ZERO_PACKET;
libusb_fill_bulk_transfer(xfer_in, usb_dev, dev_info->ep_in, buf, buffsize, usb_xfer_done, NULL, 0);
libusb_submit_transfer(xfer_in);
reads_pending += 1;
}
static void
usb_initiate_transfer_additional()
{
static const int buffsize = 256;
unsigned char *buf;
buf = (unsigned char *)malloc(buffsize);
// Tell libusb we want to know about bulk transfers
xfer_in2 = libusb_alloc_transfer(0);
//timeout if in 1000 milliseconds it hasn't been sent
xfer_in2->timeout=1000;
xfer_in2->flags |=LIBUSB_TRANSFER_ADD_ZERO_PACKET;
libusb_fill_bulk_transfer(xfer_in2, usb_dev, dev_info->ep_in2, buf, buffsize, usb_xfer_done_additional, NULL, 0);
libusb_submit_transfer(xfer_in2);
}
void LIBUSB_CALL
usb_xfer_done(struct libusb_transfer *xfer)
{
uint8_t *data = xfer->buffer;
int datalen = xfer->actual_length;
reads_pending -= 1;
if ( xfer->status == LIBUSB_TRANSFER_COMPLETED ) {
usb_debug_msg("Receiving", dev_info->ep_in, data, datalen);
alsa_write(data, datalen);
}
if ( xfer->status == LIBUSB_TRANSFER_COMPLETED ) {
usb_initiate_transfer();
} else if ( xfer->status != LIBUSB_TRANSFER_CANCELLED ) {
fatal("Stopping EP_IN, because of status %d.\nSoftware needs restarting", xfer->status);
}
free(data);
libusb_free_transfer(xfer);
}
void LIBUSB_CALL
usb_xfer_done_additional(struct libusb_transfer *xfer)
{
if ( xfer->status == LIBUSB_TRANSFER_COMPLETED ) {
// We don't need to use the information of this call, but it is needed that we
// poll this, or else it hangs and doesn't receive more data.
usb_debug_msg("Receiving", dev_info->ep_in2, xfer->buffer, xfer->actual_length);
}
if ( xfer->status == LIBUSB_TRANSFER_COMPLETED ) {
usb_initiate_transfer_additional();
} else if ( xfer->status != LIBUSB_TRANSFER_CANCELLED ) {
fatal("Stopping EP_IN2, because of status %d\nSoftware needs restarting", xfer->status);
}
free(xfer->buffer);
libusb_free_transfer(xfer);
}
int
usb_setup(char *name, size_t namelen)
{
int ret;
ret=libusb_init(&context);
if (ret < 0) {
fatal("ERROR: %s\n%s", libusb_error_name(ret), libusb_strerror(ret));
return -1;
}
//----------------------------------------------------------------------------
// Enable debug
//----------------------------------------------------------------------------
#ifdef DEBUG
libusb_set_debug(context, LIBUSB_LOG_LEVEL_WARNING);
#endif
if (libusb_pollfds_handle_timeouts(context) == 0) {
fatal("I'm too dumb to handle events on such an old system.");
return -1;
}
//----------------------------------------------------------------------------
// Get device list
//----------------------------------------------------------------------------
struct libusb_device_descriptor founddesc = {0};
{
libusb_device **devs;
ssize_t count; //holding number of devices in list
printf("Locating Hercules USB devices...\n(You can also use the lsusb command to locate this information)\n");
count = libusb_get_device_list(context, &devs);
if ( count < 0) {
fatal("Error getting the device list: %s\n%s", libusb_error_name(count), libusb_strerror(count));
return -1;
} else if (count == 0) {
warn("Seems that the USB device list is empty. Is the controller connected? ");
}
size_t idx;
for (idx = 0; idx < count; idx+=1) {
libusb_device *device = devs[idx];
struct libusb_device_descriptor listdesc = {0};
ret = libusb_get_device_descriptor(device, &listdesc);
if ( ret != 0) {
warn("Could not get descriptor for device index %ld: %s\n%s",
(long int)idx, libusb_error_name(count), libusb_strerror(count));
} else if (listdesc.idVendor == MANUFACTURER_HERCULES) {
printf("Vendor:Device = %04x:%04x\n", listdesc.idVendor, listdesc.idProduct);
founddesc = listdesc;
}
}
libusb_free_device_list(devs, 1); //free the list, unref the devices in it
//----------------------------------------------------------------------------
}
for (dev_info = devices; dev_info->product_id; dev_info += 1) {
usb_dev = libusb_open_device_with_vid_pid(context, MANUFACTURER_HERCULES, dev_info->product_id);
if (usb_dev) {
break;
} else if (dev_info->product_id == founddesc.idProduct) {
fatal("The controller %04x:%04x could not be opened.\n"
"Check that you have enough permissions over /dev/bus/usb/ subfolder elements.\n."
"You might need to create an udev rule at /etc/udev/rules.d/", founddesc.idVendor,founddesc.idProduct);
}
}
if (! usb_dev) {
if (founddesc.idVendor != MANUFACTURER_HERCULES) {
fatal("Couldn't find a Hercules controller.");
}
else {
fatal("The controller %04x:%04x is not supported.", founddesc.idVendor,founddesc.idProduct);
}
return -1;
}
// Figure out what it's called
{
struct libusb_device_descriptor ddesc;
name[0]='\0';
libusb_get_device_descriptor(libusb_get_device(usb_dev), &ddesc);
ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iManufacturer, (unsigned char *)name, namelen);
if (ret > 0) {
char *p = name + ret;
*p = ' ';
p += 1;
ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iProduct, (unsigned char *)p, namelen - ret - 1);
}
if (ret < 0) {
warn("I can't figure out what to call this thing.");
}
printf("Opened [%s]\n", name);
}
ret = libusb_claim_interface(usb_dev, dev_info->interface_number);
if (ret == 0 ) {
if (dev_info->ep_in2 != 0x0) {
libusb_claim_interface(usb_dev, dev_info->interface_number2);
usb_initiate_transfer_additional();
}
usb_initiate_transfer();
return 0;
} else {
if (ret == LIBUSB_ERROR_BUSY) {
fatal("Couldn't claim interface %d. Already in use?", dev_info->interface_number);
} else {
fatal("Couldn't claim interface %d. %s\n%s", dev_info->interface_number,
libusb_error_name(ret), libusb_strerror(ret));
}
return -1;
}
}
void
usb_finish() {
int ret;
if (usb_dev) {
ret = libusb_release_interface(usb_dev, dev_info->interface_number);
if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) {
warn("Couldn't release interface %d. %s\n%s", dev_info->interface_number,
libusb_error_name(ret), libusb_strerror(ret));
}
libusb_close(usb_dev);
usb_dev=NULL;
}
libusb_exit(context);
}
void
usb_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
{
usb_fds = libusb_get_pollfds(context);
if (usb_fds == NULL) {
warn("could not get the filedescriptors! This is unexpected");
}
int i;
for (i = 0; usb_fds[i]; i += 1) {
const struct libusb_pollfd *ufd = usb_fds[i];
if (ufd->fd > *nfds) {
*nfds = ufd->fd;
}
if (ufd->events & POLLIN) {
FD_SET(ufd->fd, rfds);
}
if (ufd->events & POLLOUT) {
FD_SET(ufd->fd, wfds);
}
}
if (reads_pending + writes_pending > 100) {
warn("%d(r)+%d(w) = %d outstanding USB transactions!", reads_pending, writes_pending, reads_pending + writes_pending);
}
}
void
usb_check_fds(fd_set *rfds, fd_set *wfds)
{
if (usb_fds == NULL) {
return;
}
int i;
for (i = 0; usb_fds[i]; i += 1) {
int fd = usb_fds[i]->fd;
if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) {
libusb_handle_events(context);
return;
}
}
#if LIBUSB_API_VERSION >= 0x01000104
libusb_free_pollfds(usb_fds);
usb_fds = NULL;
#endif // LIBUSB_API_VERSION >= 0x01000104
}
void LIBUSB_CALL
usb_write_done(struct libusb_transfer *xfer)
{
if ( xfer->status == LIBUSB_TRANSFER_TIMED_OUT ) {
warn("Send timed out");
} else if ( xfer->status && xfer->status != LIBUSB_TRANSFER_CANCELLED) {
warn("USB Write status %d", xfer->status);
}
writes_pending -= 1;
usb_debug_msg("Sent", dev_info->ep_out, xfer->buffer, xfer->actual_length);
free(xfer->buffer);
libusb_free_transfer(xfer);
}
void
usb_write(uint8_t *data, size_t datalen)
{
struct libusb_transfer *xfer;
unsigned char *buf;
writes_pending += 1;
xfer = libusb_alloc_transfer(0);
//timeout if in 1000 milliseconds it hasn't been sent
xfer->timeout=1000;
buf = (unsigned char *)malloc(datalen);
memcpy(buf, data, datalen);
libusb_fill_bulk_transfer(xfer, usb_dev, dev_info->ep_out, buf, datalen, usb_write_done, NULL, 0);
libusb_submit_transfer(xfer);
usb_debug_msg("Preparing to send", dev_info->ep_out, data, datalen);
}