From af4e4599f4d6ccb982f04ee596ce4474980eeccc Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Mon, 20 Feb 2017 06:33:13 -0800 Subject: [PATCH] Begin work on MIDI device driver --- USBHost.h | 20 +++++ enumeration.cpp | 4 + k66_usbhost.ino | 1 + midi.cpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 midi.cpp diff --git a/USBHost.h b/USBHost.h index a35d0df..1dc87d2 100644 --- a/USBHost.h +++ b/USBHost.h @@ -311,5 +311,25 @@ private: uint8_t report[8]; }; +class MIDIDevice : public USBDriver { +public: + MIDIDevice(); +protected: + virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); + virtual void disconnect(); + static void rx_callback(const Transfer_t *transfer); + static void tx_callback(const Transfer_t *transfer); + void rx_data(const Transfer_t *transfer); + void tx_data(const Transfer_t *transfer); +private: + Pipe_t *rxpipe; + Pipe_t *txpipe; + enum { BUFFERSIZE = 64 }; + uint8_t buffer[BUFFERSIZE * 2]; + uint8_t rx_ep; + uint8_t tx_ep; + uint16_t rx_size; + uint16_t tx_size; +}; #endif diff --git a/enumeration.cpp b/enumeration.cpp index 737f733..a879d51 100644 --- a/enumeration.cpp +++ b/enumeration.cpp @@ -274,6 +274,10 @@ void USBHost::claim_drivers(Device_t *dev) // found an interface, ask available drivers if they want it prev = NULL; for (driver=available_drivers; driver != NULL; driver = driver->next) { + // TODO: should parse ahead and give claim() + // an accurate length. (end - p) is the rest + // of ALL descriptors, likely more interfaces + // this driver has no business parsing if (driver->claim(dev, 1, p, end - p)) { // this driver claims iface // remove it from available_drivers list diff --git a/k66_usbhost.ino b/k66_usbhost.ino index bbe85e5..1097f8e 100644 --- a/k66_usbhost.ino +++ b/k66_usbhost.ino @@ -28,6 +28,7 @@ USBHub hub1; USBHub hub2; USBHub hub3; KeyboardController keyboard; +MIDIDevice midi1; void setup() { diff --git a/midi.cpp b/midi.cpp new file mode 100644 index 0000000..b4e58eb --- /dev/null +++ b/midi.cpp @@ -0,0 +1,198 @@ +/* USB EHCI Host for Teensy 3.6 + * Copyright 2017 Paul Stoffregen (paul@pjrc.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "USBHost.h" + + +MIDIDevice::MIDIDevice() +{ + // TODO: free Device_t, Pipe_t & Transfer_t we will need + driver_ready_for_device(this); +} + +// Audio Class-Specific Descriptor Types (audio 1.0, page 99) +// CS_UNDEFINED 0x20 +// CS_DEVICE 0x21 +// CS_CONFIGURATION 0x22 +// CS_STRING 0x23 +// CS_INTERFACE 0x24 +// CS_ENDPOINT 0x25 +// MS Class-Specific Interface Descriptor Subtypes (midi 1.0, page 36) +// MS_DESCRIPTOR_UNDEFINED 0x00 +// MS_HEADER 0x01 +// MIDI_IN_JACK 0x02 +// MIDI_OUT_JACK 0x03 +// ELEMENT 0x04 +// MS Class-Specific Endpoint Descriptor Subtypes (midi 1.0, page 36) +// DESCRIPTOR_UNDEFINED 0x00 +// MS_GENERAL 0x01 +// MS MIDI IN and OUT Jack types (midi 1.0, page 36) +// JACK_TYPE_UNDEFINED 0x00 +// EMBEDDED 0x01 +// EXTERNAL 0x02 +// Endpoint Control Selectors (midi 1.0, page 36) +// EP_CONTROL_UNDEFINED 0x00 +// ASSOCIATION_CONTROL 0x01 + +bool MIDIDevice::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) +{ + + // only claim at interface level + if (type != 1) return false; + println("MIDIDevice claim this=", (uint32_t)this, HEX); + println("len = ", len); + + const uint8_t *p = descriptors; + const uint8_t *end = p + len; + + if (p[0] != 9 || p[1] != 4) return false; // interface descriptor + //println(" bInterfaceClass=", p[5]); + //println(" bInterfaceSubClass=", p[6]); + if (p[5] != 1) return false; // bInterfaceClass: 1 = Audio class + if (p[6] != 3) return false; // bInterfaceSubClass: 3 = MIDI + p += 9; + println(" Interface is MIDI"); + rx_ep = 0; + tx_ep = 0; + + while (p < end) { + len = *p; + if (len < 4) return false; // all audio desc are at least 4 bytes + if (p + len > end) return false; // reject if beyond end of data + uint32_t type = p[1]; + //println("type: ", type); + if (type == 4 || type == 11) break; // interface or IAD, not for us + if (type == 0x24) { // 0x24 = Audio CS_INTERFACE, audio 1.0, page 99 + uint32_t subtype = p[2]; + //println("subtype: ", subtype); + if (subtype == 1) { + // Interface Header, midi 1.0, page 21 + println(" MIDI Header (ignored)"); + } else if (subtype == 2) { + // MIDI IN Jack, midi 1.0, page 22 + println(" MIDI IN Jack (ignored)"); + } else if (subtype == 3) { + // MIDI OUT Jack, midi 1.0, page 22 + println(" MIDI OUT Jack (ignored)"); + } else if (subtype == 4) { + // Element Descriptor, midi 1.0, page 23-24 + println(" MIDI Element (ignored)"); + } else { + return false; // unknown + } + } else if (type == 5) { + // endpoint descriptor + if (p[0] < 7) return false; // at least 7 bytes + if (p[3] != 2) return false; // must be bulk type + println(" MIDI Endpoint: ", p[2], HEX); + switch (p[2] & 0xF0) { + case 0x80: + // IN endpoint + if (rx_ep == 0) { + rx_ep = p[2] & 0x0F; + rx_size = p[4] | (p[5] << 8); + println(" rx_size = ", rx_size); + } + break; + case 0x00: + // OUT endpoint + if (tx_ep == 0) { + tx_ep = p[2]; + tx_size = p[4] | (p[5] << 8); + println(" tx_size = ", tx_size); + } + break; + default: + return false; + } + } else if (type == 37) { + // MIDI endpoint info, midi 1.0: 6.2.2, page 26 + println(" MIDI Endpoint Jack Association (ignored)"); + } else { + return false; // unknown + } + p += len; + } + // if an IN endpoint was found, create its pipe + if (rx_ep && rx_size <= BUFFERSIZE) { + rxpipe = new_Pipe(dev, 2, rx_ep, 1, rx_size); + if (rxpipe) { + rxpipe->callback_function = rx_callback; + queue_Data_Transfer(rxpipe, buffer, rx_size, this); + } + } else { + rxpipe = NULL; + } + // if an OUT endpoint was found, create its pipe + if (tx_ep && tx_size <= BUFFERSIZE) { + txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size); + if (txpipe) { + txpipe->callback_function = tx_callback; + } + } else { + rxpipe = NULL; + } + // claim if either pipe created + return (rxpipe || txpipe); +} + +void MIDIDevice::rx_callback(const Transfer_t *transfer) +{ + if (transfer->driver) { + ((MIDIDevice *)(transfer->driver))->rx_data(transfer); + } +} + +void MIDIDevice::tx_callback(const Transfer_t *transfer) +{ + if (transfer->driver) { + ((MIDIDevice *)(transfer->driver))->tx_data(transfer); + } +} + +void MIDIDevice::rx_data(const Transfer_t *transfer) +{ + println("MIDIDevice Receive"); + print(" MIDI Data: "); + print_hexbytes(transfer->buffer, rx_size); + // TODO: parse the new data + queue_Data_Transfer(rxpipe, buffer, rx_size, this); +} + +void MIDIDevice::tx_data(const Transfer_t *transfer) +{ + println("MIDIDevice transmit complete"); + print(" MIDI Data: "); + print_hexbytes(transfer->buffer, tx_size); + // TODO: return the buffer to the pool... +} + + +void MIDIDevice::disconnect() +{ + // TODO: free resources +} + + +