• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetOverlappeddevice::PendingHidTransfer38   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 
PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer,const PendingHidTransfer::Callback & callback)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 
~PendingHidTransfer()72 PendingHidTransfer::~PendingHidTransfer() {
73   base::MessageLoop::current()->RemoveDestructionObserver(this);
74 }
75 
TakeResultFromWindowsAPI(BOOL result)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 
OnObjectSignaled(HANDLE event_handle)89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
90   callback_.Run(this, true);
91   Release();
92 }
93 
WillDestroyCurrentMessageLoop()94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
95   watcher_.StopWatching();
96   callback_.Run(this, false);
97 }
98 
HidConnectionWin(const HidDeviceInfo & device_info)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 
~HidConnectionWin()121 HidConnectionWin::~HidConnectionWin() {
122 }
123 
PlatformClose()124 void HidConnectionWin::PlatformClose() {
125   CancelIo(file_.Get());
126 }
127 
PlatformRead(const HidConnection::ReadCallback & callback)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 
PlatformWrite(scoped_refptr<net::IOBuffer> buffer,size_t size,const WriteCallback & callback)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 
PlatformGetFeatureReport(uint8_t report_id,const ReadCallback & callback)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 
PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,size_t size,const WriteCallback & callback)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 
OnReadComplete(scoped_refptr<net::IOBuffer> buffer,const ReadCallback & callback,PendingHidTransfer * transfer,bool signaled)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 
OnReadFeatureComplete(scoped_refptr<net::IOBuffer> buffer,const ReadCallback & callback,PendingHidTransfer * transfer,bool signaled)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 
OnWriteComplete(const WriteCallback & callback,PendingHidTransfer * transfer,bool signaled)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