1// Copyright (c) 2012 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_socket_win.h"
6
7#include <string>
8
9#include "base/logging.h"
10#include "base/memory/ref_counted.h"
11#include "base/strings/sys_string_conversions.h"
12#include "device/bluetooth/bluetooth_init_win.h"
13#include "device/bluetooth/bluetooth_service_record_win.h"
14#include "net/base/io_buffer.h"
15#include "net/base/winsock_init.h"
16
17namespace {
18
19std::string FormatErrorMessage(DWORD error_code) {
20  TCHAR error_msg[1024];
21  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
22                0,
23                error_code,
24                0,
25                error_msg,
26                1024,
27                NULL);
28  return base::SysWideToUTF8(error_msg);
29}
30
31}  // namespace
32
33namespace device {
34
35BluetoothSocketWin::BluetoothSocketWin(SOCKET fd) : fd_(fd) {
36}
37
38BluetoothSocketWin::~BluetoothSocketWin() {
39  closesocket(fd_);
40}
41
42// static
43scoped_refptr<BluetoothSocket> BluetoothSocketWin::CreateBluetoothSocket(
44    const BluetoothServiceRecord& service_record) {
45  BluetoothSocketWin* bluetooth_socket = NULL;
46  if (service_record.SupportsRfcomm()) {
47    net::EnsureWinsockInit();
48    SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
49    SOCKADDR_BTH sa;
50    ZeroMemory(&sa, sizeof(sa));
51    sa.addressFamily = AF_BTH;
52    sa.port = service_record.rfcomm_channel();
53    const BluetoothServiceRecordWin* service_record_win =
54        static_cast<const BluetoothServiceRecordWin*>(&service_record);
55    sa.btAddr = service_record_win->bth_addr();
56
57    int status = connect(socket_fd,
58                         reinterpret_cast<SOCKADDR *>(&sa),
59                         sizeof(sa));
60    DWORD error_code = WSAGetLastError();
61    if (status == 0 || error_code == WSAEINPROGRESS) {
62      bluetooth_socket =
63          new BluetoothSocketWin(socket_fd);
64    } else {
65      LOG(ERROR) << "Failed to connect bluetooth socket "
66          << "(" << service_record.address() << "): "
67          << "(" << error_code << ")" << FormatErrorMessage(error_code);
68      closesocket(socket_fd);
69    }
70  }
71  // TODO(youngki) add support for L2CAP sockets as well.
72
73  return scoped_refptr<BluetoothSocketWin>(bluetooth_socket);
74}
75
76bool BluetoothSocketWin::Receive(net::GrowableIOBuffer* buffer) {
77  buffer->SetCapacity(1024);
78  int bytes_read;
79  do {
80    if (buffer->RemainingCapacity() == 0)
81      buffer->SetCapacity(buffer->capacity() * 2);
82    bytes_read = recv(fd_, buffer->data(), buffer->RemainingCapacity(), 0);
83    if (bytes_read > 0)
84      buffer->set_offset(buffer->offset() + bytes_read);
85  } while (bytes_read > 0);
86
87  DWORD error_code = WSAGetLastError();
88  if (bytes_read < 0 && error_code != WSAEWOULDBLOCK) {
89    error_message_ = FormatErrorMessage(error_code);
90    return false;
91  }
92  return true;
93}
94
95bool BluetoothSocketWin::Send(net::DrainableIOBuffer* buffer) {
96  int bytes_written;
97  do {
98    bytes_written = send(fd_, buffer->data(), buffer->BytesRemaining(), 0);
99    if (bytes_written > 0)
100      buffer->DidConsume(bytes_written);
101  } while (buffer->BytesRemaining() > 0 && bytes_written > 0);
102
103  DWORD error_code = WSAGetLastError();
104  if (bytes_written < 0 && error_code != WSAEWOULDBLOCK) {
105    error_message_ = FormatErrorMessage(error_code);
106    return false;
107  }
108  return true;
109}
110
111std::string BluetoothSocketWin::GetLastErrorMessage() const {
112  return error_message_;
113}
114
115}  // namespace device
116