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