//--------------------------------------------------------------------------- // // SCSI Target Emulator PiSCSI // for Raspberry Pi // // Copyright (C) 2022 akuker // // [ Hardware version detection routines ] // //--------------------------------------------------------------------------- #include "sbc_version.h" #include "shared/log.h" #include #include #include SBC_Version::sbc_version_type SBC_Version::m_sbc_version = sbc_version_type::sbc_unknown; // TODO: THESE NEED TO BE VALIDATED!!!! const std::string SBC_Version::m_str_raspberry_pi_1 = "Raspberry Pi 1"; const std::string SBC_Version::m_str_raspberry_pi_2_3 = "Raspberry Pi 2/3"; const std::string SBC_Version::m_str_raspberry_pi_4 = "Raspberry Pi 4"; const std::string SBC_Version::m_str_bananapi_m1_plus = "Banana Pi M1 Plus"; const std::string SBC_Version::m_str_bananapi_m2_berry = "Banana Pi M2 Berry/Ultra"; const std::string SBC_Version::m_str_bananapi_m2_zero = "Banana Pi M2 Zero"; const std::string SBC_Version::m_str_bananapi_m2_plus = "Banana Pi BPI-M2-Plus H3"; const std::string SBC_Version::m_str_bananapi_m3 = "Banana Pi M3"; const std::string SBC_Version::m_str_bananapi_m4 = "Banana Pi M4"; const std::string SBC_Version::m_str_bananapi_m5 = "Banana Pi M5"; const std::string SBC_Version::m_str_bananapi_m64 = "Banana Pi M64"; const std::string SBC_Version::m_str_unknown_sbc = "Unknown SBC"; // The strings in this table should align with the 'model' embedded // in the device tree. This can be aquired by running: // cat /proc/device-tree/model // Only the first part of the string is checked. Anything following // will be ignored. For example: // "Raspberry Pi 4 Model B" will match with both of the following: // - Raspberry Pi 4 Model B Rev 1.4 // - Raspberry Pi 4 Model B Rev 1.3 const std::map> SBC_Version::m_proc_device_tree_mapping = { {"Raspberry Pi 1 Model ", sbc_version_type::sbc_raspberry_pi_1}, {"Raspberry Pi 2 Model ", sbc_version_type::sbc_raspberry_pi_2_3}, {"Raspberry Pi 3 Model ", sbc_version_type::sbc_raspberry_pi_2_3}, {"Raspberry Pi 4 Model ", sbc_version_type::sbc_raspberry_pi_4}, {"Raspberry Pi 400 ", sbc_version_type::sbc_raspberry_pi_4}, {"Raspberry Pi Zero W", sbc_version_type::sbc_raspberry_pi_1}, {"Raspberry Pi Zero", sbc_version_type::sbc_raspberry_pi_1}, {"Banana Pi BPI-M2-Zero ", sbc_version_type::sbc_bananapi_m2_zero}, {"Banana Pi BPI-M2-Ultra ", sbc_version_type::sbc_bananapi_m2_berry}, {"Banana Pi BPI-M2-Plus H3", sbc_version_type::sbc_bananapi_m2_plus}, {"Banana Pi M2 Berry ", sbc_version_type::sbc_bananapi_m2_berry}, // sbc_bananapi_m3, TBD.... // sbc_bananapi_m4, }; const std::string SBC_Version::m_device_tree_model_path = "/proc/device-tree/model"; //--------------------------------------------------------------------------- // // Convert the SBC Version to a printable string // //--------------------------------------------------------------------------- const std::string *SBC_Version::GetString() { switch (m_sbc_version) { case sbc_version_type::sbc_raspberry_pi_1: return &m_str_raspberry_pi_1; case sbc_version_type::sbc_raspberry_pi_2_3: return &m_str_raspberry_pi_2_3; case sbc_version_type::sbc_raspberry_pi_4: return &m_str_raspberry_pi_4; case sbc_version_type::sbc_bananapi_m2_berry: return &m_str_bananapi_m2_berry; case sbc_version_type::sbc_bananapi_m2_zero: return &m_str_bananapi_m2_zero; case sbc_version_type::sbc_bananapi_m2_plus: return &m_str_bananapi_m2_plus; case sbc_version_type::sbc_bananapi_m3: return &m_str_bananapi_m3; case sbc_version_type::sbc_bananapi_m4: return &m_str_bananapi_m4; default: LOGERROR("Unknown type of sbc detected: %d", static_cast(m_sbc_version)) return &m_str_unknown_sbc; } } SBC_Version::sbc_version_type SBC_Version::GetSbcVersion() { return m_sbc_version; } //--------------------------------------------------------------------------- // // Determine which version of single board computer (Pi) is being used // based upon the device tree model string. // //--------------------------------------------------------------------------- void SBC_Version::Init() { LOGTRACE("%s", __PRETTY_FUNCTION__) std::string device_tree_model; const std::ifstream input_stream(SBC_Version::m_device_tree_model_path); if (input_stream.fail()) { #if defined(__x86_64__) || defined(__X86__) // We expect this to fail on x86 LOGINFO("Detected device %s", GetString()->c_str()) m_sbc_version = sbc_version_type::sbc_unknown; return; #else LOGERROR("Failed to open %s. Are you running as root?", SBC_Version::m_device_tree_model_path.c_str()) throw std::invalid_argument("Failed to open /proc/device-tree/model"); #endif } std::stringstream str_buffer; str_buffer << input_stream.rdbuf(); device_tree_model = str_buffer.str(); for (const auto &[key, value] : m_proc_device_tree_mapping) { if (device_tree_model.rfind(key, 0) == 0) { m_sbc_version = value; LOGINFO("Detected device %s", GetString()->c_str()) return; } } LOGERROR("%s Unable to determine single board computer type. Defaulting to Raspberry Pi 4", __PRETTY_FUNCTION__) m_sbc_version = sbc_version_type::sbc_raspberry_pi_4; } bool SBC_Version::IsRaspberryPi() { LOGTRACE("%s", __PRETTY_FUNCTION__) switch (m_sbc_version) { case sbc_version_type::sbc_raspberry_pi_1: case sbc_version_type::sbc_raspberry_pi_2_3: case sbc_version_type::sbc_raspberry_pi_4: return true; case sbc_version_type::sbc_bananapi_m2_berry: case sbc_version_type::sbc_bananapi_m2_zero: case sbc_version_type::sbc_bananapi_m2_plus: case sbc_version_type::sbc_bananapi_m3: case sbc_version_type::sbc_bananapi_m4: return false; default: return false; } } bool SBC_Version::IsBananaPi() { LOGTRACE("%s", __PRETTY_FUNCTION__) switch (m_sbc_version) { case sbc_version_type::sbc_raspberry_pi_1: case sbc_version_type::sbc_raspberry_pi_2_3: case sbc_version_type::sbc_raspberry_pi_4: return false; case sbc_version_type::sbc_bananapi_m2_berry: case sbc_version_type::sbc_bananapi_m2_zero: case sbc_version_type::sbc_bananapi_m2_plus: case sbc_version_type::sbc_bananapi_m3: case sbc_version_type::sbc_bananapi_m4: return true; default: return false; } } // The following functions are only used on the Raspberry Pi // (imported from bcm_host.c) uint32_t SBC_Version::GetDeviceTreeRanges(const char *filename, uint32_t offset) { LOGTRACE("%s", __PRETTY_FUNCTION__) uint32_t address = ~0; if (FILE *fp = fopen(filename, "rb"); fp) { fseek(fp, offset, SEEK_SET); if (std::array buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) { address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0; } fclose(fp); } return address; } #if defined __linux__ uint32_t SBC_Version::GetPeripheralAddress(void) { LOGTRACE("%s", __PRETTY_FUNCTION__) uint32_t address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 4); if (address == 0) { address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 8); } address = (address == (uint32_t)~0) ? 0x20000000 : address; LOGDEBUG("Peripheral address : 0x%8x\n", address) return address; } #elif defined __NetBSD__ uint32_t SBC_Version::GetPeripheralAddress(void) { char buf[1024]; size_t len = sizeof(buf); uint32_t address; if (sysctlbyname("hw.model", buf, &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf) { // Failed to get CPU model || Not BCM2835 // use the address of BCM283[67] address = 0x3f000000; } else { // Use BCM2835 address address = 0x20000000; } LOGDEBUG("Peripheral address : 0x%lx\n", address); return address; } #else uint32_t SBC_Version::GetPeripheralAddress(void) { return 0; } #endif