Home | History | Annotate | Download | only in serial
      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