1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/bluetooth/bluetooth_l2cap_channel_mac.h" 6 7 #include "base/logging.h" 8 #include "base/mac/sdk_forward_declarations.h" 9 #include "device/bluetooth/bluetooth_device_mac.h" 10 #include "device/bluetooth/bluetooth_socket_mac.h" 11 12 // A simple delegate class for an open L2CAP channel that forwards methods to 13 // its wrapped |channel_|. 14 @interface BluetoothL2capChannelDelegate 15 : NSObject <IOBluetoothL2CAPChannelDelegate> { 16 @private 17 device::BluetoothL2capChannelMac* channel_; // weak 18 } 19 20 - (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel; 21 22 @end 23 24 @implementation BluetoothL2capChannelDelegate 25 26 - (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel { 27 if ((self = [super init])) 28 channel_ = channel; 29 30 return self; 31 } 32 33 - (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel 34 status:(IOReturn)error { 35 channel_->OnChannelOpenComplete(l2capChannel, error); 36 } 37 38 - (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel 39 refcon:(void*)refcon 40 status:(IOReturn)error { 41 channel_->OnChannelWriteComplete(l2capChannel, refcon, error); 42 } 43 44 - (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel 45 data:(void*)dataPointer 46 length:(size_t)dataLength { 47 channel_->OnChannelDataReceived(l2capChannel, dataPointer, dataLength); 48 } 49 50 - (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel { 51 channel_->OnChannelClosed(l2capChannel); 52 } 53 54 // These methods are marked as optional in the 10.8 SDK, but not in the 10.6 55 // SDK. These empty implementations can be removed once we drop the 10.6 SDK. 56 - (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel { 57 } 58 - (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel { 59 } 60 61 @end 62 63 namespace device { 64 65 BluetoothL2capChannelMac::BluetoothL2capChannelMac( 66 BluetoothSocketMac* socket, 67 IOBluetoothL2CAPChannel* channel) 68 : channel_(channel), 69 delegate_(nil) { 70 SetSocket(socket); 71 } 72 73 BluetoothL2capChannelMac::~BluetoothL2capChannelMac() { 74 [channel_ setDelegate:nil]; 75 [channel_ closeChannel]; 76 } 77 78 // static 79 scoped_ptr<BluetoothL2capChannelMac> BluetoothL2capChannelMac::OpenAsync( 80 BluetoothSocketMac* socket, 81 IOBluetoothDevice* device, 82 BluetoothL2CAPPSM psm, 83 IOReturn* status) { 84 DCHECK(socket); 85 scoped_ptr<BluetoothL2capChannelMac> channel( 86 new BluetoothL2capChannelMac(socket, nil)); 87 88 // Retain the delegate, because IOBluetoothDevice's 89 // |-openL2CAPChannelAsync:withPSM:delegate:| assumes that it can take 90 // ownership of the delegate without calling |-retain| on it... 91 DCHECK(channel->delegate_); 92 [channel->delegate_ retain]; 93 IOBluetoothL2CAPChannel* l2cap_channel; 94 *status = [device openL2CAPChannelAsync:&l2cap_channel 95 withPSM:psm 96 delegate:channel->delegate_]; 97 if (*status == kIOReturnSuccess) 98 channel->channel_.reset([l2cap_channel retain]); 99 else 100 channel.reset(); 101 102 return channel.Pass(); 103 } 104 105 void BluetoothL2capChannelMac::SetSocket(BluetoothSocketMac* socket) { 106 BluetoothChannelMac::SetSocket(socket); 107 if (!this->socket()) 108 return; 109 110 // Now that the socket is set, it's safe to associate a delegate, which can 111 // call back to the socket. 112 DCHECK(!delegate_); 113 delegate_.reset( 114 [[BluetoothL2capChannelDelegate alloc] initWithChannel:this]); 115 [channel_ setDelegate:delegate_]; 116 } 117 118 IOBluetoothDevice* BluetoothL2capChannelMac::GetDevice() { 119 return [channel_ getDevice]; 120 } 121 122 uint16_t BluetoothL2capChannelMac::GetOutgoingMTU() { 123 return [channel_ outgoingMTU]; 124 } 125 126 IOReturn BluetoothL2capChannelMac::WriteAsync(void* data, 127 uint16_t length, 128 void* refcon) { 129 DCHECK_LE(length, GetOutgoingMTU()); 130 return [channel_ writeAsync:data length:length refcon:refcon]; 131 } 132 133 void BluetoothL2capChannelMac::OnChannelOpenComplete( 134 IOBluetoothL2CAPChannel* channel, 135 IOReturn status) { 136 if (channel_) { 137 DCHECK_EQ(channel_, channel); 138 } else { 139 // The (potentially) asynchronous connection occurred synchronously. 140 // Should only be reachable from OpenAsync(). 141 DCHECK_EQ(status, kIOReturnSuccess); 142 } 143 144 socket()->OnChannelOpenComplete( 145 BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status); 146 } 147 148 void BluetoothL2capChannelMac::OnChannelClosed( 149 IOBluetoothL2CAPChannel* channel) { 150 DCHECK_EQ(channel_, channel); 151 socket()->OnChannelClosed(); 152 } 153 154 void BluetoothL2capChannelMac::OnChannelDataReceived( 155 IOBluetoothL2CAPChannel* channel, 156 void* data, 157 size_t length) { 158 DCHECK_EQ(channel_, channel); 159 socket()->OnChannelDataReceived(data, length); 160 } 161 162 void BluetoothL2capChannelMac::OnChannelWriteComplete( 163 IOBluetoothL2CAPChannel* channel, 164 void* refcon, 165 IOReturn status) { 166 // Note: We use "CHECK" below to ensure we never run into unforeseen 167 // occurrences of asynchronous callbacks, which could lead to data 168 // corruption. 169 CHECK_EQ(channel_, channel); 170 socket()->OnChannelWriteComplete(refcon, status); 171 } 172 173 } // namespace device 174