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() {
124 void HidConnectionWin::PlatformClose() {
125 CancelIo(file_.Get());
128 void HidConnectionWin::PlatformRead(
129 const HidConnection::ReadCallback& callback) {
130 // Windows will always include the report ID (including zero if report IDs
131 // are not in use) in the buffer.
132 scoped_refptr<net::IOBufferWithSize> buffer =
133 new net::IOBufferWithSize(device_info().max_input_report_size + 1);
134 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
136 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback)));
137 transfers_.insert(transfer);
138 transfer->TakeResultFromWindowsAPI(
139 ReadFile(file_.Get(),
141 static_cast<DWORD>(buffer->size()),
143 transfer->GetOverlapped()));
146 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
148 const WriteCallback& callback) {
149 // The Windows API always wants either a report ID (if supported) or
150 // zero at the front of every output report.
151 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
152 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
153 transfers_.insert(transfer);
154 transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(),
156 static_cast<DWORD>(size),
158 transfer->GetOverlapped()));
161 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id,
162 const ReadCallback& callback) {
163 // The first byte of the destination buffer is the report ID being requested.
164 scoped_refptr<net::IOBufferWithSize> buffer =
165 new net::IOBufferWithSize(device_info().max_feature_report_size + 1);
166 buffer->data()[0] = report_id;
168 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
171 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback)));
172 transfers_.insert(transfer);
173 transfer->TakeResultFromWindowsAPI(
174 DeviceIoControl(file_.Get(),
175 IOCTL_HID_GET_FEATURE,
179 static_cast<DWORD>(buffer->size()),
181 transfer->GetOverlapped()));
184 void HidConnectionWin::PlatformSendFeatureReport(
185 scoped_refptr<net::IOBuffer> buffer,
187 const WriteCallback& callback) {
188 // The Windows API always wants either a report ID (if supported) or
189 // zero at the front of every output report.
190 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
191 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
192 transfer->TakeResultFromWindowsAPI(
193 DeviceIoControl(file_.Get(),
194 IOCTL_HID_SET_FEATURE,
196 static_cast<DWORD>(size),
200 transfer->GetOverlapped()));
203 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
204 const ReadCallback& callback,
205 PendingHidTransfer* transfer,
208 callback.Run(false, NULL, 0);
212 DWORD bytes_transferred;
213 if (GetOverlappedResult(
214 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
215 CompleteRead(buffer, bytes_transferred, callback);
217 VPLOG(1) << "HID read failed";
218 callback.Run(false, NULL, 0);
222 void HidConnectionWin::OnReadFeatureComplete(
223 scoped_refptr<net::IOBuffer> buffer,
224 const ReadCallback& callback,
225 PendingHidTransfer* transfer,
228 callback.Run(false, NULL, 0);
232 DWORD bytes_transferred;
233 if (GetOverlappedResult(
234 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
235 scoped_refptr<net::IOBuffer> new_buffer(
236 new net::IOBuffer(bytes_transferred - 1));
237 memcpy(new_buffer->data(), buffer->data() + 1, bytes_transferred - 1);
238 CompleteRead(new_buffer, bytes_transferred, callback);
240 VPLOG(1) << "HID read failed";
241 callback.Run(false, NULL, 0);
245 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback,
246 PendingHidTransfer* transfer,
253 DWORD bytes_transferred;
254 if (GetOverlappedResult(
255 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
258 VPLOG(1) << "HID write failed";
263 } // namespace device