Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / device / hid / hid_connection_win.cc
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