1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the QtBluetooth module of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include "osxbtl2capchannel_p.h" 41#include "qbluetoothaddress.h" 42#include "osxbtutility_p.h" 43#include "btdelegates_p.h" 44 45#include <QtCore/qloggingcategory.h> 46#include <QtCore/qdebug.h> 47 48QT_USE_NAMESPACE 49 50@implementation QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel) 51{ 52 QT_PREPEND_NAMESPACE(DarwinBluetooth)::ChannelDelegate *delegate; 53 IOBluetoothDevice *device; 54 IOBluetoothL2CAPChannel *channel; 55 bool connected; 56} 57 58- (id)initWithDelegate:(DarwinBluetooth::ChannelDelegate *)aDelegate 59{ 60 Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); 61 62 if (self = [super init]) { 63 delegate = aDelegate; 64 device = nil; 65 channel = nil; 66 connected = false; 67 } 68 69 return self; 70} 71 72- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(DarwinBluetooth::ChannelDelegate) *)aDelegate 73 channel:(IOBluetoothL2CAPChannel *)aChannel 74{ 75 // This type of channel does not require connect, it's created with 76 // already open channel. 77 Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); 78 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); 79 80 if (self = [super init]) { 81 delegate = aDelegate; 82 channel = [aChannel retain]; 83 [channel setDelegate:self]; 84 device = [channel.device retain]; 85 connected = true; 86 } 87 88 return self; 89} 90 91- (void)dealloc 92{ 93 // TODO: test if this implementation works at all! 94 if (channel) { 95 [channel setDelegate:nil]; 96 // From Apple's docs: 97 // "This method may only be called by the client that opened the channel 98 // in the first place. In the future asynchronous and synchronous versions 99 // will be provided that let the client know when the close process has been finished." 100 [channel closeChannel]; 101 [channel release]; 102 } 103 104 [device release]; 105 106 [super dealloc]; 107} 108 109- (IOReturn)connectAsyncToDevice:(const QBluetoothAddress &)address 110 withPSM:(BluetoothL2CAPChannelID)psm 111{ 112 if (address.isNull()) { 113 qCCritical(QT_BT_OSX) << "invalid peer address"; 114 return kIOReturnNoDevice; 115 } 116 117 // Can never be called twice. 118 if (connected || device || channel) { 119 qCCritical(QT_BT_OSX) << "connection is already active"; 120 return kIOReturnStillOpen; 121 } 122 123 QT_BT_MAC_AUTORELEASEPOOL; 124 125 const BluetoothDeviceAddress iobtAddress = OSXBluetooth::iobluetooth_address(address); 126 device = [IOBluetoothDevice deviceWithAddress:&iobtAddress]; 127 if (!device) { 128 qCCritical(QT_BT_OSX) << "failed to create a device"; 129 return kIOReturnNoDevice; 130 } 131 132 const IOReturn status = [device openL2CAPChannelAsync:&channel withPSM:psm delegate:self]; 133 if (status != kIOReturnSuccess) { 134 qCCritical(QT_BT_OSX) << "failed to open L2CAP channel"; 135 // device is still autoreleased. 136 device = nil; 137 return status; 138 } 139 140 [channel retain];// What if we're closed already? 141 [device retain]; 142 143 return kIOReturnSuccess; 144} 145 146// IOBluetoothL2CAPChannelDelegate: 147 148- (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel 149 data:(void *)dataPointer length:(size_t)dataLength 150{ 151 Q_UNUSED(l2capChannel) 152 153 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); 154 155 if (dataPointer && dataLength) 156 delegate->readChannelData(dataPointer, dataLength); 157} 158 159- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*) 160 l2capChannel status:(IOReturn)error 161{ 162 Q_UNUSED(l2capChannel) 163 164 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); 165 166 if (error != kIOReturnSuccess) { 167 delegate->setChannelError(error); 168 } else { 169 connected = true; 170 delegate->channelOpenComplete(); 171 } 172} 173 174- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel 175{ 176 Q_UNUSED(l2capChannel) 177 178 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); 179 delegate->channelClosed(); 180 connected = false; 181} 182 183- (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel 184{ 185 Q_UNUSED(l2capChannel) 186} 187 188- (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel 189 refcon:(void*)refcon status:(IOReturn)error 190{ 191 Q_UNUSED(l2capChannel) 192 Q_UNUSED(refcon) 193 194 Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); 195 196 if (error != kIOReturnSuccess) 197 delegate->setChannelError(error); 198 else 199 delegate->writeComplete(); 200} 201 202- (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel 203{ 204 Q_UNUSED(l2capChannel) 205} 206 207// Aux. methods. 208- (BluetoothL2CAPPSM)getPSM 209{ 210 if (channel) 211 return channel.PSM; 212 213 return 0; 214} 215 216- (BluetoothDeviceAddress)peerAddress 217{ 218 const BluetoothDeviceAddress *const addr = device ? [device getAddress] 219 : nullptr; 220 if (addr) 221 return *addr; 222 223 return BluetoothDeviceAddress(); 224} 225 226- (NSString *)peerName 227{ 228 if (device) 229 return device.name; 230 231 return nil; 232} 233 234- (bool)isConnected 235{ 236 return connected; 237} 238 239- (IOReturn) writeSync:(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 L2CAP channel"); 244 245 return [channel writeSync:data length:length]; 246} 247 248- (IOReturn) writeAsync:(void*)data length:(UInt16)length 249{ 250 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)"); 251 Q_ASSERT_X(length, Q_FUNC_INFO, "invalid data size"); 252 Q_ASSERT_X(connected && channel, Q_FUNC_INFO, "invalid L2CAP channel"); 253 254 return [channel writeAsync:data length:length refcon:nullptr]; 255} 256 257 258@end 259