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