blob: a9cb52f85dc918ae4809110bd9e14a615093004c [file] [log] [blame]
// 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.
#include "device/serial/serial_io_handler.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
namespace device {
SerialIoHandler::SerialIoHandler(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop,
scoped_refptr<base::MessageLoopProxy> ui_thread_message_loop)
: file_thread_message_loop_(file_thread_message_loop),
ui_thread_message_loop_(ui_thread_message_loop) {
}
SerialIoHandler::~SerialIoHandler() {
DCHECK(CalledOnValidThread());
Close();
}
void SerialIoHandler::Open(const std::string& port,
const OpenCompleteCallback& callback) {
DCHECK(CalledOnValidThread());
DCHECK(open_complete_.is_null());
open_complete_ = callback;
DCHECK(file_thread_message_loop_.get());
DCHECK(ui_thread_message_loop_.get());
RequestAccess(port, file_thread_message_loop_, ui_thread_message_loop_);
}
void SerialIoHandler::RequestAccess(
const std::string& port,
scoped_refptr<base::MessageLoopProxy> file_message_loop,
scoped_refptr<base::MessageLoopProxy> ui_message_loop) {
OnRequestAccessComplete(port, true /* success */);
}
void SerialIoHandler::OnRequestAccessComplete(const std::string& port,
bool success) {
DCHECK(CalledOnValidThread());
if (success) {
DCHECK(file_thread_message_loop_.get());
file_thread_message_loop_->PostTask(
FROM_HERE,
base::Bind(&SerialIoHandler::StartOpen,
this,
port,
base::MessageLoopProxy::current()));
return;
} else {
DCHECK(!open_complete_.is_null());
OpenCompleteCallback callback = open_complete_;
open_complete_.Reset();
callback.Run(false);
return;
}
}
void SerialIoHandler::StartOpen(
const std::string& port,
scoped_refptr<base::MessageLoopProxy> io_message_loop) {
DCHECK(!open_complete_.is_null());
DCHECK(file_thread_message_loop_->RunsTasksOnCurrentThread());
DCHECK(!file_.IsValid());
// It's the responsibility of the API wrapper around SerialIoHandler to
// validate the supplied path against the set of valid port names, and
// it is a reasonable assumption that serial port names are ASCII.
DCHECK(base::IsStringASCII(port));
base::FilePath path(base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port)));
int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
base::File::FLAG_TERMINAL_DEVICE;
base::File file(path, flags);
io_message_loop->PostTask(
FROM_HERE,
base::Bind(&SerialIoHandler::FinishOpen, this, Passed(file.Pass())));
}
void SerialIoHandler::FinishOpen(base::File file) {
DCHECK(CalledOnValidThread());
DCHECK(!open_complete_.is_null());
OpenCompleteCallback callback = open_complete_;
open_complete_.Reset();
if (!file.IsValid()) {
callback.Run(false);
return;
}
file_ = file.Pass();
bool success = PostOpen();
if (!success)
Close();
callback.Run(success);
}
bool SerialIoHandler::PostOpen() {
return true;
}
void SerialIoHandler::Close() {
if (file_.IsValid()) {
DCHECK(file_thread_message_loop_.get());
file_thread_message_loop_->PostTask(
FROM_HERE, base::Bind(&SerialIoHandler::DoClose, Passed(file_.Pass())));
}
}
// static
void SerialIoHandler::DoClose(base::File port) {
// port closed by destructor.
}
void SerialIoHandler::Read(scoped_ptr<WritableBuffer> buffer) {
DCHECK(CalledOnValidThread());
DCHECK(!IsReadPending());
pending_read_buffer_ = buffer.Pass();
read_canceled_ = false;
AddRef();
ReadImpl();
}
void SerialIoHandler::Write(scoped_ptr<ReadOnlyBuffer> buffer) {
DCHECK(CalledOnValidThread());
DCHECK(!IsWritePending());
pending_write_buffer_ = buffer.Pass();
write_canceled_ = false;
AddRef();
WriteImpl();
}
void SerialIoHandler::ReadCompleted(int bytes_read,
serial::ReceiveError error) {
DCHECK(CalledOnValidThread());
DCHECK(IsReadPending());
scoped_ptr<WritableBuffer> pending_read_buffer = pending_read_buffer_.Pass();
if (error == serial::RECEIVE_ERROR_NONE) {
pending_read_buffer->Done(bytes_read);
} else {
pending_read_buffer->DoneWithError(bytes_read, error);
}
Release();
}
void SerialIoHandler::WriteCompleted(int bytes_written,
serial::SendError error) {
DCHECK(CalledOnValidThread());
DCHECK(IsWritePending());
scoped_ptr<ReadOnlyBuffer> pending_write_buffer =
pending_write_buffer_.Pass();
if (error == serial::SEND_ERROR_NONE) {
pending_write_buffer->Done(bytes_written);
} else {
pending_write_buffer->DoneWithError(bytes_written, error);
}
Release();
}
bool SerialIoHandler::IsReadPending() const {
DCHECK(CalledOnValidThread());
return pending_read_buffer_ != NULL;
}
bool SerialIoHandler::IsWritePending() const {
DCHECK(CalledOnValidThread());
return pending_write_buffer_ != NULL;
}
void SerialIoHandler::CancelRead(serial::ReceiveError reason) {
DCHECK(CalledOnValidThread());
if (IsReadPending() && !read_canceled_) {
read_canceled_ = true;
read_cancel_reason_ = reason;
CancelReadImpl();
}
}
void SerialIoHandler::CancelWrite(serial::SendError reason) {
DCHECK(CalledOnValidThread());
if (IsWritePending() && !write_canceled_) {
write_canceled_ = true;
write_cancel_reason_ = reason;
CancelWriteImpl();
}
}
void SerialIoHandler::QueueReadCompleted(int bytes_read,
serial::ReceiveError error) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SerialIoHandler::ReadCompleted, this, bytes_read, error));
}
void SerialIoHandler::QueueWriteCompleted(int bytes_written,
serial::SendError error) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SerialIoHandler::WriteCompleted, this, bytes_written, error));
}
} // namespace device