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 += -Wall
CFLAGS += -Werror CFLAGS += -Werror
TARGETS = hdjd aac123 explore TARGETS = hdjd explore
CFLAGS += -g #CFLAGS += -g -DDEBUG
all: $(TARGETS) all: $(TARGETS)
@ -15,13 +15,5 @@ explore.o: CFLAGS += $(shell pkg-config --cflags libusb-1.0)
alsa.o: CFLAGS += $(shell pkg-config --cflags alsa) alsa.o: CFLAGS += $(shell pkg-config --cflags alsa)
usb.o: CFLAGS += $(shell pkg-config --cflags libusb-1.0) 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: clean:
rm -f $(TARGETS) *.o rm -f $(TARGETS) *.o

View File

@ -24,6 +24,7 @@ The driver currently supports:
* Hercules DJ Control MP3 e2 * Hercules DJ Control MP3 e2
* Hercules DJ Control Steel * Hercules DJ Control Steel
* Hercules DJ Console 4-Mx
Support is planned for: 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 DJ 4Set (the one Guillemot sent me doesn't even work in Windows)
* Hercules Console Mk2 (already works with HID code in Mixxx) * Hercules Console Mk2 (already works with HID code in Mixxx)
* Hercules RMX (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, 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 How To Get
---------- ----------
@ -60,12 +60,11 @@ Just run `hdjd` and it will tell you what it found.
Current Issues Current Issues
-------------- --------------
The driver is pretty crappy right now. Recent versions of Mixxx may not be setting up the USB system correctly,
If you email me, I'm a lot more likely to fix things. resulting in your disk running out of space because
Even if you just say "hi, I'm using your Hercules driver". `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, If you find a problem, please, please, pretty-please,
email me <neale@woozle.org>. 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, Thanks to the Mixxx project for connecting me with Guillemot,
and for providing something interesting to use this driver ;-) 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 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;
}

259
alsa.c
View File

@ -7,168 +7,203 @@
#include "log.h" #include "log.h"
#include "dump.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 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 int
alsa_setup(const char *name) alsa_setup(const char *name)
{ {
if (snd_seq_open(&snd_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { if (snd_seq_open(&snd_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
perror("snd_seq_open"); perror("snd_seq_open");
return -1; return -1;
} }
snd_seq_nonblock(snd_handle, 1); snd_seq_nonblock(snd_handle, 1);
snd_seq_set_client_name(snd_handle, name); snd_seq_set_client_name(snd_handle, name);
seq_port = seq_port =
snd_seq_create_simple_port(snd_handle, name, snd_seq_create_simple_port(snd_handle, name,
SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_READ |
SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
// Allocate parser object for converting to and from MIDI // Allocate parser object for converting to and from MIDI
if (snd_midi_event_new(ALSA_BUFSIZE, &midi_event_parser) < 0) { if (snd_midi_event_new(ALSA_BUFSIZE, &midi_event_parser) < 0) {
fatal("ALSA cannot allocate MIDI event parser"); fatal("ALSA cannot allocate MIDI event parser");
} return -1;
} else {
return 0; snd_midi_event_no_status(midi_event_parser,1);
return 0;
}
} }
void void
alsa_close() alsa_close()
{ {
snd_midi_event_free(midi_event_parser); 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 void
alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds) alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
{ {
npfd = snd_seq_poll_descriptors_count(snd_handle, POLLIN); npfd = snd_seq_poll_descriptors_count(snd_handle, POLLIN);
int i; int i;
if (npfd > MAX_PFDS) { if (npfd > MAX_PFDS) {
fatal("ALSA wants too many file descriptors"); fatal("ALSA wants too many file descriptors");
} }
snd_seq_poll_descriptors(snd_handle, pfd, npfd, POLLIN); snd_seq_poll_descriptors(snd_handle, pfd, npfd, POLLIN);
for (i = 0; i < npfd; i += 1) { for (i = 0; i < npfd; i += 1) {
if (pfd[i].fd > *nfds) { if (pfd[i].fd > *nfds) {
*nfds = pfd[i].fd; *nfds = pfd[i].fd;
} }
if (pfd[i].events & POLLIN) { if (pfd[i].events & POLLIN) {
FD_SET(pfd[i].fd, rfds); FD_SET(pfd[i].fd, rfds);
} }
} }
} }
void void
alsa_read_ready() alsa_read_ready()
{ {
snd_midi_event_init(midi_event_parser); snd_midi_event_init(midi_event_parser);
for (;;) { for (;;) {
snd_seq_event_t *ev; snd_seq_event_t *ev;
unsigned char buf[ALSA_BUFSIZE]; unsigned char buf[ALSA_BUFSIZE];
long converted; long converted;
int r; int r;
r = snd_seq_event_input(snd_handle, &ev); r = snd_seq_event_input(snd_handle, &ev);
if (r == -EAGAIN) { if (r == -EAGAIN) {
// input queue empty // input queue empty
break; break;
} else if (r == -ENOSPC) { } else if (r == -ENOSPC) {
warn("Input queue overflow"); warn("Input queue overflow");
continue; continue;
} else if (r < 0) { } else if (r < 0) {
warn("snd_seq_event_input() = %d", r); warn("snd_seq_event_input() = %d", r);
continue; continue;
} }
converted = snd_midi_event_decode(midi_event_parser, buf, ALSA_BUFSIZE, ev); converted = snd_midi_event_decode(midi_event_parser, buf, ALSA_BUFSIZE, ev);
if (converted < 0) { if (converted < 0) {
warn("Can't decode MIDI event type %x", ev->type); if (ev->type == SND_SEQ_EVENT_CLIENT_START ) {
continue; 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;
}
if (converted < 3) { if (converted < 3) {
int i; int i;
warn("Uh oh, weird MIDI packet with length %ld (does this make sense if prefixed with 90?)", converted); warn("Uh oh, weird MIDI packet with length %ld (does this make sense if prefixed with 90?)", converted);
for (i = 0; i < converted; i += 1) { for (i = 0; i < converted; i += 1) {
fprintf(stderr, " %02x", buf[i]); fprintf(stderr, " %02x", buf[i]);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
usb_write(buf, converted); usb_write(buf, converted);
} }
} }
void void
alsa_check_fds(fd_set *rfds, fd_set *wfds) alsa_check_fds(fd_set *rfds, fd_set *wfds)
{ {
int i; int i;
for (i = 0; i < npfd; i += 1) { for (i = 0; i < npfd; i += 1) {
int fd = pfd[i].fd; int fd = pfd[i].fd;
if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) { if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) {
alsa_read_ready(); alsa_read_ready();
return; return;
} }
} }
} }
void void
alsa_write(uint8_t *data, size_t datalen) alsa_write(uint8_t *data, size_t datalen)
{ {
size_t offset = 0; size_t offset = 0;
snd_midi_event_init(midi_event_parser); snd_midi_event_init(midi_event_parser);
for (; datalen > offset;) { while( datalen > offset) {
snd_seq_event_t ev; snd_seq_event_t ev;
long encoded; long encoded;
int r; int r;
encoded = snd_midi_event_encode(midi_event_parser, data + offset, datalen - offset, &ev); encoded = snd_midi_event_encode(midi_event_parser, data + offset, datalen - offset, &ev);
if (encoded <= 1) { if (encoded <= 1) {
int i; 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, " "); 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, "%02x ", data[i]);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
return; return;
} }
snd_seq_ev_set_direct(&ev); snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_source(&ev, seq_port); snd_seq_ev_set_source(&ev, seq_port);
snd_seq_ev_set_subs(&ev); snd_seq_ev_set_subs(&ev);
r = snd_seq_event_output(snd_handle, &ev); r = snd_seq_event_output(snd_handle, &ev);
if (r < 0) { if (r < 0) {
warn("Couldn't write an output event"); warn("Couldn't write an output event");
} }
if (r > 200) { if (r > 200) {
warn("Output buffer size %d", r); warn("Output buffer size %d", r);
} }
offset += encoded; offset += encoded;
} }
snd_seq_drain_output(snd_handle); snd_seq_drain_output(snd_handle);
} }

2
alsa.h
View File

@ -5,6 +5,8 @@
#include <sys/select.h> #include <sys/select.h>
int alsa_setup(const char *name); 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_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds);
void alsa_read_ready(); void alsa_read_ready();
void alsa_check_fds(fd_set *rfds, fd_set *wfds); void alsa_check_fds(fd_set *rfds, fd_set *wfds);

2
dump.h
View File

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

130
explore.c
View File

@ -19,92 +19,92 @@
#define DUMP_p(v) DUMPf("%s = %p", #v, v) #define DUMP_p(v) DUMPf("%s = %p", #v, v)
struct device { struct device {
char *name; char *name;
uint16_t product_id; uint16_t product_id;
uint8_t ep_in; uint8_t ep_in;
uint8_t ep_out; uint8_t ep_out;
}; };
const struct device devices[] = { const struct device devices[] = {
{"Steel", 0xb102, 0x83, 0x04}, {"Steel", 0xb102, 0x83, 0x04},
{"MP3e2", 0xb105, 0x82, 0x03}, {"MP3e2", 0xb105, 0x82, 0x03},
{"4Set", 0xb10c, 0x84, 0x02}, {"4Set", 0xb10c, 0x84, 0x02},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
struct libusb_device_handle *dev; struct libusb_device_handle *dev;
struct libusb_device_descriptor ddesc; struct libusb_device_descriptor ddesc;
unsigned char name[100]; unsigned char name[100];
const struct device *d; const struct device *d;
int ret; int ret;
if (libusb_init(NULL) < 0) { if (libusb_init(NULL) < 0) {
return 69; return 69;
} }
for (d = devices; d->product_id; d += 1) { for (d = devices; d->product_id; d += 1) {
dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id); dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id);
if (dev) { if (dev) {
break; break;
} }
} }
if (!dev) { if (!dev) {
printf("Couldn't find a controller\n"); printf("Couldn't find a controller\n");
return 69; return 69;
} }
// Figure out what this thing is called // Figure out what this thing is called
libusb_get_device_descriptor(libusb_get_device(dev), &ddesc); libusb_get_device_descriptor(libusb_get_device(dev), &ddesc);
{ {
unsigned char *p = name; unsigned char *p = name;
ret = libusb_get_string_descriptor_ascii(dev, ddesc.iManufacturer, name, sizeof(name)); ret = libusb_get_string_descriptor_ascii(dev, ddesc.iManufacturer, name, sizeof(name));
if (ret > 0) { if (ret > 0) {
p = name + ret; p = name + ret;
*p = ' '; *p = ' ';
p += 1; p += 1;
ret = libusb_get_string_descriptor_ascii(dev, ddesc.iProduct, p, sizeof(name) - ret - 1); ret = libusb_get_string_descriptor_ascii(dev, ddesc.iProduct, p, sizeof(name) - ret - 1);
} }
if (ret < 0) { if (ret < 0) {
printf("Can't figure out what to call this thing.\n"); printf("Can't figure out what to call this thing.\n");
return 69; return 69;
} }
} }
printf("Opened a %s\n", name); printf("Opened a %s\n", name);
while (1) { while (1) {
uint8_t data[256]; uint8_t data[256];
int transferred; int transferred;
int i; int i;
if ((ret = libusb_bulk_transfer(dev, d->ep_in, data, sizeof data, &transferred, 0))) { if ((ret = libusb_bulk_transfer(dev, d->ep_in, data, sizeof data, &transferred, 0))) {
break; break;
} }
for (i = 0; i < transferred; i += 1) { for (i = 0; i < transferred; i += 1) {
printf("%02x ", data[i]); printf("%02x ", data[i]);
} }
printf("\n"); printf("\n");
{ {
uint8_t data[16]; uint8_t data[16];
memset(data, 0xff, sizeof data); memset(data, 0xff, sizeof data);
libusb_bulk_transfer(dev, d->ep_out, data, sizeof data, &transferred, 0); libusb_bulk_transfer(dev, d->ep_out, data, sizeof data, &transferred, 0);
} }
} }
if (ret < 0) { if (ret < 0) {
printf("ERROR: %s\n", libusb_error_name(ret)); printf("ERROR: %s\n", libusb_error_name(ret));
} }
libusb_exit(NULL); libusb_exit(NULL);
return 0; return 0;
} }

22
hdjd.c
View File

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

408
usb.c
View File

@ -9,171 +9,339 @@
#include "log.h" #include "log.h"
#include "dump.h" #include "dump.h"
static struct libusb_device_handle *usb_dev; #define MAX_PFDS 20
static const struct device *d;
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, 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 }
};
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 writes_pending = 0;
static int reads_pending = 0; static int reads_pending = 0;
struct device { void
uint16_t product_id; usb_debug_msg(char *action, int ep, uint8_t data[], size_t datalen)
uint8_t interface_number; {
uint8_t ep_in; #ifdef DEBUG
uint8_t ep_out; 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
}
const struct device devices[] = { void usb_xfer_done(struct libusb_transfer *xfer);
{ 0xb102, 1, 0x83, 0x04 }, // Steel void usb_xfer_done_additional(struct libusb_transfer *xfer);
{ 0xb105, 1, 0x82, 0x03 }, // MP3e2
{ 0, 0, 0, 0 }
};
void usb_xfer_done(struct libusb_transfer *transfer);
void
usb_interrupting()
{
libusb_cancel_transfer(xfer_in);
if (dev_info->ep_in2 != 0x0) {
libusb_cancel_transfer(xfer_in2);
}
}
static void static void
usb_initiate_transfer() usb_initiate_transfer()
{ {
unsigned char *buf; 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 // Tell libusb we want to know about bulk transfers
struct libusb_transfer *xfer = libusb_alloc_transfer(0); xfer_in = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(xfer, usb_dev, d->ep_in, buf, 256, usb_xfer_done, NULL, 0); //timeout if in 1000 milliseconds it hasn't been sent
libusb_submit_transfer(xfer); xfer_in->timeout=1000;
reads_pending += 1; 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) usb_xfer_done(struct libusb_transfer *xfer)
{ {
uint8_t *data = xfer->buffer; uint8_t *data = xfer->buffer;
int datalen = xfer->actual_length; int datalen = xfer->actual_length;
reads_pending -= 1;
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);
}
alsa_write(data, datalen); free(data);
free(data); libusb_free_transfer(xfer);
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 int
usb_setup(char *name, size_t namelen) usb_setup(char *name, size_t namelen)
{ {
if (libusb_init(NULL) < 0) { int ret;
return -1; 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;
}
if (libusb_pollfds_handle_timeouts(NULL) == 0) { // Figure out what it's called
fatal("I'm too dumb to handle events on such an old system."); {
return -1; struct libusb_device_descriptor ddesc;
} name[0]='\0';
libusb_get_device_descriptor(libusb_get_device(usb_dev), &ddesc);
for (d = devices; d->product_id; d += 1) { ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iManufacturer, (unsigned char *)name, namelen);
usb_dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id); if (ret > 0) {
if (usb_dev) { char *p = name + ret;
break;
}
}
if (! usb_dev) {
fatal("Couldn't find a controller.");
}
// Figure out what it's called
{
int ret;
struct libusb_device_descriptor ddesc;
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 = ' ';
p += 1; p += 1;
ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iProduct, (unsigned char *)p, namelen - ret - 1); ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iProduct, (unsigned char *)p, namelen - ret - 1);
} }
if (ret < 0) { if (ret < 0) {
warn("I can't figure out what to call this thing."); warn("I can't figure out what to call this thing.");
} }
printf("Opened [%s]\n", name); printf("Opened [%s]\n", name);
} }
if (0 != libusb_claim_interface(usb_dev, d->interface_number)) { ret = libusb_claim_interface(usb_dev, dev_info->interface_number);
fatal("Couldn't claim interface %d", d->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(); }
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;
}
}
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 void
usb_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds) usb_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
{ {
const struct libusb_pollfd **usb_fds = libusb_get_pollfds(NULL); usb_fds = libusb_get_pollfds(context);
int i; if (usb_fds == NULL) {
warn("could not get the filedescriptors! This is unexpected");
}
for (i = 0; usb_fds[i]; i += 1) { int i;
const struct libusb_pollfd *ufd = usb_fds[i]; for (i = 0; usb_fds[i]; i += 1) {
const struct libusb_pollfd *ufd = usb_fds[i];
if (ufd->fd > *nfds) { if (ufd->fd > *nfds) {
*nfds = ufd->fd; *nfds = ufd->fd;
} }
if (ufd->events & POLLIN) { if (ufd->events & POLLIN) {
FD_SET(ufd->fd, rfds); FD_SET(ufd->fd, rfds);
} }
if (ufd->events & POLLOUT) { if (ufd->events & POLLOUT) {
FD_SET(ufd->fd, wfds); FD_SET(ufd->fd, 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); warn("%d(r)+%d(w) = %d outstanding USB transactions!", reads_pending, writes_pending, reads_pending + writes_pending);
} }
} }
void void
usb_check_fds(fd_set *rfds, fd_set *wfds) usb_check_fds(fd_set *rfds, fd_set *wfds)
{ {
const struct libusb_pollfd **usb_fds = libusb_get_pollfds(NULL); if (usb_fds == NULL) {
int i; return;
}
for (i = 0; usb_fds[i]; i += 1) {
int fd = usb_fds[i]->fd;
if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) { int i;
libusb_handle_events(NULL); for (i = 0; usb_fds[i]; i += 1) {
return; 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 void LIBUSB_CALL
usb_write_done(struct libusb_transfer *xfer) usb_write_done(struct libusb_transfer *xfer)
{ {
if (xfer->status) { if ( xfer->status == LIBUSB_TRANSFER_TIMED_OUT ) {
warn("USB Write status %d", xfer->status); warn("Send timed out");
} } else if ( xfer->status && xfer->status != LIBUSB_TRANSFER_CANCELLED) {
writes_pending -= 1; warn("USB Write status %d", xfer->status);
free(xfer->buffer); }
libusb_free_transfer(xfer); writes_pending -= 1;
usb_debug_msg("Sent", dev_info->ep_out, xfer->buffer, xfer->actual_length);
free(xfer->buffer);
libusb_free_transfer(xfer);
} }
void void
usb_write(uint8_t *data, size_t datalen) usb_write(uint8_t *data, size_t datalen)
{ {
struct libusb_transfer *xfer; struct libusb_transfer *xfer;
unsigned char *buf; unsigned char *buf;
writes_pending += 1; writes_pending += 1;
xfer = libusb_alloc_transfer(0); xfer = libusb_alloc_transfer(0);
buf = (unsigned char *)malloc(datalen); //timeout if in 1000 milliseconds it hasn't been sent
memcpy(buf, data, datalen); xfer->timeout=1000;
libusb_fill_bulk_transfer(xfer, usb_dev, d->ep_out, buf, datalen, usb_write_done, NULL, 0); buf = (unsigned char *)malloc(datalen);
libusb_submit_transfer(xfer); 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);
}

2
usb.h
View File

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