e5b8ca3471b2ac4cc7fbef8649b7458e770c028e
[SCSI2SD-V6.git] / src / scsi2sd-util6 / 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         myFirmwareVersion(0),
42         mySDCapacity(0)
43 {
44
45         try
46         {
47                 std::stringstream msg;
48                 msg << "Error opening HID device " << hidInfo->path << std::endl;
49
50                 myConfigHandle = hid_open_path(hidInfo->path);
51                 if (!myConfigHandle) throw std::runtime_error(msg.str());
52                 readNewDebugData();
53         }
54         catch (std::runtime_error& e)
55         {
56                 destroy();
57                 throw e;
58         }
59 }
60
61 void
62 HID::destroy()
63 {
64         if (myConfigHandle)
65         {
66                 hid_close(myConfigHandle);
67                 myConfigHandle = NULL;
68         }
69
70
71         hid_free_enumeration(myHidInfo);
72         myHidInfo = NULL;
73 }
74
75 HID::~HID()
76 {
77         destroy();
78 }
79
80 HID*
81 HID::Open()
82 {
83         hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
84         if (dev)
85         {
86                 return new HID(dev);
87         }
88         else
89         {
90                 return NULL;
91         }
92 }
93
94 void
95 HID::enterBootloader()
96 {
97         std::vector<uint8_t> out;
98         std::vector<uint8_t> cmd { S2S_CMD_REBOOT };
99         sendHIDPacket(cmd, out, 1);
100 }
101
102 void
103 HID::readSector(uint32_t sector, std::vector<uint8_t>& out)
104 {
105         std::vector<uint8_t> cmd
106         {
107                 S2S_CMD_SD_READ,
108                 static_cast<uint8_t>(sector >> 24),
109                 static_cast<uint8_t>(sector >> 16),
110                 static_cast<uint8_t>(sector >> 8),
111                 static_cast<uint8_t>(sector)
112         };
113         sendHIDPacket(cmd, out, HIDPACKET_MAX_LEN / 62);
114 }
115
116 void
117 HID::writeSector(uint32_t sector, const std::vector<uint8_t>& in)
118 {
119         std::vector<uint8_t> cmd
120         {
121                 S2S_CMD_SD_WRITE,
122                 static_cast<uint8_t>(sector >> 24),
123                 static_cast<uint8_t>(sector >> 16),
124                 static_cast<uint8_t>(sector >> 8),
125                 static_cast<uint8_t>(sector)
126         };
127         cmd.insert(cmd.end(), in.begin(), in.end());
128         std::vector<uint8_t> out;
129         sendHIDPacket(cmd, out, 1);
130         if ((out.size() < 1) || (out[0] != S2S_CFG_STATUS_GOOD))
131         {
132                 std::stringstream ss;
133                 ss << "Error writing sector " << sector;
134                 throw std::runtime_error(ss.str());
135         }
136 }
137
138 bool
139 HID::readSCSIDebugInfo(std::vector<uint8_t>& buf)
140 {
141         std::vector<uint8_t> cmd { S2S_CMD_DEBUG };
142         sendHIDPacket(cmd, buf, 1);
143         return buf.size() > 0;
144 }
145
146
147 void
148 HID::readHID(uint8_t* buffer, size_t len)
149 {
150         assert(len >= 0);
151         buffer[0] = 0; // report id
152
153         int result = -1;
154         for (int retry = 0; retry < 3 && result <= 0; ++retry)
155         {
156                 result = hid_read_timeout(myConfigHandle, buffer, len, HID_TIMEOUT_MS);
157         }
158
159         if (result < 0)
160         {
161                 const wchar_t* err = hid_error(myConfigHandle);
162                 std::stringstream ss;
163                 ss << "USB HID read failure: " << err;
164                 throw std::runtime_error(ss.str());
165         }
166 }
167
168 void
169 HID::readNewDebugData()
170 {
171         // Newer devices only have a single HID interface, and present
172         // a command to obtain the data
173         std::vector<uint8_t> cmd { S2S_CMD_DEVINFO, 0xDE, 0xAD, 0xBE, 0xEF };
174         std::vector<uint8_t> out;
175         try
176         {
177                 sendHIDPacket(cmd, out, 6);
178         }
179         catch (std::runtime_error& e)
180         {
181                 myFirmwareVersion = 0;
182                 mySDCapacity = 0;
183                 return;
184         }
185
186         out.resize(6);
187         myFirmwareVersion = (out[0] << 8) | out[1];
188         mySDCapacity =
189                 (((uint32_t)out[2]) << 24) |
190                 (((uint32_t)out[3]) << 16) |
191                 (((uint32_t)out[4]) << 8) |
192                 ((uint32_t)out[5]);
193 }
194
195 std::string
196 HID::getFirmwareVersionStr() const
197 {
198         std::stringstream ver;
199         ver <<
200                 (myFirmwareVersion >> 8) <<
201                 '.' << ((myFirmwareVersion & 0xF0) >> 4);
202
203         int rev = myFirmwareVersion & 0xF;
204         if (rev)
205         {
206                 ver << "." << rev;
207         }
208         return ver.str();
209 }
210
211
212 bool
213 HID::ping()
214 {
215         std::vector<uint8_t> cmd { S2S_CMD_PING };
216         std::vector<uint8_t> out;
217         try
218         {
219                 sendHIDPacket(cmd, out, 1);
220         }
221         catch (std::runtime_error& e)
222         {
223                 return false;
224         }
225
226         return (out.size() >= 1) && (out[0] == S2S_CFG_STATUS_GOOD);
227 }
228
229 std::vector<uint8_t>
230 HID::getSD_CSD()
231 {
232         std::vector<uint8_t> cmd { S2S_CMD_SDINFO };
233         std::vector<uint8_t> out;
234         try
235         {
236                 sendHIDPacket(cmd, out, 16);
237         }
238         catch (std::runtime_error& e)
239         {
240                 return std::vector<uint8_t>(16);
241         }
242
243         out.resize(16);
244         return out;
245 }
246
247 std::vector<uint8_t>
248 HID::getSD_CID()
249 {
250         std::vector<uint8_t> cmd { S2S_CMD_SDINFO };
251         std::vector<uint8_t> out;
252         try
253         {
254                 sendHIDPacket(cmd, out, 16);
255         }
256         catch (std::runtime_error& e)
257         {
258                 return std::vector<uint8_t>(16);
259         }
260
261         std::vector<uint8_t> result(16);
262         for (size_t i = 0; i < 16; ++i) result[i] = out[16 + i];
263         return result;
264 }
265
266 bool
267 HID::scsiSelfTest(int& code)
268 {
269         std::vector<uint8_t> cmd { S2S_CMD_SCSITEST };
270         std::vector<uint8_t> out;
271         try
272         {
273                 sendHIDPacket(cmd, out, 2);
274         }
275         catch (std::runtime_error& e)
276         {
277                 return false;
278         }
279         code = out.size() >= 2 ? out[1] : -1;
280         return (out.size() >= 1) && (out[0] == S2S_CFG_STATUS_GOOD);
281 }
282
283
284 void
285 HID::sendHIDPacket(
286         const std::vector<uint8_t>& cmd,
287         std::vector<uint8_t>& out,
288         size_t responseLength)
289 {
290         assert(cmd.size() <= HIDPACKET_MAX_LEN);
291         hidPacket_send(&cmd[0], cmd.size());
292
293         uint8_t hidBuf[HID_PACKET_SIZE];
294         const uint8_t* chunk = hidPacket_getHIDBytes(hidBuf);
295
296         while (chunk)
297         {
298                 uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
299                 memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
300                 int result = -1;
301                 for (int retry = 0; retry < 10 && result <= 0; ++retry)
302                 {
303                         result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
304                 }
305
306                 if (result <= 0)
307                 {
308                         const wchar_t* err = hid_error(myConfigHandle);
309                         std::stringstream ss;
310                         ss << "USB HID write failure: " << err;
311                         throw std::runtime_error(ss.str());
312                 }
313                 chunk = hidPacket_getHIDBytes(hidBuf);
314         }
315
316         const uint8_t* resp = NULL;
317         size_t respLen;
318         resp = hidPacket_getPacket(&respLen);
319
320         for (unsigned int retry = 0; retry < responseLength * 2 && !resp; ++retry)
321         {
322                 readHID(hidBuf, sizeof(hidBuf)); // Will block
323                 hidPacket_recv(hidBuf, HID_PACKET_SIZE);
324                 resp = hidPacket_getPacket(&respLen);
325         }
326
327         if (!resp)
328         {
329                 throw std::runtime_error("SCSI2SD config protocol error");
330         }
331         out.insert(
332                 out.end(),
333                 resp,
334                 resp + respLen);
335 }
336
337 std::string
338 HID::getHardwareVersion()
339 {
340         if (myFirmwareVersion < 0x0630)
341         {
342                 // Definitely the 2020c or newer hardware.
343                 return "V6, Rev F or older";
344         }
345         else if (myFirmwareVersion == 0x0630)
346         {
347                 return "V6, unknown.";
348         }
349         else
350         {
351                 const size_t maxUsbString = 255;
352                 wchar_t wstr[maxUsbString];
353                 int res = hid_get_product_string(myConfigHandle, wstr, maxUsbString);
354                 if (res == 0)
355                 {
356                         std::wstring prodStr(wstr);
357                         if (prodStr.find(L"2020") != std::string::npos)
358                         {
359                                 // Definitely the 2020c or newer hardware.
360                                 return "V6, 2020c or newer";
361                         }
362                         else if (prodStr.find(L"2021") != std::string::npos)
363                         {
364                                 // Definitely the 2020c or newer hardware.
365                                 return "V6, 2021 or newer";
366                         }
367                         else
368                         {
369                                 return "V6, Rev F or older";
370                         }
371                 }
372         }
373
374         return "Unknown";
375 }
376
377 std::string
378 HID::getSerialNumber()
379 {
380         const size_t maxUsbString = 255;
381         wchar_t wstr[maxUsbString];
382         int res = hid_get_serial_number_string(myConfigHandle, wstr, maxUsbString);
383         if (res == 0)
384         {
385                 std::wstring wideString(wstr);
386                 return std::string(wideString.begin(), wideString.end());
387         }
388
389         return std::string();
390 }
391
392
393 bool
394 HID::isCorrectFirmware(const std::string& path)
395 {
396         if (myFirmwareVersion < 0x0630)
397         {
398                 // Definitely the older hardware.
399                 return path.rfind("firmware.V6.revF.dfu") != std::string::npos ||
400                         path.rfind("firmware.dfu") != std::string::npos;
401         }
402         else if (myFirmwareVersion == 0x0630)
403         {
404                 // We don't know which. :-( Initial batch of 2020 boards loaded with
405                 // v6.3.0
406                 // So for now we CANNOT bundle ? User will need to selet the correct
407                 // file.
408                 return true;
409         }
410         else
411         {
412                 const size_t maxUsbString = 255;
413                 wchar_t wstr[maxUsbString];
414                 int res = hid_get_product_string(myConfigHandle, wstr, maxUsbString);
415                 if (res == 0)
416                 {
417                         std::wstring prodStr(wstr);
418                         if (prodStr.find(L"2020") != std::string::npos)
419                         {
420                                 // Definitely the 2020c or newer hardware.
421                                 return path.rfind("firmware.V6.2020.dfu") != std::string::npos;
422                         }
423                         else if (prodStr.find(L"2021") != std::string::npos)
424                         {
425                                 // Definitely the 2020c or newer hardware.
426                                 return path.rfind("firmware.V6.2021.dfu") != std::string::npos;
427                         }
428                         else
429                         {
430                                 return path.rfind("firmware.V6.revF.dfu") != std::string::npos ||
431                                         path.rfind("firmware.dfu") != std::string::npos;
432                         }
433                 }
434         }
435
436         return false;
437 }
438