1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/api/serial/serial_connection.h" 6 7#include <string> 8 9#include "base/files/file_path.h" 10#include "base/lazy_instance.h" 11#include "base/strings/string_util.h" 12#include "chrome/common/extensions/api/serial.h" 13#include "extensions/browser/api/api_resource_manager.h" 14 15namespace extensions { 16 17namespace { 18 19const int kDefaultBufferSize = 4096; 20} 21 22static base::LazyInstance< 23 BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> > > 24 g_factory = LAZY_INSTANCE_INITIALIZER; 25 26// static 27template <> 28BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> >* 29ApiResourceManager<SerialConnection>::GetFactoryInstance() { 30 return g_factory.Pointer(); 31} 32 33SerialConnection::SerialConnection(const std::string& port, 34 const std::string& owner_extension_id) 35 : ApiResource(owner_extension_id), 36 port_(port), 37 persistent_(false), 38 buffer_size_(kDefaultBufferSize), 39 receive_timeout_(0), 40 send_timeout_(0), 41 paused_(false), 42 io_handler_(SerialIoHandler::Create()) { 43 DCHECK_CURRENTLY_ON(BrowserThread::IO); 44} 45 46SerialConnection::~SerialConnection() { 47 DCHECK(open_complete_.is_null()); 48 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_DISCONNECTED); 49 io_handler_->CancelWrite(api::serial::SEND_ERROR_DISCONNECTED); 50 Close(); 51} 52 53bool SerialConnection::IsPersistent() const { return persistent(); } 54 55void SerialConnection::set_buffer_size(int buffer_size) { 56 buffer_size_ = buffer_size; 57} 58 59void SerialConnection::set_receive_timeout(int receive_timeout) { 60 receive_timeout_ = receive_timeout; 61} 62 63void SerialConnection::set_send_timeout(int send_timeout) { 64 send_timeout_ = send_timeout; 65} 66 67void SerialConnection::set_paused(bool paused) { 68 paused_ = paused; 69 if (paused) { 70 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE); 71 } 72} 73 74void SerialConnection::Open(const OpenCompleteCallback& callback) { 75 DCHECK_CURRENTLY_ON(BrowserThread::IO); 76 DCHECK(open_complete_.is_null()); 77 open_complete_ = callback; 78 BrowserThread::PostTask( 79 BrowserThread::FILE, 80 FROM_HERE, 81 base::Bind(&SerialConnection::StartOpen, base::Unretained(this))); 82} 83 84void SerialConnection::Close() { 85 DCHECK(open_complete_.is_null()); 86 DCHECK_CURRENTLY_ON(BrowserThread::IO); 87 if (file_.IsValid()) { 88 BrowserThread::PostTask( 89 BrowserThread::FILE, 90 FROM_HERE, 91 base::Bind(&SerialConnection::DoClose, Passed(file_.Pass()))); 92 } 93} 94 95bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) { 96 DCHECK_CURRENTLY_ON(BrowserThread::IO); 97 if (!receive_complete_.is_null()) 98 return false; 99 receive_complete_ = callback; 100 io_handler_->Read(buffer_size_); 101 receive_timeout_task_.reset(); 102 if (receive_timeout_ > 0) { 103 receive_timeout_task_.reset(new TimeoutTask( 104 base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()), 105 base::TimeDelta::FromMilliseconds(receive_timeout_))); 106 } 107 return true; 108} 109 110bool SerialConnection::Send(const std::string& data, 111 const SendCompleteCallback& callback) { 112 DCHECK_CURRENTLY_ON(BrowserThread::IO); 113 if (!send_complete_.is_null()) 114 return false; 115 send_complete_ = callback; 116 io_handler_->Write(data); 117 send_timeout_task_.reset(); 118 if (send_timeout_ > 0) { 119 send_timeout_task_.reset(new TimeoutTask( 120 base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()), 121 base::TimeDelta::FromMilliseconds(send_timeout_))); 122 } 123 return true; 124} 125 126bool SerialConnection::Configure( 127 const api::serial::ConnectionOptions& options) { 128 DCHECK_CURRENTLY_ON(BrowserThread::IO); 129 if (options.persistent.get()) 130 set_persistent(*options.persistent); 131 if (options.name.get()) 132 set_name(*options.name); 133 if (options.buffer_size.get()) 134 set_buffer_size(*options.buffer_size); 135 if (options.receive_timeout.get()) 136 set_receive_timeout(*options.receive_timeout); 137 if (options.send_timeout.get()) 138 set_send_timeout(*options.send_timeout); 139 bool success = ConfigurePort(options); 140 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE); 141 return success; 142} 143 144void SerialConnection::SetIoHandlerForTest( 145 scoped_refptr<SerialIoHandler> handler) { 146 io_handler_ = handler; 147} 148 149bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const { 150 DCHECK_CURRENTLY_ON(BrowserThread::IO); 151 info->paused = paused_; 152 info->persistent = persistent_; 153 info->name = name_; 154 info->buffer_size = buffer_size_; 155 info->receive_timeout = receive_timeout_; 156 info->send_timeout = send_timeout_; 157 return GetPortInfo(info); 158} 159 160void SerialConnection::StartOpen() { 161 DCHECK(!open_complete_.is_null()); 162 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 163 DCHECK(!file_.IsValid()); 164 // It's the responsibility of the API wrapper around SerialConnection to 165 // validate the supplied path against the set of valid port names, and 166 // it is a reasonable assumption that serial port names are ASCII. 167 DCHECK(base::IsStringASCII(port_)); 168 base::FilePath path( 169 base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_))); 170 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | 171 base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE | 172 base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC | 173 base::File::FLAG_TERMINAL_DEVICE; 174 base::File file(path, flags); 175 BrowserThread::PostTask( 176 BrowserThread::IO, 177 FROM_HERE, 178 base::Bind(&SerialConnection::FinishOpen, base::Unretained(this), 179 Passed(file.Pass()))); 180} 181 182void SerialConnection::FinishOpen(base::File file) { 183 DCHECK_CURRENTLY_ON(BrowserThread::IO); 184 DCHECK(!open_complete_.is_null()); 185 DCHECK(!file_.IsValid()); 186 OpenCompleteCallback callback = open_complete_; 187 open_complete_.Reset(); 188 189 if (!file.IsValid()) { 190 callback.Run(false); 191 return; 192 } 193 194 // TODO(rvargas): crbug.com/351073. This is wrong. io_handler_ keeps a copy of 195 // the handler that is not in sync with this one. 196 file_ = file.Pass(); 197 io_handler_->Initialize( 198 file_.GetPlatformFile(), 199 base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()), 200 base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr())); 201 202 bool success = PostOpen(); 203 if (!success) { 204 Close(); 205 } 206 207 callback.Run(success); 208} 209 210// static 211void SerialConnection::DoClose(base::File port) { 212 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 213 // port closed by destructor. 214} 215 216void SerialConnection::OnReceiveTimeout() { 217 DCHECK_CURRENTLY_ON(BrowserThread::IO); 218 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT); 219} 220 221void SerialConnection::OnSendTimeout() { 222 DCHECK_CURRENTLY_ON(BrowserThread::IO); 223 io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT); 224} 225 226void SerialConnection::OnAsyncReadComplete(const std::string& data, 227 api::serial::ReceiveError error) { 228 DCHECK_CURRENTLY_ON(BrowserThread::IO); 229 DCHECK(!receive_complete_.is_null()); 230 ReceiveCompleteCallback callback = receive_complete_; 231 receive_complete_.Reset(); 232 receive_timeout_task_.reset(); 233 callback.Run(data, error); 234} 235 236void SerialConnection::OnAsyncWriteComplete(int bytes_sent, 237 api::serial::SendError error) { 238 DCHECK_CURRENTLY_ON(BrowserThread::IO); 239 DCHECK(!send_complete_.is_null()); 240 SendCompleteCallback callback = send_complete_; 241 send_complete_.Reset(); 242 send_timeout_task_.reset(); 243 callback.Run(bytes_sent, error); 244} 245 246SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure, 247 const base::TimeDelta& delay) 248 : weak_factory_(this), closure_(closure), delay_(delay) { 249 base::MessageLoop::current()->PostDelayedTask( 250 FROM_HERE, 251 base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()), 252 delay_); 253} 254 255SerialConnection::TimeoutTask::~TimeoutTask() {} 256 257void SerialConnection::TimeoutTask::Run() const { closure_.Run(); } 258 259} // namespace extensions 260