summaryrefslogtreecommitdiffstats
path: root/device/serial/serial_io_handler.h
blob: 63e6c90a36a7f45ea865f15807318b05ec0031e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
#define DEVICE_SERIAL_SERIAL_IO_HANDLER_H_

#include "base/callback.h"
#include "base/files/file.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/non_thread_safe.h"
#include "device/serial/buffer.h"
#include "device/serial/serial.mojom.h"

namespace dbus {
class FileDescriptor;
}

namespace device {

// Provides a simplified interface for performing asynchronous I/O on serial
// devices by hiding platform-specific MessageLoop interfaces. Pending I/O
// operations hold a reference to this object until completion so that memory
// doesn't disappear out from under the OS.
class SerialIoHandler : public base::NonThreadSafe,
                        public base::RefCounted<SerialIoHandler> {
 public:
  // Constructs an instance of some platform-specific subclass.
  static scoped_refptr<SerialIoHandler> Create(
      scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner);

  typedef base::Callback<void(bool success)> OpenCompleteCallback;

  // Initiates an asynchronous Open of the device.
  virtual void Open(const std::string& port,
                    const serial::ConnectionOptions& options,
                    const OpenCompleteCallback& callback);

#if defined(OS_CHROMEOS)
  // Signals that the port has been opened.
  void OnPathOpened(
      scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
      dbus::FileDescriptor fd);

  // Validates the file descriptor provided by the permission broker.
  void ValidateOpenPort(
      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
      dbus::FileDescriptor fd);
#endif  // defined(OS_CHROMEOS)

  // Performs an async Read operation. Behavior is undefined if this is called
  // while a Read is already pending. Otherwise, the Done or DoneWithError
  // method on |buffer| will eventually be called with a result.
  void Read(scoped_ptr<WritableBuffer> buffer);

  // Performs an async Write operation. Behavior is undefined if this is called
  // while a Write is already pending. Otherwise, the Done or DoneWithError
  // method on |buffer| will eventually be called with a result.
  void Write(scoped_ptr<ReadOnlyBuffer> buffer);

  // Indicates whether or not a read is currently pending.
  bool IsReadPending() const;

  // Indicates whether or not a write is currently pending.
  bool IsWritePending() const;

  // Attempts to cancel a pending read operation.
  void CancelRead(serial::ReceiveError reason);

  // Attempts to cancel a pending write operation.
  void CancelWrite(serial::SendError reason);

  // Flushes input and output buffers.
  virtual bool Flush() const = 0;

  // Reads current control signals (DCD, CTS, etc.) into an existing
  // DeviceControlSignals structure. Returns |true| iff the signals were
  // successfully read.
  virtual serial::DeviceControlSignalsPtr GetControlSignals() const = 0;

  // Sets one or more control signals (DTR and/or RTS). Returns |true| iff
  // the signals were successfully set. Unininitialized flags in the
  // HostControlSignals structure are left unchanged.
  virtual bool SetControlSignals(
      const serial::HostControlSignals& control_signals) = 0;

  // Performs platform-specific port configuration. Returns |true| iff
  // configuration was successful.
  bool ConfigurePort(const serial::ConnectionOptions& options);

  // Performs a platform-specific port configuration query. Fills values in an
  // existing ConnectionInfo. Returns |true| iff port configuration was
  // successfully retrieved.
  virtual serial::ConnectionInfoPtr GetPortInfo() const = 0;

  // Initiates a BREAK signal. Places the transmission line in a break state
  // until the |ClearBreak| is called.
  virtual bool SetBreak() = 0;

  // Terminates the BREAK signal. Places the transmission line in a nonbreak
  // state.
  virtual bool ClearBreak() = 0;

 protected:
  explicit SerialIoHandler(
      scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner);
  virtual ~SerialIoHandler();

  // Performs a platform-specific read operation. This must guarantee that
  // ReadCompleted is called when the underlying async operation is completed
  // or the SerialIoHandler instance will leak.
  // NOTE: Implementations of ReadImpl should never call ReadCompleted directly.
  // Use QueueReadCompleted instead to avoid reentrancy.
  virtual void ReadImpl() = 0;

  // Performs a platform-specific write operation. This must guarantee that
  // WriteCompleted is called when the underlying async operation is completed
  // or the SerialIoHandler instance will leak.
  // NOTE: Implementations of WriteImpl should never call WriteCompleted
  // directly. Use QueueWriteCompleted instead to avoid reentrancy.
  virtual void WriteImpl() = 0;

  // Platform-specific read cancelation.
  virtual void CancelReadImpl() = 0;

  // Platform-specific write cancelation.
  virtual void CancelWriteImpl() = 0;

  // Platform-specific port configuration applies options_ to the device.
  virtual bool ConfigurePortImpl() = 0;

  // Performs platform-specific, one-time port configuration on open.
  virtual bool PostOpen();

  // Called by the implementation to signal that the active read has completed.
  // WARNING: Calling this method can destroy the SerialIoHandler instance
  // if the associated I/O operation was the only thing keeping it alive.
  void ReadCompleted(int bytes_read, serial::ReceiveError error);

  // Called by the implementation to signal that the active write has completed.
  // WARNING: Calling this method may destroy the SerialIoHandler instance
  // if the associated I/O operation was the only thing keeping it alive.
  void WriteCompleted(int bytes_written, serial::SendError error);

  // Queues a ReadCompleted call on the current thread. This is used to allow
  // ReadImpl to immediately signal completion with 0 bytes and an error,
  // without being reentrant.
  void QueueReadCompleted(int bytes_read, serial::ReceiveError error);

  // Queues a WriteCompleted call on the current thread. This is used to allow
  // WriteImpl to immediately signal completion with 0 bytes and an error,
  // without being reentrant.
  void QueueWriteCompleted(int bytes_written, serial::SendError error);

  const base::File& file() const { return file_; }

  char* pending_read_buffer() const {
    return pending_read_buffer_ ? pending_read_buffer_->GetData() : NULL;
  }

  uint32_t pending_read_buffer_len() const {
    return pending_read_buffer_ ? pending_read_buffer_->GetSize() : 0;
  }

  serial::ReceiveError read_cancel_reason() const {
    return read_cancel_reason_;
  }

  bool read_canceled() const { return read_canceled_; }

  const char* pending_write_buffer() const {
    return pending_write_buffer_ ? pending_write_buffer_->GetData() : NULL;
  }

  uint32_t pending_write_buffer_len() const {
    return pending_write_buffer_ ? pending_write_buffer_->GetSize() : 0;
  }

  serial::SendError write_cancel_reason() const { return write_cancel_reason_; }

  bool write_canceled() const { return write_canceled_; }

  const serial::ConnectionOptions& options() const { return options_; }

  // Possibly fixes up a serial port path name in a platform-specific manner.
  static std::string MaybeFixUpPortName(const std::string& port_name);

  base::SingleThreadTaskRunner* file_thread_task_runner() const {
    return file_thread_task_runner_.get();
  }

  base::SingleThreadTaskRunner* ui_thread_task_runner() const {
    return ui_thread_task_runner_.get();
  }

  const std::string& port() const { return port_; }

 private:
  friend class base::RefCounted<SerialIoHandler>;

  void MergeConnectionOptions(const serial::ConnectionOptions& options);

  // Continues an Open operation on the FILE thread.
  void StartOpen(const std::string& port,
                 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);

  // Finalizes an Open operation (continued from StartOpen) on the IO thread.
  void FinishOpen(base::File file);

  void Close();

  // Continues a Close operation on the FILE thread.
  static void DoClose(base::File port);

  // File for the opened serial device. This value is only modified from the IO
  // thread.
  base::File file_;

  // Currently applied connection options.
  serial::ConnectionOptions options_;

  scoped_ptr<WritableBuffer> pending_read_buffer_;
  serial::ReceiveError read_cancel_reason_;
  bool read_canceled_;

  scoped_ptr<ReadOnlyBuffer> pending_write_buffer_;
  serial::SendError write_cancel_reason_;
  bool write_canceled_;

  // Callback to handle the completion of a pending Open() request.
  OpenCompleteCallback open_complete_;

  scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner_;
  // On Chrome OS, PermissionBrokerClient should be called on the UI thread.
  scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;

  std::string port_;

  DISALLOW_COPY_AND_ASSIGN(SerialIoHandler);
};

}  // namespace device

#endif  // DEVICE_SERIAL_SERIAL_IO_HANDLER_H_