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