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