Implement WRITE BUFFER and WRITE WITH VERIFY commands
[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, 1);
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, 1);
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 void
372 HID::sendHIDPacket(
373         const std::vector<uint8_t>& cmd,
374         std::vector<uint8_t>& out,
375         size_t responseLength)
376 {
377         assert(cmd.size() <= HIDPACKET_MAX_LEN);
378         hidPacket_send(&cmd[0], cmd.size());
379
380         uint8_t hidBuf[HID_PACKET_SIZE];
381         const uint8_t* chunk = hidPacket_getHIDBytes(hidBuf);
382
383         while (chunk)
384         {
385                 uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
386                 memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
387                 int result = -1;
388                 for (int retry = 0; retry < 10 && result <= 0; ++retry)
389                 {
390                         result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
391                 }
392
393                 if (result <= 0)
394                 {
395                         const wchar_t* err = hid_error(myConfigHandle);
396                         std::stringstream ss;
397                         ss << "USB HID write failure: " << err;
398                         throw std::runtime_error(ss.str());
399                 }
400                 chunk = hidPacket_getHIDBytes(hidBuf);
401         }
402
403         const uint8_t* resp = NULL;
404         size_t respLen;
405         resp = hidPacket_getPacket(&respLen);
406
407         for (unsigned int retry = 0; retry < responseLength * 2 && !resp; ++retry)
408         {
409                 readHID(hidBuf, sizeof(hidBuf)); // Will block
410                 hidPacket_recv(hidBuf, HID_PACKET_SIZE);
411                 resp = hidPacket_getPacket(&respLen);
412         }
413
414         if (!resp)
415         {
416                 throw std::runtime_error("SCSI2SD config protocol error");
417         }
418         out.insert(
419                 out.end(),
420                 resp,
421                 resp + respLen);
422 }
423
424