Line data Source code
1 : #pragma once 2 : 3 : #include <cstddef> 4 : 5 : #include <MIDI_Interfaces/USBMIDI/util/Atomic.hpp> 6 : #include <Settings/NamespaceSettings.hpp> 7 : 8 : BEGIN_CS_NAMESPACE 9 : 10 : /// Sends Bulk packets (IN for device mode, OUT for host mode) 11 : template <class Derived, class MessageTypeT, uint16_t MaxPacketSizeV> 12 : struct BulkTX { 13 : public: 14 : using MessageType = MessageTypeT; 15 : 16 : /// Send a MIDI USB message. May block. 17 : /// 18 : /// @param msg 19 : /// The 4-byte MIDI USB message to send. 20 : void write(MessageType msg); 21 : 22 : /// Send multiple MIDI USB messages. May block. 23 : /// 24 : /// @param msgs 25 : /// An array of 4-byte MIDI USB messages to send. 26 : /// @param num_msgs 27 : /// The number of messages in the array. 28 : void write(const MessageType *msgs, uint32_t num_msgs); 29 : 30 : /// Send multiple MIDI USB messages. May block. 31 : template <size_t N> 32 : void write(const MessageType (&msgs)[N]) { 33 : write(msgs, N); 34 : } 35 : 36 : /// Send multiple MIDI USB messages without blocking. 37 : /// 38 : /// @param msgs 39 : /// An array of 4-byte MIDI USB messages to send. 40 : /// @param num_msgs 41 : /// The number of messages in the array. 42 : /// @return The number of messages that were actually sent. 43 : uint32_t write_nonblock(const MessageType *msgs, uint32_t num_msgs); 44 : 45 : /// Try sending the buffered data now. 46 : /// Start transmitting the latest packet if possible, even if it isn't full 47 : /// yet. If the latest packet is empty, this function has no effect. 48 : void send_now(); 49 : 50 : /// Check if all transfers have completed. 51 : bool is_done() const; 52 : 53 : protected: 54 : void reset(uint16_t packet_size = MaxPacketSize); 55 : 56 : private: 57 : static constexpr uint16_t MaxPacketSize = MaxPacketSizeV; 58 : static constexpr uint16_t SizeReserved = MaxPacketSize + 1; 59 : 60 : protected: 61 : // Derived should implement the following methods: 62 : 63 : /// Start a timeout (e.g. using a timer interrupt) that later calls 64 : /// @ref timeout_callback(). 65 : void start_timeout() = delete; 66 : /// Cancel the timeout started by @ref timeout_callback(). 67 : void cancel_timeout() = delete; 68 : /// Start a USB transfer (from the main program). 69 : void tx_start(const void *data, uint32_t size) = delete; 70 : /// Start a USB transfer (from the timeout callback). 71 : void tx_start_timeout(const void *data, uint32_t size) = delete; 72 : /// Start a USB transfer (from the USB interrupt handler). 73 : void tx_start_isr(const void *data, uint32_t size) = delete; 74 : 75 : private: 76 : constexpr static std::memory_order mo_seq = std::memory_order_seq_cst; 77 : constexpr static std::memory_order mo_rel = std::memory_order_release; 78 : constexpr static std::memory_order mo_acq = std::memory_order_acquire; 79 : constexpr static std::memory_order mo_rlx = std::memory_order_relaxed; 80 : constexpr static std::memory_order mo_acq_rel = std::memory_order_acq_rel; 81 : 82 : /// State for writing outgoing USB-MIDI data. 83 : struct Writing { 84 : struct Buffer { 85 : uint16_t size {0}; 86 : alignas(MessageType) uint8_t buffer[MaxPacketSize]; 87 : } buffers[2]; 88 : interrupt_atomic<Buffer *> active_writebuffer {&buffers[0]}; 89 : interrupt_atomic<Buffer *> sending {nullptr}; 90 : interrupt_atomic<Buffer *> send_later {nullptr}; 91 : interrupt_atomic<Buffer *> send_now {nullptr}; 92 : uint16_t packet_size = MaxPacketSize; 93 : } writing; 94 : using wbuffer_t = typename Writing::Buffer; 95 : 96 94937 : uint32_t index_of(wbuffer_t *p) const { return p - writing.buffers; } 97 94937 : wbuffer_t *other_buf(wbuffer_t *p) { 98 94937 : return &writing.buffers[!index_of(p)]; 99 : } 100 : uint32_t write_impl(const MessageType *msgs, uint32_t num_msgs); 101 : 102 : protected: 103 : void timeout_callback(); 104 : void tx_callback(); 105 : }; 106 : 107 : END_CS_NAMESPACE 108 : 109 : #include "BulkTX.ipp"