diff --git a/CMakeLists.txt b/CMakeLists.txt
index b56ffe6d4..1fafa419f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -217,6 +217,7 @@ target_include_directories(${TARGET} PUBLIC
Middlewares/Patched/ST/STM32_USB_Device_Library/Class/CDC/Inc
Middlewares/ST/STM32_USB_Host_Library/Core/Inc
Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Inc
+ Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc
Middlewares/Third_Party/FatFs/src
src
src/sys
diff --git a/Makefile b/Makefile
index c131a47d8..c3232f287 100644
--- a/Makefile
+++ b/Makefile
@@ -222,6 +222,7 @@ Middlewares/Third_Party/FatFs/src/ff_gen_drv.c \
Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc_bot.c \
Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc_scsi.c \
Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc.c \
+Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Src/usbh_midi.c \
Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c \
Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ctlreq.c \
Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ioreq.c \
@@ -297,7 +298,6 @@ C_DEFS = \
-DDATA_IN_D2_SRAM
# ^ added for easy startup access
-
C_INCLUDES = \
-I$(MODULE_DIR) \
-I$(MODULE_DIR)/sys \
@@ -311,6 +311,7 @@ C_INCLUDES = \
-IMiddlewares/Patched/ST/STM32_USB_Device_Library/Class/CDC/Inc \
-IMiddlewares/ST/STM32_USB_Host_Library/Core/Inc \
-IMiddlewares/ST/STM32_USB_Host_Library/Class/MSC/Inc \
+-IMiddlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc \
-IMiddlewares/Third_Party/FatFs/src \
-I$(MODULE_DIR) \
-I.
diff --git a/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc/usbh_midi.h b/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc/usbh_midi.h
new file mode 100644
index 000000000..22d7d0d10
--- /dev/null
+++ b/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc/usbh_midi.h
@@ -0,0 +1,87 @@
+/**
+ ******************************************************************************
+ * @file usbh_midi.h
+ * @author Greg Burns
+ * @author MCD Application Team
+ * @brief This file contains all the prototypes for the usbh_midi.c
+ ******************************************************************************
+ * @attention
+ *
+ *
© Copyright (c) 2015 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive ----------------------------------------------*/
+#ifndef __USBH_MIDI_H
+#define __USBH_MIDI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "usbh_core.h"
+
+typedef enum {
+ MIDI_INIT = 0,
+ MIDI_IDLE,
+ MIDI_RX,
+ MIDI_RX_POLL,
+ MIDI_RX_ERROR,
+ MIDI_FATAL_ERROR
+} MIDI_StateTypeDef;
+
+typedef enum {
+ MIDI_OK,
+ MIDI_BUSY,
+ MIDI_ERROR
+} MIDI_ErrorTypeDef;
+
+typedef void (*USBH_MIDI_RxCallback)(uint8_t* buff, size_t len, void* pUser);
+
+#define USBH_MIDI_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_StateTypeDef state;
+ MIDI_ErrorTypeDef error;
+ USBH_MIDI_RxCallback callback;
+ void* pUser;
+ uint8_t rxBuffer[USBH_MIDI_RX_BUF_SIZE];
+} MIDI_HandleTypeDef;
+
+/* MIDI Class Codes */
+#define USB_AUDIO_CLASS 0x01U
+#define USB_MIDI_STREAMING_SUBCLASS 0x03U
+
+extern USBH_ClassTypeDef USBH_midi;
+#define USBH_MIDI_CLASS &USBH_midi
+
+uint8_t USBH_MIDI_IsReady(USBH_HandleTypeDef *phost);
+
+MIDI_ErrorTypeDef USBH_MIDI_Transmit(USBH_HandleTypeDef *phost,
+ uint8_t* data, size_t len);
+
+void USBH_MIDI_SetReceiveCallback(USBH_HandleTypeDef *phost,
+ USBH_MIDI_RxCallback cb, void* pUser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USBH_MIDI_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Src/usbh_midi.c b/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Src/usbh_midi.c
new file mode 100644
index 000000000..e678fc5ab
--- /dev/null
+++ b/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Src/usbh_midi.c
@@ -0,0 +1,251 @@
+/**
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2015 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under Ultimate Liberty license
+ * SLA0044, the "License"; You may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * www.st.com/SLA0044
+ *
+ ******************************************************************************
+ */
+
+#include "usbh_midi.h"
+#include "daisy_core.h"
+
+static MIDI_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_midi = {
+ "MIDI",
+ USB_AUDIO_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;
+ MIDI_HandleTypeDef *MIDI_Handle;
+
+ // Single static instance of midi handle
+ phost->pActiveClass->pData = &static_midi;
+ MIDI_Handle = (MIDI_HandleTypeDef*)phost->pActiveClass->pData;
+ USBH_memset(MIDI_Handle, 0, sizeof(MIDI_HandleTypeDef));
+
+ 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);
+ 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_RX_BUF_SIZE) {
+ MIDI_Handle->InEpSize = USBH_MIDI_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__);
+ MIDI_HandleTypeDef *MIDI_Handle = (MIDI_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;
+
+ MIDI_HandleTypeDef *hMidi = (MIDI_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_SetReceiveCallback(USBH_HandleTypeDef *phost, USBH_MIDI_RxCallback cb, void* pUser)
+{
+ USBH_UsrLog(__FUNCTION__);
+ MIDI_HandleTypeDef *hMidi = (MIDI_HandleTypeDef*)phost->pActiveClass->pData;
+ hMidi->callback = cb;
+ hMidi->pUser = pUser;
+}
+
+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)
+ {
+ 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****/
diff --git a/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c b/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c
index 76874c4f2..1d7533f85 100644
--- a/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c
+++ b/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c
@@ -186,6 +186,7 @@ USBH_StatusTypeDef USBH_DeInit(USBH_HandleTypeDef *phost)
phost->device.is_ReEnumerated = 0U;
phost->device.RstCnt = 0U;
phost->device.EnumCnt = 0U;
+ phost->ClassNumber = 0U;
if (phost->pData != NULL)
{
diff --git a/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_pipes.c b/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_pipes.c
index 60fe08b90..87c5388b2 100644
--- a/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_pipes.c
+++ b/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_pipes.c
@@ -124,6 +124,10 @@ uint8_t USBH_AllocPipe(USBH_HandleTypeDef *phost, uint8_t ep_addr)
{
phost->Pipes[pipe & 0xFU] = 0x8000U | ep_addr;
}
+ else
+ {
+ USBH_ErrLog("Alloc error: no free pipe");
+ }
return (uint8_t)pipe;
}
diff --git a/core/Makefile b/core/Makefile
index bdef95373..f62e14ac1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -139,6 +139,7 @@ C_INCLUDES += \
-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Device_Library/Core/Inc \
-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Host_Library/Core/Inc \
-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Inc \
+-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc \
-I$(SYSTEM_FILES_DIR)/
ifdef DAISYSP_DIR
diff --git a/examples/MIDI_UART_Input/MIDI_UART_Input.cpp b/examples/MIDI_UART_Input/MIDI_UART_Input.cpp
index 8df73077d..53b6acd7f 100644
--- a/examples/MIDI_UART_Input/MIDI_UART_Input.cpp
+++ b/examples/MIDI_UART_Input/MIDI_UART_Input.cpp
@@ -1,10 +1,10 @@
/** Example of setting reading MIDI Input via UART
- *
- *
+ *
+ *
* This can be used with any 5-pin DIN or TRS connector that has been wired up
* to one of the UART Rx pins on Daisy.
* This will use D14 as the UART 1 Rx pin
- *
+ *
* This example will also log incoming messages to the serial port for general MIDI troubleshooting
*/
#include "daisy_seed.h"
@@ -14,7 +14,7 @@ using namespace daisy;
/** Fills string with string representation of MidiEvent::Type
* str needs to be at least 16 bytes long to store the data
- * TODO: Move this into MIDI lib or something
+ * TODO: Move this into MIDI lib or something
*/
void GetMidiTypeAsString(MidiEvent& msg, char* str)
{
@@ -69,7 +69,7 @@ int main(void)
{
MidiEvent msg = midi.PopEvent();
- /** Handle messages as they come in
+ /** Handle messages as they come in
* See DaisyExamples for some examples of this
*/
switch(msg.type)
@@ -112,4 +112,4 @@ int main(void)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/examples/MIDI_USBH_Input/MIDI_USBH_Input.cpp b/examples/MIDI_USBH_Input/MIDI_USBH_Input.cpp
new file mode 100644
index 000000000..a733a6c5c
--- /dev/null
+++ b/examples/MIDI_USBH_Input/MIDI_USBH_Input.cpp
@@ -0,0 +1,168 @@
+/** 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 "daisy_seed.h"
+#include "usbh_midi.h"
+
+/** This prevents us from having to type "daisy::" in front of a lot of things. */
+using namespace daisy;
+
+/** Fills string with string representation of MidiEvent::Type
+ * str needs to be at least 16 bytes long to store the data
+ * TODO: Move this into MIDI lib or something
+*/
+void GetMidiTypeAsString(MidiEvent& msg, char* str)
+{
+ switch(msg.type)
+ {
+ case NoteOff: strcpy(str, "NoteOff"); break;
+ case NoteOn: strcpy(str, "NoteOn"); break;
+ case PolyphonicKeyPressure: strcpy(str, "PolyKeyPres."); break;
+ case ControlChange: strcpy(str, "CC"); break;
+ case ProgramChange: strcpy(str, "Prog. Change"); break;
+ case ChannelPressure: strcpy(str, "Chn. Pressure"); break;
+ case PitchBend: strcpy(str, "PitchBend"); break;
+ case SystemCommon: strcpy(str, "Sys. Common"); break;
+ case SystemRealTime: strcpy(str, "Sys. Realtime"); break;
+ case ChannelMode: strcpy(str, "Chn. Mode"); break;
+ default: strcpy(str, "Unknown"); break;
+ }
+}
+
+/** Global Hardware access */
+DaisySeed hw;
+MidiUsbHandler midi;
+USBHostHandle usbHost;
+
+/** FIFO to hold messages as we're ready to print them */
+FIFO 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_CLASS))
+ {
+ hw.PrintLine("MIDI device class active");
+ MidiUsbHandler::Config midi_config;
+ midi_config.transport_config.periph = MidiUsbTransport::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_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_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];
+ char type_str[16];
+ GetMidiTypeAsString(msg, type_str);
+ 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);
+ }
+ }
+ }
+ }
+}
diff --git a/examples/MIDI_USBH_Input/Makefile b/examples/MIDI_USBH_Input/Makefile
new file mode 100644
index 000000000..b7ad88a33
--- /dev/null
+++ b/examples/MIDI_USBH_Input/Makefile
@@ -0,0 +1,12 @@
+# Project Name
+TARGET = MIDI_USBH_Input
+
+# Sources
+CPP_SOURCES = MIDI_USBH_Input.cpp
+
+# Library Locations
+LIBDAISY_DIR = ../..
+
+# Core location, and generic Makefile.
+SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
+include $(SYSTEM_FILES_DIR)/Makefile
diff --git a/src/dev/mcp23x17.h b/src/dev/mcp23x17.h
index ff2edf4a0..f4229fb66 100644
--- a/src/dev/mcp23x17.h
+++ b/src/dev/mcp23x17.h
@@ -3,6 +3,9 @@
#include "per/gpio.h"
#include "per/i2c.h"
+// This get defined in a public (ST) header file
+#undef SetBit
+
namespace daisy
{
// Adapted from https://github.com/blemasle/arduino-mcp23017
@@ -398,4 +401,4 @@ class Mcp23X17
};
using Mcp23017 = Mcp23X17;
-} // namespace daisy
\ No newline at end of file
+} // namespace daisy
diff --git a/src/hid/midi.h b/src/hid/midi.h
index 0ff823736..b212b3b3a 100644
--- a/src/hid/midi.h
+++ b/src/hid/midi.h
@@ -196,6 +196,7 @@ class MidiHandler
*/
bool HasEvents() const { return event_q_.GetNumElements() > 0; }
+ bool RxActive() { return transport_.RxActive(); }
/** Pops the oldest unhandled MidiEvent from the internal queue
\return The event to be handled
diff --git a/src/hid/usb_host.cpp b/src/hid/usb_host.cpp
index f3c1ea6b4..d4a31426b 100644
--- a/src/hid/usb_host.cpp
+++ b/src/hid/usb_host.cpp
@@ -1,15 +1,25 @@
+#include
#include "usb_host.h"
#include "daisy_core.h"
#include "usbh_core.h"
#include "usbh_msc.h"
+#include "logger.h"
using namespace daisy;
extern "C"
{
- extern HCD_HandleTypeDef hhcd_USB_OTG_HS;
USBH_HandleTypeDef DMA_BUFFER_MEM_SECTION hUsbHostHS;
+ void USBH_LogPrint(const char* format, ...);
+}
+
+void USBH_LogPrint(const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ Logger::PrintLineV(format, va);
+ va_end(va);
}
ApplicationTypeDef Appli_state = APPLICATION_IDLE;
@@ -17,17 +27,19 @@ ApplicationTypeDef Appli_state = APPLICATION_IDLE;
class USBHostHandle::Impl
{
public:
- Impl() {}
+ Impl() { memset(&hUsbHostHS, 0, sizeof(hUsbHostHS)); }
~Impl() {}
- Result Init(Config config);
+ Result RegisterClass(USBH_ClassTypeDef* pClass);
+ Result Init(USBHostHandle::Config& config);
Result Deinit();
+ Result Reinit();
Result Process();
Result ReEnumerate();
bool GetReady();
- inline Config &GetConfig() { return config_; }
+ inline Config& GetConfig() { return config_; }
private:
Config config_;
@@ -52,34 +64,42 @@ class USBHostHandle::Impl
}
};
-// Global dfu handle
-USBHostHandle::Impl msd_impl;
+// Global handle
+static USBHostHandle::Impl usbh_impl;
-static void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id);
+static void USBH_UserProcess(USBH_HandleTypeDef* phost, uint8_t id);
+
+USBHostHandle::Result
+USBHostHandle::Impl::RegisterClass(USBH_ClassTypeDef* pClass)
+{
+ return ConvertStatus(USBH_RegisterClass(&hUsbHostHS, pClass));
+}
-USBHostHandle::Result USBHostHandle::Impl::Init(USBHostHandle::Config config)
+USBHostHandle::Result USBHostHandle::Impl::Init(USBHostHandle::Config& config)
{
+ /* Copy in configuration */
config_ = config;
+
/* Init host Library, add supported class and start the library. */
USBH_StatusTypeDef sta;
+
sta = USBH_Init(&hUsbHostHS, USBH_UserProcess, HOST_HS);
- if(sta != USBH_OK)
- {
- return ConvertStatus(sta);
- }
- sta = USBH_RegisterClass(&hUsbHostHS, USBH_MSC_CLASS);
- if(sta != USBH_OK)
- {
- return ConvertStatus(sta);
- }
- sta = USBH_Start(&hUsbHostHS);
- if(sta != USBH_OK)
- {
- return ConvertStatus(sta);
- }
+ if(sta == USBH_OK)
+ sta = USBH_Start(&hUsbHostHS);
+
return ConvertStatus(sta);
}
+USBHostHandle::Result USBHostHandle::Impl::Reinit()
+{
+ uint32_t numClasses = hUsbHostHS.ClassNumber;
+ Deinit();
+ USBHostHandle::Result result = Init(config_);
+ // Restore registered class count
+ hUsbHostHS.ClassNumber = numClasses;
+ return result;
+}
+
USBHostHandle::Result USBHostHandle::Impl::Deinit()
{
USBH_Stop(&hUsbHostHS);
@@ -89,7 +109,12 @@ USBHostHandle::Result USBHostHandle::Impl::Deinit()
USBHostHandle::Result USBHostHandle::Impl::Process()
{
- return ConvertStatus(USBH_Process(&hUsbHostHS));
+ // The USBH state machine seems to get wedged in the
+ // abort state, re-initialize to try and clear it.
+ if(hUsbHostHS.gState == HOST_ABORT_STATE)
+ return Reinit();
+ else
+ return ConvertStatus(USBH_Process(&hUsbHostHS));
}
USBHostHandle::Result USBHostHandle::Impl::ReEnumerate()
@@ -99,14 +124,17 @@ USBHostHandle::Result USBHostHandle::Impl::ReEnumerate()
bool USBHostHandle::Impl::GetReady()
{
- return (bool)USBH_MSC_IsReady(&hUsbHostHS);
+ return Appli_state == APPLICATION_READY;
}
-// MSDHandle -> Impl
+USBHostHandle::Result USBHostHandle::RegisterClass(USBH_ClassTypeDef* pClass)
+{
+ return pimpl_->RegisterClass(pClass);
+}
-USBHostHandle::Result USBHostHandle::Init(Config config)
+USBHostHandle::Result USBHostHandle::Init(Config& config)
{
- pimpl_ = &msd_impl;
+ pimpl_ = &usbh_impl;
return pimpl_->Init(config);
}
@@ -130,6 +158,11 @@ USBHostHandle::Result USBHostHandle::ReEnumerate()
return pimpl_->ReEnumerate();
}
+bool USBHostHandle::IsActiveClass(USBH_ClassTypeDef* pClass)
+{
+ return pClass == hUsbHostHS.pActiveClass;
+}
+
bool USBHostHandle::GetPresent()
{
auto state = hUsbHostHS.gState;
@@ -137,12 +170,12 @@ bool USBHostHandle::GetPresent()
&& state != HOST_DEV_DISCONNECTED);
}
-// Shared USB IRQ Handlers are located in sys/System.cpp
+// Shared USB IRQ Handlers are located in sys/System.cpps
// This isn't super useful for our typical code structure
-static void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id)
+static void USBH_UserProcess(USBH_HandleTypeDef* phost, uint8_t id)
{
- auto &conf = msd_impl.GetConfig();
+ auto& conf = usbh_impl.GetConfig();
switch(id)
{
case HOST_USER_SELECT_CONFIGURATION: break;
@@ -180,4 +213,4 @@ static void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id)
break;
default: break;
}
-}
\ No newline at end of file
+}
diff --git a/src/hid/usb_host.h b/src/hid/usb_host.h
index 102b5ae5a..ef060b964 100644
--- a/src/hid/usb_host.h
+++ b/src/hid/usb_host.h
@@ -1,7 +1,8 @@
-#ifndef DSY_MSD
-#define DSY_MSD
+#ifndef USB_HOST_H
+#define USB_HOST_H
#include
+#include "usbh_def.h"
namespace daisy
{
@@ -39,7 +40,7 @@ class USBHostHandle
FAIL,
NOT_SUPPORTED,
UNRECOVERED_ERROR,
- ERROR_SPEED_UNKNOWN,
+ ERROR_SPEED_UNKNOWN
};
/** @brief User defineable callback for USB Connection */
@@ -49,7 +50,7 @@ class USBHostHandle
typedef void (*DisconnectCallback)(void* data);
/** @brief User defineable callback upon completion of class initialization
- * For example, when a USB drive is connected and the mass storage class
+ * For example, when a USB drive is connected and the usb device class
* initialization has finished, this callback will fire.
*
* @param userdata a pointer to some arbitrary data for use by the user.
@@ -66,7 +67,7 @@ class USBHostHandle
*/
typedef void (*ErrorCallback)(void* data);
- /** @brief Configuration structure for interfacing with MSD Driver */
+ /** @brief Configuration structure for interfacing with USB host Driver */
struct Config
{
Config()
@@ -84,17 +85,27 @@ class USBHostHandle
void* userdata;
};
+ /**
+ * Register a USB class
+ */
+ Result RegisterClass(USBH_ClassTypeDef* pClass);
+
/** Initializes the USB drivers and starts timeout.
*
* \param config Configuration struct for initialization
*/
- Result Init(Config config);
+ Result Init(USBHostHandle::Config& config);
- /** Deinitializes MSD-related peripherals
+ /** Deinitializes USB host-related peripherals
*
*/
Result Deinit();
+ /**
+ * Returns true if the specified class is active
+ */
+ bool IsActiveClass(USBH_ClassTypeDef* usbClass);
+
/** Manages usb host functionality
*
*/
@@ -115,6 +126,10 @@ class USBHostHandle
*/
bool GetPresent();
+ /** Returns name of the connected devices if there is one
+ */
+ const char* GetProductName();
+
USBHostHandle() : pimpl_(nullptr) {}
USBHostHandle(const USBHostHandle& other) = default;
USBHostHandle& operator=(const USBHostHandle& other) = default;
@@ -127,4 +142,4 @@ class USBHostHandle
} // namespace daisy
-#endif // DSY_MSD
\ No newline at end of file
+#endif // DSY_MSD
diff --git a/src/hid/usb_midi.cpp b/src/hid/usb_midi.cpp
index 811b1c404..539d0ff56 100644
--- a/src/hid/usb_midi.cpp
+++ b/src/hid/usb_midi.cpp
@@ -1,8 +1,16 @@
#include "system.h"
#include "usbd_cdc.h"
+#include "usbh_midi.h"
#include "hid/usb_midi.h"
#include
+extern "C"
+{
+ extern USBH_HandleTypeDef hUsbHostHS;
+}
+
+#define pUSB_Host &hUsbHostHS
+
using namespace daisy;
class MidiUsbTransport::Impl
@@ -12,6 +20,7 @@ class MidiUsbTransport::Impl
void StartRx(MidiRxParseCallback callback, void* context)
{
+ FlushRx();
rx_active_ = true;
parse_callback_ = callback;
parse_context_ = context;
@@ -80,6 +89,12 @@ void ReceiveCallback(uint8_t* buffer, uint32_t* length)
}
}
+static void HostReceiveCallback(uint8_t* buffer, size_t sz, void* pUser)
+{
+ uint32_t len = sz;
+ ReceiveCallback(buffer, &len);
+}
+
void MidiUsbTransport::Impl::Init(Config config)
{
// Borrowed from logger
@@ -88,36 +103,55 @@ void MidiUsbTransport::Impl::Init(Config config)
*/
// static_assert(1u == sizeof(MidiUsbTransport::Impl::usb_handle_), "UsbHandle is not static");
- // This tells the USB middleware to send out MIDI descriptors instead of CDC
- usbd_mode = USBD_MODE_MIDI;
- config_ = config;
+ config_ = config;
+ rx_active_ = false;
+
+ if(config_.periph == Config::HOST)
+ {
+ System::Delay(10);
+ USBH_MIDI_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;
+ UsbHandle::UsbPeriph periph = UsbHandle::FS_INTERNAL;
+ if(config_.periph == Config::EXTERNAL)
+ periph = UsbHandle::FS_EXTERNAL;
- usb_handle_.Init(periph);
+ usb_handle_.Init(periph);
- rx_active_ = false;
- System::Delay(10);
- usb_handle_.SetReceiveCallback(ReceiveCallback, periph);
+ System::Delay(10);
+ usb_handle_.SetReceiveCallback(ReceiveCallback, periph);
+ }
}
void MidiUsbTransport::Impl::Tx(uint8_t* buffer, size_t size)
{
- UsbHandle::Result result;
- int attempt_count = config_.tx_retry_count;
- bool should_retry;
+ int attempt_count = config_.tx_retry_count;
+ bool should_retry;
MidiToUsb(buffer, size);
do
{
- if(config_.periph == Config::EXTERNAL)
- result = usb_handle_.TransmitExternal(tx_buffer_, tx_ptr_);
+ if(config_.periph == Config::HOST)
+ {
+ MIDI_ErrorTypeDef result;
+ result = USBH_MIDI_Transmit(pUSB_Host, tx_buffer_, tx_ptr_);
+ should_retry = (result == MIDI_BUSY) && attempt_count--;
+ }
else
- result = usb_handle_.TransmitInternal(tx_buffer_, tx_ptr_);
+ {
+ 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--;
+ }
- should_retry = (result == UsbHandle::Result::ERR) && attempt_count--;
if(should_retry)
System::DelayUs(100);
diff --git a/src/hid/usb_midi.h b/src/hid/usb_midi.h
index 78e5cd983..900d5afcd 100644
--- a/src/hid/usb_midi.h
+++ b/src/hid/usb_midi.h
@@ -23,7 +23,8 @@ class MidiUsbTransport
enum Periph
{
INTERNAL = 0,
- EXTERNAL
+ EXTERNAL,
+ HOST
};
Periph periph;
diff --git a/src/usbh/usbh_conf.c b/src/usbh/usbh_conf.c
index a03a01eea..746909ea5 100644
--- a/src/usbh/usbh_conf.c
+++ b/src/usbh/usbh_conf.c
@@ -217,6 +217,8 @@ USBH_StatusTypeDef USBH_LL_Init(USBH_HandleTypeDef *phost)
/* Init USB_IP */
if(phost->id == HOST_HS)
{
+ memset(&hhcd_USB_OTG_HS, 0, sizeof(hhcd_USB_OTG_HS));
+
/* Link the driver to the stack. */
hhcd_USB_OTG_HS.pData = phost;
phost->pData = &hhcd_USB_OTG_HS;
diff --git a/src/usbh/usbh_conf.h b/src/usbh/usbh_conf.h
index 6d26b89c7..ab71b4387 100644
--- a/src/usbh/usbh_conf.h
+++ b/src/usbh/usbh_conf.h
@@ -76,7 +76,7 @@ extern "C"
#define USBH_KEEP_CFG_DESCRIPTOR 1U
/*---------- -----------*/
-#define USBH_MAX_NUM_SUPPORTED_CLASS 1U
+#define USBH_MAX_NUM_SUPPORTED_CLASS 2U
/*---------- -----------*/
#define USBH_MAX_SIZE_CONFIGURATION 256U
@@ -127,11 +127,12 @@ extern "C"
/* DEBUG macros */
#if(USBH_DEBUG_LEVEL > 0U)
-#define USBH_UsrLog(...) \
- do \
- { \
- printf(__VA_ARGS__); \
- printf("\n"); \
+ extern void USBH_LogPrint(const char* format, ...);
+#define USBH_UsrLog(...) \
+ do \
+ { \
+ USBH_LogPrint(__VA_ARGS__); \
+ USBH_LogPrint("\n"); \
} while(0)
#else
#define USBH_UsrLog(...) \
@@ -142,12 +143,12 @@ extern "C"
#if(USBH_DEBUG_LEVEL > 1U)
-#define USBH_ErrLog(...) \
- do \
- { \
- printf("ERROR: "); \
- printf(__VA_ARGS__); \
- printf("\n"); \
+#define USBH_ErrLog(...) \
+ do \
+ { \
+ USBH_LogPrint("ERROR: "); \
+ USBH_LogPrint(__VA_ARGS__); \
+ USBH_LogPrint("\n"); \
} while(0)
#else
#define USBH_ErrLog(...) \
@@ -157,12 +158,12 @@ extern "C"
#endif
#if(USBH_DEBUG_LEVEL > 2U)
-#define USBH_DbgLog(...) \
- do \
- { \
- printf("DEBUG : "); \
- printf(__VA_ARGS__); \
- printf("\n"); \
+#define USBH_DbgLog(...) \
+ do \
+ { \
+ USBH_LogPrint("DEBUG : "); \
+ USBH_LogPrint(__VA_ARGS__); \
+ USBH_LogPrint("\n"); \
} while(0)
#else
#define USBH_DbgLog(...) \