Electroneum
Loading...
Searching...
No Matches
device_io_hid.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-2019, The Monero Project
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29#if defined(HAVE_HIDAPI)
30
31#include <boost/scope_exit.hpp>
32#include "log.hpp"
33#include "device_io_hid.hpp"
34
35namespace hw {
36 namespace io {
37
38 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
39 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "device.io"
40
41 #define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
42
43 #define MAX_BLOCK 64
44
45 static std::string safe_hid_error(hid_device *hwdev) {
46 if (hwdev) {
47 return std::string((char*)hid_error(hwdev));
48 }
49 return std::string("NULL device");
50 }
51
52 static std::string safe_hid_path(const hid_device_info *hwdev_info) {
53 if (hwdev_info && hwdev_info->path) {
54 return std::string(hwdev_info->path);
55 }
56 return std::string("NULL path");
57 }
58
59 device_io_hid::device_io_hid(unsigned short c, unsigned char t, unsigned int ps, unsigned int to) :
60 channel(c),
61 tag(t),
62 packet_size(ps),
63 timeout(to),
64 usb_vid(0),
65 usb_pid(0),
66 usb_device(NULL) {
67 }
68
69 device_io_hid::device_io_hid() : device_io_hid(DEFAULT_CHANNEL, DEFAULT_TAG, DEFAULT_PACKET_SIZE, DEFAULT_TIMEOUT) {
70 }
71
72 void device_io_hid::io_hid_log(int read, unsigned char* buffer, int block_len) {
73 if (hid_verbose) {
74 char strbuffer[1024];
75 hw::buffer_to_str(strbuffer, sizeof(strbuffer), (char*)buffer, block_len);
76 MDEBUG( "HID " << (read?"<":">") <<" : "<<strbuffer);
77 }
78 }
79
80 void device_io_hid::init() {
81 int r;
82 r = hid_init();
83 ASSERT_X(r>=0, "Unable to init hidapi library. Error "+std::to_string(r)+": "+safe_hid_error(this->usb_device));
84 }
85
86 void device_io_hid::connect(void *params) {
87 hid_conn_params *p = (struct hid_conn_params*)params;
88 if (!this->connect(p->vid, p->pid, p->interface_number, p->usage_page)) {
89 ASSERT_X(false, "No device found");
90 }
91 }
92
93 void device_io_hid::connect(const std::vector<hid_conn_params> &hcpV) {
94 for (auto p: hcpV) {
95 if (this->connect(p.vid, p.pid, p.interface_number, p.usage_page)) {
96 return;
97 }
98 }
99 ASSERT_X(false, "No device found");
100 }
101
102 hid_device_info *device_io_hid::find_device(hid_device_info *devices_list, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
103 bool select_any = !interface_number && !usage_page;
104
105 MDEBUG( "Looking for " <<
106 (select_any ? "any HID Device" : "HID Device with") <<
107 (interface_number ? (" interface_number " + std::to_string(interface_number.value())) : "") <<
108 ((interface_number && usage_page) ? " or" : "") <<
109 (usage_page ? (" usage_page " + std::to_string(usage_page.value())) : ""));
110
111 hid_device_info *result = nullptr;
112 for (; devices_list != nullptr; devices_list = devices_list->next) {
113 BOOST_SCOPE_EXIT(&devices_list, &result) {
114 MDEBUG( (result == devices_list ? "SELECTED" : "SKIPPED ") <<
115 " HID Device" <<
116 " path " << safe_hid_path(devices_list) <<
117 " interface_number " << devices_list->interface_number <<
118 " usage_page " << devices_list->usage_page);
119 }
120 BOOST_SCOPE_EXIT_END
121
122 if (result != nullptr) {
123 continue;
124 }
125
126 if (select_any) {
127 result = devices_list;
128 } else if (interface_number && devices_list->interface_number == interface_number.value()) {
129 result = devices_list;
130 } else if (usage_page && devices_list->usage_page == usage_page.value()) {
131 result = devices_list;
132 }
133 }
134
135 return result;
136 }
137
138 hid_device *device_io_hid::connect(unsigned int vid, unsigned int pid, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
139 hid_device_info *hwdev_info_list;
140 hid_device *hwdev;
141
142 this->disconnect();
143
144 hwdev_info_list = hid_enumerate(vid, pid);
145 if (!hwdev_info_list) {
146 MDEBUG("Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device));
147 return NULL;
148 }
149 hwdev = NULL;
150 if (hid_device_info *device = find_device(hwdev_info_list, interface_number, usage_page)) {
151 hwdev = hid_open_path(device->path);
152 }
153 hid_free_enumeration(hwdev_info_list);
154 ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid));
155 this->usb_vid = vid;
156 this->usb_pid = pid;
157 this->usb_device = hwdev;
158 return hwdev;
159 }
160
161
162 bool device_io_hid::connected() const {
163 return this->usb_device != NULL;
164 }
165
166 int device_io_hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) {
167 unsigned char buffer[400];
168 unsigned char padding_buffer[MAX_BLOCK+1];
169 unsigned int result;
170 int hid_ret;
171 unsigned int sw_offset;
172 unsigned int remaining;
173 unsigned int offset = 0;
174
175 ASSERT_X(this->usb_device,"No device opened");
176
177 //Split command in several HID packet
178 memset(buffer, 0, sizeof(buffer));
179 result = this->wrapCommand(command, cmd_len, buffer, sizeof(buffer));
180 remaining = result;
181
182 while (remaining > 0) {
183 int block_size = (remaining > MAX_BLOCK ? MAX_BLOCK : remaining);
184 memset(padding_buffer, 0, sizeof(padding_buffer));
185 memcpy(padding_buffer+1, buffer + offset, block_size);
186 io_hid_log(0, padding_buffer, block_size+1);
187 hid_ret = hid_write(this->usb_device, padding_buffer, block_size+1);
188 ASSERT_X(hid_ret>=0, "Unable to send hidapi command. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
189 offset += block_size;
190 remaining -= block_size;
191 }
192
193 //get first response
194 memset(buffer, 0, sizeof(buffer));
195 if (!user_input) {
196 hid_ret = hid_read_timeout(this->usb_device, buffer, MAX_BLOCK, this->timeout);
197 } else {
198 hid_ret = hid_read(this->usb_device, buffer, MAX_BLOCK);
199 }
200 ASSERT_X(hid_ret>=0, "Unable to read hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
201 result = (unsigned int)hid_ret;
202 io_hid_log(1, buffer, result);
203 offset = MAX_BLOCK;
204 //parse first response and get others if any
205 for (;;) {
206 result = this->unwrapReponse(buffer, offset, response, max_resp_len);
207 if (result != 0) {
208 break;
209 }
210 hid_ret = hid_read_timeout(this->usb_device, buffer + offset, MAX_BLOCK, this->timeout);
211 ASSERT_X(hid_ret>=0, "Unable to receive hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
212 result = (unsigned int)hid_ret;
213 io_hid_log(1, buffer + offset, result);
214 offset += MAX_BLOCK;
215 }
216 return result;
217 }
218
219 void device_io_hid::disconnect(void) {
220 if (this->usb_device) {
221 hid_close(this->usb_device);
222 }
223 this->usb_vid = 0;
224 this->usb_pid = 0;
225 this->usb_device = NULL;
226 }
227
228 void device_io_hid::release() {
229 /* Do not exit, as the lib context is global*/
230 //hid_exit();
231 }
232
233 unsigned int device_io_hid::wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len) {
234 unsigned int sequence_idx = 0;
235 unsigned int offset = 0;
236 unsigned int offset_out = 0;
237 unsigned int block_size;
238
239 ASSERT_X(this->packet_size >= 3, "Invalid Packet size: "+std::to_string(this->packet_size)) ;
240 ASSERT_X(out_len >= 7, "out_len too short: "+std::to_string(out_len));
241
242 out_len -= 7;
243 out[offset_out++] = ((this->channel >> 8) & 0xff);
244 out[offset_out++] = (this->channel & 0xff);
245 out[offset_out++] = this->tag;
246 out[offset_out++] = ((sequence_idx >> 8) & 0xff);
247 out[offset_out++] = (sequence_idx & 0xff);
248 sequence_idx++;
249 out[offset_out++] = ((command_len >> 8) & 0xff);
250 out[offset_out++] = (command_len & 0xff);
251 block_size = (command_len > this->packet_size - 7 ? this->packet_size - 7 : command_len);
252 ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len));
253 out_len -= block_size;
254 memcpy(out + offset_out, command + offset, block_size);
255 offset_out += block_size;
256 offset += block_size;
257 while (offset != command_len) {
258 ASSERT_X(out_len >= 5, "out_len too short: "+std::to_string(out_len));
259 out_len -= 5;
260 out[offset_out++] = ((this->channel >> 8) & 0xff);
261 out[offset_out++] = (this->channel & 0xff);
262 out[offset_out++] = this->tag;
263 out[offset_out++] = ((sequence_idx >> 8) & 0xff);
264 out[offset_out++] = (sequence_idx & 0xff);
265 sequence_idx++;
266 block_size = ((command_len - offset) > this->packet_size - 5 ? this->packet_size - 5 : command_len - offset);
267 ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len));
268 out_len -= block_size;
269 memcpy(out + offset_out, command + offset, block_size);
270 offset_out += block_size;
271 offset += block_size;
272 }
273 while ((offset_out % this->packet_size) != 0) {
274 ASSERT_X(out_len >= 1, "out_len too short: "+std::to_string(out_len));
275 out_len--;
276 out[offset_out++] = 0;
277 }
278 return offset_out;
279 }
280
281 /*
282 * return 0 if more data are needed
283 * >0 if response is fully available
284 */
285 unsigned int device_io_hid::unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len) {
286 unsigned int sequence_idx = 0;
287 unsigned int offset = 0;
288 unsigned int offset_out = 0;
289 unsigned int response_len;
290 unsigned int block_size;
291 unsigned int val;
292
293 //end?
294 if ((data == NULL) || (data_len < 7 + 5)) {
295 return 0;
296 }
297
298 //check hid header
299 val = (data[offset]<<8) + data[offset+1];
300 offset += 2;
301 ASSERT_X(val == this->channel, "Wrong Channel");
302 val = data[offset];
303 offset++;
304 ASSERT_X(val == this->tag, "Wrong TAG");
305 val = (data[offset]<<8) + data[offset+1];
306 offset += 2;
307 ASSERT_X(val == sequence_idx, "Wrong sequence_idx");
308
309 //fetch
310 response_len = (data[offset++] << 8);
311 response_len |= data[offset++];
312 ASSERT_X(out_len >= response_len, "Out Buffer too short");
313 if (data_len < (7 + response_len)) {
314 return 0;
315 }
316 block_size = (response_len > (this->packet_size - 7) ? this->packet_size - 7 : response_len);
317 memcpy(out + offset_out, data + offset, block_size);
318 offset += block_size;
319 offset_out += block_size;
320 while (offset_out != response_len) {
321 sequence_idx++;
322 if (offset == data_len) {
323 return 0;
324 }
325 val = (data[offset]<<8) + data[offset+1];
326 offset += 2;
327 ASSERT_X(val == this->channel, "Wrong Channel");
328 val = data[offset];
329 offset++;
330 ASSERT_X(val == this->tag, "Wrong TAG");
331 val = (data[offset]<<8) + data[offset+1];
332 offset += 2;
333 ASSERT_X(val == sequence_idx, "Wrong sequence_idx");
334
335 block_size = ((response_len - offset_out) > this->packet_size - 5 ? this->packet_size - 5 : response_len - offset_out);
336 if (block_size > (data_len - offset)) {
337 return 0;
338 }
339 memcpy(out + offset_out, data + offset, block_size);
340 offset += block_size;
341 offset_out += block_size;
342 }
343 return offset_out;
344 }
345
346
347 }
348}
349
350#endif //#if defined(HAVE_HIDAPI)
void * memcpy(void *a, const void *b, size_t c)
#define MDEBUG(x)
Definition misc_log_ex.h:76
Definition device.cpp:38
void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len)
Definition log.cpp:38