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