Fix scsi2sd-config failures under windows.
[SCSI2SD.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\tEach block is 512 bytes. The maximum possible size is 2TB.\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\n");
169         printf("--sector={64-2048}\n\t\tSet the bytes-per-sector. Normally 512 bytes.\n");
170         printf("\t\tCan also be set with a SCSI MODE SELECT command.\n\n");
171         printf("--apple\t\tSet the vendor, product ID and revision fields to simulate an \n");
172         printf("\t\tapple-suppled disk. Provides support for the Apple Drive Setup\n");
173         printf("\t\tutility.\n\n");
174         printf("--vendor={vendor}\tSets the reported device vendor. Up to 8 characters.\n\n");
175         printf("--prod-id={prod-id}\tSets the reported product ID. Up to 16 characters.\n\n");
176         printf("--rev={revision}\tSets the reported device revision. Up to 4 characters.\n\n");
177         printf("\n");
178         printf("\nThe current configuration settings are displayed if no options are supplied");
179         printf("\n\n");
180         exit(1);
181 }
182
183 int main(int argc, char* argv[])
184 {
185         printf("SCSI2SD Configuration Utility.\n");
186         printf("Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\n\n");
187
188         uint16_t vendorId = 0x04B4; // Cypress
189         uint16_t productId = 0x1337; // SCSI2SD
190
191         printf(
192                 "USB device parameters\n\tVendor ID:\t0x%04X\n\tProduct ID:\t0x%04X\n",
193                 vendorId,
194                 productId);
195
196         // Enumerate and print the HID devices on the system
197         struct hid_device_info *dev = hid_enumerate(vendorId, productId);
198         if (!dev)
199         {
200                 fprintf(stderr, "ERROR: SCSI2SD USB device not found.\n");
201                 exit(1);
202         }
203
204         printf("USB Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls",
205                 dev->vendor_id, dev->product_id, dev->path, dev->serial_number);
206         printf("\n");
207         printf("  Manufacturer: %ls\n", dev->manufacturer_string);
208         printf("  Product:      %ls\n", dev->product_string);
209         printf("\n");
210
211         // Open the device using the VID, PID,
212         // and optionally the Serial number.
213         hid_device* handle = hid_open(vendorId, productId, NULL);
214         if (!handle)
215         {
216                 fprintf(
217                         stderr,
218                         "ERROR: Could not open device %s. Check permissions.\n", dev->path
219                         );
220                 exit(1);
221         }
222
223         ConfigPacket packet;
224         if (readConfig(handle, &packet) <= 0)
225         {
226                 fprintf(stderr, "ERROR: Invalid data received from device.\n");
227                 exit(1);
228         }
229
230         struct option options[] =
231         {
232                 {
233                         "id", required_argument, NULL, PARAM_ID
234                 },
235                 {
236                         "parity", no_argument, NULL, PARAM_PARITY
237                 },
238                 {
239                         "no-parity", no_argument, NULL, PARAM_NOPARITY
240                 },
241                 {
242                         "attention", no_argument, NULL, PARAM_UNITATT
243                 },
244                 {
245                         "no-attention", no_argument, NULL, PARAM_NOUNITATT
246                 },
247                 {
248                         "blocks", required_argument, NULL, PARAM_MAXBLOCKS
249                 },
250                 {
251                         "apple", no_argument, NULL, PARAM_APPLE
252                 },
253                 {
254                         "vendor", required_argument, NULL, PARAM_VENDOR
255                 },
256                 {
257                         "prod-id", required_argument, NULL, PARAM_PRODID
258                 },
259                 {
260                         "rev", required_argument, NULL, PARAM_REV
261                 },
262                 {
263                         "sector", required_argument, NULL, PARAM_BYTESPERSECTOR
264                 },
265                 {
266                         NULL, 0, NULL, 0
267                 }
268         };
269
270         int doWrite = 0;
271         int optIdx = 0;
272         int c;
273         while ((c = getopt_long(argc, argv, "", options, &optIdx)) != -1)
274         {
275                 doWrite = 1;
276                 switch (c)
277                 {
278                 case PARAM_ID:
279                 {
280                         int id = -1;
281                         if (sscanf(optarg, "%d", &id) == 1 && id >= 0 && id <= 7)
282                         {
283                                 packet.scsiId = id;
284                         }
285                         else
286                         {
287                                 usage();
288                         }
289                         break;
290                 }
291
292                 case PARAM_PARITY:
293                         packet.enableParity = 1;
294                         break;
295
296                 case PARAM_NOPARITY:
297                         packet.enableParity = 0;
298                         break;
299
300                 case PARAM_UNITATT:
301                         packet.enableUnitAttention = 1;
302                         break;
303
304                 case PARAM_NOUNITATT:
305                         packet.enableUnitAttention = 0;
306                         break;
307
308                 case PARAM_MAXBLOCKS:
309                 {
310                         int64_t maxSectors = -1;
311                         if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
312                                 maxSectors >= 0 && maxSectors <= UINT32_MAX)
313                         {
314                                 packet.maxSectors = maxSectors;
315                         }
316                         else
317                         {
318                                 usage();
319                         }
320                         break;
321                 }
322
323                 case PARAM_APPLE:
324                         memcpy(packet.vendor, " SEAGATE", 8);
325                         memcpy(packet.prodId, "          ST225N", 16);
326                         memcpy(packet.revision, "1.0 ", 4);
327                         break;
328
329                 case PARAM_VENDOR:
330                         memset(packet.vendor, ' ', 8);
331                         memcpy(packet.vendor, optarg, MIN(strlen(optarg), 8));
332                         break;
333
334                 case PARAM_PRODID:
335                         memset(packet.prodId, ' ', 16);
336                         memcpy(packet.prodId, optarg, MIN(strlen(optarg), 16));
337                         break;
338
339                 case PARAM_REV:
340                         memset(packet.revision, ' ', 4);
341                         memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
342                         break;
343
344                 case PARAM_BYTESPERSECTOR:
345                 {
346                         int64_t bytesPerSector = -1;
347                         if (sscanf(optarg, "%" PRId64, &bytesPerSector) == 1 &&
348                                 bytesPerSector >= 64 && bytesPerSector <= 2048)
349                         {
350                                 packet.bytesPerSector = bytesPerSector;
351                         }
352                         else
353                         {
354                                 usage();
355                         }
356                         break;
357                 }
358                 case '?':
359                         usage();
360                 }
361         }
362
363         if (doWrite)
364         {
365                 printf("Saving configuration...");
366                 if (writeConfig(handle, &packet) <= 0)
367                 {
368                         printf(" Fail.\n");
369                         fprintf(stderr, "ERROR: Failed to save config.\n");
370                         exit(1);
371                 }
372                 printf(" Done.\n");
373
374                 // Clear outstanding stale data
375                 readConfig(handle, &packet);
376
377                 // Proper update
378                 if (readConfig(handle, &packet) <= 0)
379                 {
380                         fprintf(stderr, "ERROR: Invalid data received from device.\n");
381                         exit(1);
382                 }
383         }
384
385         printf("\nCurrent Device Settings:\n");
386         printConfig(&packet);
387
388         return 0;
389 }
390