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/serial/serial_io_handler.h"
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/message_loop/message_loop.h"
10#include "base/strings/string_util.h"
11
12namespace device {
13
14SerialIoHandler::SerialIoHandler(
15    scoped_refptr<base::MessageLoopProxy> file_thread_message_loop)
16    : file_thread_message_loop_(file_thread_message_loop) {
17}
18
19SerialIoHandler::~SerialIoHandler() {
20  DCHECK(CalledOnValidThread());
21  Close();
22}
23
24void SerialIoHandler::Open(const std::string& port,
25                           const OpenCompleteCallback& callback) {
26  DCHECK(CalledOnValidThread());
27  DCHECK(open_complete_.is_null());
28  open_complete_ = callback;
29  DCHECK(file_thread_message_loop_.get());
30  file_thread_message_loop_->PostTask(
31      FROM_HERE,
32      base::Bind(&SerialIoHandler::StartOpen,
33                 this,
34                 port,
35                 base::MessageLoopProxy::current()));
36}
37
38void SerialIoHandler::StartOpen(
39    const std::string& port,
40    scoped_refptr<base::MessageLoopProxy> io_message_loop) {
41  DCHECK(!open_complete_.is_null());
42  DCHECK(file_thread_message_loop_->RunsTasksOnCurrentThread());
43  DCHECK(!file_.IsValid());
44  // It's the responsibility of the API wrapper around SerialIoHandler to
45  // validate the supplied path against the set of valid port names, and
46  // it is a reasonable assumption that serial port names are ASCII.
47  DCHECK(base::IsStringASCII(port));
48  base::FilePath path(base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port)));
49  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
50              base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
51              base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
52              base::File::FLAG_TERMINAL_DEVICE;
53  base::File file(path, flags);
54  io_message_loop->PostTask(
55      FROM_HERE,
56      base::Bind(&SerialIoHandler::FinishOpen, this, Passed(file.Pass())));
57}
58
59void SerialIoHandler::FinishOpen(base::File file) {
60  DCHECK(CalledOnValidThread());
61  DCHECK(!open_complete_.is_null());
62  OpenCompleteCallback callback = open_complete_;
63  open_complete_.Reset();
64
65  if (!file.IsValid()) {
66    callback.Run(false);
67    return;
68  }
69
70  file_ = file.Pass();
71
72  bool success = PostOpen();
73  if (!success)
74    Close();
75  callback.Run(success);
76}
77
78bool SerialIoHandler::PostOpen() {
79  return true;
80}
81
82void SerialIoHandler::Close() {
83  if (file_.IsValid()) {
84    DCHECK(file_thread_message_loop_.get());
85    file_thread_message_loop_->PostTask(
86        FROM_HERE, base::Bind(&SerialIoHandler::DoClose, Passed(file_.Pass())));
87  }
88}
89
90// static
91void SerialIoHandler::DoClose(base::File port) {
92  // port closed by destructor.
93}
94
95void SerialIoHandler::Read(scoped_ptr<WritableBuffer> buffer) {
96  DCHECK(CalledOnValidThread());
97  DCHECK(!IsReadPending());
98  pending_read_buffer_ = buffer.Pass();
99  read_canceled_ = false;
100  AddRef();
101  ReadImpl();
102}
103
104void SerialIoHandler::Write(scoped_ptr<ReadOnlyBuffer> buffer) {
105  DCHECK(CalledOnValidThread());
106  DCHECK(!IsWritePending());
107  pending_write_buffer_ = buffer.Pass();
108  write_canceled_ = false;
109  AddRef();
110  WriteImpl();
111}
112
113void SerialIoHandler::ReadCompleted(int bytes_read,
114                                    serial::ReceiveError error) {
115  DCHECK(CalledOnValidThread());
116  DCHECK(IsReadPending());
117  scoped_ptr<WritableBuffer> pending_read_buffer = pending_read_buffer_.Pass();
118  if (error == serial::RECEIVE_ERROR_NONE) {
119    pending_read_buffer->Done(bytes_read);
120  } else {
121    pending_read_buffer->DoneWithError(bytes_read, error);
122  }
123  Release();
124}
125
126void SerialIoHandler::WriteCompleted(int bytes_written,
127                                     serial::SendError error) {
128  DCHECK(CalledOnValidThread());
129  DCHECK(IsWritePending());
130  scoped_ptr<ReadOnlyBuffer> pending_write_buffer =
131      pending_write_buffer_.Pass();
132  if (error == serial::SEND_ERROR_NONE) {
133    pending_write_buffer->Done(bytes_written);
134  } else {
135    pending_write_buffer->DoneWithError(bytes_written, error);
136  }
137  Release();
138}
139
140bool SerialIoHandler::IsReadPending() const {
141  DCHECK(CalledOnValidThread());
142  return pending_read_buffer_ != NULL;
143}
144
145bool SerialIoHandler::IsWritePending() const {
146  DCHECK(CalledOnValidThread());
147  return pending_write_buffer_ != NULL;
148}
149
150void SerialIoHandler::CancelRead(serial::ReceiveError reason) {
151  DCHECK(CalledOnValidThread());
152  if (IsReadPending() && !read_canceled_) {
153    read_canceled_ = true;
154    read_cancel_reason_ = reason;
155    CancelReadImpl();
156  }
157}
158
159void SerialIoHandler::CancelWrite(serial::SendError reason) {
160  DCHECK(CalledOnValidThread());
161  if (IsWritePending() && !write_canceled_) {
162    write_canceled_ = true;
163    write_cancel_reason_ = reason;
164    CancelWriteImpl();
165  }
166}
167
168void SerialIoHandler::QueueReadCompleted(int bytes_read,
169                                         serial::ReceiveError error) {
170  base::MessageLoop::current()->PostTask(
171      FROM_HERE,
172      base::Bind(&SerialIoHandler::ReadCompleted, this, bytes_read, error));
173}
174
175void SerialIoHandler::QueueWriteCompleted(int bytes_written,
176                                          serial::SendError error) {
177  base::MessageLoop::current()->PostTask(
178      FROM_HERE,
179      base::Bind(&SerialIoHandler::WriteCompleted, this, bytes_written, error));
180}
181
182}  // namespace device
183