Allow mousewheel scrolling to work in print preview on HiDPI devices
[chromium-blink-merge.git] / device / serial / serial_io_handler_posix.cc
blob5b3b3351de95665b821a04822674d9760a676bcc
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)
20 #endif // defined(OS_LINUX)
22 namespace {
24 // Convert an integral bit rate to a nominal one. Returns |true|
25 // if the conversion was successful and |false| otherwise.
26 bool BitrateToSpeedConstant(int bitrate, speed_t* speed) {
27 #define BITRATE_TO_SPEED_CASE(x) \
28 case x: \
29 *speed = B##x; \
30 return true;
31 switch (bitrate) {
32 BITRATE_TO_SPEED_CASE(0)
33 BITRATE_TO_SPEED_CASE(50)
34 BITRATE_TO_SPEED_CASE(75)
35 BITRATE_TO_SPEED_CASE(110)
36 BITRATE_TO_SPEED_CASE(134)
37 BITRATE_TO_SPEED_CASE(150)
38 BITRATE_TO_SPEED_CASE(200)
39 BITRATE_TO_SPEED_CASE(300)
40 BITRATE_TO_SPEED_CASE(600)
41 BITRATE_TO_SPEED_CASE(1200)
42 BITRATE_TO_SPEED_CASE(1800)
43 BITRATE_TO_SPEED_CASE(2400)
44 BITRATE_TO_SPEED_CASE(4800)
45 BITRATE_TO_SPEED_CASE(9600)
46 BITRATE_TO_SPEED_CASE(19200)
47 BITRATE_TO_SPEED_CASE(38400)
48 #if defined(OS_POSIX) && !defined(OS_MACOSX)
49 BITRATE_TO_SPEED_CASE(57600)
50 BITRATE_TO_SPEED_CASE(115200)
51 BITRATE_TO_SPEED_CASE(230400)
52 BITRATE_TO_SPEED_CASE(460800)
53 BITRATE_TO_SPEED_CASE(576000)
54 BITRATE_TO_SPEED_CASE(921600)
55 #endif
56 default:
57 return false;
59 #undef BITRATE_TO_SPEED_CASE
62 // Convert a known nominal speed into an integral bitrate. Returns |true|
63 // if the conversion was successful and |false| otherwise.
64 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
65 #define SPEED_TO_BITRATE_CASE(x) \
66 case B##x: \
67 *bitrate = x; \
68 return true;
69 switch (speed) {
70 SPEED_TO_BITRATE_CASE(0)
71 SPEED_TO_BITRATE_CASE(50)
72 SPEED_TO_BITRATE_CASE(75)
73 SPEED_TO_BITRATE_CASE(110)
74 SPEED_TO_BITRATE_CASE(134)
75 SPEED_TO_BITRATE_CASE(150)
76 SPEED_TO_BITRATE_CASE(200)
77 SPEED_TO_BITRATE_CASE(300)
78 SPEED_TO_BITRATE_CASE(600)
79 SPEED_TO_BITRATE_CASE(1200)
80 SPEED_TO_BITRATE_CASE(1800)
81 SPEED_TO_BITRATE_CASE(2400)
82 SPEED_TO_BITRATE_CASE(4800)
83 SPEED_TO_BITRATE_CASE(9600)
84 SPEED_TO_BITRATE_CASE(19200)
85 SPEED_TO_BITRATE_CASE(38400)
86 #if defined(OS_POSIX) && !defined(OS_MACOSX)
87 SPEED_TO_BITRATE_CASE(57600)
88 SPEED_TO_BITRATE_CASE(115200)
89 SPEED_TO_BITRATE_CASE(230400)
90 SPEED_TO_BITRATE_CASE(460800)
91 SPEED_TO_BITRATE_CASE(576000)
92 SPEED_TO_BITRATE_CASE(921600)
93 #endif
94 default:
95 return false;
97 #undef SPEED_TO_BITRATE_CASE
100 bool SetCustomBitrate(base::PlatformFile file,
101 struct termios* config,
102 int bitrate) {
103 #if defined(OS_LINUX)
104 struct serial_struct serial;
105 if (ioctl(file, TIOCGSERIAL, &serial) < 0) {
106 return false;
108 serial.flags = (serial.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
109 serial.custom_divisor = serial.baud_base / bitrate;
110 if (serial.custom_divisor < 1) {
111 serial.custom_divisor = 1;
113 cfsetispeed(config, B38400);
114 cfsetospeed(config, B38400);
115 return ioctl(file, TIOCSSERIAL, &serial) >= 0;
116 #elif defined(OS_MACOSX)
117 speed_t speed = static_cast<speed_t>(bitrate);
118 cfsetispeed(config, speed);
119 cfsetospeed(config, speed);
120 return true;
121 #else
122 return false;
123 #endif
126 } // namespace
128 namespace device {
130 // static
131 scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
132 scoped_refptr<base::MessageLoopProxy> file_thread_message_loop,
133 scoped_refptr<base::MessageLoopProxy> ui_thread_message_loop) {
134 return new SerialIoHandlerPosix(file_thread_message_loop,
135 ui_thread_message_loop);
138 void SerialIoHandlerPosix::RequestAccess(
139 const std::string& port,
140 scoped_refptr<base::MessageLoopProxy> file_message_loop,
141 scoped_refptr<base::MessageLoopProxy> ui_message_loop) {
142 #if defined(OS_LINUX) && defined(OS_CHROMEOS)
143 if (base::SysInfo::IsRunningOnChromeOS()) {
144 chromeos::PermissionBrokerClient* client =
145 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
146 if (!client) {
147 DVLOG(1) << "Could not get permission_broker client.";
148 OnRequestAccessComplete(port, false /* failure */);
149 return;
151 // PermissionBrokerClient should be called on the UI thread.
152 ui_message_loop->PostTask(
153 FROM_HERE,
154 base::Bind(
155 &chromeos::PermissionBrokerClient::RequestPathAccess,
156 base::Unretained(client),
157 port,
159 base::Bind(&SerialIoHandler::OnRequestAccessComplete, this, port)));
160 } else {
161 OnRequestAccessComplete(port, true /* success */);
162 return;
164 #else
165 OnRequestAccessComplete(port, true /* success */);
166 #endif // defined(OS_LINUX) && defined(OS_CHROMEOS)
169 void SerialIoHandlerPosix::ReadImpl() {
170 DCHECK(CalledOnValidThread());
171 DCHECK(pending_read_buffer());
172 DCHECK(file().IsValid());
174 EnsureWatchingReads();
177 void SerialIoHandlerPosix::WriteImpl() {
178 DCHECK(CalledOnValidThread());
179 DCHECK(pending_write_buffer());
180 DCHECK(file().IsValid());
182 EnsureWatchingWrites();
185 void SerialIoHandlerPosix::CancelReadImpl() {
186 DCHECK(CalledOnValidThread());
187 is_watching_reads_ = false;
188 file_read_watcher_.StopWatchingFileDescriptor();
189 QueueReadCompleted(0, read_cancel_reason());
192 void SerialIoHandlerPosix::CancelWriteImpl() {
193 DCHECK(CalledOnValidThread());
194 is_watching_writes_ = false;
195 file_write_watcher_.StopWatchingFileDescriptor();
196 QueueWriteCompleted(0, write_cancel_reason());
199 bool SerialIoHandlerPosix::ConfigurePortImpl() {
200 struct termios config;
201 if (tcgetattr(file().GetPlatformFile(), &config) != 0) {
202 VPLOG(1) << "Failed to get port attributes";
203 return false;
206 // Set flags for 'raw' operation
207 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
208 config.c_iflag &=
209 ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
210 config.c_oflag &= ~OPOST;
212 // CLOCAL causes the system to disregard the DCD signal state.
213 // CREAD enables reading from the port.
214 config.c_cflag |= (CLOCAL | CREAD);
216 DCHECK(options().bitrate);
217 speed_t bitrate_opt = B0;
218 if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) {
219 cfsetispeed(&config, bitrate_opt);
220 cfsetospeed(&config, bitrate_opt);
221 } else {
222 // Attempt to set a custom speed.
223 if (!SetCustomBitrate(file().GetPlatformFile(), &config,
224 options().bitrate)) {
225 return false;
229 DCHECK(options().data_bits != serial::DATA_BITS_NONE);
230 config.c_cflag &= ~CSIZE;
231 switch (options().data_bits) {
232 case serial::DATA_BITS_SEVEN:
233 config.c_cflag |= CS7;
234 break;
235 case serial::DATA_BITS_EIGHT:
236 default:
237 config.c_cflag |= CS8;
238 break;
241 DCHECK(options().parity_bit != serial::PARITY_BIT_NONE);
242 switch (options().parity_bit) {
243 case serial::PARITY_BIT_EVEN:
244 config.c_cflag |= PARENB;
245 config.c_cflag &= ~PARODD;
246 break;
247 case serial::PARITY_BIT_ODD:
248 config.c_cflag |= (PARODD | PARENB);
249 break;
250 case serial::PARITY_BIT_NO:
251 default:
252 config.c_cflag &= ~(PARODD | PARENB);
253 break;
256 DCHECK(options().stop_bits != serial::STOP_BITS_NONE);
257 switch (options().stop_bits) {
258 case serial::STOP_BITS_TWO:
259 config.c_cflag |= CSTOPB;
260 break;
261 case serial::STOP_BITS_ONE:
262 default:
263 config.c_cflag &= ~CSTOPB;
264 break;
267 DCHECK(options().has_cts_flow_control);
268 if (options().cts_flow_control) {
269 config.c_cflag |= CRTSCTS;
270 } else {
271 config.c_cflag &= ~CRTSCTS;
274 if (tcsetattr(file().GetPlatformFile(), TCSANOW, &config) != 0) {
275 VPLOG(1) << "Failed to set port attributes";
276 return false;
278 return true;
281 SerialIoHandlerPosix::SerialIoHandlerPosix(
282 scoped_refptr<base::MessageLoopProxy> file_thread_message_loop,
283 scoped_refptr<base::MessageLoopProxy> ui_thread_message_loop)
284 : SerialIoHandler(file_thread_message_loop, ui_thread_message_loop),
285 is_watching_reads_(false),
286 is_watching_writes_(false) {
289 SerialIoHandlerPosix::~SerialIoHandlerPosix() {
292 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd) {
293 DCHECK(CalledOnValidThread());
294 DCHECK_EQ(fd, file().GetPlatformFile());
296 if (pending_read_buffer()) {
297 int bytes_read = HANDLE_EINTR(read(file().GetPlatformFile(),
298 pending_read_buffer(),
299 pending_read_buffer_len()));
300 if (bytes_read < 0) {
301 if (errno == ENXIO) {
302 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST);
303 } else {
304 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR);
306 } else if (bytes_read == 0) {
307 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST);
308 } else {
309 ReadCompleted(bytes_read, serial::RECEIVE_ERROR_NONE);
311 } else {
312 // Stop watching the fd if we get notifications with no pending
313 // reads or writes to avoid starving the message loop.
314 is_watching_reads_ = false;
315 file_read_watcher_.StopWatchingFileDescriptor();
319 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) {
320 DCHECK(CalledOnValidThread());
321 DCHECK_EQ(fd, file().GetPlatformFile());
323 if (pending_write_buffer()) {
324 int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(),
325 pending_write_buffer(),
326 pending_write_buffer_len()));
327 if (bytes_written < 0) {
328 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR);
329 } else {
330 WriteCompleted(bytes_written, serial::SEND_ERROR_NONE);
332 } else {
333 // Stop watching the fd if we get notifications with no pending
334 // writes to avoid starving the message loop.
335 is_watching_writes_ = false;
336 file_write_watcher_.StopWatchingFileDescriptor();
340 void SerialIoHandlerPosix::EnsureWatchingReads() {
341 DCHECK(CalledOnValidThread());
342 DCHECK(file().IsValid());
343 if (!is_watching_reads_) {
344 is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
345 file().GetPlatformFile(),
346 true,
347 base::MessageLoopForIO::WATCH_READ,
348 &file_read_watcher_,
349 this);
353 void SerialIoHandlerPosix::EnsureWatchingWrites() {
354 DCHECK(CalledOnValidThread());
355 DCHECK(file().IsValid());
356 if (!is_watching_writes_) {
357 is_watching_writes_ =
358 base::MessageLoopForIO::current()->WatchFileDescriptor(
359 file().GetPlatformFile(),
360 true,
361 base::MessageLoopForIO::WATCH_WRITE,
362 &file_write_watcher_,
363 this);
367 bool SerialIoHandlerPosix::Flush() const {
368 if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) {
369 VPLOG(1) << "Failed to flush port";
370 return false;
372 return true;
375 serial::DeviceControlSignalsPtr SerialIoHandlerPosix::GetControlSignals()
376 const {
377 int status;
378 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
379 VPLOG(1) << "Failed to get port control signals";
380 return serial::DeviceControlSignalsPtr();
383 serial::DeviceControlSignalsPtr signals(serial::DeviceControlSignals::New());
384 signals->dcd = (status & TIOCM_CAR) != 0;
385 signals->cts = (status & TIOCM_CTS) != 0;
386 signals->dsr = (status & TIOCM_DSR) != 0;
387 signals->ri = (status & TIOCM_RI) != 0;
388 return signals.Pass();
391 bool SerialIoHandlerPosix::SetControlSignals(
392 const serial::HostControlSignals& signals) {
393 int status;
395 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
396 VPLOG(1) << "Failed to get port control signals";
397 return false;
400 if (signals.has_dtr) {
401 if (signals.dtr) {
402 status |= TIOCM_DTR;
403 } else {
404 status &= ~TIOCM_DTR;
408 if (signals.has_rts) {
409 if (signals.rts) {
410 status |= TIOCM_RTS;
411 } else {
412 status &= ~TIOCM_RTS;
416 if (ioctl(file().GetPlatformFile(), TIOCMSET, &status) != 0) {
417 VPLOG(1) << "Failed to set port control signals";
418 return false;
420 return true;
423 serial::ConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const {
424 struct termios config;
425 if (tcgetattr(file().GetPlatformFile(), &config) == -1) {
426 VPLOG(1) << "Failed to get port info";
427 return serial::ConnectionInfoPtr();
429 serial::ConnectionInfoPtr info(serial::ConnectionInfo::New());
430 speed_t ispeed = cfgetispeed(&config);
431 speed_t ospeed = cfgetospeed(&config);
432 if (ispeed == ospeed) {
433 int bitrate = 0;
434 if (SpeedConstantToBitrate(ispeed, &bitrate)) {
435 info->bitrate = bitrate;
436 } else if (ispeed > 0) {
437 info->bitrate = static_cast<int>(ispeed);
440 if ((config.c_cflag & CSIZE) == CS7) {
441 info->data_bits = serial::DATA_BITS_SEVEN;
442 } else if ((config.c_cflag & CSIZE) == CS8) {
443 info->data_bits = serial::DATA_BITS_EIGHT;
444 } else {
445 info->data_bits = serial::DATA_BITS_NONE;
447 if (config.c_cflag & PARENB) {
448 info->parity_bit = (config.c_cflag & PARODD) ? serial::PARITY_BIT_ODD
449 : serial::PARITY_BIT_EVEN;
450 } else {
451 info->parity_bit = serial::PARITY_BIT_NO;
453 info->stop_bits =
454 (config.c_cflag & CSTOPB) ? serial::STOP_BITS_TWO : serial::STOP_BITS_ONE;
455 info->cts_flow_control = (config.c_cflag & CRTSCTS) != 0;
456 return info.Pass();
459 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) {
460 return port_name;
463 } // namespace device