hid_writer_win.cc 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. // Copyright 2018 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. #include "device/gamepad/hid_writer_win.h"
  5. #include <Unknwn.h>
  6. #include <WinDef.h>
  7. #include <stdint.h>
  8. #include <windows.h>
  9. namespace device {
  10. HidWriterWin::HidWriterWin(HANDLE device) {
  11. UINT size;
  12. UINT result =
  13. ::GetRawInputDeviceInfo(device, RIDI_DEVICENAME, nullptr, &size);
  14. if (result == 0U) {
  15. std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
  16. result = ::GetRawInputDeviceInfo(device, RIDI_DEVICENAME, name_buffer.get(),
  17. &size);
  18. if (result == size) {
  19. // Open the device handle for asynchronous I/O.
  20. hid_handle_.Set(
  21. ::CreateFile(name_buffer.get(), GENERIC_READ | GENERIC_WRITE,
  22. FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
  23. OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr));
  24. }
  25. }
  26. }
  27. HidWriterWin::~HidWriterWin() = default;
  28. size_t HidWriterWin::WriteOutputReport(base::span<const uint8_t> report) {
  29. DCHECK_GE(report.size_bytes(), 1U);
  30. if (!hid_handle_.IsValid())
  31. return 0;
  32. base::win::ScopedHandle event_handle(
  33. ::CreateEvent(nullptr, false, false, L""));
  34. OVERLAPPED overlapped = {0};
  35. overlapped.hEvent = event_handle.Get();
  36. // Set up an asynchronous write.
  37. DWORD bytes_written = 0;
  38. BOOL write_success =
  39. ::WriteFile(hid_handle_.Get(), report.data(), report.size_bytes(),
  40. &bytes_written, &overlapped);
  41. if (!write_success) {
  42. DWORD error = ::GetLastError();
  43. if (error == ERROR_IO_PENDING) {
  44. // Wait for the write to complete. This causes WriteOutputReport to behave
  45. // synchronously.
  46. DWORD wait_object = ::WaitForSingleObject(overlapped.hEvent, 100);
  47. if (wait_object == WAIT_OBJECT_0) {
  48. ::GetOverlappedResult(hid_handle_.Get(), &overlapped, &bytes_written,
  49. true);
  50. } else {
  51. // Wait failed, or the timeout was exceeded before the write completed.
  52. // Cancel the write request.
  53. if (::CancelIo(hid_handle_.Get())) {
  54. HANDLE handles[2];
  55. handles[0] = hid_handle_.Get();
  56. handles[1] = overlapped.hEvent;
  57. ::WaitForMultipleObjects(2, handles, false, INFINITE);
  58. }
  59. }
  60. }
  61. }
  62. return write_success ? bytes_written : 0;
  63. }
  64. } // namespace device