Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
gatt_midi.cpp
Go to the documentation of this file.
1#if defined(ARDUINO_RASPBERRY_PI_PICO_W) && ENABLE_BLE
2
3#define BTSTACK_FILE__ "gatt_midi.cpp"
4
5#include <atomic>
6#include <cassert>
7#include <csignal>
8#include <cstdint>
9#include <cstdlib>
10#include <cstring>
11
12#include <btstack.h>
13
14#include "../BLEAPI.hpp"
15#include "advertising.hpp"
16#include "gatt_midi.h"
17#include "hci_event_names.hpp"
18
19#include <AH/Arduino-Wrapper.h>
20#include <AH/Debug/Debug.hpp>
21
22namespace cs::midi_ble_btstack {
23
24namespace {
25
26constexpr uint16_t midi_char_value_handle =
28constexpr uint16_t midi_cccd_handle =
30
31MIDIBLEInstance *instance = nullptr;
32BLESettings settings;
33btstack_packet_callback_registration_t hci_event_callback_registration;
34btstack_packet_callback_registration_t sm_event_callback_registration;
35
36// callback/event functions
37
38// HCI_SUBEVENT_LE_CONNECTION_COMPLETE
39void connection_handler(uint8_t *packet, [[maybe_unused]] uint16_t size) {
40 if (!instance)
41 return;
42 if (hci_subevent_le_connection_complete_get_status(packet) != 0)
43 return;
44 uint16_t conn_handle =
45 hci_subevent_le_connection_complete_get_connection_handle(packet);
46 // Request bonding
47 if (settings.initiate_security)
48 sm_request_pairing(conn_handle);
49 // Update the connection parameters
50 uint16_t conn_latency = 0;
51 uint16_t supervision_timeout = 400;
52 gap_request_connection_parameter_update(
53 conn_handle, settings.connection_interval.minimum,
54 settings.connection_interval.maximum, conn_latency,
55 supervision_timeout);
56 instance->handleConnect(BLEConnectionHandle {conn_handle});
57}
58// HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE
59void connection_update_handler([[maybe_unused]] uint8_t *packet,
60 [[maybe_unused]] uint16_t size) {
61 DEBUGREF( // clang-format off
62 "Connection update: status="
63 << hci_subevent_le_connection_update_complete_get_status(packet)
64 << ", connection interval="
65 << hci_subevent_le_connection_update_complete_get_conn_interval(packet)
66 << ", connection latency="
67 << hci_subevent_le_connection_update_complete_get_conn_latency(packet)
68 << ", supervision timeout="
69 << hci_subevent_le_connection_update_complete_get_supervision_timeout(packet));
70 // clang-format on
71}
72// HCI_SUBEVENT_LE_REMOTE_CONNECTION_PARAMETER_REQUEST
73void connection_param_req_handler([[maybe_unused]] uint8_t *packet,
74 [[maybe_unused]] uint16_t size) {
75 DEBUGREF( // clang-format off
76 "Connection parameter request: interval min="
77 << hci_subevent_le_remote_connection_parameter_request_get_interval_min(packet)
78 << ", interval max="
79 << hci_subevent_le_remote_connection_parameter_request_get_interval_max(packet)
80 << ", latency="
81 << hci_subevent_le_remote_connection_parameter_request_get_latency(packet)
82 << ", timeout="
83 << hci_subevent_le_remote_connection_parameter_request_get_timeout(packet));
84 // clang-format on
85}
86// HCI_EVENT_LE_META
87void le_packet_handler(uint8_t *packet, uint16_t size) {
88 uint8_t type = hci_event_le_meta_get_subevent_code(packet);
89 DEBUGREF("LE event: " << le_event_names[type] << " (0x" << hex << type
90 << dec << ")");
91 switch (type) {
92 case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
93 connection_handler(packet, size);
94 break;
95 case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
96 connection_update_handler(packet, size);
97 break;
98 case HCI_SUBEVENT_LE_REMOTE_CONNECTION_PARAMETER_REQUEST:
99 connection_param_req_handler(packet, size);
100 break;
101 default: break;
102 }
103}
104// HCI_EVENT_LE_META
105void gattservice_handler(uint8_t *packet, [[maybe_unused]] uint16_t size) {
106 [[maybe_unused]] uint8_t type =
107 hci_event_gattservice_meta_get_subevent_code(packet);
108 DEBUGREF("GATT service event: " << gattservice_event_names[type] << " (0x"
109 << hex << type << dec << ")");
110}
111// HCI_EVENT_DISCONNECTION_COMPLETE
112void disconnect_handler(uint8_t *packet, [[maybe_unused]] uint16_t size) {
113 assert(instance);
114 uint16_t conn_handle =
115 hci_event_disconnection_complete_get_connection_handle(packet);
116 instance->handleDisconnect(BLEConnectionHandle {conn_handle});
117}
118// ATT_EVENT_MTU_EXCHANGE_COMPLETE
119void mtu_exchange_complete_handler(uint8_t *packet,
120 [[maybe_unused]] uint16_t size) {
121 assert(instance);
122 uint16_t conn_handle = att_event_mtu_exchange_complete_get_handle(packet);
123 uint16_t mtu = att_event_mtu_exchange_complete_get_MTU(packet);
124 DEBUGREF("mtu=" << mtu);
125 instance->handleMTU(BLEConnectionHandle {conn_handle}, mtu);
126}
127// GATT_EVENT_MTU
128void gatt_event_mtu_handler(uint8_t *packet, [[maybe_unused]] uint16_t size) {
129 assert(instance);
130 uint16_t conn_handle = gatt_event_mtu_get_handle(packet);
131 uint16_t mtu = gatt_event_mtu_get_MTU(packet);
132 instance->handleMTU(BLEConnectionHandle {conn_handle}, mtu);
133}
134// BTSTACK_EVENT_STATE
135void btstack_event_state_handler(uint8_t *packet,
136 [[maybe_unused]] uint16_t size) {
137 if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING)
138 return;
139 bd_addr_t local_addr;
140 gap_local_bd_addr(local_addr);
141 DEBUGREF("BTstack up and running on " << bd_addr_to_str(local_addr));
142}
143
144// Handles all HCI event packets.
145void packet_handler(uint8_t packet_type, [[maybe_unused]] uint16_t channel,
146 uint8_t *packet, uint16_t size) {
147 if (packet_type != HCI_EVENT_PACKET)
148 return;
149 auto type = hci_event_packet_get_type(packet);
150 DEBUGREF("HCI event: " << hci_event_names[type] << " (0x" << hex << type
151 << dec << ")");
152 switch (type) {
153 case HCI_EVENT_LE_META: le_packet_handler(packet, size); break;
154 case HCI_EVENT_GATTSERVICE_META:
155 gattservice_handler(packet, size);
156 break;
157 case HCI_EVENT_DISCONNECTION_COMPLETE:
158 disconnect_handler(packet, size);
159 break;
160 case SM_EVENT_JUST_WORKS_REQUEST:
161 sm_just_works_confirm(
162 sm_event_just_works_request_get_handle(packet));
163 break;
164 case ATT_EVENT_MTU_EXCHANGE_COMPLETE:
165 mtu_exchange_complete_handler(packet, size);
166 break;
167 // TODO: what's the difference with the previous one?
168 case GATT_EVENT_MTU: gatt_event_mtu_handler(packet, size); break;
169 case BTSTACK_EVENT_STATE:
170 btstack_event_state_handler(packet, size);
171 break;
172 default: break;
173 }
174}
175
176// ATT Client Read Callback for Dynamic Data
177// - if buffer == NULL, don't copy data, just return size of value
178// - if buffer != NULL, copy data and return number bytes copied
179uint16_t att_read_callback([[maybe_unused]] hci_con_handle_t connection_handle,
180 uint16_t att_handle,
181 [[maybe_unused]] uint16_t offset,
182 [[maybe_unused]] uint8_t *buffer,
183 [[maybe_unused]] uint16_t buffer_size) {
184 if (att_handle == midi_char_value_handle)
185 return 0; // MIDI always responds with no data
186 return 0;
187}
188
189int midi_cccd_write(hci_con_handle_t conn_handle, uint8_t *buffer,
190 [[maybe_unused]] uint16_t buffer_size) {
191 assert(instance);
192 bool notify = (little_endian_read_16(buffer, 0) &
193 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) != 0;
194 instance->handleSubscribe(BLEConnectionHandle {conn_handle},
195 BLECharacteristicHandle {midi_char_value_handle},
196 notify);
197 return 0;
198}
199
200int midi_value_write(hci_con_handle_t conn_handle, uint8_t *buffer,
201 uint16_t buffer_size) {
202 assert(instance);
203 BLEDataView data {buffer, buffer_size};
204 auto data_gen = [data {data}]() mutable { return std::exchange(data, {}); };
205 instance->handleData(
206 BLEConnectionHandle {conn_handle},
207 BLEDataGenerator {compat::in_place, std::move(data_gen)},
209 return 0;
210}
211
212int att_write_callback(hci_con_handle_t conn_handle, uint16_t att_handle,
213 [[maybe_unused]] uint16_t transaction_mode,
214 uint16_t offset, uint8_t *buffer, uint16_t buffer_size) {
215 DEBUGREF("ATT write: handle=" << att_handle << ", offset=" << offset
216 << ", size=" << buffer_size);
217 // Only support regular writes (no prepared/long writes)
218 if (transaction_mode != ATT_TRANSACTION_MODE_NONE)
219 return ATT_ERROR_REQUEST_NOT_SUPPORTED;
220 // Only support writes without offset
221 if (offset != 0)
222 return ATT_ERROR_INVALID_OFFSET;
223 // Client configuration update
224 if (att_handle == midi_cccd_handle)
225 return midi_cccd_write(conn_handle, buffer, buffer_size);
226 // MIDI data received
227 else if (att_handle == midi_char_value_handle)
228 return midi_value_write(conn_handle, buffer, buffer_size);
229 return 0;
230}
231
232void le_midi_setup(const BLESettings &ble_settings) {
233 l2cap_init();
234 // setup SM: no input, no output
235 sm_init();
236 sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
237 sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION |
238 SM_AUTHREQ_BONDING);
239 // setup ATT server
240 att_server_init(profile_data, att_read_callback, att_write_callback);
241 // setup advertisements
242 le_midi_setup_adv(ble_settings);
243 // register for HCI events
244 hci_event_callback_registration.callback = &packet_handler;
245 hci_add_event_handler(&hci_event_callback_registration);
246 // register for ATT events
247 att_server_register_packet_handler(packet_handler);
248 // register for SM events
249 sm_event_callback_registration.callback = &packet_handler;
250 sm_add_event_handler(&sm_event_callback_registration);
251}
252
253template <class F>
254btstack_context_callback_registration_t create_context_callback(F &f) {
255 btstack_context_callback_registration_t ret {};
256 ret.callback = +[](void *context) { (*static_cast<F *>(context))(); };
257 ret.context = &f;
258 return ret;
259}
260
261} // namespace
262
263bool init(MIDIBLEInstance &instance, BLESettings settings) {
264 cs::midi_ble_btstack::instance = &instance;
265 cs::midi_ble_btstack::settings = settings;
266 le_midi_setup(settings);
267 hci_power_control(HCI_POWER_ON);
268 // btstack_run_loop_execute(); // not necessary in background mode
269 return true;
270}
271
272void notify(BLEConnectionHandle conn_handle,
273 BLECharacteristicHandle char_handle, BLEDataView data) {
274 // DEBUGREF("[" << data.length << "] "
275 // << (AH::HexDump {data.data, data.length}));
276 [[maybe_unused]] const auto t0 = micros();
277 // Don't bother sending empty packets
278 if (!data)
279 return;
280 // Flag to know when the can-send-now callback is done
281 volatile std::atomic_bool notify_done {false};
282 // The following is executed in the BTstack can-send-now callback, so it is
283 // synchronized with the BTstack code
284 auto send = [&] {
285 DEBUGREF("notify " << micros() - t0);
286 att_server_notify(conn_handle.conn, char_handle.characteristic,
287 data.data, data.length);
288 DEBUGREF("notify done " << micros() - t0);
289 notify_done.store(true, std::memory_order_release);
290 };
291 auto send_ctx = create_context_callback(send);
292 // The following is executed in the async_context, so it is synchronized
293 // with the BTstack code
294 auto run = [&] {
295 // Request can-send-now callback to be fired later
296 DEBUGREF("req send " << micros() - t0);
297 auto ret = att_server_request_to_send_notification(&send_ctx,
298 conn_handle.conn);
299 assert(ret == ERROR_CODE_SUCCESS);
300 };
301 auto run_ctx = create_context_callback(run);
302 // Schedule the run callback on the BTstack thread, which will then schedule
303 // the send callback as soon as sending data is possible.
304 DEBUGREF("req main thread " << micros() - t0);
305 btstack_run_loop_execute_on_main_thread(&run_ctx);
306 // Wait for the can-send-now callback to clear the flag
307 while (!notify_done.load(std::memory_order_acquire)) tight_loop_contents();
308 DEBUGREF("all done " << micros() - t0);
309}
310
311} // namespace cs::midi_ble_btstack
312
313#endif
Type definitions and callback interfaces for communication between the low-level BLE stacks and highe...
@ ConsumeImmediately
Buffer is valid only during the callback. Do not keep any pointers to it.
Callable that returns the next chunk of data from a BLE packet when called.
Definition BLEAPI.hpp:66
Defines the interface for callback functions registered by the low-level BLE code.
Definition BLEAPI.hpp:127
virtual void handleData(BLEConnectionHandle conn_handle, BLEDataGenerator &&data, BLEDataLifetime lifetime)=0
Called by the BLE stack when the central writes data to the MIDI GATT characteristic.
virtual void handleConnect(BLEConnectionHandle conn_handle)=0
Called by the BLE stack when a connection is established.
virtual void handleSubscribe(BLEConnectionHandle conn_handle, BLECharacteristicHandle char_handle, bool notify)=0
Called by the BLE stack when the central subscribes to receive notifications for the MIDI GATT charac...
virtual void handleDisconnect(BLEConnectionHandle conn_handle)=0
Called by the BLE stack when a connection is terminated.
virtual void handleMTU(BLEConnectionHandle conn_handle, uint16_t mtu)=0
Called by the BLE stack when the maximum transmission unit for the connection changes.
const uint8_t profile_data[]
Definition gatt_midi.h:19
#define ATT_CHARACTERISTIC_7772E5DB_3868_4112_A1A9_F2669D106BF3_01_CLIENT_CONFIGURATION_HANDLE
Definition gatt_midi.h:65
#define ATT_CHARACTERISTIC_7772E5DB_3868_4112_A1A9_F2669D106BF3_01_VALUE_HANDLE
Definition gatt_midi.h:64
#define DEBUGREF(x)
Print an expression and its location (file and line number) to the debug output if debugging is enabl...
Definition Debug.hpp:105
constexpr Note F
F (Fa)
Definition Notes.hpp:61
static in_place_t in_place
Definition compat.hpp:27
bool init(MIDIBLEInstance &instance, BLESettings settings)
constexpr const char * gattservice_event_names[114]
void notify(BLEConnectionHandle conn_handle, BLECharacteristicHandle char_handle, BLEDataView data)
void le_midi_setup_adv(const BLESettings &ble_settings)
constexpr const char * hci_event_names[256]
constexpr const char * le_event_names[42]
Represents a handle to a local GATT characteristic.
Definition BLEAPI.hpp:30
Represents a handle to the connection to another device.
Definition BLEAPI.hpp:19
Non-owning, std::span-style read-only view of BLE data.
Definition BLEAPI.hpp:42
uint16_t length
Definition BLEAPI.hpp:44
const uint8_t * data
Definition BLEAPI.hpp:43
Configuration options for the low-level BLE code.
Definition BLEAPI.hpp:150
struct BLESettings::@4 connection_interval
Connection intervals as multiples of 1.25 milliseconds (e.g.0x000C = 15 ms).
uint16_t maximum
Definition BLEAPI.hpp:157
uint16_t minimum
Definition BLEAPI.hpp:156
bool initiate_security
Set to true if you want the Arduino to always initiate the Bluetooth bonding or secure connection.
Definition BLEAPI.hpp:163