scsi2sd-util bug fixes and hot swap support.
[SCSI2SD.git] / software / scsi2sd-util / SCSI2SD_Bootloader.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
18 #include "SCSI2SD_Bootloader.hh"
19
20 #include <iostream>
21 #include <sstream>
22 #include <stdexcept>
23
24
25 #include <string.h>
26
27 using namespace SCSI2SD;
28
29 hid_device* SCSI2SDHID_handle = NULL;
30
31 // cybtldr interface.
32 extern "C" int
33 SCSI2SDHID_OpenConnection(void)
34 {
35         return 0;
36 }
37
38 extern "C" int
39 SCSI2SDHID_CloseConnection(void)
40 {
41         return 0;
42 }
43
44 extern "C" int
45 SCSI2SDHID_ReadData(unsigned char* data, int count)
46 {
47         uint8_t buf[65];
48         buf[0] = 0; // Report ID
49
50         int result = hid_read(SCSI2SDHID_handle, buf, count);
51
52         if (result < 0)
53         {
54                 std::cerr << "USB HID Read Failure: " <<
55                         hid_error(SCSI2SDHID_handle) << std::endl;
56         }
57
58         memcpy(data, buf, count);
59
60         return (result >= 0) ? 0 : -1;
61 }
62
63 extern "C" int
64 SCSI2SDHID_WriteData(unsigned char* data, int count)
65 {
66         uint8_t buf[65];
67         buf[0] = 0; // report ID
68         int i;
69         for (i = 0; i < count; ++i)
70         {
71                 buf[i+1] = data[i];
72         }
73         int result = -1;
74         for (int retry = 0; retry < 3 && result < 0; ++retry)
75         {
76                 result = hid_write(SCSI2SDHID_handle, buf, count + 1);
77         }
78
79         if (result < 0)
80         {
81                 std::cerr << "USB HID Write Failure: " <<
82                         hid_error(SCSI2SDHID_handle) << std::endl;
83         }
84
85         return (result >= 0) ? 0 : -1;
86 }
87
88 static CyBtldr_CommunicationsData g_cyComms =
89 {
90         &SCSI2SDHID_OpenConnection,
91         &SCSI2SDHID_CloseConnection,
92         &SCSI2SDHID_ReadData,
93         &SCSI2SDHID_WriteData,
94         Bootloader::HID_PACKET_SIZE
95 };
96
97 Bootloader::OperationScope::OperationScope()
98 {
99         unsigned long blVer;
100         if (CyBtldr_StartBootloadOperation(&g_cyComms, 0x2e133069, 0, &blVer)
101                 != CYRET_SUCCESS)
102         {
103                 throw std::runtime_error("Could not start bootloader operation");
104         }
105 }
106
107 Bootloader::OperationScope::~OperationScope()
108 {
109         CyBtldr_EndBootloadOperation();
110 }
111
112 Bootloader::Bootloader(hid_device_info* hidInfo) :
113         myHidInfo(hidInfo),
114         myBootloaderHandle(NULL)
115 {
116         myBootloaderHandle = hid_open_path(hidInfo->path);
117         if (!myBootloaderHandle)
118         {
119                 std::stringstream msg;
120                 msg << "Error opening HID device " << hidInfo->path << std::endl;
121
122                 hid_free_enumeration(myHidInfo);
123                 myHidInfo = NULL;
124
125                 throw std::runtime_error(msg.str());
126         }
127         else
128         {
129                 SCSI2SDHID_handle = myBootloaderHandle;
130         }
131 }
132
133 Bootloader::~Bootloader()
134 {
135         if (myBootloaderHandle)
136         {
137                 hid_close(myBootloaderHandle);
138         }
139         SCSI2SDHID_handle = NULL;
140
141         hid_free_enumeration(myHidInfo);
142 }
143
144 Bootloader*
145 Bootloader::Open()
146 {
147         hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
148         if (dev)
149         {
150                 return new Bootloader(dev);
151         }
152         else
153         {
154                 return NULL;
155         }
156 }
157
158 Bootloader::HWInfo
159 Bootloader::getHWInfo() const
160 {
161         HWInfo info = {"unknown", "unknown", "unknown"};
162
163         switch (myHidInfo->release_number)
164         {
165         case 0x3001:
166                 info.desc = "3.5\" SCSI2SD (green)";
167                 info.version = "V3.0";
168                 info.firmwareName = "SCSI2SD-V3.cyacd";
169                 break;
170         case 0x3002:
171                 info.desc = "3.5\" SCSI2SD (yellow) or 2.5\" SCSI2SD for Apple Powerbook";
172                 info.version = "V4.1/V4.2";
173                 info.firmwareName = "SCSI2SD-V4.cyacd";
174                 break;
175         }
176         return info;
177 }
178
179 bool
180 Bootloader::isCorrectFirmware(const std::string& path) const
181 {
182         HWInfo info = getHWInfo();
183         return path.rfind(info.firmwareName) != std::string::npos;
184 }
185
186 std::string
187 Bootloader::getDevicePath() const
188 {
189         return myHidInfo->path;
190 }
191
192 std::wstring
193 Bootloader::getManufacturer() const
194 {
195         return myHidInfo->manufacturer_string;
196 }
197
198 std::wstring
199 Bootloader::getProductString() const
200 {
201         return myHidInfo->product_string;
202 }
203
204 void
205 Bootloader::load(const std::string& path, void (*progress)(uint8_t, uint16_t))
206 {
207         int result = CyBtldr_Program(
208                 path.c_str(),
209                 &g_cyComms,
210                 progress);
211
212         if (result)
213         {
214                 throw std::runtime_error("Firmware update failed");
215         }
216 }
217
218 bool
219 Bootloader::ping() const
220 {
221         try
222         {
223                 Bootloader::OperationScope operationGuard;
224
225                 int result = CyBtldr_VerifyRow(0, 0, 0);
226                 switch (result)
227                 {
228                 case CYRET_SUCCESS:
229                 case CYRET_ERR_CHECKSUM: // We supplied a dummy value of 0.
230                         return true;
231
232                 default: return false;
233                 }
234
235         }
236         catch (std::exception& e)
237         {
238                 return false;
239         }
240
241 }
242
243