back up of work during May 2019

Dependencies:   microbit

Committer:
tht216
Date:
Wed Jun 05 15:21:14 2019 +0000
Branch:
class_implmentation
Revision:
6:f372773ad32f
Parent:
1:c840c2b6f490
TODO:; 1. multi keypresses; 2. integration

Who changed what in which revision?

UserRevisionLine numberNew contents of line
xx316 1:c840c2b6f490 1 /* mbed Microcontroller Library
xx316 1:c840c2b6f490 2 * Copyright (c) 2015 ARM Limited
xx316 1:c840c2b6f490 3 *
xx316 1:c840c2b6f490 4 * Licensed under the Apache License, Version 2.0 (the "License");
xx316 1:c840c2b6f490 5 * you may not use this file except in compliance with the License.
xx316 1:c840c2b6f490 6 * You may obtain a copy of the License at
xx316 1:c840c2b6f490 7 *
xx316 1:c840c2b6f490 8 * http://www.apache.org/licenses/LICENSE-2.0
xx316 1:c840c2b6f490 9 *
xx316 1:c840c2b6f490 10 * Unless required by applicable law or agreed to in writing, software
xx316 1:c840c2b6f490 11 * distributed under the License is distributed on an "AS IS" BASIS,
xx316 1:c840c2b6f490 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
xx316 1:c840c2b6f490 13 * See the License for the specific language governing permissions and
xx316 1:c840c2b6f490 14 * limitations under the License.
xx316 1:c840c2b6f490 15 */
xx316 1:c840c2b6f490 16
xx316 1:c840c2b6f490 17 #include <errno.h>
xx316 1:c840c2b6f490 18 #include "mbed.h"
xx316 1:c840c2b6f490 19 #include "CircularBuffer.h"
xx316 1:c840c2b6f490 20
xx316 1:c840c2b6f490 21 #include "HIDServiceBase.h"
xx316 1:c840c2b6f490 22 #include "Keyboard_types.h"
xx316 1:c840c2b6f490 23
xx316 1:c840c2b6f490 24 /* TODO: make this easier to configure by application (e.g. as a template parameter for
xx316 1:c840c2b6f490 25 * KeyboardService) */
xx316 1:c840c2b6f490 26 #ifndef KEYBUFFER_SIZE
xx316 1:c840c2b6f490 27 #define KEYBUFFER_SIZE 512
xx316 1:c840c2b6f490 28 #endif
xx316 1:c840c2b6f490 29
xx316 1:c840c2b6f490 30 /**
xx316 1:c840c2b6f490 31 * Report descriptor for a standard 101 keys keyboard, following the HID specification example:
xx316 1:c840c2b6f490 32 * - 8 bytes input report (1 byte for modifiers and 6 for keys)
xx316 1:c840c2b6f490 33 * - 1 byte output report (LEDs)
xx316 1:c840c2b6f490 34 */
xx316 1:c840c2b6f490 35 report_map_t KEYBOARD_REPORT_MAP = {
xx316 1:c840c2b6f490 36 USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls
xx316 1:c840c2b6f490 37 USAGE(1), 0x06, // Keyboard
xx316 1:c840c2b6f490 38 COLLECTION(1), 0x01, // Application
xx316 1:c840c2b6f490 39 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
xx316 1:c840c2b6f490 40 USAGE_MINIMUM(1), 0xE0,
xx316 1:c840c2b6f490 41 USAGE_MAXIMUM(1), 0xE7,
xx316 1:c840c2b6f490 42 LOGICAL_MINIMUM(1), 0x00,
xx316 1:c840c2b6f490 43 LOGICAL_MAXIMUM(1), 0x01,
xx316 1:c840c2b6f490 44 REPORT_SIZE(1), 0x01, // 1 byte (Modifier)
xx316 1:c840c2b6f490 45 REPORT_COUNT(1), 0x08,
xx316 1:c840c2b6f490 46 INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position
xx316 1:c840c2b6f490 47 REPORT_COUNT(1), 0x01, // 1 byte (Reserved)
xx316 1:c840c2b6f490 48 REPORT_SIZE(1), 0x08,
xx316 1:c840c2b6f490 49 INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
xx316 1:c840c2b6f490 50 REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
xx316 1:c840c2b6f490 51 REPORT_SIZE(1), 0x01,
xx316 1:c840c2b6f490 52 USAGE_PAGE(1), 0x08, // LEDs
xx316 1:c840c2b6f490 53 USAGE_MINIMUM(1), 0x01, // Num Lock
xx316 1:c840c2b6f490 54 USAGE_MAXIMUM(1), 0x05, // Kana
xx316 1:c840c2b6f490 55 OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
xx316 1:c840c2b6f490 56 REPORT_COUNT(1), 0x01, // 3 bits (Padding)
xx316 1:c840c2b6f490 57 REPORT_SIZE(1), 0x03,
xx316 1:c840c2b6f490 58 OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
xx316 1:c840c2b6f490 59 REPORT_COUNT(1), 0x06, // 6 bytes (Keys)
xx316 1:c840c2b6f490 60 REPORT_SIZE(1), 0x08,
xx316 1:c840c2b6f490 61 LOGICAL_MINIMUM(1), 0x00,
xx316 1:c840c2b6f490 62 LOGICAL_MAXIMUM(1), 0x65, // 101 keys
xx316 1:c840c2b6f490 63 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
xx316 1:c840c2b6f490 64 USAGE_MINIMUM(1), 0x00,
xx316 1:c840c2b6f490 65 USAGE_MAXIMUM(1), 0x65,
xx316 1:c840c2b6f490 66 INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
xx316 1:c840c2b6f490 67 END_COLLECTION(0),
xx316 1:c840c2b6f490 68 };
xx316 1:c840c2b6f490 69
xx316 1:c840c2b6f490 70 /// "keys pressed" report
xx316 1:c840c2b6f490 71 static uint8_t inputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
xx316 1:c840c2b6f490 72 /// "keys released" report
xx316 1:c840c2b6f490 73 static const uint8_t emptyInputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
xx316 1:c840c2b6f490 74 /// LEDs report
xx316 1:c840c2b6f490 75 static uint8_t outputReportData[] = { 0 };
xx316 1:c840c2b6f490 76
xx316 1:c840c2b6f490 77
xx316 1:c840c2b6f490 78 /**
xx316 1:c840c2b6f490 79 * @class KeyBuffer
xx316 1:c840c2b6f490 80 *
xx316 1:c840c2b6f490 81 * Buffer used to store keys to send.
xx316 1:c840c2b6f490 82 * Internally, it is a CircularBuffer, with the added capability of putting the last char back in,
xx316 1:c840c2b6f490 83 * when we're unable to send it (ie. when BLE stack is busy)
xx316 1:c840c2b6f490 84 */
xx316 1:c840c2b6f490 85 class KeyBuffer: public CircularBuffer<uint8_t, KEYBUFFER_SIZE>
xx316 1:c840c2b6f490 86 {
xx316 1:c840c2b6f490 87 public:
xx316 1:c840c2b6f490 88 KeyBuffer() :
xx316 1:c840c2b6f490 89 CircularBuffer(),
xx316 1:c840c2b6f490 90 dataIsPending (false),
xx316 1:c840c2b6f490 91 keyUpIsPending (false)
xx316 1:c840c2b6f490 92 {
xx316 1:c840c2b6f490 93 }
xx316 1:c840c2b6f490 94
xx316 1:c840c2b6f490 95 /**
xx316 1:c840c2b6f490 96 * Mark a character as pending. When a freshly popped character cannot be sent, because the
xx316 1:c840c2b6f490 97 * underlying stack is busy, we set it as pending, and it will get popped in priority by @ref
xx316 1:c840c2b6f490 98 * getPending once reports can be sent again.
xx316 1:c840c2b6f490 99 *
xx316 1:c840c2b6f490 100 * @param data The character to send in priority. The second keyUp report is implied.
xx316 1:c840c2b6f490 101 */
xx316 1:c840c2b6f490 102 void setPending(uint8_t data)
xx316 1:c840c2b6f490 103 {
xx316 1:c840c2b6f490 104 MBED_ASSERT(dataIsPending == false);
xx316 1:c840c2b6f490 105
xx316 1:c840c2b6f490 106 dataIsPending = true;
xx316 1:c840c2b6f490 107 pendingData = data;
xx316 1:c840c2b6f490 108 keyUpIsPending = true;
xx316 1:c840c2b6f490 109 }
xx316 1:c840c2b6f490 110
xx316 1:c840c2b6f490 111 /**
xx316 1:c840c2b6f490 112 * Get pending char. Either from the high priority buffer (set with setPending), or from the
xx316 1:c840c2b6f490 113 * circular buffer.
xx316 1:c840c2b6f490 114 *
xx316 1:c840c2b6f490 115 * @param data Filled with the pending data, when present
xx316 1:c840c2b6f490 116 * @return true if data was filled
xx316 1:c840c2b6f490 117 */
xx316 1:c840c2b6f490 118 bool getPending(uint8_t &data)
xx316 1:c840c2b6f490 119 {
xx316 1:c840c2b6f490 120 if (dataIsPending) {
xx316 1:c840c2b6f490 121 dataIsPending = false;
xx316 1:c840c2b6f490 122 data = pendingData;
xx316 1:c840c2b6f490 123 return true;
xx316 1:c840c2b6f490 124 }
xx316 1:c840c2b6f490 125
xx316 1:c840c2b6f490 126 return pop(data);
xx316 1:c840c2b6f490 127 }
xx316 1:c840c2b6f490 128
xx316 1:c840c2b6f490 129 bool isSomethingPending(void)
xx316 1:c840c2b6f490 130 {
xx316 1:c840c2b6f490 131 return dataIsPending || keyUpIsPending || !empty();
xx316 1:c840c2b6f490 132 }
xx316 1:c840c2b6f490 133
xx316 1:c840c2b6f490 134 /**
xx316 1:c840c2b6f490 135 * Signal that a keyUp report is pending. This means that a character has successfully been
xx316 1:c840c2b6f490 136 * sent, but the subsequent keyUp report failed. This report is of highest priority than the
xx316 1:c840c2b6f490 137 * next character.
xx316 1:c840c2b6f490 138 */
xx316 1:c840c2b6f490 139 void setKeyUpPending(void)
xx316 1:c840c2b6f490 140 {
xx316 1:c840c2b6f490 141 keyUpIsPending = true;
xx316 1:c840c2b6f490 142 }
xx316 1:c840c2b6f490 143
xx316 1:c840c2b6f490 144 /**
xx316 1:c840c2b6f490 145 * Signal that no high-priority report is pending anymore, we can go back to the normal queue.
xx316 1:c840c2b6f490 146 */
xx316 1:c840c2b6f490 147 void clearKeyUpPending(void)
xx316 1:c840c2b6f490 148 {
xx316 1:c840c2b6f490 149 keyUpIsPending = false;
xx316 1:c840c2b6f490 150 }
xx316 1:c840c2b6f490 151
xx316 1:c840c2b6f490 152 bool isKeyUpPending(void)
xx316 1:c840c2b6f490 153 {
xx316 1:c840c2b6f490 154 return keyUpIsPending;
xx316 1:c840c2b6f490 155 }
xx316 1:c840c2b6f490 156
xx316 1:c840c2b6f490 157 protected:
xx316 1:c840c2b6f490 158 bool dataIsPending;
xx316 1:c840c2b6f490 159 uint8_t pendingData;
xx316 1:c840c2b6f490 160 bool keyUpIsPending;
xx316 1:c840c2b6f490 161 };
xx316 1:c840c2b6f490 162
xx316 1:c840c2b6f490 163
xx316 1:c840c2b6f490 164 /**
xx316 1:c840c2b6f490 165 * @class KeyboardService
xx316 1:c840c2b6f490 166 * @brief HID-over-Gatt keyboard service
xx316 1:c840c2b6f490 167 *
xx316 1:c840c2b6f490 168 * Send keyboard reports over BLE. Users should rely on the high-level functions provided by the
xx316 1:c840c2b6f490 169 * Stream API. Because we can't send batches of HID reports, we store pending keys in a circular
xx316 1:c840c2b6f490 170 * buffer and rely on the report ticker to spread them over time.
xx316 1:c840c2b6f490 171 *
xx316 1:c840c2b6f490 172 * @code
xx316 1:c840c2b6f490 173 * BLE ble;
xx316 1:c840c2b6f490 174 * KeyboardService kbd(ble);
xx316 1:c840c2b6f490 175 *
xx316 1:c840c2b6f490 176 * void once_connected_and_paired_callback(void)
xx316 1:c840c2b6f490 177 * {
xx316 1:c840c2b6f490 178 * // Sequentially send keys 'Shift'+'h', 'e', 'l', 'l', 'o', '!' and <enter>
xx316 1:c840c2b6f490 179 * kbd.printf("Hello!\n");
xx316 1:c840c2b6f490 180 * }
xx316 1:c840c2b6f490 181 * @endcode
xx316 1:c840c2b6f490 182 */
xx316 1:c840c2b6f490 183 class KeyboardService : public HIDServiceBase, public Stream
xx316 1:c840c2b6f490 184 {
xx316 1:c840c2b6f490 185 public:
xx316 1:c840c2b6f490 186 KeyboardService(BLE &_ble) :
xx316 1:c840c2b6f490 187 HIDServiceBase(_ble,
xx316 1:c840c2b6f490 188 KEYBOARD_REPORT_MAP, sizeof(KEYBOARD_REPORT_MAP),
xx316 1:c840c2b6f490 189 inputReport = emptyInputReportData,
xx316 1:c840c2b6f490 190 outputReport = outputReportData,
xx316 1:c840c2b6f490 191 featureReport = NULL,
xx316 1:c840c2b6f490 192 inputReportLength = sizeof(inputReportData),
xx316 1:c840c2b6f490 193 outputReportLength = sizeof(outputReportData),
xx316 1:c840c2b6f490 194 featureReportLength = 0,
xx316 1:c840c2b6f490 195 reportTickerDelay = 24),
xx316 1:c840c2b6f490 196 failedReports(0)
xx316 1:c840c2b6f490 197 {
xx316 1:c840c2b6f490 198 }
xx316 1:c840c2b6f490 199
xx316 1:c840c2b6f490 200 virtual void onConnection(const Gap::ConnectionCallbackParams_t *params)
xx316 1:c840c2b6f490 201 {
xx316 1:c840c2b6f490 202 HIDServiceBase::onConnection(params);
xx316 1:c840c2b6f490 203
xx316 1:c840c2b6f490 204 /* Drain buffer, in case we've been disconnected while transmitting */
xx316 1:c840c2b6f490 205 if (!reportTickerIsActive && keyBuffer.isSomethingPending())
xx316 1:c840c2b6f490 206 startReportTicker();
xx316 1:c840c2b6f490 207 }
xx316 1:c840c2b6f490 208
xx316 1:c840c2b6f490 209 virtual void onDisconnection(const Gap::DisconnectionCallbackParams_t *params)
xx316 1:c840c2b6f490 210 {
xx316 1:c840c2b6f490 211 stopReportTicker();
xx316 1:c840c2b6f490 212 HIDServiceBase::onDisconnection(params);
xx316 1:c840c2b6f490 213 }
xx316 1:c840c2b6f490 214
xx316 1:c840c2b6f490 215 /**
xx316 1:c840c2b6f490 216 * Send raw report. Should only be called by sendCallback.
xx316 1:c840c2b6f490 217 */
xx316 1:c840c2b6f490 218 virtual ble_error_t send(const report_t report)
xx316 1:c840c2b6f490 219 {
xx316 1:c840c2b6f490 220 static unsigned int consecutiveFailures = 0;
xx316 1:c840c2b6f490 221 ble_error_t ret = HIDServiceBase::send(report);
xx316 1:c840c2b6f490 222
xx316 1:c840c2b6f490 223 /*
xx316 1:c840c2b6f490 224 * Wait until a buffer is available (onDataSent)
xx316 1:c840c2b6f490 225 * TODO. This won't work, because BUSY error is not only returned when we're short of
xx316 1:c840c2b6f490 226 * notification buffers, but in other cases as well (e.g. when disconnected). We need to
xx316 1:c840c2b6f490 227 * find a reliable way of knowing when we actually need to wait for onDataSent to be called.
xx316 1:c840c2b6f490 228 if (ret == BLE_STACK_BUSY)
xx316 1:c840c2b6f490 229 stopReportTicker();
xx316 1:c840c2b6f490 230 */
xx316 1:c840c2b6f490 231 if (ret == BLE_STACK_BUSY)
xx316 1:c840c2b6f490 232 consecutiveFailures++;
xx316 1:c840c2b6f490 233 else
xx316 1:c840c2b6f490 234 consecutiveFailures = 0;
xx316 1:c840c2b6f490 235
xx316 1:c840c2b6f490 236 if (consecutiveFailures > 20) {
xx316 1:c840c2b6f490 237 /*
xx316 1:c840c2b6f490 238 * We're not transmitting anything anymore. Might as well avoid overloading the
xx316 1:c840c2b6f490 239 * system in case it can magically fix itself. Ticker will start again on next _putc
xx316 1:c840c2b6f490 240 * call. It could also be started on next connection, but we can't register a callback
xx316 1:c840c2b6f490 241 * for that, currently.
xx316 1:c840c2b6f490 242 */
xx316 1:c840c2b6f490 243 stopReportTicker();
xx316 1:c840c2b6f490 244 consecutiveFailures = 0;
xx316 1:c840c2b6f490 245 }
xx316 1:c840c2b6f490 246
xx316 1:c840c2b6f490 247 return ret;
xx316 1:c840c2b6f490 248 }
xx316 1:c840c2b6f490 249
xx316 1:c840c2b6f490 250 /**
xx316 1:c840c2b6f490 251 * Send an empty report, representing keyUp event
xx316 1:c840c2b6f490 252 */
xx316 1:c840c2b6f490 253 ble_error_t keyUpCode(void)
xx316 1:c840c2b6f490 254 {
xx316 1:c840c2b6f490 255 return send(emptyInputReportData);
xx316 1:c840c2b6f490 256 }
xx316 1:c840c2b6f490 257
xx316 1:c840c2b6f490 258 /**
xx316 1:c840c2b6f490 259 * Send a character, defined by a modifier (CTRL, SHIFT, ALT) and the key
xx316 1:c840c2b6f490 260 *
xx316 1:c840c2b6f490 261 * @param key Character to send (as defined in USB HID Usage Tables)
xx316 1:c840c2b6f490 262 * @param modifier Optional modifiers (logical OR of enum MODIFIER_KEY)
xx316 1:c840c2b6f490 263 *
xx316 1:c840c2b6f490 264 * @returns BLE_ERROR_NONE on success, or an error code otherwise.
xx316 1:c840c2b6f490 265 */
xx316 1:c840c2b6f490 266 ble_error_t keyDownCode(uint8_t key, uint8_t modifier)
xx316 1:c840c2b6f490 267 {
xx316 1:c840c2b6f490 268 inputReportData[0] = modifier;
xx316 1:c840c2b6f490 269 inputReportData[2] = keymap[key].usage;
xx316 1:c840c2b6f490 270
xx316 1:c840c2b6f490 271 return send(inputReportData);
xx316 1:c840c2b6f490 272 }
xx316 1:c840c2b6f490 273
xx316 1:c840c2b6f490 274 /**
xx316 1:c840c2b6f490 275 * Push a key on the internal FIFO
xx316 1:c840c2b6f490 276 *
xx316 1:c840c2b6f490 277 * @param c ASCII character to send
xx316 1:c840c2b6f490 278 *
xx316 1:c840c2b6f490 279 * @returns 0 on success, or ENOMEM when the FIFO is full.
xx316 1:c840c2b6f490 280 */
xx316 1:c840c2b6f490 281 virtual int _putc(int c) {
xx316 1:c840c2b6f490 282 if (keyBuffer.full()) {
xx316 1:c840c2b6f490 283 return ENOMEM;
xx316 1:c840c2b6f490 284 }
xx316 1:c840c2b6f490 285
xx316 1:c840c2b6f490 286 keyBuffer.push((unsigned char)c);
xx316 1:c840c2b6f490 287
xx316 1:c840c2b6f490 288 if (!reportTickerIsActive)
xx316 1:c840c2b6f490 289 startReportTicker();
xx316 1:c840c2b6f490 290
xx316 1:c840c2b6f490 291 return 0;
xx316 1:c840c2b6f490 292 }
xx316 1:c840c2b6f490 293
xx316 1:c840c2b6f490 294 uint8_t lockStatus() {
xx316 1:c840c2b6f490 295 // TODO: implement numlock/capslock/scrolllock
xx316 1:c840c2b6f490 296 return 0;
xx316 1:c840c2b6f490 297 }
xx316 1:c840c2b6f490 298
xx316 1:c840c2b6f490 299 /**
xx316 1:c840c2b6f490 300 * Pop a key from the internal FIFO, and attempt to send it over BLE
xx316 1:c840c2b6f490 301 */
xx316 1:c840c2b6f490 302 virtual void sendCallback(void) {
xx316 1:c840c2b6f490 303 ble_error_t ret;
xx316 1:c840c2b6f490 304 uint8_t c;
xx316 1:c840c2b6f490 305
xx316 1:c840c2b6f490 306 if (!keyBuffer.isSomethingPending()) {
xx316 1:c840c2b6f490 307 /* Stop until the next call to putc */
xx316 1:c840c2b6f490 308 stopReportTicker();
xx316 1:c840c2b6f490 309 return;
xx316 1:c840c2b6f490 310 }
xx316 1:c840c2b6f490 311
xx316 1:c840c2b6f490 312 if (!keyBuffer.isKeyUpPending()) {
xx316 1:c840c2b6f490 313 bool hasData = keyBuffer.getPending(c);
xx316 1:c840c2b6f490 314
xx316 1:c840c2b6f490 315 /*
xx316 1:c840c2b6f490 316 * If something is pending and is not a keyUp, getPending *must* return something. The
xx316 1:c840c2b6f490 317 * following is only a sanity check.
xx316 1:c840c2b6f490 318 */
xx316 1:c840c2b6f490 319 MBED_ASSERT(hasData);
xx316 1:c840c2b6f490 320
xx316 1:c840c2b6f490 321 if (hasData) {
xx316 1:c840c2b6f490 322 ret = keyDownCode(c, keymap[c].modifier);
xx316 1:c840c2b6f490 323 if (ret) {
xx316 1:c840c2b6f490 324 keyBuffer.setPending(c);
xx316 1:c840c2b6f490 325 failedReports++;
xx316 1:c840c2b6f490 326 return;
xx316 1:c840c2b6f490 327 }
xx316 1:c840c2b6f490 328 }
xx316 1:c840c2b6f490 329 }
xx316 1:c840c2b6f490 330
xx316 1:c840c2b6f490 331 ret = keyUpCode();
xx316 1:c840c2b6f490 332 if (ret) {
xx316 1:c840c2b6f490 333 keyBuffer.setKeyUpPending();
xx316 1:c840c2b6f490 334 failedReports++;
xx316 1:c840c2b6f490 335 } else {
xx316 1:c840c2b6f490 336 keyBuffer.clearKeyUpPending();
xx316 1:c840c2b6f490 337 }
xx316 1:c840c2b6f490 338 }
xx316 1:c840c2b6f490 339
xx316 1:c840c2b6f490 340 /**
xx316 1:c840c2b6f490 341 * Restart report ticker if it was disabled, after too many consecutive failures.
xx316 1:c840c2b6f490 342 *
xx316 1:c840c2b6f490 343 * This is called by the BLE stack.
xx316 1:c840c2b6f490 344 *
xx316 1:c840c2b6f490 345 * @param count Number of reports (notifications) sent
xx316 1:c840c2b6f490 346 */
xx316 1:c840c2b6f490 347 virtual void onDataSent(unsigned count)
xx316 1:c840c2b6f490 348 {
xx316 1:c840c2b6f490 349 if (!reportTickerIsActive && keyBuffer.isSomethingPending())
xx316 1:c840c2b6f490 350 startReportTicker();
xx316 1:c840c2b6f490 351 }
xx316 1:c840c2b6f490 352
xx316 1:c840c2b6f490 353 unsigned long failedReports;
xx316 1:c840c2b6f490 354
xx316 1:c840c2b6f490 355 protected:
xx316 1:c840c2b6f490 356 virtual int _getc() {
xx316 1:c840c2b6f490 357 return 0;
xx316 1:c840c2b6f490 358 }
xx316 1:c840c2b6f490 359
xx316 1:c840c2b6f490 360 protected:
xx316 1:c840c2b6f490 361 KeyBuffer keyBuffer;
xx316 1:c840c2b6f490 362
xx316 1:c840c2b6f490 363 //GattCharacteristic boot_keyboard_input_report;
xx316 1:c840c2b6f490 364 //GattCharacteristic boot_keyboard_output_report;
xx316 1:c840c2b6f490 365 };
xx316 1:c840c2b6f490 366
xx316 1:c840c2b6f490 367