This commit is contained in:
Neale Pickett 2017-06-04 19:07:52 +00:00
commit 6fd12c5c21
10 changed files with 538 additions and 476 deletions

View File

@ -1,7 +1,7 @@
CFLAGS += -Wall
CFLAGS += -Werror
TARGETS = hdjd aac123 explore
CFLAGS += -g
TARGETS = hdjd explore
#CFLAGS += -g -DDEBUG
all: $(TARGETS)
@ -15,13 +15,5 @@ explore.o: CFLAGS += $(shell pkg-config --cflags libusb-1.0)
alsa.o: CFLAGS += $(shell pkg-config --cflags alsa)
usb.o: CFLAGS += $(shell pkg-config --cflags libusb-1.0)
aac123: CFLAGS += $(shell pkg-config --cflags alsa)
aac123: LDLIBS += $(shell pkg-config --libs alsa)
aac123: LDLIBS += -lfaad -lmp4v2
aactest: CFLAGS += $(shell pkg-config --cflags alsa)
aactest: LDLIBS += $(shell pkg-config --libs alsa)
aactest: LDLIBS += -lfaad -lmp4v2
clean:
rm -f $(TARGETS) *.o

View File

@ -24,6 +24,7 @@ The driver currently supports:
* Hercules DJ Control MP3 e2
* Hercules DJ Control Steel
* Hercules DJ Console 4-Mx
Support is planned for:
@ -34,12 +35,11 @@ Things I won't support:
* Hercules DJ 4Set (the one Guillemot sent me doesn't even work in Windows)
* Hercules Console Mk2 (already works with HID code in Mixxx)
* Hercules RMX (already works with HID code in Mixxx)
* Hercules DJ Control Instinct (already works with Midi code in Mixxx,
although I *will* be contributing a mapping for it)
If you have a Hercules device that doesn't "just work" with Mixxx,
send me an email, there's a good chance I can support it with this driver.
send me an email, there's a chance I can support it with this driver.
How To Get
----------
@ -60,12 +60,11 @@ Just run `hdjd` and it will tell you what it found.
Current Issues
--------------
The driver is pretty crappy right now.
If you email me, I'm a lot more likely to fix things.
Even if you just say "hi, I'm using your Hercules driver".
Recent versions of Mixxx may not be setting up the USB system correctly,
resulting in your disk running out of space because
`syslog` and the kernel log have millions of messages.
If this happens, make sure to disable the "USB Device" for your controller in Mixxx.
* Driver must be launched after device is plugged in
* It locks up and crashes sometimes
If you find a problem, please, please, pretty-please,
email me <neale@woozle.org>.
@ -90,6 +89,12 @@ a whole bunch of controllers so I could make a better driver.
Thanks to the Mixxx project for connecting me with Guillemot,
and for providing something interesting to use this driver ;-)
Thanks to JosepMaJAZ for many stability improvements,
code cleanup,
better error checking,
and testing against the 4-Mx.
Contact Me
----------

156
aac123.c
View File

@ -1,156 +0,0 @@
#include <stdio.h>
#include <malloc.h>
#include <alsa/asoundlib.h>
#include <neaacdec.h>
#include <mp4v2/mp4v2.h>
#include "dump.h"
static int
GetAACTrack(MP4FileHandle *infile)
{
/* find AAC track */
MP4TrackId numTracks = MP4GetNumberOfTracks(infile, NULL, 0);
MP4TrackId i;
for (i = 1; i <= numTracks; i++)
{
uint8_t obj_type;
const char *track_type = MP4GetTrackType(infile, i);
if (! track_type) continue;
if (!MP4_IS_AUDIO_TRACK_TYPE(track_type)) continue;
/* MP4GetTrackAudioType */
obj_type = MP4GetTrackEsdsObjectTypeId(infile, i);
if (obj_type == MP4_INVALID_AUDIO_TYPE)
continue;
if (obj_type == MP4_MPEG4_AUDIO_TYPE) {
obj_type = MP4GetTrackAudioMpeg4Type(infile, i);
if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type))
return i;
} else {
if (MP4_IS_AAC_AUDIO_TYPE(obj_type))
return i;
}
}
/* can't decode this */
return -1;
}
int
play_file(snd_pcm_t *snd, char *fn)
{
int track;
MP4FileHandle infile;
NeAACDecHandle hDecoder;
NeAACDecConfigurationPtr config;
unsigned char *buffer;
uint32_t buffer_size;
unsigned long samplerate;
unsigned char channels;
long sampleId, numSamples;
void *sample_buffer;
infile = MP4Read(fn);
if (! infile) {
fprintf(stderr, "Unable to open stream\n");
return 1;
}
if ((track = GetAACTrack(infile)) < 0) {
fprintf(stderr, "GetAACTrack\n");
return 1;
}
hDecoder = NeAACDecOpen();
config = NeAACDecGetCurrentConfiguration(hDecoder);
config->outputFormat = FAAD_FMT_16BIT;
config->downMatrix = 1;
config->defObjectType = LC;
NeAACDecSetConfiguration(hDecoder, config);
buffer = NULL;
buffer_size = 0;
MP4GetTrackESConfiguration(infile, track, &buffer, &buffer_size);
if (NeAACDecInit2(hDecoder, buffer, buffer_size, &samplerate, &channels) < 0) {
fprintf(stderr, "Initializing decoder\n");
return 1;
}
if (snd_pcm_set_params(snd,
SND_PCM_FORMAT_S16,
SND_PCM_ACCESS_RW_INTERLEAVED,
channels,
samplerate,
1,
500000) < 0) {
fprintf(stderr, "Set ALSA params\n");
return 1;
}
if (buffer) {
free(buffer);
}
DUMP_d(MP4GetTrackMaxSampleSize(infile, track));
numSamples = MP4GetTrackNumberOfSamples(infile, track);
DUMP_d(numSamples);
for (sampleId = 1; sampleId <= numSamples; sampleId++) {
int rc;
unsigned int sample_count;
NeAACDecFrameInfo frameInfo;
buffer = NULL;
buffer_size = 0;
rc = MP4ReadSample(infile, track, sampleId, &buffer, &buffer_size, NULL, NULL, NULL, NULL);
if (rc == 0) {
fprintf(stderr, "Read failed\n");
return 1;
}
sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, buffer, buffer_size);
sample_count = frameInfo.samples;
snd_pcm_writei(snd, sample_buffer, sample_count / channels);
}
return 0;
}
int
main(int argc, char *argv[])
{
int i;
snd_pcm_t *snd;
if (snd_pcm_open(&snd, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
fprintf(stderr, "Opening ALSA\n");
return 1;
}
for (i = 1; i < argc; i += 1) {
char *fn = argv[i];
printf("%s\n", fn);
play_file(snd, fn);
}
return 0;
}

67
alsa.c
View File

@ -7,12 +7,20 @@
#include "log.h"
#include "dump.h"
static snd_seq_t *snd_handle;
static int seq_port;
static snd_midi_event_t *midi_event_parser;
#define ALSA_BUFSIZE 4096
#define MAX_PFDS 20
static snd_seq_t *snd_handle = NULL;
static int seq_port;
static snd_midi_event_t *midi_event_parser = NULL;
int npfd;
struct pollfd pfd[MAX_PFDS];
void alsa_interrupting()
{
//Currently nothing to do.
}
int
alsa_setup(const char *name)
@ -31,22 +39,25 @@ alsa_setup(const char *name)
// Allocate parser object for converting to and from MIDI
if (snd_midi_event_new(ALSA_BUFSIZE, &midi_event_parser) < 0) {
fatal("ALSA cannot allocate MIDI event parser");
}
return -1;
} else {
snd_midi_event_no_status(midi_event_parser,1);
return 0;
}
}
void
alsa_close()
{
if (midi_event_parser) {
snd_midi_event_free(midi_event_parser);
}
if (snd_handle) {
snd_seq_close(snd_handle);
}
}
#define MAX_PFDS 20
int npfd;
struct pollfd pfd[MAX_PFDS];
void
alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
{
@ -66,8 +77,6 @@ alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
FD_SET(pfd[i].fd, rfds);
}
}
}
void
@ -96,7 +105,33 @@ alsa_read_ready()
converted = snd_midi_event_decode(midi_event_parser, buf, ALSA_BUFSIZE, ev);
if (converted < 0) {
warn("Can't decode MIDI event type %x", ev->type);
if (ev->type == SND_SEQ_EVENT_CLIENT_START ) {
DUMPf("Client started \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_CLIENT_EXIT) {
DUMPf("Client exited \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_CLIENT_CHANGE) {
DUMPf("Client status/info changed \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_PORT_START) {
DUMPf("Port created \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) {
DUMPf("Port deleted \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_PORT_CHANGE) {
DUMPf("Port status/info changed \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_PORT_SUBSCRIBED) {
DUMPf("Port connected \n");
continue;
} else if (ev->type == SND_SEQ_EVENT_PORT_UNSUBSCRIBED) {
DUMPf("Port disconnected \n");
continue;
} else {
warn("Can't decode MIDI event type 0x%02x", ev->type);
}
continue;
}
@ -137,7 +172,7 @@ alsa_write(uint8_t *data, size_t datalen)
snd_midi_event_init(midi_event_parser);
for (; datalen > offset;) {
while( datalen > offset) {
snd_seq_event_t ev;
long encoded;
int r;
@ -146,9 +181,9 @@ alsa_write(uint8_t *data, size_t datalen)
if (encoded <= 1) {
int i;
warn("Unable to encode MIDI message (%ld < %ld)", encoded, datalen);
warn("Unable to encode MIDI message (%ld < %ld)", encoded, (long int)datalen);
fprintf(stderr, " ");
for (i = offset; i < datalen; i += 1) {
for (i = offset; i < (long int)datalen; i += 1) {
fprintf(stderr, "%02x ", data[i]);
}
fprintf(stderr, "\n");

2
alsa.h
View File

@ -5,6 +5,8 @@
#include <sys/select.h>
int alsa_setup(const char *name);
void alsa_close();
void alsa_interrupting();
void alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds);
void alsa_read_ready();
void alsa_check_fds(fd_set *rfds, fd_set *wfds);

2
dump.h
View File

@ -6,7 +6,7 @@
#include <string.h>
/* Some things I use for debugging */
#ifdef NODUMP
#ifndef DEBUG
# define DUMPf(fmt, args...)
#else
# define DUMPf(fmt, args...) fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##args)

22
hdjd.c
View File

@ -1,3 +1,4 @@
#include <signal.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/select.h>
@ -5,30 +6,42 @@
#include "usb.h"
#include "dump.h"
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
usb_interrupting();
alsa_interrupting();
}
int
setup()
{
char name[100];
if (usb_setup(name, sizeof(name)) < 0) {
usb_finish();
return -1;
}
if (alsa_setup(name) < 0) {
alsa_close();
return -1;
}
return 0;
}
int
main(int argc, char *argv[])
{
if (setup() < 0) {
return 69;
}
for (;;) {
signal(SIGINT, intHandler);
while (keepRunning) {
fd_set rfds;
fd_set wfds;
int nfds = 0;
@ -47,9 +60,10 @@ main(int argc, char *argv[])
alsa_check_fds(&rfds, &wfds);
usb_check_fds(&rfds, &wfds);
//DUMP();
}
printf("Exiting...\n");
usb_finish();
alsa_close();
return 0;
}

248
usb.c
View File

@ -9,82 +9,210 @@
#include "log.h"
#include "dump.h"
static struct libusb_device_handle *usb_dev;
static const struct device *d;
static int writes_pending = 0;
static int reads_pending = 0;
#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 }, // Steel
{ 0xb105, 1, 0x82, 0x03 }, // MP3e2
{ 0xb102, 1, 0x83, 0x04, 0x0, 0 }, // Steel
{ 0xb105, 1, 0x82, 0x03, 0x0, 0 }, // MP3e2
{ 0xb120, 1, 0x82, 0x03, 0x0, 0 }, // Hercules MP3 LE / Glow
{ 0xb107, 1, 0x83, 0x03, 0x0, 0 }, // Hercules Mk4
{ 0xb100, 1, 0x86, 0x06, 0x0, 0 }, // Hercules Mk2
{ 0xb109, 5, 0x83, 0x03, 0x84, 0}, // 4-Mx
{ 0, 0, 0, 0 }
};
void usb_xfer_done(struct libusb_transfer *transfer);
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(256);
buf = (unsigned char *)malloc(buffsize);
// Tell libusb we want to know about bulk transfers
struct libusb_transfer *xfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(xfer, usb_dev, d->ep_in, buf, 256, usb_xfer_done, NULL, 0);
libusb_submit_transfer(xfer);
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;
}
void
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);
usb_initiate_transfer();
}
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)
{
if (libusb_init(NULL) < 0) {
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(NULL) == 0) {
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};
for (d = devices; d->product_id; d += 1) {
usb_dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id);
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) {
fatal("Couldn't find a controller.");
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
{
int ret;
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) {
@ -100,22 +228,49 @@ usb_setup(char *name, size_t namelen)
printf("Opened [%s]\n", name);
}
if (0 != libusb_claim_interface(usb_dev, d->interface_number)) {
fatal("Couldn't claim interface %d", d->interface_number);
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;
}
}
usb_initiate_transfer();
return 0;
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)
{
const struct libusb_pollfd **usb_fds = libusb_get_pollfds(NULL);
int i;
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];
@ -130,36 +285,45 @@ usb_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
}
}
if (reads_pending + writes_pending > 10) {
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)
{
const struct libusb_pollfd **usb_fds = libusb_get_pollfds(NULL);
int i;
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(NULL);
libusb_handle_events(context);
return;
}
}
#if LIBUSB_API_VERSION >= 0x01000104
libusb_free_pollfds(usb_fds);
usb_fds = NULL;
#endif // LIBUSB_API_VERSION >= 0x01000104
}
void
void LIBUSB_CALL
usb_write_done(struct libusb_transfer *xfer)
{
if (xfer->status) {
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);
}
@ -172,8 +336,12 @@ usb_write(uint8_t *data, size_t datalen)
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, d->ep_out, buf, datalen, usb_write_done, NULL, 0);
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);
}

2
usb.h
View File

@ -5,6 +5,8 @@
#include <sys/select.h>
int usb_setup(char *name, size_t namelen);
void usb_finish();
void usb_interrupting();
void usb_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds);
void usb_check_fds(fd_set *rfds, fd_set *wfds);
void usb_write(uint8_t *data, size_t datalen);