910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
#define SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_

#include <stdint.h>

#include <memory>
#include <string>

#include "base/files/file.h"
#include "base/functional/callback.h"
#include "base/memory/raw_span.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "services/device/public/mojom/serial.mojom.h"

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::RefCountedThreadSafe<SerialIoHandler> {
 public:
  // Constructs an instance of some platform-specific subclass.
  static scoped_refptr<SerialIoHandler> Create(
      const base::FilePath& port,
      scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner);

  SerialIoHandler(const SerialIoHandler&) = delete;
  SerialIoHandler& operator=(const SerialIoHandler&) = delete;

  using OpenCompleteCallback = base::OnceCallback<void(bool success)>;
  using ReadCompleteCallback =
      base::OnceCallback<void(uint32_t bytes_read, mojom::SerialReceiveError)>;
  using WriteCompleteCallback =
      base::OnceCallback<void(uint32_t bytes_written, mojom::SerialSendError)>;

  // Initiates an asynchronous Open of the device.
  virtual void Open(const mojom::SerialConnectionOptions& options,
                    OpenCompleteCallback callback);

#if BUILDFLAG(IS_CHROMEOS)
  // Signals that the port has been opened.
  void OnPathOpened(
      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
      base::ScopedFD fd);

  // Signals that the permission broker failed to open the port.
  void OnPathOpenError(
      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
      const std::string& error_name,
      const std::string& error_message);

  // Reports the open error from the permission broker.
  void ReportPathOpenError(const std::string& error_name,
                           const std::string& error_message);
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Performs an async read operation. Behavior is undefined if this is called
  // while a read is already pending. Otherwise, |callback| will be called
  // (potentially synchronously) with a result. |buffer| must remain valid until
  // |callback| is run.
  void Read(base::span<uint8_t> buffer, ReadCompleteCallback callback);

  // Performs an async write operation. Behavior is undefined if this is called
  // while a write is already pending. Otherwise, |callback| will be called
  // (potentially synchronously) with a result. |buffer| must remain valid until
  // |callback| is run.
  void Write(base::span<const uint8_t> buffer, WriteCompleteCallback callback);

  // 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(mojom::SerialReceiveError reason);

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

  // Flushes input and output buffers.
  virtual void Flush(mojom::SerialPortFlushMode mode) const = 0;

  // Drains output buffers.
  virtual void Drain() = 0;

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

  // Sets one or more control signals. Returns |true| iff the signals were
  // successfully set. Flags not present in |control_signals| are unchanged.
  virtual bool SetControlSignals(
      const mojom::SerialHostControlSignals& control_signals) = 0;

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

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

  // Initiates an asynchronous close of the port.
  void Close(base::OnceClosure callback);

 protected:
  explicit SerialIoHandler(
      const base::FilePath& port,
      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 may call ReadCompleted directly.
  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 may call WriteCompleted directly.
  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();

  // Performs platform-specific operations before |file_| is closed.
  virtual void PreClose();

  // 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, mojom::SerialReceiveError 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, mojom::SerialSendError error);

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

  base::span<uint8_t> pending_read_buffer() const {
    return pending_read_buffer_;
  }

  mojom::SerialReceiveError read_cancel_reason() const {
    return read_cancel_reason_;
  }

  bool read_canceled() const { return read_canceled_; }

  base::span<const uint8_t> pending_write_buffer() const {
    return pending_write_buffer_;
  }

  mojom::SerialSendError write_cancel_reason() const {
    return write_cancel_reason_;
  }

  bool write_canceled() const { return write_canceled_; }

  const mojom::SerialConnectionOptions& options() const { return options_; }

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

  const base::FilePath& port() const { return port_; }

  SEQUENCE_CHECKER(sequence_checker_);

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

  void MergeConnectionOptions(const mojom::SerialConnectionOptions& options);

  // Continues an Open operation on the FILE thread.
  void StartOpen(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);

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

  // 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.
  mojom::SerialConnectionOptions options_;

  base::raw_span<uint8_t> pending_read_buffer_;
  ReadCompleteCallback pending_read_callback_;
  mojom::SerialReceiveError read_cancel_reason_;
  bool read_canceled_;

  base::raw_span<const uint8_t> pending_write_buffer_;
  WriteCompleteCallback pending_write_callback_;
  mojom::SerialSendError write_cancel_reason_;
  bool write_canceled_;

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

  const base::FilePath port_;

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

}  // namespace device

#endif  // SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_