Fix typo in scsi2sd-config Makefile for building on Mac OSX
[SCSI2SD-V6.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,
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.
53typedef 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
68static 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
106static 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
125static 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
144static 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
174int 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