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:
Wed Oct 07 11:29:52 2015 +0100
Revision:
1:7a6c2e2c9371
Parent:
0:cfd70fa91663
Child:
2:3d9adb26bdc5
Publish version 0.1 of the BLE HID lib

This version number is completely arbitrary, and probably won't stick: once the
service is stable, we'll merge it with BLE API.
It is simply used to keep examples in sync with the lib and the github
repository during development.

Who changed what in which revision?

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