Small compatibility improvements, and added scsi2sd-monitor test program
[SCSI2SD-V6.git] / software / scsi2sd-util / SCSI2SD_HID.cc
CommitLineData
75f825e6
MM
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
36using namespace SCSI2SD;
37
38HID::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
5446dc86 93 hid_read_timeout(dev, buf, HID_PACKET_SIZE, HID_TIMEOUT_MS);
75f825e6
MM
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
117void
118HID::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
135HID::~HID()
136{
137 destroy();
138}
139
140HID*
141HID::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
154void
155HID::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));
5446dc86 180 if (result <= 0)
75f825e6
MM
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
190void
191HID::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 };
5446dc86 199 sendHIDPacket(cmd, out, HIDPACKET_MAX_LEN / 62);
75f825e6
MM
200}
201
202void
203HID::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;
5446dc86 211 sendHIDPacket(cmd, out, 1);
75f825e6
MM
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
5446dc86
MM
220bool
221HID::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
75f825e6
MM
244void
245HID::readHID(uint8_t* buffer, size_t len)
246{
247 assert(len >= 0);
248 buffer[0] = 0; // report id
249
638c94ce 250 int result = -1;
5446dc86 251 for (int retry = 0; retry < 3 && result <= 0; ++retry)
75f825e6 252 {
5446dc86 253 result = hid_read_timeout(myConfigHandle, buffer, len, HID_TIMEOUT_MS);
75f825e6 254 }
75f825e6
MM
255
256 if (result < 0)
257 {
258 const wchar_t* err = hid_error(myConfigHandle);
259 std::stringstream ss;
638c94ce 260 ss << "USB HID read failure: " << err;
75f825e6
MM
261 throw std::runtime_error(ss.str());
262 }
75f825e6
MM
263}
264
265void
266HID::readDebugData()
267{
268 uint8_t buf[HID_PACKET_SIZE];
269 buf[0] = 0; // report id
5446dc86
MM
270 int result =
271 hid_read_timeout(
272 myDebugHandle,
273 buf,
274 HID_PACKET_SIZE,
275 HID_TIMEOUT_MS);
276
277 if (result <= 0)
75f825e6
MM
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
293std::string
294HID::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
317bool
318HID::ping()
319{
320 std::vector<uint8_t> cmd { CONFIG_PING };
321 std::vector<uint8_t> out;
322 try
323 {
5446dc86 324 sendHIDPacket(cmd, out, 1);
75f825e6
MM
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
5446dc86
MM
334std::vector<uint8_t>
335HID::getSD_CSD()
336{
337 std::vector<uint8_t> cmd { CONFIG_SDINFO };
338 std::vector<uint8_t> out;
339 try
340 {
9ad7cc15 341 sendHIDPacket(cmd, out, 16);
5446dc86
MM
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
352std::vector<uint8_t>
353HID::getSD_CID()
354{
355 std::vector<uint8_t> cmd { CONFIG_SDINFO };
356 std::vector<uint8_t> out;
357 try
358 {
9ad7cc15 359 sendHIDPacket(cmd, out, 16);
5446dc86
MM
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
9ad7cc15
MM
371bool
372HID::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
75f825e6 388void
5446dc86
MM
389HID::sendHIDPacket(
390 const std::vector<uint8_t>& cmd,
391 std::vector<uint8_t>& out,
392 size_t responseLength)
75f825e6
MM
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);
5446dc86 399
75f825e6
MM
400 while (chunk)
401 {
402 uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
403 memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
638c94ce 404 int result = -1;
5446dc86 405 for (int retry = 0; retry < 10 && result <= 0; ++retry)
638c94ce
MM
406 {
407 result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
638c94ce 408 }
75f825e6 409
5446dc86 410 if (result <= 0)
75f825e6
MM
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
95b51978 424 for (unsigned int retry = 0; retry < responseLength * 2 && !resp; ++retry)
75f825e6 425 {
5446dc86 426 readHID(hidBuf, sizeof(hidBuf)); // Will block
75f825e6
MM
427 hidPacket_recv(hidBuf, HID_PACKET_SIZE);
428 resp = hidPacket_getPacket(&respLen);
75f825e6
MM
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