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