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
20QT_BEGIN_NAMESPACE
21
22Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
23
24HciManager::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
59HciManager::~HciManager()
60{
61 if (hciSocket >= 0)
62 ::close(fd: hciSocket);
63
64}
65
66bool HciManager::isValid() const
67{
68 if (hciSocket && hciDev >= 0)
69 return true;
70 return false;
71}
72
73int 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 */
117bool 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
146bool 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
169bool HciManager::sendCommand(QBluezConst::OpCodeGroupField ogf, QBluezConst::OpCodeCommandField ocf, const QByteArray &parameters)
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 */
202void 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
218QBluetoothAddress 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
251QList<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
295quint16 forceIntervalIntoRange(double connectionInterval)
296{
297 return qMin<double>(a: qMax<double>(a: 7.5, b: connectionInterval), b: 4000) / 1.25;
298}
299
300struct ConnectionUpdateData {
301 quint16 minInterval;
302 quint16 maxInterval;
303 quint16 slaveLatency;
304 quint16 timeout;
305};
306ConnectionUpdateData connectionUpdateData(const QLowEnergyConnectionParameters &params)
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
321bool HciManager::sendConnectionUpdateCommand(quint16 handle,
322 const QLowEnergyConnectionParameters &params)
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
339bool HciManager::sendConnectionParameterUpdateRequest(quint16 handle,
340 const QLowEnergyConnectionParameters &params)
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 l2CapHeader;
356 const quint16 l2CapHeaderLen = 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 */
392void 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
416void 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 *header = (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
468void 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 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
525void 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
562QT_END_NAMESPACE
563
564#include "moc_hcimanager_p.cpp"
565

source code of qtconnectivity/src/bluetooth/bluez/hcimanager.cpp