//--------------------------------------------------------------------------- // // SCSI Target Emulator PiSCSI // for Raspberry Pi // // Copyright (C) 2022 Uwe Seimet // // Host Services with realtime clock and shutdown support // //--------------------------------------------------------------------------- // // Features of the host services device: // // 1. Vendor-specific mode page 0x20 returns the current date and time, see mode_page_datetime // // 2. START/STOP UNIT shuts down PiSCSI or shuts down/reboots the Raspberry Pi // a) !start && !load (STOP): Shut down PiSCSI // b) !start && load (EJECT): Shut down the Raspberry Pi // c) start && load (LOAD): Reboot the Raspberry Pi // #include "shared/piscsi_exceptions.h" #include "controllers/scsi_controller.h" #include "scsi_command_util.h" #include "host_services.h" #include using namespace scsi_defs; using namespace scsi_command_util; bool HostServices::Init(const unordered_map& params) { ModePageDevice::Init(params); AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); }); AddCommand(scsi_command::eCmdStartStop, [this] { StartStopUnit(); }); SetReady(true); return true; } void HostServices::TestUnitReady() { // Always successful EnterStatusPhase(); } vector HostServices::InquiryInternal() const { return HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false); } void HostServices::StartStopUnit() const { const bool start = GetController()->GetCmd(4) & 0x01; const bool load = GetController()->GetCmd(4) & 0x02; if (!start) { if (load) { GetController()->ScheduleShutdown(AbstractController::piscsi_shutdown_mode::STOP_PI); } else { GetController()->ScheduleShutdown(AbstractController::piscsi_shutdown_mode::STOP_PISCSI); } } else if (load) { GetController()->ScheduleShutdown(AbstractController::piscsi_shutdown_mode::RESTART_PI); } else { throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } EnterStatusPhase(); } int HostServices::ModeSense6(const vector& cdb, vector& buf) const { // Block descriptors cannot be returned if (!(cdb[1] & 0x08)) { throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } const auto length = static_cast(min(buf.size(), static_cast(cdb[4]))); fill_n(buf.begin(), length, 0); // 4 bytes basic information const int size = AddModePages(cdb, buf, 4, length, 255); buf[0] = (uint8_t)size; return size; } int HostServices::ModeSense10(const vector& cdb, vector& buf) const { // Block descriptors cannot be returned if (!(cdb[1] & 0x08)) { throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } const auto length = static_cast(min(buf.size(), static_cast(GetInt16(cdb, 7)))); fill_n(buf.begin(), length, 0); // 8 bytes basic information const int size = AddModePages(cdb, buf, 8, length, 65535); SetInt16(buf, 0, size); return size; } void HostServices::SetUpModePages(map>& pages, int page, bool changeable) const { if (page == 0x20 || page == 0x3f) { AddRealtimeClockPage(pages, changeable); } } void HostServices::AddRealtimeClockPage(map>& pages, bool changeable) const { pages[32] = vector(10); if (!changeable) { time_t t = time(nullptr); tm localtime; localtime_r(&t, &localtime); mode_page_datetime datetime; datetime.major_version = 0x01; datetime.minor_version = 0x00; datetime.year = (uint8_t)localtime.tm_year; datetime.month = (uint8_t)localtime.tm_mon; datetime.day = (uint8_t)localtime.tm_mday; datetime.hour = (uint8_t)localtime.tm_hour; datetime.minute = (uint8_t)localtime.tm_min; // Ignore leap second for simplicity datetime.second = (uint8_t)(localtime.tm_sec < 60 ? localtime.tm_sec : 59); memcpy(&pages[32][2], &datetime, sizeof(datetime)); } }