diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Device/Device.cpp | 4 | ||||
-rw-r--r-- | src/Device/Device.cpp.orig | 630 |
2 files changed, 632 insertions, 2 deletions
diff --git a/src/Device/Device.cpp b/src/Device/Device.cpp index d52fa93..3f7cf27 100644 --- a/src/Device/Device.cpp +++ b/src/Device/Device.cpp @@ -250,7 +250,7 @@ void Device::load() { mapFileToCapability(); mapFirmware(); mapMacAddress2(); - for (const auto capability : capabilityList) { + for (const auto &capability : capabilityList) { capabilities.AddMember( rapidjson::Value().SetString(capability.first.c_str(), capability.first.length(), acAlloc), @@ -296,7 +296,7 @@ void Device::load() { ethSwitch.AddMember("ports", std::move(ports), ethSwitchAlloc); } - for (const auto device : deviceInfoList) { + for (const auto &device : deviceInfoList) { deviceInfo.AddMember( rapidjson::Value().SetString(device.first.c_str(), device.first.length(), alloc), diff --git a/src/Device/Device.cpp.orig b/src/Device/Device.cpp.orig new file mode 100644 index 0000000..d52fa93 --- /dev/null +++ b/src/Device/Device.cpp.orig @@ -0,0 +1,630 @@ +/********************************************************************** + * COPYRIGHT 2020 MULTI-TECH SYSTEMS, INC. + * + * ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF + * MULTI-TECH SYSTEMS, INC. + * + * MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY + * INFORMATION AND/OR TRADE SECRET. + * + * NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION, + * DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL + * INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC. + * USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A + * WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED. + * + ***********************************************************************/ + +#include <gpiod.h> +#include "Device.h" + +const std::vector<std::string> Device::apIdentifiers = { + "ap1", "ap2", "lora", "lora-2", "slot1", "slot2"}; +const std::regex + Device::apFilters("(modalias)|(power)(.*)|(subsystem)|(uevent)"); +const std::regex Device::serialModeFilter("(.*)(serial-mode)"); +const std::regex Device::mtcdt3HwVersionFilters("(MTCDT3AC)(.*)"); +const std::regex Device::mtrFilters("(MTR-)(.*)"); +const std::regex Device::storeFilters( + "(.*)(mac-)(.*)|(.*)(-id)|(uuid)|(.*)(/eui)|(.*)(/" + "cdone)|(.*)(hw-version)|(oem-string)(.*)|(imei)|(capability)(.*)|(radio-" + "reset-backoff-seconds)|(modalias)|(power)|((subsystem)(.*))|(uevent)|(" + "board-temperature)|(reset)|(led3)|(led-ls)|(usbhd-ps-oc)|(.*)(adc[0-9])|(." + "*)(din[0-9])|(gpi[0-9])|(gpi[0-9][0-9])"); +const std::regex Device::showFilters("(modalias)|(subsystem)|(uevent)"); + +std::map<std::string, bool> Device::capabilityList = { + {"adc", false}, + {"battery", false}, + {"bluetooth", false}, + {"cell", false}, + {"cellPpp", false}, + {"cellWwan", false}, + {"din", false}, + {"dout", false}, + {"externalSerialPort", false}, + {"gpio", false}, + {"gps", false}, + {"lora", false}, + {"loraLbt", false}, + {"loraNetworkServer", false}, + {"nodeRed", false}, + {"rs232", false}, + {"rs422", false}, + {"rs485", false}, + {"serial", false}, + {"supercap", false}, + {"wifi", false}, + {"docker", false}, + {"tpm", false}, + {"userDataEncryption", false}}; + +std::map<std::string, std::string> Device::ethSwitchList; + +std::map<std::string, std::string> Device::deviceInfoList = { + {"deviceId", ""}, + {"hardwareVersion", ""}, + {"imei", ""}, + {"macAddress", "00:00:00:00:00:00"}, + {"macBluetooth", "00:00:00:00:00:00"}, + {"macWifi", "00:00:00:00:00:00"}, + {"productId", ""}, + {"uuid", ""}, + {"vendorId", ""}, + {"oemString1", ""}, + {"oemString2", ""}}; + +Device::Device() { + isRoot = !getuid(); + accessoryCardsList.reserve(accessoryCardsListSize); + accessoryCardsList.push_back(std::make_unique<Lora21Card>(*this)); + accessoryCardsList.push_back(std::make_unique<Lora21ExtCard>(*this)); + accessoryCardsList.push_back(std::make_unique<Lora03Card>(*this)); + accessoryCardsList.push_back(std::make_unique<Lora10Card>(*this)); + accessoryCardsList.push_back(std::make_unique<Lora15Card>(*this)); + accessoryCardsList.push_back(std::make_unique<Lora2G4Card>(*this)); + accessoryCardsList.push_back(std::make_unique<Gpiob>(*this)); + accessoryCardsList.push_back(std::make_unique<Mfser>(*this)); +} + +bool Device::isAccessoryCard(const char *d_name, const char *dir_name) { + return std::binary_search(apIdentifiers.begin(), apIdentifiers.end(), + dir_name) && + !regex_match(d_name, apFilters); +} + +void Device::sortAccessoryCards() { + rapidjson::SizeType i, j; + for (i = 0; i < accessoryCards.Size() - 1; i++) { + for (j = 0; j < accessoryCards.Size() - i - 1; j++) { + if (accessoryCards[j]["port"].GetString() < + accessoryCards[j + 1]["port"].GetString()) { + accessoryCards[j].Swap(accessoryCards[j + 1]); + } + } + } +} + +bool Device::isValidDirectory(const struct dirent *entry, std::string fullPath, + const char *d_name) { + std::string path = fullPath + "/" + std::string(d_name); + return (entry->d_type & DT_DIR) && strcmp(d_name, "..") != 0 && + strcmp(d_name, ".") != 0 && (path.length() < PATH_MAX); +} + +void Device::getSystemTreeJson(const char *dir_name) { + std::string fullPath = SYSFS_PLATFORM + std::string(dir_name); + DIR *d = opendir(fullPath.c_str()); + + if (!d) { + printError("Cannot open directory %s", fullPath); + exitHandler(99); + } + while (1) { + struct dirent *entry = + readdir(d); /* Gets subsequent entries from "d" */ + if (!entry) { /* If there are no more entries, exit */ + break; + } + const char *d_name = entry->d_name; /* Get file name */ + std::string fileData; + if (!(entry->d_type & DT_DIR) && + (MTS::System::readFile(fullPath + "/" + std::string(d_name), + fileData) == 0)) { + fileData = MTS::Text::trim(fileData); + if (strlen(dir_name) > 0) { + if (isAccessoryCard(d_name, dir_name)) { + if (accessoryCard.IsNull()) { + accessoryCard.SetObject(); + accessoryCard.AddMember( + "port", + rapidjson::Value().SetString(dir_name, acAlloc), + acAlloc); + } else if (accessoryCard["port"] != dir_name) { + accessoryCards.PushBack(accessoryCard, acAlloc); + accessoryCard.SetObject(); + accessoryCard.AddMember( + "port", + rapidjson::Value().SetString(dir_name, acAlloc), + acAlloc); + } + if (strcmp(d_name, "product-id") == + 0) { /* Map card specific details based on the product + id */ + for (unsigned int i = 0; i < accessoryCardsList.size(); + i++) { + if (regex_match(fileData, + accessoryCardsList[i]->GetName())) { + accessoryCardsList[i]->AddToDeviceInfo( + dir_name, fileData); + break; + } + } + } + accessoryCard.AddMember( + rapidjson::Value().SetString( + toCamelCase(d_name).c_str(), acAlloc), + rapidjson::Value().SetString(fileData.c_str(), acAlloc), + acAlloc); + } else if (strcmp(dir_name, "capability") == 0 && + fileData == "1") { + capabilityList[toCamelCase(d_name)] = true; + } else if (strcmp(dir_name, "eth-switch") == 0) { + ethSwitchList[toCamelCase(d_name)] = fileData; + } + } else if ((entry->d_type != DT_LNK)) { + if (deviceInfoList.count(toCamelCase(d_name)) > 0) { + deviceInfoList[toCamelCase(d_name)] = fileData; + } else if (strcmp(d_name, "hw-version") == 0) { + deviceInfoList["hardwareVersion"] = fileData; + if (regex_match(fileData, mtcdt3HwVersionFilters)) { + capabilityList["rs232"] = true; + capabilityList["rs422"] = true; + capabilityList["rs485"] = true; + capabilityList["serial"] = true; + } + } else if (strcmp(d_name, "mac-eth") == 0) { + deviceInfoList["macAddress"] = fileData; + } else if (strcmp(d_name, "has-radio") == 0 && + fileData == "1") { + capabilityList["cell"] = true; + } else if (strcmp(d_name, "oem-string1") == 0) { + deviceInfoList["oemString1"] = fileData; + } else if (strcmp(d_name, "oem-string2") == 0) { + deviceInfoList["oemString2"] = fileData; + }; + if (strcmp(d_name, "product-id") == 0) { + if (regex_match(fileData, mtrFilters)) { + capabilityList["rs232"] = true; + capabilityList["serial"] = true; + } + } + } + } + if (isValidDirectory(entry, fullPath, + d_name)) { /* Check that the directory is not "d" + or d's parent */ + getSystemTreeJson(d_name); /* Recursively call with the new path */ + } + } + if (closedir( + d)) { /* After going through all the entries, close the directory */ + printError("Could not close %s", fullPath); + exitHandler(errno); + } +} + +void Device::init() { + if (!isRoot) { + printError("Must be root to generate device_info.json"); + exitHandler(99); + } + load(); + writeJson(); + exitHandler(0); +} + +void Device::json() { + if (!isRoot) { + printError("Must be root to generate json"); + exitHandler(99); + } + load(); + printJson(); + exitHandler(0); +} + +void Device::load() { + deviceInfo.SetObject(); + capabilities.SetObject(); + ethSwitch.SetObject(); + accessoryCards.SetArray(); + getSystemTreeJson(""); + if (!accessoryCard.IsNull()) { + accessoryCards.PushBack(accessoryCard, acAlloc); + if (accessoryCards.Size() > 1) { + sortAccessoryCards(); + } + } + + mapFileToCapability(); + mapFirmware(); + mapMacAddress2(); + for (const auto capability : capabilityList) { + capabilities.AddMember( + rapidjson::Value().SetString(capability.first.c_str(), + capability.first.length(), acAlloc), + capability.second, acAlloc); + } + + if (ethSwitchList.count("chip") && ethSwitchList.count("numPorts")) { + auto ðSwitchAlloc = ethSwitch.GetAllocator(); + const std::string &chip = ethSwitchList.at("chip"); + ethSwitch.AddMember("chip", + rapidjson::Value().SetString( + chip.c_str(), chip.length(), ethSwitchAlloc), + ethSwitchAlloc); + rapidjson::Value ports; + ports.SetObject(); + for (int i = 0; i < std::stoi(ethSwitchList.at("numPorts")); ++i) { + const std::string num = std::to_string(i); + const std::string keyLabel = "port" + num + "Label"; + const std::string keyId = "port" + num + "Id"; + const std::string keyMac = "port" + num + "Mac"; + ; + if (ethSwitchList.count(keyLabel) && ethSwitchList.count(keyId) && + ethSwitchList.count(keyMac)) { + rapidjson::Value port; + port.SetObject(); + port.AddMember("id", + rapidjson::Value().SetInt( + std::stoi(ethSwitchList.at(keyId))), + ethSwitchAlloc); + port.AddMember("mac", + rapidjson::Value().SetString( + ethSwitchList.at(keyMac).c_str(), + ethSwitchList.at(keyMac).length(), + ethSwitchAlloc), + ethSwitchAlloc); + ports.AddMember(rapidjson::Value().SetString( + ethSwitchList.at(keyLabel).c_str(), + ethSwitchList.at(keyLabel).length(), + ethSwitchAlloc), + std::move(port), ethSwitchAlloc); + } + } + ethSwitch.AddMember("ports", std::move(ports), ethSwitchAlloc); + } + + for (const auto device : deviceInfoList) { + deviceInfo.AddMember( + rapidjson::Value().SetString(device.first.c_str(), + device.first.length(), alloc), + rapidjson::Value().SetString(device.second.c_str(), + device.second.length(), alloc), + alloc); + } + + deviceInfo.AddMember("capabilities", capabilities, alloc); + deviceInfo.AddMember("ethSwitch", ethSwitch, alloc); + deviceInfo.AddMember("accessoryCards", accessoryCards, alloc); +} + +void Device::mapFileToCapability() { + if (fileType("/opt/node-red") == S_IFDIR) { /* node-red is a directory */ + capabilityList["nodeRed"] = true; + } + if (fileType("/opt/lora/lora-network-server") == + S_IFREG) { /* lora-network-server is a regular file */ + capabilityList["loraNetworkServer"] = true; + } + if (capabilityList["cell"]) { /* only on devices with Cellular */ + if (fileType("/dev/cdc-wdm0") == S_IFCHR) { + /* Cellular modem is wwan/qmi/mbim character device */ + capabilityList["cellWwan"] = true; + } + if (!fileExists("/dev/modem_at0") && fileType("/dev/modem_at1") == S_IFCHR) { + /* Cellular modem without PPP support, probably uses ECM WWAN */ + capabilityList["cellWwan"] = true; + } else { + /* all other radios - assume PPP is supported */ + capabilityList["cellPpp"] = true; + } + } + if (fileType("/dev/ext_serial") == + S_IFCHR) { /* ext_serial is a character device */ + capabilityList["externalSerialPort"] = true; + } + if (fileType("/usr/bin/dockerd") == + S_IFREG) { /* Docker is a regular file */ + capabilityList["docker"] = true; + } + if (findFileGlob("/dev/tpm*") == S_IFCHR) { /* tpm* is a character device */ + capabilityList["tpm"] = true; + } +} + +void Device::mapMacAddress2() { + std::ifstream file("/sys/devices/platform/mts-io/base/mac-eth"); + if (file.is_open()) { + std::string line = ""; + std::getline(file, line); + deviceInfoList["macAddress1"] = line; + } +} + +void Device::mapFirmware() { + std::ifstream file(FIRMWARE_FILE); + if (!file.is_open()) { + return; + } + std::string line; + while (std::getline(file, line)) { + if (line.find(FIRMWARE_VERSION) != std::string::npos) { + deviceInfoList["firmware"] = line.substr(line.find(" ") + 1); + } else if (line.find(FIRMWARE_DATE) != std::string::npos) { + deviceInfoList["firmwareDate"] = line.substr(line.find(" ") + 1); + } else if (line.find(FIRMWARE_RELEASE) != std::string::npos) { + deviceInfoList["firmwareRelease"] = line.substr(line.find(" ") + 1); + } + } +} + +void Device::printDir(const std::string dir_name, + std::vector<std::string> &results) { + std::string fullPath = SYSFS_PLATFORM + std::string(dir_name); + DIR *d = opendir(fullPath.c_str()); + if (!d) { + printError("Cannot open directory %s", fullPath); + exitHandler(99); + } + while (1) { + struct dirent *entry = readdir(d); + if (!entry) { + break; + } + const char *d_name = entry->d_name; + if (!(entry->d_type & DT_DIR)) { + std::string result; + if (dir_name.size() > 0) { + result = (dir_name + "/"); + } + result.append(d_name); + results.push_back(result); + } + if (isValidDirectory(entry, fullPath, + d_name)) { /* Check that the directory is not "d" + or d's parent. */ + printDir(d_name, results); + } + } + if (closedir(d)) { /* After going through all the entries, close the + directory. */ + printError("Could not close %s", fullPath); + exitHandler(errno); + } +} + +void Device::printJson() { + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + deviceInfo.Accept(writer); + std::cout << buffer.GetString(); +} + +void Device::printVersion(std::string name) { + printInfo( + "%s %s\nCopyright (C) 2022 by Multi-Tech Systems\nThis program is free " + "software; you may redistribute it under the terms of\nthe GNU General " + "Public License version 2 or (at your option) any later version.\nThis " + "program has absolutely no warranty.", + name.c_str(), Version::version.c_str()); + exitHandler(0); +} + +void Device::printUsage(std::string program) { + std::vector<std::string> showResults; + printf("Usage: %s [ OPTIONS ] OBJECT [--] [ ARGUMENTS ]\n", program.c_str()); + printf("Legacy OBJECT options:\n"); + printf(" init : init & make device info json\n"); + printf(" json : init & make device info json\n"); + printf(" show : show data for file\n"); + printf(" show-trigger : show trigger data for file\n"); + printf(" store : store data for file\n"); + printf(" store-trigger : store trigger data for file\n"); + printf(" load-fpga : load fpga version\n"); + printf("Updated OBJECT options:\n"); + printf(" -I, --init : init & make device info json\n"); + printf(" -j, --json : output device info json\n"); + printf(" -s, --show : show data for file\n"); + printf(" -S, --show-trigger : show trigger data for file\n"); + printf(" -t, --store : store data for file\n"); + printf(" -T, --store-trigger : store trigger data for file\n"); + printf(" -l, --load-fpga : update fpga version\n"); + printf(" -c, --check : check fpga version\n"); + printf(" -v, --version : show version\n"); + printf(" -h, --help : show help\n"); + printf("\n"); + printf("Options\n"); + printf(" -V, --verbose : show additional debug output\n"); + printf("FPGA\n"); + printf("Arguments:\n"); + printf(" -p, --path : path for card\n"); + printf(" : options: 1 for ap1, 2 for ap2 everything else\n"); + printf(" : is an expected full path. i.e. /dev/spidevX.X\n"); + printf(" -i, --input : input file for upgrade\n"); + printf(" : files are stored in /usr/lib/mts-flash-binaries\n"); + printf("Usage:\n"); + printf(" Load : mts-io-sysfs -l -p <path> -i <file>\n"); + printf(" Check : mts-io-sysfs -c -p <path>\n"); + printf("\n"); + printf("show-name:\n"); + printf("Usage:\n"); + printf("--show-name <name>\n"); + printDir("", showResults); + sort(showResults.begin(), + showResults.end()); // Unix file tree is not sorted + + for (std::string showResult : showResults) { + if (!regex_match(showResult, showFilters)) + printf(" %s\n", showResult.c_str()); + } + printf("\n"); + printf("store-name:\n"); + printf("Usage:\n"); + printf("--store-name <name> <value>\n"); + for (std::string showResult : showResults) { + if (showResult == "radio-reset") { + printf(" %s { 0 }\n", showResult.c_str()); + } else if (showResult == "reset-monitor") { + printf(" %s { pid short-signal long-signal " + "[extra-long-signal] }\n", + showResult.c_str()); + } else if (showResult == "reset-monitor-intervals") { + printf(" %s { short-interval long-interval }\n", + showResult.c_str()); + } else if (regex_match(showResult, serialModeFilter)) { + printf(" %s { loopback | rs232 | rs485-half | " + "rs422-485-full }\n", + showResult.c_str()); + } else if (!regex_match(showResult, storeFilters)) { + printf(" %s BOOLEAN\n", showResult.c_str()); + } + } + printf("\n"); + exitHandler(0); +} + +/* If the name starts in upper case, assume + * the name is a GPIO line name from gpio-hog + * in device tree. If it starts in lower case + * assume the name is an attribute of the mts-io + * driver. GPIOs can only be zero + * or one, never a text string. + */ +void Device::show(std::string name) { + std::string fileData; + char chipname[64]; + unsigned int offset; + int retval; + + if(isupper(name[0])) { + retval = gpiod_ctxless_find_line(name.c_str(), chipname, + sizeof chipname,&offset); + if(retval != -1) + retval = gpiod_ctxless_get_value(chipname, offset, + false, CMDNAME); + if(retval != -1) { + if(retval == 0) + printf("0\n"); + else + printf("1\n"); + exitHandler(0); + } + } + + int32_t code = MTS::System::readFile(SYSFS_PLATFORM + name, fileData); + if (code == 0) { + printf("%s", fileData.c_str()); + exitHandler(0); + } + printError("cat: can't open %s%s: %s", + SYSFS_PLATFORM, name.c_str(),strerror(errno)); + exitHandler(99); +} + +void Device::showTrigger(std::string name) { + std::string fileData; + int32_t code = + MTS::System::readFile(LEDS_GPIO_DIR + name + "/trigger", fileData); + if (code == 0) { + printf("%s", fileData.c_str()); + exitHandler(0); + } + printError("Can't open %s%s/trigger: %s", + LEDS_GPIO_DIR, name.c_str(),strerror(errno)); + exitHandler(99); +} + +// Prefer gpiod if name starts with upper case and is value 0 or 1 +void Device::store(std::string name, std::string value) { + int intvalue = -1; + int prefergpiod = 0; + char chipname[64]; + unsigned int offset; + int retval; + + if (!isRoot) { + printError("Must be root to store to %s", name.c_str()); + exitHandler(99); + } + printDebug("Setting %s to %s", name.c_str(), value.c_str()); + if(isupper(name[0])) { + if (value.compare("0") == 0) + intvalue = 0; + if (value.compare("1") == 0) + intvalue = 1; + if ((intvalue != -1)) + prefergpiod = 1; + } + + if(prefergpiod) { + retval = gpiod_ctxless_find_line(name.c_str(), chipname, + sizeof chipname,&offset); + if(retval != -1) + retval = gpiod_ctxless_set_value(chipname, offset, intvalue, + false, CMDNAME, NULL, NULL); + if(retval != -1) + exitHandler(0); + } + + std::ofstream fileToWrite(SYSFS_PLATFORM + name); + if (fileToWrite.is_open()) { + fileToWrite << value; + if (fileToWrite.bad()) { + printError("Can't write %s%s: %s", SYSFS_PLATFORM, + name.c_str(),strerror(errno)); + exitHandler(98); + } + fileToWrite.close(); + exitHandler(0); + } + printError("Can't open %s%s: %s", SYSFS_PLATFORM, + name.c_str(),strerror(errno)); + exitHandler(99); +} + +void Device::storeTrigger(std::string name, std::string value) { + if (!isRoot) { + printError("Must be root to storeTrigger to %s/trigger", name.c_str()); + exitHandler(99); + } + printDebug("Setting %s to %s", name.c_str(), value.c_str()); + std::ofstream fileToWrite(LEDS_GPIO_DIR + name + "/trigger"); + if (fileToWrite.is_open()) { + fileToWrite << value; + if(fileToWrite.bad()) { + printError("Can't write %s%s/trigger: %s", + LEDS_GPIO_DIR, name.c_str(), strerror(errno)); + exitHandler(98); + } + fileToWrite.close(); + exitHandler(0); + } + printError("Can't open %s%s/trigger: %s", + LEDS_GPIO_DIR, name.c_str(), strerror(errno)); + exitHandler(99); +} + +void Device::writeJson() { + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + deviceInfo.Accept(writer); + std::ofstream os(DEVICE_INFO_FILE); + if (!os) { + printError("Can't write to %s: %s", DEVICE_INFO_FILE,strerror(errno)); + exitHandler(99); + } else { + os << buffer.GetString(); + } +} |