summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-20 20:51:13 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-20 20:51:13 +0000
commit058e5c7415c391e2641cdc30792d8de672bf4355 (patch)
treec0e7489f90cdfd388585cf5aca3591e9b142e684 /chrome
parentdfb0f4eb3ed201d428ed75a9afc1c09f43bb4663 (diff)
downloadchromium_src-058e5c7415c391e2641cdc30792d8de672bf4355.zip
chromium_src-058e5c7415c391e2641cdc30792d8de672bf4355.tar.gz
chromium_src-058e5c7415c391e2641cdc30792d8de672bf4355.tar.bz2
Update serial API.
Undoing revert from r236265. R=miket@chromium.org, rpaquay@chromium.org TBR=rch@chromium.org BUG=155861,148741,140125,169555,171948,281908 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=236252 Review URL: https://codereview.chromium.org/27246008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236283 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/api/api_resource_manager.h2
-rw-r--r--chrome/browser/extensions/api/serial/OWNERS1
-rw-r--r--chrome/browser/extensions/api/serial/serial_api.cc514
-rw-r--r--chrome/browser/extensions/api/serial/serial_api.h134
-rw-r--r--chrome/browser/extensions/api/serial/serial_apitest.cc104
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection.cc261
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection.h230
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection_posix.cc347
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection_win.cc235
-rw-r--r--chrome/browser/extensions/api/serial/serial_event_dispatcher.cc158
-rw-r--r--chrome/browser/extensions/api/serial/serial_event_dispatcher.h76
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler.cc120
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler.h173
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler_posix.cc120
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler_posix.h48
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler_win.cc145
-rw-r--r--chrome/browser/extensions/api/serial/serial_io_handler_win.h58
-rw-r--r--chrome/browser/extensions/extension_function_histogram_value.h12
-rw-r--r--chrome/chrome_browser_extensions.gypi8
-rw-r--r--chrome/common/extensions/api/serial.idl283
-rw-r--r--chrome/test/data/extensions/api_test/serial/api/background.js87
-rw-r--r--chrome/test/data/extensions/api_test/serial/real_hardware/background.js70
22 files changed, 2387 insertions, 799 deletions
diff --git a/chrome/browser/extensions/api/api_resource_manager.h b/chrome/browser/extensions/api/api_resource_manager.h
index 5596cfa..b459c22 100644
--- a/chrome/browser/extensions/api/api_resource_manager.h
+++ b/chrome/browser/extensions/api/api_resource_manager.h
@@ -24,6 +24,7 @@
namespace extensions {
namespace api {
+class SerialEventDispatcher;
class TCPServerSocketEventDispatcher;
class TCPSocketEventDispatcher;
class UDPSocketEventDispatcher;
@@ -154,6 +155,7 @@ class ApiResourceManager : public ProfileKeyedAPI,
}
private:
+ friend class api::SerialEventDispatcher;
friend class api::TCPServerSocketEventDispatcher;
friend class api::TCPSocketEventDispatcher;
friend class api::UDPSocketEventDispatcher;
diff --git a/chrome/browser/extensions/api/serial/OWNERS b/chrome/browser/extensions/api/serial/OWNERS
index 452ae4e..29f7cfb 100644
--- a/chrome/browser/extensions/api/serial/OWNERS
+++ b/chrome/browser/extensions/api/serial/OWNERS
@@ -1,3 +1,4 @@
ikarienator@chromium.org
miket@chromium.org
+rockot@chromium.org
rpaquay@chromium.org
diff --git a/chrome/browser/extensions/api/serial/serial_api.cc b/chrome/browser/extensions/api/serial/serial_api.cc
index a15f7e1..ebf2a66 100644
--- a/chrome/browser/extensions/api/serial/serial_api.cc
+++ b/chrome/browser/extensions/api/serial/serial_api.cc
@@ -4,41 +4,56 @@
#include "chrome/browser/extensions/api/serial/serial_api.h"
+#include <algorithm>
+
#include "base/values.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/api/serial/serial_connection.h"
+#include "chrome/browser/extensions/api/serial/serial_event_dispatcher.h"
#include "chrome/browser/extensions/api/serial/serial_port_enumerator.h"
+#include "chrome/common/extensions/api/serial.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
-namespace serial = extensions::api::serial;
-
namespace extensions {
-const char kConnectionIdKey[] = "connectionId";
-const char kDataKey[] = "data";
-const char kBytesReadKey[] = "bytesRead";
-const char kBytesWrittenKey[] = "bytesWritten";
-const char kBitrateKey[] = "bitrate";
-const char kDataBitKey[] = "dataBit";
-const char kParityKey[] = "parityBit";
-const char kStopBitKey[] = "stopBit";
-const char kSuccessKey[] = "success";
-const char kDcdKey[] = "dcd";
-const char kCtsKey[] = "cts";
+namespace api {
+namespace {
+
+// It's a fool's errand to come up with a default bitrate, because we don't get
+// to control both sides of the communication. Unless the other side has
+// implemented auto-bitrate detection (rare), if we pick the wrong rate, then
+// you're gonna have a bad time. Close doesn't count.
+//
+// But we'd like to pick something that has a chance of working, and 9600 is a
+// good balance between popularity and speed. So 9600 it is.
+const int kDefaultBufferSize = 4096;
+const int kDefaultBitrate = 9600;
+const serial::DataBits kDefaultDataBits = serial::DATA_BITS_EIGHT;
+const serial::ParityBit kDefaultParityBit = serial::PARITY_BIT_NO;
+const serial::StopBits kDefaultStopBits = serial::STOP_BITS_ONE;
+const int kDefaultReceiveTimeout = 0;
+const int kDefaultSendTimeout = 0;
+
+const char kErrorOpenFailed[] = "Failed to open the port.";
+const char kErrorSerialConnectionNotFound[] = "Serial connection not found.";
const char kErrorGetControlSignalsFailed[] = "Failed to get control signals.";
-const char kErrorSetControlSignalsFailed[] = "Failed to set control signals.";
-const char kSerialReadInvalidBytesToRead[] = "Number of bytes to read must "
- "be a positive number less than 1,048,576.";
+
+template <class T>
+void SetDefaultScopedPtrValue(scoped_ptr<T>& ptr, const T& value) {
+ if (!ptr.get())
+ ptr.reset(new T(value));
+}
+
+} // namespace
SerialAsyncApiFunction::SerialAsyncApiFunction()
: manager_(NULL) {
}
-SerialAsyncApiFunction::~SerialAsyncApiFunction() {
-}
+SerialAsyncApiFunction::~SerialAsyncApiFunction() {}
bool SerialAsyncApiFunction::PrePrepare() {
manager_ = ApiResourceManager<SerialConnection>::Get(GetProfile());
@@ -46,6 +61,10 @@ bool SerialAsyncApiFunction::PrePrepare() {
return true;
}
+bool SerialAsyncApiFunction::Respond() {
+ return error_.empty();
+}
+
SerialConnection* SerialAsyncApiFunction::GetSerialConnection(
int api_resource_id) {
return manager_->Get(extension_->id(), api_resource_id);
@@ -55,365 +74,338 @@ void SerialAsyncApiFunction::RemoveSerialConnection(int api_resource_id) {
manager_->Remove(extension_->id(), api_resource_id);
}
-SerialGetPortsFunction::SerialGetPortsFunction() {}
+SerialGetDevicesFunction::SerialGetDevicesFunction() {}
-bool SerialGetPortsFunction::Prepare() {
+bool SerialGetDevicesFunction::Prepare() {
set_work_thread_id(BrowserThread::FILE);
return true;
}
-void SerialGetPortsFunction::Work() {
+void SerialGetDevicesFunction::Work() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- base::ListValue* ports = new base::ListValue();
+ std::vector<linked_ptr<serial::DeviceInfo> > devices;
SerialPortEnumerator::StringSet port_names =
SerialPortEnumerator::GenerateValidSerialPortNames();
- SerialPortEnumerator::StringSet::const_iterator i = port_names.begin();
- while (i != port_names.end()) {
- ports->Append(new base::StringValue(*i++));
+ for (SerialPortEnumerator::StringSet::const_iterator iter =
+ port_names.begin();
+ iter != port_names.end();
+ ++iter) {
+ linked_ptr<serial::DeviceInfo> info(new serial::DeviceInfo);
+ info->path = *iter;
+ devices.push_back(info);
}
-
- SetResult(ports);
+ results_ = serial::GetDevices::Results::Create(devices);
}
-bool SerialGetPortsFunction::Respond() {
- return true;
-}
-
-// It's a fool's errand to come up with a default bitrate, because we don't get
-// to control both sides of the communication. Unless the other side has
-// implemented auto-bitrate detection (rare), if we pick the wrong rate, then
-// you're gonna have a bad time. Close doesn't count.
-//
-// But we'd like to pick something that has a chance of working, and 9600 is a
-// good balance between popularity and speed. So 9600 it is.
-SerialOpenFunction::SerialOpenFunction()
- : bitrate_(9600), databit_(serial::DATA_BIT_EIGHTBIT),
- parity_(serial::PARITY_BIT_NOPARITY),
- stopbit_(serial::STOP_BIT_ONESTOPBIT) {
-}
+SerialOpenFunction::SerialOpenFunction() {}
-SerialOpenFunction::~SerialOpenFunction() {
-}
+SerialOpenFunction::~SerialOpenFunction() {}
bool SerialOpenFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
-
- params_ = api::serial::Open::Params::Create(*args_);
+ params_ = serial::Open::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
- if (params_->options.get()) {
- scoped_ptr<base::DictionaryValue> options = params_->options->ToValue();
- if (options->HasKey(kBitrateKey))
- EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kBitrateKey, &bitrate_));
- if (options->HasKey(kDataBitKey)) {
- std::string data;
- options->GetString(kDataBitKey, &data);
- if (!data.empty())
- databit_ = serial::ParseDataBit(data);
- }
- if (options->HasKey(kParityKey)) {
- std::string parity;
- options->GetString(kParityKey, &parity);
- if (!parity.empty())
- parity_ = serial::ParseParityBit(parity);
- }
- if (options->HasKey(kStopBitKey)) {
- std::string stopbit;
- options->GetString(kStopBitKey, &stopbit);
- if (!stopbit.empty())
- stopbit_ = serial::ParseStopBit(stopbit);
- }
- }
+ // Fill in any omitted options to ensure a known initial configuration.
+ if (!params_->options.get())
+ params_->options.reset(new serial::ConnectionOptions());
+ serial::ConnectionOptions* options = params_->options.get();
+
+ SetDefaultScopedPtrValue(options->persistent, false);
+ SetDefaultScopedPtrValue(options->buffer_size, kDefaultBufferSize);
+ SetDefaultScopedPtrValue(options->bitrate, kDefaultBitrate);
+ SetDefaultScopedPtrValue(options->cts_flow_control, false);
+ SetDefaultScopedPtrValue(options->receive_timeout, kDefaultReceiveTimeout);
+ SetDefaultScopedPtrValue(options->send_timeout, kDefaultSendTimeout);
+
+ if (options->data_bits == serial::DATA_BITS_NONE)
+ options->data_bits = kDefaultDataBits;
+ if (options->parity_bit == serial::PARITY_BIT_NONE)
+ options->parity_bit = kDefaultParityBit;
+ if (options->stop_bits == serial::STOP_BITS_NONE)
+ options->stop_bits = kDefaultStopBits;
+
+ serial_event_dispatcher_ = SerialEventDispatcher::Get(GetProfile());
+ DCHECK(serial_event_dispatcher_);
return true;
}
void SerialOpenFunction::AsyncWorkStart() {
- Work();
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ connection_ = CreateSerialConnection(params_->path, extension_->id());
+ connection_->Open(base::Bind(&SerialOpenFunction::OnOpen, this));
}
-void SerialOpenFunction::Work() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- const SerialPortEnumerator::StringSet name_set(
- SerialPortEnumerator::GenerateValidSerialPortNames());
- if (DoesPortExist(params_->port)) {
- SerialConnection* serial_connection = CreateSerialConnection(
- params_->port,
- bitrate_,
- databit_,
- parity_,
- stopbit_,
- extension_->id());
- CHECK(serial_connection);
- int id = manager_->Add(serial_connection);
- CHECK(id);
-
- bool open_result = serial_connection->Open();
- if (!open_result) {
- serial_connection->Close();
- RemoveSerialConnection(id);
- id = -1;
- }
+void SerialOpenFunction::OnOpen(bool success) {
+ DCHECK(connection_);
- base::DictionaryValue* result = new base::DictionaryValue();
- result->SetInteger(kConnectionIdKey, id);
- SetResult(result);
- AsyncWorkCompleted();
+ if (success) {
+ if (!connection_->Configure(*params_->options.get())) {
+ connection_->Close();
+ delete connection_;
+ connection_ = NULL;
+ }
} else {
- base::DictionaryValue* result = new base::DictionaryValue();
- result->SetInteger(kConnectionIdKey, -1);
- SetResult(result);
- AsyncWorkCompleted();
+ delete connection_;
+ connection_ = NULL;
}
-}
-SerialConnection* SerialOpenFunction::CreateSerialConnection(
- const std::string& port,
- int bitrate,
- serial::DataBit databit,
- serial::ParityBit parity,
- serial::StopBit stopbit,
- const std::string& owner_extension_id) {
- return new SerialConnection(port, bitrate, databit, parity, stopbit,
- owner_extension_id);
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&SerialOpenFunction::FinishOpen, this));
}
-bool SerialOpenFunction::DoesPortExist(const std::string& port) {
- const SerialPortEnumerator::StringSet name_set(
- SerialPortEnumerator::GenerateValidSerialPortNames());
- return SerialPortEnumerator::DoesPortExist(name_set, params_->port);
-}
+void SerialOpenFunction::FinishOpen() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!connection_) {
+ error_ = kErrorOpenFailed;
+ } else {
+ int id = manager_->Add(connection_);
+ serial_event_dispatcher_->PollConnection(extension_->id(), id);
-bool SerialOpenFunction::Respond() {
- return true;
+ serial::OpenInfo open_info;
+ open_info.connection_id = id;
+ results_ = serial::Open::Results::Create(open_info);
+ }
+ AsyncWorkCompleted();
}
-SerialCloseFunction::SerialCloseFunction() {
+SerialConnection* SerialOpenFunction::CreateSerialConnection(
+ const std::string& port, const std::string& extension_id) const {
+ return new SerialConnection(port, extension_id);
}
-SerialCloseFunction::~SerialCloseFunction() {
-}
+SerialUpdateFunction::SerialUpdateFunction() {}
-bool SerialCloseFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
+SerialUpdateFunction::~SerialUpdateFunction() {}
- params_ = api::serial::Close::Params::Create(*args_);
+bool SerialUpdateFunction::Prepare() {
+ params_ = serial::Update::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
return true;
}
-void SerialCloseFunction::Work() {
- bool close_result = false;
- SerialConnection* serial_connection = GetSerialConnection(
- params_->connection_id);
- if (serial_connection) {
- serial_connection->Close();
- RemoveSerialConnection(params_->connection_id);
- close_result = true;
+void SerialUpdateFunction::Work() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
}
-
- SetResult(new base::FundamentalValue(close_result));
+ bool success = connection->Configure(params_->options);
+ results_ = serial::Update::Results::Create(success);
}
-bool SerialCloseFunction::Respond() {
+SerialCloseFunction::SerialCloseFunction() {}
+
+SerialCloseFunction::~SerialCloseFunction() {}
+
+bool SerialCloseFunction::Prepare() {
+ params_ = serial::Close::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
return true;
}
-SerialReadFunction::SerialReadFunction() {
+void SerialCloseFunction::Work() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
+ }
+ connection->Close();
+ RemoveSerialConnection(params_->connection_id);
+ results_ = serial::Close::Results::Create(true);
}
-SerialReadFunction::~SerialReadFunction() {
-}
+SerialSendFunction::SerialSendFunction() {}
-bool SerialReadFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
+SerialSendFunction::~SerialSendFunction() {}
- params_ = api::serial::Read::Params::Create(*args_);
+bool SerialSendFunction::Prepare() {
+ params_ = serial::Send::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
- if (params_->bytes_to_read <= 0 || params_->bytes_to_read >= 1024 * 1024) {
- error_ = kSerialReadInvalidBytesToRead;
- return false;
- }
return true;
}
-void SerialReadFunction::Work() {
- int bytes_read = -1;
- scoped_refptr<net::IOBufferWithSize> io_buffer(
- new net::IOBufferWithSize(params_->bytes_to_read));
- SerialConnection* serial_connection(GetSerialConnection(
- params_->connection_id));
-
- if (serial_connection)
- bytes_read = serial_connection->Read(io_buffer);
+void SerialSendFunction::AsyncWorkStart() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ AsyncWorkCompleted();
+ return;
+ }
- base::DictionaryValue* result = new base::DictionaryValue();
+ if (!connection->Send(params_->data,
+ base::Bind(&SerialSendFunction::OnSendComplete,
+ this))) {
+ OnSendComplete(0, serial::SEND_ERROR_PENDING);
+ }
+}
- // The API is defined to require a 'data' value, so we will always
- // create a BinaryValue, even if it's zero-length.
- if (bytes_read < 0)
- bytes_read = 0;
- result->SetInteger(kBytesReadKey, bytes_read);
- result->Set(kDataKey, base::BinaryValue::CreateWithCopiedBuffer(
- io_buffer->data(), bytes_read));
- SetResult(result);
+void SerialSendFunction::OnSendComplete(int bytes_sent,
+ serial::SendError error) {
+ serial::SendInfo send_info;
+ send_info.bytes_sent = bytes_sent;
+ send_info.error = error;
+ results_ = serial::Send::Results::Create(send_info);
+ AsyncWorkCompleted();
}
-bool SerialReadFunction::Respond() {
+SerialFlushFunction::SerialFlushFunction() {}
+
+SerialFlushFunction::~SerialFlushFunction() {}
+
+bool SerialFlushFunction::Prepare() {
+ params_ = serial::Flush::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
return true;
}
-SerialWriteFunction::SerialWriteFunction()
- : io_buffer_(NULL), io_buffer_size_(0) {
-}
+void SerialFlushFunction::Work() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
+ }
-SerialWriteFunction::~SerialWriteFunction() {
+ bool success = connection->Flush();
+ results_ = serial::Flush::Results::Create(success);
}
-bool SerialWriteFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
+SerialSetPausedFunction::SerialSetPausedFunction() {}
- params_ = api::serial::Write::Params::Create(*args_);
- EXTENSION_FUNCTION_VALIDATE(params_.get());
+SerialSetPausedFunction::~SerialSetPausedFunction() {}
- io_buffer_size_ = params_->data.size();
- io_buffer_ = new net::WrappedIOBuffer(params_->data.data());
+bool SerialSetPausedFunction::Prepare() {
+ params_ = serial::SetPaused::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ serial_event_dispatcher_ = SerialEventDispatcher::Get(GetProfile());
+ DCHECK(serial_event_dispatcher_);
return true;
}
-void SerialWriteFunction::Work() {
- int bytes_written = -1;
- SerialConnection* serial_connection = GetSerialConnection(
- params_->connection_id);
- if (serial_connection)
- bytes_written = serial_connection->Write(io_buffer_, io_buffer_size_);
- else
- error_ = kSerialConnectionNotFoundError;
-
- base::DictionaryValue* result = new base::DictionaryValue();
- result->SetInteger(kBytesWrittenKey, bytes_written);
- SetResult(result);
-}
+void SerialSetPausedFunction::Work() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
+ }
-bool SerialWriteFunction::Respond() {
- return true;
-}
+ if (params_->paused != connection->paused()) {
+ connection->set_paused(params_->paused);
+ if (!params_->paused) {
+ serial_event_dispatcher_->PollConnection(extension_->id(),
+ params_->connection_id);
+ }
+ }
-SerialFlushFunction::SerialFlushFunction() {
+ results_ = serial::SetPaused::Results::Create();
}
-SerialFlushFunction::~SerialFlushFunction() {
-}
+SerialGetInfoFunction::SerialGetInfoFunction() {}
-bool SerialFlushFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
+SerialGetInfoFunction::~SerialGetInfoFunction() {}
- params_ = api::serial::Flush::Params::Create(*args_);
+bool SerialGetInfoFunction::Prepare() {
+ params_ = serial::GetInfo::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
+
return true;
}
-void SerialFlushFunction::Work() {
- bool flush_result = false;
- SerialConnection* serial_connection = GetSerialConnection(
- params_->connection_id);
- if (serial_connection) {
- serial_connection->Flush();
- flush_result = true;
+void SerialGetInfoFunction::Work() {
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
}
- SetResult(new base::FundamentalValue(flush_result));
+ serial::ConnectionInfo info;
+ info.connection_id = params_->connection_id;
+ connection->GetInfo(&info);
+ results_ = serial::GetInfo::Results::Create(info);
}
-bool SerialFlushFunction::Respond() {
+SerialGetConnectionsFunction::SerialGetConnectionsFunction() {}
+
+SerialGetConnectionsFunction::~SerialGetConnectionsFunction() {}
+
+bool SerialGetConnectionsFunction::Prepare() {
return true;
}
-SerialGetControlSignalsFunction::SerialGetControlSignalsFunction()
- : api_response_(false) {
+void SerialGetConnectionsFunction::Work() {
+ std::vector<linked_ptr<serial::ConnectionInfo> > infos;
+ const base::hash_set<int>* connection_ids = manager_->GetResourceIds(
+ extension_->id());
+ if (connection_ids) {
+ for (base::hash_set<int>::const_iterator it = connection_ids->begin();
+ it != connection_ids->end(); ++it) {
+ int connection_id = *it;
+ SerialConnection *connection = GetSerialConnection(connection_id);
+ if (connection) {
+ linked_ptr<serial::ConnectionInfo> info(new serial::ConnectionInfo());
+ info->connection_id = connection_id;
+ connection->GetInfo(info.get());
+ infos.push_back(info);
+ }
+ }
+ }
+ results_ = serial::GetConnections::Results::Create(infos);
}
-SerialGetControlSignalsFunction::~SerialGetControlSignalsFunction() {
-}
+SerialGetControlSignalsFunction::SerialGetControlSignalsFunction() {}
-bool SerialGetControlSignalsFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
+SerialGetControlSignalsFunction::~SerialGetControlSignalsFunction() {}
- params_ = api::serial::GetControlSignals::Params::Create(*args_);
+bool SerialGetControlSignalsFunction::Prepare() {
+ params_ = serial::GetControlSignals::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
return true;
}
void SerialGetControlSignalsFunction::Work() {
- base::DictionaryValue *result = new base::DictionaryValue();
- SerialConnection* serial_connection = GetSerialConnection(
- params_->connection_id);
- if (serial_connection) {
- SerialConnection::ControlSignals control_signals = { 0 };
- if (serial_connection->GetControlSignals(control_signals)) {
- api_response_ = true;
- result->SetBoolean(kDcdKey, control_signals.dcd);
- result->SetBoolean(kCtsKey, control_signals.cts);
- } else {
- error_ = kErrorGetControlSignalsFailed;
- }
- } else {
- error_ = kSerialConnectionNotFoundError;
- result->SetBoolean(kSuccessKey, false);
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
}
- SetResult(result);
-}
+ serial::ControlSignals signals;
+ if (!connection->GetControlSignals(&signals)) {
+ error_ = kErrorGetControlSignalsFailed;
+ return;
+ }
-bool SerialGetControlSignalsFunction::Respond() {
- return api_response_;
+ results_ = serial::GetControlSignals::Results::Create(signals);
}
-SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() {
-}
+SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() {}
-SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() {
-}
+SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() {}
bool SerialSetControlSignalsFunction::Prepare() {
- set_work_thread_id(BrowserThread::FILE);
-
- params_ = api::serial::SetControlSignals::Params::Create(*args_);
+ params_ = serial::SetControlSignals::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
return true;
}
void SerialSetControlSignalsFunction::Work() {
- SerialConnection* serial_connection = GetSerialConnection(
- params_->connection_id);
- if (serial_connection) {
- SerialConnection::ControlSignals control_signals = { 0 };
- control_signals.should_set_dtr = params_->options.dtr.get() != NULL;
- if (control_signals.should_set_dtr)
- control_signals.dtr = *(params_->options.dtr);
- control_signals.should_set_rts = params_->options.rts.get() != NULL;
- if (control_signals.should_set_rts)
- control_signals.rts = *(params_->options.rts);
- if (serial_connection->SetControlSignals(control_signals)) {
- SetResult(new base::FundamentalValue(true));
- } else {
- error_ = kErrorSetControlSignalsFailed;
- SetResult(new base::FundamentalValue(false));
- }
- } else {
- error_ = kSerialConnectionNotFoundError;
- SetResult(new base::FundamentalValue(false));
+ SerialConnection* connection = GetSerialConnection(params_->connection_id);
+ if (!connection) {
+ error_ = kErrorSerialConnectionNotFound;
+ return;
}
-}
-bool SerialSetControlSignalsFunction::Respond() {
- return true;
+ bool success = connection->SetControlSignals(params_->signals);
+ results_ = serial::SetControlSignals::Results::Create(success);
}
+} // namespace api
+
} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_api.h b/chrome/browser/extensions/api/serial/serial_api.h
index 6798ab5..4bd6475 100644
--- a/chrome/browser/extensions/api/serial/serial_api.h
+++ b/chrome/browser/extensions/api/serial/serial_api.h
@@ -11,15 +11,14 @@
#include "chrome/browser/extensions/api/api_function.h"
#include "chrome/browser/extensions/api/api_resource_manager.h"
#include "chrome/common/extensions/api/serial.h"
-#include "net/base/io_buffer.h"
-
-namespace serial = extensions::api::serial;
namespace extensions {
class SerialConnection;
-extern const char kConnectionIdKey[];
+namespace api {
+
+class SerialEventDispatcher;
class SerialAsyncApiFunction : public AsyncApiFunction {
public:
@@ -30,6 +29,7 @@ class SerialAsyncApiFunction : public AsyncApiFunction {
// AsyncApiFunction:
virtual bool PrePrepare() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
SerialConnection* GetSerialConnection(int api_resource_id);
void RemoveSerialConnection(int api_resource_id);
@@ -37,19 +37,18 @@ class SerialAsyncApiFunction : public AsyncApiFunction {
ApiResourceManager<SerialConnection>* manager_;
};
-class SerialGetPortsFunction : public SerialAsyncApiFunction {
+class SerialGetDevicesFunction : public SerialAsyncApiFunction {
public:
- DECLARE_EXTENSION_FUNCTION("serial.getPorts", SERIAL_GETPORTS)
+ DECLARE_EXTENSION_FUNCTION("serial.getDevices", SERIAL_GETDEVICES)
- SerialGetPortsFunction();
+ SerialGetDevicesFunction();
protected:
- virtual ~SerialGetPortsFunction() {}
+ virtual ~SerialGetDevicesFunction() {}
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
};
class SerialOpenFunction : public SerialAsyncApiFunction {
@@ -64,26 +63,42 @@ class SerialOpenFunction : public SerialAsyncApiFunction {
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void AsyncWorkStart() OVERRIDE;
- virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
- // Overrideable for testing.
virtual SerialConnection* CreateSerialConnection(
const std::string& port,
- int bitrate,
- serial::DataBit databit,
- serial::ParityBit parity,
- serial::StopBit stopbit,
- const std::string& owner_extension_id);
+ const std::string& extension_id) const;
- virtual bool DoesPortExist(const std::string& port);
+ private:
+ void OnOpen(bool success);
+ void FinishOpen();
+
+ scoped_ptr<serial::Open::Params> params_;
+
+ // SerialEventDispatcher is owned by a Profile.
+ SerialEventDispatcher* serial_event_dispatcher_;
+
+ // This connection is created within SerialOpenFunction.
+ // From there it is either destroyed in OnOpen (upon failure)
+ // or its ownership is transferred to the profile's.
+ // ApiResourceManager<SerialConnection>.
+ SerialConnection* connection_;
+};
+
+class SerialUpdateFunction : public SerialAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("serial.update", SERIAL_UPDATE);
+
+ SerialUpdateFunction();
+
+ protected:
+ virtual ~SerialUpdateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
private:
- scoped_ptr<api::serial::Open::Params> params_;
- int bitrate_;
- api::serial::DataBit databit_;
- api::serial::ParityBit parity_;
- api::serial::StopBit stopbit_;
+ scoped_ptr<serial::Update::Params> params_;
};
class SerialCloseFunction : public SerialAsyncApiFunction {
@@ -98,48 +113,77 @@ class SerialCloseFunction : public SerialAsyncApiFunction {
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::Close::Params> params_;
+ scoped_ptr<serial::Close::Params> params_;
};
-class SerialReadFunction : public SerialAsyncApiFunction {
+class SerialSetPausedFunction : public SerialAsyncApiFunction {
public:
- DECLARE_EXTENSION_FUNCTION("serial.read", SERIAL_READ)
+ DECLARE_EXTENSION_FUNCTION("serial.setPaused", SERIAL_SETPAUSED)
- SerialReadFunction();
+ SerialSetPausedFunction();
protected:
- virtual ~SerialReadFunction();
+ virtual ~SerialSetPausedFunction();
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::Read::Params> params_;
+ scoped_ptr<serial::SetPaused::Params> params_;
+ SerialEventDispatcher* serial_event_dispatcher_;
};
-class SerialWriteFunction : public SerialAsyncApiFunction {
+class SerialGetInfoFunction : public SerialAsyncApiFunction {
public:
- DECLARE_EXTENSION_FUNCTION("serial.write", SERIAL_WRITE)
+ DECLARE_EXTENSION_FUNCTION("serial.getInfo", SERIAL_GETINFO)
- SerialWriteFunction();
+ SerialGetInfoFunction();
protected:
- virtual ~SerialWriteFunction();
+ virtual ~SerialGetInfoFunction();
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::Write::Params> params_;
- scoped_refptr<net::IOBuffer> io_buffer_;
- size_t io_buffer_size_;
+ scoped_ptr<serial::GetInfo::Params> params_;
+};
+
+class SerialGetConnectionsFunction : public SerialAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("serial.getConnections", SERIAL_GETCONNECTIONS);
+
+ SerialGetConnectionsFunction();
+
+ protected:
+ virtual ~SerialGetConnectionsFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+};
+
+class SerialSendFunction : public SerialAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("serial.send", SERIAL_SEND)
+
+ SerialSendFunction();
+
+ protected:
+ virtual ~SerialSendFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ void OnSendComplete(int bytes_sent, serial::SendError error);
+
+ scoped_ptr<serial::Send::Params> params_;
};
class SerialFlushFunction : public SerialAsyncApiFunction {
@@ -154,10 +198,9 @@ class SerialFlushFunction : public SerialAsyncApiFunction {
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::Flush::Params> params_;
+ scoped_ptr<serial::Flush::Params> params_;
};
class SerialGetControlSignalsFunction : public SerialAsyncApiFunction {
@@ -173,11 +216,9 @@ class SerialGetControlSignalsFunction : public SerialAsyncApiFunction {
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::GetControlSignals::Params> params_;
- bool api_response_;
+ scoped_ptr<serial::GetControlSignals::Params> params_;
};
class SerialSetControlSignalsFunction : public SerialAsyncApiFunction {
@@ -193,12 +234,13 @@ class SerialSetControlSignalsFunction : public SerialAsyncApiFunction {
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
- virtual bool Respond() OVERRIDE;
private:
- scoped_ptr<api::serial::SetControlSignals::Params> params_;
+ scoped_ptr<serial::SetControlSignals::Params> params_;
};
+} // namespace api
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_API_H_
diff --git a/chrome/browser/extensions/api/serial/serial_apitest.cc b/chrome/browser/extensions/api/serial/serial_apitest.cc
index 43b44b2..30165ac 100644
--- a/chrome/browser/extensions/api/serial/serial_apitest.cc
+++ b/chrome/browser/extensions/api/serial/serial_apitest.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/common/extensions/api/serial.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_function.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -22,8 +23,6 @@ using testing::Return;
using content::BrowserThread;
-namespace serial = extensions::api::serial;
-
namespace {
class SerialApiTest : public ExtensionApiTest {
@@ -35,42 +34,43 @@ class SerialApiTest : public ExtensionApiTest {
namespace extensions {
-class FakeSerialGetPortsFunction : public AsyncExtensionFunction {
+class FakeSerialGetDevicesFunction : public AsyncExtensionFunction {
public:
virtual bool RunImpl() OVERRIDE {
- base::ListValue* ports = new base::ListValue();
- ports->Append(Value::CreateStringValue("/dev/fakeserial"));
- ports->Append(Value::CreateStringValue("\\\\COM800\\"));
- SetResult(ports);
+ base::ListValue* devices = new base::ListValue();
+ base::DictionaryValue* device0 = new base::DictionaryValue();
+ device0->SetString("path", "/dev/fakeserial");
+ base::DictionaryValue* device1 = new base::DictionaryValue();
+ device1->SetString("path", "\\\\COM800\\");
+ devices->Append(device0);
+ devices->Append(device1);
+ SetResult(devices);
SendResponse(true);
return true;
}
protected:
- virtual ~FakeSerialGetPortsFunction() {}
+ virtual ~FakeSerialGetDevicesFunction() {}
};
class FakeEchoSerialConnection : public SerialConnection {
public:
explicit FakeEchoSerialConnection(
const std::string& port,
- int bitrate,
- serial::DataBit databit,
- serial::ParityBit parity,
- serial::StopBit stopbit,
const std::string& owner_extension_id)
- : SerialConnection(port, bitrate, databit, parity, stopbit,
- owner_extension_id),
- opened_(true) {
- Flush();
- opened_ = false;
+ : SerialConnection(port, owner_extension_id),
+ opened_(false) {
}
virtual ~FakeEchoSerialConnection() {
}
- virtual bool Open() {
+ virtual void Open(const OpenCompleteCallback& callback) {
DCHECK(!opened_);
opened_ = true;
+ callback.Run(true);
+ }
+
+ virtual bool Configure(const api::serial::ConnectionOptions& options) {
return true;
}
@@ -78,68 +78,48 @@ class FakeEchoSerialConnection : public SerialConnection {
DCHECK(opened_);
}
- virtual void Flush() {
- DCHECK(opened_);
- buffer_.clear();
+ virtual bool Receive(const ReceiveCompleteCallback& callback) {
+ read_callback_ = callback;
+ return true;
}
- virtual int Read(scoped_refptr<net::IOBufferWithSize> io_buffer) {
- DCHECK(io_buffer->data());
-
- if (buffer_.empty()) {
- return 0;
- }
- char *data = io_buffer->data();
- int bytes_to_copy = io_buffer->size();
- while (bytes_to_copy-- && !buffer_.empty()) {
- *data++ = buffer_.front();
- buffer_.pop_front();
+ virtual bool Send(const std::string& data,
+ const SendCompleteCallback& callback) {
+ callback.Run(data.length(), api::serial::SEND_ERROR_NONE);
+ if (!read_callback_.is_null()) {
+ read_callback_.Run(data, api::serial::RECEIVE_ERROR_NONE);
}
- return io_buffer->size();
+ return true;
}
- virtual int Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count) {
- DCHECK(io_buffer.get());
- DCHECK_GE(byte_count, 0);
-
- char *data = io_buffer->data();
- int count = byte_count;
- while (count--)
- buffer_.push_back(*data++);
- return byte_count;
+ virtual bool GetControlSignals(api::serial::ControlSignals* signals) const {
+ signals->dcd.reset(new bool(true));
+ signals->cts.reset(new bool(true));
+ signals->dtr.reset(new bool(true));
+ signals->ri.reset(new bool(true));
+ return true;
}
- MOCK_METHOD1(GetControlSignals, bool(ControlSignals &));
- MOCK_METHOD1(SetControlSignals, bool(const ControlSignals &));
+ MOCK_METHOD1(SetControlSignals, bool(const api::serial::ControlSignals&));
private:
bool opened_;
- std::deque<char> buffer_;
+ ReceiveCompleteCallback read_callback_;
DISALLOW_COPY_AND_ASSIGN(FakeEchoSerialConnection);
};
-class FakeSerialOpenFunction : public SerialOpenFunction {
+class FakeSerialOpenFunction : public api::SerialOpenFunction {
protected:
virtual SerialConnection* CreateSerialConnection(
const std::string& port,
- int bitrate,
- serial::DataBit databit,
- serial::ParityBit parity,
- serial::StopBit stopbit,
- const std::string& owner_extension_id) OVERRIDE {
+ const std::string& owner_extension_id) const OVERRIDE {
FakeEchoSerialConnection* serial_connection =
- new FakeEchoSerialConnection(port, bitrate, databit, parity, stopbit,
- owner_extension_id);
- EXPECT_CALL(*serial_connection, GetControlSignals(_)).
- Times(1).WillOnce(Return(true));
+ new FakeEchoSerialConnection(port, owner_extension_id);
EXPECT_CALL(*serial_connection, SetControlSignals(_)).
Times(1).WillOnce(Return(true));
return serial_connection;
}
- virtual bool DoesPortExist(const std::string& port) OVERRIDE {
- return true;
- }
protected:
virtual ~FakeSerialOpenFunction() {}
@@ -147,8 +127,8 @@ class FakeSerialOpenFunction : public SerialOpenFunction {
} // namespace extensions
-ExtensionFunction* FakeSerialGetPortsFunctionFactory() {
- return new extensions::FakeSerialGetPortsFunction();
+ExtensionFunction* FakeSerialGetDevicesFunctionFactory() {
+ return new extensions::FakeSerialGetDevicesFunction();
}
ExtensionFunction* FakeSerialOpenFunctionFactory() {
@@ -181,8 +161,8 @@ IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialFakeHardware) {
#if SIMULATE_SERIAL_PORTS
ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
- "serial.getPorts",
- FakeSerialGetPortsFunctionFactory));
+ "serial.getDevices",
+ FakeSerialGetDevicesFunctionFactory));
ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
"serial.open",
FakeSerialOpenFunctionFactory));
diff --git a/chrome/browser/extensions/api/serial/serial_connection.cc b/chrome/browser/extensions/api/serial/serial_connection.cc
index aed373c..66e19cf 100644
--- a/chrome/browser/extensions/api/serial/serial_connection.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection.cc
@@ -8,15 +8,19 @@
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
+#include "base/platform_file.h"
#include "base/strings/string_util.h"
#include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/browser/extensions/api/serial/serial_port_enumerator.h"
#include "chrome/common/extensions/api/serial.h"
-namespace serial = extensions::api::serial;
-
namespace extensions {
-const char kSerialConnectionNotFoundError[] = "Serial connection not found";
+namespace {
+
+const int kDefaultBufferSize = 4096;
+
+}
static base::LazyInstance<ProfileKeyedAPIFactory<
ApiResourceManager<SerialConnection> > >
@@ -29,65 +33,242 @@ ApiResourceManager<SerialConnection>::GetFactoryInstance() {
return &g_factory.Get();
}
-SerialConnection::SerialConnection(const std::string& port, int bitrate,
- serial::DataBit databit,
- serial::ParityBit parity,
- serial::StopBit stopbit,
+SerialConnection::SerialConnection(const std::string& port,
const std::string& owner_extension_id)
- : ApiResource(owner_extension_id), port_(port), bitrate_(bitrate),
- databit_(databit), parity_(parity), stopbit_(stopbit),
- file_(base::kInvalidPlatformFileValue) {
- CHECK_GE(bitrate, 0);
+ : ApiResource(owner_extension_id),
+ port_(port),
+ file_(base::kInvalidPlatformFileValue),
+ persistent_(false),
+ buffer_size_(kDefaultBufferSize),
+ receive_timeout_(0),
+ send_timeout_(0),
+ paused_(false),
+ io_handler_(SerialIoHandler::Create()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
}
SerialConnection::~SerialConnection() {
+ DCHECK(open_complete_.is_null());
+ io_handler_->CancelRead(api::serial::RECEIVE_ERROR_CLOSED);
+ io_handler_->CancelWrite(api::serial::SEND_ERROR_CLOSED);
Close();
}
-bool SerialConnection::Open() {
- bool created = false;
+bool SerialConnection::IsPersistent() const {
+ return persistent();
+}
- // It's the responsibility of the API wrapper around SerialConnection to
- // validate the supplied path against the set of valid port names, and
- // it is a reasonable assumption that serial port names are ASCII.
- CHECK(IsStringASCII(port_));
- base::FilePath file_path(
- base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
+void SerialConnection::set_buffer_size(int buffer_size) {
+ buffer_size_ = buffer_size;
+}
- file_ = base::CreatePlatformFile(file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_READ |
- base::PLATFORM_FILE_EXCLUSIVE_WRITE |
- base::PLATFORM_FILE_TERMINAL_DEVICE, &created, NULL);
- if (file_ == base::kInvalidPlatformFileValue) {
- return false;
+void SerialConnection::set_receive_timeout(int receive_timeout) {
+ receive_timeout_ = receive_timeout;
+}
+
+void SerialConnection::set_send_timeout(int send_timeout) {
+ send_timeout_ = send_timeout;
+}
+
+void SerialConnection::set_paused(bool paused) {
+ paused_ = paused;
+ if (paused) {
+ io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
}
- return PostOpen();
+}
+
+void SerialConnection::Open(const OpenCompleteCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(open_complete_.is_null());
+ open_complete_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&SerialConnection::StartOpen, base::Unretained(this)));
}
void SerialConnection::Close() {
+ DCHECK(open_complete_.is_null());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (file_ != base::kInvalidPlatformFileValue) {
- base::ClosePlatformFile(file_);
+ base::PlatformFile file = file_;
file_ = base::kInvalidPlatformFileValue;
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&SerialConnection::DoClose, file));
}
}
-int SerialConnection::Read(scoped_refptr<net::IOBufferWithSize> io_buffer) {
- DCHECK(io_buffer->data());
- return base::ReadPlatformFileAtCurrentPos(file_, io_buffer->data(),
- io_buffer->size());
+bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!receive_complete_.is_null())
+ return false;
+ receive_complete_ = callback;
+ io_handler_->Read(buffer_size_);
+ receive_timeout_task_.reset();
+ if (receive_timeout_ > 0) {
+ receive_timeout_task_.reset(new TimeoutTask(
+ base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()),
+ base::TimeDelta::FromMilliseconds(receive_timeout_)));
+ }
+ return true;
+}
+
+bool SerialConnection::Send(const std::string& data,
+ const SendCompleteCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!send_complete_.is_null())
+ return false;
+ send_complete_ = callback;
+ io_handler_->Write(data);
+ send_timeout_task_.reset();
+ if (send_timeout_ > 0) {
+ send_timeout_task_.reset(new TimeoutTask(
+ base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()),
+ base::TimeDelta::FromMilliseconds(send_timeout_)));
+ }
+ return true;
}
-int SerialConnection::Write(scoped_refptr<net::IOBuffer> io_buffer,
- int byte_count) {
- DCHECK(io_buffer->data());
- DCHECK_GE(byte_count, 0);
- return base::WritePlatformFileAtCurrentPos(file_, io_buffer->data(),
- byte_count);
+bool SerialConnection::Configure(
+ const api::serial::ConnectionOptions& options) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (options.persistent.get())
+ set_persistent(*options.persistent);
+ if (options.name.get())
+ set_name(*options.name);
+ if (options.buffer_size.get())
+ set_buffer_size(*options.buffer_size);
+ if (options.receive_timeout.get())
+ set_receive_timeout(*options.receive_timeout);
+ if (options.send_timeout.get())
+ set_send_timeout(*options.send_timeout);
+ bool success = ConfigurePort(options);
+ io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
+ return success;
}
-void SerialConnection::Flush() {
- base::FlushPlatformFile(file_);
+void SerialConnection::SetIoHandlerForTest(
+ scoped_refptr<SerialIoHandler> handler) {
+ io_handler_ = handler;
+}
+
+bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ info->paused = paused_;
+ info->persistent = persistent_;
+ info->name = name_;
+ info->buffer_size = buffer_size_;
+ info->receive_timeout = receive_timeout_;
+ info->send_timeout = send_timeout_;
+ return GetPortInfo(info);
+}
+
+void SerialConnection::StartOpen() {
+ DCHECK(!open_complete_.is_null());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
+ const SerialPortEnumerator::StringSet name_set(
+ SerialPortEnumerator::GenerateValidSerialPortNames());
+ base::PlatformFile file = base::kInvalidPlatformFileValue;
+ if (SerialPortEnumerator::DoesPortExist(name_set, port_)) {
+ // It's the responsibility of the API wrapper around SerialConnection 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(IsStringASCII(port_));
+ base::FilePath path(
+ base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
+ int flags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_EXCLUSIVE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE |
+ base::PLATFORM_FILE_ASYNC |
+ base::PLATFORM_FILE_TERMINAL_DEVICE;
+ file = base::CreatePlatformFile(path, flags, NULL, NULL);
+ }
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SerialConnection::FinishOpen, base::Unretained(this), file));
+}
+
+void SerialConnection::FinishOpen(base::PlatformFile file) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!open_complete_.is_null());
+ DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
+ OpenCompleteCallback callback = open_complete_;
+ open_complete_.Reset();
+
+ if (file == base::kInvalidPlatformFileValue) {
+ callback.Run(false);
+ return;
+ }
+
+ file_ = file;
+ io_handler_->Initialize(
+ file_,
+ base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
+ base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
+
+ bool success = PostOpen();
+ if (!success) {
+ Close();
+ }
+
+ callback.Run(success);
+}
+
+// static
+void SerialConnection::DoClose(base::PlatformFile port) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (port != base::kInvalidPlatformFileValue) {
+ base::ClosePlatformFile(port);
+ }
+}
+
+void SerialConnection::OnReceiveTimeout() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT);
+}
+
+void SerialConnection::OnSendTimeout() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT);
+}
+
+void SerialConnection::OnAsyncReadComplete(const std::string& data,
+ api::serial::ReceiveError error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!receive_complete_.is_null());
+ ReceiveCompleteCallback callback = receive_complete_;
+ receive_complete_.Reset();
+ receive_timeout_task_.reset();
+ callback.Run(data, error);
+}
+
+void SerialConnection::OnAsyncWriteComplete(int bytes_sent,
+ api::serial::SendError error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!send_complete_.is_null());
+ SendCompleteCallback callback = send_complete_;
+ send_complete_.Reset();
+ send_timeout_task_.reset();
+ callback.Run(bytes_sent, error);
+}
+
+SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure,
+ const base::TimeDelta& delay)
+ : weak_factory_(this),
+ closure_(closure),
+ delay_(delay) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()),
+ delay_);
+}
+
+SerialConnection::TimeoutTask::~TimeoutTask() {}
+
+void SerialConnection::TimeoutTask::Run() const {
+ closure_.Run();
}
} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_connection.h b/chrome/browser/extensions/api/serial/serial_connection.h
index f881301..6044142 100644
--- a/chrome/browser/extensions/api/serial/serial_connection.h
+++ b/chrome/browser/extensions/api/serial/serial_connection.h
@@ -8,64 +8,137 @@
#include <set>
#include <string>
+#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/platform_file.h"
+#include "base/time/time.h"
#include "chrome/browser/extensions/api/api_resource.h"
#include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/browser/extensions/api/serial/serial_io_handler.h"
#include "chrome/common/extensions/api/serial.h"
#include "content/public/browser/browser_thread.h"
-#include "net/base/io_buffer.h"
+#include "net/base/file_stream.h"
using content::BrowserThread;
-namespace serial = extensions::api::serial;
-
namespace extensions {
-extern const char kSerialConnectionNotFoundError[];
-
// Encapsulates an open serial port. Platform-specific implementations are in
// _win and _posix versions of the the .cc file.
-class SerialConnection : public ApiResource {
+// NOTE: Instances of this object should only be constructed on the IO thread,
+// and all methods should only be called on the IO thread unless otherwise
+// noted.
+class SerialConnection : public ApiResource,
+ public base::SupportsWeakPtr<SerialConnection> {
public:
- SerialConnection(const std::string& port, int bitrate,
- serial::DataBit databit, serial::ParityBit parity,
- serial::StopBit stopbit,
+ typedef base::Callback<void(bool success)> OpenCompleteCallback;
+
+ // This is the callback type expected by Receive. Note that an error result
+ // does not necessarily imply an empty |data| string, since a receive may
+ // complete partially before being interrupted by an error condition.
+ typedef base::Callback<void(const std::string& data,
+ api::serial::ReceiveError error)>
+ ReceiveCompleteCallback;
+
+ // This is the callback type expected by Send. Note that an error result
+ // does not necessarily imply 0 bytes sent, since a send may complete
+ // partially before being interrupted by an error condition.
+ typedef base::Callback<void(int bytes_sent,
+ api::serial::SendError error)>
+ SendCompleteCallback;
+
+ SerialConnection(const std::string& port,
const std::string& owner_extension_id);
virtual ~SerialConnection();
- virtual bool Open();
+ // ApiResource override.
+ virtual bool IsPersistent() const OVERRIDE;
+
+ void set_persistent(bool persistent) { persistent_ = persistent; }
+ bool persistent() const { return persistent_; }
+
+ void set_name(const std::string& name) { name_ = name; }
+ const std::string& name() const { return name_; }
+
+ void set_buffer_size(int buffer_size);
+ int buffer_size() const { return buffer_size_; }
+
+ void set_receive_timeout(int receive_timeout);
+ int receive_timeout() const { return receive_timeout_; }
+
+ void set_send_timeout(int send_timeout);
+ int send_timeout() const { return send_timeout_; }
+
+ void set_paused(bool paused);
+ bool paused() const { return paused_; }
+
+ // Initiates an asynchronous Open of the device. It is the caller's
+ // responsibility to ensure that this SerialConnection stays alive
+ // until |callback| is run.
+ virtual void Open(const OpenCompleteCallback& callback);
+
+ // Initiate a Close of the device. The SerialConnection instance will
+ // have its internal state reset synchronously upon calling this, but
+ // the underlying OS handle will be closed asynchronously.
virtual void Close();
- virtual void Flush();
-
- virtual int Read(scoped_refptr<net::IOBufferWithSize> io_buffer);
- virtual int Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count);
-
- struct ControlSignals {
- // Sent from workstation to device. The should_set_ values indicate whether
- // SetControlSignals should change the given signal (true) or else leave it
- // as-is (false).
- bool should_set_dtr;
- bool dtr;
- bool should_set_rts;
- bool rts;
-
- // Received by workstation from device. DCD (Data Carrier Detect) is
- // equivalent to RLSD (Receive Line Signal Detect) on some platforms.
- bool dcd;
- bool cts;
- };
- virtual bool GetControlSignals(ControlSignals &control_signals);
- virtual bool SetControlSignals(const ControlSignals &control_signals);
+ // Begins an asynchronous receive operation. Calling this while a Receive
+ // is already pending is a no-op and returns |false| without calling
+ // |callback|.
+ virtual bool Receive(const ReceiveCompleteCallback& callback);
+
+ // Begins an asynchronous send operation. Calling this while a Send
+ // is already pending is a no-op and returns |false| without calling
+ // |callback|.
+ virtual bool Send(const std::string& data,
+ const SendCompleteCallback& callback);
- static const BrowserThread::ID kThreadId = BrowserThread::FILE;
+ // Flushes input and output buffers.
+ virtual bool Flush() const;
+
+ // Configures some subset of port options for this connection.
+ // Omitted options are unchanged. Returns |true| iff the configuration
+ // changes were successful.
+ virtual bool Configure(const api::serial::ConnectionOptions& options);
+
+ // Connection configuration query. Fills values in an existing
+ // ConnectionInfo. Returns |true| iff the connection's information
+ // was successfully retrieved.
+ virtual bool GetInfo(api::serial::ConnectionInfo* info) const;
+
+ // Reads current control signals (DCD, CTS, etc.) into an existing
+ // ControlSignals structure. Returns |true| iff the signals were
+ // successfully read.
+ virtual bool GetControlSignals(api::serial::ControlSignals* control_signals)
+ const;
+
+ // Sets one or more control signals (DTR and/or RTS). Returns |true| iff
+ // the signals were successfully set. Unininitialized flags in the
+ // ControlSignals structure are left unchanged.
+ virtual bool SetControlSignals(
+ const api::serial::ControlSignals& control_signals);
+
+ static const BrowserThread::ID kThreadId = BrowserThread::IO;
protected:
- // Do platform-specific work after a successful Open().
+ // Overrides |io_handler_| for testing.
+ virtual void SetIoHandlerForTest(scoped_refptr<SerialIoHandler> handler);
+
+ // Performs platform-specific, one-time port configuration on open.
bool PostOpen();
- // Platform-specific port name adapter
+ // Performs platform-specific port configuration. Returns |true| iff
+ // configuration was successful.
+ bool ConfigurePort(const api::serial::ConnectionOptions& options);
+
+ // Performs a platform-specific port configuration query. Fills values in an
+ // existing ConnectionInfo. Returns |true| iff port configuration was
+ // successfully retrieved.
+ bool GetPortInfo(api::serial::ConnectionInfo* info) const;
+
+ // Possibly fixes up a serial port path name in a platform-specific manner.
static std::string MaybeFixUpPortName(const std::string &port_name);
private:
@@ -73,12 +146,93 @@ class SerialConnection : public ApiResource {
static const char* service_name() {
return "SerialConnectionManager";
}
+
+ // Encapsulates a cancelable, delayed timeout task. Posts a delayed
+ // task upon construction and implicitly cancels the task upon
+ // destruction if it hasn't run yet.
+ class TimeoutTask {
+ public:
+ TimeoutTask(const base::Closure& closure, const base::TimeDelta& delay);
+ ~TimeoutTask();
+
+ private:
+ void Run() const;
+
+ base::WeakPtrFactory<TimeoutTask> weak_factory_;
+ base::Closure closure_;
+ base::TimeDelta delay_;
+ };
+
+ // Continues an Open operation on the FILE thread.
+ void StartOpen();
+
+ // Finalizes an Open operation (continued from StartOpen) on the IO thread.
+ void FinishOpen(base::PlatformFile file);
+
+ // Continues a Close operation on the FILE thread.
+ static void DoClose(base::PlatformFile port);
+
+ // Handles a receive timeout.
+ void OnReceiveTimeout();
+
+ // Handles a send timeout.
+ void OnSendTimeout();
+
+ // Receives read completion notification from the |io_handler_|.
+ void OnAsyncReadComplete(const std::string& data,
+ api::serial::ReceiveError error);
+
+ // Receives write completion notification from the |io_handler_|.
+ void OnAsyncWriteComplete(int bytes_sent, api::serial::SendError error);
+
+ // The pathname of the serial device.
std::string port_;
- int bitrate_;
- serial::DataBit databit_;
- serial::ParityBit parity_;
- serial::StopBit stopbit_;
+
+ // File handle for the opened serial device. This value is only modified from
+ // the IO thread.
base::PlatformFile file_;
+
+ // Flag indicating whether or not the connection should persist when
+ // its host app is suspended.
+ bool persistent_;
+
+ // User-specified connection name.
+ std::string name_;
+
+ // Size of the receive buffer.
+ int buffer_size_;
+
+ // Amount of time (in ms) to wait for a Read to succeed before triggering a
+ // timeout response via onReceiveError.
+ int receive_timeout_;
+
+ // Amount of time (in ms) to wait for a Write to succeed before triggering
+ // a timeout response.
+ int send_timeout_;
+
+ // Flag indicating that the connection is paused. A paused connection will not
+ // raise new onReceive events.
+ bool paused_;
+
+ // Callback to handle the completion of a pending Open() request.
+ OpenCompleteCallback open_complete_;
+
+ // Callback to handle the completion of a pending Receive() request.
+ ReceiveCompleteCallback receive_complete_;
+
+ // Callback to handle the completion of a pending Send() request.
+ SendCompleteCallback send_complete_;
+
+ // Closure which will trigger a receive timeout unless cancelled. Reset on
+ // initialization and after every successful Receive().
+ scoped_ptr<TimeoutTask> receive_timeout_task_;
+
+ // Write timeout closure. Reset on initialization and after every successful
+ // Send().
+ scoped_ptr<TimeoutTask> send_timeout_task_;
+
+ // Asynchronous I/O handler.
+ scoped_refptr<SerialIoHandler> io_handler_;
};
} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_connection_posix.cc b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
index f70a4cf..014797a 100644
--- a/chrome/browser/extensions/api/serial/serial_connection_posix.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
@@ -2,172 +2,273 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/posix/eintr_wrapper.h"
#include "chrome/browser/extensions/api/serial/serial_connection.h"
#include <sys/ioctl.h>
#include <termios.h>
+#if defined(OS_LINUX)
+#include <linux/serial.h>
+#endif
+
namespace extensions {
namespace {
- int getBaudRate(int bitrate_) {
- switch (bitrate_) {
- case 0:
- return B0;
- case 50:
- return B50;
- case 75:
- return B75;
- case 110:
- return B110;
- case 134:
- return B134;
- case 150:
- return B150;
- case 200:
- return B200;
- case 300:
- return B300;
- case 600:
- return B600;
- case 1200:
- return B1200;
- case 1800:
- return B1800;
- case 2400:
- return B2400;
- case 4800:
- return B4800;
- case 9600:
- return B9600;
- case 19200:
- return B19200;
- case 38400:
- return B38400;
+
+// Convert an integral bit rate to a nominal one. Returns |true|
+// if the conversion was successful and |false| otherwise.
+bool BitrateToSpeedConstant(int bitrate, speed_t* speed) {
+#define BITRATE_TO_SPEED_CASE(x) case x: *speed = B ## x; return true;
+ switch (bitrate) {
+ BITRATE_TO_SPEED_CASE(0)
+ BITRATE_TO_SPEED_CASE(50)
+ BITRATE_TO_SPEED_CASE(75)
+ BITRATE_TO_SPEED_CASE(110)
+ BITRATE_TO_SPEED_CASE(134)
+ BITRATE_TO_SPEED_CASE(150)
+ BITRATE_TO_SPEED_CASE(200)
+ BITRATE_TO_SPEED_CASE(300)
+ BITRATE_TO_SPEED_CASE(600)
+ BITRATE_TO_SPEED_CASE(1200)
+ BITRATE_TO_SPEED_CASE(1800)
+ BITRATE_TO_SPEED_CASE(2400)
+ BITRATE_TO_SPEED_CASE(4800)
+ BITRATE_TO_SPEED_CASE(9600)
+ BITRATE_TO_SPEED_CASE(19200)
+ BITRATE_TO_SPEED_CASE(38400)
#if defined(OS_POSIX) && !defined(OS_MACOSX)
- case 57600:
- return B57600;
- case 115200:
- return B115200;
- case 230400:
- return B230400;
- case 460800:
- return B460800;
- case 576000:
- return B576000;
- case 921600:
- return B921600;
- default:
- return B9600;
-#else
-// MACOSX doesn't define constants bigger than 38400.
-// So if it is MACOSX and the value doesn't fit any of the defined constants
-// It will setup the bitrate with 'bitrate_' (just forwarding the value)
- default:
- return bitrate_;
+ BITRATE_TO_SPEED_CASE(57600)
+ BITRATE_TO_SPEED_CASE(115200)
+ BITRATE_TO_SPEED_CASE(230400)
+ BITRATE_TO_SPEED_CASE(460800)
+ BITRATE_TO_SPEED_CASE(576000)
+ BITRATE_TO_SPEED_CASE(921600)
#endif
- }
+ default:
+ return false;
}
-} // namespace
+#undef BITRATE_TO_SPEED_CASE
+}
-bool SerialConnection::PostOpen() {
- struct termios options;
+// Convert a known nominal speed into an integral bitrate. Returns |true|
+// if the conversion was successful and |false| otherwise.
+bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
+#define SPEED_TO_BITRATE_CASE(x) case B ## x: *bitrate = x; return true;
+ switch (speed) {
+ SPEED_TO_BITRATE_CASE(0)
+ SPEED_TO_BITRATE_CASE(50)
+ SPEED_TO_BITRATE_CASE(75)
+ SPEED_TO_BITRATE_CASE(110)
+ SPEED_TO_BITRATE_CASE(134)
+ SPEED_TO_BITRATE_CASE(150)
+ SPEED_TO_BITRATE_CASE(200)
+ SPEED_TO_BITRATE_CASE(300)
+ SPEED_TO_BITRATE_CASE(600)
+ SPEED_TO_BITRATE_CASE(1200)
+ SPEED_TO_BITRATE_CASE(1800)
+ SPEED_TO_BITRATE_CASE(2400)
+ SPEED_TO_BITRATE_CASE(4800)
+ SPEED_TO_BITRATE_CASE(9600)
+ SPEED_TO_BITRATE_CASE(19200)
+ SPEED_TO_BITRATE_CASE(38400)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ SPEED_TO_BITRATE_CASE(57600)
+ SPEED_TO_BITRATE_CASE(115200)
+ SPEED_TO_BITRATE_CASE(230400)
+ SPEED_TO_BITRATE_CASE(460800)
+ SPEED_TO_BITRATE_CASE(576000)
+ SPEED_TO_BITRATE_CASE(921600)
+#endif
+ default:
+ return false;
+ }
+#undef SPEED_TO_BITRATE_CASE
+}
- // Start with existing options and modify.
- tcgetattr(file_, &options);
+bool SetCustomBitrate(base::PlatformFile file,
+ struct termios* config,
+ int bitrate) {
+#if defined(OS_LINUX)
+ struct serial_struct serial;
+ if (ioctl(file, TIOCGSERIAL, &serial) < 0) {
+ return false;
+ }
+ serial.flags = (serial.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
+ serial.custom_divisor = serial.baud_base / bitrate;
+ if (serial.custom_divisor < 1) {
+ serial.custom_divisor = 1;
+ }
+ cfsetispeed(config, B38400);
+ cfsetospeed(config, B38400);
+ return ioctl(file, TIOCSSERIAL, &serial) >= 0;
+#else
+ return false;
+#endif
+}
- // Bitrate (sometimes erroneously referred to as baud rate).
- if (bitrate_ >= 0) {
- int bitrate_opt_ = getBaudRate(bitrate_);
+} // namespace
- cfsetispeed(&options, bitrate_opt_);
- cfsetospeed(&options, bitrate_opt_);
+bool SerialConnection::ConfigurePort(
+ const api::serial::ConnectionOptions& options) {
+ struct termios config;
+ tcgetattr(file_, &config);
+ if (options.bitrate.get()) {
+ if (*options.bitrate >= 0) {
+ speed_t bitrate_opt = B0;
+ if (BitrateToSpeedConstant(*options.bitrate, &bitrate_opt)) {
+ cfsetispeed(&config, bitrate_opt);
+ cfsetospeed(&config, bitrate_opt);
+ } else {
+ // Attempt to set a custom speed.
+ if (!SetCustomBitrate(file_, &config, *options.bitrate)) {
+ return false;
+ }
+ }
+ }
}
-
- options.c_cflag &= ~CSIZE;
- switch (databit_) {
- case serial::DATA_BIT_SEVENBIT:
- options.c_cflag |= CS7;
- break;
- case serial::DATA_BIT_EIGHTBIT:
- default:
- options.c_cflag |= CS8;
- break;
- }
- switch (stopbit_) {
- case serial::STOP_BIT_TWOSTOPBIT:
- options.c_cflag |= CSTOPB;
- break;
- case serial::STOP_BIT_ONESTOPBIT:
- default:
- options.c_cflag &= ~CSTOPB;
- break;
- }
- switch (parity_) {
- case serial::PARITY_BIT_EVENPARITY:
- options.c_cflag |= PARENB;
- options.c_cflag &= ~PARODD;
- break;
- case serial::PARITY_BIT_ODDPARITY:
- options.c_cflag |= (PARENB | PARODD);
- break;
- case serial::PARITY_BIT_NOPARITY:
- default:
- options.c_cflag &= ~(PARENB | PARODD);
- break;
+ if (options.data_bits != api::serial::DATA_BITS_NONE) {
+ config.c_cflag &= ~CSIZE;
+ switch (options.data_bits) {
+ case api::serial::DATA_BITS_SEVEN:
+ config.c_cflag |= CS7;
+ break;
+ case api::serial::DATA_BITS_EIGHT:
+ default:
+ config.c_cflag |= CS8;
+ break;
+ }
}
+ if (options.parity_bit != api::serial::PARITY_BIT_NONE) {
+ switch (options.parity_bit) {
+ case api::serial::PARITY_BIT_EVEN:
+ config.c_cflag |= PARENB;
+ config.c_cflag &= ~PARODD;
+ break;
+ case api::serial::PARITY_BIT_ODD:
+ config.c_cflag |= (PARODD | PARENB);
+ break;
+ case api::serial::PARITY_BIT_NO:
+ default:
+ config.c_cflag &= ~(PARODD | PARENB);
+ break;
+ }
+ }
+ if (options.stop_bits != api::serial::STOP_BITS_NONE) {
+ switch (options.stop_bits) {
+ case api::serial::STOP_BITS_TWO:
+ config.c_cflag |= CSTOPB;
+ break;
+ case api::serial::STOP_BITS_ONE:
+ default:
+ config.c_cflag &= ~CSTOPB;
+ break;
+ }
+ }
+ if (options.cts_flow_control.get()) {
+ if (*options.cts_flow_control){
+ config.c_cflag |= CRTSCTS;
+ } else {
+ config.c_cflag &= ~CRTSCTS;
+ }
+ }
+ return tcsetattr(file_, TCSANOW, &config) == 0;
+}
+
+bool SerialConnection::PostOpen() {
+ struct termios config;
+ tcgetattr(file_, &config);
// Set flags for 'raw' operation
- // At least on Linux the flags are persistent and thus we cannot trust
- // the default values.
- options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
- options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
- ICRNL | IXON);
- options.c_oflag &= ~OPOST;
+ config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
+ config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
+ ICRNL | IXON);
+ config.c_oflag &= ~OPOST;
- // Enable receiver and set local mode
- // See http://www.easysw.com/~mike/serial/serial.html to understand.
- options.c_cflag |= (CLOCAL | CREAD);
+ // CLOCAL causes the system to disregard the DCD signal state.
+ // CREAD enables reading from the port.
+ config.c_cflag |= (CLOCAL | CREAD);
- // Write the options.
- tcsetattr(file_, TCSANOW, &options);
+ return tcsetattr(file_, TCSANOW, &config) == 0;
+}
- return true;
+bool SerialConnection::Flush() const {
+ return tcflush(file_, TCIOFLUSH) == 0;
}
-bool SerialConnection::GetControlSignals(ControlSignals &control_signals) {
+bool SerialConnection::GetControlSignals(api::serial::ControlSignals* signals)
+ const {
int status;
- if (ioctl(file_, TIOCMGET, &status) == 0) {
- control_signals.dcd = (status & TIOCM_CAR) != 0;
- control_signals.cts = (status & TIOCM_CTS) != 0;
- return true;
+ if (ioctl(file_, TIOCMGET, &status) == -1) {
+ return false;
}
- return false;
+
+ signals->dcd.reset(new bool(status & TIOCM_CAR));
+ signals->cts.reset(new bool(status & TIOCM_CTS));
+ signals->dsr.reset(new bool(status & TIOCM_DSR));
+ signals->ri.reset(new bool(status & TIOCM_RI));
+ return true;
}
bool SerialConnection::SetControlSignals(
- const ControlSignals &control_signals) {
+ const api::serial::ControlSignals& signals) {
int status;
- if (ioctl(file_, TIOCMGET, &status) != 0)
+ if (ioctl(file_, TIOCMGET, &status) == -1) {
return false;
+ }
- if (control_signals.should_set_dtr) {
- if (control_signals.dtr)
+ if (signals.dtr.get()) {
+ if (*signals.dtr) {
status |= TIOCM_DTR;
- else
+ } else {
status &= ~TIOCM_DTR;
+ }
}
- if (control_signals.should_set_rts) {
- if (control_signals.rts)
+
+ if (signals.rts.get()) {
+ if (*signals.rts){
status |= TIOCM_RTS;
- else
+ } else{
status &= ~TIOCM_RTS;
+ }
}
return ioctl(file_, TIOCMSET, &status) == 0;
}
+bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
+ struct termios config;
+ if (tcgetattr(file_, &config) == -1) {
+ return false;
+ }
+ speed_t ispeed = cfgetispeed(&config);
+ speed_t ospeed = cfgetospeed(&config);
+ if (ispeed == ospeed) {
+ int bitrate = 0;
+ if (SpeedConstantToBitrate(ispeed, &bitrate)) {
+ info->bitrate.reset(new int(bitrate));
+ }
+ }
+ if ((config.c_cflag & CSIZE) == CS7) {
+ info->data_bits = api::serial::DATA_BITS_SEVEN;
+ } else if ((config.c_cflag & CSIZE) == CS8) {
+ info->data_bits = api::serial::DATA_BITS_EIGHT;
+ } else {
+ info->data_bits = api::serial::DATA_BITS_NONE;
+ }
+ if (config.c_cflag & PARENB) {
+ info->parity_bit = (config.c_cflag & PARODD) ? api::serial::PARITY_BIT_ODD
+ : api::serial::PARITY_BIT_EVEN;
+ } else {
+ info->parity_bit = api::serial::PARITY_BIT_NO;
+ }
+ info->stop_bits = (config.c_cflag & CSTOPB) ? api::serial::STOP_BITS_TWO
+ : api::serial::STOP_BITS_ONE;
+ info->cts_flow_control.reset(new bool((config.c_cflag & CRTSCTS) != 0));
+ return true;
+}
+
std::string SerialConnection::MaybeFixUpPortName(
const std::string &port_name) {
return port_name;
diff --git a/chrome/browser/extensions/api/serial/serial_connection_win.cc b/chrome/browser/extensions/api/serial/serial_connection_win.cc
index d6d9b12..876f1b3 100644
--- a/chrome/browser/extensions/api/serial/serial_connection_win.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection_win.cc
@@ -11,102 +11,225 @@
namespace extensions {
namespace {
-int getBaudRate(int bitrate_) {
- switch (bitrate_) {
- case 110: return CBR_110;
- case 300: return CBR_300;
- case 600: return CBR_600;
- case 1200: return CBR_1200;
- case 2400: return CBR_2400;
- case 4800: return CBR_4800;
- case 9600: return CBR_9600;
- case 14400: return CBR_14400;
- case 19200: return CBR_19200;
- case 38400: return CBR_38400;
- case 57600: return CBR_57600;
- case 115200: return CBR_115200;
- case 128000: return CBR_128000;
- case 256000: return CBR_256000;
- default: return CBR_9600;
+
+int BitrateToSpeedConstant(int bitrate) {
+#define BITRATE_TO_SPEED_CASE(x) case x: return CBR_ ## x;
+ switch (bitrate) {
+ BITRATE_TO_SPEED_CASE(110);
+ BITRATE_TO_SPEED_CASE(300);
+ BITRATE_TO_SPEED_CASE(600);
+ BITRATE_TO_SPEED_CASE(1200);
+ BITRATE_TO_SPEED_CASE(2400);
+ BITRATE_TO_SPEED_CASE(4800);
+ BITRATE_TO_SPEED_CASE(9600);
+ BITRATE_TO_SPEED_CASE(14400);
+ BITRATE_TO_SPEED_CASE(19200);
+ BITRATE_TO_SPEED_CASE(38400);
+ BITRATE_TO_SPEED_CASE(57600);
+ BITRATE_TO_SPEED_CASE(115200);
+ BITRATE_TO_SPEED_CASE(128000);
+ BITRATE_TO_SPEED_CASE(256000);
+ default:
+ // If the bitrate doesn't match that of one of the standard
+ // index constants, it may be provided as-is to the DCB
+ // structure, according to MSDN.
+ return bitrate;
}
+#undef BITRATE_TO_SPEED_CASE
}
-int getDataBit(serial::DataBit databit) {
- switch (databit) {
- case serial::DATA_BIT_SEVENBIT:
+int DataBitsEnumToConstant(api::serial::DataBits data_bits) {
+ switch (data_bits) {
+ case api::serial::DATA_BITS_SEVEN:
return 7;
- case serial::DATA_BIT_EIGHTBIT:
+ case api::serial::DATA_BITS_EIGHT:
default:
return 8;
}
}
-int getParity(serial::ParityBit parity) {
- switch (parity) {
- case serial::PARITY_BIT_EVENPARITY:
+int ParityBitEnumToConstant(api::serial::ParityBit parity_bit) {
+ switch (parity_bit) {
+ case api::serial::PARITY_BIT_EVEN:
return EVENPARITY;
- case serial::PARITY_BIT_ODDPARITY:
+ case api::serial::PARITY_BIT_ODD:
return SPACEPARITY;
- case serial::PARITY_BIT_NOPARITY:
+ case api::serial::PARITY_BIT_NO:
default:
return NOPARITY;
}
}
-int getStopBit(serial::StopBit stopbit) {
- switch (stopbit) {
- case serial::STOP_BIT_TWOSTOPBIT:
+int StopBitsEnumToConstant(api::serial::StopBits stop_bits) {
+ switch (stop_bits) {
+ case api::serial::STOP_BITS_TWO:
return TWOSTOPBITS;
- case serial::STOP_BIT_ONESTOPBIT:
+ case api::serial::STOP_BITS_ONE:
default:
return ONESTOPBIT;
}
}
+
+int SpeedConstantToBitrate(int speed) {
+#define SPEED_TO_BITRATE_CASE(x) case CBR_ ## x: return x;
+ switch (speed) {
+ SPEED_TO_BITRATE_CASE(110);
+ SPEED_TO_BITRATE_CASE(300);
+ SPEED_TO_BITRATE_CASE(600);
+ SPEED_TO_BITRATE_CASE(1200);
+ SPEED_TO_BITRATE_CASE(2400);
+ SPEED_TO_BITRATE_CASE(4800);
+ SPEED_TO_BITRATE_CASE(9600);
+ SPEED_TO_BITRATE_CASE(14400);
+ SPEED_TO_BITRATE_CASE(19200);
+ SPEED_TO_BITRATE_CASE(38400);
+ SPEED_TO_BITRATE_CASE(57600);
+ SPEED_TO_BITRATE_CASE(115200);
+ SPEED_TO_BITRATE_CASE(128000);
+ SPEED_TO_BITRATE_CASE(256000);
+ default:
+ // If it's not one of the standard index constants,
+ // it should be an integral baud rate, according to
+ // MSDN.
+ return speed;
+ }
+#undef SPEED_TO_BITRATE_CASE
+}
+
+api::serial::DataBits DataBitsConstantToEnum(int data_bits) {
+ switch (data_bits) {
+ case 7:
+ return api::serial::DATA_BITS_SEVEN;
+ case 8:
+ default:
+ return api::serial::DATA_BITS_EIGHT;
+ }
+}
+
+api::serial::ParityBit ParityBitConstantToEnum(int parity_bit) {
+ switch (parity_bit) {
+ case EVENPARITY:
+ return api::serial::PARITY_BIT_EVEN;
+ case ODDPARITY:
+ return api::serial::PARITY_BIT_ODD;
+ case NOPARITY:
+ default:
+ return api::serial::PARITY_BIT_NO;
+ }
+}
+
+api::serial::StopBits StopBitsConstantToEnum(int stop_bits) {
+ switch (stop_bits) {
+ case TWOSTOPBITS:
+ return api::serial::STOP_BITS_TWO;
+ case ONESTOPBIT:
+ default:
+ return api::serial::STOP_BITS_ONE;
+ }
+}
+
} // namespace
-bool SerialConnection::PostOpen() {
- // Set timeouts so that reads return immediately with whatever could be read
- // without blocking.
- COMMTIMEOUTS timeouts = { 0 };
- timeouts.ReadIntervalTimeout = MAXDWORD;
- if (!::SetCommTimeouts(file_, &timeouts))
+bool SerialConnection::ConfigurePort(
+ const api::serial::ConnectionOptions& options) {
+ DCB config = { 0 };
+ config.DCBlength = sizeof(config);
+ if (!GetCommState(file_, &config)) {
return false;
+ }
+ if (options.bitrate.get())
+ config.BaudRate = BitrateToSpeedConstant(*options.bitrate);
+ if (options.data_bits != api::serial::DATA_BITS_NONE)
+ config.ByteSize = DataBitsEnumToConstant(options.data_bits);
+ if (options.parity_bit != api::serial::PARITY_BIT_NONE)
+ config.Parity = ParityBitEnumToConstant(options.parity_bit);
+ if (options.stop_bits != api::serial::STOP_BITS_NONE)
+ config.StopBits = StopBitsEnumToConstant(options.stop_bits);
+ if (options.cts_flow_control.get()) {
+ if (*options.cts_flow_control) {
+ config.fOutxCtsFlow = TRUE;
+ config.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ } else {
+ config.fOutxCtsFlow = FALSE;
+ config.fRtsControl = RTS_CONTROL_ENABLE;
+ }
+ }
+ return SetCommState(file_, &config) != 0;
+}
- DCB dcb = { 0 };
- dcb.DCBlength = sizeof(dcb);
- if (!GetCommState(file_, &dcb))
+bool SerialConnection::PostOpen() {
+ // Set a very brief read interval timeout. This prevents the asynchronous I/O
+ // system from being way too eager to fire off completion events which would
+ // in turn result in a lot of onReceive events being fired (i.e., one for
+ // every individual byte received on the serial buffer.)
+ COMMTIMEOUTS timeouts = { 0 };
+ timeouts.ReadIntervalTimeout = 10;
+ if (!::SetCommTimeouts(file_, &timeouts)) {
return false;
+ }
- dcb.BaudRate = getBaudRate(bitrate_);
- dcb.ByteSize = getDataBit(databit_);
- dcb.Parity = getParity(parity_);
- dcb.StopBits = getStopBit(stopbit_);
-
- if (!SetCommState(file_, &dcb))
+ DCB config = { 0 };
+ config.DCBlength = sizeof(config);
+ if (!GetCommState(file_, &config)) {
return false;
+ }
+ // Setup some sane default state.
+ config.fBinary = TRUE;
+ config.fParity = FALSE;
+ config.fAbortOnError = TRUE;
+ config.fOutxCtsFlow = FALSE;
+ config.fOutxDsrFlow = FALSE;
+ config.fRtsControl = RTS_CONTROL_ENABLE;
+ config.fDtrControl = DTR_CONTROL_ENABLE;
+ config.fDsrSensitivity = FALSE;
+ config.fOutX = FALSE;
+ config.fInX = FALSE;
+ return SetCommState(file_, &config) != 0;
+}
- return true;
+bool SerialConnection::Flush() const {
+ return PurgeComm(file_, PURGE_RXCLEAR | PURGE_TXCLEAR) != 0;
}
-bool SerialConnection::GetControlSignals(ControlSignals &control_signals) {
- DWORD dwModemStatus;
- if (!GetCommModemStatus(file_, &dwModemStatus))
+bool SerialConnection::GetControlSignals(api::serial::ControlSignals* signals)
+ const {
+ DWORD status;
+ if (!GetCommModemStatus(file_, &status)) {
return false;
- control_signals.dcd = (MS_RLSD_ON & dwModemStatus) != 0;
- control_signals.cts = (MS_CTS_ON & dwModemStatus) != 0;
+ }
+ signals->dcd.reset(new bool((status & MS_RLSD_ON) != 0));
+ signals->cts.reset(new bool((status & MS_CTS_ON) != 0));
+ signals->dsr.reset(new bool((status & MS_DSR_ON) != 0));
+ signals->ri.reset(new bool((status & MS_RING_ON) != 0));
return true;
}
bool SerialConnection::SetControlSignals(
- const ControlSignals &control_signals) {
- if (control_signals.should_set_dtr) {
- if (!EscapeCommFunction(file_, control_signals.dtr ? SETDTR : CLRDTR))
+ const api::serial::ControlSignals& signals) {
+ if (signals.dtr.get()) {
+ if (!EscapeCommFunction(file_, *signals.dtr ? SETDTR : CLRDTR)) {
return false;
+ }
}
- if (control_signals.should_set_rts) {
- if (!EscapeCommFunction(file_, control_signals.rts ? SETRTS : CLRRTS))
+ if (signals.rts.get()) {
+ if (!EscapeCommFunction(file_, *signals.rts ? SETRTS : CLRRTS)) {
return false;
+ }
+ }
+ return true;
+}
+
+bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
+ DCB config = { 0 };
+ config.DCBlength = sizeof(config);
+ if (!GetCommState(file_, &config)) {
+ return false;
}
+ info->bitrate.reset(new int(SpeedConstantToBitrate(config.BaudRate)));
+ info->data_bits = DataBitsConstantToEnum(config.ByteSize);
+ info->parity_bit = ParityBitConstantToEnum(config.Parity);
+ info->stop_bits = StopBitsConstantToEnum(config.StopBits);
+ info->cts_flow_control.reset(new bool(config.fOutxCtsFlow != 0));
return true;
}
diff --git a/chrome/browser/extensions/api/serial/serial_event_dispatcher.cc b/chrome/browser/extensions/api/serial/serial_event_dispatcher.cc
new file mode 100644
index 0000000..5d475c2
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_event_dispatcher.cc
@@ -0,0 +1,158 @@
+// Copyright 2013 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 "chrome/browser/extensions/api/serial/serial_event_dispatcher.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/serial/serial_connection.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+
+namespace extensions {
+
+namespace api {
+
+namespace {
+
+bool ShouldPauseOnReceiveError(serial::ReceiveError error) {
+ return error == serial::RECEIVE_ERROR_DEVICE_LOST ||
+ error == serial::RECEIVE_ERROR_SYSTEM_ERROR ||
+ error == serial::RECEIVE_ERROR_CLOSED;
+}
+
+} // namespace
+
+static base::LazyInstance<ProfileKeyedAPIFactory<SerialEventDispatcher> >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ProfileKeyedAPIFactory<SerialEventDispatcher>*
+ SerialEventDispatcher::GetFactoryInstance() {
+ return &g_factory.Get();
+}
+
+// static
+SerialEventDispatcher* SerialEventDispatcher::Get(Profile* profile) {
+ return ProfileKeyedAPIFactory<SerialEventDispatcher>::GetForProfile(profile);
+}
+
+SerialEventDispatcher::SerialEventDispatcher(Profile* profile)
+ : thread_id_(SerialConnection::kThreadId),
+ profile_(profile) {
+ ApiResourceManager<SerialConnection>* manager =
+ ApiResourceManager<SerialConnection>::Get(profile);
+ DCHECK(manager) << "No serial connection manager.";
+ connections_ = manager->data_;
+}
+
+SerialEventDispatcher::~SerialEventDispatcher() {}
+
+SerialEventDispatcher::ReceiveParams::ReceiveParams() {}
+
+SerialEventDispatcher::ReceiveParams::~ReceiveParams() {}
+
+void SerialEventDispatcher::PollConnection(const std::string& extension_id,
+ int connection_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ ReceiveParams params;
+ params.thread_id = thread_id_;
+ params.profile_id = profile_;
+ params.extension_id = extension_id;
+ params.connections = connections_;
+ params.connection_id = connection_id;
+
+ StartReceive(params);
+}
+
+// static
+void SerialEventDispatcher::StartReceive(const ReceiveParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ SerialConnection* connection =
+ params.connections->Get(params.extension_id, params.connection_id);
+ if (!connection)
+ return;
+ DCHECK(params.extension_id == connection->owner_extension_id());
+
+ if (connection->paused())
+ return;
+
+ connection->Receive(base::Bind(&ReceiveCallback, params));
+}
+
+// static
+void SerialEventDispatcher::ReceiveCallback(const ReceiveParams& params,
+ const std::string& data,
+ serial::ReceiveError error) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ // Note that an error (e.g. timeout) does not necessarily mean that no data
+ // was read, so we may fire an onReceive regardless of any error code.
+ if (data.length() > 0) {
+ serial::ReceiveInfo receive_info;
+ receive_info.connection_id = params.connection_id;
+ receive_info.data = data;
+ scoped_ptr<base::ListValue> args = serial::OnReceive::Create(receive_info);
+ scoped_ptr<extensions::Event> event(
+ new extensions::Event(serial::OnReceive::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+ }
+
+ if (error != serial::RECEIVE_ERROR_NONE) {
+ serial::ReceiveErrorInfo error_info;
+ error_info.connection_id = params.connection_id;
+ error_info.error = error;
+ scoped_ptr<base::ListValue> args =
+ serial::OnReceiveError::Create(error_info);
+ scoped_ptr<extensions::Event> event(
+ new extensions::Event(serial::OnReceiveError::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+ if (ShouldPauseOnReceiveError(error)) {
+ SerialConnection* connection =
+ params.connections->Get(params.extension_id, params.connection_id);
+ if (connection)
+ connection->set_paused(true);
+ }
+ }
+
+ // Queue up the next read operation.
+ BrowserThread::PostTask(params.thread_id,
+ FROM_HERE,
+ base::Bind(&StartReceive, params));
+}
+
+// static
+void SerialEventDispatcher::PostEvent(const ReceiveParams& params,
+ scoped_ptr<extensions::Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DispatchEvent,
+ params.profile_id,
+ params.extension_id,
+ base::Passed(event.Pass())));
+}
+
+// static
+void SerialEventDispatcher::DispatchEvent(void* profile_id,
+ const std::string& extension_id,
+ scoped_ptr<extensions::Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ Profile* profile = reinterpret_cast<Profile*>(profile_id);
+ if (!g_browser_process->profile_manager()->IsValidProfile(profile))
+ return;
+
+ EventRouter* router = ExtensionSystem::Get(profile)->event_router();
+ if (router)
+ router->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+} // namespace api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_event_dispatcher.h b/chrome/browser/extensions/api/serial/serial_event_dispatcher.h
new file mode 100644
index 0000000..7ed44f9
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_event_dispatcher.h
@@ -0,0 +1,76 @@
+// Copyright 2013 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 CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_EVENT_DISPATCHER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_EVENT_DISPATCHER_H_
+
+#include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/common/extensions/api/serial.h"
+
+namespace extensions {
+
+struct Event;
+class SerialConnection;
+
+namespace api {
+
+// Per-profile dispatcher for events on serial connections.
+class SerialEventDispatcher : public ProfileKeyedAPI {
+ public:
+ explicit SerialEventDispatcher(Profile* profile);
+ virtual ~SerialEventDispatcher();
+
+ // Start receiving data and firing events for a connection.
+ void PollConnection(const std::string& extension_id, int connection_id);
+
+ static SerialEventDispatcher* Get(Profile* profile);
+
+ // ProfileKeyedAPI implementation.
+ static ProfileKeyedAPIFactory<SerialEventDispatcher>* GetFactoryInstance();
+
+ private:
+ typedef ApiResourceManager<SerialConnection>::ApiResourceData ConnectionData;
+ friend class ProfileKeyedAPIFactory<SerialEventDispatcher>;
+
+ // ProfileKeyedAPI implementation.
+ static const char *service_name() {
+ return "SerialEventDispatcher";
+ }
+ static const bool kServiceHasOwnInstanceInIncognito = true;
+ static const bool kServiceIsNULLWhileTesting = true;
+
+ struct ReceiveParams {
+ ReceiveParams();
+ ~ReceiveParams();
+
+ content::BrowserThread::ID thread_id;
+ void* profile_id;
+ std::string extension_id;
+ scoped_refptr<ConnectionData> connections;
+ int connection_id;
+ };
+
+ static void StartReceive(const ReceiveParams& params);
+
+ static void ReceiveCallback(const ReceiveParams& params,
+ const std::string& data,
+ serial::ReceiveError error);
+
+ static void PostEvent(const ReceiveParams& params,
+ scoped_ptr<extensions::Event> event);
+
+ static void DispatchEvent(void *profile_id,
+ const std::string& extension_id,
+ scoped_ptr<extensions::Event> event);
+
+ content::BrowserThread::ID thread_id_;
+ Profile* const profile_;
+ scoped_refptr<ConnectionData> connections_;
+};
+
+} // namespace api
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_EVENT_DISPATCHER_H_
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler.cc b/chrome/browser/extensions/api/serial/serial_io_handler.cc
new file mode 100644
index 0000000..683dde6
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler.cc
@@ -0,0 +1,120 @@
+// Copyright 2013 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 "chrome/browser/extensions/api/serial/serial_io_handler.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+
+namespace extensions {
+
+SerialIoHandler::SerialIoHandler()
+ : file_(base::kInvalidPlatformFileValue),
+ pending_read_buffer_len_(0),
+ pending_write_buffer_len_(0) {
+}
+
+SerialIoHandler::~SerialIoHandler() {
+ DCHECK(CalledOnValidThread());
+}
+
+void SerialIoHandler::Initialize(base::PlatformFile file,
+ const ReadCompleteCallback& read_callback,
+ const WriteCompleteCallback& write_callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
+
+ file_ = file;
+ read_complete_ = read_callback;
+ write_complete_ = write_callback;
+
+ InitializeImpl();
+}
+
+void SerialIoHandler::Read(int max_bytes) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!IsReadPending());
+ pending_read_buffer_ = new net::IOBuffer(max_bytes);
+ pending_read_buffer_len_ = max_bytes;
+ read_canceled_ = false;
+ AddRef();
+ ReadImpl();
+}
+
+void SerialIoHandler::Write(const std::string& data) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!IsWritePending());
+ pending_write_buffer_ = new net::IOBuffer(data.length());
+ pending_write_buffer_len_ = data.length();
+ memcpy(pending_write_buffer_->data(), data.data(), pending_write_buffer_len_);
+ write_canceled_ = false;
+ AddRef();
+ WriteImpl();
+}
+
+void SerialIoHandler::ReadCompleted(int bytes_read,
+ api::serial::ReceiveError error) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsReadPending());
+ read_complete_.Run(std::string(pending_read_buffer_->data(), bytes_read),
+ error);
+ pending_read_buffer_ = NULL;
+ pending_read_buffer_len_ = 0;
+ Release();
+}
+
+void SerialIoHandler::WriteCompleted(int bytes_written,
+ api::serial::SendError error) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsWritePending());
+ write_complete_.Run(bytes_written, error);
+ pending_write_buffer_ = NULL;
+ pending_write_buffer_len_ = 0;
+ 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(api::serial::ReceiveError reason) {
+ DCHECK(CalledOnValidThread());
+ if (IsReadPending()) {
+ read_canceled_ = true;
+ read_cancel_reason_ = reason;
+ CancelReadImpl();
+ }
+}
+
+void SerialIoHandler::CancelWrite(api::serial::SendError reason) {
+ DCHECK(CalledOnValidThread());
+ if (IsWritePending()) {
+ write_canceled_ = true;
+ write_cancel_reason_ = reason;
+ CancelWriteImpl();
+ }
+}
+
+void SerialIoHandler::QueueReadCompleted(int bytes_read,
+ api::serial::ReceiveError error) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&SerialIoHandler::ReadCompleted, this,
+ bytes_read, error));
+}
+
+void SerialIoHandler::QueueWriteCompleted(int bytes_written,
+ api::serial::SendError error) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&SerialIoHandler::WriteCompleted, this,
+ bytes_written, error));
+}
+
+} // namespace extensions
+
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler.h b/chrome/browser/extensions/api/serial/serial_io_handler.h
new file mode 100644
index 0000000..a98f520
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler.h
@@ -0,0 +1,173 @@
+// Copyright 2013 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 CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/platform_file.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/common/extensions/api/serial.h"
+#include "net/base/io_buffer.h"
+
+namespace extensions {
+
+// 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();
+
+ // Called with a string of bytes read, and a result code. Note that an error
+ // result does not necessarily imply 0 bytes read.
+ typedef base::Callback<void(const std::string& data,
+ api::serial::ReceiveError error)>
+ ReadCompleteCallback;
+
+ // Called with the number of bytes written and a result code. Note that an
+ // error result does not necessarily imply 0 bytes written.
+ typedef base::Callback<void(int bytes_written, api::serial::SendError error)>
+ WriteCompleteCallback;
+
+ // Initializes the handler on the current message loop. Must be called exactly
+ // once before performing any I/O through the handler.
+ void Initialize(base::PlatformFile file,
+ const ReadCompleteCallback& read_callback,
+ const WriteCompleteCallback& write_callback);
+
+ // Performs an async Read operation. Behavior is undefined if this is called
+ // while a Read is already pending. Otherwise, the ReadCompleteCallback
+ // (see above) will eventually be called with a result.
+ void Read(int max_bytes);
+
+ // Performs an async Write operation. Behavior is undefined if this is called
+ // while a Write is already pending. Otherwise, the WriteCompleteCallback
+ // (see above) will eventually be called with a result.
+ void Write(const std::string& data);
+
+ // 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(api::serial::ReceiveError reason);
+
+ // Attempts to cancel a pending write operation.
+ void CancelWrite(api::serial::SendError reason);
+
+ protected:
+ SerialIoHandler();
+ virtual ~SerialIoHandler();
+
+ // Performs platform-specific initialization. |file_|, |read_complete_| and
+ // |write_complete_| all hold initialized values before this is called.
+ virtual void InitializeImpl() {}
+
+ // 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 Writempl 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;
+
+ // 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, api::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, api::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, api::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, api::serial::SendError error);
+
+ base::PlatformFile file() const {
+ return file_;
+ }
+
+ net::IOBuffer* pending_read_buffer() const {
+ return pending_read_buffer_.get();
+ }
+
+ int pending_read_buffer_len() const {
+ return pending_read_buffer_len_;
+ }
+
+ api::serial::ReceiveError read_cancel_reason() const {
+ return read_cancel_reason_;
+ }
+
+ bool read_canceled() const {
+ return read_canceled_;
+ }
+
+ net::IOBuffer* pending_write_buffer() const {
+ return pending_write_buffer_.get();
+ }
+
+ int pending_write_buffer_len() const {
+ return pending_write_buffer_len_;
+ }
+
+ api::serial::SendError write_cancel_reason() const {
+ return write_cancel_reason_;
+ }
+
+ bool write_canceled() const {
+ return write_canceled_;
+ }
+
+ private:
+ friend class base::RefCounted<SerialIoHandler>;
+
+ base::PlatformFile file_;
+
+ scoped_refptr<net::IOBuffer> pending_read_buffer_;
+ int pending_read_buffer_len_;
+ api::serial::ReceiveError read_cancel_reason_;
+ bool read_canceled_;
+
+ scoped_refptr<net::IOBuffer> pending_write_buffer_;
+ int pending_write_buffer_len_;
+ api::serial::SendError write_cancel_reason_;
+ bool write_canceled_;
+
+ ReadCompleteCallback read_complete_;
+ WriteCompleteCallback write_complete_;
+
+ DISALLOW_COPY_AND_ASSIGN(SerialIoHandler);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_H_
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler_posix.cc b/chrome/browser/extensions/api/serial/serial_io_handler_posix.cc
new file mode 100644
index 0000000..e7ade11
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler_posix.cc
@@ -0,0 +1,120 @@
+// Copyright 2013 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 "chrome/browser/extensions/api/serial/serial_io_handler_posix.h"
+
+#include "base/posix/eintr_wrapper.h"
+
+namespace extensions {
+
+// static
+scoped_refptr<SerialIoHandler> SerialIoHandler::Create() {
+ return new SerialIoHandlerPosix();
+}
+
+void SerialIoHandlerPosix::ReadImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(pending_read_buffer());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+
+ EnsureWatchingReads();
+}
+
+void SerialIoHandlerPosix::WriteImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(pending_write_buffer());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+
+ EnsureWatchingWrites();
+}
+
+void SerialIoHandlerPosix::CancelReadImpl() {
+ DCHECK(CalledOnValidThread());
+ is_watching_reads_ = false;
+ file_read_watcher_.StopWatchingFileDescriptor();
+}
+
+void SerialIoHandlerPosix::CancelWriteImpl() {
+ DCHECK(CalledOnValidThread());
+ is_watching_writes_ = false;
+ file_write_watcher_.StopWatchingFileDescriptor();
+}
+
+SerialIoHandlerPosix::SerialIoHandlerPosix()
+ : is_watching_reads_(false),
+ is_watching_writes_(false) {
+}
+
+SerialIoHandlerPosix::~SerialIoHandlerPosix() {}
+
+void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(fd, file());
+
+ if (pending_read_buffer()) {
+ int bytes_read = HANDLE_EINTR(read(file(),
+ pending_read_buffer()->data(),
+ pending_read_buffer_len()));
+ if (bytes_read < 0) {
+ if (errno == ENXIO) {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_DEVICE_LOST);
+ } else {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ }
+ } else if (bytes_read == 0) {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_DEVICE_LOST);
+ } else {
+ ReadCompleted(bytes_read, api::serial::RECEIVE_ERROR_NONE);
+ }
+ } else {
+ // Stop watching the fd if we get notifications with no pending
+ // reads or writes to avoid starving the message loop.
+ is_watching_reads_ = false;
+ file_read_watcher_.StopWatchingFileDescriptor();
+ }
+}
+
+void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(fd, file());
+
+ if (pending_write_buffer()) {
+ int bytes_written = HANDLE_EINTR(write(file(),
+ pending_write_buffer()->data(),
+ pending_write_buffer_len()));
+ if (bytes_written < 0) {
+ WriteCompleted(0, api::serial::SEND_ERROR_SYSTEM_ERROR);
+ } else {
+ WriteCompleted(bytes_written, api::serial::SEND_ERROR_NONE);
+ }
+ } else {
+ // Stop watching the fd if we get notifications with no pending
+ // writes to avoid starving the message loop.
+ is_watching_writes_ = false;
+ file_write_watcher_.StopWatchingFileDescriptor();
+ }
+}
+
+void SerialIoHandlerPosix::EnsureWatchingReads() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+ if (!is_watching_reads_) {
+ is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
+ file(), true, base::MessageLoopForIO::WATCH_READ,
+ &file_read_watcher_, this);
+ }
+}
+
+void SerialIoHandlerPosix::EnsureWatchingWrites() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+ if (!is_watching_writes_) {
+ is_watching_writes_ =
+ base::MessageLoopForIO::current()->WatchFileDescriptor(
+ file(), true, base::MessageLoopForIO::WATCH_WRITE,
+ &file_write_watcher_, this);
+ }
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler_posix.h b/chrome/browser/extensions/api/serial/serial_io_handler_posix.h
new file mode 100644
index 0000000..7b0bd64
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler_posix.h
@@ -0,0 +1,48 @@
+// Copyright 2013 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 CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/extensions/api/serial/serial_io_handler.h"
+
+namespace extensions {
+
+class SerialIoHandlerPosix : public SerialIoHandler,
+ public base::MessageLoopForIO::Watcher {
+ protected:
+ // SerialIoHandler impl.
+ virtual void ReadImpl() OVERRIDE;
+ virtual void WriteImpl() OVERRIDE;
+ virtual void CancelReadImpl() OVERRIDE;
+ virtual void CancelWriteImpl() OVERRIDE;
+
+ private:
+ friend class SerialIoHandler;
+
+ SerialIoHandlerPosix();
+ virtual ~SerialIoHandlerPosix();
+
+ // base::MessageLoopForIO::Watcher implementation.
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+
+ void EnsureWatchingReads();
+ void EnsureWatchingWrites();
+
+ base::MessageLoopForIO::FileDescriptorWatcher file_read_watcher_;
+ base::MessageLoopForIO::FileDescriptorWatcher file_write_watcher_;
+
+ // Flags indicating if the message loop is watching the device for IO events.
+ bool is_watching_reads_;
+ bool is_watching_writes_;
+
+ DISALLOW_COPY_AND_ASSIGN(SerialIoHandlerPosix);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler_win.cc b/chrome/browser/extensions/api/serial/serial_io_handler_win.cc
new file mode 100644
index 0000000..b083fed
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler_win.cc
@@ -0,0 +1,145 @@
+// Copyright 2013 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 "chrome/browser/extensions/api/serial/serial_io_handler_win.h"
+
+#include <windows.h>
+
+namespace extensions {
+
+// static
+scoped_refptr<SerialIoHandler> SerialIoHandler::Create() {
+ return new SerialIoHandlerWin();
+}
+
+void SerialIoHandlerWin::InitializeImpl() {
+ DCHECK(!comm_context_);
+ DCHECK(!read_context_);
+ DCHECK(!write_context_);
+
+ base::MessageLoopForIO::current()->RegisterIOHandler(file(), this);
+
+ comm_context_.reset(new base::MessageLoopForIO::IOContext());
+ comm_context_->handler = this;
+ memset(&comm_context_->overlapped, 0, sizeof(comm_context_->overlapped));
+
+ read_context_.reset(new base::MessageLoopForIO::IOContext());
+ read_context_->handler = this;
+ memset(&read_context_->overlapped, 0, sizeof(read_context_->overlapped));
+
+ write_context_.reset(new base::MessageLoopForIO::IOContext());
+ write_context_->handler = this;
+ memset(&write_context_->overlapped, 0, sizeof(write_context_->overlapped));
+}
+
+void SerialIoHandlerWin::ReadImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(pending_read_buffer());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+
+ DWORD errors;
+ COMSTAT status;
+ if (!ClearCommError(file(), &errors, &status) || errors != 0) {
+ QueueReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ return;
+ }
+
+ SetCommMask(file(), EV_RXCHAR);
+
+ event_mask_ = 0;
+ BOOL ok = ::WaitCommEvent(file(), &event_mask_, &comm_context_->overlapped);
+ if (!ok && GetLastError() != ERROR_IO_PENDING) {
+ QueueReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ }
+ is_comm_pending_ = true;
+}
+
+void SerialIoHandlerWin::WriteImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(pending_write_buffer());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+
+ BOOL ok = ::WriteFile(file(),
+ pending_write_buffer()->data(),
+ pending_write_buffer_len(), NULL,
+ &write_context_->overlapped);
+ if (!ok && GetLastError() != ERROR_IO_PENDING) {
+ QueueWriteCompleted(0, api::serial::SEND_ERROR_SYSTEM_ERROR);
+ }
+}
+
+void SerialIoHandlerWin::CancelReadImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+ if (is_comm_pending_) {
+ ::CancelIoEx(file(), &comm_context_->overlapped);
+ } else {
+ ::CancelIoEx(file(), &read_context_->overlapped);
+ }
+}
+
+void SerialIoHandlerWin::CancelWriteImpl() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(file(), base::kInvalidPlatformFileValue);
+ ::CancelIoEx(file(), &write_context_->overlapped);
+}
+
+SerialIoHandlerWin::SerialIoHandlerWin()
+ : event_mask_(0),
+ is_comm_pending_(false) {
+}
+
+SerialIoHandlerWin::~SerialIoHandlerWin() {
+}
+
+void SerialIoHandlerWin::OnIOCompleted(
+ base::MessageLoopForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) {
+ DCHECK(CalledOnValidThread());
+ if (context == comm_context_) {
+ is_comm_pending_ = false;
+ if (read_canceled()) {
+ ReadCompleted(bytes_transferred, read_cancel_reason());
+ } else if (error != ERROR_SUCCESS && error == ERROR_OPERATION_ABORTED) {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ } else if (pending_read_buffer()) {
+ BOOL ok = ::ReadFile(file(),
+ pending_read_buffer()->data(),
+ pending_read_buffer_len(),
+ NULL,
+ &read_context_->overlapped);
+ if (!ok && GetLastError() != ERROR_IO_PENDING) {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ }
+ }
+ } else if (context == read_context_) {
+ if (read_canceled()) {
+ ReadCompleted(bytes_transferred, read_cancel_reason());
+ } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
+ ReadCompleted(0, api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ } else {
+ ReadCompleted(
+ bytes_transferred,
+ error == ERROR_SUCCESS ? api::serial::RECEIVE_ERROR_NONE
+ : api::serial::RECEIVE_ERROR_SYSTEM_ERROR);
+ }
+ } else if (context == write_context_) {
+ DCHECK(pending_write_buffer());
+ if (write_canceled()) {
+ WriteCompleted(0, write_cancel_reason());
+ } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
+ WriteCompleted(0, api::serial::SEND_ERROR_SYSTEM_ERROR);
+ } else {
+ WriteCompleted(
+ bytes_transferred,
+ error == ERROR_SUCCESS ? api::serial::SEND_ERROR_NONE
+ : api::serial::SEND_ERROR_SYSTEM_ERROR);
+ }
+ } else {
+ NOTREACHED() << "Invalid IOContext";
+ }
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_io_handler_win.h b/chrome/browser/extensions/api/serial/serial_io_handler_win.h
new file mode 100644
index 0000000..16cb723
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_io_handler_win.h
@@ -0,0 +1,58 @@
+// Copyright 2013 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 CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/extensions/api/serial/serial_io_handler.h"
+
+namespace extensions {
+
+class SerialIoHandlerWin : public SerialIoHandler,
+ public base::MessageLoopForIO::IOHandler {
+ protected:
+ // SerialIoHandler implementation.
+ virtual void InitializeImpl() OVERRIDE;
+ virtual void ReadImpl() OVERRIDE;
+ virtual void WriteImpl() OVERRIDE;
+ virtual void CancelReadImpl() OVERRIDE;
+ virtual void CancelWriteImpl() OVERRIDE;
+
+ private:
+ friend class SerialIoHandler;
+
+ SerialIoHandlerWin();
+ virtual ~SerialIoHandlerWin();
+
+ // base::MessageLoopForIO::IOHandler implementation.
+ virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered,
+ DWORD error) OVERRIDE;
+
+ // Context used for asynchronous WaitCommEvent calls.
+ scoped_ptr<base::MessageLoopForIO::IOContext> comm_context_;
+
+ // Context used for overlapped reads.
+ scoped_ptr<base::MessageLoopForIO::IOContext> read_context_;
+
+ // Context used for overlapped writes.
+ scoped_ptr<base::MessageLoopForIO::IOContext> write_context_;
+
+ // Asynchronous event mask state
+ DWORD event_mask_;
+
+ // Indicates if a pending read is waiting on initial data arrival via
+ // WaitCommEvent, as opposed to waiting on actual ReadFile completion
+ // after a corresponding WaitCommEvent has completed.
+ bool is_comm_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(SerialIoHandlerWin);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+
diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h
index de232509..23af274 100644
--- a/chrome/browser/extensions/extension_function_histogram_value.h
+++ b/chrome/browser/extensions/extension_function_histogram_value.h
@@ -132,7 +132,7 @@ enum HistogramValue {
INPUTMETHODPRIVATE_GET,
USB_CLOSEDEVICE,
TTS_STOP,
- SERIAL_GETPORTS,
+ DELETED_SERIAL_GETPORTS,
DELETED_FILEBROWSERPRIVATE_CLEARDRIVECACHE,
SERIAL_GETCONTROLSIGNALS,
DEVELOPERPRIVATE_ENABLE,
@@ -177,7 +177,7 @@ enum HistogramValue {
STORAGE_SET,
FONTSETTINGS_GETDEFAULTFONTSIZE,
EXTENSION_SETUPDATEURLDATA,
- SERIAL_WRITE,
+ DELETED_SERIAL_WRITE,
IDLE_QUERYSTATE,
DELETED_EXPERIMENTAL_RLZ_GETACCESSPOINTRLZ,
WEBSTOREPRIVATE_SETSTORELOGIN,
@@ -396,7 +396,7 @@ enum HistogramValue {
TABS_DETECTLANGUAGE,
METRICSPRIVATE_RECORDVALUE,
BOOKMARKMANAGERPRIVATE_SORTCHILDREN,
- SERIAL_READ,
+ DELETED_SERIAL_READ,
APP_CURRENTWINDOWINTERNAL_MAXIMIZE,
EXPERIMENTAL_DISCOVERY_CLEARALLSUGGESTIONS,
DELETED_MANAGEDMODEPRIVATE_ENTER,
@@ -693,6 +693,12 @@ enum HistogramValue {
SCREENLOCKPRIVATE_SHOWMESSAGE,
FEEDBACKPRIVATE_GETHISTOGRAMS,
SYSTEM_NETWORK_GETNETWORKINTERFACES,
+ SERIAL_GETDEVICES,
+ SERIAL_UPDATE,
+ SERIAL_SETPAUSED,
+ SERIAL_GETINFO,
+ SERIAL_GETCONNECTIONS,
+ SERIAL_SEND,
ENUM_BOUNDARY // Last entry: Add new entries above.
};
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 8014ba5..0aac4ad 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -450,6 +450,14 @@
'browser/extensions/api/serial/serial_connection.h',
'browser/extensions/api/serial/serial_connection_posix.cc',
'browser/extensions/api/serial/serial_connection_win.cc',
+ 'browser/extensions/api/serial/serial_event_dispatcher.cc',
+ 'browser/extensions/api/serial/serial_event_dispatcher.h',
+ 'browser/extensions/api/serial/serial_io_handler.cc',
+ 'browser/extensions/api/serial/serial_io_handler.h',
+ 'browser/extensions/api/serial/serial_io_handler_posix.cc',
+ 'browser/extensions/api/serial/serial_io_handler_posix.h',
+ 'browser/extensions/api/serial/serial_io_handler_win.cc',
+ 'browser/extensions/api/serial/serial_io_handler_win.h',
'browser/extensions/api/serial/serial_port_enumerator.cc',
'browser/extensions/api/serial/serial_port_enumerator.h',
'browser/extensions/api/serial/serial_port_enumerator_posix.cc',
diff --git a/chrome/common/extensions/api/serial.idl b/chrome/common/extensions/api/serial.idl
index c6429ff..919de55 100644
--- a/chrome/common/extensions/api/serial.idl
+++ b/chrome/common/extensions/api/serial.idl
@@ -6,13 +6,33 @@
// connected to a serial port.
namespace serial {
- callback GetPortsCallback = void (DOMString[] ports);
+ dictionary DeviceInfo {
+ // The device's system path. This should be passed as the <code>path</code>
+ // argument to <code>chrome.serial.open</code> in order to open this device.
+ DOMString path;
+ };
+
+ callback GetDevicesCallback = void (DeviceInfo[] ports);
+
+ enum DataBits { seven, eight };
+ enum ParityBit { no, odd, even };
+ enum StopBits { one, two };
+
+ dictionary ConnectionOptions {
+ // Flag indicating whether or not the connection should be left open when
+ // the application is suspended (see
+ // <a href="http://developer.chrome.com/apps/app_lifecycle.html">Manage App
+ // Lifecycle</a>). The default value is "false." When the application is
+ // loaded, any serial connections previously opened with persistent=true
+ // can be fetched with <code>getConnections</code>.
+ boolean? persistent;
+
+ // An application-defined string to associate with the connection.
+ DOMString? name;
- enum DataBit { sevenbit, eightbit };
- enum ParityBit { noparity, oddparity, evenparity };
- enum StopBit { onestopbit, twostopbit };
+ // The size of the buffer used to receive data. The default value is 4096.
+ long? bufferSize;
- dictionary OpenOptions {
// The requested bitrate of the connection to be opened. For compatibility
// with the widest range of hardware, this number should match one of
// commonly-available bitrates, such as 110, 300, 1200, 2400, 4800, 9600,
@@ -21,45 +41,125 @@ namespace serial {
// bitrate, even if the port itself supports that bitrate. <code>9600</code>
// will be passed by default.
long? bitrate;
- // <code>"eightbit"</code> will be passed by default.
- DataBit? dataBit;
- // <code>"noparity"</code> will be passed by default.
+
+ // <code>"eight"</code> will be passed by default.
+ DataBits? dataBits;
+
+ // <code>"no"</code> will be passed by default.
ParityBit? parityBit;
- // <code>"onestopbit"</code> will be passed by default.
- StopBit? stopBit;
+
+ // <code>"one"</code> will be passed by default.
+ StopBits? stopBits;
+
+ // Flag indicating whether or not to enable RTS/CTS hardware flow control.
+ // Defaults to false.
+ boolean? ctsFlowControl;
+
+ // The maximum amount of time (in milliseconds) to wait for new data before
+ // raising an <code>onReceiveError</code> event with a "timeout" error.
+ // If zero, receive timeout errors will not be raised for the connection.
+ // Defaults to 0.
+ long? receiveTimeout;
+
+ // The maximum amount of time (in milliseconds) to wait for a
+ // <code>send</code> operation to complete before calling the callback with
+ // a "timeout" error. If zero, send timeout errors will not be triggered.
+ // Defaults to 0.
+ long? sendTimeout;
};
+ // Result of the <code>open</code> method.
dictionary OpenInfo {
// The id of the opened connection.
long connectionId;
};
+ // Callback from the <code>open</code> method;
callback OpenCallback = void (OpenInfo openInfo);
+ // Callback from the <code>update</code> method.
+ callback UpdateCallback = void (boolean result);
+
// Returns true if operation was successful.
callback CloseCallback = void (boolean result);
- dictionary ReadInfo {
- // The number of bytes received, or a negative number if an error occurred.
- // This number will be smaller than the number of bytes requested in the
- // original read call if the call would need to block to read that number
- // of bytes.
- long bytesRead;
+ // Callback from the <code>setPaused</code> method.
+ callback SetPausedCallback = void ();
- // The data received.
- ArrayBuffer data;
+ // Result of the <code>getInfo</code> method.
+ dictionary ConnectionInfo {
+ // The id of the serial port connection.
+ long connectionId;
+
+ // Flag indicating whether the connection is blocked from firing onReceive
+ // events.
+ boolean paused;
+
+ // See <code>ConnectionOptions.persistent</code>
+ boolean persistent;
+
+ // See <code>ConnectionOptions.name</code>
+ DOMString name;
+
+ // See <code>ConnectionOptions.bufferSize</code>
+ long bufferSize;
+
+ // See <code>ConnectionOptions.receiveTimeout</code>
+ long receiveTimeout;
+
+ // See <code>ConnectionOptions.sendTimeout</code>
+ long sendTimeout;
+
+ // See <code>ConnectionOptions.bitrate</code>. This field may be omitted
+ // or inaccurate if a non-standard bitrate is in use, or if an error
+ // occurred while querying the underlying device.
+ long? bitrate;
+
+ // See <code>ConnectionOptions.dataBits</code>. This field may be omitted
+ // if an error occurred while querying the underlying device.
+ DataBits? dataBits;
+
+ // See <code>ConnectionOptions.parityBit</code>. This field may be omitted
+ // if an error occurred while querying the underlying device.
+ ParityBit? parityBit;
+
+ // See <code>ConnectionOptions.stopBits</code>. This field may be omitted
+ // if an error occurred while querying the underlying device.
+ StopBits? stopBits;
+
+ // See <code>ConnectionOptions.ctsFlowControl</code>. This field may be
+ // omitted if an error occurred while querying the underlying device.
+ boolean? ctsFlowControl;
};
- callback ReadCallback = void (ReadInfo readInfo);
+ callback GetInfoCallback = void (ConnectionInfo connectionInfo);
+
+ callback GetConnectionsCallback = void (ConnectionInfo[] connectionInfos);
- dictionary WriteInfo {
- // The number of bytes written.
- long bytesWritten;
+ enum SendError {
+ // The connection was closed.
+ closed,
+
+ // A send was already pending.
+ pending,
+
+ // The send timed out.
+ timeout,
+
+ // A system error occurred and the connection may be unrecoverable.
+ system_error
};
- callback WriteCallback = void (WriteInfo writeInfo);
+ dictionary SendInfo {
+ // The number of bytes sent.
+ long bytesSent;
+
+ // An error code if an error occurred.
+ SendError? error;
+ };
+
+ callback SendCallback = void (SendInfo sendInfo);
- // Returns true if operation was successful.
callback FlushCallback = void (boolean result);
// Boolean true = mark signal (negative serial voltage).
@@ -69,78 +169,139 @@ namespace serial {
// change. Signals not included in the dictionary will be left unchanged.
//
// GetControlSignals includes all receivable signals.
- dictionary ControlSignalOptions {
+ dictionary ControlSignals {
// Serial control signals that your machine can send. Missing fields will
- // be set to false.
+ // not be changed.
boolean? dtr;
boolean? rts;
- // Serial control signals that your machine can receive. If a get operation
- // fails, success will be false, and these fields will be absent.
+ // Serial control signals that your machine can receive.
//
// DCD (Data Carrier Detect) is equivalent to RLSD (Receive Line Signal
// Detect) on some platforms.
boolean? dcd;
boolean? cts;
+ boolean? ri;
+ boolean? dsr;
};
// Returns a snapshot of current control signals.
- callback GetControlSignalsCallback = void (ControlSignalOptions options);
+ callback GetControlSignalsCallback = void (ControlSignals signals);
// Returns true if operation was successful.
callback SetControlSignalsCallback = void (boolean result);
+ // Data from an <code>onReceive</code> event.
+ dictionary ReceiveInfo {
+ // The connection identifier.
+ long connectionId;
+
+ // The data received.
+ ArrayBuffer data;
+ };
+
+ enum ReceiveError {
+ // The connection was closed.
+ closed,
+
+ // No data has been received for <code>receiveTimeout</code> milliseconds.
+ timeout,
+
+ // The device was most likely disconnected from the host.
+ device_lost,
+
+ // A system error occurred and the connection may be unrecoverable.
+ system_error
+ };
+
+ // Data from an <code>onReceiveError</code> event.
+ dictionary ReceiveErrorInfo {
+ // The connection identifier.
+ long connectionId;
+
+ // An error code indicating what went wrong.
+ ReceiveError error;
+ };
+
interface Functions {
- // Returns names of valid ports on this machine, each of which is likely to
- // be valid to pass as the port argument to open(). The list is regenerated
- // each time this method is called, as port validity is dynamic.
- //
- // |callback| : Called with the list of ports.
- static void getPorts(GetPortsCallback callback);
+ // Returns information about available serial devices on the system.
+ // The list is regenerated each time this method is called.
+ // |callback| : Called with the list of <code>DeviceInfo</code> objects.
+ static void getDevices(GetDevicesCallback callback);
// Opens a connection to the given serial port.
- // |port| : The name of the serial port to open.
- // |options| : Connection options.
+ // |path| : The system path of the serial port to open.
+ // |options| : Port configuration options.
// |callback| : Called when the connection has been opened.
- static void open(DOMString port,
- optional OpenOptions options,
+ static void open(DOMString path,
+ optional ConnectionOptions options,
OpenCallback callback);
+ // Update the option settings on an open serial port.
+ // |connectionId| : The id of the opened connection.
+ // |options| : Port configuration options.
+ // |callback| : Called when the configuation has completed.
+ static void update(long connectionId,
+ ConnectionOptions options,
+ UpdateCallback callback);
+
// Closes an open connection.
// |connectionId| : The id of the opened connection.
// |callback| : Called when the connection has been closed.
- static void close(long connectionId,
- CloseCallback callback);
+ static void close(long connectionId, CloseCallback callback);
- // Reads a byte from the given connection.
- // |connectionId| : The id of the connection.
- // |bytesToRead| : The number of bytes to read.
- // |callback| : Called when all the requested bytes have been read or
- // when the read blocks.
- static void read(long connectionId,
- long bytesToRead,
- ReadCallback callback);
-
- // Writes a string to the given connection.
+ // Pauses or unpauses an open connection.
+ // |connectionId| : The id of the opened connection.
+ // |paused| : Flag to indicate whether to pause or unpause.
+ // |callback| : Called when the connection has been successfully paused or
+ // unpaused.
+ static void setPaused(long connectionId,
+ boolean paused,
+ SetPausedCallback callback);
+
+ // Retrieves the state of a given connection.
+ // |connectionId| : The id of the opened connection.
+ // |callback| : Called with connection state information when available.
+ static void getInfo(long connectionId, GetInfoCallback callback);
+
+ // Retrieves the list of currently opened serial port connections owned by
+ // the application.
+ // |callback| : Called with the list of connections when available.
+ static void getConnections(GetConnectionsCallback callback);
+
+ // Writes data to the given connection.
// |connectionId| : The id of the connection.
- // |data| : The string to write.
- // |callback| : Called when the string has been written.
- static void write(long connectionId,
- ArrayBuffer data,
- WriteCallback callback);
+ // |data| : The data to send.
+ // |callback| : Called when the operation has completed.
+ static void send(long connectionId,
+ ArrayBuffer data,
+ SendCallback callback);
// Flushes all bytes in the given connection's input and output buffers.
- // |connectionId| : The id of the connection.
- // |callback| : Called when the flush is complete.
- static void flush(long connectionId,
- FlushCallback callback);
+ static void flush(long connectionId, FlushCallback callback);
+ // Retrieves the state of control signals on a given connection.
+ // |connectionId| : The id of the connection.
+ // |callback| : Called when the control signals are available.
static void getControlSignals(long connectionId,
GetControlSignalsCallback callback);
+ // Sets the state of control signals on a given connection.
+ // |connectionId| : The id of the connection.
+ // |callback| : Called once the control signals have been set.
static void setControlSignals(long connectionId,
- ControlSignalOptions options,
+ ControlSignals signals,
SetControlSignalsCallback callback);
};
+ interface Events {
+ // Event raised when data has been read from the connection.
+ // |info| : Event data.
+ static void onReceive(ReceiveInfo info);
+
+ // Event raised when an error occurred while the runtime was waiting for
+ // data on the serial port. Once this event is raised, the connection is set
+ // to <code>paused</code>.
+ static void onReceiveError(ReceiveErrorInfo info);
+ };
};
diff --git a/chrome/test/data/extensions/api_test/serial/api/background.js b/chrome/test/data/extensions/api_test/serial/api/background.js
index 67c0e30..bdc3df7 100644
--- a/chrome/test/data/extensions/api_test/serial/api/background.js
+++ b/chrome/test/data/extensions/api_test/serial/api/background.js
@@ -23,19 +23,19 @@ var createTestArrayBuffer = function() {
var testSerial = function() {
var serialPort = null;
var connectionId = -1;
- var readTries = 10;
- var writeBuffer = createTestArrayBuffer();
- var writeBufferUint8View = new Uint8Array(writeBuffer);
- var bufferLength = writeBufferUint8View.length;
- var readBuffer = new ArrayBuffer(bufferLength);
- var readBufferUint8View = new Uint8Array(readBuffer);
- var bytesToRead = bufferLength;
+ var receiveTries = 10;
+ var sendBuffer = createTestArrayBuffer();
+ var sendBufferUint8View = new Uint8Array(sendBuffer);
+ var bufferLength = sendBufferUint8View.length;
+ var receiveBuffer = new ArrayBuffer(bufferLength);
+ var receiveBufferUint8View = new Uint8Array(receiveBuffer);
+ var bytesToReceive = bufferLength;
var operation = 0;
var doNextOperation = function() {
switch (operation++) {
case 0:
- serial.getPorts(onGetPorts);
+ serial.getDevices(onGetDevices);
break;
case 1:
var bitrate = 57600;
@@ -50,13 +50,9 @@ var testSerial = function() {
serial.getControlSignals(connectionId,onGetControlSignals);
break;
case 4:
- serial.write(connectionId, writeBuffer, onWrite);
- break;
- case 5:
- serial.read(connectionId, bytesToRead, onRead);
- break;
- case 6:
- serial.flush(connectionId, onFlush);
+ serial.onReceive.addListener(onReceive);
+ serial.onReceiveError.addListener(onReceiveError);
+ serial.send(connectionId, sendBuffer, onSend);
break;
case 50: // GOTO 4 EVER
serial.close(connectionId, onClose);
@@ -84,40 +80,35 @@ var testSerial = function() {
doNextOperation();
};
- var onFlush = function(result) {
- chrome.test.assertTrue(result);
- doNextOperation();
- }
-
- var onRead = function(readInfo) {
- bytesToRead -= readInfo.bytesRead;
- var readBufferIndex = bufferLength - readInfo.bytesRead;
- var messageUint8View = new Uint8Array(readInfo.data);
- for (var i = 0; i < readInfo.bytesRead; i++)
- readBufferUint8View[i + readBufferIndex] = messageUint8View[i];
- if (bytesToRead == 0) {
- chrome.test.assertEq(writeBufferUint8View, readBufferUint8View,
- 'Buffer read was not equal to buffer written.');
+ var onReceive = function(receiveInfo) {
+ var data = new Uint8Array(receiveInfo.data);
+ bytesToReceive -= data.length;
+ var receiveBufferIndex = bufferLength - data.length;
+ for (var i = 0; i < data.length; i++)
+ receiveBufferUint8View[i + receiveBufferIndex] = data[i];
+ if (bytesToReceive == 0) {
+ chrome.test.assertEq(sendBufferUint8View, receiveBufferUint8View,
+ 'Buffer received was not equal to buffer sent.');
doNextOperation();
- } else {
- if (--readTries > 0)
- setTimeout(repeatOperation, 100);
- else
- chrome.test.assertTrue(
- false,
- 'read() failed to return requested number of bytes.');
+ } else if (--receiveTries <= 0) {
+ chrome.test.fail('receive() failed to return requested number of bytes.');
}
};
- var onWrite = function(writeInfo) {
- chrome.test.assertEq(bufferLength, writeInfo.bytesWritten,
- 'Failed to write byte.');
- doNextOperation();
+ var onReceiveError = function(errorInfo) {
+ chrome.test.fail('Failed to receive serial data');
+ };
+
+ var onSend = function(sendInfo) {
+ chrome.test.assertEq(bufferLength, sendInfo.bytesSent,
+ 'Failed to send byte.');
};
var onGetControlSignals = function(options) {
- chrome.test.assertTrue(typeof options.dcd != 'undefined');
- chrome.test.assertTrue(typeof options.cts != 'undefined');
+ chrome.test.assertTrue(typeof options.dcd != 'undefined', "No DCD set");
+ chrome.test.assertTrue(typeof options.cts != 'undefined', "No CTS set");
+ chrome.test.assertTrue(typeof options.dtr != 'undefined', "No DTR set");
+ chrome.test.assertTrue(typeof options.ri != 'undefined', "No RI set");
doNextOperation();
};
@@ -132,18 +123,18 @@ var testSerial = function() {
doNextOperation();
};
- var onGetPorts = function(ports) {
- if (ports.length > 0) {
+ var onGetDevices = function(devices) {
+ if (devices.length > 0) {
var portNumber = 0;
- while (portNumber < ports.length) {
- if (shouldSkipPort(ports[portNumber])) {
+ while (portNumber < devices.length) {
+ if (shouldSkipPort(devices[portNumber].path)) {
portNumber++;
continue;
} else
break;
}
- if (portNumber < ports.length) {
- serialPort = ports[portNumber];
+ if (portNumber < devices.length) {
+ serialPort = devices[portNumber].path;
doNextOperation();
} else {
// We didn't find a port that we think we should try.
diff --git a/chrome/test/data/extensions/api_test/serial/real_hardware/background.js b/chrome/test/data/extensions/api_test/serial/real_hardware/background.js
index 27e033d..19d86d9 100644
--- a/chrome/test/data/extensions/api_test/serial/real_hardware/background.js
+++ b/chrome/test/data/extensions/api_test/serial/real_hardware/background.js
@@ -2,73 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// TODO(miket): opening Bluetooth ports on OSX is unreliable. Investigate.
-function shouldSkipPort(portName) {
- return portName.match(/[Bb]luetooth/);
-}
-
-var testGetPorts = function() {
- var onGetPorts = function(ports) {
+var testGetDevices = function() {
+ var onGetDevices = function(devices) {
// Any length is potentially valid, because we're on unknown hardware. But
- // we are testing at least that the ports member was filled in, so it's
+ // we are testing at least that the devices member was filled in, so it's
// still a somewhat meaningful test.
- chrome.test.assertTrue(ports.length >= 0);
+ chrome.test.assertTrue(devices.length >= 0);
chrome.test.succeed();
}
- chrome.serial.getPorts(onGetPorts);
+ chrome.serial.getDevices(onGetDevices);
};
-var testMaybeOpenPort = function() {
- var onGetPorts = function(ports) {
- // We're testing as much as we can here without actually assuming the
- // existence of attached hardware.
- //
- // TODO(miket): is there any chance that just opening a serial port but not
- // doing anything could be harmful to devices attached to a developer's
- // machine?
- if (ports.length > 0) {
- var currentPort = 0;
-
- var onFinishedWithPort = function() {
- if (currentPort >= ports.length)
- chrome.test.succeed();
- else
- testPort();
- };
-
- var onClose = function(r) {
- onFinishedWithPort();
- };
-
- var onOpen = function(connectionInfo) {
- var id = connectionInfo.connectionId;
- if (id > 0)
- chrome.serial.close(id, onClose);
- else
- onFinishedWithPort();
- };
-
- var testPort = function() {
- var port = ports[currentPort++];
-
- if (shouldSkipPort(port)) {
- onFinishedWithPort();
- } else {
- console.log("Opening serial device " + port);
- chrome.serial.open(port, onOpen);
- }
- }
-
- testPort();
- } else {
- // There aren't any valid ports on this machine. That's OK.
- chrome.test.succeed();
- }
- }
-
- chrome.serial.getPorts(onGetPorts);
-};
+// TODO(rockot): As infrastructure is built for testing device APIs in Chrome,
+// we should obviously build real hardware tests here. For now, no attempt is
+// made to open real devices on the test system.
-var tests = [testGetPorts, testMaybeOpenPort];
+var tests = [testGetDevices];
chrome.test.runTests(tests);