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(delegate, Q_FUNC_INFO, "invalid delegate (null)");
146
147 // Timer can be null, if we're running as a server, and the channel is
148 // opened as a result of a client connection
149 if (channelOpenTimer)
150 channelOpenTimer->stop();
151 if (error != kIOReturnSuccess) {
152 delegate->setChannelError(error);
153 delegate->channelClosed();
154 } else {
155 connected = true;
156 delegate->channelOpenComplete();
157 }
158}
159
160- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel
161{
162 Q_UNUSED(rfcommChannel);
163
164 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
165 delegate->channelClosed();
166 connected = false;
167}
168
169- (void)rfcommChannelControlSignalsChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
170{
171 Q_UNUSED(rfcommChannel);
172}
173
174- (void)rfcommChannelFlowControlChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
175{
176 Q_UNUSED(rfcommChannel);
177}
178
179- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
180 refcon:(void*)refcon status:(IOReturn)error
181{
182 Q_UNUSED(rfcommChannel);
183 Q_UNUSED(refcon);
184
185 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
186
187 if (error != kIOReturnSuccess)
188 delegate->setChannelError(error);
189 else
190 delegate->writeComplete();
191}
192
193- (void)rfcommChannelQueueSpaceAvailable:(IOBluetoothRFCOMMChannel*)rfcommChannel
194{
195 Q_UNUSED(rfcommChannel);
196}
197
198- (BluetoothRFCOMMChannelID)getChannelID
199{
200 if (channel)
201 return [channel getChannelID];
202
203 return 0;
204}
205
206- (BluetoothDeviceAddress)peerAddress
207{
208 const BluetoothDeviceAddress *const addr = device ? [device getAddress]
209 : nullptr;
210 if (addr)
211 return *addr;
212
213 return BluetoothDeviceAddress();
214}
215
216- (NSString *)peerName
217{
218 if (device)
219 return device.name;
220
221 return nil;
222}
223
224- (BluetoothRFCOMMMTU)getMTU
225{
226 if (channel)
227 return [channel getMTU];
228
229 return 0;
230}
231
232- (IOReturn) writeSync:(void*)data length:(UInt16)length
233{
234 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
235 Q_ASSERT_X(length, Q_FUNC_INFO, "invalid data size");
236 Q_ASSERT_X(connected && channel, Q_FUNC_INFO, "invalid RFCOMM channel");
237
238 return [channel writeSync:data length:length];
239}
240
241- (IOReturn) writeAsync:(void*)data length:(UInt16)length
242{
243 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
244 Q_ASSERT_X(length, Q_FUNC_INFO, "invalid data size");
245 Q_ASSERT_X(connected && channel, Q_FUNC_INFO, "invalid RFCOMM channel");
246
247 return [channel writeAsync:data length:length refcon:nullptr];
248}
249
250- (void)handleChannelOpenTimeout
251{
252 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)");
253 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid RFCOMM channel");
254
255 delegate->setChannelError(kIOReturnNotOpen);
256 [channel closeChannel];
257 delegate->channelClosed();
258}
259
260@end
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
static constexpr auto channelOpenTimeoutMs
Q_FORWARD_DECLARE_OBJC_CLASS(NSString)