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 "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_scheduler/post_task.h"
15 #include "base/task_scheduler/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 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
27 : ui_thread_task_runner_(ui_thread_task_runner) {
28 options_.bitrate = 9600;
29 options_.data_bits = mojom::SerialDataBits::EIGHT;
30 options_.parity_bit = mojom::SerialParityBit::NO_PARITY;
31 options_.stop_bits = mojom::SerialStopBits::ONE;
32 options_.cts_flow_control = false;
33 options_.has_cts_flow_control = true;
36 SerialIoHandler::~SerialIoHandler() {
37 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
41 void SerialIoHandler::Open(const std::string& port,
42 const mojom::SerialConnectionOptions& options,
43 OpenCompleteCallback callback) {
44 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
45 DCHECK(!open_complete_);
46 open_complete_ = std::move(callback);
47 DCHECK(ui_thread_task_runner_.get());
48 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(
61 &chromeos::PermissionBrokerClient::OpenPath, base::Unretained(client),
62 port, base::Bind(&SerialIoHandler::OnPathOpened, this, task_runner),
63 base::Bind(&SerialIoHandler::OnPathOpenError, this, task_runner)));
65 base::PostTaskWithTraits(
67 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
68 base::BindOnce(&SerialIoHandler::StartOpen, this, port,
69 base::ThreadTaskRunnerHandle::Get()));
70 #endif // defined(OS_CHROMEOS)
73 #if defined(OS_CHROMEOS)
75 void SerialIoHandler::OnPathOpened(
76 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
78 base::File file(fd.release());
79 io_thread_task_runner->PostTask(
81 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
84 void SerialIoHandler::OnPathOpenError(
85 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
86 const std::string& error_name,
87 const std::string& error_message) {
88 io_thread_task_runner->PostTask(
89 FROM_HERE, base::BindOnce(&SerialIoHandler::ReportPathOpenError, this,
90 error_name, error_message));
93 void SerialIoHandler::ReportPathOpenError(const std::string& error_name,
94 const std::string& error_message) {
95 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
96 DCHECK(open_complete_);
97 LOG(ERROR) << "Permission broker failed to open '" << port_
98 << "': " << error_name << ": " << error_message;
99 std::move(open_complete_).Run(false);
104 void SerialIoHandler::MergeConnectionOptions(
105 const mojom::SerialConnectionOptions& options) {
106 if (options.bitrate) {
107 options_.bitrate = options.bitrate;
109 if (options.data_bits != mojom::SerialDataBits::NONE) {
110 options_.data_bits = options.data_bits;
112 if (options.parity_bit != mojom::SerialParityBit::NONE) {
113 options_.parity_bit = options.parity_bit;
115 if (options.stop_bits != mojom::SerialStopBits::NONE) {
116 options_.stop_bits = options.stop_bits;
118 if (options.has_cts_flow_control) {
119 DCHECK(options_.has_cts_flow_control);
120 options_.cts_flow_control = options.cts_flow_control;
124 void SerialIoHandler::StartOpen(
125 const std::string& port,
126 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
127 DCHECK(open_complete_);
128 DCHECK(!file_.IsValid());
129 // It's the responsibility of the API wrapper around SerialIoHandler to
130 // validate the supplied path against the set of valid port names, and
131 // it is a reasonable assumption that serial port names are ASCII.
132 DCHECK(base::IsStringASCII(port));
133 base::FilePath path(base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port)));
134 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
135 base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
136 base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
137 base::File::FLAG_TERMINAL_DEVICE;
138 base::File file(path, flags);
139 io_task_runner->PostTask(
141 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
144 void SerialIoHandler::FinishOpen(base::File file) {
145 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
146 DCHECK(open_complete_);
148 if (!file.IsValid()) {
149 LOG(ERROR) << "Failed to open serial port: "
150 << base::File::ErrorToString(file.error_details());
151 std::move(open_complete_).Run(false);
155 file_ = std::move(file);
157 bool success = PostOpen() && ConfigurePortImpl();
161 std::move(open_complete_).Run(success);
164 bool SerialIoHandler::PostOpen() {
168 void SerialIoHandler::Close() {
169 if (file_.IsValid()) {
170 base::PostTaskWithTraits(
172 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
173 base::BindOnce(&SerialIoHandler::DoClose, std::move(file_)));
178 void SerialIoHandler::DoClose(base::File port) {
179 // port closed by destructor.
182 void SerialIoHandler::Read(std::unique_ptr<WritableBuffer> buffer) {
183 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184 DCHECK(!IsReadPending());
185 pending_read_buffer_ = std::move(buffer);
186 read_canceled_ = false;
191 void SerialIoHandler::Write(std::unique_ptr<ReadOnlyBuffer> buffer) {
192 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193 DCHECK(!IsWritePending());
194 pending_write_buffer_ = std::move(buffer);
195 write_canceled_ = false;
200 void SerialIoHandler::ReadCompleted(int bytes_read,
201 mojom::SerialReceiveError error) {
202 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
203 DCHECK(IsReadPending());
204 std::unique_ptr<WritableBuffer> pending_read_buffer =
205 std::move(pending_read_buffer_);
206 if (error == mojom::SerialReceiveError::NONE) {
207 pending_read_buffer->Done(bytes_read);
209 pending_read_buffer->DoneWithError(bytes_read, static_cast<int32_t>(error));
214 void SerialIoHandler::WriteCompleted(int bytes_written,
215 mojom::SerialSendError error) {
216 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
217 DCHECK(IsWritePending());
218 std::unique_ptr<ReadOnlyBuffer> pending_write_buffer =
219 std::move(pending_write_buffer_);
220 if (error == mojom::SerialSendError::NONE) {
221 pending_write_buffer->Done(bytes_written);
223 pending_write_buffer->DoneWithError(bytes_written,
224 static_cast<int32_t>(error));
229 bool SerialIoHandler::IsReadPending() const {
230 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
231 return pending_read_buffer_ != NULL;
234 bool SerialIoHandler::IsWritePending() const {
235 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
236 return pending_write_buffer_ != NULL;
239 void SerialIoHandler::CancelRead(mojom::SerialReceiveError reason) {
240 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
241 if (IsReadPending() && !read_canceled_) {
242 read_canceled_ = true;
243 read_cancel_reason_ = reason;
248 void SerialIoHandler::CancelWrite(mojom::SerialSendError reason) {
249 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
250 if (IsWritePending() && !write_canceled_) {
251 write_canceled_ = true;
252 write_cancel_reason_ = reason;
257 bool SerialIoHandler::ConfigurePort(
258 const mojom::SerialConnectionOptions& options) {
259 MergeConnectionOptions(options);
260 return ConfigurePortImpl();
263 void SerialIoHandler::QueueReadCompleted(int bytes_read,
264 mojom::SerialReceiveError error) {
265 base::ThreadTaskRunnerHandle::Get()->PostTask(
267 base::BindOnce(&SerialIoHandler::ReadCompleted, this, bytes_read, error));
270 void SerialIoHandler::QueueWriteCompleted(int bytes_written,
271 mojom::SerialSendError error) {
272 base::ThreadTaskRunnerHandle::Get()->PostTask(
273 FROM_HERE, base::BindOnce(&SerialIoHandler::WriteCompleted, this,
274 bytes_written, error));
277 } // namespace device