| 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 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/files/file.h" | 11 #include "base/files/file.h" |
| 12 #include "base/macros.h" | 12 #include "base/macros.h" |
| 13 #include "base/memory/ptr_util.h" |
| 13 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
| 14 #include "base/numerics/safe_conversions.h" | 15 #include "base/numerics/safe_conversions.h" |
| 15 #include "base/win/object_watcher.h" | 16 #include "base/win/object_watcher.h" |
| 16 #include "components/device_event_log/device_event_log.h" | 17 #include "components/device_event_log/device_event_log.h" |
| 17 | 18 |
| 18 #define INITGUID | 19 #define INITGUID |
| 19 | 20 |
| 20 #include <windows.h> | 21 #include <windows.h> |
| 21 #include <hidclass.h> | 22 #include <hidclass.h> |
| 22 | 23 |
| 23 extern "C" { | 24 extern "C" { |
| 24 #include <hidsdi.h> | 25 #include <hidsdi.h> |
| 25 } | 26 } |
| 26 | 27 |
| 27 #include <setupapi.h> | 28 #include <setupapi.h> |
| 28 #include <winioctl.h> | 29 #include <winioctl.h> |
| 29 | 30 |
| 30 namespace device { | 31 namespace device { |
| 31 | 32 |
| 32 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, | 33 class PendingHidTransfer : public base::win::ObjectWatcher::Delegate, |
| 33 public base::win::ObjectWatcher::Delegate, | 34 public base::MessageLoop::DestructionObserver { |
| 34 public base::MessageLoop::DestructionObserver { | 35 public: |
| 35 typedef base::Callback<void(PendingHidTransfer*, bool)> Callback; | 36 typedef base::OnceCallback<void(PendingHidTransfer*, bool)> Callback; |
| 36 | 37 |
| 37 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, | 38 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, Callback callback); |
| 38 const Callback& callback); | 39 ~PendingHidTransfer() override; |
| 39 | 40 |
| 40 void TakeResultFromWindowsAPI(BOOL result); | 41 void TakeResultFromWindowsAPI(BOOL result); |
| 41 | 42 |
| 42 OVERLAPPED* GetOverlapped() { return &overlapped_; } | 43 OVERLAPPED* GetOverlapped() { return &overlapped_; } |
| 43 | 44 |
| 44 // Implements base::win::ObjectWatcher::Delegate. | 45 // Implements base::win::ObjectWatcher::Delegate. |
| 45 void OnObjectSignaled(HANDLE object) override; | 46 void OnObjectSignaled(HANDLE object) override; |
| 46 | 47 |
| 47 // Implements base::MessageLoop::DestructionObserver | 48 // Implements base::MessageLoop::DestructionObserver |
| 48 void WillDestroyCurrentMessageLoop() override; | 49 void WillDestroyCurrentMessageLoop() override; |
| 49 | 50 |
| 51 private: |
| 50 // The buffer isn't used by this object but it's important that a reference | 52 // The buffer isn't used by this object but it's important that a reference |
| 51 // to it is held until the transfer completes. | 53 // to it is held until the transfer completes. |
| 52 scoped_refptr<net::IOBuffer> buffer_; | 54 scoped_refptr<net::IOBuffer> buffer_; |
| 53 Callback callback_; | 55 Callback callback_; |
| 54 OVERLAPPED overlapped_; | 56 OVERLAPPED overlapped_; |
| 55 base::win::ScopedHandle event_; | 57 base::win::ScopedHandle event_; |
| 56 base::win::ObjectWatcher watcher_; | 58 base::win::ObjectWatcher watcher_; |
| 57 | 59 |
| 58 private: | |
| 59 friend class base::RefCounted<PendingHidTransfer>; | |
| 60 | |
| 61 ~PendingHidTransfer() override; | |
| 62 | |
| 63 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); | 60 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); |
| 64 }; | 61 }; |
| 65 | 62 |
| 66 PendingHidTransfer::PendingHidTransfer( | 63 PendingHidTransfer::PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, |
| 67 scoped_refptr<net::IOBuffer> buffer, | 64 PendingHidTransfer::Callback callback) |
| 68 const PendingHidTransfer::Callback& callback) | |
| 69 : buffer_(buffer), | 65 : buffer_(buffer), |
| 70 callback_(callback), | 66 callback_(std::move(callback)), |
| 71 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { | 67 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { |
| 72 memset(&overlapped_, 0, sizeof(OVERLAPPED)); | 68 memset(&overlapped_, 0, sizeof(OVERLAPPED)); |
| 73 overlapped_.hEvent = event_.Get(); | 69 overlapped_.hEvent = event_.Get(); |
| 74 } | 70 } |
| 75 | 71 |
| 76 PendingHidTransfer::~PendingHidTransfer() { | 72 PendingHidTransfer::~PendingHidTransfer() { |
| 77 base::MessageLoop::current()->RemoveDestructionObserver(this); | 73 base::MessageLoop::current()->RemoveDestructionObserver(this); |
| 74 if (callback_) |
| 75 std::move(callback_).Run(this, false); |
| 78 } | 76 } |
| 79 | 77 |
| 80 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { | 78 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { |
| 81 if (result) { | 79 if (result) { |
| 82 callback_.Run(this, true); | 80 std::move(callback_).Run(this, true); |
| 83 } else if (GetLastError() == ERROR_IO_PENDING) { | 81 } else if (GetLastError() == ERROR_IO_PENDING) { |
| 84 base::MessageLoop::current()->AddDestructionObserver(this); | 82 base::MessageLoop::current()->AddDestructionObserver(this); |
| 85 AddRef(); | |
| 86 watcher_.StartWatchingOnce(event_.Get(), this); | 83 watcher_.StartWatchingOnce(event_.Get(), this); |
| 87 } else { | 84 } else { |
| 88 HID_PLOG(EVENT) << "HID transfer failed"; | 85 HID_PLOG(EVENT) << "HID transfer failed"; |
| 89 callback_.Run(this, false); | 86 std::move(callback_).Run(this, false); |
| 90 } | 87 } |
| 91 } | 88 } |
| 92 | 89 |
| 93 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { | 90 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { |
| 94 callback_.Run(this, true); | 91 std::move(callback_).Run(this, true); |
| 95 Release(); | |
| 96 } | 92 } |
| 97 | 93 |
| 98 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { | 94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { |
| 99 watcher_.StopWatching(); | 95 watcher_.StopWatching(); |
| 100 callback_.Run(this, false); | 96 std::move(callback_).Run(this, false); |
| 101 } | 97 } |
| 102 | 98 |
| 103 HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info, | 99 HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info, |
| 104 base::win::ScopedHandle file) | 100 base::win::ScopedHandle file) |
| 105 : HidConnection(device_info) { | 101 : HidConnection(device_info), file_(std::move(file)) {} |
| 106 file_ = std::move(file); | |
| 107 } | |
| 108 | 102 |
| 109 HidConnectionWin::~HidConnectionWin() { | 103 HidConnectionWin::~HidConnectionWin() { |
| 104 DCHECK(!file_.IsValid()); |
| 105 DCHECK(transfers_.empty()); |
| 110 } | 106 } |
| 111 | 107 |
| 112 void HidConnectionWin::PlatformClose() { | 108 void HidConnectionWin::PlatformClose() { |
| 113 CancelIo(file_.Get()); | 109 CancelIo(file_.Get()); |
| 110 file_.Close(); |
| 111 transfers_.clear(); |
| 114 } | 112 } |
| 115 | 113 |
| 116 void HidConnectionWin::PlatformRead( | 114 void HidConnectionWin::PlatformRead( |
| 117 const HidConnection::ReadCallback& callback) { | 115 const HidConnection::ReadCallback& callback) { |
| 118 // Windows will always include the report ID (including zero if report IDs | 116 // Windows will always include the report ID (including zero if report IDs |
| 119 // are not in use) in the buffer. | 117 // are not in use) in the buffer. |
| 120 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( | 118 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( |
| 121 base::checked_cast<int>(device_info()->max_input_report_size() + 1)); | 119 base::checked_cast<int>(device_info()->max_input_report_size() + 1)); |
| 122 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( | 120 transfers_.push_back(base::MakeUnique<PendingHidTransfer>( |
| 123 buffer, | 121 buffer, base::BindOnce(&HidConnectionWin::OnReadComplete, this, buffer, |
| 124 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback))); | 122 callback))); |
| 125 transfers_.insert(transfer); | 123 transfers_.back()->TakeResultFromWindowsAPI( |
| 126 transfer->TakeResultFromWindowsAPI( | 124 ReadFile(file_.Get(), buffer->data(), static_cast<DWORD>(buffer->size()), |
| 127 ReadFile(file_.Get(), | 125 NULL, transfers_.back()->GetOverlapped())); |
| 128 buffer->data(), | |
| 129 static_cast<DWORD>(buffer->size()), | |
| 130 NULL, | |
| 131 transfer->GetOverlapped())); | |
| 132 } | 126 } |
| 133 | 127 |
| 134 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, | 128 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, |
| 135 size_t size, | 129 size_t size, |
| 136 const WriteCallback& callback) { | 130 const WriteCallback& callback) { |
| 137 size_t expected_size = device_info()->max_output_report_size() + 1; | 131 size_t expected_size = device_info()->max_output_report_size() + 1; |
| 138 DCHECK(size <= expected_size); | 132 DCHECK(size <= expected_size); |
| 139 // The Windows API always wants either a report ID (if supported) or zero at | 133 // The Windows API always wants either a report ID (if supported) or zero at |
| 140 // the front of every output report and requires that the buffer size be equal | 134 // the front of every output report and requires that the buffer size be equal |
| 141 // to the maximum output report size supported by this collection. | 135 // to the maximum output report size supported by this collection. |
| 142 if (size < expected_size) { | 136 if (size < expected_size) { |
| 143 scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer( | 137 scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer( |
| 144 base::checked_cast<int>(expected_size)); | 138 base::checked_cast<int>(expected_size)); |
| 145 memcpy(tmp_buffer->data(), buffer->data(), size); | 139 memcpy(tmp_buffer->data(), buffer->data(), size); |
| 146 memset(tmp_buffer->data() + size, 0, expected_size - size); | 140 memset(tmp_buffer->data() + size, 0, expected_size - size); |
| 147 buffer = tmp_buffer; | 141 buffer = tmp_buffer; |
| 148 size = expected_size; | 142 size = expected_size; |
| 149 } | 143 } |
| 150 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( | 144 transfers_.push_back(base::MakeUnique<PendingHidTransfer>( |
| 151 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); | 145 buffer, |
| 152 transfers_.insert(transfer); | 146 base::BindOnce(&HidConnectionWin::OnWriteComplete, this, callback))); |
| 153 transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(), | 147 transfers_.back()->TakeResultFromWindowsAPI( |
| 154 buffer->data(), | 148 WriteFile(file_.Get(), buffer->data(), static_cast<DWORD>(size), NULL, |
| 155 static_cast<DWORD>(size), | 149 transfers_.back()->GetOverlapped())); |
| 156 NULL, | |
| 157 transfer->GetOverlapped())); | |
| 158 } | 150 } |
| 159 | 151 |
| 160 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id, | 152 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id, |
| 161 const ReadCallback& callback) { | 153 const ReadCallback& callback) { |
| 162 // The first byte of the destination buffer is the report ID being requested. | 154 // The first byte of the destination buffer is the report ID being requested. |
| 163 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( | 155 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( |
| 164 base::checked_cast<int>(device_info()->max_feature_report_size() + 1)); | 156 base::checked_cast<int>(device_info()->max_feature_report_size() + 1)); |
| 165 buffer->data()[0] = report_id; | 157 buffer->data()[0] = report_id; |
| 166 | 158 |
| 167 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( | 159 transfers_.push_back(base::MakeUnique<PendingHidTransfer>( |
| 168 buffer, | 160 buffer, base::BindOnce(&HidConnectionWin::OnReadFeatureComplete, this, |
| 169 base::Bind( | 161 buffer, callback))); |
| 170 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback))); | 162 transfers_.back()->TakeResultFromWindowsAPI( |
| 171 transfers_.insert(transfer); | 163 DeviceIoControl(file_.Get(), IOCTL_HID_GET_FEATURE, NULL, 0, |
| 172 transfer->TakeResultFromWindowsAPI( | 164 buffer->data(), static_cast<DWORD>(buffer->size()), NULL, |
| 173 DeviceIoControl(file_.Get(), | 165 transfers_.back()->GetOverlapped())); |
| 174 IOCTL_HID_GET_FEATURE, | |
| 175 NULL, | |
| 176 0, | |
| 177 buffer->data(), | |
| 178 static_cast<DWORD>(buffer->size()), | |
| 179 NULL, | |
| 180 transfer->GetOverlapped())); | |
| 181 } | 166 } |
| 182 | 167 |
| 183 void HidConnectionWin::PlatformSendFeatureReport( | 168 void HidConnectionWin::PlatformSendFeatureReport( |
| 184 scoped_refptr<net::IOBuffer> buffer, | 169 scoped_refptr<net::IOBuffer> buffer, |
| 185 size_t size, | 170 size_t size, |
| 186 const WriteCallback& callback) { | 171 const WriteCallback& callback) { |
| 187 // The Windows API always wants either a report ID (if supported) or | 172 // The Windows API always wants either a report ID (if supported) or |
| 188 // zero at the front of every output report. | 173 // zero at the front of every output report. |
| 189 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( | 174 transfers_.push_back(base::MakeUnique<PendingHidTransfer>( |
| 190 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); | 175 buffer, |
| 191 transfer->TakeResultFromWindowsAPI( | 176 base::BindOnce(&HidConnectionWin::OnWriteComplete, this, callback))); |
| 192 DeviceIoControl(file_.Get(), | 177 transfers_.back()->TakeResultFromWindowsAPI( |
| 193 IOCTL_HID_SET_FEATURE, | 178 DeviceIoControl(file_.Get(), IOCTL_HID_SET_FEATURE, buffer->data(), |
| 194 buffer->data(), | 179 static_cast<DWORD>(size), NULL, 0, NULL, |
| 195 static_cast<DWORD>(size), | 180 transfers_.back()->GetOverlapped())); |
| 196 NULL, | |
| 197 0, | |
| 198 NULL, | |
| 199 transfer->GetOverlapped())); | |
| 200 } | 181 } |
| 201 | 182 |
| 202 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer, | 183 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer, |
| 203 const ReadCallback& callback, | 184 const ReadCallback& callback, |
| 204 PendingHidTransfer* transfer, | 185 PendingHidTransfer* transfer_raw, |
| 205 bool signaled) { | 186 bool signaled) { |
| 206 if (!signaled) { | 187 if (!file_.IsValid()) { |
| 207 callback.Run(false, NULL, 0); | 188 callback.Run(false, nullptr, 0); |
| 208 return; | 189 return; |
| 209 } | 190 } |
| 210 | 191 |
| 192 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw); |
| 211 DWORD bytes_transferred; | 193 DWORD bytes_transferred; |
| 212 if (GetOverlappedResult( | 194 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(), |
| 213 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { | 195 &bytes_transferred, FALSE)) { |
| 214 CompleteRead(buffer, bytes_transferred, callback); | 196 CompleteRead(buffer, bytes_transferred, callback); |
| 215 } else { | 197 } else { |
| 216 HID_PLOG(EVENT) << "HID read failed"; | 198 HID_PLOG(EVENT) << "HID read failed"; |
| 217 callback.Run(false, NULL, 0); | 199 callback.Run(false, nullptr, 0); |
| 218 } | 200 } |
| 219 } | 201 } |
| 220 | 202 |
| 221 void HidConnectionWin::OnReadFeatureComplete( | 203 void HidConnectionWin::OnReadFeatureComplete( |
| 222 scoped_refptr<net::IOBuffer> buffer, | 204 scoped_refptr<net::IOBuffer> buffer, |
| 223 const ReadCallback& callback, | 205 const ReadCallback& callback, |
| 224 PendingHidTransfer* transfer, | 206 PendingHidTransfer* transfer_raw, |
| 225 bool signaled) { | 207 bool signaled) { |
| 226 if (!signaled) { | 208 if (!file_.IsValid()) { |
| 227 callback.Run(false, NULL, 0); | 209 callback.Run(false, nullptr, 0); |
| 228 return; | 210 return; |
| 229 } | 211 } |
| 230 | 212 |
| 213 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw); |
| 231 DWORD bytes_transferred; | 214 DWORD bytes_transferred; |
| 232 if (GetOverlappedResult( | 215 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(), |
| 233 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { | 216 &bytes_transferred, FALSE)) { |
| 234 callback.Run(true, buffer, bytes_transferred); | 217 callback.Run(true, buffer, bytes_transferred); |
| 235 } else { | 218 } else { |
| 236 HID_PLOG(EVENT) << "HID read failed"; | 219 HID_PLOG(EVENT) << "HID read failed"; |
| 237 callback.Run(false, NULL, 0); | 220 callback.Run(false, nullptr, 0); |
| 238 } | 221 } |
| 239 } | 222 } |
| 240 | 223 |
| 241 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback, | 224 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback, |
| 242 PendingHidTransfer* transfer, | 225 PendingHidTransfer* transfer_raw, |
| 243 bool signaled) { | 226 bool signaled) { |
| 244 if (!signaled) { | 227 if (!file_.IsValid()) { |
| 245 callback.Run(false); | 228 callback.Run(false); |
| 246 return; | 229 return; |
| 247 } | 230 } |
| 248 | 231 |
| 232 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw); |
| 249 DWORD bytes_transferred; | 233 DWORD bytes_transferred; |
| 250 if (GetOverlappedResult( | 234 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(), |
| 251 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { | 235 &bytes_transferred, FALSE)) { |
| 252 callback.Run(true); | 236 callback.Run(true); |
| 253 } else { | 237 } else { |
| 254 HID_PLOG(EVENT) << "HID write failed"; | 238 HID_PLOG(EVENT) << "HID write failed"; |
| 255 callback.Run(false); | 239 callback.Run(false); |
| 256 } | 240 } |
| 257 } | 241 } |
| 258 | 242 |
| 243 std::unique_ptr<PendingHidTransfer> HidConnectionWin::UnlinkTransfer( |
| 244 PendingHidTransfer* transfer) { |
| 245 auto it = std::find_if( |
| 246 transfers_.begin(), transfers_.end(), |
| 247 [transfer](const std::unique_ptr<PendingHidTransfer>& this_transfer) { |
| 248 return transfer == this_transfer.get(); |
| 249 }); |
| 250 DCHECK(it != transfers_.end()); |
| 251 std::unique_ptr<PendingHidTransfer> saved_transfer = std::move(*it); |
| 252 transfers_.erase(it); |
| 253 return saved_transfer; |
| 254 } |
| 255 |
| 259 } // namespace device | 256 } // namespace device |
| OLD | NEW |