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_rfcomm_channel_mac.h"
6
7#include "base/logging.h"
8#include "device/bluetooth/bluetooth_device_mac.h"
9#include "device/bluetooth/bluetooth_socket_mac.h"
10
11// A simple delegate class for an open RFCOMM channel that forwards methods to
12// its wrapped |channel_|.
13@interface BluetoothRfcommChannelDelegate
14    : NSObject <IOBluetoothRFCOMMChannelDelegate> {
15 @private
16  device::BluetoothRfcommChannelMac* channel_;  // weak
17}
18
19- (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel;
20
21@end
22
23@implementation BluetoothRfcommChannelDelegate
24
25- (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel {
26  if ((self = [super init]))
27    channel_ = channel;
28
29  return self;
30}
31
32- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
33                           status:(IOReturn)error {
34  channel_->OnChannelOpenComplete(rfcommChannel, error);
35}
36
37- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
38                            refcon:(void*)refcon
39                            status:(IOReturn)error {
40  channel_->OnChannelWriteComplete(rfcommChannel, refcon, error);
41}
42
43- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
44                     data:(void*)dataPointer
45                   length:(size_t)dataLength {
46  channel_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
47}
48
49- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
50  channel_->OnChannelClosed(rfcommChannel);
51}
52
53@end
54
55namespace device {
56
57BluetoothRfcommChannelMac::BluetoothRfcommChannelMac(
58    BluetoothSocketMac* socket,
59    IOBluetoothRFCOMMChannel* channel)
60    : channel_(channel),
61      delegate_(nil) {
62  SetSocket(socket);
63}
64
65BluetoothRfcommChannelMac::~BluetoothRfcommChannelMac() {
66  [channel_ setDelegate:nil];
67  [channel_ closeChannel];
68}
69
70// static
71scoped_ptr<BluetoothRfcommChannelMac> BluetoothRfcommChannelMac::OpenAsync(
72    BluetoothSocketMac* socket,
73    IOBluetoothDevice* device,
74    BluetoothRFCOMMChannelID channel_id,
75    IOReturn* status) {
76  DCHECK(socket);
77  scoped_ptr<BluetoothRfcommChannelMac> channel(
78      new BluetoothRfcommChannelMac(socket, nil));
79
80  // Retain the delegate, because IOBluetoothDevice's
81  // |-openRFCOMMChannelAsync:withChannelID:delegate:| assumes that it can take
82  // ownership of the delegate without calling |-retain| on it...
83  DCHECK(channel->delegate_);
84  [channel->delegate_ retain];
85  IOBluetoothRFCOMMChannel* rfcomm_channel;
86  *status = [device openRFCOMMChannelAsync:&rfcomm_channel
87                             withChannelID:channel_id
88                                  delegate:channel->delegate_];
89  if (*status == kIOReturnSuccess) {
90    // Note: No need to retain the |rfcomm_channel| -- the returned channel is
91    // already retained.
92    channel->channel_.reset(rfcomm_channel);
93  } else {
94    channel.reset();
95  }
96
97  return channel.Pass();
98}
99
100void BluetoothRfcommChannelMac::SetSocket(BluetoothSocketMac* socket) {
101  BluetoothChannelMac::SetSocket(socket);
102  if (!this->socket())
103    return;
104
105  // Now that the socket is set, it's safe to associate a delegate, which can
106  // call back to the socket.
107  DCHECK(!delegate_);
108  delegate_.reset(
109      [[BluetoothRfcommChannelDelegate alloc] initWithChannel:this]);
110  [channel_ setDelegate:delegate_];
111}
112
113IOBluetoothDevice* BluetoothRfcommChannelMac::GetDevice() {
114  return [channel_ getDevice];
115}
116
117uint16_t BluetoothRfcommChannelMac::GetOutgoingMTU() {
118  return [channel_ getMTU];
119}
120
121IOReturn BluetoothRfcommChannelMac::WriteAsync(void* data,
122                                               uint16_t length,
123                                               void* refcon) {
124  DCHECK_LE(length, GetOutgoingMTU());
125  return [channel_ writeAsync:data length:length refcon:refcon];
126}
127
128void BluetoothRfcommChannelMac::OnChannelOpenComplete(
129    IOBluetoothRFCOMMChannel* channel,
130    IOReturn status) {
131  if (channel_) {
132    DCHECK_EQ(channel_, channel);
133  } else {
134    // The (potentially) asynchronous connection occurred synchronously.
135    // Should only be reachable from OpenAsync().
136    DCHECK_EQ(status, kIOReturnSuccess);
137  }
138
139  socket()->OnChannelOpenComplete(
140      BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status);
141}
142
143void BluetoothRfcommChannelMac::OnChannelClosed(
144    IOBluetoothRFCOMMChannel* channel) {
145  DCHECK_EQ(channel_, channel);
146  socket()->OnChannelClosed();
147}
148
149void BluetoothRfcommChannelMac::OnChannelDataReceived(
150    IOBluetoothRFCOMMChannel* channel,
151    void* data,
152    size_t length) {
153  DCHECK_EQ(channel_, channel);
154  socket()->OnChannelDataReceived(data, length);
155}
156
157void BluetoothRfcommChannelMac::OnChannelWriteComplete(
158    IOBluetoothRFCOMMChannel* channel,
159    void* refcon,
160    IOReturn status) {
161  // Note: We use "CHECK" below to ensure we never run into unforeseen
162  // occurrences of asynchronous callbacks, which could lead to data
163  // corruption.
164  CHECK_EQ(channel_, channel);
165  socket()->OnChannelWriteComplete(refcon, status);
166}
167
168}  // namespace device
169