aboutsummaryrefslogtreecommitdiff
path: root/tmk_core
diff options
context:
space:
mode:
authorRyan <fauxpark@gmail.com>2021-09-21 19:58:46 +1000
committerGitHub <noreply@github.com>2021-09-21 19:58:46 +1000
commit20ea5f3fb5ba108153148e57981a7e804fd4be61 (patch)
treec6ff504e55d223df435ca439673e4771d480f079 /tmk_core
parentc38a7308054f2072d234ee0d44d3bea9f809a63d (diff)
downloadqmk_firmware-20ea5f3fb5ba108153148e57981a7e804fd4be61.tar.gz
qmk_firmware-20ea5f3fb5ba108153148e57981a7e804fd4be61.zip
Relocate Adafruit BLE code (#14530)
Diffstat (limited to 'tmk_core')
-rw-r--r--tmk_core/protocol/lufa/adafruit_ble.cpp701
-rw-r--r--tmk_core/protocol/lufa/adafruit_ble.h59
-rw-r--r--tmk_core/protocol/lufa/outputselect.c79
-rw-r--r--tmk_core/protocol/lufa/outputselect.h34
-rw-r--r--tmk_core/protocol/lufa/ringbuffer.hpp66
5 files changed, 0 insertions, 939 deletions
diff --git a/tmk_core/protocol/lufa/adafruit_ble.cpp b/tmk_core/protocol/lufa/adafruit_ble.cpp
deleted file mode 100644
index 3f2cc3573..000000000
--- a/tmk_core/protocol/lufa/adafruit_ble.cpp
+++ /dev/null
@@ -1,701 +0,0 @@
1#include "adafruit_ble.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <alloca.h>
6#include "debug.h"
7#include "timer.h"
8#include "action_util.h"
9#include "ringbuffer.hpp"
10#include <string.h>
11#include "spi_master.h"
12#include "wait.h"
13#include "analog.h"
14#include "progmem.h"
15
16// These are the pin assignments for the 32u4 boards.
17// You may define them to something else in your config.h
18// if yours is wired up differently.
19#ifndef AdafruitBleResetPin
20# define AdafruitBleResetPin D4
21#endif
22
23#ifndef AdafruitBleCSPin
24# define AdafruitBleCSPin B4
25#endif
26
27#ifndef AdafruitBleIRQPin
28# define AdafruitBleIRQPin E6
29#endif
30
31#ifndef AdafruitBleSpiClockSpeed
32# define AdafruitBleSpiClockSpeed 4000000UL // SCK frequency
33#endif
34
35#define SCK_DIVISOR (F_CPU / AdafruitBleSpiClockSpeed)
36
37#define SAMPLE_BATTERY
38#define ConnectionUpdateInterval 1000 /* milliseconds */
39
40#ifndef BATTERY_LEVEL_PIN
41# define BATTERY_LEVEL_PIN B5
42#endif
43
44static struct {
45 bool is_connected;
46 bool initialized;
47 bool configured;
48
49#define ProbedEvents 1
50#define UsingEvents 2
51 bool event_flags;
52
53#ifdef SAMPLE_BATTERY
54 uint16_t last_battery_update;
55 uint32_t vbat;
56#endif
57 uint16_t last_connection_update;
58} state;
59
60// Commands are encoded using SDEP and sent via SPI
61// https://github.com/adafruit/Adafruit_BluefruitLE_nRF51/blob/master/SDEP.md
62
63#define SdepMaxPayload 16
64struct sdep_msg {
65 uint8_t type;
66 uint8_t cmd_low;
67 uint8_t cmd_high;
68 struct __attribute__((packed)) {
69 uint8_t len : 7;
70 uint8_t more : 1;
71 };
72 uint8_t payload[SdepMaxPayload];
73} __attribute__((packed));
74
75// The recv latency is relatively high, so when we're hammering keys quickly,
76// we want to avoid waiting for the responses in the matrix loop. We maintain
77// a short queue for that. Since there is quite a lot of space overhead for
78// the AT command representation wrapped up in SDEP, we queue the minimal
79// information here.
80
81enum queue_type {
82 QTKeyReport, // 1-byte modifier + 6-byte key report
83 QTConsumer, // 16-bit key code
84#ifdef MOUSE_ENABLE
85 QTMouseMove, // 4-byte mouse report
86#endif
87};
88
89struct queue_item {
90 enum queue_type queue_type;
91 uint16_t added;
92 union __attribute__((packed)) {
93 struct __attribute__((packed)) {
94 uint8_t modifier;
95 uint8_t keys[6];
96 } key;
97
98 uint16_t consumer;
99 struct __attribute__((packed)) {
100 int8_t x, y, scroll, pan;
101 uint8_t buttons;
102 } mousemove;
103 };
104};
105
106// Items that we wish to send
107static RingBuffer<queue_item, 40> send_buf;
108// Pending response; while pending, we can't send any more requests.
109// This records the time at which we sent the command for which we
110// are expecting a response.
111static RingBuffer<uint16_t, 2> resp_buf;
112
113static bool process_queue_item(struct queue_item *item, uint16_t timeout);
114
115enum sdep_type {
116 SdepCommand = 0x10,
117 SdepResponse = 0x20,
118 SdepAlert = 0x40,
119 SdepError = 0x80,
120 SdepSlaveNotReady = 0xFE, // Try again later
121 SdepSlaveOverflow = 0xFF, // You read more data than is available
122};
123
124enum ble_cmd {
125 BleInitialize = 0xBEEF,
126 BleAtWrapper = 0x0A00,
127 BleUartTx = 0x0A01,
128 BleUartRx = 0x0A02,
129};
130
131enum ble_system_event_bits {
132 BleSystemConnected = 0,
133 BleSystemDisconnected = 1,
134 BleSystemUartRx = 8,
135 BleSystemMidiRx = 10,
136};
137
138#define SdepTimeout 150 /* milliseconds */
139#define SdepShortTimeout 10 /* milliseconds */
140#define SdepBackOff 25 /* microseconds */
141#define BatteryUpdateInterval 10000 /* milliseconds */
142
143static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout = SdepTimeout);
144static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose = false);
145
146// Send a single SDEP packet
147static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) {
148 spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
149 uint16_t timerStart = timer_read();
150 bool success = false;
151 bool ready = false;
152
153 do {
154 ready = spi_write(msg->type) != SdepSlaveNotReady;
155 if (ready) {
156 break;
157 }
158
159 // Release it and let it initialize
160 spi_stop();
161 wait_us(SdepBackOff);
162 spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
163 } while (timer_elapsed(timerStart) < timeout);
164
165 if (ready) {
166 // Slave is ready; send the rest of the packet
167 spi_transmit(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len);
168 success = true;
169 }
170
171 spi_stop();
172
173 return success;
174}
175
176static inline void sdep_build_pkt(struct sdep_msg *msg, uint16_t command, const uint8_t *payload, uint8_t len, bool moredata) {
177 msg->type = SdepCommand;
178 msg->cmd_low = command & 0xFF;
179 msg->cmd_high = command >> 8;
180 msg->len = len;
181 msg->more = (moredata && len == SdepMaxPayload) ? 1 : 0;
182
183 static_assert(sizeof(*msg) == 20, "msg is correctly packed");
184
185 memcpy(msg->payload, payload, len);
186}
187
188// Read a single SDEP packet
189static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) {
190 bool success = false;
191 uint16_t timerStart = timer_read();
192 bool ready = false;
193
194 do {
195 ready = readPin(AdafruitBleIRQPin);
196 if (ready) {
197 break;
198 }
199 wait_us(1);
200 } while (timer_elapsed(timerStart) < timeout);
201
202 if (ready) {
203 spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
204
205 do {
206 // Read the command type, waiting for the data to be ready
207 msg->type = spi_read();
208 if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) {
209 // Release it and let it initialize
210 spi_stop();
211 wait_us(SdepBackOff);
212 spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
213 continue;
214 }
215
216 // Read the rest of the header
217 spi_receive(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)));
218
219 // and get the payload if there is any
220 if (msg->len <= SdepMaxPayload) {
221 spi_receive(msg->payload, msg->len);
222 }
223 success = true;
224 break;
225 } while (timer_elapsed(timerStart) < timeout);
226
227 spi_stop();
228 }
229 return success;
230}
231
232static void resp_buf_read_one(bool greedy) {
233 uint16_t last_send;
234 if (!resp_buf.peek(last_send)) {
235 return;
236 }
237
238 if (readPin(AdafruitBleIRQPin)) {
239 struct sdep_msg msg;
240
241 again:
242 if (sdep_recv_pkt(&msg, SdepTimeout)) {
243 if (!msg.more) {
244 // We got it; consume this entry
245 resp_buf.get(last_send);
246 dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send));
247 }
248
249 if (greedy && resp_buf.peek(last_send) && readPin(AdafruitBleIRQPin)) {
250 goto again;
251 }
252 }
253
254 } else if (timer_elapsed(last_send) > SdepTimeout * 2) {
255 dprintf("waiting_for_result: timeout, resp_buf size %d\n", (int)resp_buf.size());
256
257 // Timed out: consume this entry
258 resp_buf.get(last_send);
259 }
260}
261
262static void send_buf_send_one(uint16_t timeout = SdepTimeout) {
263 struct queue_item item;
264
265 // Don't send anything more until we get an ACK
266 if (!resp_buf.empty()) {
267 return;
268 }
269
270 if (!send_buf.peek(item)) {
271 return;
272 }
273 if (process_queue_item(&item, timeout)) {
274 // commit that peek
275 send_buf.get(item);
276 dprintf("send_buf_send_one: have %d remaining\n", (int)send_buf.size());
277 } else {
278 dprint("failed to send, will retry\n");
279 wait_ms(SdepTimeout);
280 resp_buf_read_one(true);
281 }
282}
283
284static void resp_buf_wait(const char *cmd) {
285 bool didPrint = false;
286 while (!resp_buf.empty()) {
287 if (!didPrint) {
288 dprintf("wait on buf for %s\n", cmd);
289 didPrint = true;
290 }
291 resp_buf_read_one(true);
292 }
293}
294
295static bool ble_init(void) {
296 state.initialized = false;
297 state.configured = false;
298 state.is_connected = false;
299
300 setPinInput(AdafruitBleIRQPin);
301
302 spi_init();
303
304 // Perform a hardware reset
305 setPinOutput(AdafruitBleResetPin);
306 writePinHigh(AdafruitBleResetPin);
307 writePinLow(AdafruitBleResetPin);
308 wait_ms(10);
309 writePinHigh(AdafruitBleResetPin);
310
311 wait_ms(1000); // Give it a second to initialize
312
313 state.initialized = true;
314 return state.initialized;
315}
316
317static inline uint8_t min(uint8_t a, uint8_t b) { return a < b ? a : b; }
318
319static bool read_response(char *resp, uint16_t resplen, bool verbose) {
320 char *dest = resp;
321 char *end = dest + resplen;
322
323 while (true) {
324 struct sdep_msg msg;
325
326 if (!sdep_recv_pkt(&msg, 2 * SdepTimeout)) {
327 dprint("sdep_recv_pkt failed\n");
328 return false;
329 }
330
331 if (msg.type != SdepResponse) {
332 *resp = 0;
333 return false;
334 }
335
336 uint8_t len = min(msg.len, end - dest);
337 if (len > 0) {
338 memcpy(dest, msg.payload, len);
339 dest += len;
340 }
341
342 if (!msg.more) {
343 // No more data is expected!
344 break;
345 }
346 }
347
348 // Ensure the response is NUL terminated
349 *dest = 0;
350
351 // "Parse" the result text; we want to snip off the trailing OK or ERROR line
352 // Rewind past the possible trailing CRLF so that we can strip it
353 --dest;
354 while (dest > resp && (dest[0] == '\n' || dest[0] == '\r')) {
355 *dest = 0;
356 --dest;
357 }
358
359 // Look back for start of preceeding line
360 char *last_line = strrchr(resp, '\n');
361 if (last_line) {
362 ++last_line;
363 } else {
364 last_line = resp;
365 }
366
367 bool success = false;
368 static const char kOK[] PROGMEM = "OK";
369
370 success = !strcmp_P(last_line, kOK);
371
372 if (verbose || !success) {
373 dprintf("result: %s\n", resp);
374 }
375 return success;
376}
377
378static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout) {
379 const char * end = cmd + strlen(cmd);
380 struct sdep_msg msg;
381
382 if (verbose) {
383 dprintf("ble send: %s\n", cmd);
384 }
385
386 if (resp) {
387 // They want to decode the response, so we need to flush and wait
388 // for all pending I/O to finish before we start this one, so
389 // that we don't confuse the results
390 resp_buf_wait(cmd);
391 *resp = 0;
392 }
393
394 // Fragment the command into a series of SDEP packets
395 while (end - cmd > SdepMaxPayload) {
396 sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, SdepMaxPayload, true);
397 if (!sdep_send_pkt(&msg, timeout)) {
398 return false;
399 }
400 cmd += SdepMaxPayload;
401 }
402
403 sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, end - cmd, false);
404 if (!sdep_send_pkt(&msg, timeout)) {
405 return false;
406 }
407
408 if (resp == NULL) {
409 uint16_t now = timer_read();
410 while (!resp_buf.enqueue(now)) {
411 resp_buf_read_one(false);
412 }
413 uint16_t later = timer_read();
414 if (TIMER_DIFF_16(later, now) > 0) {
415 dprintf("waited %dms for resp_buf\n", TIMER_DIFF_16(later, now));
416 }
417 return true;
418 }
419
420 return read_response(resp, resplen, verbose);
421}
422
423bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose) {
424 char *cmdbuf = (char *)alloca(strlen_P(cmd) + 1);
425 strcpy_P(cmdbuf, cmd);
426 return at_command(cmdbuf, resp, resplen, verbose);
427}
428
429bool adafruit_ble_is_connected(void) { return state.is_connected; }
430
431bool adafruit_ble_enable_keyboard(void) {
432 char resbuf[128];
433
434 if (!state.initialized && !ble_init()) {
435 return false;
436 }
437
438 state.configured = false;
439
440 // Disable command echo
441 static const char kEcho[] PROGMEM = "ATE=0";
442 // Make the advertised name match the keyboard
443 static const char kGapDevName[] PROGMEM = "AT+GAPDEVNAME=" STR(PRODUCT);
444 // Turn on keyboard support
445 static const char kHidEnOn[] PROGMEM = "AT+BLEHIDEN=1";
446
447 // Adjust intervals to improve latency. This causes the "central"
448 // system (computer/tablet) to poll us every 10-30 ms. We can't
449 // set a smaller value than 10ms, and 30ms seems to be the natural
450 // processing time on my macbook. Keeping it constrained to that
451 // feels reasonable to type to.
452 static const char kGapIntervals[] PROGMEM = "AT+GAPINTERVALS=10,30,,";
453
454 // Reset the device so that it picks up the above changes
455 static const char kATZ[] PROGMEM = "ATZ";
456
457 // Turn down the power level a bit
458 static const char kPower[] PROGMEM = "AT+BLEPOWERLEVEL=-12";
459 static PGM_P const configure_commands[] PROGMEM = {
460 kEcho, kGapIntervals, kGapDevName, kHidEnOn, kPower, kATZ,
461 };
462
463 uint8_t i;
464 for (i = 0; i < sizeof(configure_commands) / sizeof(configure_commands[0]); ++i) {
465 PGM_P cmd;
466 memcpy_P(&cmd, configure_commands + i, sizeof(cmd));
467
468 if (!at_command_P(cmd, resbuf, sizeof(resbuf))) {
469 dprintf("failed BLE command: %S: %s\n", cmd, resbuf);
470 goto fail;
471 }
472 }
473
474 state.configured = true;
475
476 // Check connection status in a little while; allow the ATZ time
477 // to kick in.
478 state.last_connection_update = timer_read();
479fail:
480 return state.configured;
481}
482
483static void set_connected(bool connected) {
484 if (connected != state.is_connected) {
485 if (connected) {
486 dprint("BLE connected\n");
487 } else {
488 dprint("BLE disconnected\n");
489 }
490 state.is_connected = connected;
491
492 // TODO: if modifiers are down on the USB interface and
493 // we cut over to BLE or vice versa, they will remain stuck.
494 // This feels like a good point to do something like clearing
495 // the keyboard and/or generating a fake all keys up message.
496 // However, I've noticed that it takes a couple of seconds
497 // for macOS to to start recognizing key presses after BLE
498 // is in the connected state, so I worry that doing that
499 // here may not be good enough.
500 }
501}
502
503void adafruit_ble_task(void) {
504 char resbuf[48];
505
506 if (!state.configured && !adafruit_ble_enable_keyboard()) {
507 return;
508 }
509 resp_buf_read_one(true);
510 send_buf_send_one(SdepShortTimeout);
511
512 if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(AdafruitBleIRQPin)) {
513 // Must be an event update
514 if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) {
515 uint32_t mask = strtoul(resbuf, NULL, 16);
516
517 if (mask & BleSystemConnected) {
518 set_connected(true);
519 } else if (mask & BleSystemDisconnected) {
520 set_connected(false);
521 }
522 }
523 }
524
525 if (timer_elapsed(state.last_connection_update) > ConnectionUpdateInterval) {
526 bool shouldPoll = true;
527 if (!(state.event_flags & ProbedEvents)) {
528 // Request notifications about connection status changes.
529 // This only works in SPIFRIEND firmware > 0.6.7, which is why
530 // we check for this conditionally here.
531 // Note that at the time of writing, HID reports only work correctly
532 // with Apple products on firmware version 0.6.7!
533 // https://forums.adafruit.com/viewtopic.php?f=8&t=104052
534 if (at_command_P(PSTR("AT+EVENTENABLE=0x1"), resbuf, sizeof(resbuf))) {
535 at_command_P(PSTR("AT+EVENTENABLE=0x2"), resbuf, sizeof(resbuf));
536 state.event_flags |= UsingEvents;
537 }
538 state.event_flags |= ProbedEvents;
539
540 // leave shouldPoll == true so that we check at least once
541 // before relying solely on events
542 } else {
543 shouldPoll = false;
544 }
545
546 static const char kGetConn[] PROGMEM = "AT+GAPGETCONN";
547 state.last_connection_update = timer_read();
548
549 if (at_command_P(kGetConn, resbuf, sizeof(resbuf))) {
550 set_connected(atoi(resbuf));
551 }
552 }
553
554#ifdef SAMPLE_BATTERY
555 if (timer_elapsed(state.last_battery_update) > BatteryUpdateInterval && resp_buf.empty()) {
556 state.last_battery_update = timer_read();
557
558 state.vbat = analogReadPin(BATTERY_LEVEL_PIN);
559 }
560#endif
561}
562
563static bool process_queue_item(struct queue_item *item, uint16_t timeout) {
564 char cmdbuf[48];
565 char fmtbuf[64];
566
567 // Arrange to re-check connection after keys have settled
568 state.last_connection_update = timer_read();
569
570#if 1
571 if (TIMER_DIFF_16(state.last_connection_update, item->added) > 0) {
572 dprintf("send latency %dms\n", TIMER_DIFF_16(state.last_connection_update, item->added));
573 }
574#endif
575
576 switch (item->queue_type) {
577 case QTKeyReport:
578 strcpy_P(fmtbuf, PSTR("AT+BLEKEYBOARDCODE=%02x-00-%02x-%02x-%02x-%02x-%02x-%02x"));
579 snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->key.modifier, item->key.keys[0], item->key.keys[1], item->key.keys[2], item->key.keys[3], item->key.keys[4], item->key.keys[5]);
580 return at_command(cmdbuf, NULL, 0, true, timeout);
581
582 case QTConsumer:
583 strcpy_P(fmtbuf, PSTR("AT+BLEHIDCONTROLKEY=0x%04x"));
584 snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->consumer);
585 return at_command(cmdbuf, NULL, 0, true, timeout);
586
587#ifdef MOUSE_ENABLE
588 case QTMouseMove:
589 strcpy_P(fmtbuf, PSTR("AT+BLEHIDMOUSEMOVE=%d,%d,%d,%d"));
590 snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->mousemove.x, item->mousemove.y, item->mousemove.scroll, item->mousemove.pan);
591 if (!at_command(cmdbuf, NULL, 0, true, timeout)) {
592 return false;
593 }
594 strcpy_P(cmdbuf, PSTR("AT+BLEHIDMOUSEBUTTON="));
595 if (item->mousemove.buttons & MOUSE_BTN1) {
596 strcat(cmdbuf, "L");
597 }
598 if (item->mousemove.buttons & MOUSE_BTN2) {
599 strcat(cmdbuf, "R");
600 }
601 if (item->mousemove.buttons & MOUSE_BTN3) {
602 strcat(cmdbuf, "M");
603 }
604 if (item->mousemove.buttons == 0) {
605 strcat(cmdbuf, "0");
606 }
607 return at_command(cmdbuf, NULL, 0, true, timeout);
608#endif
609 default:
610 return true;
611 }
612}
613
614void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys) {
615 struct queue_item item;
616 bool didWait = false;
617
618 item.queue_type = QTKeyReport;
619 item.key.modifier = hid_modifier_mask;
620 item.added = timer_read();
621
622 while (nkeys >= 0) {
623 item.key.keys[0] = keys[0];
624 item.key.keys[1] = nkeys >= 1 ? keys[1] : 0;
625 item.key.keys[2] = nkeys >= 2 ? keys[2] : 0;
626 item.key.keys[3] = nkeys >= 3 ? keys[3] : 0;
627 item.key.keys[4] = nkeys >= 4 ? keys[4] : 0;
628 item.key.keys[5] = nkeys >= 5 ? keys[5] : 0;
629
630 if (!send_buf.enqueue(item)) {
631 if (!didWait) {
632 dprint("wait for buf space\n");
633 didWait = true;
634 }
635 send_buf_send_one();
636 continue;
637 }
638
639 if (nkeys <= 6) {
640 return;
641 }
642
643 nkeys -= 6;
644 keys += 6;
645 }
646}
647
648void adafruit_ble_send_consumer_key(uint16_t usage) {
649 struct queue_item item;
650
651 item.queue_type = QTConsumer;
652 item.consumer = usage;
653
654 while (!send_buf.enqueue(item)) {
655 send_buf_send_one();
656 }
657}
658
659#ifdef MOUSE_ENABLE
660void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons) {
661 struct queue_item item;
662
663 item.queue_type = QTMouseMove;
664 item.mousemove.x = x;
665 item.mousemove.y = y;
666 item.mousemove.scroll = scroll;
667 item.mousemove.pan = pan;
668 item.mousemove.buttons = buttons;
669
670 while (!send_buf.enqueue(item)) {
671 send_buf_send_one();
672 }
673}
674#endif
675
676uint32_t adafruit_ble_read_battery_voltage(void) { return state.vbat; }
677
678bool adafruit_ble_set_mode_leds(bool on) {
679 if (!state.configured) {
680 return false;
681 }
682
683 // The "mode" led is the red blinky one
684 at_command_P(on ? PSTR("AT+HWMODELED=1") : PSTR("AT+HWMODELED=0"), NULL, 0);
685
686 // Pin 19 is the blue "connected" LED; turn that off too.
687 // When turning LEDs back on, don't turn that LED on if we're
688 // not connected, as that would be confusing.
689 at_command_P(on && state.is_connected ? PSTR("AT+HWGPIO=19,1") : PSTR("AT+HWGPIO=19,0"), NULL, 0);
690 return true;
691}
692
693// https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/ble-generic#at-plus-blepowerlevel
694bool adafruit_ble_set_power_level(int8_t level) {
695 char cmd[46];
696 if (!state.configured) {
697 return false;
698 }
699 snprintf(cmd, sizeof(cmd), "AT+BLEPOWERLEVEL=%d", level);
700 return at_command(cmd, NULL, 0, false);
701}
diff --git a/tmk_core/protocol/lufa/adafruit_ble.h b/tmk_core/protocol/lufa/adafruit_ble.h
deleted file mode 100644
index b43e0771d..000000000
--- a/tmk_core/protocol/lufa/adafruit_ble.h
+++ /dev/null
@@ -1,59 +0,0 @@
1/* Bluetooth Low Energy Protocol for QMK.
2 * Author: Wez Furlong, 2016
3 * Supports the Adafruit BLE board built around the nRF51822 chip.
4 */
5
6#pragma once
7
8#include <stdbool.h>
9#include <stdint.h>
10#include <string.h>
11
12#include "config_common.h"
13
14#ifdef __cplusplus
15extern "C" {
16#endif
17
18/* Instruct the module to enable HID keyboard support and reset */
19extern bool adafruit_ble_enable_keyboard(void);
20
21/* Query to see if the BLE module is connected */
22extern bool adafruit_ble_query_is_connected(void);
23
24/* Returns true if we believe that the BLE module is connected.
25 * This uses our cached understanding that is maintained by
26 * calling ble_task() periodically. */
27extern bool adafruit_ble_is_connected(void);
28
29/* Call this periodically to process BLE-originated things */
30extern void adafruit_ble_task(void);
31
32/* Generates keypress events for a set of keys.
33 * The hid modifier mask specifies the state of the modifier keys for
34 * this set of keys.
35 * Also sends a key release indicator, so that the keys do not remain
36 * held down. */
37extern void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys);
38
39/* Send a consumer usage.
40 * (milliseconds) */
41extern void adafruit_ble_send_consumer_key(uint16_t usage);
42
43#ifdef MOUSE_ENABLE
44/* Send a mouse/wheel movement report.
45 * The parameters are signed and indicate positive or negative direction
46 * change. */
47extern void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons);
48#endif
49
50/* Compute battery voltage by reading an analog pin.
51 * Returns the integer number of millivolts */
52extern uint32_t adafruit_ble_read_battery_voltage(void);
53
54extern bool adafruit_ble_set_mode_leds(bool on);
55extern bool adafruit_ble_set_power_level(int8_t level);
56
57#ifdef __cplusplus
58}
59#endif
diff --git a/tmk_core/protocol/lufa/outputselect.c b/tmk_core/protocol/lufa/outputselect.c
deleted file mode 100644
index f758c6528..000000000
--- a/tmk_core/protocol/lufa/outputselect.c
+++ /dev/null
@@ -1,79 +0,0 @@
1/*
2Copyright 2017 Priyadi Iman Nurcahyo
3This program is free software: you can redistribute it and/or modify
4it under the terms of the GNU General Public License as published by
5the Free Software Foundation, either version 2 of the License, or
6(at your option) any later version.
7This program is distributed in the hope that it will be useful,
8but WITHOUT ANY WARRANTY; without even the implied warranty of
9MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10GNU General Public License for more details.
11You should have received a copy of the GNU General Public License
12along with this program. If not, see <http://www.gnu.org/licenses/>.
13*/
14
15#include "outputselect.h"
16
17#if defined(PROTOCOL_LUFA)
18# include "lufa.h"
19#endif
20
21#ifdef MODULE_ADAFRUIT_BLE
22# include "adafruit_ble.h"
23#endif
24
25uint8_t desired_output = OUTPUT_DEFAULT;
26
27/** \brief Set Output
28 *
29 * FIXME: Needs doc
30 */
31void set_output(uint8_t output) {
32 set_output_user(output);
33 desired_output = output;
34}
35
36/** \brief Set Output User
37 *
38 * FIXME: Needs doc
39 */
40__attribute__((weak)) void set_output_user(uint8_t output) {}
41
42static bool is_usb_configured(void) {
43#if defined(PROTOCOL_LUFA)
44 return USB_DeviceState == DEVICE_STATE_Configured;
45#endif
46}
47
48/** \brief Auto Detect Output
49 *
50 * FIXME: Needs doc
51 */
52uint8_t auto_detect_output(void) {
53 if (is_usb_configured()) {
54 return OUTPUT_USB;
55 }
56
57#ifdef MODULE_ADAFRUIT_BLE
58 if (adafruit_ble_is_connected()) {
59 return OUTPUT_BLUETOOTH;
60 }
61#endif
62
63#ifdef BLUETOOTH_ENABLE
64 return OUTPUT_BLUETOOTH; // should check if BT is connected here
65#endif
66
67 return OUTPUT_NONE;
68}
69
70/** \brief Where To Send
71 *
72 * FIXME: Needs doc
73 */
74uint8_t where_to_send(void) {
75 if (desired_output == OUTPUT_AUTO) {
76 return auto_detect_output();
77 }
78 return desired_output;
79}
diff --git a/tmk_core/protocol/lufa/outputselect.h b/tmk_core/protocol/lufa/outputselect.h
deleted file mode 100644
index c4548e112..000000000
--- a/tmk_core/protocol/lufa/outputselect.h
+++ /dev/null
@@ -1,34 +0,0 @@
1/*
2Copyright 2017 Priyadi Iman Nurcahyo
3This program is free software: you can redistribute it and/or modify
4it under the terms of the GNU General Public License as published by
5the Free Software Foundation, either version 2 of the License, or
6(at your option) any later version.
7This program is distributed in the hope that it will be useful,
8but WITHOUT ANY WARRANTY; without even the implied warranty of
9MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10GNU General Public License for more details.
11You should have received a copy of the GNU General Public License
12along with this program. If not, see <http://www.gnu.org/licenses/>.
13*/
14
15#pragma once
16
17#include <stdint.h>
18
19enum outputs {
20 OUTPUT_AUTO,
21
22 OUTPUT_NONE,
23 OUTPUT_USB,
24 OUTPUT_BLUETOOTH
25};
26
27#ifndef OUTPUT_DEFAULT
28# define OUTPUT_DEFAULT OUTPUT_AUTO
29#endif
30
31void set_output(uint8_t output);
32void set_output_user(uint8_t output);
33uint8_t auto_detect_output(void);
34uint8_t where_to_send(void);
diff --git a/tmk_core/protocol/lufa/ringbuffer.hpp b/tmk_core/protocol/lufa/ringbuffer.hpp
deleted file mode 100644
index 70a3c4881..000000000
--- a/tmk_core/protocol/lufa/ringbuffer.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
1#pragma once
2// A simple ringbuffer holding Size elements of type T
3template <typename T, uint8_t Size>
4class RingBuffer {
5 protected:
6 T buf_[Size];
7 uint8_t head_{0}, tail_{0};
8 public:
9 inline uint8_t nextPosition(uint8_t position) {
10 return (position + 1) % Size;
11 }
12
13 inline uint8_t prevPosition(uint8_t position) {
14 if (position == 0) {
15 return Size - 1;
16 }
17 return position - 1;
18 }
19
20 inline bool enqueue(const T &item) {
21 static_assert(Size > 1, "RingBuffer size must be > 1");
22 uint8_t next = nextPosition(head_);
23 if (next == tail_) {
24 // Full
25 return false;
26 }
27
28 buf_[head_] = item;
29 head_ = next;
30 return true;
31 }
32
33 inline bool get(T &dest, bool commit = true) {
34 auto tail = tail_;
35 if (tail == head_) {
36 // No more data
37 return false;
38 }
39
40 dest = buf_[tail];
41 tail = nextPosition(tail);
42
43 if (commit) {
44 tail_ = tail;
45 }
46 return true;
47 }
48
49 inline bool empty() const { return head_ == tail_; }
50
51 inline uint8_t size() const {
52 int diff = head_ - tail_;
53 if (diff >= 0) {
54 return diff;
55 }
56 return Size + diff;
57 }
58
59 inline T& front() {
60 return buf_[tail_];
61 }
62
63 inline bool peek(T &item) {
64 return get(item, false);
65 }
66};