// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/api/serial/serial_api.h" #include <algorithm> #include <vector> #include "base/values.h" #include "content/public/browser/browser_thread.h" #include "device/serial/serial_device_enumerator.h" #include "extensions/browser/api/serial/serial_connection.h" #include "extensions/browser/api/serial/serial_event_dispatcher.h" #include "extensions/common/api/serial.h" using content::BrowserThread; namespace extensions { namespace core_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 kErrorConnectFailed[] = "Failed to connect to the port."; const char kErrorSerialConnectionNotFound[] = "Serial connection not found."; const char kErrorGetControlSignalsFailed[] = "Failed to get control signals."; 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() { } bool SerialAsyncApiFunction::PrePrepare() { manager_ = ApiResourceManager<SerialConnection>::Get(browser_context()); DCHECK(manager_); return true; } bool SerialAsyncApiFunction::Respond() { return error_.empty(); } SerialConnection* SerialAsyncApiFunction::GetSerialConnection( int api_resource_id) { return manager_->Get(extension_->id(), api_resource_id); } void SerialAsyncApiFunction::RemoveSerialConnection(int api_resource_id) { manager_->Remove(extension_->id(), api_resource_id); } SerialGetDevicesFunction::SerialGetDevicesFunction() { } bool SerialGetDevicesFunction::Prepare() { set_work_thread_id(BrowserThread::FILE); return true; } void SerialGetDevicesFunction::Work() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); scoped_ptr<device::SerialDeviceEnumerator> enumerator = device::SerialDeviceEnumerator::Create(); mojo::Array<device::serial::DeviceInfoPtr> devices = enumerator->GetDevices(); results_ = serial::GetDevices::Results::Create( devices.To<std::vector<linked_ptr<serial::DeviceInfo> > >()); } SerialConnectFunction::SerialConnectFunction() { } SerialConnectFunction::~SerialConnectFunction() { } bool SerialConnectFunction::Prepare() { params_ = serial::Connect::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); // 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(browser_context()); DCHECK(serial_event_dispatcher_); return true; } void SerialConnectFunction::AsyncWorkStart() { DCHECK_CURRENTLY_ON(BrowserThread::IO); connection_ = CreateSerialConnection(params_->path, extension_->id()); connection_->Open(base::Bind(&SerialConnectFunction::OnConnected, this)); } void SerialConnectFunction::OnConnected(bool success) { DCHECK(connection_); if (success) { if (!connection_->Configure(*params_->options.get())) { delete connection_; connection_ = NULL; } } else { delete connection_; connection_ = NULL; } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&SerialConnectFunction::FinishConnect, this)); } void SerialConnectFunction::FinishConnect() { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!connection_) { error_ = kErrorConnectFailed; } else { int id = manager_->Add(connection_); serial::ConnectionInfo info; info.connection_id = id; if (connection_->GetInfo(&info)) { serial_event_dispatcher_->PollConnection(extension_->id(), id); results_ = serial::Connect::Results::Create(info); } else { RemoveSerialConnection(id); error_ = kErrorConnectFailed; } } AsyncWorkCompleted(); } SerialConnection* SerialConnectFunction::CreateSerialConnection( const std::string& port, const std::string& extension_id) const { return new SerialConnection(port, extension_id); } SerialUpdateFunction::SerialUpdateFunction() { } SerialUpdateFunction::~SerialUpdateFunction() { } bool SerialUpdateFunction::Prepare() { params_ = serial::Update::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialUpdateFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } bool success = connection->Configure(params_->options); results_ = serial::Update::Results::Create(success); } SerialDisconnectFunction::SerialDisconnectFunction() { } SerialDisconnectFunction::~SerialDisconnectFunction() { } bool SerialDisconnectFunction::Prepare() { params_ = serial::Disconnect::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialDisconnectFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } RemoveSerialConnection(params_->connection_id); results_ = serial::Disconnect::Results::Create(true); } SerialSendFunction::SerialSendFunction() { } SerialSendFunction::~SerialSendFunction() { } bool SerialSendFunction::Prepare() { params_ = serial::Send::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialSendFunction::AsyncWorkStart() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; AsyncWorkCompleted(); return; } if (!connection->Send( params_->data, base::Bind(&SerialSendFunction::OnSendComplete, this))) { OnSendComplete(0, serial::SEND_ERROR_PENDING); } } 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(); } SerialFlushFunction::SerialFlushFunction() { } SerialFlushFunction::~SerialFlushFunction() { } bool SerialFlushFunction::Prepare() { params_ = serial::Flush::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialFlushFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } bool success = connection->Flush(); results_ = serial::Flush::Results::Create(success); } SerialSetPausedFunction::SerialSetPausedFunction() { } SerialSetPausedFunction::~SerialSetPausedFunction() { } bool SerialSetPausedFunction::Prepare() { params_ = serial::SetPaused::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); serial_event_dispatcher_ = SerialEventDispatcher::Get(browser_context()); DCHECK(serial_event_dispatcher_); return true; } void SerialSetPausedFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } if (params_->paused != connection->paused()) { connection->set_paused(params_->paused); if (!params_->paused) { serial_event_dispatcher_->PollConnection(extension_->id(), params_->connection_id); } } results_ = serial::SetPaused::Results::Create(); } SerialGetInfoFunction::SerialGetInfoFunction() { } SerialGetInfoFunction::~SerialGetInfoFunction() { } bool SerialGetInfoFunction::Prepare() { params_ = serial::GetInfo::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialGetInfoFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } serial::ConnectionInfo info; info.connection_id = params_->connection_id; connection->GetInfo(&info); results_ = serial::GetInfo::Results::Create(info); } SerialGetConnectionsFunction::SerialGetConnectionsFunction() { } SerialGetConnectionsFunction::~SerialGetConnectionsFunction() { } bool SerialGetConnectionsFunction::Prepare() { return true; } 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() { params_ = serial::GetControlSignals::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialGetControlSignalsFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } serial::DeviceControlSignals signals; if (!connection->GetControlSignals(&signals)) { error_ = kErrorGetControlSignalsFailed; return; } results_ = serial::GetControlSignals::Results::Create(signals); } SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() { } SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() { } bool SerialSetControlSignalsFunction::Prepare() { params_ = serial::SetControlSignals::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_.get()); return true; } void SerialSetControlSignalsFunction::Work() { SerialConnection* connection = GetSerialConnection(params_->connection_id); if (!connection) { error_ = kErrorSerialConnectionNotFound; return; } bool success = connection->SetControlSignals(params_->signals); results_ = serial::SetControlSignals::Results::Create(success); } } // namespace core_api } // namespace extensions namespace mojo { // static linked_ptr<extensions::core_api::serial::DeviceInfo> TypeConverter< linked_ptr<extensions::core_api::serial::DeviceInfo>, device::serial::DeviceInfoPtr>::Convert(const device::serial::DeviceInfoPtr& device) { linked_ptr<extensions::core_api::serial::DeviceInfo> info( new extensions::core_api::serial::DeviceInfo); info->path = device->path; if (device->has_vendor_id) info->vendor_id.reset(new int(static_cast<int>(device->vendor_id))); if (device->has_product_id) info->product_id.reset(new int(static_cast<int>(device->product_id))); if (device->display_name) info->display_name.reset(new std::string(device->display_name)); return info; } } // namespace mojo