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(...) \