Merge scsi2sd-util GUI changes
[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 = hid_write(SCSI2SDHID_handle, buf, count + 1);
74
75         if (result < 0)
76         {
77                 std::cerr << "USB HID Write Failure: " <<
78                         hid_error(SCSI2SDHID_handle) << std::endl;
79         }
80
81         return (result >= 0) ? 0 : -1;
82 }
83
84 static CyBtldr_CommunicationsData g_cyComms =
85 {
86         &SCSI2SDHID_OpenConnection,
87         &SCSI2SDHID_CloseConnection,
88         &SCSI2SDHID_ReadData,
89         &SCSI2SDHID_WriteData,
90         Bootloader::HID_PACKET_SIZE
91 };
92
93 Bootloader::OperationScope::OperationScope()
94 {
95         unsigned long blVer;
96         if (CyBtldr_StartBootloadOperation(&g_cyComms, 0x2e133069, 0, &blVer)
97                 != CYRET_SUCCESS)
98         {
99                 throw std::runtime_error("Could not start bootloader operation");
100         }
101 }
102
103 Bootloader::OperationScope::~OperationScope()
104 {
105         CyBtldr_EndBootloadOperation();
106 }
107
108 Bootloader::Bootloader(hid_device_info* hidInfo) :
109         myHidInfo(hidInfo),
110         myBootloaderHandle(NULL)
111 {
112         myBootloaderHandle = hid_open_path(hidInfo->path);
113         if (!myBootloaderHandle)
114         {
115                 std::stringstream msg;
116                 msg << "Error opening HID device " << hidInfo->path << std::endl;
117
118                 hid_free_enumeration(myHidInfo);
119                 myHidInfo = NULL;
120
121                 throw std::runtime_error(msg.str());
122         }
123         else
124         {
125                 SCSI2SDHID_handle = myBootloaderHandle;
126         }
127 }
128
129 Bootloader::~Bootloader()
130 {
131         if (myBootloaderHandle)
132         {
133                 hid_close(myBootloaderHandle);
134         }
135         SCSI2SDHID_handle = NULL;
136
137         hid_free_enumeration(myHidInfo);
138 }
139
140 Bootloader*
141 Bootloader::Open()
142 {
143         hid_device_info* dev = hid_enumerate(VENDOR_ID, PRODUCT_ID);
144         if (dev)
145         {
146                 return new Bootloader(dev);
147         }
148         else
149         {
150                 return NULL;
151         }
152 }
153
154 Bootloader::HWInfo
155 Bootloader::getHWInfo() const
156 {
157         HWInfo info = {"unknown", "unknown", "unknown"};
158
159         switch (myHidInfo->release_number)
160         {
161         case 0x3001:
162                 info.desc = "3.5\" SCSI2SD (green)";
163                 info.version = "V3.0";
164                 info.firmwareName = "SCSI2SD-V3.cyacd";
165                 break;
166         case 0x3002:
167                 info.desc = "3.5\" SCSI2SD (yellow) or 2.5\" SCSI2SD for Apple Powerbook";
168                 info.version = "V4.1/V4.2";
169                 info.firmwareName = "SCSI2SD-V4.cyacd";
170                 break;
171         }
172         return info;
173 }
174
175 bool
176 Bootloader::isCorrectFirmware(const std::string& path) const
177 {
178         HWInfo info = getHWInfo();
179         return path.rfind(info.firmwareName) != std::string::npos;
180 }
181
182 std::string
183 Bootloader::getDevicePath() const
184 {
185         return myHidInfo->path;
186 }
187
188 std::wstring
189 Bootloader::getManufacturer() const
190 {
191         return myHidInfo->manufacturer_string;
192 }
193
194 std::wstring
195 Bootloader::getProductString() const
196 {
197         return myHidInfo->product_string;
198 }
199
200 void
201 Bootloader::load(const std::string& path, void (*progress)(uint8_t, uint16_t))
202 {
203         int result = CyBtldr_Program(
204                 path.c_str(),
205                 &g_cyComms,
206                 progress);
207
208         if (result)
209         {
210                 throw std::runtime_error("Firmware update failed");
211         }
212 }
213
214 bool
215 Bootloader::ping() const
216 {
217         try
218         {
219                 Bootloader::OperationScope operationGuard;
220
221                 int result = CyBtldr_VerifyRow(0, 0, 0);
222                 switch (result)
223                 {
224                 case CYRET_SUCCESS:
225                 case CYRET_ERR_CHECKSUM: // We supplied a dummy value of 0.
226                         return true;
227
228                 default: return false;
229                 }
230
231         }
232         catch (std::exception& e)
233         {
234                 return false;
235         }
236
237 }
238
239