mirror of https://github.com/nealey/hdjd.git
Merge branch 'master' of https://github.com/nealey/hdjd
This commit is contained in:
commit
6fd12c5c21
12
Makefile
12
Makefile
|
@ -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
|
||||||
|
|
21
README.md
21
README.md
|
@ -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
156
aac123.c
|
@ -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
67
alsa.c
|
@ -7,12 +7,20 @@
|
||||||
#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)
|
||||||
|
@ -31,22 +39,25 @@ alsa_setup(const char *name)
|
||||||
// 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 {
|
||||||
|
snd_midi_event_no_status(midi_event_parser,1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
alsa_close()
|
alsa_close()
|
||||||
{
|
{
|
||||||
|
if (midi_event_parser) {
|
||||||
snd_midi_event_free(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)
|
||||||
{
|
{
|
||||||
|
@ -66,8 +77,6 @@ alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
|
||||||
FD_SET(pfd[i].fd, rfds);
|
FD_SET(pfd[i].fd, rfds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -96,7 +105,33 @@ alsa_read_ready()
|
||||||
|
|
||||||
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 ) {
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +172,7 @@ alsa_write(uint8_t *data, size_t datalen)
|
||||||
|
|
||||||
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;
|
||||||
|
@ -146,9 +181,9 @@ alsa_write(uint8_t *data, size_t datalen)
|
||||||
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");
|
||||||
|
|
2
alsa.h
2
alsa.h
|
@ -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
2
dump.h
|
@ -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)
|
||||||
|
|
22
hdjd.c
22
hdjd.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
248
usb.c
248
usb.c
|
@ -9,82 +9,210 @@
|
||||||
#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;
|
|
||||||
|
|
||||||
static int writes_pending = 0;
|
|
||||||
static int reads_pending = 0;
|
|
||||||
|
|
||||||
struct device {
|
struct device {
|
||||||
uint16_t product_id;
|
uint16_t product_id;
|
||||||
uint8_t interface_number;
|
uint8_t interface_number;
|
||||||
uint8_t ep_in;
|
uint8_t ep_in;
|
||||||
uint8_t ep_out;
|
uint8_t ep_out;
|
||||||
|
uint8_t ep_in2;
|
||||||
|
uint8_t interface_number2;
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct device devices[] = {
|
const struct device devices[] = {
|
||||||
{ 0xb102, 1, 0x83, 0x04 }, // Steel
|
{ 0xb102, 1, 0x83, 0x04, 0x0, 0 }, // Steel
|
||||||
{ 0xb105, 1, 0x82, 0x03 }, // MP3e2
|
{ 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 }
|
{ 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
|
static void
|
||||||
usb_initiate_transfer()
|
usb_initiate_transfer()
|
||||||
{
|
{
|
||||||
|
static const int buffsize = 256;
|
||||||
unsigned char *buf;
|
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;
|
||||||
|
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;
|
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);
|
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);
|
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;
|
||||||
|
ret=libusb_init(&context);
|
||||||
|
if (ret < 0) {
|
||||||
|
fatal("ERROR: %s\n%s", libusb_error_name(ret), libusb_strerror(ret));
|
||||||
return -1;
|
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.");
|
fatal("I'm too dumb to handle events on such an old system.");
|
||||||
return -1;
|
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) {
|
ret = libusb_get_device_descriptor(device, &listdesc);
|
||||||
usb_dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id);
|
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) {
|
if (usb_dev) {
|
||||||
break;
|
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 (! 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
|
// Figure out what it's called
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct libusb_device_descriptor ddesc;
|
struct libusb_device_descriptor ddesc;
|
||||||
|
name[0]='\0';
|
||||||
libusb_get_device_descriptor(libusb_get_device(usb_dev), &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);
|
ret = libusb_get_string_descriptor_ascii(usb_dev, ddesc.iManufacturer, (unsigned char *)name, namelen);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
|
@ -100,22 +228,49 @@ usb_setup(char *name, size_t namelen)
|
||||||
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();
|
||||||
|
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_initiate_transfer();
|
usb_finish() {
|
||||||
|
int ret;
|
||||||
return 0;
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
for (i = 0; usb_fds[i]; i += 1) {
|
for (i = 0; usb_fds[i]; i += 1) {
|
||||||
const struct libusb_pollfd *ufd = usb_fds[i];
|
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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
for (i = 0; usb_fds[i]; i += 1) {
|
for (i = 0; usb_fds[i]; i += 1) {
|
||||||
int fd = usb_fds[i]->fd;
|
int fd = usb_fds[i]->fd;
|
||||||
|
|
||||||
if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) {
|
if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) {
|
||||||
libusb_handle_events(NULL);
|
libusb_handle_events(context);
|
||||||
return;
|
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("Send timed out");
|
||||||
|
} else if ( xfer->status && xfer->status != LIBUSB_TRANSFER_CANCELLED) {
|
||||||
warn("USB Write status %d", xfer->status);
|
warn("USB Write status %d", xfer->status);
|
||||||
}
|
}
|
||||||
writes_pending -= 1;
|
writes_pending -= 1;
|
||||||
|
usb_debug_msg("Sent", dev_info->ep_out, xfer->buffer, xfer->actual_length);
|
||||||
free(xfer->buffer);
|
free(xfer->buffer);
|
||||||
libusb_free_transfer(xfer);
|
libusb_free_transfer(xfer);
|
||||||
}
|
}
|
||||||
|
@ -172,8 +336,12 @@ usb_write(uint8_t *data, size_t datalen)
|
||||||
|
|
||||||
writes_pending += 1;
|
writes_pending += 1;
|
||||||
xfer = libusb_alloc_transfer(0);
|
xfer = libusb_alloc_transfer(0);
|
||||||
|
//timeout if in 1000 milliseconds it hasn't been sent
|
||||||
|
xfer->timeout=1000;
|
||||||
buf = (unsigned char *)malloc(datalen);
|
buf = (unsigned char *)malloc(datalen);
|
||||||
memcpy(buf, data, 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);
|
libusb_submit_transfer(xfer);
|
||||||
|
|
||||||
|
usb_debug_msg("Preparing to send", dev_info->ep_out, data, datalen);
|
||||||
}
|
}
|
2
usb.h
2
usb.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue