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/win/object_watcher.h"
28 struct PendingHidTransfer
: public base::RefCounted
<PendingHidTransfer
>,
29 public base::win::ObjectWatcher::Delegate
,
30 public base::MessageLoop::DestructionObserver
{
31 typedef base::Callback
<void(PendingHidTransfer
*, bool)> Callback
;
33 PendingHidTransfer(scoped_refptr
<net::IOBuffer
> buffer
,
34 const Callback
& callback
);
36 void TakeResultFromWindowsAPI(BOOL result
);
38 OVERLAPPED
* GetOverlapped() { return &overlapped_
; }
40 // Implements base::win::ObjectWatcher::Delegate.
41 virtual void OnObjectSignaled(HANDLE object
) OVERRIDE
;
43 // Implements base::MessageLoop::DestructionObserver
44 virtual void WillDestroyCurrentMessageLoop() OVERRIDE
;
46 // The buffer isn't used by this object but it's important that a reference
47 // to it is held until the transfer completes.
48 scoped_refptr
<net::IOBuffer
> buffer_
;
50 OVERLAPPED overlapped_
;
51 base::win::ScopedHandle event_
;
52 base::win::ObjectWatcher watcher_
;
55 friend class base::RefCounted
<PendingHidTransfer
>;
57 virtual ~PendingHidTransfer();
59 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer
);
62 PendingHidTransfer::PendingHidTransfer(
63 scoped_refptr
<net::IOBuffer
> buffer
,
64 const PendingHidTransfer::Callback
& callback
)
67 event_(CreateEvent(NULL
, FALSE
, FALSE
, NULL
)) {
68 memset(&overlapped_
, 0, sizeof(OVERLAPPED
));
69 overlapped_
.hEvent
= event_
.Get();
72 PendingHidTransfer::~PendingHidTransfer() {
73 base::MessageLoop::current()->RemoveDestructionObserver(this);
76 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result
) {
78 callback_
.Run(this, true);
79 } else if (GetLastError() == ERROR_IO_PENDING
) {
80 base::MessageLoop::current()->AddDestructionObserver(this);
82 watcher_
.StartWatching(event_
.Get(), this);
84 VPLOG(1) << "HID transfer failed";
85 callback_
.Run(this, false);
89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle
) {
90 callback_
.Run(this, true);
94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
95 watcher_
.StopWatching();
96 callback_
.Run(this, false);
99 HidConnectionWin::HidConnectionWin(const HidDeviceInfo
& device_info
)
100 : HidConnection(device_info
) {
101 file_
.Set(CreateFileA(device_info
.device_id
.c_str(),
102 GENERIC_WRITE
| GENERIC_READ
,
103 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
106 FILE_FLAG_OVERLAPPED
,
109 if (!file_
.IsValid() &&
110 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED
) {
111 file_
.Set(CreateFileA(device_info
.device_id
.c_str(),
116 FILE_FLAG_OVERLAPPED
,
121 HidConnectionWin::~HidConnectionWin() {
122 CancelIo(file_
.Get());
125 void HidConnectionWin::PlatformRead(
126 const HidConnection::ReadCallback
& callback
) {
127 // Windows will always include the report ID (including zero if report IDs
128 // are not in use) in the buffer.
129 scoped_refptr
<net::IOBufferWithSize
> buffer
=
130 new net::IOBufferWithSize(device_info().max_input_report_size
+ 1);
131 scoped_refptr
<PendingHidTransfer
> transfer(new PendingHidTransfer(
133 base::Bind(&HidConnectionWin::OnReadComplete
, this, buffer
, callback
)));
134 transfers_
.insert(transfer
);
135 transfer
->TakeResultFromWindowsAPI(
136 ReadFile(file_
.Get(),
138 static_cast<DWORD
>(buffer
->size()),
140 transfer
->GetOverlapped()));
143 void HidConnectionWin::PlatformWrite(scoped_refptr
<net::IOBuffer
> buffer
,
145 const WriteCallback
& callback
) {
146 // The Windows API always wants either a report ID (if supported) or
147 // zero at the front of every output report.
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
=
162 new net::IOBufferWithSize(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_
, transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
212 CompleteRead(buffer
, bytes_transferred
, callback
);
214 VPLOG(1) << "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_
, 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
);
237 VPLOG(1) << "HID read failed";
238 callback
.Run(false, NULL
, 0);
242 void HidConnectionWin::OnWriteComplete(const WriteCallback
& callback
,
243 PendingHidTransfer
* transfer
,
250 DWORD bytes_transferred
;
251 if (GetOverlappedResult(
252 file_
, transfer
->GetOverlapped(), &bytes_transferred
, FALSE
)) {
255 VPLOG(1) << "HID write failed";
260 } // namespace device