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. 4 5 #include "device/hid/hid_connection_win.h" 6 7 #include <cstring> 8 9 #include "base/bind.h" 10 #include "base/files/file.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/win/object_watcher.h" 13 14 #define INITGUID 15 16 #include <windows.h> 17 #include <hidclass.h> 18 19 extern "C" { 20 #include <hidsdi.h> 21 } 22 23 #include <setupapi.h> 24 #include <winioctl.h> 25 26 namespace device { 27 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; 32 33 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, 34 const Callback& callback); 35 36 void TakeResultFromWindowsAPI(BOOL result); 37 38 OVERLAPPED* GetOverlapped() { return &overlapped_; } 39 40 // Implements base::win::ObjectWatcher::Delegate. 41 virtual void OnObjectSignaled(HANDLE object) OVERRIDE; 42 43 // Implements base::MessageLoop::DestructionObserver 44 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; 45 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_; 49 Callback callback_; 50 OVERLAPPED overlapped_; 51 base::win::ScopedHandle event_; 52 base::win::ObjectWatcher watcher_; 53 54 private: 55 friend class base::RefCounted<PendingHidTransfer>; 56 57 virtual ~PendingHidTransfer(); 58 59 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); 60 }; 61 62 PendingHidTransfer::PendingHidTransfer( 63 scoped_refptr<net::IOBuffer> buffer, 64 const PendingHidTransfer::Callback& callback) 65 : buffer_(buffer), 66 callback_(callback), 67 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { 68 memset(&overlapped_, 0, sizeof(OVERLAPPED)); 69 overlapped_.hEvent = event_.Get(); 70 } 71 72 PendingHidTransfer::~PendingHidTransfer() { 73 base::MessageLoop::current()->RemoveDestructionObserver(this); 74 } 75 76 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { 77 if (result) { 78 callback_.Run(this, true); 79 } else if (GetLastError() == ERROR_IO_PENDING) { 80 base::MessageLoop::current()->AddDestructionObserver(this); 81 AddRef(); 82 watcher_.StartWatching(event_.Get(), this); 83 } else { 84 VPLOG(1) << "HID transfer failed"; 85 callback_.Run(this, false); 86 } 87 } 88 89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { 90 callback_.Run(this, true); 91 Release(); 92 } 93 94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { 95 watcher_.StopWatching(); 96 callback_.Run(this, false); 97 } 98 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, 104 NULL, 105 OPEN_EXISTING, 106 FILE_FLAG_OVERLAPPED, 107 NULL)); 108 109 if (!file_.IsValid() && 110 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { 111 file_.Set(CreateFileA(device_info.device_id.c_str(), 112 GENERIC_READ, 113 FILE_SHARE_READ, 114 NULL, 115 OPEN_EXISTING, 116 FILE_FLAG_OVERLAPPED, 117 NULL)); 118 } 119 } 120 121 HidConnectionWin::~HidConnectionWin() { 122 } 123 124 void HidConnectionWin::PlatformClose() { 125 CancelIo(file_.Get()); 126 } 127 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( 135 buffer, 136 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback))); 137 transfers_.insert(transfer); 138 transfer->TakeResultFromWindowsAPI( 139 ReadFile(file_.Get(), 140 buffer->data(), 141 static_cast<DWORD>(buffer->size()), 142 NULL, 143 transfer->GetOverlapped())); 144 } 145 146 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, 147 size_t size, 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(), 155 buffer->data(), 156 static_cast<DWORD>(size), 157 NULL, 158 transfer->GetOverlapped())); 159 } 160 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; 167 168 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( 169 buffer, 170 base::Bind( 171 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback))); 172 transfers_.insert(transfer); 173 transfer->TakeResultFromWindowsAPI( 174 DeviceIoControl(file_.Get(), 175 IOCTL_HID_GET_FEATURE, 176 NULL, 177 0, 178 buffer->data(), 179 static_cast<DWORD>(buffer->size()), 180 NULL, 181 transfer->GetOverlapped())); 182 } 183 184 void HidConnectionWin::PlatformSendFeatureReport( 185 scoped_refptr<net::IOBuffer> buffer, 186 size_t size, 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, 195 buffer->data(), 196 static_cast<DWORD>(size), 197 NULL, 198 0, 199 NULL, 200 transfer->GetOverlapped())); 201 } 202 203 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer, 204 const ReadCallback& callback, 205 PendingHidTransfer* transfer, 206 bool signaled) { 207 if (!signaled) { 208 callback.Run(false, NULL, 0); 209 return; 210 } 211 212 DWORD bytes_transferred; 213 if (GetOverlappedResult( 214 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { 215 CompleteRead(buffer, bytes_transferred, callback); 216 } else { 217 VPLOG(1) << "HID read failed"; 218 callback.Run(false, NULL, 0); 219 } 220 } 221 222 void HidConnectionWin::OnReadFeatureComplete( 223 scoped_refptr<net::IOBuffer> buffer, 224 const ReadCallback& callback, 225 PendingHidTransfer* transfer, 226 bool signaled) { 227 if (!signaled) { 228 callback.Run(false, NULL, 0); 229 return; 230 } 231 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); 239 } else { 240 VPLOG(1) << "HID read failed"; 241 callback.Run(false, NULL, 0); 242 } 243 } 244 245 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback, 246 PendingHidTransfer* transfer, 247 bool signaled) { 248 if (!signaled) { 249 callback.Run(false); 250 return; 251 } 252 253 DWORD bytes_transferred; 254 if (GetOverlappedResult( 255 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { 256 callback.Run(true); 257 } else { 258 VPLOG(1) << "HID write failed"; 259 callback.Run(false); 260 } 261 } 262 263 } // namespace device 264