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:   BLENano_HID BLE_HID_MouseScrollDemo BLE_HID_KeyboardStreamDemo Shervs_TestKeyboard_TinyBLE ... more

The development repository is currently hosted on github. It contains examples and documentation. This is a snapshot of the library. The documentation can be read on github, or on docs.mbed.com.

Committer:
Jean-Philippe Brucker
Date:
Thu Nov 19 15:00:39 2015 +0000
Revision:
3:4f8429a1905b
Parent:
1:7a6c2e2c9371
Version 0.3

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 "mbed.h"
Jean-Philippe Brucker 0:cfd70fa91663 18 #include "HIDServiceBase.h"
Jean-Philippe Brucker 0:cfd70fa91663 19
Jean-Philippe Brucker 0:cfd70fa91663 20 HIDServiceBase::HIDServiceBase(BLE &_ble,
Jean-Philippe Brucker 0:cfd70fa91663 21 report_map_t reportMap,
Jean-Philippe Brucker 0:cfd70fa91663 22 uint8_t reportMapSize,
Jean-Philippe Brucker 0:cfd70fa91663 23 report_t inputReport,
Jean-Philippe Brucker 0:cfd70fa91663 24 report_t outputReport,
Jean-Philippe Brucker 0:cfd70fa91663 25 report_t featureReport,
Jean-Philippe Brucker 0:cfd70fa91663 26 uint8_t inputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 27 uint8_t outputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 28 uint8_t featureReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 29 uint8_t inputReportTickerDelay) :
Jean-Philippe Brucker 0:cfd70fa91663 30 ble(_ble),
Jean-Philippe Brucker 0:cfd70fa91663 31 connected (false),
Jean-Philippe Brucker 0:cfd70fa91663 32 reportMapLength(reportMapSize),
Jean-Philippe Brucker 0:cfd70fa91663 33
Jean-Philippe Brucker 0:cfd70fa91663 34 inputReport(inputReport),
Jean-Philippe Brucker 0:cfd70fa91663 35 outputReport(outputReport),
Jean-Philippe Brucker 0:cfd70fa91663 36 featureReport(featureReport),
Jean-Philippe Brucker 0:cfd70fa91663 37
Jean-Philippe Brucker 0:cfd70fa91663 38 inputReportLength(inputReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 39 outputReportLength(outputReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 40 featureReportLength(featureReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 41
Jean-Philippe Brucker 0:cfd70fa91663 42 protocolMode(REPORT_PROTOCOL),
Jean-Philippe Brucker 0:cfd70fa91663 43
Jean-Philippe Brucker 0:cfd70fa91663 44 inputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 45 (uint8_t *)&inputReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 46 outputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 47 (uint8_t *)&outputReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 48 featureReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 49 (uint8_t *)&featureReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 50
Jean-Philippe Brucker 0:cfd70fa91663 51 protocolModeCharacteristic(GattCharacteristic::UUID_PROTOCOL_MODE_CHAR, &protocolMode, 1, 1,
Jean-Philippe Brucker 0:cfd70fa91663 52 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 53 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE),
Jean-Philippe Brucker 0:cfd70fa91663 54
Jean-Philippe Brucker 0:cfd70fa91663 55 inputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 56 (uint8_t *)inputReport, inputReportLength, inputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 57 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 58 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
Jean-Philippe Brucker 0:cfd70fa91663 59 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 60 inputReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 61
Jean-Philippe Brucker 0:cfd70fa91663 62 outputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 63 (uint8_t *)outputReport, outputReportLength, outputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 64 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 65 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE
Jean-Philippe Brucker 0:cfd70fa91663 66 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 67 outputReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 68
Jean-Philippe Brucker 0:cfd70fa91663 69 featureReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 70 (uint8_t *)featureReport, featureReportLength, featureReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 71 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 72 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 73 featureReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 74
Jean-Philippe Brucker 0:cfd70fa91663 75 /*
Jean-Philippe Brucker 0:cfd70fa91663 76 * We need to set reportMap content as const, in order to let the compiler put it into flash
Jean-Philippe Brucker 0:cfd70fa91663 77 * instead of RAM. The characteristic is read-only so it won't be written, but
Jean-Philippe Brucker 0:cfd70fa91663 78 * GattCharacteristic constructor takes non-const arguments only. Hence the cast.
Jean-Philippe Brucker 0:cfd70fa91663 79 */
Jean-Philippe Brucker 0:cfd70fa91663 80 reportMapCharacteristic(GattCharacteristic::UUID_REPORT_MAP_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 81 const_cast<uint8_t*>(reportMap), reportMapLength, reportMapLength,
Jean-Philippe Brucker 0:cfd70fa91663 82 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ),
Jean-Philippe Brucker 0:cfd70fa91663 83
Jean-Philippe Brucker 0:cfd70fa91663 84 HIDInformationCharacteristic(GattCharacteristic::UUID_HID_INFORMATION_CHAR, HIDInformation()),
Jean-Philippe Brucker 0:cfd70fa91663 85 HIDControlPointCharacteristic(GattCharacteristic::UUID_HID_CONTROL_POINT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 86 &controlPointCommand, 1, 1,
Jean-Philippe Brucker 0:cfd70fa91663 87 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE),
Jean-Philippe Brucker 0:cfd70fa91663 88
Jean-Philippe Brucker 0:cfd70fa91663 89 reportTickerDelay(inputReportTickerDelay),
Jean-Philippe Brucker 0:cfd70fa91663 90 reportTickerIsActive(false)
Jean-Philippe Brucker 0:cfd70fa91663 91 {
Jean-Philippe Brucker 0:cfd70fa91663 92 static GattCharacteristic *characteristics[] = {
Jean-Philippe Brucker 0:cfd70fa91663 93 &HIDInformationCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 94 &reportMapCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 95 &protocolModeCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 96 &HIDControlPointCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 97 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 98 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 99 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 100 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 101 NULL
Jean-Philippe Brucker 0:cfd70fa91663 102 };
Jean-Philippe Brucker 0:cfd70fa91663 103
Jean-Philippe Brucker 0:cfd70fa91663 104 unsigned int charIndex = 4;
Jean-Philippe Brucker 0:cfd70fa91663 105 /*
Jean-Philippe Brucker 0:cfd70fa91663 106 * Report characteristics are optional, and depend on the reportMap descriptor
Jean-Philippe Brucker 0:cfd70fa91663 107 * Note: at least one should be present, but we don't check that at the moment.
Jean-Philippe Brucker 0:cfd70fa91663 108 */
Jean-Philippe Brucker 0:cfd70fa91663 109 if (inputReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 110 characteristics[charIndex++] = &inputReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 111 if (outputReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 112 characteristics[charIndex++] = &outputReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 113 if (featureReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 114 characteristics[charIndex++] = &featureReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 115
Jean-Philippe Brucker 0:cfd70fa91663 116 /* TODO: let children add some more characteristics, namely boot keyboard and mouse (They are
Jean-Philippe Brucker 0:cfd70fa91663 117 * mandatory as per HIDS spec.) Ex:
Jean-Philippe Brucker 0:cfd70fa91663 118 *
Jean-Philippe Brucker 0:cfd70fa91663 119 * addExtraCharacteristics(characteristics, int& charIndex);
Jean-Philippe Brucker 0:cfd70fa91663 120 */
Jean-Philippe Brucker 0:cfd70fa91663 121
Jean-Philippe Brucker 0:cfd70fa91663 122 GattService service(GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
Jean-Philippe Brucker 0:cfd70fa91663 123 characteristics, charIndex);
Jean-Philippe Brucker 0:cfd70fa91663 124
Jean-Philippe Brucker 1:7a6c2e2c9371 125 ble.gattServer().addService(service);
Jean-Philippe Brucker 0:cfd70fa91663 126
Jean-Philippe Brucker 1:7a6c2e2c9371 127 ble.gap().onConnection(this, &HIDServiceBase::onConnection);
Jean-Philippe Brucker 1:7a6c2e2c9371 128 ble.gap().onDisconnection(this, &HIDServiceBase::onDisconnection);
Jean-Philippe Brucker 1:7a6c2e2c9371 129
Jean-Philippe Brucker 1:7a6c2e2c9371 130 ble.gattServer().onDataSent(this, &HIDServiceBase::onDataSent);
Jean-Philippe Brucker 0:cfd70fa91663 131
Jean-Philippe Brucker 0:cfd70fa91663 132 /*
Jean-Philippe Brucker 0:cfd70fa91663 133 * Change preferred connection params, in order to optimize the notification frequency. Most
Jean-Philippe Brucker 0:cfd70fa91663 134 * OSes seem to respect this, even though they are not required to.
Jean-Philippe Brucker 0:cfd70fa91663 135 *
Jean-Philippe Brucker 0:cfd70fa91663 136 * Some OSes don't handle reconnection well, at the moment, so we set the maximum possible
Jean-Philippe Brucker 0:cfd70fa91663 137 * timeout, 32 seconds
Jean-Philippe Brucker 0:cfd70fa91663 138 */
Jean-Philippe Brucker 0:cfd70fa91663 139 uint16_t minInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(reportTickerDelay / 2);
Jean-Philippe Brucker 0:cfd70fa91663 140 if (minInterval < 6)
Jean-Philippe Brucker 0:cfd70fa91663 141 minInterval = 6;
Jean-Philippe Brucker 0:cfd70fa91663 142 uint16_t maxInterval = minInterval * 2;
Jean-Philippe Brucker 0:cfd70fa91663 143 Gap::ConnectionParams_t params = {minInterval, maxInterval, 0, 3200};
Jean-Philippe Brucker 0:cfd70fa91663 144
Jean-Philippe Brucker 0:cfd70fa91663 145 ble.gap().setPreferredConnectionParams(&params);
Jean-Philippe Brucker 0:cfd70fa91663 146
Jean-Philippe Brucker 0:cfd70fa91663 147 SecurityManager::SecurityMode_t securityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;
Jean-Philippe Brucker 0:cfd70fa91663 148 protocolModeCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 149 reportMapCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 150 inputReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 151 outputReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 152 featureReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 153 }
Jean-Philippe Brucker 0:cfd70fa91663 154
Jean-Philippe Brucker 0:cfd70fa91663 155 void HIDServiceBase::startReportTicker(void) {
Jean-Philippe Brucker 0:cfd70fa91663 156 if (reportTickerIsActive)
Jean-Philippe Brucker 0:cfd70fa91663 157 return;
Jean-Philippe Brucker 0:cfd70fa91663 158 reportTicker.attach_us(this, &HIDServiceBase::sendCallback, reportTickerDelay * 1000);
Jean-Philippe Brucker 0:cfd70fa91663 159 reportTickerIsActive = true;
Jean-Philippe Brucker 0:cfd70fa91663 160 }
Jean-Philippe Brucker 0:cfd70fa91663 161
Jean-Philippe Brucker 0:cfd70fa91663 162 void HIDServiceBase::stopReportTicker(void) {
Jean-Philippe Brucker 0:cfd70fa91663 163 reportTicker.detach();
Jean-Philippe Brucker 0:cfd70fa91663 164 reportTickerIsActive = false;
Jean-Philippe Brucker 0:cfd70fa91663 165 }
Jean-Philippe Brucker 0:cfd70fa91663 166
Jean-Philippe Brucker 0:cfd70fa91663 167 void HIDServiceBase::onDataSent(unsigned count) {
Jean-Philippe Brucker 0:cfd70fa91663 168 startReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 169 }
Jean-Philippe Brucker 0:cfd70fa91663 170
Jean-Philippe Brucker 0:cfd70fa91663 171 GattAttribute** HIDServiceBase::inputReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 172 inputReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 173 inputReportReferenceData.type = INPUT_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 174
Jean-Philippe Brucker 0:cfd70fa91663 175 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 176 &inputReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 177 };
Jean-Philippe Brucker 0:cfd70fa91663 178 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 179 }
Jean-Philippe Brucker 0:cfd70fa91663 180
Jean-Philippe Brucker 0:cfd70fa91663 181 GattAttribute** HIDServiceBase::outputReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 182 outputReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 183 outputReportReferenceData.type = OUTPUT_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 184
Jean-Philippe Brucker 0:cfd70fa91663 185 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 186 &outputReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 187 };
Jean-Philippe Brucker 0:cfd70fa91663 188 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 189 }
Jean-Philippe Brucker 0:cfd70fa91663 190
Jean-Philippe Brucker 0:cfd70fa91663 191 GattAttribute** HIDServiceBase::featureReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 192 featureReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 193 featureReportReferenceData.type = FEATURE_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 194
Jean-Philippe Brucker 0:cfd70fa91663 195 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 196 &featureReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 197 };
Jean-Philippe Brucker 0:cfd70fa91663 198 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 199 }
Jean-Philippe Brucker 0:cfd70fa91663 200
Jean-Philippe Brucker 0:cfd70fa91663 201
Jean-Philippe Brucker 0:cfd70fa91663 202 HID_information_t* HIDServiceBase::HIDInformation() {
Jean-Philippe Brucker 0:cfd70fa91663 203 static HID_information_t info = {HID_VERSION_1_11, 0x00, 0x03};
Jean-Philippe Brucker 0:cfd70fa91663 204
Jean-Philippe Brucker 0:cfd70fa91663 205 return &info;
Jean-Philippe Brucker 0:cfd70fa91663 206 }
Jean-Philippe Brucker 0:cfd70fa91663 207
Jean-Philippe Brucker 0:cfd70fa91663 208 ble_error_t HIDServiceBase::send(const report_t report) {
Jean-Philippe Brucker 0:cfd70fa91663 209 return ble.gattServer().write(inputReportCharacteristic.getValueHandle(),
Jean-Philippe Brucker 0:cfd70fa91663 210 report,
Jean-Philippe Brucker 0:cfd70fa91663 211 inputReportLength);
Jean-Philippe Brucker 0:cfd70fa91663 212 }
Jean-Philippe Brucker 0:cfd70fa91663 213
Jean-Philippe Brucker 0:cfd70fa91663 214 ble_error_t HIDServiceBase::read(report_t report) {
Jean-Philippe Brucker 0:cfd70fa91663 215 // TODO. For the time being, we'll just have HID input reports...
Jean-Philippe Brucker 0:cfd70fa91663 216 return BLE_ERROR_NOT_IMPLEMENTED;
Jean-Philippe Brucker 0:cfd70fa91663 217 }
Jean-Philippe Brucker 0:cfd70fa91663 218
Jean-Philippe Brucker 1:7a6c2e2c9371 219 void HIDServiceBase::onConnection(const Gap::ConnectionCallbackParams_t *params)
Jean-Philippe Brucker 1:7a6c2e2c9371 220 {
Jean-Philippe Brucker 1:7a6c2e2c9371 221 this->connected = true;
Jean-Philippe Brucker 1:7a6c2e2c9371 222 }
Jean-Philippe Brucker 1:7a6c2e2c9371 223
Jean-Philippe Brucker 1:7a6c2e2c9371 224 void HIDServiceBase::onDisconnection(const Gap::DisconnectionCallbackParams_t *params)
Jean-Philippe Brucker 1:7a6c2e2c9371 225 {
Jean-Philippe Brucker 1:7a6c2e2c9371 226 this->connected = false;
Jean-Philippe Brucker 1:7a6c2e2c9371 227 }