Commit b50715c5 authored by sammc@chromium.org's avatar sammc@chromium.org

Add a partial Mojo serial connection interface and implementation.

This adds a Connect method to SerialService and a new partly complete
Connection interface. This change includes methods for:
- setting connection options
- getting connection info
- setting host control signals
- getting device control signals
- flushing buffers

BUG=389016

Review URL: https://codereview.chromium.org/401563002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284399 0039d316-1c4b-4281-b951-d872f2087c98
parent 25ef4f69
......@@ -792,6 +792,7 @@
'../components/components_resources.gyp:components_resources',
'../components/components_strings.gyp:components_strings',
'../device/bluetooth/bluetooth.gyp:device_bluetooth_mocks',
'../device/serial/serial.gyp:device_serial_test_util',
'../extensions/common/api/api.gyp:extensions_api',
'../google_apis/google_apis.gyp:google_apis_test_support',
'../media/cast/cast.gyp:cast_test_utility',
......
......@@ -22,6 +22,7 @@
'usb/usb.gyp:device_usb',
'hid/hid.gyp:device_hid',
'serial/serial.gyp:device_serial',
'serial/serial.gyp:device_serial_test_util',
],
'sources': [
'bluetooth/bluetooth_adapter_mac_unittest.mm',
......@@ -43,6 +44,7 @@
'hid/hid_report_descriptor_unittest.cc',
'hid/hid_service_unittest.cc',
'hid/input_service_linux_unittest.cc',
'serial/serial_connection_unittest.cc',
'serial/serial_service_unittest.cc',
'test/run_all_unittests.cc',
],
......
......@@ -9,6 +9,10 @@ static_library("serial") {
output_name = "device_serial"
sources = [
"serial_connection.cc",
"serial_connection.h",
"serial_connection_factory.cc",
"serial_connection_factory.h",
"serial_device_enumerator.cc",
"serial_device_enumerator.h",
"serial_device_enumerator_linux.cc",
......@@ -17,6 +21,14 @@ static_library("serial") {
"serial_device_enumerator_mac.h",
"serial_device_enumerator_win.cc",
"serial_device_enumerator_win.h",
"serial_io_handler.cc",
"serial_io_handler.h",
"serial_io_handler_posix.cc",
"serial_io_handler_posix.h",
"serial_io_handler_win.cc",
"serial_io_handler_win.h",
"serial_service_impl.cc",
"serial_service_impl.h",
]
if (is_linux) {
......@@ -28,6 +40,20 @@ static_library("serial") {
]
}
# GYP version: device/serial/serial.gyp:device_serial_test_util
static_library("test_util") {
output_name = "device_serial_test_util"
sources = [
"test_serial_io_handler.cc",
"test_serial_io_handler.h",
]
deps = [
":serial",
]
}
mojom("serial_mojo") {
visibility = ":serial"
......
......@@ -36,6 +36,10 @@
],
'sources': [
'serial.mojom',
'serial_connection.cc',
'serial_connection.h',
'serial_connection_factory.cc',
'serial_connection_factory.h',
'serial_device_enumerator.cc',
'serial_device_enumerator.h',
'serial_device_enumerator_linux.cc',
......@@ -54,5 +58,17 @@
'serial_service_impl.h',
],
},
{
# GN version: //device/serial:test_util
'target_name': 'device_serial_test_util',
'type': 'static_library',
'dependencies': [
'device_serial',
],
'sources': [
'test_serial_io_handler.cc',
'test_serial_io_handler.h',
],
},
],
}
......@@ -81,6 +81,22 @@ struct DeviceControlSignals {
interface SerialService {
GetDevices() => (DeviceInfo[] devices);
// Creates a |Connection| to |path| with options specified by |options|,
// returning it via |connection|. This will fail and |connection| will not be
// usable if |path| does not specify a valid serial device or there is an
// error connecting to or configuring the connection.
Connect(string path,
ConnectionOptions options,
Connection& connection);
};
interface Connection {
GetInfo() => (ConnectionInfo info);
SetOptions(ConnectionOptions options) => (bool success);
SetControlSignals(HostControlSignals signals) => (bool success);
GetControlSignals() => (DeviceControlSignals signals);
Flush() => (bool success);
};
}
// 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_connection.h"
#include "device/serial/serial_io_handler.h"
namespace device {
SerialConnection::SerialConnection(scoped_refptr<SerialIoHandler> io_handler)
: io_handler_(io_handler) {
// TODO(sammc): Call io_handler_->Initialize() once we support send and recv.
}
SerialConnection::~SerialConnection() {
}
void SerialConnection::GetInfo(
const mojo::Callback<void(serial::ConnectionInfoPtr)>& callback) {
callback.Run(io_handler_->GetPortInfo());
}
void SerialConnection::SetOptions(serial::ConnectionOptionsPtr options,
const mojo::Callback<void(bool)>& callback) {
callback.Run(io_handler_->ConfigurePort(*options));
io_handler_->CancelRead(device::serial::RECEIVE_ERROR_NONE);
}
void SerialConnection::SetControlSignals(
serial::HostControlSignalsPtr signals,
const mojo::Callback<void(bool)>& callback) {
callback.Run(io_handler_->SetControlSignals(*signals));
}
void SerialConnection::GetControlSignals(
const mojo::Callback<void(serial::DeviceControlSignalsPtr)>& callback) {
callback.Run(io_handler_->GetControlSignals());
}
void SerialConnection::Flush(const mojo::Callback<void(bool)>& callback) {
callback.Run(io_handler_->Flush());
}
void SerialConnection::OnConnectionError() {
delete this;
}
} // namespace device
// 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_CONNECTION_H_
#define DEVICE_SERIAL_SERIAL_CONNECTION_H_
#include "base/memory/ref_counted.h"
#include "device/serial/serial.mojom.h"
#include "mojo/public/cpp/bindings/interface_impl.h"
namespace device {
class SerialIoHandler;
class SerialConnection : public mojo::InterfaceImpl<serial::Connection> {
public:
explicit SerialConnection(scoped_refptr<SerialIoHandler> io_handler);
virtual ~SerialConnection();
// mojo::InterfaceImpl<serial::Connection> overrides.
virtual void GetInfo(
const mojo::Callback<void(serial::ConnectionInfoPtr)>& callback) OVERRIDE;
virtual void SetOptions(serial::ConnectionOptionsPtr options,
const mojo::Callback<void(bool)>& callback) OVERRIDE;
virtual void SetControlSignals(
serial::HostControlSignalsPtr signals,
const mojo::Callback<void(bool)>& callback) OVERRIDE;
virtual void GetControlSignals(const mojo::Callback<
void(serial::DeviceControlSignalsPtr)>& callback) OVERRIDE;
virtual void Flush(const mojo::Callback<void(bool)>& callback) OVERRIDE;
virtual void OnConnectionError() OVERRIDE;
private:
scoped_refptr<SerialIoHandler> io_handler_;
DISALLOW_COPY_AND_ASSIGN(SerialConnection);
};
} // namespace device
#endif // DEVICE_SERIAL_SERIAL_CONNECTION_H_
// 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_connection_factory.h"
#include "base/bind.h"
#include "base/location.h"
#include "device/serial/serial_connection.h"
#include "device/serial/serial_io_handler.h"
namespace device {
namespace {
void FillDefaultConnectionOptions(serial::ConnectionOptions* options) {
if (!options->bitrate)
options->bitrate = 9600;
if (options->data_bits == serial::DATA_BITS_NONE)
options->data_bits = serial::DATA_BITS_EIGHT;
if (options->stop_bits == serial::STOP_BITS_NONE)
options->stop_bits = serial::STOP_BITS_ONE;
if (options->parity_bit == serial::PARITY_BIT_NONE)
options->parity_bit = serial::PARITY_BIT_NO;
if (!options->has_cts_flow_control) {
options->has_cts_flow_control = true;
options->cts_flow_control = false;
}
}
} // namespace
class SerialConnectionFactory::ConnectTask
: public base::RefCountedThreadSafe<SerialConnectionFactory::ConnectTask> {
public:
ConnectTask(scoped_refptr<SerialConnectionFactory> factory,
const std::string& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request);
void Run();
private:
friend class base::RefCountedThreadSafe<SerialConnectionFactory::ConnectTask>;
virtual ~ConnectTask();
void Connect();
void OnConnected(bool success);
scoped_refptr<SerialConnectionFactory> factory_;
const std::string path_;
serial::ConnectionOptionsPtr options_;
mojo::InterfaceRequest<serial::Connection> connection_request_;
scoped_refptr<SerialIoHandler> io_handler_;
DISALLOW_COPY_AND_ASSIGN(ConnectTask);
};
SerialConnectionFactory::SerialConnectionFactory(
const IoHandlerFactory& io_handler_factory,
scoped_refptr<base::MessageLoopProxy> connect_message_loop)
: io_handler_factory_(io_handler_factory),
connect_message_loop_(connect_message_loop) {
}
void SerialConnectionFactory::CreateConnection(
const std::string& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request) {
scoped_refptr<ConnectTask> task(
new ConnectTask(this, path, options.Pass(), connection_request.Pass()));
task->Run();
}
SerialConnectionFactory::~SerialConnectionFactory() {
}
SerialConnectionFactory::ConnectTask::ConnectTask(
scoped_refptr<SerialConnectionFactory> factory,
const std::string& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request)
: factory_(factory),
path_(path),
options_(options.Pass()),
connection_request_(connection_request.Pass()) {
}
void SerialConnectionFactory::ConnectTask::Run() {
factory_->connect_message_loop_->PostTask(
FROM_HERE,
base::Bind(&SerialConnectionFactory::ConnectTask::Connect, this));
}
SerialConnectionFactory::ConnectTask::~ConnectTask() {
}
void SerialConnectionFactory::ConnectTask::Connect() {
io_handler_ = factory_->io_handler_factory_.Run();
io_handler_->Open(
path_,
base::Bind(&SerialConnectionFactory::ConnectTask::OnConnected, this));
}
void SerialConnectionFactory::ConnectTask::OnConnected(bool success) {
DCHECK(io_handler_);
if (!success)
return;
if (!options_)
options_ = serial::ConnectionOptions::New();
FillDefaultConnectionOptions(options_.get());
if (!io_handler_->ConfigurePort(*options_))
return;
mojo::BindToRequest(new SerialConnection(io_handler_), &connection_request_);
}
} // namespace device
// 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_CONNECTION_FACTORY_H_
#define DEVICE_SERIAL_SERIAL_CONNECTION_FACTORY_H_
#include <string>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
#include "device/serial/serial.mojom.h"
#include "mojo/public/cpp/bindings/interface_request.h"
namespace device {
class SerialIoHandler;
class SerialConnectionFactory
: public base::RefCountedThreadSafe<SerialConnectionFactory> {
public:
typedef base::Callback<scoped_refptr<SerialIoHandler>()> IoHandlerFactory;
SerialConnectionFactory(
const IoHandlerFactory& io_handler_factory,
scoped_refptr<base::MessageLoopProxy> connect_message_loop);
void CreateConnection(
const std::string& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request);
private:
friend class base::RefCountedThreadSafe<SerialConnectionFactory>;
class ConnectTask;
virtual ~SerialConnectionFactory();
const IoHandlerFactory io_handler_factory_;
scoped_refptr<base::MessageLoopProxy> connect_message_loop_;
DISALLOW_COPY_AND_ASSIGN(SerialConnectionFactory);
};
} // namespace device
#endif // DEVICE_SERIAL_SERIAL_CONNECTION_FACTORY_H_
// 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 "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "device/serial/serial.mojom.h"
#include "device/serial/serial_service_impl.h"
#include "device/serial/test_serial_io_handler.h"
#include "mojo/public/cpp/bindings/error_handler.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
class FakeSerialDeviceEnumerator : public SerialDeviceEnumerator {
virtual mojo::Array<serial::DeviceInfoPtr> GetDevices() OVERRIDE {
mojo::Array<serial::DeviceInfoPtr> devices(1);
devices[0] = serial::DeviceInfo::New();
devices[0]->path = "device";
return devices.Pass();
}
};
} // namespace
class SerialConnectionTest : public testing::Test, public mojo::ErrorHandler {
public:
SerialConnectionTest() : connected_(false), success_(false) {}
virtual void SetUp() OVERRIDE {
message_loop_.reset(new base::MessageLoop);
mojo::InterfacePtr<serial::SerialService> service;
mojo::BindToProxy(
new SerialServiceImpl(
new SerialConnectionFactory(
base::Bind(&SerialConnectionTest::CreateIoHandler,
base::Unretained(this)),
base::MessageLoopProxy::current()),
scoped_ptr<SerialDeviceEnumerator>(new FakeSerialDeviceEnumerator)),
&service);
service.set_error_handler(this);
service->Connect(
"device", serial::ConnectionOptions::New(), mojo::Get(&connection_));
connection_.set_error_handler(this);
connection_->GetInfo(
base::Bind(&SerialConnectionTest::StoreInfo, base::Unretained(this)));
RunMessageLoop();
ASSERT_TRUE(io_handler_);
}
void StoreInfo(serial::ConnectionInfoPtr options) {
info_ = options.Pass();
StopMessageLoop();
}
void StoreControlSignals(serial::DeviceControlSignalsPtr signals) {
signals_ = signals.Pass();
StopMessageLoop();
}
void StoreSuccess(bool success) {
success_ = success;
StopMessageLoop();
}
void RunMessageLoop() {
run_loop_.reset(new base::RunLoop);
run_loop_->Run();
}
void StopMessageLoop() {
ASSERT_TRUE(message_loop_);
ASSERT_TRUE(run_loop_);
message_loop_->PostTask(FROM_HERE, run_loop_->QuitClosure());
}
scoped_refptr<SerialIoHandler> CreateIoHandler() {
io_handler_ = new TestSerialIoHandler;
return io_handler_;
}
virtual void OnConnectionError() OVERRIDE {
StopMessageLoop();
FAIL() << "Connection error";
}
mojo::Array<serial::DeviceInfoPtr> devices_;
serial::ConnectionInfoPtr info_;
serial::DeviceControlSignalsPtr signals_;
bool connected_;
bool success_;
scoped_ptr<base::MessageLoop> message_loop_;
scoped_ptr<base::RunLoop> run_loop_;
mojo::InterfacePtr<serial::Connection> connection_;
scoped_refptr<TestSerialIoHandler> io_handler_;
private:
DISALLOW_COPY_AND_ASSIGN(SerialConnectionTest);
};
TEST_F(SerialConnectionTest, GetInfo) {
// |info_| is filled in during SetUp().
ASSERT_TRUE(info_);
EXPECT_EQ(9600u, info_->bitrate);
EXPECT_EQ(serial::DATA_BITS_EIGHT, info_->data_bits);
EXPECT_EQ(serial::PARITY_BIT_NO, info_->parity_bit);
EXPECT_EQ(serial::STOP_BITS_ONE, info_->stop_bits);
EXPECT_FALSE(info_->cts_flow_control);
}
TEST_F(SerialConnectionTest, SetOptions) {
serial::ConnectionOptionsPtr options(serial::ConnectionOptions::New());
options->bitrate = 12345;
options->data_bits = serial::DATA_BITS_SEVEN;
options->has_cts_flow_control = true;
options->cts_flow_control = true;
connection_->SetOptions(
options.Pass(),
base::Bind(&SerialConnectionTest::StoreSuccess, base::Unretained(this)));
RunMessageLoop();
ASSERT_TRUE(success_);
serial::ConnectionInfo* info = io_handler_->connection_info();
EXPECT_EQ(12345u, info->bitrate);
EXPECT_EQ(serial::DATA_BITS_SEVEN, info->data_bits);
EXPECT_EQ(serial::PARITY_BIT_NO, info->parity_bit);
EXPECT_EQ(serial::STOP_BITS_ONE, info->stop_bits);
EXPECT_TRUE(info->cts_flow_control);
}
TEST_F(SerialConnectionTest, GetControlSignals) {
connection_->GetControlSignals(base::Bind(
&SerialConnectionTest::StoreControlSignals, base::Unretained(this)));
serial::DeviceControlSignals* signals = io_handler_->device_control_signals();
signals->dcd = true;
signals->dsr = true;
RunMessageLoop();
ASSERT_TRUE(signals_);
EXPECT_TRUE(signals_->dcd);
EXPECT_FALSE(signals_->cts);
EXPECT_FALSE(signals_->ri);
EXPECT_TRUE(signals_->dsr);
}
TEST_F(SerialConnectionTest, SetControlSignals) {
serial::HostControlSignalsPtr signals(serial::HostControlSignals::New());
signals->has_dtr = true;
signals->dtr = true;
signals->has_rts = true;
signals->rts = true;
connection_->SetControlSignals(
signals.Pass(),
base::Bind(&SerialConnectionTest::StoreSuccess, base::Unretained(this)));
RunMessageLoop();
ASSERT_TRUE(success_);
EXPECT_TRUE(io_handler_->dtr());
EXPECT_TRUE(io_handler_->rts());
}
TEST_F(SerialConnectionTest, Flush) {
ASSERT_EQ(0, io_handler_->flushes());
connection_->Flush(
base::Bind(&SerialConnectionTest::StoreSuccess, base::Unretained(this)));
RunMessageLoop();
ASSERT_TRUE(success_);
EXPECT_EQ(1, io_handler_->flushes());
}
TEST_F(SerialConnectionTest, Disconnect) {
connection_.reset();
message_loop_.reset();
EXPECT_TRUE(io_handler_->HasOneRef());
}
} // namespace device
......@@ -11,8 +11,11 @@
namespace device {
SerialIoHandler::SerialIoHandler()
: pending_read_buffer_len_(0), pending_write_buffer_len_(0) {
SerialIoHandler::SerialIoHandler(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop)
: pending_read_buffer_len_(0),
pending_write_buffer_len_(0),
file_thread_message_loop_(file_thread_message_loop) {
}
SerialIoHandler::~SerialIoHandler() {
......@@ -20,14 +23,11 @@ SerialIoHandler::~SerialIoHandler() {
Close();
}
void SerialIoHandler::Initialize(
const ReadCompleteCallback& read_callback,
const WriteCompleteCallback& write_callback,
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop) {
void SerialIoHandler::Initialize(const ReadCompleteCallback& read_callback,
const WriteCompleteCallback& write_callback) {
DCHECK(CalledOnValidThread());
read_complete_ = read_callback;
write_complete_ = write_callback;
file_thread_message_loop_ = file_thread_message_loop;
}
void SerialIoHandler::Open(const std::string& port,
......
......@@ -23,7 +23,8 @@ class SerialIoHandler : public base::NonThreadSafe,
public base::RefCounted<SerialIoHandler> {
public:
// Constructs an instance of some platform-specific subclass.
static scoped_refptr<SerialIoHandler> Create();
static scoped_refptr<SerialIoHandler> Create(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop);
typedef base::Callback<void(bool success)> OpenCompleteCallback;
......@@ -39,10 +40,8 @@ class SerialIoHandler : public base::NonThreadSafe,
// Initializes the handler on the current message loop. Must be called exactly
// once before performing any I/O through the handler.
virtual void Initialize(
const ReadCompleteCallback& read_callback,
const WriteCompleteCallback& write_callback,
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop);
virtual void Initialize(const ReadCompleteCallback& read_callback,
const WriteCompleteCallback& write_callback);
// Initiates an asynchronous Open of the device.
virtual void Open(const std::string& port,
......@@ -94,7 +93,8 @@ class SerialIoHandler : public base::NonThreadSafe,
virtual serial::ConnectionInfoPtr GetPortInfo() const = 0;
protected:
SerialIoHandler();
explicit SerialIoHandler(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop);
virtual ~SerialIoHandler();
// Performs a platform-specific read operation. This must guarantee that
......
......@@ -122,8 +122,9 @@ bool SetCustomBitrate(base::PlatformFile file,
namespace device {
// static
scoped_refptr<SerialIoHandler> SerialIoHandler::Create() {
return new SerialIoHandlerPosix();
scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop) {
return new SerialIoHandlerPosix(file_thread_message_loop);
}
void SerialIoHandlerPosix::ReadImpl() {
......@@ -156,8 +157,11 @@ void SerialIoHandlerPosix::CancelWriteImpl() {
QueueWriteCompleted(0, write_cancel_reason());
}
SerialIoHandlerPosix::SerialIoHandlerPosix()
: is_watching_reads_(false), is_watching_writes_(false) {
SerialIoHandlerPosix::SerialIoHandlerPosix(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop)
: SerialIoHandler(file_thread_message_loop),
is_watching_reads_(false),
is_watching_writes_(false) {
}
SerialIoHandlerPosix::~SerialIoHandlerPosix() {
......
......@@ -29,7 +29,8 @@ class SerialIoHandlerPosix : public SerialIoHandler,
private:
friend class SerialIoHandler;
SerialIoHandlerPosix();
SerialIoHandlerPosix(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop);
virtual ~SerialIoHandlerPosix();
// base::MessageLoopForIO::Watcher implementation.
......
......@@ -133,8 +133,9 @@ serial::StopBits StopBitsConstantToEnum(int stop_bits) {
} // namespace
// static
scoped_refptr<SerialIoHandler> SerialIoHandler::Create() {
return new SerialIoHandlerWin();
scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop) {
return new SerialIoHandlerWin(file_thread_message_loop);
}
bool SerialIoHandlerWin::PostOpen() {
......@@ -237,8 +238,11 @@ void SerialIoHandlerWin::CancelWriteImpl() {
::CancelIo(file().GetPlatformFile());
}
SerialIoHandlerWin::SerialIoHandlerWin()
: event_mask_(0), is_comm_pending_(false) {
SerialIoHandlerWin::SerialIoHandlerWin(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop)
: SerialIoHandler(file_thread_message_loop),
event_mask_(0),
is_comm_pending_(false) {
}
SerialIoHandlerWin::~SerialIoHandlerWin() {
......
......@@ -30,7 +30,8 @@ class SerialIoHandlerWin : public SerialIoHandler,
private:
friend class SerialIoHandler;
SerialIoHandlerWin();
explicit SerialIoHandlerWin(
scoped_refptr<base::MessageLoopProxy> file_thread_message_loop);
virtual ~SerialIoHandlerWin();
// base::MessageLoopForIO::IOHandler implementation.
......
......@@ -6,10 +6,20 @@
#include "base/bind.h"
#include "base/location.h"
#include "device/serial/serial_io_handler.h"
namespace device {
SerialServiceImpl::SerialServiceImpl() {
SerialServiceImpl::SerialServiceImpl(
scoped_refptr<SerialConnectionFactory> connection_factory)
: connection_factory_(connection_factory) {
}
SerialServiceImpl::SerialServiceImpl(
scoped_refptr<SerialConnectionFactory> connection_factory,
scoped_ptr<SerialDeviceEnumerator> device_enumerator)
: device_enumerator_(device_enumerator.Pass()),
connection_factory_(connection_factory) {
}
SerialServiceImpl::~SerialServiceImpl() {
......@@ -17,28 +27,65 @@ SerialServiceImpl::~SerialServiceImpl() {
// static
void SerialServiceImpl::Create(
scoped_refptr<base::MessageLoopProxy> io_message_loop,
mojo::InterfaceRequest<serial::SerialService> request) {
mojo::BindToRequest(new SerialServiceImpl(), &request);
mojo::BindToRequest(new SerialServiceImpl(new SerialConnectionFactory(
base::Bind(SerialIoHandler::Create,
base::MessageLoopProxy::current()),
io_message_loop)),
&request);
}
// static
void SerialServiceImpl::CreateOnMessageLoop(
scoped_refptr<base::MessageLoopProxy> message_loop,
scoped_refptr<base::MessageLoopProxy> io_message_loop,
mojo::InterfaceRequest<serial::SerialService> request) {
message_loop->PostTask(
FROM_HERE,
base::Bind(&SerialServiceImpl::Create, base::Passed(&request)));
base::Bind(
&SerialServiceImpl::Create, io_message_loop, base::Passed(&request)));
}
void SerialServiceImpl::GetDevices(
const mojo::Callback<void(mojo::Array<serial::DeviceInfoPtr>)>& callback) {
if (!device_enumerator_)
device_enumerator_ = SerialDeviceEnumerator::Create();
callback.Run(device_enumerator_->GetDevices());
callback.Run(GetDeviceEnumerator()->GetDevices());
}
void SerialServiceImpl::Connect(
const mojo::String& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request) {
if (!IsValidPath(path))
return;
connection_factory_->CreateConnection(
path, options.Pass(), connection_request.Pass());
}
void SerialServiceImpl::OnConnectionError() {
delete this;
}
SerialDeviceEnumerator* SerialServiceImpl::GetDeviceEnumerator() {
if (!device_enumerator_)
device_enumerator_ = SerialDeviceEnumerator::Create();
return device_enumerator_.get();
}
bool SerialServiceImpl::IsValidPath(const mojo::String& path) {
mojo::Array<serial::DeviceInfoPtr> devices(
GetDeviceEnumerator()->GetDevices());
for (size_t i = 0; i < devices.size(); i++) {
if (path == devices[i]->path)
return true;
}
return false;
}
void SerialServiceImpl::OnConnected(
const mojo::Callback<void(serial::ConnectionInfoPtr)>& callback,
serial::ConnectionInfoPtr result) {
callback.Run(result.Pass());
}
} // namespace device
......@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "device/serial/serial.mojom.h"
#include "device/serial/serial_connection_factory.h"
#include "device/serial/serial_device_enumerator.h"
#include "mojo/public/cpp/bindings/interface_impl.h"
......@@ -15,21 +16,39 @@ namespace device {
class SerialServiceImpl : public mojo::InterfaceImpl<serial::SerialService> {
public:
SerialServiceImpl();
explicit SerialServiceImpl(
scoped_refptr<SerialConnectionFactory> connection_factory);
SerialServiceImpl(scoped_refptr<SerialConnectionFactory> connection_factory,
scoped_ptr<SerialDeviceEnumerator> device_enumerator);
virtual ~SerialServiceImpl();
static void Create(mojo::InterfaceRequest<serial::SerialService> request);
static void Create(scoped_refptr<base::MessageLoopProxy> io_message_loop,
mojo::InterfaceRequest<serial::SerialService> request);
static void CreateOnMessageLoop(
scoped_refptr<base::MessageLoopProxy> message_loop,
scoped_refptr<base::MessageLoopProxy> io_message_loop,
mojo::InterfaceRequest<serial::SerialService> request);
// mojo::InterfaceImpl<SerialService> overrides.
virtual void GetDevices(const mojo::Callback<
void(mojo::Array<serial::DeviceInfoPtr>)>& callback) OVERRIDE;
virtual void Connect(
const mojo::String& path,
serial::ConnectionOptionsPtr options,
mojo::InterfaceRequest<serial::Connection> connection_request) OVERRIDE;
virtual void OnConnectionError() OVERRIDE;
private:
SerialDeviceEnumerator* GetDeviceEnumerator();
bool IsValidPath(const mojo::String& path);
void OnConnected(
const mojo::Callback<void(serial::ConnectionInfoPtr)>& callback,
serial::ConnectionInfoPtr result);
scoped_ptr<SerialDeviceEnumerator> device_enumerator_;
scoped_refptr<SerialConnectionFactory> connection_factory_;
DISALLOW_COPY_AND_ASSIGN(SerialServiceImpl);
};
} // namespace device
......
......@@ -7,12 +7,40 @@
#include "base/run_loop.h"
#include "device/serial/serial.mojom.h"
#include "device/serial/serial_service_impl.h"
#include "device/serial/test_serial_io_handler.h"
#include "mojo/public/cpp/bindings/error_handler.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
class FakeSerialDeviceEnumerator : public SerialDeviceEnumerator {
virtual mojo::Array<serial::DeviceInfoPtr> GetDevices() OVERRIDE {
mojo::Array<serial::DeviceInfoPtr> devices(1);
devices[0] = serial::DeviceInfo::New();
devices[0]->path = "device";
return devices.Pass();
}
};
class FailToOpenIoHandler : public TestSerialIoHandler {
public:
static scoped_refptr<SerialIoHandler> Create() {
return new FailToOpenIoHandler;
}
virtual void Open(const std::string& port,
const OpenCompleteCallback& callback) OVERRIDE {
callback.Run(false);
}
protected:
virtual ~FailToOpenIoHandler() {}
};
} // namespace
class SerialServiceTest : public testing::Test, public mojo::ErrorHandler {
public:
......@@ -20,17 +48,32 @@ class SerialServiceTest : public testing::Test, public mojo::ErrorHandler {
void StoreDevices(mojo::Array<serial::DeviceInfoPtr> devices) {
devices_ = devices.Pass();
message_loop_.PostTask(FROM_HERE, run_loop_.QuitClosure());
StopMessageLoop();
}
virtual void OnConnectionError() OVERRIDE {
message_loop_.PostTask(FROM_HERE, run_loop_.QuitClosure());
FAIL() << "Connection error";
StopMessageLoop();
EXPECT_TRUE(expecting_error_);
}
void RunMessageLoop() {
run_loop_.reset(new base::RunLoop);
run_loop_->Run();
}
void StopMessageLoop() {
ASSERT_TRUE(run_loop_);
message_loop_.PostTask(FROM_HERE, run_loop_->QuitClosure());
}
void OnGotInfo(serial::ConnectionInfoPtr options) { StopMessageLoop(); }
base::MessageLoop message_loop_;
base::RunLoop run_loop_;
scoped_ptr<base::RunLoop> run_loop_;
mojo::Array<serial::DeviceInfoPtr> devices_;
scoped_refptr<TestSerialIoHandler> io_handler_;
bool expecting_error_;
serial::ConnectionInfoPtr info_;
private:
DISALLOW_COPY_AND_ASSIGN(SerialServiceTest);
......@@ -38,16 +81,69 @@ class SerialServiceTest : public testing::Test, public mojo::ErrorHandler {
TEST_F(SerialServiceTest, GetDevices) {
mojo::InterfacePtr<serial::SerialService> service;
SerialServiceImpl::Create(mojo::Get(&service));
SerialServiceImpl::Create(NULL, mojo::Get(&service));
service.set_error_handler(this);
mojo::Array<serial::DeviceInfoPtr> result;
service->GetDevices(
base::Bind(&SerialServiceTest::StoreDevices, base::Unretained(this)));
run_loop_.Run();
RunMessageLoop();
// Because we're running on unknown hardware, only check that we received a
// non-null result.
EXPECT_TRUE(devices_);
}
TEST_F(SerialServiceTest, Connect) {
mojo::InterfacePtr<serial::SerialService> service;
mojo::BindToProxy(
new SerialServiceImpl(
new SerialConnectionFactory(base::Bind(&TestSerialIoHandler::Create),
base::MessageLoopProxy::current()),
scoped_ptr<SerialDeviceEnumerator>(new FakeSerialDeviceEnumerator)),
&service);
service.set_error_handler(this);
mojo::InterfacePtr<serial::Connection> connection;
service->Connect(
"device", serial::ConnectionOptions::New(), mojo::Get(&connection));
connection.set_error_handler(this);
connection->GetInfo(
base::Bind(&SerialServiceTest::OnGotInfo, base::Unretained(this)));
RunMessageLoop();
connection.reset();
}
TEST_F(SerialServiceTest, ConnectInvalidPath) {
mojo::InterfacePtr<serial::SerialService> service;
mojo::BindToProxy(
new SerialServiceImpl(
new SerialConnectionFactory(base::Bind(&TestSerialIoHandler::Create),
base::MessageLoopProxy::current()),
scoped_ptr<SerialDeviceEnumerator>(new FakeSerialDeviceEnumerator)),
&service);
mojo::InterfacePtr<serial::Connection> connection;
service->Connect(
"invalid_path", serial::ConnectionOptions::New(), mojo::Get(&connection));
connection.set_error_handler(this);
expecting_error_ = true;
RunMessageLoop();
EXPECT_TRUE(connection.encountered_error());
}
TEST_F(SerialServiceTest, ConnectOpenFailed) {
mojo::InterfacePtr<serial::SerialService> service;
mojo::BindToProxy(
new SerialServiceImpl(
new SerialConnectionFactory(base::Bind(&FailToOpenIoHandler::Create),
base::MessageLoopProxy::current()),
scoped_ptr<SerialDeviceEnumerator>(new FakeSerialDeviceEnumerator)),
&service);
mojo::InterfacePtr<serial::Connection> connection;
service->Connect(
"device", serial::ConnectionOptions::New(), mojo::Get(&connection));
expecting_error_ = true;
connection.set_error_handler(this);
RunMessageLoop();
EXPECT_TRUE(connection.encountered_error());
}
} // namespace device
// 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/test_serial_io_handler.h"
#include "base/bind.h"
#include "device/serial/serial.mojom.h"
namespace device {
TestSerialIoHandler::TestSerialIoHandler()
: SerialIoHandler(NULL),
opened_(false),
dtr_(false),
rts_(false),
flushes_(0) {
}
scoped_refptr<SerialIoHandler> TestSerialIoHandler::Create() {
return scoped_refptr<SerialIoHandler>(new TestSerialIoHandler);
}
void TestSerialIoHandler::Open(const std::string& port,
const OpenCompleteCallback& callback) {
DCHECK(!opened_);
opened_ = true;
callback.Run(true);
}
bool TestSerialIoHandler::ConfigurePort(
const serial::ConnectionOptions& options) {
if (options.bitrate)
info_.bitrate = options.bitrate;
if (options.data_bits != serial::DATA_BITS_NONE)
info_.data_bits = options.data_bits;
if (options.parity_bit != serial::PARITY_BIT_NONE)
info_.parity_bit = options.parity_bit;
if (options.stop_bits != serial::STOP_BITS_NONE)
info_.stop_bits = options.stop_bits;
if (options.has_cts_flow_control)
info_.cts_flow_control = options.cts_flow_control;
return true;
}
void TestSerialIoHandler::ReadImpl() {
}
void TestSerialIoHandler::CancelReadImpl() {
QueueReadCompleted(0, read_cancel_reason());
}
void TestSerialIoHandler::WriteImpl() {
DCHECK(pending_read_buffer());
DCHECK_LE(pending_write_buffer_len(), pending_read_buffer_len());
memcpy(pending_read_buffer()->data(),
pending_write_buffer()->data(),
pending_write_buffer_len());
QueueReadCompleted(pending_write_buffer_len(), serial::RECEIVE_ERROR_NONE);
QueueWriteCompleted(pending_write_buffer_len(), serial::SEND_ERROR_NONE);
}
void TestSerialIoHandler::CancelWriteImpl() {
QueueWriteCompleted(0, write_cancel_reason());
}
serial::DeviceControlSignalsPtr TestSerialIoHandler::GetControlSignals() const {
serial::DeviceControlSignalsPtr signals(serial::DeviceControlSignals::New());
*signals = device_control_signals_;
return signals.Pass();
}
serial::ConnectionInfoPtr TestSerialIoHandler::GetPortInfo() const {
serial::ConnectionInfoPtr info(serial::ConnectionInfo::New());
*info = info_;
return info.Pass();
}
bool TestSerialIoHandler::Flush() const {
flushes_++;
return true;
}
bool TestSerialIoHandler::SetControlSignals(
const serial::HostControlSignals& signals) {
if (signals.has_dtr)
dtr_ = signals.dtr;
if (signals.has_rts)
rts_ = signals.rts;
return true;
}
TestSerialIoHandler::~TestSerialIoHandler() {
}
} // namespace device
// 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_TEST_SERIAL_IO_HANDLER_H_
#define DEVICE_SERIAL_TEST_SERIAL_IO_HANDLER_H_
#include <string>
#include "base/callback.h"
#include "device/serial/serial.mojom.h"
#include "device/serial/serial_io_handler.h"
namespace device {
class TestSerialIoHandler : public SerialIoHandler {
public:
TestSerialIoHandler();
static scoped_refptr<SerialIoHandler> Create();
// SerialIoHandler overrides.
virtual void Open(const std::string& port,
const OpenCompleteCallback& callback) OVERRIDE;
virtual bool ConfigurePort(const serial::ConnectionOptions& options) OVERRIDE;
virtual void ReadImpl() OVERRIDE;
virtual void CancelReadImpl() OVERRIDE;
virtual void WriteImpl() OVERRIDE;
virtual void CancelWriteImpl() OVERRIDE;
virtual serial::DeviceControlSignalsPtr GetControlSignals() const OVERRIDE;
virtual serial::ConnectionInfoPtr GetPortInfo() const OVERRIDE;
virtual bool Flush() const OVERRIDE;
virtual bool SetControlSignals(
const serial::HostControlSignals& signals) OVERRIDE;
serial::ConnectionInfo* connection_info() { return &info_; }
serial::DeviceControlSignals* device_control_signals() {
return &device_control_signals_;
}
bool dtr() { return dtr_; }
bool rts() { return rts_; }
int flushes() { return flushes_; }
protected:
virtual ~TestSerialIoHandler();
private:
bool opened_;
serial::ConnectionInfo info_;
serial::DeviceControlSignals device_control_signals_;
bool dtr_;
bool rts_;
mutable int flushes_;
DISALLOW_COPY_AND_ASSIGN(TestSerialIoHandler);
};
} // namespace device
#endif // DEVICE_SERIAL_TEST_SERIAL_IO_HANDLER_H_
......@@ -5,6 +5,7 @@
#include <string>
#include "chrome/browser/extensions/extension_apitest.h"
#include "device/serial/test_serial_io_handler.h"
#include "extensions/browser/api/serial/serial_api.h"
#include "extensions/browser/api/serial/serial_connection.h"
#include "extensions/browser/extension_function.h"
......@@ -44,68 +45,15 @@ class FakeSerialGetDevicesFunction : public AsyncExtensionFunction {
virtual ~FakeSerialGetDevicesFunction() {}
};
class FakeEchoSerialIoHandler : public device::SerialIoHandler {
class FakeEchoSerialIoHandler : public device::TestSerialIoHandler {
public:
explicit FakeEchoSerialIoHandler() : opened_(false) {}
virtual void Open(const std::string& port,
const OpenCompleteCallback& callback) OVERRIDE {
DCHECK(!opened_);
opened_ = true;
callback.Run(true);
}
virtual bool ConfigurePort(
const device::serial::ConnectionOptions& options) OVERRIDE {
return true;
}
virtual void ReadImpl() OVERRIDE {}
virtual void CancelReadImpl() OVERRIDE {
QueueReadCompleted(0, read_cancel_reason());
}
virtual void WriteImpl() OVERRIDE {
DCHECK(pending_read_buffer());
DCHECK_LE(pending_write_buffer_len(), pending_read_buffer_len());
memcpy(pending_read_buffer()->data(),
pending_write_buffer()->data(),
pending_write_buffer_len());
QueueReadCompleted(pending_write_buffer_len(),
device::serial::RECEIVE_ERROR_NONE);
QueueWriteCompleted(pending_write_buffer_len(),
device::serial::SEND_ERROR_NONE);
explicit FakeEchoSerialIoHandler() {
device_control_signals()->dcd = true;
device_control_signals()->cts = true;
device_control_signals()->ri = true;
device_control_signals()->dsr = true;
}
virtual void CancelWriteImpl() OVERRIDE {
QueueWriteCompleted(0, write_cancel_reason());
}
virtual device::serial::DeviceControlSignalsPtr GetControlSignals()
const OVERRIDE {
device::serial::DeviceControlSignalsPtr signals(
device::serial::DeviceControlSignals::New());
signals->dcd = true;
signals->cts = true;
signals->ri = true;
signals->dsr = true;
return signals.Pass();
}
virtual device::serial::ConnectionInfoPtr GetPortInfo() const OVERRIDE {
device::serial::ConnectionInfoPtr info(
device::serial::ConnectionInfo::New());
info->bitrate = 9600;
info->data_bits = device::serial::DATA_BITS_EIGHT;
info->parity_bit = device::serial::PARITY_BIT_NO;
info->stop_bits = device::serial::STOP_BITS_ONE;
info->cts_flow_control = false;
return info.Pass();
}
virtual bool Flush() const OVERRIDE { return true; }
MOCK_METHOD1(SetControlSignals,
bool(const device::serial::HostControlSignals&));
......@@ -113,8 +61,6 @@ class FakeEchoSerialIoHandler : public device::SerialIoHandler {
virtual ~FakeEchoSerialIoHandler() {}
private:
bool opened_;
DISALLOW_COPY_AND_ASSIGN(FakeEchoSerialIoHandler);
};
......
......@@ -155,13 +155,13 @@ SerialConnection::SerialConnection(const std::string& port,
receive_timeout_(0),
send_timeout_(0),
paused_(false),
io_handler_(device::SerialIoHandler::Create()) {
io_handler_(device::SerialIoHandler::Create(
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE))) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
io_handler_->Initialize(
base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()),
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE));
base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
}
SerialConnection::~SerialConnection() {
......@@ -252,9 +252,7 @@ void SerialConnection::SetIoHandlerForTest(
io_handler_ = handler;
io_handler_->Initialize(
base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()),
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE));
base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
}
bool SerialConnection::GetInfo(core_api::serial::ConnectionInfo* info) const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment