HID-over-GATT implementation with the BLE API. This library allows to create devices such as mouse, keyboard or joystick, over Bluetooth Low Energy.

Dependents:   Seeed_Tiny_BLE_FTHR_Peripheral

Fork of BLE_HID by Jean-Philippe Brucker

Committer:
Jean-Philippe Brucker
Date:
Tue Sep 15 20:16:58 2015 +0100
Revision:
0:cfd70fa91663
Child:
1:7a6c2e2c9371
Initial version

Provides all the components for an HID-over-GATT Profile implementation using
BLE API.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jean-Philippe Brucker 0:cfd70fa91663 1 #include <errno.h>
Jean-Philippe Brucker 0:cfd70fa91663 2 #include "mbed.h"
Jean-Philippe Brucker 0:cfd70fa91663 3 #include "CircularBuffer.h"
Jean-Philippe Brucker 0:cfd70fa91663 4
Jean-Philippe Brucker 0:cfd70fa91663 5 #include "HIDServiceBase.h"
Jean-Philippe Brucker 0:cfd70fa91663 6 #include "Keyboard_types.h"
Jean-Philippe Brucker 0:cfd70fa91663 7
Jean-Philippe Brucker 0:cfd70fa91663 8 /* TODO: make this easier to configure by application (e.g. as a template parameter for
Jean-Philippe Brucker 0:cfd70fa91663 9 * KeyboardService) */
Jean-Philippe Brucker 0:cfd70fa91663 10 #ifndef KEYBUFFER_SIZE
Jean-Philippe Brucker 0:cfd70fa91663 11 #define KEYBUFFER_SIZE 512
Jean-Philippe Brucker 0:cfd70fa91663 12 #endif
Jean-Philippe Brucker 0:cfd70fa91663 13
Jean-Philippe Brucker 0:cfd70fa91663 14 report_map_t KEYBOARD_REPORT_MAP = {
Jean-Philippe Brucker 0:cfd70fa91663 15 USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls
Jean-Philippe Brucker 0:cfd70fa91663 16 USAGE(1), 0x06, // Keyboard
Jean-Philippe Brucker 0:cfd70fa91663 17 COLLECTION(1), 0x01, // Application
Jean-Philippe Brucker 0:cfd70fa91663 18 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
Jean-Philippe Brucker 0:cfd70fa91663 19 USAGE_MINIMUM(1), 0xE0,
Jean-Philippe Brucker 0:cfd70fa91663 20 USAGE_MAXIMUM(1), 0xE7,
Jean-Philippe Brucker 0:cfd70fa91663 21 LOGICAL_MINIMUM(1), 0x00,
Jean-Philippe Brucker 0:cfd70fa91663 22 LOGICAL_MAXIMUM(1), 0x01,
Jean-Philippe Brucker 0:cfd70fa91663 23 REPORT_SIZE(1), 0x01, // 1 byte (Modifier)
Jean-Philippe Brucker 0:cfd70fa91663 24 REPORT_COUNT(1), 0x08,
Jean-Philippe Brucker 0:cfd70fa91663 25 INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position
Jean-Philippe Brucker 0:cfd70fa91663 26 REPORT_COUNT(1), 0x01, // 1 byte (Reserved)
Jean-Philippe Brucker 0:cfd70fa91663 27 REPORT_SIZE(1), 0x08,
Jean-Philippe Brucker 0:cfd70fa91663 28 INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
Jean-Philippe Brucker 0:cfd70fa91663 29 REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
Jean-Philippe Brucker 0:cfd70fa91663 30 REPORT_SIZE(1), 0x01,
Jean-Philippe Brucker 0:cfd70fa91663 31 USAGE_PAGE(1), 0x08, // LEDs
Jean-Philippe Brucker 0:cfd70fa91663 32 USAGE_MINIMUM(1), 0x01, // Num Lock
Jean-Philippe Brucker 0:cfd70fa91663 33 USAGE_MAXIMUM(1), 0x05, // Kana
Jean-Philippe Brucker 0:cfd70fa91663 34 OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
Jean-Philippe Brucker 0:cfd70fa91663 35 REPORT_COUNT(1), 0x01, // 3 bits (Padding)
Jean-Philippe Brucker 0:cfd70fa91663 36 REPORT_SIZE(1), 0x03,
Jean-Philippe Brucker 0:cfd70fa91663 37 OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
Jean-Philippe Brucker 0:cfd70fa91663 38 REPORT_COUNT(1), 0x06, // 6 bytes (Keys)
Jean-Philippe Brucker 0:cfd70fa91663 39 REPORT_SIZE(1), 0x08,
Jean-Philippe Brucker 0:cfd70fa91663 40 LOGICAL_MINIMUM(1), 0x00,
Jean-Philippe Brucker 0:cfd70fa91663 41 LOGICAL_MAXIMUM(1), 0x65, // 101 keys
Jean-Philippe Brucker 0:cfd70fa91663 42 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
Jean-Philippe Brucker 0:cfd70fa91663 43 USAGE_MINIMUM(1), 0x00,
Jean-Philippe Brucker 0:cfd70fa91663 44 USAGE_MAXIMUM(1), 0x65,
Jean-Philippe Brucker 0:cfd70fa91663 45 INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
Jean-Philippe Brucker 0:cfd70fa91663 46 END_COLLECTION(0),
Jean-Philippe Brucker 0:cfd70fa91663 47 };
Jean-Philippe Brucker 0:cfd70fa91663 48
Jean-Philippe Brucker 0:cfd70fa91663 49 static uint8_t inputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
Jean-Philippe Brucker 0:cfd70fa91663 50 static const uint8_t emptyInputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
Jean-Philippe Brucker 0:cfd70fa91663 51 static uint8_t outputReportData[] = { 0 };
Jean-Philippe Brucker 0:cfd70fa91663 52
Jean-Philippe Brucker 0:cfd70fa91663 53 /**
Jean-Philippe Brucker 0:cfd70fa91663 54 * @class KeyBuffer
Jean-Philippe Brucker 0:cfd70fa91663 55 *
Jean-Philippe Brucker 0:cfd70fa91663 56 * Buffer used to store keys to send.
Jean-Philippe Brucker 0:cfd70fa91663 57 * Internally, it is a CircularBuffer, with the added capability of putting the last char back in,
Jean-Philippe Brucker 0:cfd70fa91663 58 * when we're unable to send it (ie. when BLE stack is busy)
Jean-Philippe Brucker 0:cfd70fa91663 59 */
Jean-Philippe Brucker 0:cfd70fa91663 60 class KeyBuffer: public CircularBuffer<uint8_t, KEYBUFFER_SIZE>
Jean-Philippe Brucker 0:cfd70fa91663 61 {
Jean-Philippe Brucker 0:cfd70fa91663 62 public:
Jean-Philippe Brucker 0:cfd70fa91663 63 KeyBuffer() :
Jean-Philippe Brucker 0:cfd70fa91663 64 CircularBuffer(),
Jean-Philippe Brucker 0:cfd70fa91663 65 dataIsPending (false),
Jean-Philippe Brucker 0:cfd70fa91663 66 keyUpIsPending (false)
Jean-Philippe Brucker 0:cfd70fa91663 67 {
Jean-Philippe Brucker 0:cfd70fa91663 68 }
Jean-Philippe Brucker 0:cfd70fa91663 69
Jean-Philippe Brucker 0:cfd70fa91663 70 /**
Jean-Philippe Brucker 0:cfd70fa91663 71 * Mark a character as pending. When a freshly popped character cannot be sent, because the
Jean-Philippe Brucker 0:cfd70fa91663 72 * underlying stack is busy, we set it as pending, and it will get popped in priority by @ref
Jean-Philippe Brucker 0:cfd70fa91663 73 * getPending once reports can be sent again.
Jean-Philippe Brucker 0:cfd70fa91663 74 *
Jean-Philippe Brucker 0:cfd70fa91663 75 * @param data The character to send in priority. The second keyUp report is implied.
Jean-Philippe Brucker 0:cfd70fa91663 76 */
Jean-Philippe Brucker 0:cfd70fa91663 77 void setPending(uint8_t data)
Jean-Philippe Brucker 0:cfd70fa91663 78 {
Jean-Philippe Brucker 0:cfd70fa91663 79 MBED_ASSERT(dataIsPending == false);
Jean-Philippe Brucker 0:cfd70fa91663 80
Jean-Philippe Brucker 0:cfd70fa91663 81 dataIsPending = true;
Jean-Philippe Brucker 0:cfd70fa91663 82 pendingData = data;
Jean-Philippe Brucker 0:cfd70fa91663 83 keyUpIsPending = true;
Jean-Philippe Brucker 0:cfd70fa91663 84 }
Jean-Philippe Brucker 0:cfd70fa91663 85
Jean-Philippe Brucker 0:cfd70fa91663 86 /**
Jean-Philippe Brucker 0:cfd70fa91663 87 * Get pending char. Either from the high priority buffer (set with setPending), or from the
Jean-Philippe Brucker 0:cfd70fa91663 88 * circular buffer.
Jean-Philippe Brucker 0:cfd70fa91663 89 *
Jean-Philippe Brucker 0:cfd70fa91663 90 * @param data Filled with the pending data, when present
Jean-Philippe Brucker 0:cfd70fa91663 91 * @return true if data was filled
Jean-Philippe Brucker 0:cfd70fa91663 92 */
Jean-Philippe Brucker 0:cfd70fa91663 93 bool getPending(uint8_t &data)
Jean-Philippe Brucker 0:cfd70fa91663 94 {
Jean-Philippe Brucker 0:cfd70fa91663 95 if (dataIsPending) {
Jean-Philippe Brucker 0:cfd70fa91663 96 dataIsPending = false;
Jean-Philippe Brucker 0:cfd70fa91663 97 data = pendingData;
Jean-Philippe Brucker 0:cfd70fa91663 98 return true;
Jean-Philippe Brucker 0:cfd70fa91663 99 }
Jean-Philippe Brucker 0:cfd70fa91663 100
Jean-Philippe Brucker 0:cfd70fa91663 101 return pop(data);
Jean-Philippe Brucker 0:cfd70fa91663 102 }
Jean-Philippe Brucker 0:cfd70fa91663 103
Jean-Philippe Brucker 0:cfd70fa91663 104 bool isSomethingPending(void)
Jean-Philippe Brucker 0:cfd70fa91663 105 {
Jean-Philippe Brucker 0:cfd70fa91663 106 return dataIsPending || keyUpIsPending || !empty();
Jean-Philippe Brucker 0:cfd70fa91663 107 }
Jean-Philippe Brucker 0:cfd70fa91663 108
Jean-Philippe Brucker 0:cfd70fa91663 109 /**
Jean-Philippe Brucker 0:cfd70fa91663 110 * Signal that a keyUp report is pending. This means that a character has successfully been
Jean-Philippe Brucker 0:cfd70fa91663 111 * sent, but the subsequent keyUp report failed. This report is of highest priority than the
Jean-Philippe Brucker 0:cfd70fa91663 112 * next character.
Jean-Philippe Brucker 0:cfd70fa91663 113 */
Jean-Philippe Brucker 0:cfd70fa91663 114 void setKeyUpPending(void)
Jean-Philippe Brucker 0:cfd70fa91663 115 {
Jean-Philippe Brucker 0:cfd70fa91663 116 keyUpIsPending = true;
Jean-Philippe Brucker 0:cfd70fa91663 117 }
Jean-Philippe Brucker 0:cfd70fa91663 118
Jean-Philippe Brucker 0:cfd70fa91663 119 /**
Jean-Philippe Brucker 0:cfd70fa91663 120 * Signal that no high-priority report is pending anymore, we can go back to the normal queue.
Jean-Philippe Brucker 0:cfd70fa91663 121 */
Jean-Philippe Brucker 0:cfd70fa91663 122 void clearKeyUpPending(void)
Jean-Philippe Brucker 0:cfd70fa91663 123 {
Jean-Philippe Brucker 0:cfd70fa91663 124 keyUpIsPending = false;
Jean-Philippe Brucker 0:cfd70fa91663 125 }
Jean-Philippe Brucker 0:cfd70fa91663 126
Jean-Philippe Brucker 0:cfd70fa91663 127 bool isKeyUpPending(void)
Jean-Philippe Brucker 0:cfd70fa91663 128 {
Jean-Philippe Brucker 0:cfd70fa91663 129 return keyUpIsPending;
Jean-Philippe Brucker 0:cfd70fa91663 130 }
Jean-Philippe Brucker 0:cfd70fa91663 131
Jean-Philippe Brucker 0:cfd70fa91663 132 protected:
Jean-Philippe Brucker 0:cfd70fa91663 133 bool dataIsPending;
Jean-Philippe Brucker 0:cfd70fa91663 134 uint8_t pendingData;
Jean-Philippe Brucker 0:cfd70fa91663 135 bool keyUpIsPending;
Jean-Philippe Brucker 0:cfd70fa91663 136 };
Jean-Philippe Brucker 0:cfd70fa91663 137
Jean-Philippe Brucker 0:cfd70fa91663 138 class KeyboardService : public HIDServiceBase, public Stream
Jean-Philippe Brucker 0:cfd70fa91663 139 {
Jean-Philippe Brucker 0:cfd70fa91663 140 public:
Jean-Philippe Brucker 0:cfd70fa91663 141 KeyboardService(BLE &_ble) :
Jean-Philippe Brucker 0:cfd70fa91663 142 HIDServiceBase(_ble,
Jean-Philippe Brucker 0:cfd70fa91663 143 KEYBOARD_REPORT_MAP, sizeof(KEYBOARD_REPORT_MAP),
Jean-Philippe Brucker 0:cfd70fa91663 144 inputReport = emptyInputReportData,
Jean-Philippe Brucker 0:cfd70fa91663 145 outputReport = outputReportData,
Jean-Philippe Brucker 0:cfd70fa91663 146 featureReport = NULL,
Jean-Philippe Brucker 0:cfd70fa91663 147 inputReportLength = sizeof(inputReportData),
Jean-Philippe Brucker 0:cfd70fa91663 148 outputReportLength = sizeof(outputReportData),
Jean-Philippe Brucker 0:cfd70fa91663 149 featureReportLength = 0,
Jean-Philippe Brucker 0:cfd70fa91663 150 reportTickerDelay = 24),
Jean-Philippe Brucker 0:cfd70fa91663 151 failedReports(0)
Jean-Philippe Brucker 0:cfd70fa91663 152 {
Jean-Philippe Brucker 0:cfd70fa91663 153 }
Jean-Philippe Brucker 0:cfd70fa91663 154
Jean-Philippe Brucker 0:cfd70fa91663 155 virtual ble_error_t send(const report_t report)
Jean-Philippe Brucker 0:cfd70fa91663 156 {
Jean-Philippe Brucker 0:cfd70fa91663 157 static unsigned int consecutiveFailures = 0;
Jean-Philippe Brucker 0:cfd70fa91663 158 ble_error_t ret = HIDServiceBase::send(report);
Jean-Philippe Brucker 0:cfd70fa91663 159
Jean-Philippe Brucker 0:cfd70fa91663 160 /*
Jean-Philippe Brucker 0:cfd70fa91663 161 * Wait until a buffer is available (onDataSent)
Jean-Philippe Brucker 0:cfd70fa91663 162 * TODO. This won't work, because BUSY error is not only returned when we're short of
Jean-Philippe Brucker 0:cfd70fa91663 163 * notification buffers, but in other cases as well (e.g. when disconnected). We need to
Jean-Philippe Brucker 0:cfd70fa91663 164 * find a reliable way of knowing when we actually need to wait for onDataSent to be called.
Jean-Philippe Brucker 0:cfd70fa91663 165 if (ret == BLE_STACK_BUSY)
Jean-Philippe Brucker 0:cfd70fa91663 166 stopReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 167 */
Jean-Philippe Brucker 0:cfd70fa91663 168 if (ret == BLE_STACK_BUSY)
Jean-Philippe Brucker 0:cfd70fa91663 169 consecutiveFailures++;
Jean-Philippe Brucker 0:cfd70fa91663 170 else
Jean-Philippe Brucker 0:cfd70fa91663 171 consecutiveFailures = 0;
Jean-Philippe Brucker 0:cfd70fa91663 172
Jean-Philippe Brucker 0:cfd70fa91663 173 if (consecutiveFailures > 20) {
Jean-Philippe Brucker 0:cfd70fa91663 174 /*
Jean-Philippe Brucker 0:cfd70fa91663 175 * We're not tranmitting anything anymore. Might as well avoid overloading the
Jean-Philippe Brucker 0:cfd70fa91663 176 * system in case it can magically fix itself. Ticker will start again on next _putc
Jean-Philippe Brucker 0:cfd70fa91663 177 * call. It could also be started on next connection, but we can't register a callback
Jean-Philippe Brucker 0:cfd70fa91663 178 * for that, currently.
Jean-Philippe Brucker 0:cfd70fa91663 179 */
Jean-Philippe Brucker 0:cfd70fa91663 180 stopReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 181 consecutiveFailures = 0;
Jean-Philippe Brucker 0:cfd70fa91663 182 }
Jean-Philippe Brucker 0:cfd70fa91663 183
Jean-Philippe Brucker 0:cfd70fa91663 184 return ret;
Jean-Philippe Brucker 0:cfd70fa91663 185 }
Jean-Philippe Brucker 0:cfd70fa91663 186
Jean-Philippe Brucker 0:cfd70fa91663 187 /**
Jean-Philippe Brucker 0:cfd70fa91663 188 * Send an empty report, representing keyUp event
Jean-Philippe Brucker 0:cfd70fa91663 189 */
Jean-Philippe Brucker 0:cfd70fa91663 190 ble_error_t keyUpCode(void)
Jean-Philippe Brucker 0:cfd70fa91663 191 {
Jean-Philippe Brucker 0:cfd70fa91663 192 return send(emptyInputReportData);
Jean-Philippe Brucker 0:cfd70fa91663 193 }
Jean-Philippe Brucker 0:cfd70fa91663 194
Jean-Philippe Brucker 0:cfd70fa91663 195 ble_error_t keyDownCode(uint8_t key, uint8_t modifier)
Jean-Philippe Brucker 0:cfd70fa91663 196 {
Jean-Philippe Brucker 0:cfd70fa91663 197 inputReportData[0] = modifier;
Jean-Philippe Brucker 0:cfd70fa91663 198 inputReportData[2] = keymap[key].usage;
Jean-Philippe Brucker 0:cfd70fa91663 199
Jean-Philippe Brucker 0:cfd70fa91663 200 return send(inputReportData);
Jean-Philippe Brucker 0:cfd70fa91663 201 }
Jean-Philippe Brucker 0:cfd70fa91663 202
Jean-Philippe Brucker 0:cfd70fa91663 203 virtual int _putc(int c) {
Jean-Philippe Brucker 0:cfd70fa91663 204 if (keyBuffer.full()) {
Jean-Philippe Brucker 0:cfd70fa91663 205 return ENOMEM;
Jean-Philippe Brucker 0:cfd70fa91663 206 }
Jean-Philippe Brucker 0:cfd70fa91663 207
Jean-Philippe Brucker 0:cfd70fa91663 208 keyBuffer.push((unsigned char)c);
Jean-Philippe Brucker 0:cfd70fa91663 209
Jean-Philippe Brucker 0:cfd70fa91663 210 if (!reportTickerIsActive)
Jean-Philippe Brucker 0:cfd70fa91663 211 startReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 212
Jean-Philippe Brucker 0:cfd70fa91663 213 return 0;
Jean-Philippe Brucker 0:cfd70fa91663 214 }
Jean-Philippe Brucker 0:cfd70fa91663 215
Jean-Philippe Brucker 0:cfd70fa91663 216 uint8_t lockStatus() {
Jean-Philippe Brucker 0:cfd70fa91663 217 // TODO: implement numlock/capslock/scrolllock
Jean-Philippe Brucker 0:cfd70fa91663 218 return 0;
Jean-Philippe Brucker 0:cfd70fa91663 219 }
Jean-Philippe Brucker 0:cfd70fa91663 220
Jean-Philippe Brucker 0:cfd70fa91663 221 virtual void sendCallback(void) {
Jean-Philippe Brucker 0:cfd70fa91663 222 ble_error_t ret;
Jean-Philippe Brucker 0:cfd70fa91663 223 uint8_t c;
Jean-Philippe Brucker 0:cfd70fa91663 224
Jean-Philippe Brucker 0:cfd70fa91663 225 if (!keyBuffer.isSomethingPending()) {
Jean-Philippe Brucker 0:cfd70fa91663 226 /* Stop until the next call to putc */
Jean-Philippe Brucker 0:cfd70fa91663 227 stopReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 228 return;
Jean-Philippe Brucker 0:cfd70fa91663 229 }
Jean-Philippe Brucker 0:cfd70fa91663 230
Jean-Philippe Brucker 0:cfd70fa91663 231 if (!keyBuffer.isKeyUpPending()) {
Jean-Philippe Brucker 0:cfd70fa91663 232 bool hasData = keyBuffer.getPending(c);
Jean-Philippe Brucker 0:cfd70fa91663 233
Jean-Philippe Brucker 0:cfd70fa91663 234 /*
Jean-Philippe Brucker 0:cfd70fa91663 235 * If something is pending and is not a keyUp, getPending *must* return something. The
Jean-Philippe Brucker 0:cfd70fa91663 236 * following is only a sanity check.
Jean-Philippe Brucker 0:cfd70fa91663 237 */
Jean-Philippe Brucker 0:cfd70fa91663 238 MBED_ASSERT(hasData);
Jean-Philippe Brucker 0:cfd70fa91663 239
Jean-Philippe Brucker 0:cfd70fa91663 240 if (hasData) {
Jean-Philippe Brucker 0:cfd70fa91663 241 ret = keyDownCode(c, keymap[c].modifier);
Jean-Philippe Brucker 0:cfd70fa91663 242 if (ret) {
Jean-Philippe Brucker 0:cfd70fa91663 243 keyBuffer.setPending(c);
Jean-Philippe Brucker 0:cfd70fa91663 244 failedReports++;
Jean-Philippe Brucker 0:cfd70fa91663 245 return;
Jean-Philippe Brucker 0:cfd70fa91663 246 }
Jean-Philippe Brucker 0:cfd70fa91663 247 }
Jean-Philippe Brucker 0:cfd70fa91663 248 }
Jean-Philippe Brucker 0:cfd70fa91663 249
Jean-Philippe Brucker 0:cfd70fa91663 250 ret = keyUpCode();
Jean-Philippe Brucker 0:cfd70fa91663 251 if (ret) {
Jean-Philippe Brucker 0:cfd70fa91663 252 keyBuffer.setKeyUpPending();
Jean-Philippe Brucker 0:cfd70fa91663 253 failedReports++;
Jean-Philippe Brucker 0:cfd70fa91663 254 } else {
Jean-Philippe Brucker 0:cfd70fa91663 255 keyBuffer.clearKeyUpPending();
Jean-Philippe Brucker 0:cfd70fa91663 256 }
Jean-Philippe Brucker 0:cfd70fa91663 257 }
Jean-Philippe Brucker 0:cfd70fa91663 258
Jean-Philippe Brucker 0:cfd70fa91663 259 virtual void onDataSent(unsigned count)
Jean-Philippe Brucker 0:cfd70fa91663 260 {
Jean-Philippe Brucker 0:cfd70fa91663 261 if (!reportTickerIsActive && keyBuffer.isSomethingPending())
Jean-Philippe Brucker 0:cfd70fa91663 262 startReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 263 }
Jean-Philippe Brucker 0:cfd70fa91663 264
Jean-Philippe Brucker 0:cfd70fa91663 265 unsigned long failedReports;
Jean-Philippe Brucker 0:cfd70fa91663 266
Jean-Philippe Brucker 0:cfd70fa91663 267 protected:
Jean-Philippe Brucker 0:cfd70fa91663 268 virtual int _getc() {
Jean-Philippe Brucker 0:cfd70fa91663 269 return 0;
Jean-Philippe Brucker 0:cfd70fa91663 270 }
Jean-Philippe Brucker 0:cfd70fa91663 271
Jean-Philippe Brucker 0:cfd70fa91663 272 protected:
Jean-Philippe Brucker 0:cfd70fa91663 273 KeyBuffer keyBuffer;
Jean-Philippe Brucker 0:cfd70fa91663 274
Jean-Philippe Brucker 0:cfd70fa91663 275 //GattCharacteristic boot_keyboard_input_report;
Jean-Philippe Brucker 0:cfd70fa91663 276 //GattCharacteristic boot_keyboard_output_report;
Jean-Philippe Brucker 0:cfd70fa91663 277 };
Jean-Philippe Brucker 0:cfd70fa91663 278