e81821b815a84b6ae9786b052bd713ee15c3c92a
[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(dev, buf, HID_PACKET_SIZE);
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);
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);
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 void
221 HID::readHID(uint8_t* buffer, size_t len)
222 {
223         assert(len >= 0);
224         buffer[0] = 0; // report id
225
226         int result = -1;
227         for (int retry = 0; retry < 3 && result < 0; ++retry)
228         {
229                 result = hid_read(myConfigHandle, buffer, len);
230         }
231
232         if (result < 0)
233         {
234                 const wchar_t* err = hid_error(myConfigHandle);
235                 std::stringstream ss;
236                 ss << "USB HID read failure: " << err;
237                 throw std::runtime_error(ss.str());
238         }
239 }
240
241 void
242 HID::readDebugData()
243 {
244         uint8_t buf[HID_PACKET_SIZE];
245         buf[0] = 0; // report id
246         int result = hid_read(myDebugHandle, buf, HID_PACKET_SIZE);
247
248         if (result < 0)
249         {
250                 const wchar_t* err = hid_error(myDebugHandle);
251                 std::stringstream ss;
252                 ss << "USB HID read failure: " << err;
253                 throw std::runtime_error(ss.str());
254         }
255         myFirmwareVersion = (((uint16_t)buf[62]) << 8) | buf[63];
256
257         mySDCapacity =
258                 (((uint32_t)buf[58]) << 24) |
259                 (((uint32_t)buf[59]) << 16) |
260                 (((uint32_t)buf[60]) << 8) |
261                 ((uint32_t)buf[61]);
262 }
263
264 std::string
265 HID::getFirmwareVersionStr() const
266 {
267         if (myFirmwareVersion == 0)
268         {
269                 return "Unknown (3.0 - 3.4)";
270         }
271         else
272         {
273                 std::stringstream ver;
274                 ver << std::hex <<
275                         (myFirmwareVersion >> 8) <<
276                         '.' << ((myFirmwareVersion & 0xF0) >> 4);
277
278                 int rev = myFirmwareVersion & 0xF;
279                 if (rev)
280                 {
281                         ver << "." << rev;
282                 }
283                 return ver.str();
284         }
285 }
286
287
288 bool
289 HID::ping()
290 {
291         std::vector<uint8_t> cmd { CONFIG_PING };
292         std::vector<uint8_t> out;
293         try
294         {
295                 sendHIDPacket(cmd, out);
296         }
297         catch (std::runtime_error& e)
298         {
299                 return false;
300         }
301
302         return (out.size() >= 1) && (out[0] == CONFIG_STATUS_GOOD);
303 }
304
305 void
306 HID::sendHIDPacket(const std::vector<uint8_t>& cmd, std::vector<uint8_t>& out)
307 {
308         assert(cmd.size() <= HIDPACKET_MAX_LEN);
309         hidPacket_send(&cmd[0], cmd.size());
310
311         uint8_t hidBuf[HID_PACKET_SIZE];
312         const uint8_t* chunk = hidPacket_getHIDBytes(hidBuf);
313         while (chunk)
314         {
315                 uint8_t reportBuf[HID_PACKET_SIZE + 1] = { 0x00 }; // Report ID
316                 memcpy(&reportBuf[1], chunk, HID_PACKET_SIZE);
317                 int result = -1;
318                 for (int retry = 0; retry < 10 && result < 0; ++retry)
319                 {
320                         result = hid_write(myConfigHandle, reportBuf, sizeof(reportBuf));
321                         if (result < 0) wxMilliSleep(8);
322                 }
323
324                 if (result < 0)
325                 {
326                         const wchar_t* err = hid_error(myConfigHandle);
327                         std::stringstream ss;
328                         ss << "USB HID write failure: " << err;
329                         throw std::runtime_error(ss.str());
330                 }
331                 chunk = hidPacket_getHIDBytes(hidBuf);
332         }
333
334         const uint8_t* resp = NULL;
335         size_t respLen;
336         resp = hidPacket_getPacket(&respLen);
337
338         // We need to poll quick enough to not skip packets
339         // This depends on the USB HID device poll rate parameter.
340         // SCSI2SD uses a 32ms poll rate, so we check for new chunks at 4x
341         // that rate.
342         for (int retry = 0; retry < (HIDPACKET_MAX_LEN / 62) * 30 && !resp; ++retry)
343         {
344                 readHID(hidBuf, sizeof(hidBuf));
345                 hidPacket_recv(hidBuf, HID_PACKET_SIZE);
346                 resp = hidPacket_getPacket(&respLen);
347                 if (!resp) wxMilliSleep(8);
348         }
349
350         if (!resp)
351         {
352                 throw std::runtime_error("SCSI2SD config protocol error");
353         }
354         out.insert(
355                 out.end(),
356                 resp,
357                 resp + respLen);
358 }
359
360