Browse Source

starting point: usb midi device class drivers

only copied and renamed so far, but still only serve device class midi. working.
usb_midi_launchpad
heck 6 months ago
parent
commit
7b5629db32
  1. 141
      examples/main_launchpad.cc
  2. 362
      src/usb_midi_launchpad_transport.cc
  3. 60
      src/usb_midi_launchpad_transport.hh
  4. 236
      src/usbh_midi_launchpad.c
  5. 64
      src/usbh_midi_launchpad.h

141
examples/main_launchpad.cc

@ -0,0 +1,141 @@
/** 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:
// Do something on Note On events
{
uint8_t bytes[3] = { 0x90, 0x00, 0x00 };
bytes[1] = msg.data[0];
bytes[2] = msg.data[1];
midi.SendMessage(bytes, 3);
}
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);
}
}
}
}
}

362
src/usb_midi_launchpad_transport.cc

@ -0,0 +1,362 @@
#include "system.h"
#include "usbd_cdc.h"
#include "usbh_midi_launchpad.h"
#include "usb_midi_launchpad_transport.hh"
#include <cassert>
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* buffer, uint8_t length);
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;
void ReceiveCallback(uint8_t* buffer, uint32_t* length)
{
if(midi_usb_handle.RxActive())
{
for(uint16_t i = 0; i < *length; i += 4)
{
size_t remaining_bytes = *length - i;
uint8_t packet_length = remaining_bytes > 4 ? 4 : remaining_bytes;
midi_usb_handle.UsbToMidi(buffer + i, packet_length);
midi_usb_handle.Parse();
}
}
}
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, tx_buffer_, tx_ptr_);
should_retry = (result == MIDI_BUSY) && attempt_count--;
}
else
{
UsbHandle::Result result;
if(config_.periph == Config::EXTERNAL)
result = usb_handle_.TransmitExternal(tx_buffer_, tx_ptr_);
else
result = usb_handle_.TransmitInternal(tx_buffer_, tx_ptr_);
should_retry
= (result == UsbHandle::Result::ERR) && attempt_count--;
}
if(should_retry)
System::DelayUs(100);
} while(should_retry);
tx_ptr_ = 0;
}
void UsbMidiLaunchpadTransport::Impl::UsbToMidi(uint8_t* buffer, uint8_t length)
{
// A length of less than four in the buffer indicates
// a garbled message, since USB MIDI packets usually*
// require 4 bytes per message
if(length < 4)
return;
// Right now, Daisy only supports a single cable, so we don't
// need to extract that value from the upper nibble
uint8_t code_index = buffer[0] & 0xF;
if(code_index == 0x0 || code_index == 0x1)
{
// 0x0 and 0x1 are reserved codes, and if they come up,
// there's probably been an error. *0xF indicates data is
// sent one byte at a time, rather than in packets of four.
// This functionality could be supported later.
// The single-byte mode does still come through as 32-bit messages
return;
}
// Only writing as many bytes as necessary
for(uint8_t i = 0; i < code_index_size_[code_index]; i++)
{
if(rx_buffer_.writable() > 0)
rx_buffer_.Write(buffer[1 + i]);
else
{
rx_active_ = false; // disable on overflow
break;
}
}
}
void UsbMidiLaunchpadTransport::Impl::MidiToUsbSingle(uint8_t* buffer, size_t size)
{
if(size == 0)
return;
// Channel voice messages
if((buffer[0] & 0xF0) != 0xF0)
{
// Check message validity
if((buffer[0] & 0xF0) == 0xC0 || (buffer[0] & 0xF0) == 0xD0)
{
if(size != 2)
return; // error
}
else
{
if(size != 3)
return; //error
}
// CIN is the same as status byte for channel voice messages
tx_buffer_[tx_ptr_ + 0] = (buffer[0] & 0xF0) >> 4;
tx_buffer_[tx_ptr_ + 1] = buffer[0];
tx_buffer_[tx_ptr_ + 2] = buffer[1];
tx_buffer_[tx_ptr_ + 3] = size == 3 ? buffer[2] : 0;
tx_ptr_ += 4;
}
else // buffer[0] & 0xF0 == 0xF0 aka System common or realtime
{
if(0xF2 == buffer[0])
// three byte message
{
if(size != 3)
return; // error
tx_buffer_[tx_ptr_ + 0] = 0x03;
tx_buffer_[tx_ptr_ + 1] = buffer[0];
tx_buffer_[tx_ptr_ + 2] = buffer[1];
tx_buffer_[tx_ptr_ + 3] = buffer[2];
tx_ptr_ += 4;
}
if(0xF1 == buffer[0] || 0xF3 == buffer[0])
// two byte messages
{
if(size != 2)
return; // error
tx_buffer_[tx_ptr_ + 0] = 0x02;
tx_buffer_[tx_ptr_ + 1] = buffer[0];
tx_buffer_[tx_ptr_ + 2] = buffer[1];
tx_buffer_[tx_ptr_ + 3] = 0;
tx_ptr_ += 4;
}
else if(0xF4 <= buffer[0])
// one byte message
{
if(size != 1)
return; // error
tx_buffer_[tx_ptr_ + 0] = 0x05;
tx_buffer_[tx_ptr_ + 1] = buffer[0];
tx_buffer_[tx_ptr_ + 2] = 0;
tx_buffer_[tx_ptr_ + 3] = 0;
tx_ptr_ += 4;
}
else // sysex
{
size_t i = 0;
// Sysex messages are split up into several 4 bytes packets
// first ones use CIN 0x04
// but packet containing the SysEx stop byte use a different CIN
for(i = 0; i + 3 < size; i += 3, tx_ptr_ += 4)
{
tx_buffer_[tx_ptr_] = 0x04;
tx_buffer_[tx_ptr_ + 1] = buffer[i];
tx_buffer_[tx_ptr_ + 2] = buffer[i + 1];
tx_buffer_[tx_ptr_ + 3] = buffer[i + 2];
}
// Fill CIN for terminating bytes
// 0x05 for 1 remaining byte
// 0x06 for 2
// 0x07 for 3
tx_buffer_[tx_ptr_] = 0x05 + (size - i - 1);
tx_ptr_++;
for(; i < size; ++i, ++tx_ptr_)
tx_buffer_[tx_ptr_] = buffer[i];
for(; (tx_ptr_ % 4) != 0; ++tx_ptr_)
tx_buffer_[tx_ptr_] = 0;
}
}
}
void UsbMidiLaunchpadTransport::Impl::MidiToUsb(uint8_t* buffer, size_t size)
{
// We'll assume your message starts with a status byte!
size_t status_index = 0;
while(status_index < size)
{
// Search for next status byte or end
size_t next_status = status_index;
for(size_t j = status_index + 1; j < size; j++)
{
if(buffer[j] & 0x80)
{
next_status = j;
break;
}
}
if(next_status == status_index)
{
// Either we're at the end or it's malformed
next_status = size;
}
MidiToUsbSingle(buffer + status_index, next_status - status_index);
status_index = next_status;
}
}
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);
}

60
src/usb_midi_launchpad_transport.hh

@ -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

236
src/usbh_midi_launchpad.c

@ -0,0 +1,236 @@
#include "usbh_midi_launchpad.h"
#include "daisy_core.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 0x80U
/**
* @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);
(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);
(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_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);
len -= sz;
++numUrbs;
}
return MIDI_OK;
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

64
src/usbh_midi_launchpad.h

@ -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
} USB_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;
USB_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 0x01U
#define USB_LAUNCHPAD_STREAMING_SUBCLASS 0x03U
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…
Cancel
Save