Home | History | Annotate | Download | only in bluetooth
      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