Home | History | Annotate | Download | only in hid
      1 // Copyright (c) 2014 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 "device/hid/hid_connection_win.h"
      6 
      7 #include <cstring>
      8 
      9 #include "base/files/file.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/stl_util.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "base/win/object_watcher.h"
     14 #include "base/win/scoped_handle.h"
     15 #include "device/hid/hid_service.h"
     16 #include "device/hid/hid_service_win.h"
     17 
     18 #define INITGUID
     19 
     20 #include <windows.h>
     21 #include <hidclass.h>
     22 
     23 extern "C" {
     24 #include <hidsdi.h>
     25 }
     26 
     27 #include <setupapi.h>
     28 #include <winioctl.h>
     29 
     30 namespace device {
     31 
     32 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
     33                             public base::win::ObjectWatcher::Delegate,
     34                             public base::MessageLoop::DestructionObserver {
     35   PendingHidTransfer(scoped_refptr<HidConnectionWin> connection,
     36                      scoped_refptr<net::IOBufferWithSize> target_buffer,
     37                      scoped_refptr<net::IOBufferWithSize> receive_buffer,
     38                      HidConnection::IOCallback callback);
     39 
     40   void TakeResultFromWindowsAPI(BOOL result);
     41 
     42   OVERLAPPED* GetOverlapped() { return &overlapped_; }
     43 
     44   // Implements base::win::ObjectWatcher::Delegate.
     45   virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
     46 
     47   // Implements base::MessageLoop::DestructionObserver
     48   virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
     49 
     50   scoped_refptr<HidConnectionWin> connection_;
     51   scoped_refptr<net::IOBufferWithSize> target_buffer_;
     52   scoped_refptr<net::IOBufferWithSize> receive_buffer_;
     53   HidConnection::IOCallback callback_;
     54   OVERLAPPED overlapped_;
     55   base::win::ScopedHandle event_;
     56   base::win::ObjectWatcher watcher_;
     57 
     58  private:
     59   friend class base::RefCounted<PendingHidTransfer>;
     60 
     61   virtual ~PendingHidTransfer();
     62 
     63   DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
     64 };
     65 
     66 PendingHidTransfer::PendingHidTransfer(
     67     scoped_refptr<HidConnectionWin> connection,
     68     scoped_refptr<net::IOBufferWithSize> target_buffer,
     69     scoped_refptr<net::IOBufferWithSize> receive_buffer,
     70     HidConnection::IOCallback callback)
     71     : connection_(connection),
     72       target_buffer_(target_buffer),
     73       receive_buffer_(receive_buffer),
     74       callback_(callback),
     75       event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
     76   memset(&overlapped_, 0, sizeof(OVERLAPPED));
     77   overlapped_.hEvent = event_.Get();
     78 }
     79 
     80 PendingHidTransfer::~PendingHidTransfer() {
     81   base::MessageLoop::current()->RemoveDestructionObserver(this);
     82 }
     83 
     84 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
     85   if (result || GetLastError() != ERROR_IO_PENDING) {
     86     connection_->OnTransferFinished(this);
     87   } else {
     88     base::MessageLoop::current()->AddDestructionObserver(this);
     89     AddRef();
     90     watcher_.StartWatching(event_.Get(), this);
     91   }
     92 }
     93 
     94 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
     95   connection_->OnTransferFinished(this);
     96   Release();
     97 }
     98 
     99 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
    100   watcher_.StopWatching();
    101   connection_->OnTransferCanceled(this);
    102 }
    103 
    104 HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
    105     : HidConnection(device_info) {
    106   DCHECK(thread_checker_.CalledOnValidThread());
    107   file_.Set(CreateFileA(device_info.device_id.c_str(),
    108                         GENERIC_WRITE | GENERIC_READ,
    109                         FILE_SHARE_READ | FILE_SHARE_WRITE,
    110                         NULL,
    111                         OPEN_EXISTING,
    112                         FILE_FLAG_OVERLAPPED,
    113                         NULL));
    114 
    115   if (!file_.IsValid() &&
    116       GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
    117     file_.Set(CreateFileA(device_info.device_id.c_str(),
    118                           GENERIC_READ,
    119                           FILE_SHARE_READ,
    120                           NULL,
    121                           OPEN_EXISTING,
    122                           FILE_FLAG_OVERLAPPED,
    123                           NULL));
    124   }
    125 }
    126 
    127 bool HidConnectionWin::available() const {
    128   return file_.IsValid();
    129 }
    130 
    131 HidConnectionWin::~HidConnectionWin() {
    132   DCHECK(thread_checker_.CalledOnValidThread());
    133   CancelIo(file_.Get());
    134 }
    135 
    136 void HidConnectionWin::Read(scoped_refptr<net::IOBufferWithSize> buffer,
    137                             const HidConnection::IOCallback& callback) {
    138   DCHECK(thread_checker_.CalledOnValidThread());
    139   if (device_info().input_report_size == 0) {
    140     // The device does not support input reports.
    141     callback.Run(false, 0);
    142     return;
    143   }
    144 
    145   // This fairly awkward logic is correct: If Windows does not expect a device
    146   // to supply a report ID in its input reports, it requires the buffer to be
    147   // 1 byte larger than what the device actually sends.
    148   int receive_buffer_size = device_info().input_report_size;
    149   int expected_buffer_size = receive_buffer_size;
    150   if (!device_info().has_report_id)
    151     expected_buffer_size -= 1;
    152 
    153   if (buffer->size() < expected_buffer_size) {
    154     callback.Run(false, 0);
    155     return;
    156   }
    157 
    158   scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
    159   if (receive_buffer_size != expected_buffer_size)
    160     receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
    161   scoped_refptr<PendingHidTransfer> transfer(
    162       new PendingHidTransfer(this, buffer, receive_buffer, callback));
    163   transfers_.insert(transfer);
    164   transfer->TakeResultFromWindowsAPI(
    165       ReadFile(file_.Get(),
    166                receive_buffer->data(),
    167                static_cast<DWORD>(receive_buffer->size()),
    168                NULL,
    169                transfer->GetOverlapped()));
    170 }
    171 
    172 void HidConnectionWin::Write(uint8_t report_id,
    173                              scoped_refptr<net::IOBufferWithSize> buffer,
    174                              const HidConnection::IOCallback& callback) {
    175   DCHECK(thread_checker_.CalledOnValidThread());
    176   if (device_info().output_report_size == 0) {
    177     // The device does not support output reports.
    178     callback.Run(false, 0);
    179     return;
    180   }
    181 
    182   // The Windows API always wants either a report ID (if supported) or
    183   // zero at the front of every output report.
    184   scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
    185   output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
    186   output_buffer->data()[0] = report_id;
    187   memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
    188 
    189   scoped_refptr<PendingHidTransfer> transfer(
    190       new PendingHidTransfer(this, output_buffer, NULL, callback));
    191   transfers_.insert(transfer);
    192   transfer->TakeResultFromWindowsAPI(
    193       WriteFile(file_.Get(),
    194                 output_buffer->data(),
    195                 static_cast<DWORD>(output_buffer->size()),
    196                 NULL,
    197                 transfer->GetOverlapped()));
    198 }
    199 
    200 void HidConnectionWin::GetFeatureReport(
    201     uint8_t report_id,
    202     scoped_refptr<net::IOBufferWithSize> buffer,
    203     const IOCallback& callback) {
    204   DCHECK(thread_checker_.CalledOnValidThread());
    205   if (device_info().feature_report_size == 0) {
    206     // The device does not support feature reports.
    207     callback.Run(false, 0);
    208     return;
    209   }
    210 
    211   int receive_buffer_size = device_info().feature_report_size;
    212   int expected_buffer_size = receive_buffer_size;
    213   if (!device_info().has_report_id)
    214     expected_buffer_size -= 1;
    215   if (buffer->size() < expected_buffer_size) {
    216     callback.Run(false, 0);
    217     return;
    218   }
    219 
    220   scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
    221   if (receive_buffer_size != expected_buffer_size)
    222     receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
    223 
    224   // The first byte of the destination buffer is the report ID being requested.
    225   receive_buffer->data()[0] = report_id;
    226   scoped_refptr<PendingHidTransfer> transfer(
    227       new PendingHidTransfer(this, buffer, receive_buffer, callback));
    228   transfers_.insert(transfer);
    229   transfer->TakeResultFromWindowsAPI(
    230       DeviceIoControl(file_.Get(),
    231                       IOCTL_HID_GET_FEATURE,
    232                       NULL,
    233                       0,
    234                       receive_buffer->data(),
    235                       static_cast<DWORD>(receive_buffer->size()),
    236                       NULL,
    237                       transfer->GetOverlapped()));
    238 }
    239 
    240 void HidConnectionWin::SendFeatureReport(
    241     uint8_t report_id,
    242     scoped_refptr<net::IOBufferWithSize> buffer,
    243     const IOCallback& callback) {
    244   DCHECK(thread_checker_.CalledOnValidThread());
    245   if (device_info().feature_report_size == 0) {
    246     // The device does not support feature reports.
    247     callback.Run(false, 0);
    248     return;
    249   }
    250 
    251   // The Windows API always wants either a report ID (if supported) or
    252   // zero at the front of every output report.
    253   scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
    254   output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
    255   output_buffer->data()[0] = report_id;
    256   memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
    257 
    258   scoped_refptr<PendingHidTransfer> transfer(
    259       new PendingHidTransfer(this, output_buffer, NULL, callback));
    260   transfer->TakeResultFromWindowsAPI(
    261       DeviceIoControl(file_.Get(),
    262                       IOCTL_HID_SET_FEATURE,
    263                       output_buffer->data(),
    264                       static_cast<DWORD>(output_buffer->size()),
    265                       NULL,
    266                       0,
    267                       NULL,
    268                       transfer->GetOverlapped()));
    269 }
    270 
    271 void HidConnectionWin::OnTransferFinished(
    272     scoped_refptr<PendingHidTransfer> transfer) {
    273   DWORD bytes_transferred;
    274   transfers_.erase(transfer);
    275   if (GetOverlappedResult(
    276           file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
    277     if (bytes_transferred == 0)
    278       transfer->callback_.Run(true, 0);
    279     // If this is an input transfer and the receive buffer is not the same as
    280     // the target buffer, we need to copy the receive buffer into the target
    281     // buffer, discarding the first byte. This is because the target buffer's
    282     // owner is not expecting a report ID but Windows will always provide one.
    283     if (transfer->receive_buffer_ &&
    284         transfer->receive_buffer_ != transfer->target_buffer_) {
    285       // Move one byte forward.
    286       --bytes_transferred;
    287       memcpy(transfer->target_buffer_->data(),
    288              transfer->receive_buffer_->data() + 1,
    289              bytes_transferred);
    290     }
    291     transfer->callback_.Run(true, bytes_transferred);
    292   } else {
    293     transfer->callback_.Run(false, 0);
    294   }
    295 }
    296 
    297 void HidConnectionWin::OnTransferCanceled(
    298     scoped_refptr<PendingHidTransfer> transfer) {
    299   transfers_.erase(transfer);
    300   transfer->callback_.Run(false, 0);
    301 }
    302 
    303 }  // namespace device
    304