|
|
@@ -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 <Arduino.h> |
|
|
|
#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 |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|