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.
5 #include "services/device/serial/serial_io_handler.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/strings/string_util.h"
14 #include "base/task/post_task.h"
15 #include "base/task/task_traits.h"
16 #include "build/build_config.h"
18 #if defined(OS_CHROMEOS)
19 #include "chromeos/dbus/dbus_thread_manager.h"
20 #include "chromeos/dbus/permission_broker_client.h"
21 #endif // defined(OS_CHROMEOS)
25 SerialIoHandler::SerialIoHandler(
26 const base::FilePath& port,
27 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
28 : port_(port), ui_thread_task_runner_(ui_thread_task_runner) {
29 options_.bitrate = 9600;
30 options_.data_bits = mojom::SerialDataBits::EIGHT;
31 options_.parity_bit = mojom::SerialParityBit::NO_PARITY;
32 options_.stop_bits = mojom::SerialStopBits::ONE;
33 options_.cts_flow_control = false;
34 options_.has_cts_flow_control = true;
37 SerialIoHandler::~SerialIoHandler() {
38 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
42 void SerialIoHandler::Open(const mojom::SerialConnectionOptions& options,
43 OpenCompleteCallback callback) {
44 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
45 DCHECK(!open_complete_);
46 DCHECK(!port_.empty());
47 open_complete_ = std::move(callback);
48 DCHECK(ui_thread_task_runner_.get());
49 MergeConnectionOptions(options);
51 #if defined(OS_CHROMEOS)
52 chromeos::PermissionBrokerClient* client =
53 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
54 DCHECK(client) << "Could not get permission_broker client.";
55 // PermissionBrokerClient should be called on the UI thread.
56 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
57 base::ThreadTaskRunnerHandle::Get();
58 ui_thread_task_runner_->PostTask(
60 base::BindOnce(&chromeos::PermissionBrokerClient::OpenPath,
61 base::Unretained(client), port_.value(),
62 base::BindRepeating(&SerialIoHandler::OnPathOpened, this,
64 base::BindRepeating(&SerialIoHandler::OnPathOpenError,
67 base::PostTaskWithTraits(
69 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
70 base::BindOnce(&SerialIoHandler::StartOpen, this,
71 base::ThreadTaskRunnerHandle::Get()));
72 #endif // defined(OS_CHROMEOS)
75 #if defined(OS_CHROMEOS)
77 void SerialIoHandler::OnPathOpened(
78 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
80 base::File file(fd.release());
81 io_thread_task_runner->PostTask(
83 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
86 void SerialIoHandler::OnPathOpenError(
87 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
88 const std::string& error_name,
89 const std::string& error_message) {
90 io_thread_task_runner->PostTask(
91 FROM_HERE, base::BindOnce(&SerialIoHandler::ReportPathOpenError, this,
92 error_name, error_message));
95 void SerialIoHandler::ReportPathOpenError(const std::string& error_name,
96 const std::string& error_message) {
97 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
98 DCHECK(open_complete_);
99 LOG(ERROR) << "Permission broker failed to open '" << port_
100 << "': " << error_name << ": " << error_message;
101 std::move(open_complete_).Run(false);
106 void SerialIoHandler::MergeConnectionOptions(
107 const mojom::SerialConnectionOptions& options) {
108 if (options.bitrate) {
109 options_.bitrate = options.bitrate;
111 if (options.data_bits != mojom::SerialDataBits::NONE) {
112 options_.data_bits = options.data_bits;
114 if (options.parity_bit != mojom::SerialParityBit::NONE) {
115 options_.parity_bit = options.parity_bit;
117 if (options.stop_bits != mojom::SerialStopBits::NONE) {
118 options_.stop_bits = options.stop_bits;
120 if (options.has_cts_flow_control) {
121 DCHECK(options_.has_cts_flow_control);
122 options_.cts_flow_control = options.cts_flow_control;
126 void SerialIoHandler::StartOpen(
127 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
128 DCHECK(open_complete_);
129 DCHECK(!file_.IsValid());
130 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
131 base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
132 base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
133 base::File::FLAG_TERMINAL_DEVICE;
134 base::File file(port_, flags);
135 io_task_runner->PostTask(
137 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
140 void SerialIoHandler::FinishOpen(base::File file) {
141 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
142 DCHECK(open_complete_);
144 if (!file.IsValid()) {
145 LOG(ERROR) << "Failed to open serial port: "
146 << base::File::ErrorToString(file.error_details());
147 std::move(open_complete_).Run(false);
151 file_ = std::move(file);
153 bool success = PostOpen() && ConfigurePortImpl();
157 std::move(open_complete_).Run(success);
160 bool SerialIoHandler::PostOpen() {
164 void SerialIoHandler::Close() {
165 if (file_.IsValid()) {
166 base::PostTaskWithTraits(
168 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
169 base::BindOnce(&SerialIoHandler::DoClose, std::move(file_)));
174 void SerialIoHandler::DoClose(base::File port) {
175 // port closed by destructor.
178 void SerialIoHandler::Read(std::unique_ptr<WritableBuffer> buffer) {
179 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
180 DCHECK(!IsReadPending());
181 pending_read_buffer_ = std::move(buffer);
182 read_canceled_ = false;
187 void SerialIoHandler::Write(std::unique_ptr<ReadOnlyBuffer> buffer) {
188 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
189 DCHECK(!IsWritePending());
190 pending_write_buffer_ = std::move(buffer);
191 write_canceled_ = false;
196 void SerialIoHandler::ReadCompleted(int bytes_read,
197 mojom::SerialReceiveError error) {
198 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
199 DCHECK(IsReadPending());
200 std::unique_ptr<WritableBuffer> pending_read_buffer =
201 std::move(pending_read_buffer_);
202 if (error == mojom::SerialReceiveError::NONE) {
203 pending_read_buffer->Done(bytes_read);
205 pending_read_buffer->DoneWithError(bytes_read, static_cast<int32_t>(error));
210 void SerialIoHandler::WriteCompleted(int bytes_written,
211 mojom::SerialSendError error) {
212 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
213 DCHECK(IsWritePending());
214 std::unique_ptr<ReadOnlyBuffer> pending_write_buffer =
215 std::move(pending_write_buffer_);
216 if (error == mojom::SerialSendError::NONE) {
217 pending_write_buffer->Done(bytes_written);
219 pending_write_buffer->DoneWithError(bytes_written,
220 static_cast<int32_t>(error));
225 bool SerialIoHandler::IsReadPending() const {
226 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
227 return pending_read_buffer_ != NULL;
230 bool SerialIoHandler::IsWritePending() const {
231 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
232 return pending_write_buffer_ != NULL;
235 void SerialIoHandler::CancelRead(mojom::SerialReceiveError reason) {
236 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
237 if (IsReadPending() && !read_canceled_) {
238 read_canceled_ = true;
239 read_cancel_reason_ = reason;
244 void SerialIoHandler::CancelWrite(mojom::SerialSendError reason) {
245 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
246 if (IsWritePending() && !write_canceled_) {
247 write_canceled_ = true;
248 write_cancel_reason_ = reason;
253 bool SerialIoHandler::ConfigurePort(
254 const mojom::SerialConnectionOptions& options) {
255 MergeConnectionOptions(options);
256 return ConfigurePortImpl();
259 void SerialIoHandler::QueueReadCompleted(int bytes_read,
260 mojom::SerialReceiveError error) {
261 base::ThreadTaskRunnerHandle::Get()->PostTask(
263 base::BindOnce(&SerialIoHandler::ReadCompleted, this, bytes_read, error));
266 void SerialIoHandler::QueueWriteCompleted(int bytes_written,
267 mojom::SerialSendError error) {
268 base::ThreadTaskRunnerHandle::Get()->PostTask(
269 FROM_HERE, base::BindOnce(&SerialIoHandler::WriteCompleted, this,
270 bytes_written, error));
273 } // namespace device