hid_connection_win.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright (c) 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "device/hid/hid_connection_win.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <cstring>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/message_loop/message_loop.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/stl_util.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/win/object_watcher.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/win/scoped_handle.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "device/hid/hid_service.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "device/hid/hid_service_win.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#define INITGUID
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <windows.h>
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <hidclass.h>
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)extern "C" {
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <hidsdi.h>
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <setupapi.h>
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <winioctl.h>
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace device {
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            public base::win::ObjectWatcher::Delegate,
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            public base::MessageLoop::DestructionObserver {
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  PendingHidTransfer(scoped_refptr<HidConnectionWin> connection,
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     scoped_refptr<net::IOBufferWithSize> target_buffer,
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     scoped_refptr<net::IOBufferWithSize> receive_buffer,
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     HidConnection::IOCallback callback);
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  void TakeResultFromWindowsAPI(BOOL result);
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OVERLAPPED* GetOverlapped() { return &overlapped_; }
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Implements base::win::ObjectWatcher::Delegate.
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Implements base::MessageLoop::DestructionObserver
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<HidConnectionWin> connection_;
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> target_buffer_;
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> receive_buffer_;
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  HidConnection::IOCallback callback_;
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OVERLAPPED overlapped_;
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::win::ScopedHandle event_;
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::win::ObjectWatcher watcher_;
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  friend class base::RefCounted<PendingHidTransfer>;
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~PendingHidTransfer();
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)PendingHidTransfer::PendingHidTransfer(
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<HidConnectionWin> connection,
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<net::IOBufferWithSize> target_buffer,
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<net::IOBufferWithSize> receive_buffer,
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HidConnection::IOCallback callback)
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : connection_(connection),
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      target_buffer_(target_buffer),
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      receive_buffer_(receive_buffer),
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      callback_(callback),
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  memset(&overlapped_, 0, sizeof(OVERLAPPED));
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  overlapped_.hEvent = event_.Get();
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)PendingHidTransfer::~PendingHidTransfer() {
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::MessageLoop::current()->RemoveDestructionObserver(this);
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (result || GetLastError() != ERROR_IO_PENDING) {
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    connection_->OnTransferFinished(this);
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::MessageLoop::current()->AddDestructionObserver(this);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AddRef();
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    watcher_.StartWatching(event_.Get(), this);
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  connection_->OnTransferFinished(this);
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Release();
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  watcher_.StopWatching();
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  connection_->OnTransferCanceled(this);
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : HidConnection(device_info) {
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  file_.Set(CreateFileA(device_info.device_id.c_str(),
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        GENERIC_WRITE | GENERIC_READ,
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        FILE_SHARE_READ | FILE_SHARE_WRITE,
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        NULL,
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        OPEN_EXISTING,
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        FILE_FLAG_OVERLAPPED,
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        NULL));
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HidConnectionWin::available() const {
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return file_.IsValid();
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)HidConnectionWin::~HidConnectionWin() {
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CancelIo(file_.Get());
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::Read(scoped_refptr<net::IOBufferWithSize> buffer,
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            const HidConnection::IOCallback& callback) {
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_info().input_report_size == 0) {
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The device does not support input reports.
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (buffer->size() < device_info().input_report_size) {
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If the device doesn't support report IDs, the caller should not be
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // expecting one; however, Windows will always expect enough space for one,
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // so we need to use a buffer with one extra byte of space in this case.
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!device_info().has_report_id)
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    receive_buffer = new net::IOBufferWithSize(buffer->size() + 1);
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<PendingHidTransfer> transfer(
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      new PendingHidTransfer(this, buffer, receive_buffer, callback));
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfers_.insert(transfer);
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transfer->TakeResultFromWindowsAPI(
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ReadFile(file_.Get(),
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               receive_buffer->data(),
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               static_cast<DWORD>(receive_buffer->size()),
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               NULL,
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               transfer->GetOverlapped()));
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::Write(uint8_t report_id,
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                             scoped_refptr<net::IOBufferWithSize> buffer,
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             const HidConnection::IOCallback& callback) {
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_info().output_report_size == 0) {
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The device does not support output reports.
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The Windows API always wants either a report ID (if supported) or
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // zero at the front of every output report.
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  output_buffer->data()[0] = report_id;
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<PendingHidTransfer> transfer(
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      new PendingHidTransfer(this, buffer, NULL, callback));
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfers_.insert(transfer);
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfer->TakeResultFromWindowsAPI(
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      WriteFile(file_.Get(),
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                output_buffer->data(),
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                static_cast<DWORD>(output_buffer->size()),
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                NULL,
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                transfer->GetOverlapped()));
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::GetFeatureReport(
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    uint8_t report_id,
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<net::IOBufferWithSize> buffer,
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const IOCallback& callback) {
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_info().feature_report_size == 0) {
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The device does not support feature reports.
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (buffer->size() < device_info().feature_report_size) {
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!device_info().has_report_id)
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    receive_buffer = new net::IOBufferWithSize(buffer->size() + 1);
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The first byte of the destination buffer is the report ID being requested.
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  receive_buffer->data()[0] = report_id;
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<PendingHidTransfer> transfer(
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      new PendingHidTransfer(this, buffer, receive_buffer, callback));
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfers_.insert(transfer);
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfer->TakeResultFromWindowsAPI(
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeviceIoControl(file_.Get(),
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      IOCTL_HID_GET_FEATURE,
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      NULL,
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      0,
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      receive_buffer->data(),
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      static_cast<DWORD>(receive_buffer->size()),
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      NULL,
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      transfer->GetOverlapped()));
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::SendFeatureReport(
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    uint8_t report_id,
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<net::IOBufferWithSize> buffer,
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const IOCallback& callback) {
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_info().feature_report_size == 0) {
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The device does not support feature reports.
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (buffer->size() < device_info().feature_report_size) {
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    callback.Run(false, 0);
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The Windows API always wants either a report ID (if supported) or
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // zero at the front of every output report.
238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  output_buffer->data()[0] = report_id;
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_refptr<PendingHidTransfer> transfer(
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      new PendingHidTransfer(this, buffer, NULL, callback));
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  transfer->TakeResultFromWindowsAPI(
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeviceIoControl(file_.Get(),
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      IOCTL_HID_SET_FEATURE,
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      output_buffer->data(),
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      static_cast<DWORD>(output_buffer->size()),
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      NULL,
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      0,
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      NULL,
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      transfer->GetOverlapped()));
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::OnTransferFinished(
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<PendingHidTransfer> transfer) {
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DWORD bytes_transferred;
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transfers_.erase(transfer);
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (GetOverlappedResult(
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (bytes_transferred == 0)
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      transfer->callback_.Run(true, 0);
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // If this is an input transfer and the receive buffer is not the same as
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // the target buffer, we need to copy the receive buffer into the target
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // buffer, discarding the first byte. This is because the target buffer's
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // owner is not expecting a report ID but Windows will always provide one.
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (transfer->receive_buffer_ &&
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        transfer->receive_buffer_ != transfer->target_buffer_) {
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Move one byte forward.
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      --bytes_transferred;
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      memcpy(transfer->target_buffer_->data(),
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             transfer->receive_buffer_->data() + 1,
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             bytes_transferred);
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    transfer->callback_.Run(true, bytes_transferred);
277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    transfer->callback_.Run(false, 0);
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidConnectionWin::OnTransferCanceled(
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_refptr<PendingHidTransfer> transfer) {
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transfers_.erase(transfer);
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transfer->callback_.Run(false, 0);
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace device
289