1 //-----------------------------------------------------------------------------
2 //
3 // HidControllerWinRT.cpp
4 //
5 // WinRT implementation of a HidController
6 //
7 // Copyright (c) 2015 Microsoft Corporation
8 // All rights reserved.
9 //
10 // SOFTWARE NOTICE AND LICENSE
11 //
12 // This file is part of OpenZWave.
13 //
14 // OpenZWave is free software: you can redistribute it and/or modify
15 // it under the terms of the GNU Lesser General Public License as published
16 // by the Free Software Foundation, either version 3 of the License,
17 // or (at your option) any later version.
18 //
19 // OpenZWave is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU Lesser General Public License for more details.
23 //
24 // You should have received a copy of the GNU Lesser General Public License
25 // along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
26 //
27 //-----------------------------------------------------------------------------
28
29 #include "Defs.h"
30 #include "HidControllerWinRT.h"
31 #include "platform/Log.h"
32
33 #include <ppltasks.h>
34 #include <winstring.h>
35
36 using namespace Windows::Devices::Enumeration;
37 using namespace Windows::Devices::HumanInterfaceDevice;
38 using namespace Windows::Devices::Usb;
39 using namespace Windows::Foundation;
40 using namespace Windows::Storage::Streams;
41 using namespace Platform;
42 using namespace Concurrency;
43 using namespace OpenZWave;
44
45 #define AQS_FORMAT L"System.Devices.InterfaceClassGuid:=\"{4D1E55B2-F16F-11CF-88CB-001111000030}\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True AND System.DeviceInterface.Hid.VendorId:=%04d AND System.DeviceInterface.Hid.ProductId:=%04d"
46 #define AQS_LENGTH 300
47
48 //-----------------------------------------------------------------------------
49 // <HidControllerImpl::HidControllerImpl>
50 // Constructor
51 //-----------------------------------------------------------------------------
HidController()52 HidController::HidController() :
53 m_vendorId(0x1b5f), // Wayne Dalton
54 m_productId(0x01), // ControlThink ThinkStick
55 m_serialNumber(""),
56 m_hidControllerName(""),
57 m_bOpen(false)
58 {
59 }
60
61 //-----------------------------------------------------------------------------
62 // <HidControllerImpl::~HidControllerImpl>
63 // Destructor
64 //-----------------------------------------------------------------------------
~HidController()65 HidController::~HidController()
66 {
67 if (m_device != nullptr)
68 {
69 m_device->InputReportReceived -= m_inputReportEventToken;
70 }
71 }
72
73 //-----------------------------------------------------------------------------
74 // <HidController::SetVendorId>
75 // Set the USB vendor ID search value. The HID port must be closed for the setting to be accepted.
76 //-----------------------------------------------------------------------------
SetVendorId(uint32 const _vendorId)77 bool HidController::SetVendorId
78 (
79 uint32 const _vendorId
80 )
81 {
82 if (m_bOpen)
83 {
84 return false;
85 }
86
87 m_vendorId = _vendorId;
88 return true;
89 }
90
91 //-----------------------------------------------------------------------------
92 // <HidController::SetProductId>
93 // Set the USB product ID search value. The HID port must be closed for the setting to be accepted.
94 //-----------------------------------------------------------------------------
SetProductId(uint32 const _productId)95 bool HidController::SetProductId (uint32 const _productId)
96 {
97 if (m_bOpen)
98 {
99 return false;
100 }
101
102 m_productId = _productId;
103 return true;
104 }
105
106 //-----------------------------------------------------------------------------
107 // <HidController::SetSerialNumber>
108 // Set the USB serial number search value. The HID port must be closed for the setting to be accepted.
109 //-----------------------------------------------------------------------------
SetSerialNumber(string const & _serialNumber)110 bool HidController::SetSerialNumber (string const& _serialNumber)
111 {
112 if (m_bOpen)
113 {
114 return false;
115 }
116
117 m_serialNumber = _serialNumber;
118 return true;
119 }
120
121 //-----------------------------------------------------------------------------
122 // <HidControllerImpl::Open>
123 // Open and configure a HID port
124 //-----------------------------------------------------------------------------
Open(string const & _hidControllerName)125 bool HidController::Open(string const& _hidControllerName)
126 {
127 if (m_bOpen)
128 {
129 return false;
130 }
131
132 m_hidControllerName = _hidControllerName;
133
134 bool success = false;
135 try
136 {
137 create_task(Init()).then([&success, this](bool initResult)
138 {
139 success = initResult;
140 if (success && m_device != nullptr)
141 {
142 m_inputReportEventToken = m_device->InputReportReceived += ref new TypedEventHandler<HidDevice^, HidInputReportReceivedEventArgs^>
143 ([this](HidDevice ^sender, HidInputReportReceivedEventArgs ^args)
144 {
145 auto reader = DataReader::FromBuffer(args->Report->Data);
146 uint32 bufferSize = reader->UnconsumedBufferLength;
147 std::vector<uint8> data(bufferSize);
148
149 if (!data.empty())
150 {
151 reader->ReadBytes(::Platform::ArrayReference<uint8>(&data[0], bufferSize));
152 Put(&data[0], bufferSize);
153 }
154 });
155 }
156
157 }).wait();
158 }
159 catch (Platform::Exception^ ex)
160 {
161 }
162
163 return success;
164 }
165
166 //-----------------------------------------------------------------------------
167 // <HidControllerImpl::Close>
168 // Close a HID port
169 //-----------------------------------------------------------------------------
Close()170 bool HidController::Close()
171 {
172 if (m_device != nullptr)
173 {
174 m_device->InputReportReceived -= m_inputReportEventToken;
175 delete m_device;
176 m_device = nullptr;
177 }
178 return true;
179 }
180
181 //-----------------------------------------------------------------------------
182 // <HidControllerImpl::Init>
183 // Open the HID port
184 //-----------------------------------------------------------------------------
Init()185 task<bool> HidController::Init()
186 {
187 // Yields the same as API above w/o the usage page and usage Id filters
188 wchar_t buffer[AQS_LENGTH];
189 swprintf_s(buffer, AQS_FORMAT, m_vendorId, m_productId);
190 auto selector = ref new String(buffer);
191
192 return create_task(Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(selector))
193 .then([this](DeviceInformationCollection ^ devices) -> String ^
194 {
195 String ^deviceId = L"";
196 for (auto iterator = devices->First(); iterator->HasCurrent; iterator->MoveNext())
197 {
198 // Not sure how to differentiate when there are multiple things returned.
199 // Just return first matching ID for now
200 deviceId = iterator->Current->Id;
201 break;
202 }
203 return deviceId;
204
205 }).then([this](String ^deviceId) -> IAsyncOperation<HidDevice ^> ^
206 {
207 return HidDevice::FromIdAsync(deviceId, Windows::Storage::FileAccessMode::Read);
208
209 }).then([this](task<HidDevice ^> deviceTask) -> bool
210 {
211 try
212 {
213 m_device = deviceTask.get();
214
215 if (m_device == nullptr)
216 {
217 return false;
218 }
219
220 // Send Report ID 2 - 1 byte "0x04"
221 // Enables ZWave packet reports on ID 4 (tx) and ID 5 (rx)
222 uint8 data = 0x04;
223 SendFeatureReport(&data, 1, 2);
224
225 return true;
226 }
227 catch (Platform::Exception^ ex)
228 {
229 return false;
230 }
231 });
232 }
233
234 //-----------------------------------------------------------------------------
235 // <HidControllerImpl::Write>
236 // Send data to the HID port
237 //-----------------------------------------------------------------------------
Write(uint8 * _buffer,uint32 _length)238 uint32 HidController::Write
239 (
240 uint8* _buffer,
241 uint32 _length
242 )
243 {
244 // report Id 0x04 is tx feature report
245 return SendFeatureReport(_buffer, _length, 0x04);
246 }
247
248 //-----------------------------------------------------------------------------
249 // <HidController::SendFeatureReport>
250 // Send a feature report with the specified data and report ID
251 //-----------------------------------------------------------------------------
SendFeatureReport(uint8 * _buffer,uint32 _length,unsigned short reportId)252 uint32 HidController::SendFeatureReport
253 (
254 uint8* _buffer,
255 uint32 _length,
256 unsigned short reportId
257 )
258 {
259 auto featureReport = m_device->CreateFeatureReport();
260 auto dataWriter = ref new DataWriter();
261
262 auto array = ref new Array<uint8>(_buffer, _length);
263 dataWriter->WriteBytes(ArrayReference<uint8>(_buffer, _length));
264
265 featureReport->Data = dataWriter->DetachBuffer();
266
267 uint32 bytesWritten = 0;
268 try
269 {
270 bytesWritten = create_task(m_device->SendFeatureReportAsync(featureReport)).get();
271 }
272 catch (Platform::Exception^)
273 {
274 }
275 return bytesWritten;
276 }
277