Compare commits

...

3 Commits

Author SHA1 Message Date
heck 5e802093a7 Merge branch 'master' into usb_midi_launchpad 5 months ago
heck 58adc59e59 launchpad as usb midi driver: rx working. (almost) minimal changes 5 months ago
heck 7b5629db32 starting point: usb midi device class drivers 6 months ago
  1. 145
      examples/main_launchpad.cc
  2. 242
      src/usb_midi_launchpad_transport.cc
  3. 60
      src/usb_midi_launchpad_transport.hh
  4. 253
      src/usbh_midi_launchpad.c
  5. 64
      src/usbh_midi_launchpad.h

145
examples/main_launchpad.cc

@ -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);
}
}
}
}
}

242
src/usb_midi_launchpad_transport.cc

@ -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);
}

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

253
src/usbh_midi_launchpad.c

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

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
} 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…
Cancel
Save