mac: Let IPhotoDataProvider::GetAlbumNames() return albums in a deterministic order.
[chromium-blink-merge.git] / device / hid / hid_connection_win.cc
blob2e5eab24dd63220ddee74081636e4d530bcb44f4
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.
5 #include "device/hid/hid_connection_win.h"
7 #include <cstring>
9 #include "base/bind.h"
10 #include "base/files/file.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/win/object_watcher.h"
14 #include "components/device_event_log/device_event_log.h"
16 #define INITGUID
18 #include <windows.h>
19 #include <hidclass.h>
21 extern "C" {
22 #include <hidsdi.h>
25 #include <setupapi.h>
26 #include <winioctl.h>
28 namespace device {
30 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
31 public base::win::ObjectWatcher::Delegate,
32 public base::MessageLoop::DestructionObserver {
33 typedef base::Callback<void(PendingHidTransfer*, bool)> Callback;
35 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer,
36 const Callback& callback);
38 void TakeResultFromWindowsAPI(BOOL result);
40 OVERLAPPED* GetOverlapped() { return &overlapped_; }
42 // Implements base::win::ObjectWatcher::Delegate.
43 void OnObjectSignaled(HANDLE object) override;
45 // Implements base::MessageLoop::DestructionObserver
46 void WillDestroyCurrentMessageLoop() override;
48 // The buffer isn't used by this object but it's important that a reference
49 // to it is held until the transfer completes.
50 scoped_refptr<net::IOBuffer> buffer_;
51 Callback callback_;
52 OVERLAPPED overlapped_;
53 base::win::ScopedHandle event_;
54 base::win::ObjectWatcher watcher_;
56 private:
57 friend class base::RefCounted<PendingHidTransfer>;
59 ~PendingHidTransfer() override;
61 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
64 PendingHidTransfer::PendingHidTransfer(
65 scoped_refptr<net::IOBuffer> buffer,
66 const PendingHidTransfer::Callback& callback)
67 : buffer_(buffer),
68 callback_(callback),
69 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
70 memset(&overlapped_, 0, sizeof(OVERLAPPED));
71 overlapped_.hEvent = event_.Get();
74 PendingHidTransfer::~PendingHidTransfer() {
75 base::MessageLoop::current()->RemoveDestructionObserver(this);
78 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
79 if (result) {
80 callback_.Run(this, true);
81 } else if (GetLastError() == ERROR_IO_PENDING) {
82 base::MessageLoop::current()->AddDestructionObserver(this);
83 AddRef();
84 watcher_.StartWatching(event_.Get(), this);
85 } else {
86 HID_PLOG(EVENT) << "HID transfer failed";
87 callback_.Run(this, false);
91 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
92 callback_.Run(this, true);
93 Release();
96 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
97 watcher_.StopWatching();
98 callback_.Run(this, false);
101 HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info,
102 base::win::ScopedHandle file)
103 : HidConnection(device_info) {
104 file_ = file.Pass();
107 HidConnectionWin::~HidConnectionWin() {
110 void HidConnectionWin::PlatformClose() {
111 CancelIo(file_.Get());
114 void HidConnectionWin::PlatformRead(
115 const HidConnection::ReadCallback& callback) {
116 // Windows will always include the report ID (including zero if report IDs
117 // are not in use) in the buffer.
118 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize(
119 base::checked_cast<int>(device_info()->max_input_report_size() + 1));
120 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
121 buffer,
122 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback)));
123 transfers_.insert(transfer);
124 transfer->TakeResultFromWindowsAPI(
125 ReadFile(file_.Get(),
126 buffer->data(),
127 static_cast<DWORD>(buffer->size()),
128 NULL,
129 transfer->GetOverlapped()));
132 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
133 size_t size,
134 const WriteCallback& callback) {
135 size_t expected_size = device_info()->max_output_report_size() + 1;
136 DCHECK(size <= expected_size);
137 // The Windows API always wants either a report ID (if supported) or zero at
138 // the front of every output report and requires that the buffer size be equal
139 // to the maximum output report size supported by this collection.
140 if (size < expected_size) {
141 scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer(
142 base::checked_cast<int>(expected_size));
143 memcpy(tmp_buffer->data(), buffer->data(), size);
144 memset(tmp_buffer->data() + size, 0, expected_size - size);
145 buffer = tmp_buffer;
146 size = expected_size;
148 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
149 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
150 transfers_.insert(transfer);
151 transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(),
152 buffer->data(),
153 static_cast<DWORD>(size),
154 NULL,
155 transfer->GetOverlapped()));
158 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id,
159 const ReadCallback& callback) {
160 // The first byte of the destination buffer is the report ID being requested.
161 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize(
162 base::checked_cast<int>(device_info()->max_feature_report_size() + 1));
163 buffer->data()[0] = report_id;
165 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
166 buffer,
167 base::Bind(
168 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback)));
169 transfers_.insert(transfer);
170 transfer->TakeResultFromWindowsAPI(
171 DeviceIoControl(file_.Get(),
172 IOCTL_HID_GET_FEATURE,
173 NULL,
175 buffer->data(),
176 static_cast<DWORD>(buffer->size()),
177 NULL,
178 transfer->GetOverlapped()));
181 void HidConnectionWin::PlatformSendFeatureReport(
182 scoped_refptr<net::IOBuffer> buffer,
183 size_t size,
184 const WriteCallback& callback) {
185 // The Windows API always wants either a report ID (if supported) or
186 // zero at the front of every output report.
187 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
188 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
189 transfer->TakeResultFromWindowsAPI(
190 DeviceIoControl(file_.Get(),
191 IOCTL_HID_SET_FEATURE,
192 buffer->data(),
193 static_cast<DWORD>(size),
194 NULL,
196 NULL,
197 transfer->GetOverlapped()));
200 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
201 const ReadCallback& callback,
202 PendingHidTransfer* transfer,
203 bool signaled) {
204 if (!signaled) {
205 callback.Run(false, NULL, 0);
206 return;
209 DWORD bytes_transferred;
210 if (GetOverlappedResult(
211 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
212 CompleteRead(buffer, bytes_transferred, callback);
213 } else {
214 HID_PLOG(EVENT) << "HID read failed";
215 callback.Run(false, NULL, 0);
219 void HidConnectionWin::OnReadFeatureComplete(
220 scoped_refptr<net::IOBuffer> buffer,
221 const ReadCallback& callback,
222 PendingHidTransfer* transfer,
223 bool signaled) {
224 if (!signaled) {
225 callback.Run(false, NULL, 0);
226 return;
229 DWORD bytes_transferred;
230 if (GetOverlappedResult(
231 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
232 callback.Run(true, buffer, bytes_transferred);
233 } else {
234 HID_PLOG(EVENT) << "HID read failed";
235 callback.Run(false, NULL, 0);
239 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback,
240 PendingHidTransfer* transfer,
241 bool signaled) {
242 if (!signaled) {
243 callback.Run(false);
244 return;
247 DWORD bytes_transferred;
248 if (GetOverlappedResult(
249 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
250 callback.Run(true);
251 } else {
252 HID_PLOG(EVENT) << "HID write failed";
253 callback.Run(false);
257 } // namespace device