• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
63namespace device {
64
65BluetoothL2capChannelMac::BluetoothL2capChannelMac(
66    BluetoothSocketMac* socket,
67    IOBluetoothL2CAPChannel* channel)
68    : channel_(channel),
69      delegate_(nil) {
70  SetSocket(socket);
71}
72
73BluetoothL2capChannelMac::~BluetoothL2capChannelMac() {
74  [channel_ setDelegate:nil];
75  [channel_ closeChannel];
76}
77
78// static
79scoped_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
105void 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
118IOBluetoothDevice* BluetoothL2capChannelMac::GetDevice() {
119  return [channel_ getDevice];
120}
121
122uint16_t BluetoothL2capChannelMac::GetOutgoingMTU() {
123  return [channel_ outgoingMTU];
124}
125
126IOReturn 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
133void 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
148void BluetoothL2capChannelMac::OnChannelClosed(
149    IOBluetoothL2CAPChannel* channel) {
150  DCHECK_EQ(channel_, channel);
151  socket()->OnChannelClosed();
152}
153
154void BluetoothL2capChannelMac::OnChannelDataReceived(
155    IOBluetoothL2CAPChannel* channel,
156    void* data,
157    size_t length) {
158  DCHECK_EQ(channel_, channel);
159  socket()->OnChannelDataReceived(data, length);
160}
161
162void 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