Compare commits
3 Commits
master
...
usb_midi_l
Author | SHA1 | Date |
---|---|---|
|
5e802093a7 | 5 months ago |
|
58adc59e59 | 5 months ago |
|
7b5629db32 | 6 months ago |
5 changed files with 764 additions and 0 deletions
@ -0,0 +1,145 @@ |
|||
/** Example of setting reading MIDI Input via USB Host
|
|||
* |
|||
* |
|||
* This requires a USB-A connector |
|||
* |
|||
* This example will also log incoming messages to the serial port for general MIDI troubleshooting |
|||
*/ |
|||
#include "usb_midi_launchpad_transport.hh" |
|||
#include "daisy_seed.h" |
|||
#include "usbh_midi_launchpad.h" |
|||
|
|||
/** This prevents us from having to type "daisy::" in front of a lot of things. */ |
|||
using namespace daisy; |
|||
using MidiUsbLaunchpadHandler = MidiHandler<UsbMidiLaunchpadTransport>; |
|||
|
|||
|
|||
/** Global Hardware access */ |
|||
DaisySeed hw{}; |
|||
//MidiUsbHandler midi;
|
|||
MidiUsbLaunchpadHandler midi; |
|||
USBHostHandle usbHost; |
|||
|
|||
/** FIFO to hold messages as we're ready to print them */ |
|||
FIFO<MidiEvent, 128> event_log; |
|||
|
|||
void USBH_Connect(void* data) |
|||
{ |
|||
hw.PrintLine("device connected"); |
|||
} |
|||
|
|||
void USBH_Disconnect(void* data) |
|||
{ |
|||
hw.PrintLine("device disconnected"); |
|||
} |
|||
|
|||
void USBH_ClassActive(void* data) |
|||
{ |
|||
if (usbHost.IsActiveClass(USBH_MIDI_LAUNCHPAD_CLASS)) { |
|||
hw.PrintLine("MIDI device class active"); |
|||
MidiUsbLaunchpadHandler::Config midi_config{}; |
|||
midi_config.transport_config.periph = UsbMidiLaunchpadTransport::Config::Periph::HOST; |
|||
midi.Init(midi_config); |
|||
midi.StartReceive(); |
|||
} |
|||
} |
|||
|
|||
void USBH_Error(void* data) |
|||
{ |
|||
hw.PrintLine("USB device error"); |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
/** Initialize our hardware */ |
|||
hw.Init(); |
|||
|
|||
hw.StartLog(true); |
|||
|
|||
hw.PrintLine("MIDI USB Host start"); |
|||
|
|||
/** Configure USB host */ |
|||
USBHostHandle::Config usbhConfig{}; |
|||
usbhConfig.connect_callback = USBH_Connect; |
|||
usbhConfig.disconnect_callback = USBH_Disconnect; |
|||
usbhConfig.class_active_callback = USBH_ClassActive; |
|||
usbhConfig.error_callback = USBH_Error; |
|||
usbHost.Init(usbhConfig); |
|||
|
|||
usbHost.RegisterClass(USBH_MIDI_LAUNCHPAD_CLASS); |
|||
|
|||
uint32_t now = System::GetNow(); |
|||
uint32_t log_time = System::GetNow(); |
|||
uint32_t blink_time = 0; |
|||
bool ledState = false; |
|||
|
|||
hw.PrintLine("MIDI USB Host initialized"); |
|||
|
|||
/** Infinite Loop */ |
|||
while (1) { |
|||
now = System::GetNow(); |
|||
|
|||
if (now > blink_time) { |
|||
hw.SetLed(ledState); |
|||
ledState = !ledState; |
|||
if (usbHost.GetPresent()) |
|||
blink_time = now + 400; |
|||
else |
|||
blink_time = now + 80; |
|||
} |
|||
/** Run USB host process */ |
|||
usbHost.Process(); |
|||
|
|||
if (usbHost.IsActiveClass(USBH_MIDI_LAUNCHPAD_CLASS) && midi.RxActive()) { |
|||
/** Process MIDI in the background */ |
|||
midi.Listen(); |
|||
|
|||
/** Loop through any MIDI Events */ |
|||
while (midi.HasEvents()) { |
|||
MidiEvent msg = midi.PopEvent(); |
|||
|
|||
/** Handle messages as they come in
|
|||
* See DaisyExamples for some examples of this |
|||
*/ |
|||
switch (msg.type) { |
|||
|
|||
case NoteOn: |
|||
case NoteOff: |
|||
// Do something on Note On events
|
|||
{ |
|||
uint8_t bytes[2] = { 0x00, 0x00 }; |
|||
bytes[0] = msg.data[0]; |
|||
bytes[1] = msg.data[1]; |
|||
midi.SendMessage(bytes, 2); |
|||
} |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
/** Regardless of message, let's add the message data to our queue to output */ |
|||
event_log.PushBack(msg); |
|||
} |
|||
|
|||
/** Now separately, every 5ms we'll print the top message in our queue if there is one */ |
|||
if (now - log_time > 5) { |
|||
log_time = now; |
|||
if (!event_log.IsEmpty()) { |
|||
auto msg = event_log.PopFront(); |
|||
char outstr[128]; |
|||
const char* type_str = MidiEvent::GetTypeAsString(msg); |
|||
sprintf( |
|||
outstr, |
|||
"time:\t%ld\ttype: %s\tChannel: %d\tData MSB: " |
|||
"%d\tData LSB: %d\n", |
|||
now, |
|||
type_str, |
|||
msg.channel, |
|||
msg.data[0], |
|||
msg.data[1]); |
|||
hw.PrintLine(outstr); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,242 @@ |
|||
#include "system.h" |
|||
#include "usbd_cdc.h" |
|||
#include "usbh_midi_launchpad.h" |
|||
#include "usb_midi_launchpad_transport.hh" |
|||
#include <cassert> |
|||
#include "daisy_seed.h" |
|||
|
|||
extern "C" |
|||
{ |
|||
extern USBH_HandleTypeDef hUsbHostHS; |
|||
} |
|||
|
|||
#define pUSB_Host &hUsbHostHS |
|||
|
|||
using namespace daisy; |
|||
|
|||
class UsbMidiLaunchpadTransport::Impl |
|||
{ |
|||
public: |
|||
void Init(Config config); |
|||
|
|||
void StartRx(MidiRxParseCallback callback, void* context) |
|||
{ |
|||
FlushRx(); |
|||
rx_active_ = true; |
|||
parse_callback_ = callback; |
|||
parse_context_ = context; |
|||
} |
|||
|
|||
bool RxActive() { return rx_active_; } |
|||
void FlushRx() { rx_buffer_.Flush(); } |
|||
void Tx(uint8_t* buffer, size_t size); |
|||
|
|||
void UsbToMidi(uint8_t); |
|||
void MidiToUsb(uint8_t* buffer, size_t length); |
|||
void Parse(); |
|||
|
|||
private: |
|||
void MidiToUsbSingle(uint8_t* buffer, size_t length); |
|||
|
|||
/** USB Handle for CDC transfers
|
|||
*/ |
|||
UsbHandle usb_handle_; |
|||
Config config_; |
|||
|
|||
static constexpr size_t kBufferSize = 1024; |
|||
bool rx_active_; |
|||
// This corresponds to 256 midi messages
|
|||
RingBuffer<uint8_t, kBufferSize> rx_buffer_; |
|||
MidiRxParseCallback parse_callback_; |
|||
void* parse_context_; |
|||
|
|||
// simple, self-managed buffer
|
|||
uint8_t tx_buffer_[kBufferSize]; |
|||
size_t tx_ptr_; |
|||
|
|||
// MIDI message size determined by the
|
|||
// code index number. You can find this
|
|||
// table in the MIDI USB spec 1.0
|
|||
const uint8_t code_index_size_[16] |
|||
= {3, 3, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1}; |
|||
|
|||
const uint8_t midi_message_size_[16] |
|||
= {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0}; |
|||
|
|||
// Masks to check for message type, and byte content
|
|||
const uint8_t kStatusByteMask = 0x80; |
|||
const uint8_t kMessageMask = 0x70; |
|||
const uint8_t kDataByteMask = 0x7F; |
|||
const uint8_t kSystemCommonMask = 0xF0; |
|||
const uint8_t kChannelMask = 0x0F; |
|||
const uint8_t kRealTimeMask = 0xF8; |
|||
const uint8_t kSystemRealTimeMask = 0x07; |
|||
}; |
|||
|
|||
// Global Impl
|
|||
static UsbMidiLaunchpadTransport::Impl midi_usb_handle; |
|||
|
|||
extern daisy::DaisySeed hw; |
|||
|
|||
void to_string_array_uint(char* outbuf, uint8_t* arr, size_t len) |
|||
{ |
|||
for (uint32_t i = 0; i < len; i++) { |
|||
char charbuf[100] = ""; |
|||
sprintf(charbuf, "[%x]", arr[i]); |
|||
strcat(outbuf, charbuf); |
|||
} |
|||
} |
|||
|
|||
void ReceiveCallback(uint8_t* buffer, uint32_t* length) |
|||
{ |
|||
{ |
|||
char strbuf[1000] = ""; |
|||
to_string_array_uint(strbuf, buffer, *length); |
|||
hw.PrintLine("RX RAW: %x - %s", *length, strbuf); |
|||
} |
|||
if (midi_usb_handle.RxActive()) { |
|||
for (unsigned int notemsg = 0; notemsg < *length; notemsg += 2) { |
|||
uint8_t midi_note[3] = { 0x90, 0x00, 0x00 }; |
|||
midi_note[1] = buffer[notemsg]; |
|||
midi_note[2] = buffer[notemsg += 1]; |
|||
midi_usb_handle.UsbToMidi(midi_note[0]); |
|||
midi_usb_handle.UsbToMidi(midi_note[1]); |
|||
midi_usb_handle.UsbToMidi(midi_note[2]); |
|||
midi_usb_handle.Parse(); |
|||
|
|||
char strbuf[1000] = ""; |
|||
to_string_array_uint(strbuf, midi_note, 3); |
|||
hw.PrintLine("RX NOTE: %s", strbuf); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void HostReceiveCallback(uint8_t* buffer, size_t sz, void* pUser) |
|||
{ |
|||
uint32_t len = sz; |
|||
ReceiveCallback(buffer, &len); |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::Init(Config config) |
|||
{ |
|||
// Borrowed from logger
|
|||
/** this implementation relies on the fact that UsbHandle class has no member variables and can be shared
|
|||
* assert this statement: |
|||
*/ |
|||
// static_assert(1u == sizeof(UsbMidiLaunchpadTransport::Impl::usb_handle_), "UsbHandle is not static");
|
|||
|
|||
config_ = config; |
|||
rx_active_ = false; |
|||
|
|||
if(config_.periph == Config::HOST) |
|||
{ |
|||
System::Delay(10); |
|||
USBH_MIDI_Launchpad_SetReceiveCallback(pUSB_Host, HostReceiveCallback, nullptr); |
|||
} |
|||
else |
|||
{ |
|||
// This tells the USB middleware to send out MIDI descriptors instead of CDC
|
|||
usbd_mode = USBD_MODE_MIDI; |
|||
|
|||
UsbHandle::UsbPeriph periph = UsbHandle::FS_INTERNAL; |
|||
if(config_.periph == Config::EXTERNAL) |
|||
periph = UsbHandle::FS_EXTERNAL; |
|||
|
|||
usb_handle_.Init(periph); |
|||
|
|||
System::Delay(10); |
|||
usb_handle_.SetReceiveCallback(ReceiveCallback, periph); |
|||
} |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::Tx(uint8_t* buffer, size_t size) |
|||
{ |
|||
int attempt_count = config_.tx_retry_count; |
|||
bool should_retry; |
|||
|
|||
// MidiToUsb(buffer, size);
|
|||
do { |
|||
if (config_.periph == Config::HOST) { |
|||
MIDI_Launchpad_ErrorTypeDef result; |
|||
result = USBH_MIDI_Launchpad_Transmit(pUSB_Host, buffer, size); |
|||
{ |
|||
char strbuf[1000] = ""; |
|||
to_string_array_uint(strbuf, buffer, size); |
|||
hw.PrintLine("Transport Tx() RES=%i - %s",result, strbuf); |
|||
} |
|||
|
|||
should_retry = (result == MIDI_BUSY) && attempt_count--; |
|||
} |
|||
if (should_retry) { |
|||
System::DelayUs(100); |
|||
} |
|||
} while (should_retry); |
|||
|
|||
tx_ptr_ = 0; |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::UsbToMidi(uint8_t buffer) |
|||
{ |
|||
if (rx_buffer_.writable() > 0) { |
|||
rx_buffer_.Write(buffer); |
|||
} else { |
|||
rx_active_ = false; // disable on overflow
|
|||
} |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::MidiToUsbSingle(uint8_t* buffer, size_t size) |
|||
{ |
|||
for (int i = 0; i < size; i++) { |
|||
tx_buffer_[tx_ptr_] = buffer[i]; |
|||
tx_ptr_++; |
|||
} |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::MidiToUsb(uint8_t* buffer, size_t size) |
|||
{ |
|||
MidiToUsbSingle(buffer, size); |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Impl::Parse() |
|||
{ |
|||
if(parse_callback_) |
|||
{ |
|||
uint8_t bytes[kBufferSize]; |
|||
size_t i = 0; |
|||
while(!rx_buffer_.isEmpty()) |
|||
{ |
|||
bytes[i++] = rx_buffer_.Read(); |
|||
} |
|||
parse_callback_(bytes, i, parse_context_); |
|||
} |
|||
} |
|||
|
|||
////////////////////////////////////////////////
|
|||
// UsbMidiLaunchpadTransport -> UsbMidiLaunchpadTransport::Impl
|
|||
////////////////////////////////////////////////
|
|||
|
|||
void UsbMidiLaunchpadTransport::Init(UsbMidiLaunchpadTransport::Config config) |
|||
{ |
|||
pimpl_ = &midi_usb_handle; |
|||
pimpl_->Init(config); |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::StartRx(MidiRxParseCallback callback, void* context) |
|||
{ |
|||
pimpl_->StartRx(callback, context); |
|||
} |
|||
|
|||
bool UsbMidiLaunchpadTransport::RxActive() |
|||
{ |
|||
return pimpl_->RxActive(); |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::FlushRx() |
|||
{ |
|||
pimpl_->FlushRx(); |
|||
} |
|||
|
|||
void UsbMidiLaunchpadTransport::Tx(uint8_t* buffer, size_t size) |
|||
{ |
|||
pimpl_->Tx(buffer, size); |
|||
} |
@ -0,0 +1,60 @@ |
|||
#ifndef LIBDIZZY_USB_MIDI_LAUNCHPAD_TRANSPORT_HH |
|||
#define LIBDIZZY_USB_MIDI_LAUNCHPAD_TRANSPORT_HH |
|||
|
|||
#include "hid/usb.h" |
|||
#include "sys/system.h" |
|||
#include "util/ringbuffer.h" |
|||
|
|||
namespace daisy { |
|||
/** @brief USB Transport for MIDI
|
|||
* @ingroup midi |
|||
*/ |
|||
class UsbMidiLaunchpadTransport { |
|||
public: |
|||
typedef void (*MidiRxParseCallback)(uint8_t* data, size_t size, void* context); |
|||
|
|||
struct Config { |
|||
enum Periph { |
|||
INTERNAL = 0, |
|||
EXTERNAL, |
|||
HOST |
|||
}; |
|||
|
|||
Periph periph; |
|||
|
|||
/**
|
|||
* When sending MIDI messages immediately back-to-back in user code, |
|||
* sometimes the USB CDC driver is still "busy". |
|||
* |
|||
* This option configures the number of times to retry a Tx after |
|||
* delaying for 100 microseconds (default = 3 retries). |
|||
* |
|||
* If you set this to zero, Tx will not retry so the attempt will block |
|||
* for slightly less time, but transmit can fail if the Tx state is busy. |
|||
*/ |
|||
uint8_t tx_retry_count; |
|||
|
|||
Config() : periph(INTERNAL), tx_retry_count(3) {} |
|||
}; |
|||
|
|||
void Init(Config config); |
|||
|
|||
void StartRx(MidiRxParseCallback callback, void* context); |
|||
bool RxActive(); |
|||
void FlushRx(); |
|||
void Tx(uint8_t* buffer, size_t size); |
|||
|
|||
class Impl; |
|||
|
|||
UsbMidiLaunchpadTransport() : pimpl_(nullptr) {} |
|||
~UsbMidiLaunchpadTransport() {} |
|||
UsbMidiLaunchpadTransport(const UsbMidiLaunchpadTransport& other) = default; |
|||
UsbMidiLaunchpadTransport& operator=(const UsbMidiLaunchpadTransport& other) = default; |
|||
|
|||
private: |
|||
Impl* pimpl_; |
|||
}; |
|||
|
|||
} // namespace daisy
|
|||
|
|||
#endif |
@ -0,0 +1,253 @@ |
|||
#include "usbh_midi_launchpad.h" |
|||
#include "daisy_core.h" |
|||
#include <stdbool.h> |
|||
|
|||
static USB_MIDI_Launchpad_HandleTypeDef DMA_BUFFER_MEM_SECTION static_midi; |
|||
|
|||
static USBH_StatusTypeDef USBH_MIDI_InterfaceInit(USBH_HandleTypeDef *phost); |
|||
static USBH_StatusTypeDef USBH_MIDI_InterfaceDeInit(USBH_HandleTypeDef *phost); |
|||
static USBH_StatusTypeDef USBH_MIDI_Process(USBH_HandleTypeDef *phost); |
|||
static USBH_StatusTypeDef USBH_MIDI_ClassRequest(USBH_HandleTypeDef *phost); |
|||
static USBH_StatusTypeDef USBH_MIDI_SOFProcess(USBH_HandleTypeDef *phost); |
|||
|
|||
USBH_ClassTypeDef USBH_Launchpad_midi = { |
|||
"MIDI", |
|||
USB_LAUNCHPAD_CLASS, |
|||
USBH_MIDI_InterfaceInit, |
|||
USBH_MIDI_InterfaceDeInit, |
|||
USBH_MIDI_ClassRequest, |
|||
USBH_MIDI_Process, |
|||
USBH_MIDI_SOFProcess, |
|||
NULL, |
|||
}; |
|||
|
|||
#define EP_IN 0x81U |
|||
|
|||
/**
|
|||
* @brief USBH_MIDI_InterfaceInit |
|||
* The function init the MIDI class. |
|||
* @param phost: Host handle |
|||
* @retval USBH Status |
|||
*/ |
|||
static USBH_StatusTypeDef USBH_MIDI_InterfaceInit(USBH_HandleTypeDef *phost) |
|||
{ |
|||
USBH_UsrLog(__FUNCTION__); |
|||
USBH_StatusTypeDef status; |
|||
USB_MIDI_Launchpad_HandleTypeDef *MIDI_Handle; |
|||
|
|||
// Single static instance of midi handle
|
|||
phost->pActiveClass->pData = &static_midi; |
|||
MIDI_Handle = (USB_MIDI_Launchpad_HandleTypeDef *)phost->pActiveClass->pData; |
|||
USBH_memset(MIDI_Handle, 0, sizeof(USB_MIDI_Launchpad_HandleTypeDef)); |
|||
|
|||
uint8_t interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, |
|||
USB_LAUNCHPAD_STREAMING_SUBCLASS, 0xFFU); |
|||
|
|||
if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) { |
|||
USBH_DbgLog("Cannot find interface for %s class.", phost->pActiveClass->Name); |
|||
return USBH_FAIL; |
|||
} |
|||
status = USBH_SelectInterface(phost, interface); |
|||
if (status != USBH_OK) { |
|||
return USBH_FAIL; |
|||
} |
|||
|
|||
/* Find the endpoints */ |
|||
for (int ep = 0; ep < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; ++ep) { |
|||
if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[ep].bEndpointAddress & EP_IN) { |
|||
MIDI_Handle->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[ep].bEndpointAddress; |
|||
MIDI_Handle->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[ep].wMaxPacketSize & 0x03FFU; |
|||
if (MIDI_Handle->InEpSize > USBH_MIDI_LAUNCHPAD_RX_BUF_SIZE) { |
|||
MIDI_Handle->InEpSize = USBH_MIDI_LAUNCHPAD_RX_BUF_SIZE; |
|||
} |
|||
/* Allocate and open input pipe */ |
|||
MIDI_Handle->InPipe = USBH_AllocPipe(phost, MIDI_Handle->InEp); |
|||
USBH_OpenPipe(phost, MIDI_Handle->InPipe, MIDI_Handle->InEp, |
|||
phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, |
|||
MIDI_Handle->InEpSize); |
|||
// USBH_OpenPipe(phost, MIDI_Handle->InPipe, MIDI_Handle->InEp,
|
|||
// phost->device.address, phost->device.speed, USB_EP_TYPE_INTR,
|
|||
// MIDI_Handle->InEpSize);
|
|||
(void)USBH_LL_SetToggle(phost, MIDI_Handle->InPipe, 0U); |
|||
USBH_UsrLog("InEP[%d] %02x size=%u", ep, MIDI_Handle->InEp, MIDI_Handle->InEpSize); |
|||
} else { |
|||
MIDI_Handle->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[ep].bEndpointAddress; |
|||
MIDI_Handle->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[ep].wMaxPacketSize & 0x03FFU; |
|||
/* Allocate and open output pipe */ |
|||
MIDI_Handle->OutPipe = USBH_AllocPipe(phost, MIDI_Handle->OutEp); |
|||
USBH_OpenPipe(phost, MIDI_Handle->OutPipe, MIDI_Handle->OutEp, |
|||
phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, |
|||
MIDI_Handle->OutEpSize); |
|||
// USBH_OpenPipe(phost, MIDI_Handle->OutPipe, MIDI_Handle->OutEp,
|
|||
// phost->device.address, phost->device.speed, USB_EP_TYPE_INTR,
|
|||
// MIDI_Handle->OutEpSize);
|
|||
(void)USBH_LL_SetToggle(phost, MIDI_Handle->OutPipe, 0U); |
|||
USBH_UsrLog("OutEP[%d] %02x size=%u", ep, MIDI_Handle->OutEp, MIDI_Handle->OutEpSize); |
|||
} |
|||
} |
|||
|
|||
MIDI_Handle->state = MIDI_INIT; |
|||
MIDI_Handle->error = MIDI_OK; |
|||
|
|||
return USBH_OK; |
|||
} |
|||
|
|||
/**
|
|||
* @brief USBH_MIDI_InterfaceDeInit |
|||
* The function DeInit the Pipes used for the MIDI class. |
|||
* @param phost: Host handle |
|||
* @retval USBH Status |
|||
*/ |
|||
static USBH_StatusTypeDef USBH_MIDI_InterfaceDeInit(USBH_HandleTypeDef *phost) |
|||
{ |
|||
USBH_UsrLog(__FUNCTION__); |
|||
USB_MIDI_Launchpad_HandleTypeDef *MIDI_Handle = (USB_MIDI_Launchpad_HandleTypeDef *) phost->pActiveClass->pData; |
|||
if (MIDI_Handle) { |
|||
if (MIDI_Handle->InPipe) { |
|||
USBH_ClosePipe(phost, MIDI_Handle->InPipe); |
|||
USBH_FreePipe(phost, MIDI_Handle->InPipe); |
|||
MIDI_Handle->InPipe = 0U; /* Reset the Channel as Free */ |
|||
} |
|||
if (MIDI_Handle->OutPipe) { |
|||
USBH_ClosePipe(phost, MIDI_Handle->OutPipe); |
|||
USBH_FreePipe(phost, MIDI_Handle->OutPipe); |
|||
MIDI_Handle->InPipe = 0U; /* Reset the Channel as Free */ |
|||
} |
|||
phost->pActiveClass->pData = 0U; |
|||
MIDI_Handle->state = MIDI_INIT; |
|||
MIDI_Handle->error = MIDI_OK; |
|||
} |
|||
return USBH_OK; |
|||
} |
|||
|
|||
/**
|
|||
* @brief USBH_MIDI_ClassRequest |
|||
* The function is responsible for handling Standard requests |
|||
* for MIDI class. |
|||
* @param phost: Host handle |
|||
* @retval USBH Status |
|||
*/ |
|||
static USBH_StatusTypeDef USBH_MIDI_ClassRequest(USBH_HandleTypeDef *phost) |
|||
{ |
|||
phost->pUser(phost, HOST_USER_CLASS_ACTIVE); |
|||
return USBH_OK; |
|||
} |
|||
|
|||
/**
|
|||
* @brief USBH_MIDI_Process |
|||
* The function is for managing state machine for MIDI data transfers |
|||
* @param phost: Host handle |
|||
* @retval USBH Status |
|||
*/ |
|||
static USBH_StatusTypeDef USBH_MIDI_Process(USBH_HandleTypeDef *phost) |
|||
{ |
|||
if (!phost->pActiveClass || !phost->pActiveClass->pData) |
|||
return USBH_FAIL; |
|||
|
|||
USB_MIDI_Launchpad_HandleTypeDef *hMidi = (USB_MIDI_Launchpad_HandleTypeDef *)phost->pActiveClass->pData; |
|||
USBH_StatusTypeDef error = USBH_OK; |
|||
USBH_URBStateTypeDef rxStatus; |
|||
|
|||
switch (hMidi->state) { |
|||
case MIDI_INIT: |
|||
hMidi->state = MIDI_IDLE; |
|||
break; |
|||
case MIDI_IDLE: |
|||
hMidi->state = MIDI_RX; |
|||
break; |
|||
case MIDI_RX: |
|||
// Always returns USBH_OK, call USBH_LL_GetURBState() for status
|
|||
// USBH_InterruptReceiveData(phost, hMidi->rxBuffer, hMidi->InEpSize, hMidi->InPipe);
|
|||
// static uint8_t togglestate = 0;
|
|||
// if(togglestate == 0) {
|
|||
// togglestate = 1;
|
|||
// } else {
|
|||
// togglestate = 0;
|
|||
// }
|
|||
// (void)USBH_LL_SetToggle(phost, hMidi->InPipe, togglestate);
|
|||
|
|||
USBH_BulkReceiveData(phost, hMidi->rxBuffer, hMidi->InEpSize, hMidi->InPipe); |
|||
hMidi->state = MIDI_RX_POLL; |
|||
break; |
|||
case MIDI_RX_POLL: |
|||
rxStatus = USBH_LL_GetURBState(phost, hMidi->InPipe); |
|||
if (rxStatus == USBH_URB_NOTREADY || rxStatus == USBH_URB_IDLE) { |
|||
hMidi->state = MIDI_RX_POLL; |
|||
} else if (rxStatus == USBH_URB_DONE) { |
|||
size_t sz = USBH_LL_GetLastXferSize(phost, hMidi->InPipe); |
|||
hMidi->state = MIDI_RX; |
|||
if (hMidi->callback) { |
|||
hMidi->callback(hMidi->rxBuffer, sz, hMidi->pUser); |
|||
} |
|||
} else { |
|||
hMidi->state = MIDI_RX_ERROR; |
|||
error = USBH_FAIL; |
|||
} |
|||
break; |
|||
case MIDI_RX_ERROR: |
|||
error = USBH_ClrFeature(phost, hMidi->InEp); |
|||
if (error == USBH_FAIL) { |
|||
USBH_MIDI_InterfaceDeInit(phost); |
|||
hMidi->state = MIDI_FATAL_ERROR; |
|||
} else { |
|||
hMidi->state = MIDI_IDLE; |
|||
} |
|||
break; |
|||
case MIDI_FATAL_ERROR: |
|||
return USBH_FAIL; |
|||
} |
|||
return error; |
|||
} |
|||
|
|||
/**
|
|||
* @brief USBH_MIDI_SOFProcess |
|||
* The function is for SOF state |
|||
* @param phost: Host handle |
|||
* @retval USBH Status |
|||
*/ |
|||
static USBH_StatusTypeDef USBH_MIDI_SOFProcess(USBH_HandleTypeDef *phost) |
|||
{ |
|||
/* Prevent unused argument(s) compilation warning */ |
|||
UNUSED(phost); |
|||
return USBH_OK; |
|||
} |
|||
|
|||
void USBH_MIDI_Launchpad_SetReceiveCallback(USBH_HandleTypeDef *phost, USBH_MIDI_Launchpad_RxCallback cb, void* pUser) |
|||
{ |
|||
USBH_UsrLog(__FUNCTION__); |
|||
USB_MIDI_Launchpad_HandleTypeDef *hMidi = (USB_MIDI_Launchpad_HandleTypeDef *)phost->pActiveClass->pData; |
|||
hMidi->callback = cb; |
|||
hMidi->pUser = pUser; |
|||
} |
|||
|
|||
MIDI_Launchpad_ErrorTypeDef USBH_MIDI_Launchpad_Transmit(USBH_HandleTypeDef *phost, uint8_t* data, size_t len) |
|||
{ |
|||
USB_MIDI_Launchpad_HandleTypeDef *hMidi = (USB_MIDI_Launchpad_HandleTypeDef *)phost->pActiveClass->pData; |
|||
int numUrbs = 0; |
|||
// This only blocks if data won't fit into one URB
|
|||
while(len) |
|||
{ |
|||
USBH_URBStateTypeDef txStatus = USBH_LL_GetURBState(phost, hMidi->OutPipe); |
|||
while(txStatus != USBH_URB_IDLE && txStatus != USBH_URB_DONE) |
|||
{ |
|||
if(txStatus == USBH_URB_ERROR || txStatus == USBH_URB_STALL) |
|||
{ |
|||
USBH_ClrFeature(phost, hMidi->OutEp); |
|||
return MIDI_ERROR; |
|||
} |
|||
if(numUrbs == 0) |
|||
return MIDI_BUSY; |
|||
|
|||
// Give previous URB time to complete
|
|||
USBH_Delay(2); |
|||
} |
|||
size_t sz = (len <= hMidi->OutEpSize) ? len : hMidi->OutEpSize; |
|||
USBH_BulkSendData(phost, data, sz, hMidi->OutPipe, 1); |
|||
// USBH_InterruptSendData(phost, data, sz, hMidi->OutPipe);
|
|||
len -= sz; |
|||
++numUrbs; |
|||
} |
|||
return MIDI_OK; |
|||
} |
|||
|
|||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,64 @@ |
|||
#ifndef __USBH_MIDI_H |
|||
#define __USBH_MIDI_H |
|||
|
|||
#include "usbh_core.h" |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
typedef enum { |
|||
MIDI_INIT = 0, |
|||
MIDI_IDLE, |
|||
MIDI_RX, |
|||
MIDI_RX_POLL, |
|||
MIDI_RX_ERROR, |
|||
MIDI_FATAL_ERROR |
|||
} MIDI_Launchpad_StateTypeDef; |
|||
|
|||
typedef enum { |
|||
MIDI_OK, |
|||
MIDI_BUSY, |
|||
MIDI_ERROR |
|||
} MIDI_Launchpad_ErrorTypeDef; |
|||
|
|||
typedef void (*USBH_MIDI_Launchpad_RxCallback)(uint8_t* buff, size_t len, void* pUser); |
|||
|
|||
#define USBH_MIDI_LAUNCHPAD_RX_BUF_SIZE 64 |
|||
|
|||
/* Structure for MIDI process */ |
|||
typedef struct _MIDI_Process { |
|||
uint8_t InPipe; |
|||
uint8_t InEp; |
|||
uint16_t InEpSize; |
|||
uint8_t OutPipe; |
|||
uint8_t OutEp; |
|||
uint16_t OutEpSize; |
|||
MIDI_Launchpad_StateTypeDef state; |
|||
MIDI_Launchpad_ErrorTypeDef error; |
|||
USBH_MIDI_Launchpad_RxCallback callback; |
|||
void* pUser; |
|||
uint8_t rxBuffer[USBH_MIDI_LAUNCHPAD_RX_BUF_SIZE]; |
|||
} USB_MIDI_Launchpad_HandleTypeDef; |
|||
|
|||
/* MIDI Class Codes */ |
|||
#define USB_LAUNCHPAD_CLASS 0xFFU |
|||
#define USB_LAUNCHPAD_STREAMING_SUBCLASS 0x0U |
|||
|
|||
extern USBH_ClassTypeDef USBH_Launchpad_midi; |
|||
#define USBH_MIDI_LAUNCHPAD_CLASS &USBH_Launchpad_midi |
|||
|
|||
uint8_t USBH_MIDI_IsReady(USBH_HandleTypeDef* phost); |
|||
|
|||
void USBH_MIDI_Launchpad_SetReceiveCallback( |
|||
USBH_HandleTypeDef* phost, |
|||
USBH_MIDI_Launchpad_RxCallback cb, |
|||
void* pUser); |
|||
|
|||
MIDI_Launchpad_ErrorTypeDef USBH_MIDI_Launchpad_Transmit(USBH_HandleTypeDef* phost, uint8_t* data, size_t len); |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
#endif /* __USBH_MIDI_H */ |
Loading…
Reference in new issue