Small compatibility improvements, and added scsi2sd-monitor test program
[SCSI2SD-V6.git] / software / scsi2sd-util / SCSI2SD_HID.cc
1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
2 //
3 // This file is part of SCSI2SD.
4 //
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
17 #include "SCSI2SD_HID.hh"
18 #include "scsi2sd.h"
19 #include "hidpacket.h"
20
21 // For compilers that support precompilation, includes "wx/wx.h".
22 #include <wx/wxprec.h>
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #include <wx/utils.h>
28
29 #include <cassert>
30 #include <stdexcept>
31 #include <sstream>
32
33 #include <iostream>
34 #include <string.h> // memcpy
35
36 using namespace SCSI2SD;
37
38 HID::HID(hid_device_info* hidInfo) :
39 myHidInfo(hidInfo),
40 myConfigHandle(NULL),
41 myDebugHandle(NULL),
42 myFirmwareVersion(0),
43 mySDCapacity(0)
44 {
45
46 // hidInfo->interface_number not supported on mac, and interfaces
47 // are enumerated in a random order. :-(
48 // We rely on the watchdog value of the debug interface changing on each
49 // read to differentiate the interfaces.
50 try
51 {
52 while (hidInfo && !(myConfigHandle && myDebugHandle))
53 {
54 std::stringstream msg;
55 msg << "Error opening HID device " << hidInfo->path << std::endl;
56
57 if ((hidInfo->interface_number == CONFIG_INTERFACE) ||
58 (hidInfo->usage_page == 0xFF00))
59 {
60 myConfigHandle = hid_open_path(hidInfo->path);
61 if (!myConfigHandle) throw std::runtime_error(msg.str());
62
63 hidInfo = hidInfo->next;
64 }
65 else if ((hidInfo->interface_number == DEBUG_INTERFACE) ||
66 (hidInfo->usage_page == 0xFF01))
67 {
68 myDebugHandle = hid_open_path(hidInfo->path);
69 if (!myDebugHandle) throw std::runtime_error(msg.str());
70 readDebugData();
71 hidInfo = hidInfo->next;
72 }
73 else if (hidInfo->interface_number == -1)
74 {
75 // hidInfo->interface_number not supported on mac, and
76 // interfaces are enumerated in a random order. :-(
77 // We rely on the watchdog value of the debug interface
78 // changing on each read to differentiate the interfaces.
79 // Not necessary since firmware 3.5.2 as the usage_page can
80 // be used instead.
81 hid_device* dev = hid_open_path(hidInfo->path);
82 if (!dev)
83 {
84 throw std::runtime_error(msg.str());
85 }
86
87 uint8_t buf[HID_PACKET_SIZE];
88 int watchVal = -1;
89 int configIntFound = 1;
90 for (int i = 0; i < 4; ++i)
91 {
92 buf[0] = 0; // report id
93 hid_read_timeout(dev, buf, HID_PACKET_SIZE, HID_TIMEOUT_MS);
94 if (watchVal == -1) watchVal = buf[25];
95 configIntFound = configIntFound && (buf[25] == watchVal);
96 }
97
98 if (configIntFound)
99 {
100 myConfigHandle = dev;
101 }
102 else
103 {
104 myDebugHandle = dev;
105 readDebugData();
106 }
107 }
108 }
109 }
110 catch (std::runtime_error& e)
111 {
112 destroy();
113 throw e;
114 }
115 }
116
117 void
118 HID::destroy()
119 {
120 if (myConfigHandle)
121 {
122 hid_close(myConfigHandle);
123 myConfigHandle = NULL;
124 }
125 if (myDebugHandle)
126 {
127 hid_close(myDebugHandle);
128 myDebugHandle = NULL;
129 }
130
131 hid_free_enumeration(myHidInfo);
132 myHidInfo = NULL;
133 }
134
135 HID::~HID()
136 {
137 destroy();
138 }
139
140 HID*
141 HID::Open()
142 {
143 hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
144 if (dev)
145 {
146 return new HID(dev);
147 }
148 else
149 {
150 return NULL;
151 }
152 }
153
154 void
155 HID::enterBootloader()
156 {
157 // Reboot commands added in firmware 3.5
158 if (!myDebugHandle)
159 {
160 throw std::runtime_error(
161 "Cannot enter SCSI2SD bootloader: debug interface not found");
162 }
163 else if (myFirmwareVersion == 0)
164 {
165 throw std::runtime_error(
166 "Cannot enter SCSI2SD bootloader: old firmware version running.\n"
167 "The SCSI2SD board cannot reset itself. Please disconnect and \n"
168 "reconnect the USB cable.\n");
169 }
170 else
171 {
172 uint8_t hidBuf[HID_PACKET_SIZE + 1] =
173 {
174 0x00, // Report ID;
175 0x01 // Reboot command
176 // 63 bytes unused.
177 };
178
179 int result = hid_write(myDebugHandle, hidBuf, sizeof(hidBuf));
180 if (result <= 0)
181 {
182 const wchar_t* err = hid_error(myDebugHandle);
183 std::stringstream ss;
184 ss << "USB HID write failure: " << err;
185 throw std::runtime_error(ss.str());
186 }
187 }
188 }
189
190 void
191 HID::readFlashRow(int array, int row, std::vector<uint8_t>& out)
192 {
193 std::vector<uint8_t> cmd
194 {
195 CONFIG_READFLASH,
196 static_cast<uint8_t>(array),
197 static_cast<uint8_t>(row)
198 };
199 sendHIDPacket(cmd, out, HIDPACKET_MAX_LEN / 62);
200 }
201
202 void
203 HID::writeFlashRow(int array, int row, const std::vector<uint8_t>& in)
204 {
205 std::vector<uint8_t> cmd;
206 cmd.push_back(CONFIG_WRITEFLASH);
207 cmd.insert(cmd.end(), in.begin(), in.end());
208 cmd.push_back(static_cast<uint8_t>(array));
209 cmd.push_back(static_cast<uint8_t>(row));
210 std::vector<uint8_t> out;
211 sendHIDPacket(cmd, out, 1);
212 if ((out.size() < 1) || (out[0] != CONFIG_STATUS_GOOD))
213 {
214 std::stringstream ss;
215 ss << "Error writing flash " << array << "/" << row;
216 throw std::runtime_error(ss.str());
217 }
218 }
219
220 bool
221 HID::readSCSIDebugInfo(std::vector<uint8_t>& buf)
222 {
223 buf[0] = 0; // report id
224 hid_set_nonblocking(myDebugHandle, 1);
225 int result =
226 hid_read_timeout(
227 myDebugHandle,
228 &buf[0],
229 HID_PACKET_SIZE,
230 HID_TIMEOUT_MS);
231 hid_set_nonblocking(myDebugHandle, 0);
232
233 if (result <= 0)
234 {
235 const wchar_t* err = hid_error(myDebugHandle);
236 std::stringstream ss;
237 ss << "USB HID read failure: " << err;
238 throw std::runtime_error(ss.str());
239 }
240 return result > 0;
241 }
242
243
244 void
245 HID::readHID(uint8_t* buffer, size_t len)
246 {
247 assert(len >= 0);
248 buffer[0] = 0; // report id
249
250 int result = -1;
251 for (int retry = 0; retry < 3 && result <= 0; ++retry)
252 {
253 result = hid_read_timeout(myConfigHandle, buffer, len, HID_TIMEOUT_MS);
254 }
255
256 if (result < 0)
257 {
258 const wchar_t* err = hid_error(myConfigHandle);
259 std::stringstream ss;
260 ss << "USB HID read failure: " << err;
261 throw std::runtime_error(ss.str());
262 }
263 }
264
265 void
266 HID::readDebugData()
267 {
268 uint8_t buf[HID_PACKET_SIZE];
269 buf[0] = 0; // report id
270 int result =
271 hid_read_timeout(
272 myDebugHandle,
273 buf,
274 HID_PACKET_SIZE,
275 HID_TIMEOUT_MS);
276
277 if (result <= 0)
278 {
279 const wchar_t* err = hid_error(myDebugHandle);
280 std::stringstream ss;
281 ss << "USB HID read failure: " << err;
282 throw std::runtime_error(ss.str());
283 }
284 myFirmwareVersion = (((uint16_t)buf[62]) << 8) | buf[63];
285
286 mySDCapacity =
287 (((uint32_t)buf[58]) << 24) |
288 (((uint32_t)buf[59]) << 16) |
289 (((uint32_t)buf[60]) << 8) |
290 ((uint32_t)buf[61]);
291 }
292
293 std::string
294 HID::getFirmwareVersionStr() const
295 {
296 if (myFirmwareVersion == 0)
297 {
298 return "Unknown (3.0 - 3.4)";
299 }
300 else
301 {
302 std::stringstream ver;
303 ver << std::hex <<
304 (myFirmwareVersion >> 8) <<
305 '.' << ((myFirmwareVersion & 0xF0) >> 4);
306
307 int rev = myFirmwareVersion & 0xF;
308 if (rev)
309 {
310 ver << "." << rev;
311 }
312 return ver.str();
313 }
314 }
315
316
317 bool
318 HID::ping()
319 {
320 std::vector<uint8_t> cmd { CONFIG_PING };
321 std::vector<uint8_t> out;
322 try
323 {
324 sendHIDPacket(cmd, out, 1);
325 }
326 catch (std::runtime_error& e)
327 {
328 return false;
329 }
330
331 return (out.size() >= 1) && (out[0] == CONFIG_STATUS_GOOD);
332 }
333
334 std::vector<uint8_t>
335 HID::getSD_CSD()
336 {
337 std::vector<uint8_t> cmd { CONFIG_SDINFO };
338 std::vector<uint8_t> out;
339 try
340 {
341 sendHIDPacket(cmd, out, 16);
342 }
343 catch (std::runtime_error& e)
344 {
345 return std::vector<uint8_t>(16);
346 }
347
348 out.resize(16);
349 return out;
350 }
351
352 std::vector<uint8_t>
353 HID::getSD_CID()
354 {
355 std::vector<uint8_t> cmd { CONFIG_SDINFO };
356 std::vector<uint8_t> out;
357 try
358 {
359 sendHIDPacket(cmd, out, 16);
360 }
361 catch (std::runtime_error& e)
362 {
363 return std::vector<uint8_t>(16);
364 }
365
366 std::vector<uint8_t> result(16);
367 for (size_t i = 0; i < 16; ++i) result[i] = out[16 + i];
368 return result;
369 }
370
371 bool
372 HID::scsiSelfTest()
373 {
374 std::vector<uint8_t> cmd { CONFIG_SCSITEST };
375 std::vector<uint8_t> out;
376 try
377 {
378 sendHIDPacket(cmd, out, 2);
379 }
380 catch (std::runtime_error& e)
381 {
382 return false;
383 }
384 return (out.size() >= 1) && (out[0] == CONFIG_STATUS_GOOD);
385 }
386
387
388 void
389 HID::sendHIDPacket(
390 const std::vector<uint8_t>& cmd,
391 std::vector<uint8_t>& out,
392 size_t responseLength)
393 {
394 assert(cmd.size() <= HIDPACKET_MAX_LEN);
395 hidPacket_send(&cmd[0], cmd.size());
396
397 uint8_t hidBuf[HID_PACKET_SIZE];
398 const uint8_t* chunk = hidPacket_getHIDBytes(hidBuf);
399
400 while (chunk)
401 {
402 uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
403 memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
404 int result = -1;
405 for (int retry = 0; retry < 10 && result <= 0; ++retry)
406 {
407 result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
408 }
409
410 if (result <= 0)
411 {
412 const wchar_t* err = hid_error(myConfigHandle);
413 std::stringstream ss;
414 ss << "USB HID write failure: " << err;
415 throw std::runtime_error(ss.str());
416 }
417 chunk = hidPacket_getHIDBytes(hidBuf);
418 }
419
420 const uint8_t* resp = NULL;
421 size_t respLen;
422 resp = hidPacket_getPacket(&respLen);
423
424 for (unsigned int retry = 0; retry < responseLength * 2 && !resp; ++retry)
425 {
426 readHID(hidBuf, sizeof(hidBuf)); // Will block
427 hidPacket_recv(hidBuf, HID_PACKET_SIZE);
428 resp = hidPacket_getPacket(&respLen);
429 }
430
431 if (!resp)
432 {
433 throw std::runtime_error("SCSI2SD config protocol error");
434 }
435 out.insert(
436 out.end(),
437 resp,
438 resp + respLen);
439 }
440
441