diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index ac8b18de3..4f974f1ee 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -1,6 +1,7 @@ #include "apps_container.h" #include "global_preferences.h" #include +#include //TODO REMOVE extern "C" { #include @@ -78,6 +79,13 @@ void AppsContainer::suspend(bool checkIfPowerKeyReleased) { } bool AppsContainer::dispatchEvent(Ion::Events::Event event) { + if (event == Ion::Events::USBEnumeration) { + switchTo(appSnapshotAtIndex(0)); + // TODO remove include kandinsky.h + Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorBlue); + Ion::USB::DFU(); + } + bool alphaLockWantsRedraw = updateAlphaLock(); bool didProcessEvent = Container::dispatchEvent(event); diff --git a/escher/src/run_loop.cpp b/escher/src/run_loop.cpp index c35fcbd6a..7f5916f1a 100644 --- a/escher/src/run_loop.cpp +++ b/escher/src/run_loop.cpp @@ -31,7 +31,6 @@ bool RunLoop::step() { int eventDuration = Timer::TickDuration; int timeout = eventDuration; Ion::Events::Event event = Ion::Events::getEvent(&timeout); - assert(event.isDefined()); eventDuration -= timeout; assert(eventDuration >= 0); @@ -54,6 +53,12 @@ bool RunLoop::step() { } } + if (event == Ion::Events::USBEnumeration) { + dispatchEvent(event); + } + + assert(event.isDefined()); + #if ESCHER_LOG_EVENTS_BINARY Ion::Console::writeChar((char)event.id()); #endif diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index b79842ca7..5784816ec 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -215,6 +215,7 @@ constexpr Event UpperZ = Event::ShiftAlphaKey(Keyboard::Key::H4); constexpr Event None = Event::Special(0); constexpr Event Termination = Event::Special(1); constexpr Event TimerFire = Event::Special(2); +constexpr Event USBEnumeration = Event::Special(3); } } diff --git a/ion/include/ion/usb.h b/ion/include/ion/usb.h index 760d9cc63..f30aacbaa 100644 --- a/ion/include/ion/usb.h +++ b/ion/include/ion/usb.h @@ -5,6 +5,7 @@ namespace Ion { namespace USB { bool isPlugged(); +void DFU(); } } diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 4d1ccd6c4..3597947ec 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -29,7 +29,7 @@ objs += $(addprefix ion/src/device/, \ wakeup.o \ ) -objs += $(addprefix ion/src/device/usb/, \ +usb_objs += $(addprefix ion/src/device/usb/, \ calculator.o \ device.o\ dfu_interface.o\ @@ -39,6 +39,10 @@ objs += $(addprefix ion/src/device/usb/, \ setup_packet.o\ ) +$(usb_objs): SFLAGS += -fPIC + +objs += $(usb_objs) + objs += $(addprefix ion/src/device/usb/stack/, \ configuration_descriptor.o \ descriptor.o\ diff --git a/ion/src/device/boot/flash.ld b/ion/src/device/boot/flash.ld index e5ddcc9e4..4aa70d303 100644 --- a/ion/src/device/boot/flash.ld +++ b/ion/src/device/boot/flash.ld @@ -38,6 +38,16 @@ SECTIONS { KEEP(*(.header)) } >FLASH + .usb : { + . = ALIGN(4); + _flash_usb_stack_start = .; + *(.text.*3Ion3USB6Device*) + *(.text.memcpy) + *(.text.strlen) + *(.text.*Register*) + _flash_usb_stack_end = .; + } >FLASH + .text : { . = ALIGN(4); *(.text) diff --git a/ion/src/device/events_keyboard.cpp b/ion/src/device/events_keyboard.cpp index 36487a0a8..ae6bcf14e 100644 --- a/ion/src/device/events_keyboard.cpp +++ b/ion/src/device/events_keyboard.cpp @@ -1,4 +1,5 @@ #include +#include "regs/otg.h" #include namespace Ion { @@ -33,6 +34,12 @@ Event getEvent(int * timeout) { uint64_t keysSeenUp = 0; uint64_t keysSeenTransitionningFromUpToDown = 0; while (true) { + // Check if the USB is plugged and if we are being enumerated by a host. + if (OTG.GINTSTS()->getUSBRST()) { + // The device is being enumerated. Fire an event. + return Events::USBEnumeration; + } + Keyboard::State state = Keyboard::scan(); keysSeenUp |= ~state; keysSeenTransitionningFromUpToDown = keysSeenUp & state; diff --git a/ion/src/device/usb.cpp b/ion/src/device/usb.cpp index 80c861209..bd5877e64 100644 --- a/ion/src/device/usb.cpp +++ b/ion/src/device/usb.cpp @@ -4,6 +4,11 @@ #include "device.h" #include "display.h" #include "regs/regs.h" +#include +#include + +extern char _flash_usb_stack_start; +extern char _flash_usb_stack_end; namespace Ion { namespace USB { @@ -12,6 +17,43 @@ bool isPlugged() { return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin()); } +typedef void (*PollFunctionPointer)(Device::Calculator *); + +void DFU() { + size_t usb_stack_size = &_flash_usb_stack_end - &_flash_usb_stack_start; + + /* 1 - Allocate a buffer in RAM that's large enough to contain all the code + * and objects of our USB stack. */ + + size_t ram_buffer_size = usb_stack_size + sizeof(Device::Calculator); + char * ram_buffer = static_cast(malloc(ram_buffer_size)); + if (ram_buffer == nullptr) { + // Allocation failure + return; + } + + // 2 - Copy the USB stack code from Flash to RAM + char * ram_usb_stack_start = ram_buffer; + memcpy(ram_usb_stack_start, &_flash_usb_stack_start, usb_stack_size); + + // 3 - Initialize data in RAM buffer + Device::Calculator * calculator = new (ram_buffer + usb_stack_size) Device::Calculator(); + + // 4 - Figure out the address of Ion::USB::Device::poll() in RAM + char * flash_poll_function_address = reinterpret_cast(&Ion::USB::Device::poll); + assert(flash_poll_function_address >= &_flash_usb_stack_start); + assert(flash_poll_function_address < &_flash_usb_stack_end); + char * ram_poll_function_address = flash_poll_function_address - &_flash_usb_stack_start + ram_usb_stack_start; + PollFunctionPointer ram_poll_function = reinterpret_cast(ram_poll_function_address); + + // 5 - Execute Ion::USB::Device::poll() from RAM + ram_poll_function(calculator); + + // 6 - Upon return, delete data and free the buffer in RAM + calculator->~Calculator(); + free(ram_buffer); +} + } } @@ -19,18 +61,23 @@ namespace Ion { namespace USB { namespace Device { -Calculator * calculator() { - static Calculator calculator; - return &calculator; +void poll(Calculator * calculator) { + // Wait for speed enumeration done + while (!OTG.GINTSTS()->getENUMDNE()) { + } + + while (true) { + calculator->poll(); + } } void init() { initGPIO(); - calculator()->init(); + initOTG(); } void shutdown() { - calculator()->shutdown(); + shutdownOTG(); shutdownGPIO(); } @@ -74,6 +121,68 @@ void shutdownGPIO() { } } +void initOTG() { + // Wait for AHB idle + while (!OTG.GRSTCTL()->getAHBIDL()) { + } + + // Core soft reset + OTG.GRSTCTL()->setCSRST(true); + while (OTG.GRSTCTL()->getCSRST()) { + } + + // Enable the USB transceiver + OTG.GCCFG()->setPWRDWN(true); + // FIXME: Understand why VBDEN is required + OTG.GCCFG()->setVBDEN(true); + + // Get out of soft-disconnected state + OTG.DCTL()->setSDIS(false); + + // Force peripheral only mode + OTG.GUSBCFG()->setFDMOD(true); + + /* Configure the USB turnaround time. + * This has to be configured depending on the AHB clock speed. */ + OTG.GUSBCFG()->setTRDT(0x6); + + // Clear the interrupts + OTG.GINTSTS()->set(0); + + // Full speed device + OTG.DCFG()->setDSPD(OTG::DCFG::DSPD::FullSpeed); + + // FIFO-size = 128 * 32bits + // FIXME: Explain :-) Maybe we can increase it. + OTG.GRXFSIZ()->setRXFD(128); + + // Unmask the interrupt line assertions + OTG.GAHBCFG()->setGINTMSK(true); + + // Restart the PHY clock. + OTG.PCGCCTL()->setSTPPCLK(0); + + // Pick which interrupts we're interested in + class OTG::GINTMSK intMask(0); // Reset value + intMask.setENUMDNEM(true); // Speed enumeration done + intMask.setUSBRST(true); // USB reset + intMask.setRXFLVLM(true); // Receive FIFO non empty + intMask.setIEPINT(true); // IN endpoint interrupt + intMask.setWUIM(true); // Resume / wakeup + intMask.setUSBSUSPM(true); // USB suspend + OTG.GINTMSK()->set(intMask); + + // Unmask IN endpoint interrupt 0 + OTG.DAINTMSK()->setIEPM(true); + + // Unmask the transfer completed interrupt + OTG.DIEPMSK()->setXFRCM(true); +} + +void shutdownOTG() { + //TODO ? +} + } } } diff --git a/ion/src/device/usb.h b/ion/src/device/usb.h index 81ed46f4a..1f404c6d8 100644 --- a/ion/src/device/usb.h +++ b/ion/src/device/usb.h @@ -24,8 +24,11 @@ void init(); void shutdown(); void initGPIO(); void shutdownGPIO(); +void initOTG(); +void shutdownOTG(); -Calculator * calculator(); +// Enters DFU mode, returns when detached. +void poll(Calculator * calculator); } } diff --git a/ion/src/device/usb/calculator.h b/ion/src/device/usb/calculator.h index fde11162b..2b34ae5ba 100644 --- a/ion/src/device/usb/calculator.h +++ b/ion/src/device/usb/calculator.h @@ -9,7 +9,7 @@ #include "stack/dfu_functional_descriptor.h" #include "stack/interface_descriptor.h" #include "stack/language_id_string_descriptor.h" -#include "stack/string_descriptor.h" +#include "stack/relocatable_string_descriptor.h" #include #include @@ -90,10 +90,10 @@ private: InterfaceDescriptor m_interfaceDescriptor; ConfigurationDescriptor m_configurationDescriptor; LanguageIDStringDescriptor m_languageStringDescriptor; - StringDescriptor m_manufacturerStringDescriptor; - StringDescriptor m_productStringDescriptor; - StringDescriptor m_serialNumberStringDescriptor; - StringDescriptor m_interfaceStringDescriptor; + RelocatableStringDescriptor<9> m_manufacturerStringDescriptor; + RelocatableStringDescriptor<11> m_productStringDescriptor; + RelocatableStringDescriptor<6> m_serialNumberStringDescriptor; + RelocatableStringDescriptor<47> m_interfaceStringDescriptor; Descriptor * m_descriptors[7]; // We do not need to include m_interfaceDescriptor nor m_dfuFunctionalDescriptor, because they are inluded in other descriptors. diff --git a/ion/src/device/usb/device.cpp b/ion/src/device/usb/device.cpp index 700bab310..cd77a39a7 100644 --- a/ion/src/device/usb/device.cpp +++ b/ion/src/device/usb/device.cpp @@ -7,82 +7,7 @@ namespace Device { static inline uint16_t min(uint16_t x, uint16_t y) { return (xgetAHBIDL()) { - } - - // Core soft reset - OTG.GRSTCTL()->setCSRST(true); - while (OTG.GRSTCTL()->getCSRST()) { - } - - // Enable the USB transceiver - OTG.GCCFG()->setPWRDWN(true); - // FIXME: Understand why VBDEN is required - OTG.GCCFG()->setVBDEN(true); - - // Get out of soft-disconnected state - OTG.DCTL()->setSDIS(false); - - // Force peripheral only mode - OTG.GUSBCFG()->setFDMOD(true); - - /* Configure the USB turnaround time. - * This has to be configured depending on the AHB clock speed. */ - OTG.GUSBCFG()->setTRDT(0x6); - - // Clear the interrupts - OTG.GINTSTS()->set(0); - - // Full speed device - OTG.DCFG()->setDSPD(OTG::DCFG::DSPD::FullSpeed); - - // FIFO-size = 128 * 32bits - // FIXME: Explain :-) Maybe we can increase it. - OTG.GRXFSIZ()->setRXFD(128); - - // Unmask the interrupt line assertions - OTG.GAHBCFG()->setGINTMSK(true); - - // Restart the PHY clock. - OTG.PCGCCTL()->setSTPPCLK(0); - - // Pick which interrupts we're interested in - class OTG::GINTMSK intMask(0); // Reset value - intMask.setENUMDNEM(true); // Speed enumeration done - intMask.setUSBRST(true); // USB reset - intMask.setRXFLVLM(true); // Receive FIFO non empty - intMask.setIEPINT(true); // IN endpoint interrupt - intMask.setWUIM(true); // Resume / wakeup - intMask.setUSBSUSPM(true); // USB suspend - OTG.GINTMSK()->set(intMask); - - // Unmask IN endpoint interrupt 0 - OTG.DAINTMSK()->setIEPM(1); - - // Unmask the transfer completed interrupt - OTG.DIEPMSK()->setXFRCM(true); - - // Wait for an USB reset - while (!OTG.GINTSTS()->getUSBRST()) { - } - - // Wait for ENUMDNE - while (!OTG.GINTSTS()->getENUMDNE()) { - } - - while (true) { - poll(); - } -} - -void Device::shutdown() { - //TODO ? -} - void Device::poll() { - // Read the interrupts class OTG::GINTSTS intsts(OTG.GINTSTS()->get()); diff --git a/ion/src/device/usb/device.h b/ion/src/device/usb/device.h index c80da4a70..030204ef5 100644 --- a/ion/src/device/usb/device.h +++ b/ion/src/device/usb/device.h @@ -20,13 +20,7 @@ public: m_ep0(this, interface) { } - - void init(); - void shutdown(); void poll(); - - //virtual bool controlTransfer(struct usb_setup_data *req, uint8_t **buf, uint16_t *len);/*, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req));*/ - protected: virtual Descriptor * descriptor(uint8_t type, uint8_t index) = 0; virtual void setActiveConfiguration(uint8_t configurationIndex) = 0; diff --git a/ion/src/device/usb/stack/relocatable_string_descriptor.h b/ion/src/device/usb/stack/relocatable_string_descriptor.h new file mode 100644 index 000000000..56e9d19f9 --- /dev/null +++ b/ion/src/device/usb/stack/relocatable_string_descriptor.h @@ -0,0 +1,31 @@ +#ifndef ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H +#define ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H + +#include "string_descriptor.h" +#include + +namespace Ion { +namespace USB { +namespace Device { + +template +class RelocatableStringDescriptor : public StringDescriptor { +public: + RelocatableStringDescriptor(const char * string) : + StringDescriptor() + { + strlcpy(m_string, string, T); + } +protected: + const char * string() const override { + return m_string; + } +private: + char m_string[T]; +}; + +} +} +} + +#endif diff --git a/ion/src/device/usb/stack/string_descriptor.cpp b/ion/src/device/usb/stack/string_descriptor.cpp index a3afb1482..9ba7ef663 100644 --- a/ion/src/device/usb/stack/string_descriptor.cpp +++ b/ion/src/device/usb/stack/string_descriptor.cpp @@ -1,4 +1,5 @@ #include "string_descriptor.h" +#include namespace Ion { namespace USB { @@ -6,7 +7,7 @@ namespace Device { void StringDescriptor::push(Channel * c) const { Descriptor::push(c); - const char * stringPointer = m_string; + const char * stringPointer = string(); while (*stringPointer != 0) { uint16_t stringAsUTF16CodePoint = *stringPointer; c->push(stringAsUTF16CodePoint); @@ -16,7 +17,7 @@ void StringDescriptor::push(Channel * c) const { uint8_t StringDescriptor::bLength() const { // The script is returned in UTF-16, hence the multiplication. - return Descriptor::bLength() + 2*strlen(m_string); + return Descriptor::bLength() + 2*strlen(string()); } } diff --git a/ion/src/device/usb/stack/string_descriptor.h b/ion/src/device/usb/stack/string_descriptor.h index f8b708956..1c9b5e6d9 100644 --- a/ion/src/device/usb/stack/string_descriptor.h +++ b/ion/src/device/usb/stack/string_descriptor.h @@ -2,7 +2,6 @@ #define ION_DEVICE_USB_STACK_STRING_DESCRIPTOR_H #include "descriptor.h" -#include namespace Ion { namespace USB { @@ -10,16 +9,11 @@ namespace Device { class StringDescriptor : public Descriptor { public: - constexpr StringDescriptor(const char * string) : - Descriptor(0x03), - m_string(string) - { - } + StringDescriptor() : Descriptor(0x03) { } protected: void push(Channel * c) const override; virtual uint8_t bLength() const override; -private: - const char * m_string; + virtual const char * string() const = 0; }; } diff --git a/ion/src/shared/dummy/usb.cpp b/ion/src/shared/dummy/usb.cpp index 208058977..bc077012e 100644 --- a/ion/src/shared/dummy/usb.cpp +++ b/ion/src/shared/dummy/usb.cpp @@ -3,3 +3,6 @@ bool Ion::USB::isPlugged() { return false; } + +void Ion::USB::DFU() { +} diff --git a/ion/src/simulator/usb.cpp b/ion/src/simulator/usb.cpp index 447cca33a..bc077012e 100644 --- a/ion/src/simulator/usb.cpp +++ b/ion/src/simulator/usb.cpp @@ -4,3 +4,5 @@ bool Ion::USB::isPlugged() { return false; } +void Ion::USB::DFU() { +}