From ce7c770a862c148f7379fd6e2f0feda19c2837a6 Mon Sep 17 00:00:00 2001 From: JosepMaJAZ Date: Mon, 16 Jan 2017 21:09:02 +0100 Subject: [PATCH] Added support for DJ Console 4-Mx and improved the overall code --- alsa.c | 73 +++++++++++---- alsa.h | 2 + hdjd.c | 22 ++++- usb.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++---------- usb.h | 2 + 5 files changed, 319 insertions(+), 72 deletions(-) diff --git a/alsa.c b/alsa.c index 6765b66..c33eb7d 100644 --- a/alsa.c +++ b/alsa.c @@ -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 0; + return -1; + } else { + snd_midi_event_no_status(midi_event_parser,1); + return 0; + } } void 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 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 @@ -93,10 +102,36 @@ alsa_read_ready() warn("snd_seq_event_input() = %d", r); continue; } - + 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 ) { + printf("Client started \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_CLIENT_EXIT) { + printf("Client exited \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_CLIENT_CHANGE) { + printf("Client status/info changed \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_PORT_START) { + printf("Port created \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) { + printf("Port deleted \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_PORT_CHANGE) { + printf("Port status/info changed \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_PORT_SUBSCRIBED) { + printf("Port connected \n"); + continue; + } else if (ev->type == SND_SEQ_EVENT_PORT_UNSUBSCRIBED) { + printf("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"); diff --git a/alsa.h b/alsa.h index 52afcb5..c960132 100644 --- a/alsa.h +++ b/alsa.h @@ -5,6 +5,8 @@ #include 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); diff --git a/hdjd.c b/hdjd.c index bea9414..62f5b31 100644 --- a/hdjd.c +++ b/hdjd.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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; } diff --git a/usb.c b/usb.c index fb3951b..7afe577 100644 --- a/usb.c +++ b/usb.c @@ -9,82 +9,211 @@ #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_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; } +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 usb_xfer_done(struct libusb_transfer *xfer) { uint8_t *data = xfer->buffer; int datalen = xfer->actual_length; - reads_pending -= 1; - - alsa_write(data, datalen); + if ( xfer->status == LIBUSB_TRANSFER_COMPLETED ) { + #ifndef NDEBUG + printf("Receiving on %02x:", dev_info->ep_in); + int i; + for (i = 0; i < datalen; i += 1) { + printf("%02x ", data[i]); + } + printf("\n"); + #endif + alsa_write(data, datalen); + } free(data); libusb_free_transfer(xfer); - usb_initiate_transfer(); + 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); + } +} +void +usb_xfer_done_additional(struct libusb_transfer *xfer) +{ + uint8_t *data = xfer->buffer; + 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. + #ifndef NDEBUG + int datalen = xfer->actual_length; + printf("Receiving on %02x:", dev_info->ep_in2); + int i; + for (i = 0; i < datalen; i += 1) { + printf("%02x ", data[i]); + } + printf("\n"); + #endif + } + free(data); + libusb_free_transfer(xfer); + 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); + } } 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; } - - if (libusb_pollfds_handle_timeouts(NULL) == 0) { + //---------------------------------------------------------------------------- + // Enable debug + //---------------------------------------------------------------------------- + #ifndef NDEBUG + 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; } - - for (d = devices; d->product_id; d += 1) { - usb_dev = libusb_open_device_with_vid_pid(NULL, 0x6f8, d->product_id); - if (usb_dev) { - break; - } + //---------------------------------------------------------------------------- + // 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) { - 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 +229,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); - } - - - usb_initiate_transfer(); + 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; + } +} - 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]; @@ -129,38 +285,65 @@ usb_fd_setup(int *nfds, fd_set *rfds, fd_set *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); } - } void usb_check_fds(fd_set *rfds, fd_set *wfds) { - const struct libusb_pollfd **usb_fds = libusb_get_pollfds(NULL); + 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 usb_write_done(struct libusb_transfer *xfer) { - if (xfer->status) { + if ( xfer->status == LIBUSB_TRANSFER_TIMED_OUT ) { + warn("Send timed out"); +#ifndef NDEBUG + uint8_t *data = xfer->buffer; + int datalen = xfer->actual_length; + int i; + for (i = 0; i < datalen; i += 1) { + printf("%02x ", data[i]); + } + printf("\n"); +#endif + } else if ( xfer->status && xfer->status != LIBUSB_TRANSFER_CANCELLED) { warn("USB Write status %d", xfer->status); } writes_pending -= 1; - free(xfer->buffer); + uint8_t *data = xfer->buffer; +#ifndef NDEBUG + int datalen = xfer->actual_length; + printf("Sent on %02x:", dev_info->ep_out); + int i; + for (i = 0; i < datalen; i += 1) { + printf("%02x ", data[i]); + } + printf("\n"); +#endif + free(data); libusb_free_transfer(xfer); } @@ -169,11 +352,22 @@ 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, 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); -} \ No newline at end of file +#ifndef NDEBUG + printf("Preparing to send on %02x:", dev_info->ep_out); + int i; + for (i = 0; i < datalen; i += 1) { + printf("%02x ", data[i]); + } + printf("\n"); +#endif + +} diff --git a/usb.h b/usb.h index e4538a2..cba61b8 100644 --- a/usb.h +++ b/usb.h @@ -5,6 +5,8 @@ #include 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);