1 // Copyright 2014 The Chromium Authors
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/callback_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/location.h"
14 #include "base/strings/string_util.h"
15 #include "base/task/task_traits.h"
16 #include "base/task/thread_pool.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "build/build_config.h"
19 #include "components/device_event_log/device_event_log.h"
21 #if BUILDFLAG(IS_CHROMEOS)
22 #include "chromeos/dbus/permission_broker/permission_broker_client.h" // nogncheck
23 #endif // BUILDFLAG(IS_CHROMEOS)
27 SerialIoHandler::SerialIoHandler(
28 const base::FilePath& port,
29 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
30 : port_(port), ui_thread_task_runner_(ui_thread_task_runner) {
31 options_.bitrate = 9600;
32 options_.data_bits = mojom::SerialDataBits::EIGHT;
33 options_.parity_bit = mojom::SerialParityBit::NO_PARITY;
34 options_.stop_bits = mojom::SerialStopBits::ONE;
35 options_.cts_flow_control = false;
36 options_.has_cts_flow_control = true;
39 SerialIoHandler::~SerialIoHandler() {
40 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
41 Close(base::DoNothing());
44 void SerialIoHandler::Open(const mojom::SerialConnectionOptions& options,
45 OpenCompleteCallback callback) {
46 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
47 DCHECK(!open_complete_);
48 DCHECK(!port_.empty());
49 open_complete_ = std::move(callback);
50 DCHECK(ui_thread_task_runner_.get());
51 MergeConnectionOptions(options);
53 #if BUILDFLAG(IS_CHROMEOS)
54 // Note: dbus clients are destroyed in PostDestroyThreads so passing |client|
55 // as unretained is safe.
56 auto* client = chromeos::PermissionBrokerClient::Get();
57 DCHECK(client) << "Could not get permission_broker client.";
58 // PermissionBrokerClient should be called on the UI thread.
59 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
60 base::ThreadTaskRunnerHandle::Get();
61 ui_thread_task_runner_->PostTask(
63 base::BindOnce(&chromeos::PermissionBrokerClient::OpenPath,
64 base::Unretained(client), port_.value(),
65 base::BindRepeating(&SerialIoHandler::OnPathOpened, this,
67 base::BindRepeating(&SerialIoHandler::OnPathOpenError,
70 base::ThreadPool::PostTask(
72 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
73 base::BindOnce(&SerialIoHandler::StartOpen, this,
74 base::ThreadTaskRunnerHandle::Get()));
75 #endif // BUILDFLAG(IS_CHROMEOS)
78 #if BUILDFLAG(IS_CHROMEOS)
80 void SerialIoHandler::OnPathOpened(
81 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
83 base::File file(std::move(fd));
84 io_thread_task_runner->PostTask(
86 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
89 void SerialIoHandler::OnPathOpenError(
90 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
91 const std::string& error_name,
92 const std::string& error_message) {
93 io_thread_task_runner->PostTask(
94 FROM_HERE, base::BindOnce(&SerialIoHandler::ReportPathOpenError, this,
95 error_name, error_message));
98 void SerialIoHandler::ReportPathOpenError(const std::string& error_name,
99 const std::string& error_message) {
100 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
101 DCHECK(open_complete_);
102 SERIAL_LOG(ERROR) << "Permission broker failed to open '" << port_
103 << "': " << error_name << ": " << error_message;
104 std::move(open_complete_).Run(false);
107 #endif // BUILDFLAG(IS_CHROMEOS)
109 void SerialIoHandler::MergeConnectionOptions(
110 const mojom::SerialConnectionOptions& options) {
111 if (options.bitrate) {
112 options_.bitrate = options.bitrate;
114 if (options.data_bits != mojom::SerialDataBits::NONE) {
115 options_.data_bits = options.data_bits;
117 if (options.parity_bit != mojom::SerialParityBit::NONE) {
118 options_.parity_bit = options.parity_bit;
120 if (options.stop_bits != mojom::SerialStopBits::NONE) {
121 options_.stop_bits = options.stop_bits;
123 if (options.has_cts_flow_control) {
124 DCHECK(options_.has_cts_flow_control);
125 options_.cts_flow_control = options.cts_flow_control;
129 void SerialIoHandler::StartOpen(
130 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
131 DCHECK(open_complete_);
132 DCHECK(!file_.IsValid());
133 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
134 base::File::FLAG_WIN_EXCLUSIVE_READ | base::File::FLAG_WRITE |
135 base::File::FLAG_WIN_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
136 base::File::FLAG_TERMINAL_DEVICE;
137 base::File file(port_, flags);
138 io_task_runner->PostTask(
140 base::BindOnce(&SerialIoHandler::FinishOpen, this, std::move(file)));
143 void SerialIoHandler::FinishOpen(base::File file) {
144 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
145 DCHECK(open_complete_);
146 if (!file.IsValid()) {
147 SERIAL_LOG(ERROR) << "Failed to open serial port: "
148 << base::File::ErrorToString(file.error_details());
149 std::move(open_complete_).Run(false);
153 file_ = std::move(file);
155 bool success = PostOpen() && ConfigurePortImpl();
157 Close(base::DoNothing());
159 std::move(open_complete_).Run(success);
162 bool SerialIoHandler::PostOpen() {
166 void SerialIoHandler::PreClose() {}
168 void SerialIoHandler::Close(base::OnceClosure callback) {
169 if (file_.IsValid()) {
170 CancelRead(mojom::SerialReceiveError::DISCONNECTED);
171 CancelWrite(mojom::SerialSendError::DISCONNECTED);
173 base::ThreadPool::PostTaskAndReply(
175 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
176 base::BindOnce(&SerialIoHandler::DoClose, std::move(file_)),
177 std::move(callback));
179 std::move(callback).Run();
184 void SerialIoHandler::DoClose(base::File port) {
185 // port closed by destructor.
188 void SerialIoHandler::Read(base::span<uint8_t> buffer,
189 ReadCompleteCallback callback) {
190 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
191 DCHECK(!IsReadPending());
193 pending_read_buffer_ = buffer;
194 pending_read_callback_ = std::move(callback);
195 read_canceled_ = false;
198 if (!file().IsValid()) {
199 ReadCompleted(0, mojom::SerialReceiveError::DISCONNECTED);
206 void SerialIoHandler::Write(base::span<const uint8_t> buffer,
207 WriteCompleteCallback callback) {
208 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
209 DCHECK(!IsWritePending());
211 pending_write_buffer_ = buffer;
212 pending_write_callback_ = std::move(callback);
213 write_canceled_ = false;
216 if (!file().IsValid()) {
217 WriteCompleted(0, mojom::SerialSendError::DISCONNECTED);
224 void SerialIoHandler::ReadCompleted(int bytes_read,
225 mojom::SerialReceiveError error) {
226 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
227 DCHECK(IsReadPending());
228 pending_read_buffer_ = base::span<uint8_t>();
229 std::move(pending_read_callback_).Run(bytes_read, error);
233 void SerialIoHandler::WriteCompleted(int bytes_written,
234 mojom::SerialSendError error) {
235 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
236 DCHECK(IsWritePending());
237 pending_write_buffer_ = base::span<const uint8_t>();
238 std::move(pending_write_callback_).Run(bytes_written, error);
242 bool SerialIoHandler::IsReadPending() const {
243 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
244 return !pending_read_callback_.is_null();
247 bool SerialIoHandler::IsWritePending() const {
248 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
249 return !pending_write_callback_.is_null();
252 void SerialIoHandler::CancelRead(mojom::SerialReceiveError reason) {
253 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
254 if (IsReadPending() && !read_canceled_) {
255 read_canceled_ = true;
256 read_cancel_reason_ = reason;
261 void SerialIoHandler::CancelWrite(mojom::SerialSendError reason) {
262 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
263 if (IsWritePending() && !write_canceled_) {
264 write_canceled_ = true;
265 write_cancel_reason_ = reason;
270 bool SerialIoHandler::ConfigurePort(
271 const mojom::SerialConnectionOptions& options) {
272 MergeConnectionOptions(options);
273 return ConfigurePortImpl();
276 } // namespace device