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_profile.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 ScopedProfile below once crbug.com/418183 is fixed.
93 tracked_objects::ScopedProfile
tracking_profile(
94 FROM_HERE_WITH_EXPLICIT_FUNCTION(
95 "PendingHidTransfer_OnObjectSignaled"));
97 callback_
.Run(this, true);
101 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
102 watcher_
.StopWatching();
103 callback_
.Run(this, false);
106 HidConnectionWin::HidConnectionWin(const HidDeviceInfo
& device_info
)
107 : HidConnection(device_info
) {
108 file_
.Set(CreateFileA(device_info
.device_id
.c_str(),
109 GENERIC_WRITE
| GENERIC_READ
,
110 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
113 FILE_FLAG_OVERLAPPED
,
116 if (!file_
.IsValid() &&
117 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED
) {
118 file_
.Set(CreateFileA(device_info
.device_id
.c_str(),
123 FILE_FLAG_OVERLAPPED
,
128 HidConnectionWin::~HidConnectionWin() {
131 void HidConnectionWin::PlatformClose() {
132 CancelIo(file_
.Get());
135 void HidConnectionWin::PlatformRead(
136 const HidConnection::ReadCallback
& callback
) {
137 // Windows will always include the report ID (including zero if report IDs
138 // are not in use) in the buffer.
139 scoped_refptr
<net::IOBufferWithSize
> buffer
= new net::IOBufferWithSize(
140 base::checked_cast
<int>(device_info().max_input_report_size
+ 1));
141 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
143 base::Bind(&HidConnectionWin::OnReadComplete
, this, buffer
, callback
)));
144 transfers_
.insert(transfer
);
145 transfer
->TakeResultFromWindowsAPI(
146 ReadFile(file_
.Get(),
148 static_cast<DWORD
>(buffer
->size()),
150 transfer
->GetOverlapped()));
153 void HidConnectionWin::PlatformWrite(scoped_refptr
<net::IOBuffer
> buffer
,
155 const WriteCallback
& callback
) {
156 // The Windows API always wants either a report ID (if supported) or
157 // zero at the front of every output report.
158 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
159 buffer
, base::Bind(&HidConnectionWin::OnWriteComplete
, this, callback
)));
160 transfers_
.insert(transfer
);
161 transfer
->TakeResultFromWindowsAPI(WriteFile(file_
.Get(),
163 static_cast<DWORD
>(size
),
165 transfer
->GetOverlapped()));
168 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id
,
169 const ReadCallback
& callback
) {
170 // The first byte of the destination buffer is the report ID being requested.
171 scoped_refptr
<net::IOBufferWithSize
> buffer
= new net::IOBufferWithSize(
172 base::checked_cast
<int>(device_info().max_feature_report_size
+ 1));
173 buffer
->data()[0] = report_id
;
175 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
178 &HidConnectionWin::OnReadFeatureComplete
, this, buffer
, callback
)));
179 transfers_
.insert(transfer
);
180 transfer
->TakeResultFromWindowsAPI(
181 DeviceIoControl(file_
.Get(),
182 IOCTL_HID_GET_FEATURE
,
186 static_cast<DWORD
>(buffer
->size()),
188 transfer
->GetOverlapped()));
191 void HidConnectionWin::PlatformSendFeatureReport(
192 scoped_refptr
<net::IOBuffer
> buffer
,
194 const WriteCallback
& callback
) {
195 // The Windows API always wants either a report ID (if supported) or
196 // zero at the front of every output report.
197 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
198 buffer
, base::Bind(&HidConnectionWin::OnWriteComplete
, this, callback
)));
199 transfer
->TakeResultFromWindowsAPI(
200 DeviceIoControl(file_
.Get(),
201 IOCTL_HID_SET_FEATURE
,
203 static_cast<DWORD
>(size
),
207 transfer
->GetOverlapped()));
210 void HidConnectionWin::OnReadComplete(scoped_refptr
<net::IOBuffer
> buffer
,
211 const ReadCallback
& callback
,
212 PendingHidTransfer
* transfer
,
215 callback
.Run(false, NULL
, 0);
219 DWORD bytes_transferred
;
220 if (GetOverlappedResult(
221 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
222 CompleteRead(buffer
, bytes_transferred
, callback
);
224 VPLOG(1) << "HID read failed";
225 callback
.Run(false, NULL
, 0);
229 void HidConnectionWin::OnReadFeatureComplete(
230 scoped_refptr
<net::IOBuffer
> buffer
,
231 const ReadCallback
& callback
,
232 PendingHidTransfer
* transfer
,
235 callback
.Run(false, NULL
, 0);
239 DWORD bytes_transferred
;
240 if (GetOverlappedResult(
241 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
242 callback
.Run(true, buffer
, bytes_transferred
);
244 VPLOG(1) << "HID read failed";
245 callback
.Run(false, NULL
, 0);
249 void HidConnectionWin::OnWriteComplete(const WriteCallback
& callback
,
250 PendingHidTransfer
* transfer
,
257 DWORD bytes_transferred
;
258 if (GetOverlappedResult(
259 file_
.Get(), transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
262 VPLOG(1) << "HID write failed";
267 } // namespace device