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/profiler/scoped_tracker.h"
14 #include "base/win/object_watcher.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 virtual void OnObjectSignaled(HANDLE object
) override
;
45 // Implements base::MessageLoop::DestructionObserver
46 virtual 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 virtual ~PendingHidTransfer();
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 VPLOG(1) << "HID transfer failed";
87 callback_
.Run(this, false);
91 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle
) {
92 // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
93 tracked_objects::ScopedTracker
tracking_profile(
94 FROM_HERE_WITH_EXPLICIT_FUNCTION("PendingHidTransfer_OnObjectSignaled"));
96 callback_
.Run(this, true);
100 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
101 watcher_
.StopWatching();
102 callback_
.Run(this, false);
105 HidConnectionWin::HidConnectionWin(scoped_refptr
<HidDeviceInfo
> device_info
,
106 base::win::ScopedHandle file
)
107 : HidConnection(device_info
) {
111 HidConnectionWin::~HidConnectionWin() {
114 void HidConnectionWin::PlatformClose() {
115 CancelIo(file_
.Get());
118 void HidConnectionWin::PlatformRead(
119 const HidConnection::ReadCallback
& callback
) {
120 // Windows will always include the report ID (including zero if report IDs
121 // are not in use) in the buffer.
122 scoped_refptr
<net::IOBufferWithSize
> buffer
= new net::IOBufferWithSize(
123 base::checked_cast
<int>(device_info()->max_input_report_size() + 1));
124 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
126 base::Bind(&HidConnectionWin::OnReadComplete
, this, buffer
, callback
)));
127 transfers_
.insert(transfer
);
128 transfer
->TakeResultFromWindowsAPI(
129 ReadFile(file_
.Get(),
131 static_cast<DWORD
>(buffer
->size()),
133 transfer
->GetOverlapped()));
136 void HidConnectionWin::PlatformWrite(scoped_refptr
<net::IOBuffer
> buffer
,
138 const WriteCallback
& callback
) {
139 // The Windows API always wants either a report ID (if supported) or
140 // zero at the front of every output report.
141 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
142 buffer
, base::Bind(&HidConnectionWin::OnWriteComplete
, this, callback
)));
143 transfers_
.insert(transfer
);
144 transfer
->TakeResultFromWindowsAPI(WriteFile(file_
.Get(),
146 static_cast<DWORD
>(size
),
148 transfer
->GetOverlapped()));
151 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id
,
152 const ReadCallback
& callback
) {
153 // The first byte of the destination buffer is the report ID being requested.
154 scoped_refptr
<net::IOBufferWithSize
> buffer
= new net::IOBufferWithSize(
155 base::checked_cast
<int>(device_info()->max_feature_report_size() + 1));
156 buffer
->data()[0] = report_id
;
158 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
161 &HidConnectionWin::OnReadFeatureComplete
, this, buffer
, callback
)));
162 transfers_
.insert(transfer
);
163 transfer
->TakeResultFromWindowsAPI(
164 DeviceIoControl(file_
.Get(),
165 IOCTL_HID_GET_FEATURE
,
169 static_cast<DWORD
>(buffer
->size()),
171 transfer
->GetOverlapped()));
174 void HidConnectionWin::PlatformSendFeatureReport(
175 scoped_refptr
<net::IOBuffer
> buffer
,
177 const WriteCallback
& callback
) {
178 // The Windows API always wants either a report ID (if supported) or
179 // zero at the front of every output report.
180 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
181 buffer
, base::Bind(&HidConnectionWin::OnWriteComplete
, this, callback
)));
182 transfer
->TakeResultFromWindowsAPI(
183 DeviceIoControl(file_
.Get(),
184 IOCTL_HID_SET_FEATURE
,
186 static_cast<DWORD
>(size
),
190 transfer
->GetOverlapped()));
193 void HidConnectionWin::OnReadComplete(scoped_refptr
<net::IOBuffer
> buffer
,
194 const ReadCallback
& callback
,
195 PendingHidTransfer
* transfer
,
198 callback
.Run(false, NULL
, 0);
202 DWORD bytes_transferred
;
203 if (GetOverlappedResult(
204 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
205 CompleteRead(buffer
, bytes_transferred
, callback
);
207 VPLOG(1) << "HID read failed";
208 callback
.Run(false, NULL
, 0);
212 void HidConnectionWin::OnReadFeatureComplete(
213 scoped_refptr
<net::IOBuffer
> buffer
,
214 const ReadCallback
& callback
,
215 PendingHidTransfer
* transfer
,
218 callback
.Run(false, NULL
, 0);
222 DWORD bytes_transferred
;
223 if (GetOverlappedResult(
224 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
225 callback
.Run(true, buffer
, bytes_transferred
);
227 VPLOG(1) << "HID read failed";
228 callback
.Run(false, NULL
, 0);
232 void HidConnectionWin::OnWriteComplete(const WriteCallback
& callback
,
233 PendingHidTransfer
* transfer
,
240 DWORD bytes_transferred
;
241 if (GetOverlappedResult(
242 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
245 VPLOG(1) << "HID write failed";
250 } // namespace device