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/bind.h"
     10 #include "base/files/file.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/win/object_watcher.h"
     13 
     14 #define INITGUID
     15 
     16 #include <windows.h>
     17 #include <hidclass.h>
     18 
     19 extern "C" {
     20 #include <hidsdi.h>
     21 }
     22 
     23 #include <setupapi.h>
     24 #include <winioctl.h>
     25 
     26 namespace device {
     27 
     28 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
     29                             public base::win::ObjectWatcher::Delegate,
     30                             public base::MessageLoop::DestructionObserver {
     31   typedef base::Callback<void(PendingHidTransfer*, bool)> Callback;
     32 
     33   PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer,
     34                      const Callback& callback);
     35 
     36   void TakeResultFromWindowsAPI(BOOL result);
     37 
     38   OVERLAPPED* GetOverlapped() { return &overlapped_; }
     39 
     40   // Implements base::win::ObjectWatcher::Delegate.
     41   virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
     42 
     43   // Implements base::MessageLoop::DestructionObserver
     44   virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
     45 
     46   // The buffer isn't used by this object but it's important that a reference
     47   // to it is held until the transfer completes.
     48   scoped_refptr<net::IOBuffer> buffer_;
     49   Callback callback_;
     50   OVERLAPPED overlapped_;
     51   base::win::ScopedHandle event_;
     52   base::win::ObjectWatcher watcher_;
     53 
     54  private:
     55   friend class base::RefCounted<PendingHidTransfer>;
     56 
     57   virtual ~PendingHidTransfer();
     58 
     59   DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
     60 };
     61 
     62 PendingHidTransfer::PendingHidTransfer(
     63     scoped_refptr<net::IOBuffer> buffer,
     64     const PendingHidTransfer::Callback& callback)
     65     : buffer_(buffer),
     66       callback_(callback),
     67       event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
     68   memset(&overlapped_, 0, sizeof(OVERLAPPED));
     69   overlapped_.hEvent = event_.Get();
     70 }
     71 
     72 PendingHidTransfer::~PendingHidTransfer() {
     73   base::MessageLoop::current()->RemoveDestructionObserver(this);
     74 }
     75 
     76 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
     77   if (result) {
     78     callback_.Run(this, true);
     79   } else if (GetLastError() == ERROR_IO_PENDING) {
     80     base::MessageLoop::current()->AddDestructionObserver(this);
     81     AddRef();
     82     watcher_.StartWatching(event_.Get(), this);
     83   } else {
     84     VPLOG(1) << "HID transfer failed";
     85     callback_.Run(this, false);
     86   }
     87 }
     88 
     89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
     90   callback_.Run(this, true);
     91   Release();
     92 }
     93 
     94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
     95   watcher_.StopWatching();
     96   callback_.Run(this, false);
     97 }
     98 
     99 HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
    100     : HidConnection(device_info) {
    101   file_.Set(CreateFileA(device_info.device_id.c_str(),
    102                         GENERIC_WRITE | GENERIC_READ,
    103                         FILE_SHARE_READ | FILE_SHARE_WRITE,
    104                         NULL,
    105                         OPEN_EXISTING,
    106                         FILE_FLAG_OVERLAPPED,
    107                         NULL));
    108 
    109   if (!file_.IsValid() &&
    110       GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
    111     file_.Set(CreateFileA(device_info.device_id.c_str(),
    112                           GENERIC_READ,
    113                           FILE_SHARE_READ,
    114                           NULL,
    115                           OPEN_EXISTING,
    116                           FILE_FLAG_OVERLAPPED,
    117                           NULL));
    118   }
    119 }
    120 
    121 HidConnectionWin::~HidConnectionWin() {
    122 }
    123 
    124 void HidConnectionWin::PlatformClose() {
    125   CancelIo(file_.Get());
    126 }
    127 
    128 void HidConnectionWin::PlatformRead(
    129     const HidConnection::ReadCallback& callback) {
    130   // Windows will always include the report ID (including zero if report IDs
    131   // are not in use) in the buffer.
    132   scoped_refptr<net::IOBufferWithSize> buffer =
    133       new net::IOBufferWithSize(device_info().max_input_report_size + 1);
    134   scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
    135       buffer,
    136       base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback)));
    137   transfers_.insert(transfer);
    138   transfer->TakeResultFromWindowsAPI(
    139       ReadFile(file_.Get(),
    140                buffer->data(),
    141                static_cast<DWORD>(buffer->size()),
    142                NULL,
    143                transfer->GetOverlapped()));
    144 }
    145 
    146 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
    147                                      size_t size,
    148                                      const WriteCallback& callback) {
    149   // The Windows API always wants either a report ID (if supported) or
    150   // zero at the front of every output report.
    151   scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
    152       buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
    153   transfers_.insert(transfer);
    154   transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(),
    155                                                buffer->data(),
    156                                                static_cast<DWORD>(size),
    157                                                NULL,
    158                                                transfer->GetOverlapped()));
    159 }
    160 
    161 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id,
    162                                                 const ReadCallback& callback) {
    163   // The first byte of the destination buffer is the report ID being requested.
    164   scoped_refptr<net::IOBufferWithSize> buffer =
    165       new net::IOBufferWithSize(device_info().max_feature_report_size + 1);
    166   buffer->data()[0] = report_id;
    167 
    168   scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
    169       buffer,
    170       base::Bind(
    171           &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback)));
    172   transfers_.insert(transfer);
    173   transfer->TakeResultFromWindowsAPI(
    174       DeviceIoControl(file_.Get(),
    175                       IOCTL_HID_GET_FEATURE,
    176                       NULL,
    177                       0,
    178                       buffer->data(),
    179                       static_cast<DWORD>(buffer->size()),
    180                       NULL,
    181                       transfer->GetOverlapped()));
    182 }
    183 
    184 void HidConnectionWin::PlatformSendFeatureReport(
    185     scoped_refptr<net::IOBuffer> buffer,
    186     size_t size,
    187     const WriteCallback& callback) {
    188   // The Windows API always wants either a report ID (if supported) or
    189   // zero at the front of every output report.
    190   scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
    191       buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
    192   transfer->TakeResultFromWindowsAPI(
    193       DeviceIoControl(file_.Get(),
    194                       IOCTL_HID_SET_FEATURE,
    195                       buffer->data(),
    196                       static_cast<DWORD>(size),
    197                       NULL,
    198                       0,
    199                       NULL,
    200                       transfer->GetOverlapped()));
    201 }
    202 
    203 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
    204                                       const ReadCallback& callback,
    205                                       PendingHidTransfer* transfer,
    206                                       bool signaled) {
    207   if (!signaled) {
    208     callback.Run(false, NULL, 0);
    209     return;
    210   }
    211 
    212   DWORD bytes_transferred;
    213   if (GetOverlappedResult(
    214           file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
    215     CompleteRead(buffer, bytes_transferred, callback);
    216   } else {
    217     VPLOG(1) << "HID read failed";
    218     callback.Run(false, NULL, 0);
    219   }
    220 }
    221 
    222 void HidConnectionWin::OnReadFeatureComplete(
    223     scoped_refptr<net::IOBuffer> buffer,
    224     const ReadCallback& callback,
    225     PendingHidTransfer* transfer,
    226     bool signaled) {
    227   if (!signaled) {
    228     callback.Run(false, NULL, 0);
    229     return;
    230   }
    231 
    232   DWORD bytes_transferred;
    233   if (GetOverlappedResult(
    234           file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
    235     scoped_refptr<net::IOBuffer> new_buffer(
    236         new net::IOBuffer(bytes_transferred - 1));
    237     memcpy(new_buffer->data(), buffer->data() + 1, bytes_transferred - 1);
    238     CompleteRead(new_buffer, bytes_transferred, callback);
    239   } else {
    240     VPLOG(1) << "HID read failed";
    241     callback.Run(false, NULL, 0);
    242   }
    243 }
    244 
    245 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback,
    246                                        PendingHidTransfer* transfer,
    247                                        bool signaled) {
    248   if (!signaled) {
    249     callback.Run(false);
    250     return;
    251   }
    252 
    253   DWORD bytes_transferred;
    254   if (GetOverlappedResult(
    255           file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
    256     callback.Run(true);
    257   } else {
    258     VPLOG(1) << "HID write failed";
    259     callback.Run(false);
    260   }
    261 }
    262 
    263 }  // namespace device
    264