| OLD | NEW |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/hid/hid_connection_win.h" | 5 #include "device/hid/hid_connection_win.h" |
| 6 | 6 |
| 7 #include <cstring> | 7 #include <cstring> |
| 8 | 8 |
| 9 #include "base/bind.h" |
| 9 #include "base/files/file.h" | 10 #include "base/files/file.h" |
| 10 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
| 11 #include "base/win/object_watcher.h" | 12 #include "base/win/object_watcher.h" |
| 12 | 13 |
| 13 #define INITGUID | 14 #define INITGUID |
| 14 | 15 |
| 15 #include <windows.h> | 16 #include <windows.h> |
| 16 #include <hidclass.h> | 17 #include <hidclass.h> |
| 17 | 18 |
| 18 extern "C" { | 19 extern "C" { |
| 19 #include <hidsdi.h> | 20 #include <hidsdi.h> |
| 20 } | 21 } |
| 21 | 22 |
| 22 #include <setupapi.h> | 23 #include <setupapi.h> |
| 23 #include <winioctl.h> | 24 #include <winioctl.h> |
| 24 | 25 |
| 25 namespace device { | 26 namespace device { |
| 26 | 27 |
| 27 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, | 28 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, |
| 28 public base::win::ObjectWatcher::Delegate, | 29 public base::win::ObjectWatcher::Delegate, |
| 29 public base::MessageLoop::DestructionObserver { | 30 public base::MessageLoop::DestructionObserver { |
| 30 PendingHidTransfer(scoped_refptr<HidConnectionWin> connection, | 31 typedef base::Callback<void(PendingHidTransfer*, bool)> Callback; |
| 31 scoped_refptr<net::IOBufferWithSize> target_buffer, | 32 |
| 32 scoped_refptr<net::IOBufferWithSize> receive_buffer, | 33 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, |
| 33 HidConnection::IOCallback callback); | 34 const Callback& callback); |
| 34 | 35 |
| 35 void TakeResultFromWindowsAPI(BOOL result); | 36 void TakeResultFromWindowsAPI(BOOL result); |
| 36 | 37 |
| 37 OVERLAPPED* GetOverlapped() { return &overlapped_; } | 38 OVERLAPPED* GetOverlapped() { return &overlapped_; } |
| 38 | 39 |
| 39 // Implements base::win::ObjectWatcher::Delegate. | 40 // Implements base::win::ObjectWatcher::Delegate. |
| 40 virtual void OnObjectSignaled(HANDLE object) OVERRIDE; | 41 virtual void OnObjectSignaled(HANDLE object) OVERRIDE; |
| 41 | 42 |
| 42 // Implements base::MessageLoop::DestructionObserver | 43 // Implements base::MessageLoop::DestructionObserver |
| 43 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; | 44 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; |
| 44 | 45 |
| 45 scoped_refptr<HidConnectionWin> connection_; | 46 // The buffer isn't used by this object but it's important that a reference |
| 46 scoped_refptr<net::IOBufferWithSize> target_buffer_; | 47 // to it is held until the transfer completes. |
| 47 scoped_refptr<net::IOBufferWithSize> receive_buffer_; | 48 scoped_refptr<net::IOBuffer> buffer_; |
| 48 HidConnection::IOCallback callback_; | 49 Callback callback_; |
| 49 OVERLAPPED overlapped_; | 50 OVERLAPPED overlapped_; |
| 50 base::win::ScopedHandle event_; | 51 base::win::ScopedHandle event_; |
| 51 base::win::ObjectWatcher watcher_; | 52 base::win::ObjectWatcher watcher_; |
| 52 | 53 |
| 53 private: | 54 private: |
| 54 friend class base::RefCounted<PendingHidTransfer>; | 55 friend class base::RefCounted<PendingHidTransfer>; |
| 55 | 56 |
| 56 virtual ~PendingHidTransfer(); | 57 virtual ~PendingHidTransfer(); |
| 57 | 58 |
| 58 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); | 59 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); |
| 59 }; | 60 }; |
| 60 | 61 |
| 61 PendingHidTransfer::PendingHidTransfer( | 62 PendingHidTransfer::PendingHidTransfer( |
| 62 scoped_refptr<HidConnectionWin> connection, | 63 scoped_refptr<net::IOBuffer> buffer, |
| 63 scoped_refptr<net::IOBufferWithSize> target_buffer, | 64 const PendingHidTransfer::Callback& callback) |
| 64 scoped_refptr<net::IOBufferWithSize> receive_buffer, | 65 : buffer_(buffer), |
| 65 HidConnection::IOCallback callback) | |
| 66 : connection_(connection), | |
| 67 target_buffer_(target_buffer), | |
| 68 receive_buffer_(receive_buffer), | |
| 69 callback_(callback), | 66 callback_(callback), |
| 70 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { | 67 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { |
| 71 memset(&overlapped_, 0, sizeof(OVERLAPPED)); | 68 memset(&overlapped_, 0, sizeof(OVERLAPPED)); |
| 72 overlapped_.hEvent = event_.Get(); | 69 overlapped_.hEvent = event_.Get(); |
| 73 } | 70 } |
| 74 | 71 |
| 75 PendingHidTransfer::~PendingHidTransfer() { | 72 PendingHidTransfer::~PendingHidTransfer() { |
| 76 base::MessageLoop::current()->RemoveDestructionObserver(this); | 73 base::MessageLoop::current()->RemoveDestructionObserver(this); |
| 77 } | 74 } |
| 78 | 75 |
| 79 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { | 76 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { |
| 80 if (result || GetLastError() != ERROR_IO_PENDING) { | 77 if (result) { |
| 81 connection_->OnTransferFinished(this); | 78 callback_.Run(this, true); |
| 82 } else { | 79 } else if (GetLastError() == ERROR_IO_PENDING) { |
| 83 base::MessageLoop::current()->AddDestructionObserver(this); | 80 base::MessageLoop::current()->AddDestructionObserver(this); |
| 84 AddRef(); | 81 AddRef(); |
| 85 watcher_.StartWatching(event_.Get(), this); | 82 watcher_.StartWatching(event_.Get(), this); |
| 83 } else { |
| 84 VPLOG(1) << "HID transfer failed"; |
| 85 callback_.Run(this, false); |
| 86 } | 86 } |
| 87 } | 87 } |
| 88 | 88 |
| 89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { | 89 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { |
| 90 connection_->OnTransferFinished(this); | 90 callback_.Run(this, true); |
| 91 Release(); | 91 Release(); |
| 92 } | 92 } |
| 93 | 93 |
| 94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { | 94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { |
| 95 watcher_.StopWatching(); | 95 watcher_.StopWatching(); |
| 96 connection_->OnTransferCanceled(this); | 96 callback_.Run(this, false); |
| 97 } | 97 } |
| 98 | 98 |
| 99 HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info) | 99 HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info) |
| 100 : HidConnection(device_info) { | 100 : HidConnection(device_info) { |
| 101 file_.Set(CreateFileA(device_info.device_id.c_str(), | 101 file_.Set(CreateFileA(device_info.device_id.c_str(), |
| 102 GENERIC_WRITE | GENERIC_READ, | 102 GENERIC_WRITE | GENERIC_READ, |
| 103 FILE_SHARE_READ | FILE_SHARE_WRITE, | 103 FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 104 NULL, | 104 NULL, |
| 105 OPEN_EXISTING, | 105 OPEN_EXISTING, |
| 106 FILE_FLAG_OVERLAPPED, | 106 FILE_FLAG_OVERLAPPED, |
| 107 NULL)); | 107 NULL)); |
| 108 | 108 |
| 109 if (!file_.IsValid() && | 109 if (!file_.IsValid() && |
| 110 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { | 110 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { |
| 111 file_.Set(CreateFileA(device_info.device_id.c_str(), | 111 file_.Set(CreateFileA(device_info.device_id.c_str(), |
| 112 GENERIC_READ, | 112 GENERIC_READ, |
| 113 FILE_SHARE_READ, | 113 FILE_SHARE_READ, |
| 114 NULL, | 114 NULL, |
| 115 OPEN_EXISTING, | 115 OPEN_EXISTING, |
| 116 FILE_FLAG_OVERLAPPED, | 116 FILE_FLAG_OVERLAPPED, |
| 117 NULL)); | 117 NULL)); |
| 118 } | 118 } |
| 119 } | 119 } |
| 120 | 120 |
| 121 HidConnectionWin::~HidConnectionWin() { | 121 HidConnectionWin::~HidConnectionWin() { |
| 122 CancelIo(file_.Get()); | 122 CancelIo(file_.Get()); |
| 123 } | 123 } |
| 124 | 124 |
| 125 void HidConnectionWin::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, | 125 void HidConnectionWin::PlatformRead( |
| 126 const HidConnection::IOCallback& callback) { | 126 const HidConnection::ReadCallback& callback) { |
| 127 // Windows will always include the report ID (including zero if report IDs | 127 // Windows will always include the report ID (including zero if report IDs |
| 128 // are not in use) in the buffer. | 128 // are not in use) in the buffer. |
| 129 scoped_refptr<net::IOBufferWithSize> receive_buffer = | 129 scoped_refptr<net::IOBufferWithSize> buffer = |
| 130 new net::IOBufferWithSize(device_info().max_input_report_size + 1); | 130 new net::IOBufferWithSize(device_info().max_input_report_size + 1); |
| 131 | 131 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( |
| 132 scoped_refptr<PendingHidTransfer> transfer( | 132 buffer, |
| 133 new PendingHidTransfer(this, buffer, receive_buffer, callback)); | 133 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback))); |
| 134 transfers_.insert(transfer); | 134 transfers_.insert(transfer); |
| 135 transfer->TakeResultFromWindowsAPI( | 135 transfer->TakeResultFromWindowsAPI( |
| 136 ReadFile(file_.Get(), | 136 ReadFile(file_.Get(), |
| 137 receive_buffer->data(), | 137 buffer->data(), |
| 138 static_cast<DWORD>(receive_buffer->size()), | 138 static_cast<DWORD>(buffer->size()), |
| 139 NULL, | 139 NULL, |
| 140 transfer->GetOverlapped())); | 140 transfer->GetOverlapped())); |
| 141 } | 141 } |
| 142 | 142 |
| 143 void HidConnectionWin::PlatformWrite( | 143 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, |
| 144 uint8_t report_id, | 144 size_t size, |
| 145 scoped_refptr<net::IOBufferWithSize> buffer, | 145 const WriteCallback& callback) { |
| 146 const HidConnection::IOCallback& callback) { | |
| 147 // The Windows API always wants either a report ID (if supported) or | 146 // The Windows API always wants either a report ID (if supported) or |
| 148 // zero at the front of every output report. | 147 // zero at the front of every output report. |
| 149 scoped_refptr<net::IOBufferWithSize> output_buffer( | 148 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( |
| 150 new net::IOBufferWithSize(buffer->size() + 1)); | 149 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); |
| 151 output_buffer->data()[0] = report_id; | |
| 152 memcpy(output_buffer->data() + 1, buffer->data(), buffer->size()); | |
| 153 | |
| 154 scoped_refptr<PendingHidTransfer> transfer( | |
| 155 new PendingHidTransfer(this, output_buffer, NULL, callback)); | |
| 156 transfers_.insert(transfer); | 150 transfers_.insert(transfer); |
| 157 transfer->TakeResultFromWindowsAPI( | 151 transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(), |
| 158 WriteFile(file_.Get(), | 152 buffer->data(), |
| 159 output_buffer->data(), | 153 static_cast<DWORD>(size), |
| 160 static_cast<DWORD>(output_buffer->size()), | 154 NULL, |
| 161 NULL, | 155 transfer->GetOverlapped())); |
| 162 transfer->GetOverlapped())); | |
| 163 } | 156 } |
| 164 | 157 |
| 165 void HidConnectionWin::PlatformGetFeatureReport( | 158 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id, |
| 166 uint8_t report_id, | 159 const ReadCallback& callback) { |
| 167 scoped_refptr<net::IOBufferWithSize> buffer, | |
| 168 const IOCallback& callback) { | |
| 169 int expected_report_size = device_info().max_feature_report_size; | |
| 170 if (device_info().has_report_id) { | |
| 171 expected_report_size++; | |
| 172 } | |
| 173 scoped_refptr<net::IOBufferWithSize> receive_buffer = | |
| 174 new net::IOBufferWithSize(expected_report_size); | |
| 175 // The first byte of the destination buffer is the report ID being requested. | 160 // The first byte of the destination buffer is the report ID being requested. |
| 176 receive_buffer->data()[0] = report_id; | 161 scoped_refptr<net::IOBufferWithSize> buffer = |
| 162 new net::IOBufferWithSize(device_info().max_feature_report_size + 1); |
| 163 buffer->data()[0] = report_id; |
| 177 | 164 |
| 178 scoped_refptr<PendingHidTransfer> transfer( | 165 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( |
| 179 new PendingHidTransfer(this, buffer, receive_buffer, callback)); | 166 buffer, |
| 167 base::Bind( |
| 168 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback))); |
| 180 transfers_.insert(transfer); | 169 transfers_.insert(transfer); |
| 181 transfer->TakeResultFromWindowsAPI( | 170 transfer->TakeResultFromWindowsAPI( |
| 182 DeviceIoControl(file_.Get(), | 171 DeviceIoControl(file_.Get(), |
| 183 IOCTL_HID_GET_FEATURE, | 172 IOCTL_HID_GET_FEATURE, |
| 184 NULL, | 173 NULL, |
| 185 0, | 174 0, |
| 186 receive_buffer->data(), | 175 buffer->data(), |
| 187 static_cast<DWORD>(receive_buffer->size()), | 176 static_cast<DWORD>(buffer->size()), |
| 188 NULL, | 177 NULL, |
| 189 transfer->GetOverlapped())); | 178 transfer->GetOverlapped())); |
| 190 } | 179 } |
| 191 | 180 |
| 192 void HidConnectionWin::PlatformSendFeatureReport( | 181 void HidConnectionWin::PlatformSendFeatureReport( |
| 193 uint8_t report_id, | 182 scoped_refptr<net::IOBuffer> buffer, |
| 194 scoped_refptr<net::IOBufferWithSize> buffer, | 183 size_t size, |
| 195 const IOCallback& callback) { | 184 const WriteCallback& callback) { |
| 196 // The Windows API always wants either a report ID (if supported) or | 185 // The Windows API always wants either a report ID (if supported) or |
| 197 // zero at the front of every output report. | 186 // zero at the front of every output report. |
| 198 scoped_refptr<net::IOBufferWithSize> output_buffer(buffer); | 187 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( |
| 199 output_buffer = new net::IOBufferWithSize(buffer->size() + 1); | 188 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); |
| 200 output_buffer->data()[0] = report_id; | |
| 201 memcpy(output_buffer->data() + 1, buffer->data(), buffer->size()); | |
| 202 | |
| 203 scoped_refptr<PendingHidTransfer> transfer( | |
| 204 new PendingHidTransfer(this, output_buffer, NULL, callback)); | |
| 205 transfer->TakeResultFromWindowsAPI( | 189 transfer->TakeResultFromWindowsAPI( |
| 206 DeviceIoControl(file_.Get(), | 190 DeviceIoControl(file_.Get(), |
| 207 IOCTL_HID_SET_FEATURE, | 191 IOCTL_HID_SET_FEATURE, |
| 208 output_buffer->data(), | 192 buffer->data(), |
| 209 static_cast<DWORD>(output_buffer->size()), | 193 static_cast<DWORD>(size), |
| 210 NULL, | 194 NULL, |
| 211 0, | 195 0, |
| 212 NULL, | 196 NULL, |
| 213 transfer->GetOverlapped())); | 197 transfer->GetOverlapped())); |
| 214 } | 198 } |
| 215 | 199 |
| 216 void HidConnectionWin::OnTransferFinished( | 200 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer, |
| 217 scoped_refptr<PendingHidTransfer> transfer) { | 201 const ReadCallback& callback, |
| 218 transfers_.erase(transfer); | 202 PendingHidTransfer* transfer, |
| 203 bool signaled) { |
| 204 if (!signaled) { |
| 205 callback.Run(false, NULL, 0); |
| 206 return; |
| 207 } |
| 219 | 208 |
| 220 DWORD bytes_transferred; | 209 DWORD bytes_transferred; |
| 221 if (GetOverlappedResult( | 210 if (GetOverlappedResult( |
| 222 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { | 211 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { |
| 223 if (bytes_transferred == 0) { | 212 CompleteRead(buffer, bytes_transferred, callback); |
| 224 transfer->callback_.Run(true, 0); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 if (transfer->receive_buffer_) { | |
| 229 // If owner HID top-level collection does not have report ID, we need to | |
| 230 // copy the receive buffer into the target buffer, discarding the first | |
| 231 // byte. This is because the target buffer's owner is not expecting a | |
| 232 // report ID but Windows will always provide one. | |
| 233 if (!device_info().has_report_id) { | |
| 234 uint8_t report_id = transfer->receive_buffer_->data()[0]; | |
| 235 // Assert first byte is 0x00 | |
| 236 if (report_id != HidConnection::kNullReportId) { | |
| 237 VLOG(1) << "Unexpected report ID in HID report:" << report_id; | |
| 238 transfer->callback_.Run(false, 0); | |
| 239 } else { | |
| 240 // Move one byte forward. | |
| 241 --bytes_transferred; | |
| 242 memcpy(transfer->target_buffer_->data(), | |
| 243 transfer->receive_buffer_->data() + 1, | |
| 244 bytes_transferred); | |
| 245 } | |
| 246 } else { | |
| 247 memcpy(transfer->target_buffer_->data(), | |
| 248 transfer->receive_buffer_->data(), | |
| 249 bytes_transferred); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 CompleteRead( | |
| 254 transfer->target_buffer_, bytes_transferred, transfer->callback_); | |
| 255 } else { | 213 } else { |
| 256 VPLOG(1) << "HID transfer failed"; | 214 VPLOG(1) << "HID read failed"; |
| 257 transfer->callback_.Run(false, 0); | 215 callback.Run(false, NULL, 0); |
| 258 } | 216 } |
| 259 } | 217 } |
| 260 | 218 |
| 261 void HidConnectionWin::OnTransferCanceled( | 219 void HidConnectionWin::OnReadFeatureComplete( |
| 262 scoped_refptr<PendingHidTransfer> transfer) { | 220 scoped_refptr<net::IOBuffer> buffer, |
| 263 transfers_.erase(transfer); | 221 const ReadCallback& callback, |
| 264 transfer->callback_.Run(false, 0); | 222 PendingHidTransfer* transfer, |
| 223 bool signaled) { |
| 224 if (!signaled) { |
| 225 callback.Run(false, NULL, 0); |
| 226 return; |
| 227 } |
| 228 |
| 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); |
| 236 } else { |
| 237 VPLOG(1) << "HID read failed"; |
| 238 callback.Run(false, NULL, 0); |
| 239 } |
| 240 } |
| 241 |
| 242 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback, |
| 243 PendingHidTransfer* transfer, |
| 244 bool signaled) { |
| 245 if (!signaled) { |
| 246 callback.Run(false); |
| 247 return; |
| 248 } |
| 249 |
| 250 DWORD bytes_transferred; |
| 251 if (GetOverlappedResult( |
| 252 file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { |
| 253 callback.Run(true); |
| 254 } else { |
| 255 VPLOG(1) << "HID write failed"; |
| 256 callback.Run(false); |
| 257 } |
| 265 } | 258 } |
| 266 | 259 |
| 267 } // namespace device | 260 } // namespace device |
| OLD | NEW |