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
KeyboardService.h@1:7a6c2e2c9371, 2015-10-07 (annotated)
- 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?
User | Revision | Line number | New 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 |