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.
5 #include "chrome/browser/extensions/api/serial/serial_connection.h"
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/common/extensions/api/serial.h"
16 namespace extensions {
20 const int kDefaultBufferSize = 4096;
24 static base::LazyInstance<ProfileKeyedAPIFactory<
25 ApiResourceManager<SerialConnection> > >
26 g_factory = LAZY_INSTANCE_INITIALIZER;
30 ProfileKeyedAPIFactory<ApiResourceManager<SerialConnection> >*
31 ApiResourceManager<SerialConnection>::GetFactoryInstance() {
32 return g_factory.Pointer();
35 SerialConnection::SerialConnection(const std::string& port,
36 const std::string& owner_extension_id)
37 : ApiResource(owner_extension_id),
39 file_(base::kInvalidPlatformFileValue),
41 buffer_size_(kDefaultBufferSize),
45 io_handler_(SerialIoHandler::Create()) {
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
49 SerialConnection::~SerialConnection() {
50 DCHECK(open_complete_.is_null());
51 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_DISCONNECTED);
52 io_handler_->CancelWrite(api::serial::SEND_ERROR_DISCONNECTED);
56 bool SerialConnection::IsPersistent() const {
60 void SerialConnection::set_buffer_size(int buffer_size) {
61 buffer_size_ = buffer_size;
64 void SerialConnection::set_receive_timeout(int receive_timeout) {
65 receive_timeout_ = receive_timeout;
68 void SerialConnection::set_send_timeout(int send_timeout) {
69 send_timeout_ = send_timeout;
72 void SerialConnection::set_paused(bool paused) {
75 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
79 void SerialConnection::Open(const OpenCompleteCallback& callback) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81 DCHECK(open_complete_.is_null());
82 open_complete_ = callback;
83 BrowserThread::PostTask(
84 BrowserThread::FILE, FROM_HERE,
85 base::Bind(&SerialConnection::StartOpen, base::Unretained(this)));
88 void SerialConnection::Close() {
89 DCHECK(open_complete_.is_null());
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
91 if (file_ != base::kInvalidPlatformFileValue) {
92 base::PlatformFile file = file_;
93 file_ = base::kInvalidPlatformFileValue;
94 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
95 base::Bind(&SerialConnection::DoClose, file));
99 bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) {
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
101 if (!receive_complete_.is_null())
103 receive_complete_ = callback;
104 io_handler_->Read(buffer_size_);
105 receive_timeout_task_.reset();
106 if (receive_timeout_ > 0) {
107 receive_timeout_task_.reset(new TimeoutTask(
108 base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()),
109 base::TimeDelta::FromMilliseconds(receive_timeout_)));
114 bool SerialConnection::Send(const std::string& data,
115 const SendCompleteCallback& callback) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117 if (!send_complete_.is_null())
119 send_complete_ = callback;
120 io_handler_->Write(data);
121 send_timeout_task_.reset();
122 if (send_timeout_ > 0) {
123 send_timeout_task_.reset(new TimeoutTask(
124 base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()),
125 base::TimeDelta::FromMilliseconds(send_timeout_)));
130 bool SerialConnection::Configure(
131 const api::serial::ConnectionOptions& options) {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
133 if (options.persistent.get())
134 set_persistent(*options.persistent);
135 if (options.name.get())
136 set_name(*options.name);
137 if (options.buffer_size.get())
138 set_buffer_size(*options.buffer_size);
139 if (options.receive_timeout.get())
140 set_receive_timeout(*options.receive_timeout);
141 if (options.send_timeout.get())
142 set_send_timeout(*options.send_timeout);
143 bool success = ConfigurePort(options);
144 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
148 void SerialConnection::SetIoHandlerForTest(
149 scoped_refptr<SerialIoHandler> handler) {
150 io_handler_ = handler;
153 bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const {
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
155 info->paused = paused_;
156 info->persistent = persistent_;
158 info->buffer_size = buffer_size_;
159 info->receive_timeout = receive_timeout_;
160 info->send_timeout = send_timeout_;
161 return GetPortInfo(info);
164 void SerialConnection::StartOpen() {
165 DCHECK(!open_complete_.is_null());
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
167 DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
168 base::PlatformFile file = base::kInvalidPlatformFileValue;
169 // It's the responsibility of the API wrapper around SerialConnection to
170 // validate the supplied path against the set of valid port names, and
171 // it is a reasonable assumption that serial port names are ASCII.
172 DCHECK(IsStringASCII(port_));
174 base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
175 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
176 base::PLATFORM_FILE_EXCLUSIVE_READ | base::PLATFORM_FILE_WRITE |
177 base::PLATFORM_FILE_EXCLUSIVE_WRITE | base::PLATFORM_FILE_ASYNC |
178 base::PLATFORM_FILE_TERMINAL_DEVICE;
179 file = base::CreatePlatformFile(path, flags, NULL, NULL);
180 BrowserThread::PostTask(
181 BrowserThread::IO, FROM_HERE,
182 base::Bind(&SerialConnection::FinishOpen, base::Unretained(this), file));
185 void SerialConnection::FinishOpen(base::PlatformFile file) {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
187 DCHECK(!open_complete_.is_null());
188 DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
189 OpenCompleteCallback callback = open_complete_;
190 open_complete_.Reset();
192 if (file == base::kInvalidPlatformFileValue) {
198 io_handler_->Initialize(
200 base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
201 base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
203 bool success = PostOpen();
208 callback.Run(success);
212 void SerialConnection::DoClose(base::PlatformFile port) {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
214 if (port != base::kInvalidPlatformFileValue) {
215 base::ClosePlatformFile(port);
219 void SerialConnection::OnReceiveTimeout() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
221 io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT);
224 void SerialConnection::OnSendTimeout() {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226 io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT);
229 void SerialConnection::OnAsyncReadComplete(const std::string& data,
230 api::serial::ReceiveError error) {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232 DCHECK(!receive_complete_.is_null());
233 ReceiveCompleteCallback callback = receive_complete_;
234 receive_complete_.Reset();
235 receive_timeout_task_.reset();
236 callback.Run(data, error);
239 void SerialConnection::OnAsyncWriteComplete(int bytes_sent,
240 api::serial::SendError error) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
242 DCHECK(!send_complete_.is_null());
243 SendCompleteCallback callback = send_complete_;
244 send_complete_.Reset();
245 send_timeout_task_.reset();
246 callback.Run(bytes_sent, error);
249 SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure,
250 const base::TimeDelta& delay)
251 : weak_factory_(this),
254 base::MessageLoop::current()->PostDelayedTask(
256 base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()),
260 SerialConnection::TimeoutTask::~TimeoutTask() {}
262 void SerialConnection::TimeoutTask::Run() const {
266 } // namespace extensions