Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / device / serial / serial_io_handler_posix.cc
blob52aad173d397064ef9d9ddf6c54d76d8524b0a3e
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_posix.h"
7 #include <sys/ioctl.h>
8 #include <termios.h>
10 #include "base/posix/eintr_wrapper.h"
12 #if defined(OS_LINUX)
13 #include <linux/serial.h>
14 #if defined(OS_CHROMEOS)
15 #include "base/bind.h"
16 #include "base/sys_info.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/permission_broker_client.h"
19 #endif // defined(OS_CHROMEOS)
21 // The definition of struct termios2 is copied from asm-generic/termbits.h
22 // because including that header directly conflicts with termios.h.
23 extern "C" {
24 struct termios2 {
25 tcflag_t c_iflag; // input mode flags
26 tcflag_t c_oflag; // output mode flags
27 tcflag_t c_cflag; // control mode flags
28 tcflag_t c_lflag; // local mode flags
29 cc_t c_line; // line discipline
30 cc_t c_cc[19]; // control characters
31 speed_t c_ispeed; // input speed
32 speed_t c_ospeed; // output speed
36 #endif // defined(OS_LINUX)
38 namespace {
40 // Convert an integral bit rate to a nominal one. Returns |true|
41 // if the conversion was successful and |false| otherwise.
42 bool BitrateToSpeedConstant(int bitrate, speed_t* speed) {
43 #define BITRATE_TO_SPEED_CASE(x) \
44 case x: \
45 *speed = B##x; \
46 return true;
47 switch (bitrate) {
48 BITRATE_TO_SPEED_CASE(0)
49 BITRATE_TO_SPEED_CASE(50)
50 BITRATE_TO_SPEED_CASE(75)
51 BITRATE_TO_SPEED_CASE(110)
52 BITRATE_TO_SPEED_CASE(134)
53 BITRATE_TO_SPEED_CASE(150)
54 BITRATE_TO_SPEED_CASE(200)
55 BITRATE_TO_SPEED_CASE(300)
56 BITRATE_TO_SPEED_CASE(600)
57 BITRATE_TO_SPEED_CASE(1200)
58 BITRATE_TO_SPEED_CASE(1800)
59 BITRATE_TO_SPEED_CASE(2400)
60 BITRATE_TO_SPEED_CASE(4800)
61 BITRATE_TO_SPEED_CASE(9600)
62 BITRATE_TO_SPEED_CASE(19200)
63 BITRATE_TO_SPEED_CASE(38400)
64 #if !defined(OS_MACOSX)
65 BITRATE_TO_SPEED_CASE(57600)
66 BITRATE_TO_SPEED_CASE(115200)
67 BITRATE_TO_SPEED_CASE(230400)
68 BITRATE_TO_SPEED_CASE(460800)
69 BITRATE_TO_SPEED_CASE(576000)
70 BITRATE_TO_SPEED_CASE(921600)
71 #endif
72 default:
73 return false;
75 #undef BITRATE_TO_SPEED_CASE
78 // Convert a known nominal speed into an integral bitrate. Returns |true|
79 // if the conversion was successful and |false| otherwise.
80 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
81 #define SPEED_TO_BITRATE_CASE(x) \
82 case B##x: \
83 *bitrate = x; \
84 return true;
85 switch (speed) {
86 SPEED_TO_BITRATE_CASE(0)
87 SPEED_TO_BITRATE_CASE(50)
88 SPEED_TO_BITRATE_CASE(75)
89 SPEED_TO_BITRATE_CASE(110)
90 SPEED_TO_BITRATE_CASE(134)
91 SPEED_TO_BITRATE_CASE(150)
92 SPEED_TO_BITRATE_CASE(200)
93 SPEED_TO_BITRATE_CASE(300)
94 SPEED_TO_BITRATE_CASE(600)
95 SPEED_TO_BITRATE_CASE(1200)
96 SPEED_TO_BITRATE_CASE(1800)
97 SPEED_TO_BITRATE_CASE(2400)
98 SPEED_TO_BITRATE_CASE(4800)
99 SPEED_TO_BITRATE_CASE(9600)
100 SPEED_TO_BITRATE_CASE(19200)
101 SPEED_TO_BITRATE_CASE(38400)
102 #if !defined(OS_MACOSX)
103 SPEED_TO_BITRATE_CASE(57600)
104 SPEED_TO_BITRATE_CASE(115200)
105 SPEED_TO_BITRATE_CASE(230400)
106 SPEED_TO_BITRATE_CASE(460800)
107 SPEED_TO_BITRATE_CASE(576000)
108 SPEED_TO_BITRATE_CASE(921600)
109 #endif
110 default:
111 return false;
113 #undef SPEED_TO_BITRATE_CASE
116 bool SetCustomBitrate(base::PlatformFile file,
117 struct termios* config,
118 int bitrate) {
119 #if defined(OS_LINUX)
120 struct termios2 tio;
121 if (ioctl(file, TCGETS2, &tio) < 0) {
122 VPLOG(1) << "Failed to get parameters to set custom bitrate";
123 return false;
125 tio.c_cflag &= ~CBAUD;
126 tio.c_cflag |= CBAUDEX;
127 tio.c_ispeed = bitrate;
128 tio.c_ospeed = bitrate;
129 if (ioctl(file, TCSETS2, &tio) < 0) {
130 VPLOG(1) << "Failed to set custom bitrate";
131 return false;
133 return true;
134 #elif defined(OS_MACOSX)
135 speed_t speed = static_cast<speed_t>(bitrate);
136 cfsetispeed(config, speed);
137 cfsetospeed(config, speed);
138 return true;
139 #else
140 return false;
141 #endif
144 } // namespace
146 namespace device {
148 // static
149 scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
150 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
151 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) {
152 return new SerialIoHandlerPosix(file_thread_task_runner,
153 ui_thread_task_runner);
156 void SerialIoHandlerPosix::RequestAccess(
157 const std::string& port,
158 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
159 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
160 #if defined(OS_LINUX) && defined(OS_CHROMEOS)
161 if (base::SysInfo::IsRunningOnChromeOS()) {
162 chromeos::PermissionBrokerClient* client =
163 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
164 if (!client) {
165 DVLOG(1) << "Could not get permission_broker client.";
166 OnRequestAccessComplete(port, false /* failure */);
167 return;
169 // PermissionBrokerClient should be called on the UI thread.
170 ui_task_runner->PostTask(
171 FROM_HERE,
172 base::Bind(
173 &chromeos::PermissionBrokerClient::RequestPathAccess,
174 base::Unretained(client), port, -1,
175 base::Bind(&SerialIoHandler::OnRequestAccessComplete, this, port)));
176 } else {
177 OnRequestAccessComplete(port, true /* success */);
178 return;
180 #else
181 OnRequestAccessComplete(port, true /* success */);
182 #endif // defined(OS_LINUX) && defined(OS_CHROMEOS)
185 void SerialIoHandlerPosix::ReadImpl() {
186 DCHECK(CalledOnValidThread());
187 DCHECK(pending_read_buffer());
188 DCHECK(file().IsValid());
190 EnsureWatchingReads();
193 void SerialIoHandlerPosix::WriteImpl() {
194 DCHECK(CalledOnValidThread());
195 DCHECK(pending_write_buffer());
196 DCHECK(file().IsValid());
198 EnsureWatchingWrites();
201 void SerialIoHandlerPosix::CancelReadImpl() {
202 DCHECK(CalledOnValidThread());
203 is_watching_reads_ = false;
204 file_read_watcher_.StopWatchingFileDescriptor();
205 QueueReadCompleted(0, read_cancel_reason());
208 void SerialIoHandlerPosix::CancelWriteImpl() {
209 DCHECK(CalledOnValidThread());
210 is_watching_writes_ = false;
211 file_write_watcher_.StopWatchingFileDescriptor();
212 QueueWriteCompleted(0, write_cancel_reason());
215 bool SerialIoHandlerPosix::ConfigurePortImpl() {
216 struct termios config;
217 if (tcgetattr(file().GetPlatformFile(), &config) != 0) {
218 VPLOG(1) << "Failed to get port attributes";
219 return false;
222 // Set flags for 'raw' operation
223 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
224 config.c_iflag &=
225 ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
226 config.c_oflag &= ~OPOST;
228 // CLOCAL causes the system to disregard the DCD signal state.
229 // CREAD enables reading from the port.
230 config.c_cflag |= (CLOCAL | CREAD);
232 DCHECK(options().bitrate);
233 speed_t bitrate_opt = B0;
234 if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) {
235 cfsetispeed(&config, bitrate_opt);
236 cfsetospeed(&config, bitrate_opt);
237 } else {
238 // Attempt to set a custom speed.
239 if (!SetCustomBitrate(file().GetPlatformFile(), &config,
240 options().bitrate)) {
241 return false;
245 DCHECK(options().data_bits != serial::DATA_BITS_NONE);
246 config.c_cflag &= ~CSIZE;
247 switch (options().data_bits) {
248 case serial::DATA_BITS_SEVEN:
249 config.c_cflag |= CS7;
250 break;
251 case serial::DATA_BITS_EIGHT:
252 default:
253 config.c_cflag |= CS8;
254 break;
257 DCHECK(options().parity_bit != serial::PARITY_BIT_NONE);
258 switch (options().parity_bit) {
259 case serial::PARITY_BIT_EVEN:
260 config.c_cflag |= PARENB;
261 config.c_cflag &= ~PARODD;
262 break;
263 case serial::PARITY_BIT_ODD:
264 config.c_cflag |= (PARODD | PARENB);
265 break;
266 case serial::PARITY_BIT_NO:
267 default:
268 config.c_cflag &= ~(PARODD | PARENB);
269 break;
272 DCHECK(options().stop_bits != serial::STOP_BITS_NONE);
273 switch (options().stop_bits) {
274 case serial::STOP_BITS_TWO:
275 config.c_cflag |= CSTOPB;
276 break;
277 case serial::STOP_BITS_ONE:
278 default:
279 config.c_cflag &= ~CSTOPB;
280 break;
283 DCHECK(options().has_cts_flow_control);
284 if (options().cts_flow_control) {
285 config.c_cflag |= CRTSCTS;
286 } else {
287 config.c_cflag &= ~CRTSCTS;
290 if (tcsetattr(file().GetPlatformFile(), TCSANOW, &config) != 0) {
291 VPLOG(1) << "Failed to set port attributes";
292 return false;
294 return true;
297 SerialIoHandlerPosix::SerialIoHandlerPosix(
298 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
299 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
300 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner),
301 is_watching_reads_(false),
302 is_watching_writes_(false) {
305 SerialIoHandlerPosix::~SerialIoHandlerPosix() {
308 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd) {
309 DCHECK(CalledOnValidThread());
310 DCHECK_EQ(fd, file().GetPlatformFile());
312 if (pending_read_buffer()) {
313 int bytes_read = HANDLE_EINTR(read(file().GetPlatformFile(),
314 pending_read_buffer(),
315 pending_read_buffer_len()));
316 if (bytes_read < 0) {
317 if (errno == ENXIO) {
318 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST);
319 } else {
320 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR);
322 } else if (bytes_read == 0) {
323 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST);
324 } else {
325 ReadCompleted(bytes_read, serial::RECEIVE_ERROR_NONE);
327 } else {
328 // Stop watching the fd if we get notifications with no pending
329 // reads or writes to avoid starving the message loop.
330 is_watching_reads_ = false;
331 file_read_watcher_.StopWatchingFileDescriptor();
335 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) {
336 DCHECK(CalledOnValidThread());
337 DCHECK_EQ(fd, file().GetPlatformFile());
339 if (pending_write_buffer()) {
340 int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(),
341 pending_write_buffer(),
342 pending_write_buffer_len()));
343 if (bytes_written < 0) {
344 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR);
345 } else {
346 WriteCompleted(bytes_written, serial::SEND_ERROR_NONE);
348 } else {
349 // Stop watching the fd if we get notifications with no pending
350 // writes to avoid starving the message loop.
351 is_watching_writes_ = false;
352 file_write_watcher_.StopWatchingFileDescriptor();
356 void SerialIoHandlerPosix::EnsureWatchingReads() {
357 DCHECK(CalledOnValidThread());
358 DCHECK(file().IsValid());
359 if (!is_watching_reads_) {
360 is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
361 file().GetPlatformFile(),
362 true,
363 base::MessageLoopForIO::WATCH_READ,
364 &file_read_watcher_,
365 this);
369 void SerialIoHandlerPosix::EnsureWatchingWrites() {
370 DCHECK(CalledOnValidThread());
371 DCHECK(file().IsValid());
372 if (!is_watching_writes_) {
373 is_watching_writes_ =
374 base::MessageLoopForIO::current()->WatchFileDescriptor(
375 file().GetPlatformFile(),
376 true,
377 base::MessageLoopForIO::WATCH_WRITE,
378 &file_write_watcher_,
379 this);
383 bool SerialIoHandlerPosix::Flush() const {
384 if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) {
385 VPLOG(1) << "Failed to flush port";
386 return false;
388 return true;
391 serial::DeviceControlSignalsPtr SerialIoHandlerPosix::GetControlSignals()
392 const {
393 int status;
394 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
395 VPLOG(1) << "Failed to get port control signals";
396 return serial::DeviceControlSignalsPtr();
399 serial::DeviceControlSignalsPtr signals(serial::DeviceControlSignals::New());
400 signals->dcd = (status & TIOCM_CAR) != 0;
401 signals->cts = (status & TIOCM_CTS) != 0;
402 signals->dsr = (status & TIOCM_DSR) != 0;
403 signals->ri = (status & TIOCM_RI) != 0;
404 return signals.Pass();
407 bool SerialIoHandlerPosix::SetControlSignals(
408 const serial::HostControlSignals& signals) {
409 int status;
411 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
412 VPLOG(1) << "Failed to get port control signals";
413 return false;
416 if (signals.has_dtr) {
417 if (signals.dtr) {
418 status |= TIOCM_DTR;
419 } else {
420 status &= ~TIOCM_DTR;
424 if (signals.has_rts) {
425 if (signals.rts) {
426 status |= TIOCM_RTS;
427 } else {
428 status &= ~TIOCM_RTS;
432 if (ioctl(file().GetPlatformFile(), TIOCMSET, &status) != 0) {
433 VPLOG(1) << "Failed to set port control signals";
434 return false;
436 return true;
439 serial::ConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const {
440 struct termios config;
441 if (tcgetattr(file().GetPlatformFile(), &config) == -1) {
442 VPLOG(1) << "Failed to get port info";
443 return serial::ConnectionInfoPtr();
445 serial::ConnectionInfoPtr info(serial::ConnectionInfo::New());
446 speed_t ispeed = cfgetispeed(&config);
447 speed_t ospeed = cfgetospeed(&config);
448 if (ispeed == ospeed) {
449 int bitrate = 0;
450 if (SpeedConstantToBitrate(ispeed, &bitrate)) {
451 info->bitrate = bitrate;
452 } else if (ispeed > 0) {
453 info->bitrate = static_cast<int>(ispeed);
456 if ((config.c_cflag & CSIZE) == CS7) {
457 info->data_bits = serial::DATA_BITS_SEVEN;
458 } else if ((config.c_cflag & CSIZE) == CS8) {
459 info->data_bits = serial::DATA_BITS_EIGHT;
460 } else {
461 info->data_bits = serial::DATA_BITS_NONE;
463 if (config.c_cflag & PARENB) {
464 info->parity_bit = (config.c_cflag & PARODD) ? serial::PARITY_BIT_ODD
465 : serial::PARITY_BIT_EVEN;
466 } else {
467 info->parity_bit = serial::PARITY_BIT_NO;
469 info->stop_bits =
470 (config.c_cflag & CSTOPB) ? serial::STOP_BITS_TWO : serial::STOP_BITS_ONE;
471 info->cts_flow_control = (config.c_cflag & CRTSCTS) != 0;
472 return info.Pass();
475 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) {
476 return port_name;
479 } // namespace device