1// Copyright 2013 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.
4
5#include "chrome/browser/extensions/api/serial/serial_io_handler_posix.h"
6
7#include "base/posix/eintr_wrapper.h"
8
9namespace extensions {
10
11// static
12scoped_refptr<SerialIoHandler> SerialIoHandler::Create() {
13  return new SerialIoHandlerPosix();
14}
15
16void SerialIoHandlerPosix::ReadImpl() {
17  DCHECK(CalledOnValidThread());
18  DCHECK(pending_read_buffer());
19  DCHECK_NE(file(), base::kInvalidPlatformFileValue);
20
21  EnsureWatchingReads();
22}
23
24void SerialIoHandlerPosix::WriteImpl() {
25  DCHECK(CalledOnValidThread());
26  DCHECK(pending_write_buffer());
27  DCHECK_NE(file(), base::kInvalidPlatformFileValue);
28
29  EnsureWatchingWrites();
30}
31
32void SerialIoHandlerPosix::CancelReadImpl() {
33  DCHECK(CalledOnValidThread());
34  is_watching_reads_ = false;
35  file_read_watcher_.StopWatchingFileDescriptor();
36}
37
38void SerialIoHandlerPosix::CancelWriteImpl() {
39  DCHECK(CalledOnValidThread());
40  is_watching_writes_ = false;
41  file_write_watcher_.StopWatchingFileDescriptor();
42}
43
44SerialIoHandlerPosix::SerialIoHandlerPosix()
45    : is_watching_reads_(false),
46      is_watching_writes_(false) {
47}
48
49SerialIoHandlerPosix::~SerialIoHandlerPosix() {}
50
51void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd) {
52  DCHECK(CalledOnValidThread());
53  DCHECK_EQ(fd, file());
54
55  if (pending_read_buffer()) {
56    int bytes_read = HANDLE_EINTR(read(file(),
57                                       pending_read_buffer()->data(),
58                                       pending_read_buffer_len()));
59    if (bytes_read < 0) {
60      if (errno == ENXIO) {
61        ReadCompleted(0, api::serial::RECEIVE_ERROR_DEVICE_LOST);
62      } else {
63        ReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
64      }
65    } else if (bytes_read == 0) {
66      ReadCompleted(0, api::serial::RECEIVE_ERROR_DEVICE_LOST);
67    } else {
68      ReadCompleted(bytes_read, api::serial::RECEIVE_ERROR_NONE);
69    }
70  } else {
71    // Stop watching the fd if we get notifications with no pending
72    // reads or writes to avoid starving the message loop.
73    is_watching_reads_ = false;
74    file_read_watcher_.StopWatchingFileDescriptor();
75  }
76}
77
78void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) {
79  DCHECK(CalledOnValidThread());
80  DCHECK_EQ(fd, file());
81
82  if (pending_write_buffer()) {
83    int bytes_written = HANDLE_EINTR(write(file(),
84                                           pending_write_buffer()->data(),
85                                           pending_write_buffer_len()));
86    if (bytes_written < 0) {
87      WriteCompleted(0, api::serial::SEND_ERROR_SYSTEM_ERROR);
88    } else {
89      WriteCompleted(bytes_written, api::serial::SEND_ERROR_NONE);
90    }
91  } else {
92    // Stop watching the fd if we get notifications with no pending
93    // writes to avoid starving the message loop.
94    is_watching_writes_ = false;
95    file_write_watcher_.StopWatchingFileDescriptor();
96  }
97}
98
99void SerialIoHandlerPosix::EnsureWatchingReads() {
100  DCHECK(CalledOnValidThread());
101  DCHECK_NE(file(), base::kInvalidPlatformFileValue);
102  if (!is_watching_reads_) {
103    is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
104        file(), true, base::MessageLoopForIO::WATCH_READ,
105        &file_read_watcher_, this);
106  }
107}
108
109void SerialIoHandlerPosix::EnsureWatchingWrites() {
110  DCHECK(CalledOnValidThread());
111  DCHECK_NE(file(), base::kInvalidPlatformFileValue);
112  if (!is_watching_writes_) {
113    is_watching_writes_ =
114        base::MessageLoopForIO::current()->WatchFileDescriptor(
115            file(), true, base::MessageLoopForIO::WATCH_WRITE,
116            &file_write_watcher_, this);
117  }
118}
119
120}  // namespace extensions
121