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 : /// Get the number messages that failed to send.
54 : uint32_t getWriteError() const { return writing.error.load(mo_rlx); }
55 : /// Get and clear the number messages that failed to send.
56 : uint32_t clearWriteError() { return writing.error.exchange(0, mo_rlx); }
57 :
58 : protected:
59 : void reset(uint16_t packet_size = MaxPacketSize);
60 : bool wait_connect();
61 :
62 : private:
63 : static constexpr uint16_t MaxPacketSize = MaxPacketSizeV;
64 : static constexpr uint16_t SizeReserved = MaxPacketSize + 1;
65 :
66 : protected:
67 : // Derived should implement the following methods:
68 :
69 : /// Start a timeout (e.g. using a timer interrupt) that later calls
70 : /// @ref timeout_callback().
71 : void start_timeout() = delete;
72 : /// Cancel the timeout started by @ref timeout_callback().
73 : void cancel_timeout() = delete;
74 : /// Start a USB transfer (from the main program).
75 : void tx_start(const void *data, uint32_t size) = delete;
76 : /// Start a USB transfer (from the timeout callback).
77 : void tx_start_timeout(const void *data, uint32_t size) = delete;
78 : /// Start a USB transfer (from the USB interrupt handler).
79 : void tx_start_isr(const void *data, uint32_t size) = delete;
80 :
81 : private:
82 : constexpr static std::memory_order mo_seq = std::memory_order_seq_cst;
83 : constexpr static std::memory_order mo_rel = std::memory_order_release;
84 : constexpr static std::memory_order mo_acq = std::memory_order_acquire;
85 : constexpr static std::memory_order mo_rlx = std::memory_order_relaxed;
86 : constexpr static std::memory_order mo_acq_rel = std::memory_order_acq_rel;
87 :
88 : /// State for writing outgoing USB-MIDI data.
89 : struct Writing {
90 : struct Buffer {
91 : uint16_t size {0};
92 : alignas(MessageType) uint8_t buffer[MaxPacketSize];
93 : } buffers[2];
94 : interrupt_atomic<Buffer *> active_writebuffer {&buffers[0]};
95 : interrupt_atomic<Buffer *> sending {nullptr};
96 : interrupt_atomic<Buffer *> send_later {nullptr};
97 : interrupt_atomic<Buffer *> send_now {nullptr};
98 : interrupt_atomic<uint32_t> error {0};
99 : uint16_t packet_size = MaxPacketSize;
100 : } writing;
101 : using wbuffer_t = typename Writing::Buffer;
102 : bool disconnected = false;
103 :
104 94936 : uint32_t index_of(wbuffer_t *p) const { return p - writing.buffers; }
105 94936 : wbuffer_t *other_buf(wbuffer_t *p) {
106 94936 : return &writing.buffers[!index_of(p)];
107 : }
108 : uint32_t write_impl(const MessageType *msgs, uint32_t num_msgs);
109 :
110 : protected:
111 : void timeout_callback();
112 : void tx_callback();
113 : };
114 :
115 : END_CS_NAMESPACE
116 :
117 : #include "BulkTX.ipp"
|