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