// Copyright (C) 2013 Michael McMaster // // This file is part of SCSI2SD. // // SCSI2SD is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // SCSI2SD is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SCSI2SD. If not, see . #include "SCSI2SD_HID.hh" #if __cplusplus >= 201103L #include using std::shared_ptr; #else #include using std::tr1::shared_ptr; #endif #include #include #include // Request extended stdio format macros. #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include // htonl/ntohl includes. #ifdef _WIN32 #include #else #include #endif #include "hidapi.h" #define MIN(a,b) (a < b ? a : b) using namespace SCSI2SD; enum { PARAM_ID, PARAM_PARITY, PARAM_NOPARITY, PARAM_UNITATT, PARAM_NOUNITATT, PARAM_MAXBLOCKS, PARAM_APPLE, PARAM_VENDOR, PARAM_PRODID, PARAM_REV, PARAM_BYTESPERSECTOR, PARAM_RESET }; // Must be consistent with the structure defined in the SCSI2SD config.h header. // We always transfer data in network byte order. struct __attribute__((packed)) ConfigPacket { uint8_t scsiId; char vendor[8]; char prodId[16]; char revision[4]; uint8_t enableParity; uint8_t enableUnitAttention; uint8_t reserved1; // Unused. Ensures maxBlocks is aligned. uint32_t maxSectors; uint16_t bytesPerSector; // Pad to 64 bytes, which is what we can fit into a USB HID packet. char reserved[26]; void fromNet() { maxSectors = ntohl(maxSectors); bytesPerSector = ntohs(bytesPerSector); } void toNet() { maxSectors = htonl(maxSectors); bytesPerSector = htons(bytesPerSector); } void reset() { scsiId = 0; strcpy(vendor, " codesrc"); strcpy(prodId, " SCSI2SD"); strcpy(revision, " 3.5"); enableParity = 1; enableUnitAttention = 1; reserved1 = 0; maxSectors = 0; bytesPerSector = 512; } }; static void printConfig(ConfigPacket* packet) { std::cout << "SCSI ID:\t\t\t" << static_cast(packet->scsiId) << "\n" << "Vendor:\t\t\t\t\"" << std::string(packet->vendor, 8) << "\"\n" << "Product ID:\t\t\t\"" << std::string(packet->prodId, 16) << "\"\n" << "Revision:\t\t\t\"" << std::string(packet->revision, 4) << "\"\n" << "\n" << "Parity Checking:\t\t" << (packet->enableParity ? "enabled" : "disabled") << "\n" << "Unit Attention Condition:\t" << (packet->enableUnitAttention ? "enabled" : "disabled") << "\n" << "Bytes per sector:\t\t" << packet->bytesPerSector << std::endl; if (packet->maxSectors) { char sizeBuf[64]; uint64_t maxBytes = packet->maxSectors * (uint64_t) packet->bytesPerSector; if (maxBytes > (1024*1024*1024)) { sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0)); } else if (maxBytes > (1024*1024)) { sprintf(sizeBuf, "%.02fMB", maxBytes / (1024.0*1024.0)); } else if (maxBytes > (1024)) { sprintf(sizeBuf, "%.02fKB", maxBytes / (1024.0)); } else { sprintf(sizeBuf, "%" PRIu64 " bytes", maxBytes); } std::cout <<"Maximum Size:\t\t\t" << sizeBuf << " (" << packet->maxSectors << " sectors)" << std::endl; } else { std::cout << "Maximum Size:\t\t\tUnlimited" << std::endl; } } static void usage() { printf("Usage: scsi2sd-config [options...]\n"); printf("\n"); printf("--id={0-7}\tSCSI device ID.\n\n"); printf("--parity\tCheck the SCSI parity signal, and reject data where\n"); printf("\t\tthe parity is bad.\n\n"); printf("--no-parity\tDon't check the SCSI parity signal.\n"); printf("\t\tThis is required for SCSI host controllers that do not provide\n"); printf("\t\tparity.\n\n"); printf("--attention\tRespond with a Unit Attention status on device reset.\n"); printf("\t\tSome systems will fail on this response, even though it is\n"); printf("\t\trequired by the SCSI-2 standard.\n\n"); printf("--no-attention\tDisable Unit Attention responses.\n\n"); printf("--blocks={0-4294967295}\n\t\tSet a limit to the reported device size.\n"); printf("\t\tThe size of each block/sector is set by the --sector parameter.\n"); printf("\t\tThe reported size will be the lower of this value and the SD\n"); printf("\t\tcard size. 0 disables the limit.\n"); printf("\t\tThe maximum possible size is 2TB.\n\n"); printf("--sector={64-8192}\n\t\tSet the bytes-per-sector. Normally 512 bytes.\n"); printf("\t\tCan also be set with a SCSI MODE SELECT command.\n\n"); printf("--apple\t\tSet the vendor, product ID and revision fields to simulate an \n"); printf("\t\tapple-suppled disk. Provides support for the Apple Drive Setup\n"); printf("\t\tutility.\n\n"); printf("--vendor={vendor}\tSets the reported device vendor. Up to 8 characters.\n\n"); printf("--prod-id={prod-id}\tSets the reported product ID. Up to 16 characters.\n\n"); printf("--rev={revision}\tSets the reported device revision. Up to 4 characters.\n\n"); printf("--reset\tRevert all settings to factory defaults.\n\n"); printf("\n"); printf("\nThe current configuration settings are displayed if no options are supplied"); printf("\n\n"); exit(1); } int main(int argc, char* argv[]) { printf("SCSI2SD Configuration Utility.\n"); printf("Copyright (C) 2013 Michael McMaster \n\n"); printf( "USB device parameters\n\tVendor ID:\t0x%04X\n\tProduct ID:\t0x%04X\n", HID::VENDOR_ID, HID::PRODUCT_ID); // Enumerate and print the HID devices on the system shared_ptr scsi2sdHID(HID::Open()); if (!scsi2sdHID) { fprintf(stderr, "ERROR: SCSI2SD USB device not found.\n"); exit(1); } std::stringstream foundMsg; foundMsg << "Device Found\n" << " Firmware Version:\t" << scsi2sdHID->getFirmwareVersionStr(); std::cout << foundMsg.str() << std::endl; ConfigPacket packet; try { scsi2sdHID->readConfig( reinterpret_cast(&packet), sizeof(packet) ); packet.fromNet(); } catch (std::exception& e) { std::cerr << "ERROR: Invalid data received from device.\n" << e.what() << std::endl; exit(1); } struct option options[] = { { "id", required_argument, NULL, PARAM_ID }, { "parity", no_argument, NULL, PARAM_PARITY }, { "no-parity", no_argument, NULL, PARAM_NOPARITY }, { "attention", no_argument, NULL, PARAM_UNITATT }, { "no-attention", no_argument, NULL, PARAM_NOUNITATT }, { "blocks", required_argument, NULL, PARAM_MAXBLOCKS }, { "apple", no_argument, NULL, PARAM_APPLE }, { "vendor", required_argument, NULL, PARAM_VENDOR }, { "prod-id", required_argument, NULL, PARAM_PRODID }, { "rev", required_argument, NULL, PARAM_REV }, { "sector", required_argument, NULL, PARAM_BYTESPERSECTOR }, { "reset", no_argument, NULL, PARAM_RESET }, { NULL, 0, NULL, 0 } }; int doWrite = 0; int optIdx = 0; int c; while ((c = getopt_long(argc, argv, "", options, &optIdx)) != -1) { doWrite = 1; switch (c) { case PARAM_ID: { int id = -1; if (sscanf(optarg, "%d", &id) == 1 && id >= 0 && id <= 7) { packet.scsiId = id; } else { usage(); } break; } case PARAM_PARITY: packet.enableParity = 1; break; case PARAM_NOPARITY: packet.enableParity = 0; break; case PARAM_UNITATT: packet.enableUnitAttention = 1; break; case PARAM_NOUNITATT: packet.enableUnitAttention = 0; break; case PARAM_MAXBLOCKS: { int64_t maxSectors = -1; if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 && maxSectors >= 0 && maxSectors <= 0xffffffff) { packet.maxSectors = maxSectors; } else { usage(); } break; } case PARAM_APPLE: memcpy(packet.vendor, " SEAGATE", 8); memcpy(packet.prodId, " ST225N", 16); memcpy(packet.revision, "1.0 ", 4); break; case PARAM_VENDOR: memset(packet.vendor, ' ', 8); memcpy(packet.vendor, optarg, MIN(strlen(optarg), 8)); break; case PARAM_PRODID: memset(packet.prodId, ' ', 16); memcpy(packet.prodId, optarg, MIN(strlen(optarg), 16)); break; case PARAM_REV: memset(packet.revision, ' ', 4); memcpy(packet.revision, optarg, MIN(strlen(optarg), 4)); break; case PARAM_BYTESPERSECTOR: { int64_t bytesPerSector = -1; if (sscanf(optarg, "%" PRId64, &bytesPerSector) == 1 && bytesPerSector >= 64 && bytesPerSector <= 8192) { packet.bytesPerSector = bytesPerSector; } else { usage(); } break; } case PARAM_RESET: packet.reset(); break; case '?': doWrite = 0; usage(); } } if (doWrite) { printf("\nSaving configuration..."); try { packet.toNet(); scsi2sdHID->saveConfig( reinterpret_cast(&packet), sizeof(packet)); } catch (std::exception& e) { printf(" Fail.\n"); std::cerr << "ERROR: Failed to save config.\n" << e.what() << std::endl; exit(1); } printf(" Done.\n"); #ifdef _WIN32 Sleep(1000); //ms #else sleep(1); // Wait for the data to be saved to eeprom #endif // Clear outstanding stale data scsi2sdHID->readConfig( reinterpret_cast(&packet), sizeof(packet)); // Proper update scsi2sdHID->readConfig( reinterpret_cast(&packet), sizeof(packet)); packet.fromNet(); } printf("\nCurrent Device Settings:\n"); printConfig(&packet); return 0; }