Ugly workaround for lack of multiple HID device support on OSX.
[SCSI2SD-V6.git] / software / bootloaderhost / 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
19 #include <cassert>
20 #include <stdexcept>
21 #include <sstream>
22
23 #include <iostream>
24 #include <string.h> // memcpy
25
26 using namespace SCSI2SD;
27
28 HID::HID(hid_device_info* hidInfo) :
29         myHidInfo(hidInfo),
30         myConfigHandle(NULL),
31         myDebugHandle(NULL),
32         myFirmwareVersion(0),
33         mySDCapacity(0)
34 {
35         // hidInfo->interface_number not supported on mac, and interfaces
36         // are enumerated in a random order. :-(
37         // We rely on the watchdog value of the debug interface changing on each
38         // read to differentiate the interfaces.
39         while (hidInfo && !(myConfigHandle && myDebugHandle))
40         {
41                 if (hidInfo->interface_number == CONFIG_INTERFACE)
42                 {
43                         myConfigHandle = hid_open_path(hidInfo->path);
44                         hidInfo = hidInfo->next;
45                 }
46                 else if (hidInfo->interface_number == DEBUG_INTERFACE)
47                 {
48                         myDebugHandle = hid_open_path(hidInfo->path);
49                         readDebugData();
50                         hidInfo = hidInfo->next;
51                 }
52                 else if (hidInfo->interface_number == -1)
53                 {
54                         // hidInfo->interface_number not supported on mac, and
55                         // interfaces are enumerated in a random order. :-(
56                         // We rely on the watchdog value of the debug interface
57                         // changing on each read to differentiate the interfaces.
58                         hid_device* dev = hid_open_path(hidInfo->path);
59                         if (!dev)
60                         {
61                                 hidInfo = hidInfo->next;
62                                 continue;
63                         }
64
65                         uint8_t buf[HID_PACKET_SIZE];
66                         int watchVal = -1;
67                         int configIntFound = 1;
68                         for (int i = 0; i < 4; ++i)
69                         {
70                                 buf[0] = 0; // report id
71                                 hid_read(dev, buf, HID_PACKET_SIZE);
72                                 if (watchVal == -1) watchVal = buf[25];
73                                 configIntFound = configIntFound && (buf[25] == watchVal);
74                         }
75
76                         if (configIntFound)
77                         {
78                                 myConfigHandle = dev;
79                         }
80                         else
81                         {
82                                 myDebugHandle = dev;
83                                 readDebugData();
84                         }
85                 }
86         }
87 }
88
89 HID::~HID()
90 {
91         if (myConfigHandle)
92         {
93                 hid_close(myConfigHandle);
94         }
95         if (myDebugHandle)
96         {
97                 hid_close(myDebugHandle);
98         }
99
100         hid_free_enumeration(myHidInfo);
101 }
102
103 HID*
104 HID::Open()
105 {
106         hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
107         if (dev)
108         {
109                 return new HID(dev);
110         }
111         else
112         {
113                 return NULL;
114         }
115 }
116
117 void
118 HID::enterBootloader()
119 {
120         // Reboot commands added in firmware 3.5
121         if (!myDebugHandle)
122         {
123                 throw std::runtime_error(
124                         "Cannot enter SCSI2SD bootloader: debug interface not found");
125         }
126         else if (myFirmwareVersion == 0)
127         {
128                 throw std::runtime_error(
129                         "Cannot enter SCSI2SD bootloader: old firmware version running.\n"
130                         "The SCSI2SD board cannot reset itself. Please disconnect and \n"
131                         "reconnect the USB cable.\n");
132         }
133         else
134         {
135                 uint8_t hidBuf[HID_PACKET_SIZE + 1] =
136                 {
137                         0x00, // Report ID;
138                         0x01 // Reboot command
139                         // 63 bytes unused.
140                 };
141
142                 int result = hid_write(myDebugHandle, hidBuf, sizeof(hidBuf));
143                 if (result < 0)
144                 {
145                         const wchar_t* err = hid_error(myDebugHandle);
146                         std::stringstream ss;
147                         ss << "USB HID write failure: " << err;
148                         throw std::runtime_error(ss.str());
149                 }
150         }
151 }
152
153 void
154 HID::readConfig(uint8_t* buffer, size_t len)
155 {
156         assert(len >= 0);
157         buffer[0] = 0; // report id
158
159         int result = hid_read(myConfigHandle, buffer, len);
160
161         if (result < 0)
162         {
163                 const wchar_t* err = hid_error(myConfigHandle);
164                 std::stringstream ss;
165                 ss << "USB HID read failure: " << err;
166                 throw std::runtime_error(ss.str());
167         }
168 }
169
170 void
171 HID::saveConfig(uint8_t* buffer, size_t len)
172 {
173         assert(len >= 0 && len <= HID_PACKET_SIZE);
174
175         uint8_t hidBuf[HID_PACKET_SIZE + 1] =
176         {
177                 0x00, // Report ID;
178         };
179         memcpy(&hidBuf[1], buffer, len);
180
181         int result = hid_write(myConfigHandle, hidBuf, len + 1);
182
183         if (result < 0)
184         {
185                 const wchar_t* err = hid_error(myConfigHandle);
186                 std::stringstream ss;
187                 ss << "USB HID write failure: " << err;
188                 throw std::runtime_error(ss.str());
189         }
190
191
192 }
193
194 void
195 HID::readDebugData()
196 {
197         uint8_t buf[HID_PACKET_SIZE];
198         buf[0] = 0; // report id
199         int result = hid_read(myDebugHandle, buf, HID_PACKET_SIZE);
200
201         if (result < 0)
202         {
203                 const wchar_t* err = hid_error(myDebugHandle);
204                 std::stringstream ss;
205                 ss << "USB HID read failure: " << err;
206                 throw std::runtime_error(ss.str());
207         }
208         myFirmwareVersion = (((uint16_t)buf[62]) << 8) | buf[63];
209
210         mySDCapacity =
211                 (((uint32_t)buf[58]) << 24) |
212                 (((uint32_t)buf[59]) << 16) |
213                 (((uint32_t)buf[60]) << 8) |
214                 ((uint32_t)buf[61]);
215 }
216
217 std::string
218 HID::getFirmwareVersionStr() const
219 {
220         if (myFirmwareVersion == 0)
221         {
222                 return "Unknown (3.0 - 3.4)";
223         }
224         else
225         {
226                 std::stringstream ver;
227                 ver << std::hex <<
228                         (myFirmwareVersion >> 8) <<
229                         '.' << (myFirmwareVersion & 0xFF);
230                 return ver.str();
231         }
232 }
233
234