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
btrfcommchannel.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7#include "btutility_p.h"
8
9#include <QtCore/qtimer.h>
10
11QT_USE_NAMESPACE
12
13namespace {
14
15static constexpr auto channelOpenTimeoutMs = std::chrono::milliseconds{20000};
16
17} // namespace
18
19@implementation DarwinBTRFCOMMChannel
20{
21 QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate;
22 IOBluetoothDevice *device;
23 IOBluetoothRFCOMMChannel *channel;
24 bool connected;
25 std::unique_ptr<QTimer> channelOpenTimer;
26}
27
28- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate
29{
30 Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
31
32 if (self = [super init]) {
33 delegate = aDelegate;
34 device = nil;
35 channel = nil;
36 connected = false;
37 }
38
39 return self;
40}
41
42- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate
43 channel:(IOBluetoothRFCOMMChannel *)aChannel
44{
45 // This type of channel does not require connect, it's created with
46 // already open channel.
47 Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)");
48 Q_ASSERT_X(aChannel, Q_FUNC_INFO, "invalid channel (nil)");
49
50 if (self = [super init]) {
51 delegate = aDelegate;
52 channel = [aChannel retain];
53 [channel setDelegate:self];
54 device = [[channel getDevice] retain];
55 connected = true;
56 }
57
58 return self;
59}
60
61- (void)dealloc
62{
63 if (channel) {
64 [channel setDelegate:nil];
65 [channel closeChannel];
66 [channel release];
67 }
68
69 [device release];
70
71 [super dealloc];
72}
73
74// A single async connection (you can not reuse this object).
75- (IOReturn)connectAsyncToDevice:(const QBluetoothAddress &)address
76 withChannelID:(BluetoothRFCOMMChannelID)channelID
77{
78 if (address.isNull()) {
79 qCCritical(QT_BT_DARWIN) << "invalid peer address";
80 return kIOReturnNoDevice;
81 }
82
83 // Can never be called twice.
84 if (connected || device || channel) {
85 qCCritical(QT_BT_DARWIN) << "connection is already active";
86 return kIOReturnStillOpen;
87 }
88
90
91 const BluetoothDeviceAddress iobtAddress = DarwinBluetooth::iobluetooth_address(address);
92 device = [IOBluetoothDevice deviceWithAddress:&iobtAddress];
93 if (!device) { // TODO: do I always check this BTW??? Apple's docs say nothing about nil.
94 qCCritical(QT_BT_DARWIN) << "failed to create a device";
95 return kIOReturnNoDevice;
96 }
97
98 const IOReturn status = [device openRFCOMMChannelAsync:&channel
99 withChannelID:channelID delegate:self];
100 if (status != kIOReturnSuccess) {
101 qCCritical(QT_BT_DARWIN) << "failed to open RFCOMM channel";
102 // device is still autoreleased.
103 device = nil;
104 return status;
105 }
106
107 if (!channelOpenTimer) {
108 channelOpenTimer.reset(new QTimer);
109 QObject::connect(channelOpenTimer.get(), &QTimer::timeout,
110 channelOpenTimer.get(), [self]() {
111 qCDebug(QT_BT_DARWIN) << "Could not open the RFCOMM channel within the specified "
112 "timeout. Assuming that the remote device is not available.";
113 [self handleChannelOpenTimeout];
114 });
115 channelOpenTimer->setSingleShot(true);
116 }
117 channelOpenTimer->start(channelOpenTimeoutMs);
118
119 [channel retain];// What if we're closed already?
120 [device retain];
121
122 return kIOReturnSuccess;
123}
124
125- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
126 data:(void *)dataPointer length:(size_t)dataLength
127{
128 Q_UNUSED(rfcommChannel);
129
130 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
131
132 // Not sure if it can ever happen and if
133 // assert is better.
134 if (!dataPointer || !dataLength)
135 return;
136
137 delegate->readChannelData(dataPointer, dataLength);
138}
139
140- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
141 status:(IOReturn)error
142{
143 Q_UNUSED(rfcommChannel);
144
145 Q_ASSERT_X(channelOpenTimer.get(), Q_FUNC_INFO, "invalid timer (null)");
146 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
147
148 channelOpenTimer->stop();
149 if (error != kIOReturnSuccess) {
150 delegate->setChannelError(error);
151 delegate->channelClosed();
152 } else {
153 connected = true;
154 delegate->channelOpenComplete();
155 }
156}
157
158- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel
159{
160 Q_UNUSED(rfcommChannel);
161
162 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
163 delegate->channelClosed();
164 connected = false;
165}
166
167- (void)rfcommChannelControlSignalsChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
168{
169 Q_UNUSED(rfcommChannel);
170}
171
172- (void)rfcommChannelFlowControlChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
173{
174 Q_UNUSED(rfcommChannel);
175}
176
177- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
178 refcon:(void*)refcon status:(IOReturn)error
179{
180 Q_UNUSED(rfcommChannel);
181 Q_UNUSED(refcon);
182
183 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
184
185 if (error != kIOReturnSuccess)
186 delegate->setChannelError(error);
187 else
188 delegate->writeComplete();
189}
190
191- (void)rfcommChannelQueueSpaceAvailable:(IOBluetoothRFCOMMChannel*)rfcommChannel
192{
193 Q_UNUSED(rfcommChannel);
194}
195
196- (BluetoothRFCOMMChannelID)getChannelID
197{
198 if (channel)
199 return [channel getChannelID];
200
201 return 0;
202}
203
204- (BluetoothDeviceAddress)peerAddress
205{
206 const BluetoothDeviceAddress *const addr = device ? [device getAddress]
207 : nullptr;
208 if (addr)
209 return *addr;
210
211 return BluetoothDeviceAddress();
212}
213
214- (NSString *)peerName
215{
216 if (device)
217 return device.name;
218
219 return nil;
220}
221
222- (BluetoothRFCOMMMTU)getMTU
223{
224 if (channel)
225 return [channel getMTU];
226
227 return 0;
228}
229
230- (IOReturn) writeSync:(void*)data length:(UInt16)length
231{
232 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
233 Q_ASSERT_X(length, Q_FUNC_INFO, "invalid data size");
234 Q_ASSERT_X(connected && channel, Q_FUNC_INFO, "invalid RFCOMM channel");
235
236 return [channel writeSync:data length:length];
237}
238
239- (IOReturn) writeAsync:(void*)data length:(UInt16)length
240{
241 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
242 Q_ASSERT_X(length, Q_FUNC_INFO, "invalid data size");
243 Q_ASSERT_X(connected && channel, Q_FUNC_INFO, "invalid RFCOMM channel");
244
245 return [channel writeAsync:data length:length refcon:nullptr];
246}
247
248- (void)handleChannelOpenTimeout
249{
250 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
251 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid RFCOMM channel");
252
253 delegate->setChannelError(kIOReturnNotOpen);
254 [channel closeChannel];
255 delegate->channelClosed();
256}
257
258@end
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
static constexpr auto channelOpenTimeoutMs
Q_FORWARD_DECLARE_OBJC_CLASS(NSString)