Removed C++11 dependencies, and compile Windows binaries staically with mingw.
[SCSI2SD.git] / software / scsi2sd-config / scsi2sd-config.cc
1 //      Copyright (C) 2013 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_HID.hh"
19
20 #if __cplusplus >= 201103L
21 #include <memory>
22 using std::shared_ptr;
23 #else
24 #include <tr1/memory>
25 using std::tr1::shared_ptr;
26 #endif
27
28 #include <iomanip>
29 #include <iostream>
30 #include <sstream>
31
32 // Request extended stdio format macros.
33 #define __STDC_FORMAT_MACROS
34 #include <inttypes.h>
35
36 #include <getopt.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 // htonl/ntohl includes.
44 #ifdef _WIN32
45 #include <winsock2.h>
46 #else
47 #include <arpa/inet.h>
48 #endif
49
50 #include "hidapi.h"
51
52 #define MIN(a,b) (a < b ? a : b)
53
54 using namespace SCSI2SD;
55
56 enum
57 {
58         PARAM_ID,
59         PARAM_PARITY,
60         PARAM_NOPARITY,
61         PARAM_UNITATT,
62         PARAM_NOUNITATT,
63         PARAM_MAXBLOCKS,
64         PARAM_APPLE,
65         PARAM_VENDOR,
66         PARAM_PRODID,
67         PARAM_REV,
68         PARAM_BYTESPERSECTOR,
69         PARAM_RESET
70 };
71
72 // Must be consistent with the structure defined in the SCSI2SD config.h header.
73 // We always transfer data in network byte order.
74 struct __attribute__((packed)) ConfigPacket 
75 {
76         uint8_t scsiId;
77         char vendor[8];
78         char prodId[16];
79         char revision[4];
80         uint8_t enableParity;
81         uint8_t enableUnitAttention;
82         uint8_t reserved1; // Unused. Ensures maxBlocks is aligned.
83         uint32_t maxSectors;
84         uint16_t bytesPerSector;
85
86
87         // Pad to 64 bytes, which is what we can fit into a USB HID packet.
88         char reserved[26];
89
90         void fromNet()
91         {
92                 maxSectors = ntohl(maxSectors);
93                 bytesPerSector = ntohs(bytesPerSector);
94         }
95         void toNet()
96         {
97                 maxSectors = htonl(maxSectors);
98                 bytesPerSector = htons(bytesPerSector);
99         }
100         void reset()
101         {
102                 scsiId = 0;
103                 strcpy(vendor, " codesrc");
104                 strcpy(prodId, "         SCSI2SD");
105                 strcpy(revision, " 3.5");
106                 enableParity = 1;
107                 enableUnitAttention = 1;
108                 reserved1 = 0;
109                 maxSectors = 0;
110                 bytesPerSector = 512;
111         }
112 };
113
114 static void printConfig(ConfigPacket* packet)
115 {
116         printf("SCSI ID:\t\t\t%d\n", packet->scsiId);
117         printf("Vendor:\t\t\t\t\"%.*s\"\n", 8, packet->vendor);
118         printf("Product ID:\t\t\t\"%.*s\"\n", 16, packet->prodId);
119         printf("Revision:\t\t\t\"%.*s\"\n", 4, packet->revision);
120         printf("\n");
121         printf("Parity Checking:\t\t%s\n", packet->enableParity ? "enabled" : "disabled");
122         printf("Unit Attention Condition:\t%s\n", packet->enableUnitAttention ? "enabled" : "disabled");
123         printf("Bytes per sector:\t\t%d\n", packet->bytesPerSector);
124         if (packet->maxSectors)
125         {
126                 char sizeBuf[64];
127                 uint64_t maxBytes = packet->maxSectors * (uint64_t) packet->bytesPerSector;
128                 if (maxBytes > (1024*1024*1024))
129                 {
130                         sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0));
131                 }
132                 else if (maxBytes > (1024*1024))
133                 {
134                         sprintf(sizeBuf, "%.02fMB", maxBytes / (1024.0*1024.0));
135                 }
136                 else if (maxBytes > (1024))
137                 {
138                         sprintf(sizeBuf, "%.02fKB", maxBytes / (1024.0));
139                 }
140                 else
141                 {
142                         sprintf(sizeBuf, "%" PRIu64 " bytes", maxBytes);
143                 }
144
145                 printf("Maximum Size:\t\t\t%s (%d sectors)\n", sizeBuf, packet->maxSectors);
146         }
147         else
148         {
149                 printf("Maximum Size:\t\t\tUnlimited\n");
150         }
151 }
152
153 static void usage()
154 {
155         printf("Usage: scsi2sd-config [options...]\n");
156         printf("\n");
157         printf("--id={0-7}\tSCSI device ID.\n\n");
158         printf("--parity\tCheck the SCSI parity signal, and reject data where\n");
159         printf("\t\tthe parity is bad.\n\n");
160         printf("--no-parity\tDon't check the SCSI parity signal.\n");
161         printf("\t\tThis is required for SCSI host controllers that do not provide\n");
162         printf("\t\tparity.\n\n");
163         printf("--attention\tRespond with a Unit Attention status on device reset.\n");
164         printf("\t\tSome systems will fail on this response, even though it is\n");
165         printf("\t\trequired by the SCSI-2 standard.\n\n");
166         printf("--no-attention\tDisable Unit Attention responses.\n\n");
167         printf("--blocks={0-4294967295}\n\t\tSet a limit to the reported device size.\n");
168         printf("\t\tThe size of each block/sector is set by the --sector parameter.\n");
169         printf("\t\tThe reported size will be the lower of this value and the SD\n");
170         printf("\t\tcard size. 0 disables the limit.\n");
171         printf("\t\tThe maximum possible size is 2TB.\n\n");
172         printf("--sector={64-8192}\n\t\tSet the bytes-per-sector. Normally 512 bytes.\n");
173         printf("\t\tCan also be set with a SCSI MODE SELECT command.\n\n");
174         printf("--apple\t\tSet the vendor, product ID and revision fields to simulate an \n");
175         printf("\t\tapple-suppled disk. Provides support for the Apple Drive Setup\n");
176         printf("\t\tutility.\n\n");
177         printf("--vendor={vendor}\tSets the reported device vendor. Up to 8 characters.\n\n");
178         printf("--prod-id={prod-id}\tSets the reported product ID. Up to 16 characters.\n\n");
179         printf("--rev={revision}\tSets the reported device revision. Up to 4 characters.\n\n");
180         printf("--reset\tRevert all settings to factory defaults.\n\n");
181         printf("\n");
182         printf("\nThe current configuration settings are displayed if no options are supplied");
183         printf("\n\n");
184         exit(1);
185 }
186
187 int main(int argc, char* argv[])
188 {
189         printf("SCSI2SD Configuration Utility.\n");
190         printf("Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\n\n");
191
192         printf(
193                 "USB device parameters\n\tVendor ID:\t0x%04X\n\tProduct ID:\t0x%04X\n",
194                 HID::VENDOR_ID,
195                 HID::PRODUCT_ID);
196
197         // Enumerate and print the HID devices on the system
198         shared_ptr<HID> scsi2sdHID(HID::Open());
199
200         if (!scsi2sdHID)
201         {
202                 fprintf(stderr, "ERROR: SCSI2SD USB device not found.\n");
203                 exit(1);
204         }
205
206         std::stringstream foundMsg;
207         foundMsg <<
208                 "Device Found\n" <<
209                 "  Firmware Version:\t" << scsi2sdHID->getFirmwareVersionStr();
210
211         std::cout << foundMsg.str() << std::endl;
212
213         ConfigPacket packet;
214         try
215         {
216                 scsi2sdHID->readConfig(
217                         reinterpret_cast<uint8_t*>(&packet),
218                         sizeof(packet)
219                         );
220                 packet.fromNet();
221         }
222         catch (std::exception& e)
223         {
224                 std::cerr << "ERROR: Invalid data received from device.\n" <<
225                         e.what() << std::endl;
226                 exit(1);
227         }
228
229         struct option options[] =
230         {
231                 {
232                         "id", required_argument, NULL, PARAM_ID
233                 },
234                 {
235                         "parity", no_argument, NULL, PARAM_PARITY
236                 },
237                 {
238                         "no-parity", no_argument, NULL, PARAM_NOPARITY
239                 },
240                 {
241                         "attention", no_argument, NULL, PARAM_UNITATT
242                 },
243                 {
244                         "no-attention", no_argument, NULL, PARAM_NOUNITATT
245                 },
246                 {
247                         "blocks", required_argument, NULL, PARAM_MAXBLOCKS
248                 },
249                 {
250                         "apple", no_argument, NULL, PARAM_APPLE
251                 },
252                 {
253                         "vendor", required_argument, NULL, PARAM_VENDOR
254                 },
255                 {
256                         "prod-id", required_argument, NULL, PARAM_PRODID
257                 },
258                 {
259                         "rev", required_argument, NULL, PARAM_REV
260                 },
261                 {
262                         "sector", required_argument, NULL, PARAM_BYTESPERSECTOR
263                 },
264                 {
265                         "reset", no_argument, NULL, PARAM_RESET
266                 },
267                 {
268                         NULL, 0, NULL, 0
269                 }
270         };
271
272         int doWrite = 0;
273         int optIdx = 0;
274         int c;
275         while ((c = getopt_long(argc, argv, "", options, &optIdx)) != -1)
276         {
277                 doWrite = 1;
278                 switch (c)
279                 {
280                 case PARAM_ID:
281                 {
282                         int id = -1;
283                         if (sscanf(optarg, "%d", &id) == 1 && id >= 0 && id <= 7)
284                         {
285                                 packet.scsiId = id;
286                         }
287                         else
288                         {
289                                 usage();
290                         }
291                         break;
292                 }
293
294                 case PARAM_PARITY:
295                         packet.enableParity = 1;
296                         break;
297
298                 case PARAM_NOPARITY:
299                         packet.enableParity = 0;
300                         break;
301
302                 case PARAM_UNITATT:
303                         packet.enableUnitAttention = 1;
304                         break;
305
306                 case PARAM_NOUNITATT:
307                         packet.enableUnitAttention = 0;
308                         break;
309
310                 case PARAM_MAXBLOCKS:
311                 {
312                         int64_t maxSectors = -1;
313                         if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
314                                 maxSectors >= 0 && maxSectors <= UINT32_MAX)
315                         {
316                                 packet.maxSectors = maxSectors;
317                         }
318                         else
319                         {
320                                 usage();
321                         }
322                         break;
323                 }
324
325                 case PARAM_APPLE:
326                         memcpy(packet.vendor, " SEAGATE", 8);
327                         memcpy(packet.prodId, "          ST225N", 16);
328                         memcpy(packet.revision, "1.0 ", 4);
329                         break;
330
331                 case PARAM_VENDOR:
332                         memset(packet.vendor, ' ', 8);
333                         memcpy(packet.vendor, optarg, MIN(strlen(optarg), 8));
334                         break;
335
336                 case PARAM_PRODID:
337                         memset(packet.prodId, ' ', 16);
338                         memcpy(packet.prodId, optarg, MIN(strlen(optarg), 16));
339                         break;
340
341                 case PARAM_REV:
342                         memset(packet.revision, ' ', 4);
343                         memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
344                         break;
345
346                 case PARAM_BYTESPERSECTOR:
347                 {
348                         int64_t bytesPerSector = -1;
349                         if (sscanf(optarg, "%" PRId64, &bytesPerSector) == 1 &&
350                                 bytesPerSector >= 64 && bytesPerSector <= 8192)
351                         {
352                                 packet.bytesPerSector = bytesPerSector;
353                         }
354                         else
355                         {
356                                 usage();
357                         }
358                         break;
359                 }
360                 case PARAM_RESET:
361                         packet.reset();
362                         break;
363
364                 case '?':
365                         usage();
366                 }
367         }
368
369         if (doWrite)
370         {
371                 printf("\nSaving configuration...");
372                 try
373                 {
374                         packet.toNet();
375                         scsi2sdHID->saveConfig(
376                                 reinterpret_cast<uint8_t*>(&packet),
377                                 sizeof(packet));
378                 }
379                 catch (std::exception& e)
380                 {
381                         printf(" Fail.\n");
382                         std::cerr << "ERROR: Failed to save config.\n" << e.what() << std::endl;
383                         exit(1);
384                 }
385                 printf(" Done.\n");
386
387                 sleep(1); // Wait for the data to be saved to eeprom
388
389                 // Clear outstanding stale data
390                 scsi2sdHID->readConfig(
391                         reinterpret_cast<uint8_t*>(&packet),
392                         sizeof(packet));
393
394                 // Proper update
395                 scsi2sdHID->readConfig(
396                         reinterpret_cast<uint8_t*>(&packet),
397                         sizeof(packet));
398                 packet.fromNet();
399         }
400
401         printf("\nCurrent Device Settings:\n");
402         printConfig(&packet);
403
404         return 0;
405 }
406