Line data Source code
1 : #pragma once
2 :
3 : /**
4 : * @file
5 : * Type definitions and callback interfaces for communication between the
6 : * low-level BLE stacks and higher-level MIDI BLE backends.
7 : */
8 :
9 : #include <Settings/NamespaceSettings.hpp>
10 :
11 : #include "Util/compat.hpp"
12 :
13 : #include <cstddef>
14 : #include <cstdint>
15 :
16 : BEGIN_CS_NAMESPACE
17 :
18 : /// Represents a handle to the connection to another device.
19 : struct BLEConnectionHandle {
20 : uint16_t conn = 0xFFFF;
21 : explicit operator bool() const { return conn != 0xFFFF; }
22 :
23 : #if __cplusplus < 201402L
24 : BLEConnectionHandle() = default;
25 : BLEConnectionHandle(uint16_t conn) : conn {conn} {}
26 : #endif
27 : };
28 :
29 : /// Represents a handle to a local GATT characteristic.
30 : struct BLECharacteristicHandle {
31 : uint16_t characteristic = 0xFFFF;
32 : explicit operator bool() const { return characteristic != 0xFFFF; }
33 :
34 : #if __cplusplus < 201402L
35 : BLECharacteristicHandle() = default;
36 : BLECharacteristicHandle(uint16_t characteristic)
37 : : characteristic {characteristic} {}
38 : #endif
39 : };
40 :
41 : /// Non-owning, std::span-style read-only view of BLE data.
42 : struct BLEDataView {
43 : const uint8_t *data = nullptr;
44 : uint16_t length = 0;
45 30 : explicit operator bool() const { return length > 0; }
46 :
47 : #if __cplusplus < 201402L
48 : BLEDataView() = default;
49 : BLEDataView(const uint8_t *data, uint16_t length)
50 : : data {data}, length {length} {}
51 : #endif
52 : };
53 :
54 : /// Describes a byte buffer containing (part of) a BLE packet.
55 : /// Packets can be stored across multiple buffers, in which case the first
56 : /// first buffer has type `Packet` and subsequent buffers of the same packet
57 : /// have the type `Continuation`.
58 : enum class BLEDataType : uint8_t {
59 : None = 0, ///< No buffers available.
60 : Packet, ///< Buffer contains the start of a BLE packet.
61 : Continuation, ///< Buffer contains a chunk of a BLE packet.
62 : };
63 :
64 : /// Callable that returns the next chunk of data from a BLE packet when called.
65 : /// Uses type erasure with a static buffer (no dynamic memory allocations).
66 : class BLEDataGenerator {
67 : public:
68 : /// Get the next chunk of data from the BLE packet.
69 : /// Returns a chunk of size zero to indicate completion.
70 : /// @pre This wrapper is not empty.
71 : /// @pre There is still data available. Calling this function again after
72 : /// the previous call returned an empty chunk is not allowed.
73 : BLEDataView operator()();
74 : /// Release the resources of the underlying data generator.
75 : void clear();
76 : /// Check if this wrapper contains an underlying data generator.
77 : explicit operator bool() const { return instance; }
78 :
79 : /// Create an empty BLEDataGenerator.
80 : BLEDataGenerator() = default;
81 : /// Store a callable of type @p T and initialize it by @p args.
82 : template <class T, class... Args>
83 : BLEDataGenerator(compat::in_place_type_t<T>, Args &&...args);
84 : /// Store a callable of type @p T (with cv qualifiers and references
85 : /// removed) and initialize it by forwarding @p t.
86 : template <class T>
87 : BLEDataGenerator(compat::in_place_t, T &&t);
88 : BLEDataGenerator(const BLEDataGenerator &) = delete;
89 : BLEDataGenerator &operator=(const BLEDataGenerator &) = delete;
90 : BLEDataGenerator(BLEDataGenerator &&other) noexcept;
91 : BLEDataGenerator &operator=(BLEDataGenerator &&other) noexcept;
92 15 : ~BLEDataGenerator() { clear(); }
93 :
94 : private:
95 : /// Type-erased interface.
96 : struct Iface;
97 : /// Specific class that implements the type-erased interface, wrapping the
98 : /// type @p T.
99 : template <class T>
100 : struct Impl;
101 : /// Alignment of the buffer to allocate the underlying data generator.
102 : using buffer_align_t = max_align_t;
103 : /// Size of the buffer to allocate the underlying data generator.
104 : static constexpr size_t capacity = 4 * sizeof(void *) - sizeof(Iface *);
105 : /// Buffer used for allocation of the underlying data generator.
106 : alignas(buffer_align_t) compat::byte storage[capacity];
107 : //// Type-erased pointer to the underlying data generator in @ref storage.
108 : Iface *instance = nullptr;
109 : };
110 :
111 : /// Should a buffer of BLEData be consumed immediately inside of the callback,
112 : /// or can we hold on to it and process it later?
113 : enum class BLEDataLifetime {
114 : /// Buffer is valid only during the callback. Do not keep any pointers to it.
115 : ConsumeImmediately,
116 : /// Buffer is valid for as long as the owning @ref BLEDataGenerator is not
117 : /// resumed or destroyed.
118 : Managed,
119 : };
120 :
121 : /// Defines the interface for callback functions registered by the low-level
122 : /// BLE code.
123 : /// @warning These functions may be called from different tasks/threads or
124 : /// low-priority interrupt handlers. You cannot take locks, and you
125 : /// need to synchronize appropriately (e.g. using `std::atomic` or
126 : /// by using critical sections).
127 : class MIDIBLEInstance {
128 : public:
129 29 : virtual ~MIDIBLEInstance() = default;
130 : /// Called by the BLE stack when a connection is established.
131 : virtual void handleConnect(BLEConnectionHandle conn_handle) = 0;
132 : /// Called by the BLE stack when a connection is terminated.
133 : virtual void handleDisconnect(BLEConnectionHandle conn_handle) = 0;
134 : /// Called by the BLE stack when the maximum transmission unit for the
135 : /// connection changes.
136 : virtual void handleMTU(BLEConnectionHandle conn_handle, uint16_t mtu) = 0;
137 : /// Called by the BLE stack when the central subscribes to receive
138 : /// notifications for the MIDI GATT characteristic.
139 : virtual void handleSubscribe(BLEConnectionHandle conn_handle,
140 : BLECharacteristicHandle char_handle,
141 : bool notify) = 0;
142 : /// Called by the BLE stack when the central writes data to the MIDI GATT
143 : /// characteristic.
144 : virtual void handleData(BLEConnectionHandle conn_handle,
145 : BLEDataGenerator &&data,
146 : BLEDataLifetime lifetime) = 0;
147 : };
148 :
149 : /// Configuration options for the low-level BLE code.
150 : struct BLESettings {
151 : /// Device name (used for advertising)
152 : const char *device_name = "Control Surface MIDI";
153 : /// Connection intervals as multiples of 1.25 milliseconds
154 : /// (e.g.0x000C = 15 ms).
155 : struct {
156 : uint16_t minimum = 0x000C;
157 : uint16_t maximum = 0x000C;
158 : } connection_interval {};
159 : /// Encrypt the MIDI characteristic (NimBLE only).
160 : bool require_encryption = false;
161 : /// Set to true if you want the Arduino to always initiate the Bluetooth
162 : /// bonding or secure connection. As a result, it will show up as a "paired"
163 : /// device on your computer/phone/tablet. If set to false, security is still
164 : /// supported, but the central device should take the initiative.
165 : bool initiate_security = false;
166 : };
167 :
168 : END_CS_NAMESPACE
169 :
170 : #include "BLEAPI.ipp"
|