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