Merge Powerbook pcb updates
[SCSI2SD.git] / software / scsi2sd-config / main.c
CommitLineData
7193c6f2
MM
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
37enum
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,
0bb5ed83
MM
48 PARAM_REV,
49 PARAM_BYTESPERSECTOR
7193c6f2
MM
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.
54typedef 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.
0bb5ed83
MM
63 uint32_t maxSectors;
64 uint16_t bytesPerSector;
65
7193c6f2
MM
66
67 // Pad to 64 bytes, which is what we can fit into a USB HID packet.
537ddcc9 68 char reserved[26];
7193c6f2
MM
69} ConfigPacket;
70
71static 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");
0bb5ed83
MM
80 printf("Bytes per sector:\t\t%d\n", packet->bytesPerSector);
81 if (packet->maxSectors)
7193c6f2
MM
82 {
83 char sizeBuf[64];
0bb5ed83 84 uint64_t maxBytes = packet->maxSectors * (uint64_t) packet->bytesPerSector;
7193c6f2
MM
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
0bb5ed83 102 printf("Maximum Size:\t\t\t%s (%d sectors)\n", sizeBuf, packet->maxSectors);
7193c6f2
MM
103 }
104 else
105 {
106 printf("Maximum Size:\t\t\tUnlimited\n");
107 }
108}
109
110static 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);
0bb5ed83
MM
124 packet->maxSectors = ntohl(packet->maxSectors);
125 packet->bytesPerSector = ntohs(packet->bytesPerSector);
7193c6f2
MM
126
127 return result;
128}
129
130static int writeConfig(hid_device* handle, ConfigPacket* packet)
131{
132 unsigned char buf[1 + sizeof(ConfigPacket)];
133 buf[0] = 0; // report ID
134
0bb5ed83
MM
135 packet->maxSectors = htonl(packet->maxSectors);
136 packet->bytesPerSector = htons(packet->bytesPerSector);
7193c6f2 137 memcpy(buf + 1, packet, sizeof(ConfigPacket));
0bb5ed83
MM
138 packet->maxSectors = ntohl(packet->maxSectors);
139 packet->bytesPerSector = ntohs(packet->bytesPerSector);
7193c6f2
MM
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
151static 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");
0bb5ed83
MM
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");
7193c6f2
MM
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
183int 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 },
0bb5ed83
MM
262 {
263 "sector", required_argument, NULL, PARAM_BYTESPERSECTOR
264 },
7193c6f2
MM
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 {
0bb5ed83
MM
310 int64_t maxSectors = -1;
311 if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
312 maxSectors >= 0 && maxSectors <= UINT32_MAX)
7193c6f2 313 {
0bb5ed83 314 packet.maxSectors = maxSectors;
7193c6f2
MM
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
0bb5ed83
MM
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 }
7193c6f2
MM
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