diff --git a/src/SerialCDC_tusb.cpp b/src/SerialCDC_tusb.cpp index c1eabba39..5fab1436c 100644 --- a/src/SerialCDC_tusb.cpp +++ b/src/SerialCDC_tusb.cpp @@ -9,6 +9,7 @@ #if SUPPORT_USB +#include "TinyUsbInterface.h" #include "SerialCDC_tusb.h" #if CORE_USES_TINYUSB @@ -27,18 +28,35 @@ SerialCDC::SerialCDC() noexcept void SerialCDC::Start(Pin p) noexcept { +#if CFG_TUH_ENABLED + if (CoreUsbIsHostMode()) + { + return; + } +#endif + vBusPin = p; while (!tud_inited()) { delay(10); } running = true; } void SerialCDC::end() noexcept { +#if CFG_TUH_ENABLED + if (CoreUsbIsHostMode()) + { + return; + } +#endif running = false; } bool SerialCDC::IsConnected() const noexcept { - return tud_cdc_connected(); + return +#if CFG_TUH_ENABLED + !CoreUsbIsHostMode() && +#endif + tud_cdc_connected(); } // Overridden virtual functions @@ -49,36 +67,57 @@ bool SerialCDC::IsConnected() const noexcept // available() returned nonzero bit read() never read it. Now we check neither when reading. int SerialCDC::read() noexcept { - if (!running) - { - return -1; - } - - if (tud_cdc_available()) - { - return tud_cdc_read_char(); - } - return -1; + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) + { + return -1; + } + + if (tud_cdc_available()) + { + return tud_cdc_read_char(); + } + return -1; } int SerialCDC::available() noexcept { - if (!running) - { - return 0; - } + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) + { + return 0; + } - return tud_cdc_available(); + return tud_cdc_available(); } size_t SerialCDC::readBytes(char * _ecv_array buffer, size_t length) noexcept { + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) + { + return 0; + } + return tud_cdc_read (buffer, length); } void SerialCDC::flush() noexcept { - if (!running) + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) { return; } @@ -88,7 +127,11 @@ void SerialCDC::flush() noexcept size_t SerialCDC::canWrite() noexcept { - if (!running) + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) { return 0; } @@ -99,13 +142,23 @@ size_t SerialCDC::canWrite() noexcept // Write single character, blocking size_t SerialCDC::write(uint8_t c) noexcept { +#if CFG_TUH_ENABLED + if (CoreUsbIsHostMode()) + { + return 0; + } +#endif return write(&c, 1); } // Blocking write block size_t SerialCDC::write(const uint8_t *buf, size_t length) noexcept { - if (!running) + if (!running +#if CFG_TUH_ENABLED + || CoreUsbIsHostMode() +#endif + ) { return 0; } diff --git a/src/TinyUsbInterface.cpp b/src/TinyUsbInterface.cpp index 8914e0817..1df7f6dd7 100644 --- a/src/TinyUsbInterface.cpp +++ b/src/TinyUsbInterface.cpp @@ -29,6 +29,8 @@ #include "class/hid/hid_device.h" #include "class/audio/audio.h" #include "class/midi/midi.h" +#include "host/hcd.h" +#include "device/dcd.h" #if SAME70 @@ -266,11 +268,27 @@ extern "C" const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t lang return desc_str; } +#if CFG_TUH_ENABLED +static volatile bool isHostMode = false; +static volatile bool changingMode = false; + +static Pin UsbVbusDetect; +static Pin UsbVbusOn; +static Pin UsbModeSwitch; +static Pin UsbModeDetect; +#endif + // Call this to initialise the hardware -void CoreUsbInit(NvicPriority priority) noexcept +void CoreUsbInit(NvicPriority priority, Pin usbVbusDetect, Pin usbVbusOn, Pin usbModeSwitch, Pin usbModeDetect) noexcept { -#if SAME70 +#if CFG_TUH_ENABLED + UsbVbusDetect = usbVbusDetect; + UsbVbusOn = usbVbusOn; + UsbModeSwitch = usbModeSwitch; + UsbModeDetect = usbModeDetect; +#endif +#if SAME70 // Set the USB interrupt priority to a level that is allowed to make FreeRTOS calls NVIC_SetPriority(USBHS_IRQn, priority); @@ -323,24 +341,162 @@ void CoreUsbInit(NvicPriority priority) noexcept #endif } +#if CFG_TUH_ENABLED +bool CoreUsbSetHostMode(bool hostMode, const StringRef& reply) +{ + if (changingMode) + { + reply.printf("Previous USB mode change still in progress"); + return false; + } + + if (!CoreUsbIsHostMode() && hostMode && digitalRead(UsbVbusDetect)) + { + reply.printf("Unable to change to host mode, board plugged in to computer\n"); + return false; + } + + if (hostMode != CoreUsbIsHostMode()) + { + CoreUsbStop(); + changingMode = true; + } + + return true; +} + +bool CoreUsbIsHostMode() +{ + return isHostMode; +} + // USB Device Driver task // This top level thread process all usb events and invoke callbacks extern "C" void CoreUsbDeviceTask(void* param) noexcept { (void)param; - // This should be called after scheduler/kernel is started. - // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. - tusb_init(); + while (true) + { + digitalWrite(UsbVbusOn, isHostMode); + + auto tusb_init = isHostMode ? tuh_init : tud_init; + if (changingMode) + { + auto tusb_inited = isHostMode ? tuh_inited : tud_inited; + if (tusb_inited()) + { + // TinyUSB class drivers already inited previously, just call + // the functions to initialize the USB peripheral. + if (isHostMode) + { + hcd_init(0); + hcd_int_enable(0); + } + else + { + dcd_init(0); + dcd_int_enable(0); + } + } + else + { + tusb_init(0); + } + } + else + { + tusb_init(0); + } + + auto tusb_task = isHostMode ? tuh_task_ext : tud_task_ext; - // RTOS forever loop - while (1) + changingMode = false; + while (!changingMode) + { + tusb_task(100, false); + } + + // Disable pipes, deallocate DPRAM + for (int i = 9; i >= 0; i--) + { + if (isHostMode) + { + USBHS->USBHS_HSTPIP &= ~(USBHS_HSTPIP_PEN0 << i); + USBHS->USBHS_HSTPIPCFG[i] &= ~(USBHS_HSTPIPCFG_ALLOC); + } + else + { + USBHS->USBHS_DEVEPT &= ~(USBHS_DEVEPT_EPEN0 << i); + USBHS->USBHS_DEVEPTCFG[i] &= ~(USBHS_DEVEPTCFG_ALLOC); + } + } + + // Reset DMA registers + for (int i = 0; i < 7; i++) + { + if (isHostMode) + { + USBHS->USBHS_HSTDMA[i].USBHS_HSTDMAADDRESS = 0; + USBHS->USBHS_HSTDMA[i].USBHS_HSTDMACONTROL = 0; + USBHS->USBHS_HSTDMA[i].USBHS_HSTDMASTATUS = 0; + } + else + { + USBHS->USBHS_DEVDMA[i].USBHS_DEVDMAADDRESS = 0; + USBHS->USBHS_DEVDMA[i].USBHS_DEVDMACONTROL = 0; + USBHS->USBHS_DEVDMA[i].USBHS_DEVDMASTATUS = 0; + } + } + + // Deinit current tinyUSB host context. Not needed for device + // since changing mode requires the board to not be connected + // to a host, so if we are changing mode then the DCD_EVENT_UNPLUGGED + // must have been handled in the past already. + if (isHostMode) + { + hcd_event_device_remove(0, false); + tusb_task(100, false); + } + + // Reset USB hardware + USBHS->USBHS_CTRL &= ~(USBHS_CTRL_USBE | USBHS_CTRL_UIMOD); + USBHS->USBHS_CTRL |= USBHS_CTRL_FRZCLK; + + // Toggle mode for next loop + isHostMode = !isHostMode; + } +} +#else +extern "C" void CoreUsbDeviceTask(void* param) noexcept +{ + (void)param; + + tud_init(0); + while (true) { - // tinyusb device task tud_task(); -// tud_cdc_write_flush(); } } +#endif + +void CoreUsbStop() +{ +#if CFG_TUH_ENABLED + digitalWrite(UsbVbusOn, false); +#endif + +#if SAME5x + NVIC_DisableIRQ(USB_0_IRQn); + NVIC_DisableIRQ(USB_1_IRQn); + NVIC_DisableIRQ(USB_2_IRQn); + NVIC_DisableIRQ(USB_3_IRQn); + USB->DEVICE.CTRLA.reg &= ~USB_CTRLA_ENABLE; +#elif SAME70 + NVIC_DisableIRQ((IRQn_Type)ID_USBHS); + USBHS->USBHS_CTRL &= ~USBHS_CTRL_USBE; +#endif +} #if RP2040 // RP2040 USB configuration has HID enabled by default @@ -390,7 +546,12 @@ uint32_t numUsbInterrupts = 0; extern "C" void USBHS_Handler() noexcept { ++numUsbInterrupts; +#if CFG_TUH_ENABLED + auto tusb_handler = isHostMode ? tuh_int_handler : tud_int_handler; + tusb_handler(0); +#else tud_int_handler(0); +#endif } #elif SAME5x diff --git a/src/TinyUsbInterface.h b/src/TinyUsbInterface.h index b5dd7f5f3..da99ee8fc 100644 --- a/src/TinyUsbInterface.h +++ b/src/TinyUsbInterface.h @@ -23,12 +23,23 @@ #define TINYUSBINTERFACE_H_INCLUDED #include +#include #if SUPPORT_USB && CORE_USES_TINYUSB +#include "tusb_option.h" -void CoreUsbInit(NvicPriority priority) noexcept; // call this to initialise the hardware +void CoreUsbInit(NvicPriority priority, Pin usbVbusDetect = NoPin, Pin usbVbusOn = NoPin, Pin usbModeSwitch = NoPin, Pin usbModeDetect = NoPin) noexcept; // call this to initialise the hardware extern "C" [[noreturn]] void CoreUsbDeviceTask(void* param) noexcept; // this must be called by the USB task +void CoreUsbStop(); +#if CFG_TUH_ENABLED +bool CoreUsbSetHostMode(bool hostMode, const StringRef& reply); +bool CoreUsbIsHostMode(); #endif +#else +#define CFG_TUH_ENABLED 0 +#endif + + #endif // TINYUSBINTERFACE_H_INCLUDED