| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com> | 
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 4 |  | 
| 5 | #include "hcimanager_p.h" | 
| 6 |  | 
| 7 | #include "qbluetoothsocketbase_p.h" | 
| 8 | #include "qlowenergyconnectionparameters.h" | 
| 9 |  | 
| 10 | #include <QtCore/qloggingcategory.h> | 
| 11 |  | 
| 12 | #include <cstring> | 
| 13 | #include <errno.h> | 
| 14 | #include <sys/types.h> | 
| 15 | #include <sys/socket.h> | 
| 16 | #include <sys/ioctl.h> | 
| 17 | #include <sys/uio.h> | 
| 18 | #include <unistd.h> | 
| 19 |  | 
| 20 | QT_BEGIN_NAMESPACE | 
| 21 |  | 
| 22 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) | 
| 23 |  | 
| 24 | HciManager::HciManager(const QBluetoothAddress& deviceAdapter) : | 
| 25 |     QObject(nullptr), hciSocket(-1), hciDev(-1) | 
| 26 | { | 
| 27 |     hciSocket = ::socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); | 
| 28 |     if (hciSocket < 0) { | 
| 29 |         qCWarning(QT_BT_BLUEZ) << "Cannot open HCI socket" ; | 
| 30 |         return; //TODO error report | 
| 31 |     } | 
| 32 |  | 
| 33 |     hciDev = hciForAddress(deviceAdapter); | 
| 34 |     if (hciDev < 0) { | 
| 35 |         qCWarning(QT_BT_BLUEZ) << "Cannot find hci dev for"  << deviceAdapter.toString(); | 
| 36 |         close(fd: hciSocket); | 
| 37 |         hciSocket = -1; | 
| 38 |         return; | 
| 39 |     } | 
| 40 |  | 
| 41 |     struct sockaddr_hci addr; | 
| 42 |  | 
| 43 |     memset(s: &addr, c: 0, n: sizeof(struct sockaddr_hci)); | 
| 44 |     addr.hci_dev = hciDev; | 
| 45 |     addr.hci_family = AF_BLUETOOTH; | 
| 46 |  | 
| 47 |     if (::bind(fd: hciSocket, addr: (struct sockaddr *) (&addr), len: sizeof(addr)) < 0) { | 
| 48 |         qCWarning(QT_BT_BLUEZ) << "HCI bind failed:"  << strerror(errno); | 
| 49 |         close(fd: hciSocket); | 
| 50 |         hciSocket = hciDev = -1; | 
| 51 |         return; | 
| 52 |     } | 
| 53 |  | 
| 54 |     notifier = new QSocketNotifier(hciSocket, QSocketNotifier::Read, this); | 
| 55 |     connect(sender: notifier, SIGNAL(activated(QSocketDescriptor)), receiver: this, SLOT(_q_readNotify())); | 
| 56 |  | 
| 57 | } | 
| 58 |  | 
| 59 | HciManager::~HciManager() | 
| 60 | { | 
| 61 |     if (hciSocket >= 0) | 
| 62 |         ::close(fd: hciSocket); | 
| 63 |  | 
| 64 | } | 
| 65 |  | 
| 66 | bool HciManager::isValid() const | 
| 67 | { | 
| 68 |     if (hciSocket && hciDev >= 0) | 
| 69 |         return true; | 
| 70 |     return false; | 
| 71 | } | 
| 72 |  | 
| 73 | int HciManager::hciForAddress(const QBluetoothAddress &deviceAdapter) | 
| 74 | { | 
| 75 |     if (hciSocket < 0) | 
| 76 |         return -1; | 
| 77 |  | 
| 78 |     bdaddr_t adapter; | 
| 79 |     convertAddress(from: deviceAdapter.toUInt64(), to&: adapter.b); | 
| 80 |  | 
| 81 |     struct hci_dev_req *devRequest = nullptr; | 
| 82 |     struct hci_dev_list_req *devRequestList = nullptr; | 
| 83 |     struct hci_dev_info devInfo; | 
| 84 |     const int devListSize = sizeof(struct hci_dev_list_req) | 
| 85 |                         + HCI_MAX_DEV * sizeof(struct hci_dev_req); | 
| 86 |  | 
| 87 |     devRequestList = (hci_dev_list_req *) malloc(size: devListSize); | 
| 88 |     if (!devRequestList) | 
| 89 |         return -1; | 
| 90 |  | 
| 91 |     QScopedPointer<hci_dev_list_req, QScopedPointerPodDeleter> p(devRequestList); | 
| 92 |  | 
| 93 |     memset(s: p.data(), c: 0, n: devListSize); | 
| 94 |     p->dev_num = HCI_MAX_DEV; | 
| 95 |     devRequest = p->dev_req; | 
| 96 |  | 
| 97 |     if (ioctl(fd: hciSocket, HCIGETDEVLIST, devRequestList) < 0) | 
| 98 |         return -1; | 
| 99 |  | 
| 100 |     for (int i = 0; i < devRequestList->dev_num; i++) { | 
| 101 |         devInfo.dev_id = (devRequest+i)->dev_id; | 
| 102 |         if (ioctl(fd: hciSocket, HCIGETDEVINFO, &devInfo) < 0) { | 
| 103 |             continue; | 
| 104 |         } | 
| 105 |  | 
| 106 |         int result = memcmp(s1: &adapter, s2: &devInfo.bdaddr, n: sizeof(bdaddr_t)); | 
| 107 |         if (result == 0 || deviceAdapter.isNull()) // addresses match | 
| 108 |             return devInfo.dev_id; | 
| 109 |     } | 
| 110 |  | 
| 111 |     return -1; | 
| 112 | } | 
| 113 |  | 
| 114 | /* | 
| 115 |  * Returns true if \a event was successfully enabled | 
| 116 |  */ | 
| 117 | bool HciManager::monitorEvent(HciManager::HciEvent event) | 
| 118 | { | 
| 119 |     if (!isValid()) | 
| 120 |         return false; | 
| 121 |  | 
| 122 |     // this event is already enabled | 
| 123 |     // TODO runningEvents does not seem to be used | 
| 124 |     if (runningEvents.contains(value: event)) | 
| 125 |         return true; | 
| 126 |  | 
| 127 |     hci_filter filter; | 
| 128 |     socklen_t length = sizeof(hci_filter); | 
| 129 |     if (getsockopt(fd: hciSocket, SOL_HCI, HCI_FILTER, optval: &filter, optlen: &length) < 0) { | 
| 130 |         qCWarning(QT_BT_BLUEZ) << "Cannot retrieve HCI filter settings" ; | 
| 131 |         return false; | 
| 132 |     } | 
| 133 |  | 
| 134 |     hci_filter_set_ptype(HCI_EVENT_PKT, f: &filter); | 
| 135 |     hci_filter_set_event(e: static_cast<int>(event), f: &filter); | 
| 136 |     //hci_filter_all_events(&filter); | 
| 137 |  | 
| 138 |     if (setsockopt(fd: hciSocket, SOL_HCI, HCI_FILTER, optval: &filter, optlen: sizeof(hci_filter)) < 0) { | 
| 139 |         qCWarning(QT_BT_BLUEZ) << "Could not set HCI socket options:"  << strerror(errno); | 
| 140 |         return false; | 
| 141 |     } | 
| 142 |  | 
| 143 |     return true; | 
| 144 | } | 
| 145 |  | 
| 146 | bool HciManager::monitorAclPackets() | 
| 147 | { | 
| 148 |     if (!isValid()) | 
| 149 |         return false; | 
| 150 |  | 
| 151 |     hci_filter filter; | 
| 152 |     socklen_t length = sizeof(hci_filter); | 
| 153 |     if (getsockopt(fd: hciSocket, SOL_HCI, HCI_FILTER, optval: &filter, optlen: &length) < 0) { | 
| 154 |         qCWarning(QT_BT_BLUEZ) << "Cannot retrieve HCI filter settings" ; | 
| 155 |         return false; | 
| 156 |     } | 
| 157 |  | 
| 158 |     hci_filter_set_ptype(HCI_ACL_PKT, f: &filter); | 
| 159 |     hci_filter_all_events(f: &filter); | 
| 160 |  | 
| 161 |     if (setsockopt(fd: hciSocket, SOL_HCI, HCI_FILTER, optval: &filter, optlen: sizeof(hci_filter)) < 0) { | 
| 162 |         qCWarning(QT_BT_BLUEZ) << "Could not set HCI socket options:"  << strerror(errno); | 
| 163 |         return false; | 
| 164 |     } | 
| 165 |  | 
| 166 |     return true; | 
| 167 | } | 
| 168 |  | 
| 169 | bool HciManager::sendCommand(QBluezConst::OpCodeGroupField ogf, QBluezConst::OpCodeCommandField ocf, const QByteArray ¶meters) | 
| 170 | { | 
| 171 |     qCDebug(QT_BT_BLUEZ) << "sending command; ogf:"  << ogf << "ocf:"  << ocf; | 
| 172 |     quint8 packetType = HCI_COMMAND_PKT; | 
| 173 |     hci_command_hdr command = { | 
| 174 |         opCodePack(ogf, ocf), | 
| 175 |         .plen: static_cast<uint8_t>(parameters.size()) | 
| 176 |     }; | 
| 177 |     static_assert(sizeof command == 3, "unexpected struct size" ); | 
| 178 |     struct iovec iv[3]; | 
| 179 |     iv[0].iov_base = &packetType; | 
| 180 |     iv[0].iov_len  = 1; | 
| 181 |     iv[1].iov_base = &command; | 
| 182 |     iv[1].iov_len  = sizeof command; | 
| 183 |     int ivn = 2; | 
| 184 |     if (!parameters.isEmpty()) { | 
| 185 |         iv[2].iov_base = const_cast<char *>(parameters.constData()); // const_cast is safe, since iov_base will not get modified. | 
| 186 |         iv[2].iov_len  = parameters.size(); | 
| 187 |         ++ivn; | 
| 188 |     } | 
| 189 |     while (writev(fd: hciSocket, iovec: iv, count: ivn) < 0) { | 
| 190 |         if (errno == EAGAIN || errno == EINTR) | 
| 191 |             continue; | 
| 192 |         qCDebug(QT_BT_BLUEZ()) << "hci command failure:"  << strerror(errno); | 
| 193 |         return false; | 
| 194 |     } | 
| 195 |     qCDebug(QT_BT_BLUEZ) << "command sent successfully" ; | 
| 196 |     return true; | 
| 197 | } | 
| 198 |  | 
| 199 | /* | 
| 200 |  * Unsubscribe from all events | 
| 201 |  */ | 
| 202 | void HciManager::stopEvents() | 
| 203 | { | 
| 204 |     if (!isValid()) | 
| 205 |         return; | 
| 206 |  | 
| 207 |     hci_filter filter; | 
| 208 |     hci_filter_clear(f: &filter); | 
| 209 |  | 
| 210 |     if (setsockopt(fd: hciSocket, SOL_HCI, HCI_FILTER, optval: &filter, optlen: sizeof(hci_filter)) < 0) { | 
| 211 |         qCWarning(QT_BT_BLUEZ) << "Could not clear HCI socket options:"  << strerror(errno); | 
| 212 |         return; | 
| 213 |     } | 
| 214 |  | 
| 215 |     runningEvents.clear(); | 
| 216 | } | 
| 217 |  | 
| 218 | QBluetoothAddress HciManager::addressForConnectionHandle(quint16 handle) const | 
| 219 | { | 
| 220 |     if (!isValid()) | 
| 221 |         return QBluetoothAddress(); | 
| 222 |  | 
| 223 |     hci_conn_info *info; | 
| 224 |     hci_conn_list_req *infoList; | 
| 225 |  | 
| 226 |     const int maxNoOfConnections = 20; | 
| 227 |     infoList = (hci_conn_list_req *) | 
| 228 |             malloc(size: sizeof(hci_conn_list_req) + maxNoOfConnections * sizeof(hci_conn_info)); | 
| 229 |  | 
| 230 |     if (!infoList) | 
| 231 |         return QBluetoothAddress(); | 
| 232 |  | 
| 233 |     QScopedPointer<hci_conn_list_req, QScopedPointerPodDeleter> p(infoList); | 
| 234 |     p->conn_num = maxNoOfConnections; | 
| 235 |     p->dev_id = hciDev; | 
| 236 |     info = p->conn_info; | 
| 237 |  | 
| 238 |     if (ioctl(fd: hciSocket, HCIGETCONNLIST, (void *) infoList) < 0) { | 
| 239 |         qCWarning(QT_BT_BLUEZ) << "Cannot retrieve connection list" ; | 
| 240 |         return QBluetoothAddress(); | 
| 241 |     } | 
| 242 |  | 
| 243 |     for (int i = 0; i < infoList->conn_num; i++) { | 
| 244 |         if (info[i].handle == handle) | 
| 245 |             return QBluetoothAddress(convertAddress(from: info[i].bdaddr.b)); | 
| 246 |     } | 
| 247 |  | 
| 248 |     return QBluetoothAddress(); | 
| 249 | } | 
| 250 |  | 
| 251 | QList<quint16> HciManager::activeLowEnergyConnections() const | 
| 252 | { | 
| 253 |     if (!isValid()) | 
| 254 |         return QList<quint16>(); | 
| 255 |  | 
| 256 |     hci_conn_info *info; | 
| 257 |     hci_conn_list_req *infoList; | 
| 258 |  | 
| 259 |     const int maxNoOfConnections = 20; | 
| 260 |     infoList = (hci_conn_list_req *) | 
| 261 |             malloc(size: sizeof(hci_conn_list_req) + maxNoOfConnections * sizeof(hci_conn_info)); | 
| 262 |  | 
| 263 |     if (!infoList) | 
| 264 |         return QList<quint16>(); | 
| 265 |  | 
| 266 |     QScopedPointer<hci_conn_list_req, QScopedPointerPodDeleter> p(infoList); | 
| 267 |     p->conn_num = maxNoOfConnections; | 
| 268 |     p->dev_id = hciDev; | 
| 269 |     info = p->conn_info; | 
| 270 |  | 
| 271 |     if (ioctl(fd: hciSocket, HCIGETCONNLIST, (void *) infoList) < 0) { | 
| 272 |         qCWarning(QT_BT_BLUEZ) << "Cannot retrieve connection list" ; | 
| 273 |         return QList<quint16>(); | 
| 274 |     } | 
| 275 |  | 
| 276 |     QList<quint16> activeLowEnergyHandles; | 
| 277 |     for (int i = 0; i < infoList->conn_num; i++) { | 
| 278 |         switch (info[i].type) { | 
| 279 |         case SCO_LINK: | 
| 280 |         case ACL_LINK: | 
| 281 |         case ESCO_LINK: | 
| 282 |             continue; | 
| 283 |         case LE_LINK: | 
| 284 |             activeLowEnergyHandles.append(t: info[i].handle); | 
| 285 |             break; | 
| 286 |         default: | 
| 287 |             qCWarning(QT_BT_BLUEZ) << "Unknown active connection type:"  << Qt::hex << info[i].type; | 
| 288 |             break; | 
| 289 |         } | 
| 290 |     } | 
| 291 |  | 
| 292 |     return activeLowEnergyHandles; | 
| 293 | } | 
| 294 |  | 
| 295 | quint16 forceIntervalIntoRange(double connectionInterval) | 
| 296 | { | 
| 297 |     return qMin<double>(a: qMax<double>(a: 7.5, b: connectionInterval), b: 4000) / 1.25; | 
| 298 | } | 
| 299 |  | 
| 300 | struct ConnectionUpdateData { | 
| 301 |     quint16 minInterval; | 
| 302 |     quint16 maxInterval; | 
| 303 |     quint16 slaveLatency; | 
| 304 |     quint16 timeout; | 
| 305 | }; | 
| 306 | ConnectionUpdateData connectionUpdateData(const QLowEnergyConnectionParameters ¶ms) | 
| 307 | { | 
| 308 |     ConnectionUpdateData data; | 
| 309 |     const quint16 minInterval = forceIntervalIntoRange(connectionInterval: params.minimumInterval()); | 
| 310 |     const quint16 maxInterval = forceIntervalIntoRange(connectionInterval: params.maximumInterval()); | 
| 311 |     data.minInterval = qToLittleEndian(source: minInterval); | 
| 312 |     data.maxInterval = qToLittleEndian(source: maxInterval); | 
| 313 |     const quint16 latency = qMax<quint16>(a: 0, b: qMin<quint16>(a: params.latency(), b: 499)); | 
| 314 |     data.slaveLatency = qToLittleEndian(source: latency); | 
| 315 |     const quint16 timeout | 
| 316 |             = qMax<quint16>(a: 100, b: qMin<quint16>(a: 32000, b: params.supervisionTimeout())) / 10; | 
| 317 |     data.timeout = qToLittleEndian(source: timeout); | 
| 318 |     return data; | 
| 319 | } | 
| 320 |  | 
| 321 | bool HciManager::sendConnectionUpdateCommand(quint16 handle, | 
| 322 |                                              const QLowEnergyConnectionParameters ¶ms) | 
| 323 | { | 
| 324 |     struct CommandParams { | 
| 325 |         quint16 handle; | 
| 326 |         ConnectionUpdateData data; | 
| 327 |         quint16 minCeLength; | 
| 328 |         quint16 maxCeLength; | 
| 329 |     } commandParams; | 
| 330 |     commandParams.handle = qToLittleEndian(source: handle); | 
| 331 |     commandParams.data = connectionUpdateData(params); | 
| 332 |     commandParams.minCeLength = 0; | 
| 333 |     commandParams.maxCeLength = qToLittleEndian(source: quint16(0xffff)); | 
| 334 |     const QByteArray data = QByteArray::fromRawData(data: reinterpret_cast<char *>(&commandParams), | 
| 335 |                                                     size: sizeof commandParams); | 
| 336 |     return sendCommand(ogf: QBluezConst::OgfLinkControl, ocf: QBluezConst::OcfLeConnectionUpdate, parameters: data); | 
| 337 | } | 
| 338 |  | 
| 339 | bool HciManager::sendConnectionParameterUpdateRequest(quint16 handle, | 
| 340 |                                                       const QLowEnergyConnectionParameters ¶ms) | 
| 341 | { | 
| 342 |     ConnectionUpdateData connUpdateData = connectionUpdateData(params); | 
| 343 |  | 
| 344 |     // Vol 3, part A, 4 | 
| 345 |     struct SignalingPacket { | 
| 346 |         quint8 code; | 
| 347 |         quint8 identifier; | 
| 348 |         quint16 length; | 
| 349 |     } signalingPacket; | 
| 350 |     signalingPacket.code = 0x12; | 
| 351 |     signalingPacket.identifier = ++sigPacketIdentifier; | 
| 352 |     const quint16 sigPacketLen = sizeof connUpdateData; | 
| 353 |     signalingPacket.length = qToLittleEndian(source: sigPacketLen); | 
| 354 |  | 
| 355 |     L2CapHeader ; | 
| 356 |     const quint16  = sizeof signalingPacket + sigPacketLen; | 
| 357 |     l2CapHeader.length = qToLittleEndian(source: l2CapHeaderLen); | 
| 358 |     l2CapHeader.channelId = qToLittleEndian(source: quint16(SIGNALING_CHANNEL_ID)); | 
| 359 |  | 
| 360 |     // Vol 2, part E, 5.4.2 | 
| 361 |     AclData aclData; | 
| 362 |     aclData.handle = qToLittleEndian(source: handle); // Works because the next two values are zero. | 
| 363 |     aclData.pbFlag = 0; | 
| 364 |     aclData.bcFlag = 0; | 
| 365 |     aclData.dataLen = qToLittleEndian(source: quint16(sizeof l2CapHeader + l2CapHeaderLen)); | 
| 366 |  | 
| 367 |     struct iovec iv[5]; | 
| 368 |     quint8 packetType = HCI_ACL_PKT; | 
| 369 |     iv[0].iov_base = &packetType; | 
| 370 |     iv[0].iov_len  = 1; | 
| 371 |     iv[1].iov_base = &aclData; | 
| 372 |     iv[1].iov_len  = sizeof aclData; | 
| 373 |     iv[2].iov_base = &l2CapHeader; | 
| 374 |     iv[2].iov_len = sizeof l2CapHeader; | 
| 375 |     iv[3].iov_base = &signalingPacket; | 
| 376 |     iv[3].iov_len = sizeof signalingPacket; | 
| 377 |     iv[4].iov_base = &connUpdateData; | 
| 378 |     iv[4].iov_len = sizeof connUpdateData; | 
| 379 |     while (writev(fd: hciSocket, iovec: iv, count: sizeof iv / sizeof *iv) < 0) { | 
| 380 |         if (errno == EAGAIN || errno == EINTR) | 
| 381 |             continue; | 
| 382 |         qCDebug(QT_BT_BLUEZ()) << "failure writing HCI ACL packet:"  << strerror(errno); | 
| 383 |         return false; | 
| 384 |     } | 
| 385 |     qCDebug(QT_BT_BLUEZ) << "Connection Update Request packet sent successfully" ; | 
| 386 |     return true; | 
| 387 | } | 
| 388 |  | 
| 389 | /*! | 
| 390 |  * Process all incoming HCI events. Function cannot process anything else but events. | 
| 391 |  */ | 
| 392 | void HciManager::_q_readNotify() | 
| 393 | { | 
| 394 |     unsigned char buffer[qMax<int>(HCI_MAX_EVENT_SIZE, b: sizeof(AclData))]; | 
| 395 |  | 
| 396 |     const auto size = ::read(fd: hciSocket, buf: buffer, nbytes: sizeof(buffer)); | 
| 397 |     if (size < 0) { | 
| 398 |         if (errno != EAGAIN && errno != EINTR) | 
| 399 |             qCWarning(QT_BT_BLUEZ) << "Failed reading HCI events:"  << qt_error_string(errno); | 
| 400 |  | 
| 401 |         return; | 
| 402 |     } | 
| 403 |  | 
| 404 |     switch (buffer[0]) { | 
| 405 |     case HCI_EVENT_PKT: | 
| 406 |         handleHciEventPacket(data: buffer + 1, size: size - 1); | 
| 407 |         break; | 
| 408 |     case HCI_ACL_PKT: | 
| 409 |         handleHciAclPacket(data: buffer + 1, size: size - 1); | 
| 410 |         break; | 
| 411 |     default: | 
| 412 |         qCWarning(QT_BT_BLUEZ) << "Ignoring unexpected HCI packet type"  << buffer[0]; | 
| 413 |     } | 
| 414 | } | 
| 415 |  | 
| 416 | void HciManager::handleHciEventPacket(const quint8 *data, int size) | 
| 417 | { | 
| 418 |     if (size < HCI_EVENT_HDR_SIZE) { | 
| 419 |         qCWarning(QT_BT_BLUEZ) << "Unexpected HCI event packet size:"  << size; | 
| 420 |         return; | 
| 421 |     } | 
| 422 |  | 
| 423 |     hci_event_hdr * = (hci_event_hdr *) data; | 
| 424 |  | 
| 425 |     size -= HCI_EVENT_HDR_SIZE; | 
| 426 |     data += HCI_EVENT_HDR_SIZE; | 
| 427 |  | 
| 428 |     if (header->plen != size) { | 
| 429 |         qCWarning(QT_BT_BLUEZ) << "Invalid HCI event packet size" ; | 
| 430 |         return; | 
| 431 |     } | 
| 432 |  | 
| 433 |     qCDebug(QT_BT_BLUEZ) << "HCI event triggered, type:"  << (HciManager::HciEvent)header->evt | 
| 434 |                          << "type code:"  << Qt::hex << header->evt; | 
| 435 |  | 
| 436 |     switch ((HciManager::HciEvent)header->evt) { | 
| 437 |     case HciEvent::EVT_ENCRYPT_CHANGE: { | 
| 438 |         const evt_encrypt_change *event = (evt_encrypt_change *) data; | 
| 439 |         qCDebug(QT_BT_BLUEZ) << "HCI Encrypt change, status:"  | 
| 440 |                              << (event->status == 0 ? "Success"  : "Failed" ) | 
| 441 |                              << "handle:"  << Qt::hex << event->handle | 
| 442 |                              << "encrypt:"  << event->encrypt; | 
| 443 |  | 
| 444 |         QBluetoothAddress remoteDevice = addressForConnectionHandle(handle: event->handle); | 
| 445 |         if (!remoteDevice.isNull()) | 
| 446 |             emit encryptionChangedEvent(address: remoteDevice, wasSuccess: event->status == 0); | 
| 447 |     } break; | 
| 448 |     case HciEvent::EVT_CMD_COMPLETE: { | 
| 449 |         auto * const event = reinterpret_cast<const evt_cmd_complete *>(data); | 
| 450 |         static_assert(sizeof *event == 3, "unexpected struct size" ); | 
| 451 |  | 
| 452 |         // There is always a status byte right after the generic structure. | 
| 453 |         Q_ASSERT(size > static_cast<int>(sizeof *event)); | 
| 454 |         const quint8 status = data[sizeof *event]; | 
| 455 |         const auto additionalData = QByteArray(reinterpret_cast<const char *>(data) | 
| 456 |                                                + sizeof *event + 1, size - sizeof *event - 1); | 
| 457 |         emit commandCompleted(opCode: event->opcode, status, data: additionalData); | 
| 458 |     } break; | 
| 459 |     case HciEvent::EVT_LE_META_EVENT: | 
| 460 |         handleLeMetaEvent(data); | 
| 461 |         break; | 
| 462 |     default: | 
| 463 |         break; | 
| 464 |     } | 
| 465 |  | 
| 466 | } | 
| 467 |  | 
| 468 | void HciManager::handleHciAclPacket(const quint8 *data, int size) | 
| 469 | { | 
| 470 |     if (size < int(sizeof(AclData))) { | 
| 471 |         qCWarning(QT_BT_BLUEZ) << "Unexpected HCI ACL packet size" ; | 
| 472 |         return; | 
| 473 |     } | 
| 474 |  | 
| 475 |     quint16 rawAclData[sizeof(AclData) / sizeof(quint16)]; | 
| 476 |     rawAclData[0] = bt_get_le16(ptr: data); | 
| 477 |     rawAclData[1] = bt_get_le16(ptr: data + sizeof(quint16)); | 
| 478 |     const AclData *aclData = reinterpret_cast<AclData *>(rawAclData); | 
| 479 |     data += sizeof *aclData; | 
| 480 |     size -= sizeof *aclData; | 
| 481 |  | 
| 482 |     // Consider only directed, complete messages. | 
| 483 |     if ((aclData->pbFlag != 0 && aclData->pbFlag != 2) || aclData->bcFlag != 0) | 
| 484 |         return; | 
| 485 |  | 
| 486 |     if (size < aclData->dataLen) { | 
| 487 |         qCWarning(QT_BT_BLUEZ) << "HCI ACL packet data size"  << size | 
| 488 |                                << "is smaller than specified size"  << aclData->dataLen; | 
| 489 |         return; | 
| 490 |     } | 
| 491 |  | 
| 492 | //    qCDebug(QT_BT_BLUEZ) << "handle:" << aclData->handle << "PB:" << aclData->pbFlag | 
| 493 | //                         << "BC:" << aclData->bcFlag << "data len:" << aclData->dataLen; | 
| 494 |  | 
| 495 |     if (size < int(sizeof(L2CapHeader))) { | 
| 496 |         qCWarning(QT_BT_BLUEZ) << "Unexpected HCI ACL packet size" ; | 
| 497 |         return; | 
| 498 |     } | 
| 499 |     L2CapHeader  = *reinterpret_cast<const L2CapHeader*>(data); | 
| 500 |     l2CapHeader.channelId = qFromLittleEndian(source: l2CapHeader.channelId); | 
| 501 |     l2CapHeader.length = qFromLittleEndian(source: l2CapHeader.length); | 
| 502 |     data += sizeof l2CapHeader; | 
| 503 |     size -= sizeof l2CapHeader; | 
| 504 |     if (size < l2CapHeader.length) { | 
| 505 |         qCWarning(QT_BT_BLUEZ) << "L2Cap payload size"  << size << "is smaller than specified size"  | 
| 506 |                                << l2CapHeader.length; | 
| 507 |         return; | 
| 508 |     } | 
| 509 | //    qCDebug(QT_BT_BLUEZ) << "l2cap channel id:" << l2CapHeader.channelId | 
| 510 | //                         << "payload length:" << l2CapHeader.length; | 
| 511 |     if (l2CapHeader.channelId != SECURITY_CHANNEL_ID) | 
| 512 |         return; | 
| 513 |     if (*data != 0xa) // "Signing Information". Spec v4.2, Vol 3, Part H, 3.6.6 | 
| 514 |         return; | 
| 515 |     if (size != 17) { | 
| 516 |         qCWarning(QT_BT_BLUEZ) << "Unexpected key size"  << size << "in Signing Information packet" ; | 
| 517 |         return; | 
| 518 |     } | 
| 519 |     BluezUint128 csrk; | 
| 520 |     memcpy(dest: &csrk, src: data + 1, n: sizeof csrk); | 
| 521 |     const bool isRemoteKey = aclData->pbFlag == 2; | 
| 522 |     emit signatureResolvingKeyReceived(connHandle: aclData->handle, remoteKey: isRemoteKey, csrk); | 
| 523 | } | 
| 524 |  | 
| 525 | void HciManager::handleLeMetaEvent(const quint8 *data) | 
| 526 | { | 
| 527 |     // Spec v5.3, Vol 4, part E, 7.7.65.* | 
| 528 |     switch (*data) { | 
| 529 |     case 0x1: // HCI_LE_Connection_Complete | 
| 530 |     case 0xA: // HCI_LE_Enhanced_Connection_Complete | 
| 531 |     { | 
| 532 |         const quint16 handle = bt_get_le16(ptr: data + 2); | 
| 533 |         emit connectionComplete(handle); | 
| 534 |         break; | 
| 535 |     } | 
| 536 |     case 0x3: { | 
| 537 |         // TODO: From little endian! | 
| 538 |         struct ConnectionUpdateData { | 
| 539 |             quint8 status; | 
| 540 |             quint16 handle; | 
| 541 |             quint16 interval; | 
| 542 |             quint16 latency; | 
| 543 |             quint16 timeout; | 
| 544 |         } __attribute((packed)); | 
| 545 |         const auto * const updateData | 
| 546 |                 = reinterpret_cast<const ConnectionUpdateData *>(data + 1); | 
| 547 |         if (updateData->status == 0) { | 
| 548 |             QLowEnergyConnectionParameters params; | 
| 549 |             const double interval = qFromLittleEndian(source: updateData->interval) * 1.25; | 
| 550 |             params.setIntervalRange(minimum: interval, maximum: interval); | 
| 551 |             params.setLatency(qFromLittleEndian(source: updateData->latency)); | 
| 552 |             params.setSupervisionTimeout(qFromLittleEndian(source: updateData->timeout) * 10); | 
| 553 |             emit connectionUpdate(handle: qFromLittleEndian(source: updateData->handle), parameters: params); | 
| 554 |         } | 
| 555 |         break; | 
| 556 |     } | 
| 557 |     default: | 
| 558 |         break; | 
| 559 |     } | 
| 560 | } | 
| 561 |  | 
| 562 | QT_END_NAMESPACE | 
| 563 |  | 
| 564 | #include "moc_hcimanager_p.cpp" | 
| 565 |  |