Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ extern "C" {

#define USB_CDC_CONFIG_DESC_SIZ 67U
#define USB_MIDI_CONFIG_DESC_SIZ 101U
#define USB_MIDI2_CONFIG_DESC_SIZ 141U
#define USB_MIDI2_GTB_DESC_SIZ 18U
#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE
#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE

Expand Down Expand Up @@ -147,9 +149,11 @@ extern USBD_ClassTypeDef USBD_CDC;
#define USBD_CDC_CLASS &USBD_CDC

// these are used to hack in an optional MIDI mode
#define USBD_MODE_CDC 0
#define USBD_MODE_MIDI 1
#define USBD_MODE_CDC 0
#define USBD_MODE_MIDI 1
#define USBD_MODE_MIDI2 2
extern uint8_t usbd_mode;
extern uint8_t usbd_midi2_alt_setting;

/**
* @}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,74 @@ __ALIGN_BEGIN uint8_t USBD_MIDI_CfgDesc[USB_MIDI_CONFIG_DESC_SIZ] __ALIGN_END =
0x05, 0x25, 0x01, 0x01, 0x03
};

// USB MIDI 2.0 Configuration Descriptor: Alt 0 (MIDI 1.0) + Alt 1 (UMP)
__ALIGN_BEGIN uint8_t USBD_MIDI2_CfgDesc[USB_MIDI2_CONFIG_DESC_SIZ] __ALIGN_END =
{
// Configuration Descriptor (9)
0x09, 0x02,
LOBYTE(USB_MIDI2_CONFIG_DESC_SIZ), HIBYTE(USB_MIDI2_CONFIG_DESC_SIZ),
0x02, 0x01, 0x00, 0xC0, 0x50,

// Audio Control Interface (9)
0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
// CS AC Interface Header (9)
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01,

// ==== Alt 0: MIDIStreaming (MIDI 1.0 compat) ====
// Standard MS Interface (9) -- bAlternateSetting=0
0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00,
// CS MS Header (7) -- bcdMSC=0x0100
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00,
// MIDI IN Jack Embedded (6)
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
// MIDI IN Jack External (6)
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
// MIDI OUT Jack Embedded (9)
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
// MIDI OUT Jack External (9)
0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00,
// Bulk OUT Endpoint (9)
0x09, 0x05, CDC_OUT_EP, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
// CS Endpoint General (5)
0x05, 0x25, 0x01, 0x01, 0x01,
// Bulk IN Endpoint (9)
0x09, 0x05, CDC_IN_EP, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
// CS Endpoint General (5)
0x05, 0x25, 0x01, 0x01, 0x03,

// ==== Alt 1: MIDIStreaming (MIDI 2.0 UMP) ====
// Standard MS Interface (9) -- bAlternateSetting=1
0x09, 0x04, 0x01, 0x01, 0x02, 0x01, 0x03, 0x00, 0x00,
// CS MS Header (7) -- bcdMSC=0x0200
0x07, 0x24, 0x01, 0x00, 0x02, 0x07, 0x00,
// Bulk OUT Endpoint (7) -- 7 bytes on Alt 1 (USB MIDI 2.0 Table B-17)
0x07, 0x05, CDC_OUT_EP, 0x02, 0x40, 0x00, 0x00,
// CS Endpoint General 2.0 (5) -- subtype=0x02, 1 GTB, ID=1
0x05, 0x25, 0x02, 0x01, 0x01,
// Bulk IN Endpoint (7)
0x07, 0x05, CDC_IN_EP, 0x02, 0x40, 0x00, 0x00,
// CS Endpoint General 2.0 (5) -- subtype=0x02, 1 GTB, ID=1
0x05, 0x25, 0x02, 0x01, 0x01,
};

// Group Terminal Block Descriptor -- served via GET_DESCRIPTOR(0x26)
__ALIGN_BEGIN uint8_t USBD_MIDI2_GTB_Desc[USB_MIDI2_GTB_DESC_SIZ] __ALIGN_END =
{
// GTB Header (5)
0x05, 0x26, 0x01,
LOBYTE(USB_MIDI2_GTB_DESC_SIZ), HIBYTE(USB_MIDI2_GTB_DESC_SIZ),
// GTB Entry (13) -- 1 group, bidirectional, native MIDI 2.0
0x0D, 0x26, 0x02,
0x01, // bGrpTrmBlkID
0x00, // bGrpTrmBlkType: bidirectional
0x00, // nGroupTrm: group 0
0x01, // nNumGroupTrm: 1
0x00, // iBlockItem: no string
0x11, // bMIDIProtocol: MIDI_2_0 (no MIDI-CI negotiation)
0x00, 0x00, // wMaxInputBandwidth: unknown
0x00, 0x00, // wMaxOutputBandwidth: unknown
};

/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
Expand Down Expand Up @@ -700,6 +768,10 @@ static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev,
case USB_REQ_GET_INTERFACE:
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (usbd_mode == USBD_MODE_MIDI2)
{
ifalt = usbd_midi2_alt_setting;
}
(void)USBD_CtlSendData(pdev, &ifalt, 1U);
}
else
Expand All @@ -710,13 +782,36 @@ static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev,
break;

case USB_REQ_SET_INTERFACE:
if (pdev->dev_state != USBD_STATE_CONFIGURED)
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (usbd_mode == USBD_MODE_MIDI2)
{
usbd_midi2_alt_setting = (uint8_t)LOBYTE(req->wValue);
/* Re-arm OUT endpoint for new packet format */
(void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP,
hcdc->RxBuffer, CDC_DATA_FS_OUT_PACKET_SIZE);
}
}
else
{
USBD_CtlError(pdev, req);
ret = USBD_FAIL;
}
break;

case USB_REQ_GET_DESCRIPTOR:
if (usbd_mode == USBD_MODE_MIDI2
&& ((req->wValue >> 8) == 0x26U))
{
uint16_t gtb_len = sizeof(USBD_MIDI2_GTB_Desc);
if (gtb_len > req->wLength)
{
gtb_len = req->wLength;
}
(void)USBD_CtlSendData(pdev, USBD_MIDI2_GTB_Desc, gtb_len);
}
break;

case USB_REQ_CLEAR_FEATURE:
break;

Expand Down Expand Up @@ -851,6 +946,11 @@ static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length)
*length = sizeof (USBD_MIDI_CfgDesc);
return USBD_MIDI_CfgDesc;
}
case USBD_MODE_MIDI2:
{
*length = sizeof (USBD_MIDI2_CfgDesc);
return USBD_MIDI2_CfgDesc;
}
}

return USBD_CDC_CfgFSDesc;
Expand All @@ -877,6 +977,11 @@ static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length)
*length = sizeof (USBD_MIDI_CfgDesc);
return USBD_MIDI_CfgDesc;
}
case USBD_MODE_MIDI2:
{
*length = sizeof (USBD_MIDI2_CfgDesc);
return USBD_MIDI2_CfgDesc;
}
}

return USBD_CDC_CfgHSDesc;
Expand All @@ -903,6 +1008,11 @@ static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length)
*length = sizeof (USBD_MIDI_CfgDesc);
return USBD_MIDI_CfgDesc;
}
case USBD_MODE_MIDI2:
{
*length = sizeof (USBD_MIDI2_CfgDesc);
return USBD_MIDI2_CfgDesc;
}
}

return USBD_CDC_OtherSpeedCfgDesc;
Expand Down
11 changes: 11 additions & 0 deletions Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc/usbh_midi.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ typedef enum {
typedef void (*USBH_MIDI_RxCallback)(uint8_t* buff, size_t len, void* pUser);

#define USBH_MIDI_RX_BUF_SIZE 64
#define USBH_MIDI_TX_BUF_SIZE 64

/* Structure for MIDI process */
typedef struct _MIDI_Process {
Expand All @@ -61,6 +62,15 @@ typedef struct _MIDI_Process {
USBH_MIDI_RxCallback callback;
void* pUser;
uint8_t rxBuffer[USBH_MIDI_RX_BUF_SIZE];
/* Received bytes are copied here and the RX pipe re-armed before the
class callback runs, so the pipe is never idle while a packet is
processed (the device would otherwise NAK and drop under load). */
uint8_t procBuffer[USBH_MIDI_RX_BUF_SIZE];
/* DMA-coherent Tx staging. The handle is transferred by DMA with no
cache maintenance, so Tx data (which may come from cached memory)
is copied here before submitting, as rxBuffer is for the receive. */
uint8_t txBuffer[USBH_MIDI_TX_BUF_SIZE];
uint8_t altSetting; /* 0 = MIDI 1.0, 1 = MIDI 2.0 UMP */
} MIDI_HandleTypeDef;

/* MIDI Class Codes */
Expand All @@ -71,6 +81,7 @@ extern USBH_ClassTypeDef USBH_midi;
#define USBH_MIDI_CLASS &USBH_midi

uint8_t USBH_MIDI_IsReady(USBH_HandleTypeDef *phost);
uint8_t USBH_MIDI_GetAltSetting(USBH_HandleTypeDef *phost);

MIDI_ErrorTypeDef USBH_MIDI_Transmit(USBH_HandleTypeDef *phost,
uint8_t* data, size_t len);
Expand Down
109 changes: 93 additions & 16 deletions Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Src/usbh_midi.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,61 @@ static USBH_StatusTypeDef USBH_MIDI_InterfaceInit(USBH_HandleTypeDef *phost)
MIDI_Handle = (MIDI_HandleTypeDef*)phost->pActiveClass->pData;
USBH_memset(MIDI_Handle, 0, sizeof(MIDI_HandleTypeDef));

/* Find MIDI Streaming interface (Alt 0 first) */
uint8_t interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode,
USB_MIDI_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);

/* Check if device has Alt 1 (MIDI 2.0 UMP). USBH_SelectInterface
* bounds-checks against CfgDesc.bNumInterfaces, which counts unique
* interface numbers (NOT entries in Itf_Desc[]), so the Alt 1 index
* would always fail that check. Pass the Alt 0 index to satisfy the
* bounds check; the Alt 1 index drives the endpoint scan + SET_INTERFACE
* below. */
uint8_t alt0_idx = interface;
uint8_t ifnum = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber;
uint8_t alt1_idx = USBH_FindInterfaceIndex(phost, ifnum, 1U);
uint8_t use_alt = 0U;

if (alt1_idx != 0xFFU) {
/* Device supports MIDI 2.0 -- the endpoint scan will use Alt 1 */
interface = alt1_idx;
use_alt = 1U;
USBH_UsrLog("MIDI 2.0 Alt 1 found, selecting UMP mode");
}

status = USBH_SelectInterface(phost, alt0_idx);
if (status != USBH_OK) {
return USBH_FAIL;
}

/* Send SET_INTERFACE if using Alt 1. USBH_SetInterface is async via
* USBH_CtlReq; calling it once just queues the transfer. We must
* drive it to completion BEFORE opening the IN/OUT pipes below,
* otherwise the host opens Alt 1 pipes while the device is still
* on Alt 0 and the device sends nothing on those pipes. */
if (use_alt == 1U) {
USBH_StatusTypeDef setif_status;
uint32_t setif_start = HAL_GetTick();
do {
setif_status = USBH_SetInterface(phost, ifnum, 1U);
USBH_Delay(1U);
if ((HAL_GetTick() - setif_start) > 100U) {
USBH_ErrLog("SET_INTERFACE Alt 1 timeout");
break;
}
} while (setif_status == USBH_BUSY);
if (setif_status != USBH_OK) {
USBH_ErrLog("SET_INTERFACE Alt 1 failed");
}
}

MIDI_Handle->altSetting = use_alt;

/* 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) {
Expand Down Expand Up @@ -174,11 +217,23 @@ static USBH_StatusTypeDef USBH_MIDI_Process(USBH_HandleTypeDef *phost)
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);
if (sz > USBH_MIDI_RX_BUF_SIZE) {
sz = USBH_MIDI_RX_BUF_SIZE;
}
/* Copy out and re-arm before the callback so the pipe is
never idle while a packet is processed (the device would
otherwise NAK and drop under load). */
if (sz > 0U) {
USBH_memcpy(hMidi->procBuffer, hMidi->rxBuffer, sz);
}
USBH_BulkReceiveData(phost, hMidi->rxBuffer, hMidi->InEpSize,
hMidi->InPipe);
hMidi->state = MIDI_RX_POLL;
if (sz > 0U && hMidi->callback) {
hMidi->callback(hMidi->procBuffer, sz, hMidi->pUser);
}
} else {
USBH_ErrLog("MIDI URB unexpected state=%u", (unsigned)rxStatus);
hMidi->state = MIDI_RX_ERROR;
error = USBH_FAIL;
}
Expand Down Expand Up @@ -211,6 +266,15 @@ static USBH_StatusTypeDef USBH_MIDI_SOFProcess(USBH_HandleTypeDef *phost)
return USBH_OK;
}

uint8_t USBH_MIDI_GetAltSetting(USBH_HandleTypeDef *phost)
{
MIDI_HandleTypeDef *hMidi = (MIDI_HandleTypeDef*)phost->pActiveClass->pData;
if (hMidi) {
return hMidi->altSetting;
}
return 0U;
}

void USBH_MIDI_SetReceiveCallback(USBH_HandleTypeDef *phost, USBH_MIDI_RxCallback cb, void* pUser)
{
USBH_UsrLog(__FUNCTION__);
Expand All @@ -222,28 +286,41 @@ void USBH_MIDI_SetReceiveCallback(USBH_HandleTypeDef *phost, USBH_MIDI_RxCallbac
MIDI_ErrorTypeDef USBH_MIDI_Transmit(USBH_HandleTypeDef *phost, uint8_t* data, size_t len)
{
MIDI_HandleTypeDef *hMidi = (MIDI_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)
size_t cap = (hMidi->OutEpSize < USBH_MIDI_TX_BUF_SIZE)
? hMidi->OutEpSize : USBH_MIDI_TX_BUF_SIZE;
size_t sz = (len <= cap) ? len : cap;

// Stage into the DMA-coherent handle buffer: the caller's buffer
// may be cached (AXI SRAM) and the OTG DMA does no cache
// maintenance, so it must read from the DMA section.
USBH_memcpy(hMidi->txBuffer, data, sz);
USBH_BulkSendData(phost, hMidi->txBuffer, (uint16_t)sz, hMidi->OutPipe, 1);

// Drive the OUT URB to completion: poll the URB state and resubmit
// on NOTREADY (device NAK). Without this the URB is submitted and
// abandoned, so a NAK is never retried and data never arrives.
uint32_t start = HAL_GetTick();
for(;;)
{
if(txStatus == USBH_URB_ERROR || txStatus == USBH_URB_STALL)
USBH_URBStateTypeDef st
= USBH_LL_GetURBState(phost, hMidi->OutPipe);
if(st == USBH_URB_DONE)
break;
if(st == USBH_URB_ERROR || st == USBH_URB_STALL)
{
USBH_ClrFeature(phost, hMidi->OutEp);
return MIDI_ERROR;
}
if(numUrbs == 0)
if(st == USBH_URB_NOTREADY)
USBH_BulkSendData(phost, hMidi->txBuffer, (uint16_t)sz, hMidi->OutPipe, 1);
if((HAL_GetTick() - start) > 50U)
return MIDI_BUSY;

// Give previous URB time to complete
USBH_Delay(2);
USBH_Delay(1);
}
size_t sz = (len <= hMidi->OutEpSize) ? len : hMidi->OutEpSize;
USBH_BulkSendData(phost, data, sz, hMidi->OutPipe, 1);
data += sz;
len -= sz;
++numUrbs;
}
return MIDI_OK;
}
Expand Down
3 changes: 3 additions & 0 deletions examples/MIDI2_Host/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
set(FIRMWARE_NAME MIDI2_Host)
set(FIRMWARE_SOURCES MIDI2_Host.cpp)
include(DaisyProject)
Loading
Loading