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"
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"
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_
;
52 OVERLAPPED overlapped_
;
53 base::win::ScopedHandle event_
;
54 base::win::ObjectWatcher watcher_
;
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
)
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
) {
80 callback_
.Run(this, true);
81 } else if (GetLastError() == ERROR_IO_PENDING
) {
82 base::MessageLoop::current()->AddDestructionObserver(this);
84 watcher_
.StartWatching(event_
.Get(), this);
86 HID_PLOG(EVENT
) << "HID transfer failed";
87 callback_
.Run(this, false);
91 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle
) {
92 callback_
.Run(this, true);
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
) {
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(
122 base::Bind(&HidConnectionWin::OnReadComplete
, this, buffer
, callback
)));
123 transfers_
.insert(transfer
);
124 transfer
->TakeResultFromWindowsAPI(
125 ReadFile(file_
.Get(),
127 static_cast<DWORD
>(buffer
->size()),
129 transfer
->GetOverlapped()));
132 void HidConnectionWin::PlatformWrite(scoped_refptr
<net::IOBuffer
> buffer
,
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
);
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(),
153 static_cast<DWORD
>(size
),
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(
168 &HidConnectionWin::OnReadFeatureComplete
, this, buffer
, callback
)));
169 transfers_
.insert(transfer
);
170 transfer
->TakeResultFromWindowsAPI(
171 DeviceIoControl(file_
.Get(),
172 IOCTL_HID_GET_FEATURE
,
176 static_cast<DWORD
>(buffer
->size()),
178 transfer
->GetOverlapped()));
181 void HidConnectionWin::PlatformSendFeatureReport(
182 scoped_refptr
<net::IOBuffer
> buffer
,
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
,
193 static_cast<DWORD
>(size
),
197 transfer
->GetOverlapped()));
200 void HidConnectionWin::OnReadComplete(scoped_refptr
<net::IOBuffer
> buffer
,
201 const ReadCallback
& callback
,
202 PendingHidTransfer
* transfer
,
205 callback
.Run(false, NULL
, 0);
209 DWORD bytes_transferred
;
210 if (GetOverlappedResult(
211 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
212 CompleteRead(buffer
, bytes_transferred
, callback
);
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
,
225 callback
.Run(false, NULL
, 0);
229 DWORD bytes_transferred
;
230 if (GetOverlappedResult(
231 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
232 callback
.Run(true, buffer
, bytes_transferred
);
234 HID_PLOG(EVENT
) << "HID read failed";
235 callback
.Run(false, NULL
, 0);
239 void HidConnectionWin::OnWriteComplete(const WriteCallback
& callback
,
240 PendingHidTransfer
* transfer
,
247 DWORD bytes_transferred
;
248 if (GetOverlappedResult(
249 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
252 HID_PLOG(EVENT
) << "HID write failed";
257 } // namespace device