/********************************************************************** * 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 #include "Device.h" const std::string cmdname ("mts-io-sysfs"); const std::vector 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::iotRtrVersionFilters("(IOTR)(.*)"); 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 Device::capabilityList = { {"adc", false}, {"battery", false}, {"bluetooth", false}, {"cell", false}, {"din", false}, {"dout", false}, {"gpio", false}, {"gps", false}, {"lora", false}, {"loraLbt", false}, {"loraNetworkServer", false}, {"nodeRed", false}, {"serial", false}, {"supercap", false}, {"wifi", false}, {"docker", false}, {"tpm", false}, {"userDataEncryption", false}}; std::map Device::ethSwitchList; std::string Device::extIoStr = "{\"dInputs\":[],\"dOutputs\":[]}";; std::string Device::serialStr = "[]"; std::map 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(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*this)); accessoryCardsList.push_back(std::make_unique(*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["serial"] = true; } if (regex_match(fileData, iotRtrVersionFilters)) { mapMacAddress2Iotr(); /* The order of the elements is important! This array is used to access Digital IOs, in particular in SMS commands! */ extIoStr = "{\"dInputs\":[\"USER_DIO_IN\"],\"dOutputs\":[\"USER_DIO_OUT\"]}"; capabilityList["serial"] = true; serialStr = "[{" "\"available\": true," "\"id\": \"serial0\"," "\"name\": \"RS232\"," "\"device\": \"/dev/ttymxc1\"," "\"role\": \"DCE\"," "\"modes\": {\"RS-232\": [\"hw-flowcontrol\", \"dcd-dtr\"]}," "\"speed\": [9600, 19200, 38400, 57600, 115200]" "},{" "\"available\": true," "\"id\": \"serial1\"," "\"name\": \"RS232/485\"," "\"device\": \"/dev/ttymxc2\"," "\"role\": \"DCE\"," "\"modes\": {\"RS-232\": [\"hw-flowcontrol\"], \"RS-485-HALF\": [\"rs4xx-termination\"]}," "\"speed\": [9600, 19200, 38400, 57600, 115200, 230400, 460800, 930600]" "}]"; } } 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(deviceInfo, DEVICE_INFO_FILE); 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); rapidjson::Document extIo; extIo.Parse<0>(extIoStr.c_str()); auto &extAlloc = extIo.GetAllocator(); deviceInfo.AddMember(KEY_EXTIO, extIo, extAlloc); rapidjson::Document serial; serial.Parse<0>(serialStr.c_str()); auto &serAlloc = serial.GetAllocator(); deviceInfo.AddMember(KEY_SERIALS, serial, serAlloc); } 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 (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::mapMacAddress2Iotr() { std::ifstream file("/sys/devices/platform/mts-io/mac-eth1"); if (file.is_open()) { std::string line = ""; std::getline(file, line); deviceInfoList["macAddress1"] = line; } } 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 &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 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::vector showResults; printf("Usage: %s [ OPTIONS ] OBJECT [--] [ ARGUMENTS ]\n", program_name.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(" -u, --duration : optional field for operation. it defines in milliseconds\n"); printf("Usage:\n"); printf(" Load : mts-io-sysfs -l -p -i \n"); printf(" Check : mts-io-sysfs -c -p \n"); printf("\n"); printf("show-name:\n"); printf("Usage:\n"); printf("--show \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()); } if (regex_match(hw_version, iotRtrVersionFilters)) { printf(" serial1/%s\n", SERIAL_TERMINATION.c_str()); printf(" serial1/%s\n", SERIAL_MODE.c_str()); printf("}\n"); } printf("\n"); printf("store-name:\n"); printf("Usage:\n"); printf("--store \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, storeFilters)) { printf(" %s BOOLEAN\n", showResult.c_str()); } } if (regex_match(hw_version, iotRtrVersionFilters)) { printf(" serial1/%s { 0 | 1 }\n", SERIAL_TERMINATION.c_str()); printf(" serial1/%s {", SERIAL_MODE.c_str()); unsigned int c = 1; for (auto const &m : mModes) { printf(" %s ", m.first.c_str()); if (c != mModes.size()) { printf("|"); } c++; }; printf("}\n"); } printf("\n"); exitHandler(0); } /* * Print an error message with errno string * and exit with internal identifying number. * parm1: Message to be printed * parm2: Saved errno value * parm3: An integer exit value (-1 means don't exit) * Returns: Void */ const int NOEXIT = -1; void Device::simpleError(std::string msg, int error, int exitval) { std::cerr << cmdname; std::cerr << ": "; std::cerr << msg; std::cerr << ": "; const int len = 512; char buf[len]; std::cerr << strerror_r(error,buf,len); std::cerr << "\n"; if (exitval > NOEXIT) exitHandler(exitval); } /* * Given a GPIO name, find the chip and line number * parm1: Name of GPIO * parm2: pointer to GPIO offset where the name * was found on return * Returns: chip where GPIO name was found * * Caller should free the chip pointer with * gpiod_chip_close. * * It is assumed the gpio devices are numbered * starting at 0, and are all sequential. * * This function was in the old libgpiod and * was removed from the new library. This * functionality should not have been removed. */ struct gpiod_chip * Device::gpio_request::gpiod_ctxless_find_line(Device *device, std::string name, unsigned int *r_offset) { const std::string gpioDevPrefix = "/dev/gpiochip"; int chipnum; char *gpiodevname = NULL; struct gpiod_chip *found_chip; struct gpiod_chip_info *chip_info; struct gpiod_line_info *info; int num_lines = 0; int offset; const char *nm; Device d; for(chipnum=0;1;chipnum++) { std::string gpioDevName = gpioDevPrefix; gpioDevName += std::to_string(chipnum); found_chip = gpiod_chip_open(gpioDevName.c_str()); if (found_chip == NULL) { if(chipnum == 0) { int error = errno; std::string msg = "Could not open "; msg.append(gpiodevname); device->simpleError(msg,error,NOEXIT); } free(gpiodevname); gpiodevname = NULL; break; } chip_info = gpiod_chip_get_info(found_chip); if (!chip_info) { int error = errno; std::string msg = "Can't read info from GPIO "; msg.append(gpiodevname); device->simpleError(msg,error,100); } num_lines = gpiod_chip_info_get_num_lines(chip_info); gpiod_chip_info_free(chip_info); free(gpiodevname); for (offset = 0; offset < num_lines; offset++) { info = gpiod_chip_get_line_info(found_chip, offset); nm = gpiod_line_info_get_name(info); if (nm && (name.compare(nm) == 0)) { *r_offset = offset; gpiod_line_info_free(info); return found_chip; // Caller must close chip } gpiod_line_info_free(info); } gpiod_chip_close(found_chip); } *r_offset = 0xffffffff; return NULL; } Device::gpio_request:: gpio_request(Device *device, const std::string name) { line_request = NULL; settings = NULL; line_config = NULL; request_config = NULL; if(name.empty()) device->simpleError("Name not provided\n",EINVAL,101); chip = gpiod_ctxless_find_line(device, name, &rq_offset); if(chip == NULL) return; request_config = gpiod_request_config_new(); if (request_config == NULL) device->simpleError("Could not allocate gpiod request_config", errno, 102); gpiod_request_config_set_consumer(request_config,"mts-io-sysfs"); line_config = gpiod_line_config_new(); if (line_config == NULL) device->simpleError("Could not allocate gpiod line_config", errno, 103); settings = gpiod_line_settings_new(); if (settings == NULL) device->simpleError("Could not allocate gpiod line_settings", errno, 104); int result = -1; result = gpiod_line_settings_set_direction(settings,GPIOD_LINE_DIRECTION_AS_IS); if (result == -1) device->simpleError("Could not set direction gpiod line_settings_set_direction GPIOD_LINE_DIRECTION_AS_IS", errno, 105); result = gpiod_line_settings_set_bias(settings,GPIOD_LINE_BIAS_AS_IS); if (result == -1) device->simpleError("Could not set bias gpiod line_settings_set_bias GPIOD_LINE_BIAS_AS_IS", errno, 106); unsigned int offsets[1]; size_t num_offsets = 1; offsets[0] = rq_offset; // Exactly one GPIO is being queried result = gpiod_line_config_add_line_settings(line_config,offsets,num_offsets,settings); if (result == -1) device->simpleError("Could not add line settings gpiod line_config_add_line_settings", errno, 107); line_request = gpiod_chip_request_lines(chip,request_config,line_config); // acquire line(s) for exclusive usage. if (line_request == NULL) device->simpleError("Could not request lines gpiod chip_request_lines", errno, 108); } Device::gpio_request:: ~gpio_request() { if(line_request) gpiod_line_request_release(line_request); if(settings) gpiod_line_settings_free(settings); if(line_config) gpiod_line_config_free(line_config); if(request_config) gpiod_request_config_free(request_config); if(chip) gpiod_chip_close(chip); } struct gpiod_line_request * Device::gpio_request::request() { /* This value can be NULL if the gpio name was * not found. */ return this->line_request; }; unsigned int Device::gpio_request::offset() { return this->rq_offset; }; /* 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. * * If the data pointer (2nd parm) is not NULL, * return the text of the value. This covers * non-gpio instances, such as device-id. */ int Device::getPinValue(const std::string &name, std::string *data) { std::string fileData; if(isupper(name[0])) { gpio_request gr(this, name); if (gr.request()) { enum gpiod_line_value lv = gpiod_line_request_get_value(gr.request(),gr.offset()); if(lv != GPIOD_LINE_VALUE_ERROR) { if(lv == GPIOD_LINE_VALUE_INACTIVE) { if(data != NULL) *data = "0"; return 0; } else { if(data != NULL) { *data = "1"; return 0; } else return 1; } } else simpleError("Invalid GPIO line value", errno, 109); } } // Upper case name int32_t code = MTS::System::readFile(SYSFS_PLATFORM + name, fileData); if (code == 0) { if (data != NULL) { *data = fileData; return 0; } if (fileData == "1") { return 1; }; return 0; } printError("cat: can't open %s%s: %s", SYSFS_PLATFORM, name.c_str(),strerror(errno)); exitHandler(99); return 99; // never be returned } void Device::show(std::string name) { std::string fileData; int result; if (regex_match(hw_version, iotRtrVersionFilters)) { showSerialModesMTR3(name); } result = getPinValue(name,&fileData); if (result == 0) printf("%s\n", fileData.c_str()); exitHandler(result); } // End of show 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) { 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 (regex_match(hw_version, iotRtrVersionFilters)) { setSerialModesMTR3(name, value); } if(isupper(name[0])) { enum gpiod_line_value lv = GPIOD_LINE_VALUE_ERROR; if (value.compare("0") == 0) lv = GPIOD_LINE_VALUE_INACTIVE; if (value.compare("1") == 0) lv = GPIOD_LINE_VALUE_ACTIVE; if (lv != GPIOD_LINE_VALUE_ERROR) { gpio_request gr(this, name); int retval; if (gr.request()) { retval = gpiod_line_request_set_value(gr.request(),gr.offset(),lv); if(retval == -1) { simpleError("Could not set GPIO value",errno,111); } return; } } } 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(); } else { 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(const rapidjson::Document &json, const std::string &file) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); json.Accept(writer); std::ofstream os(file); if (!os.is_open()) { printError("Can't write to %s: %s", file.c_str(),strerror(errno)); exitHandler(99); } else { os << buffer.GetString(); } } bool Device::readJson(rapidjson::Document &json, const std::string &file) { std::ifstream is(file); if (!is.is_open()) { printDebug("Can't read to %s: %s", file.c_str(), strerror(errno)); return false; } rapidjson::IStreamWrapper isw { is }; json.ParseStream(isw); if (json.HasParseError()) { printError("%s Parse error", file.c_str()); return false; } return true; } void Device::storeOutputStateToNonvolatile(const std::string& name, const std::string& value) { rapidjson::Document deviceInfo; if (!readJson(deviceInfo, DEVICE_INFO_FILE)){ return; } if (!deviceInfo.HasMember(KEY_EXTIO)){ return; } if (!deviceInfo[KEY_EXTIO].HasMember(KEY_DOUTPUTS)){ return; } const rapidjson::Value &outputs = deviceInfo[KEY_EXTIO][KEY_DOUTPUTS]; bool found = false; for (auto itr = outputs.Begin(); itr != outputs.End(); ++itr) { auto listedOutput = (*itr).GetString(); if (name == listedOutput) { found = true; break; } } if (!found) { return; } rapidjson::Document stateIo; rapidjson::Document::AllocatorType &alloc = stateIo.GetAllocator(); rapidjson::Value jname(name.c_str(), alloc); rapidjson::Value jvalue(value.c_str(), alloc); readJson( stateIo, STORED_DIGITAL_OUTPUTS_STATE_FILE); if (stateIo.IsNull()) { stateIo.SetObject(); }; if (!stateIo.HasMember(jname)) { stateIo.AddMember(jname, jvalue, alloc); } else { stateIo[jname] = jvalue; } writeJson(stateIo, STORED_DIGITAL_OUTPUTS_STATE_FILE); } void Device::setSerialModesMTR3(const std::string ¶meter, const std::string &value) { std::vector parts = MTS::Text::split(parameter, '/'); if (parts.size() < 2) { // it is not a serial things, return and compare to other options // serial things look like `serial1/mode' return; } const std::string &serialName = parts[0]; const std::string &mode = parts[1]; if (serialName != "serial1") { printUsage(); } if ((mode != SERIAL_MODE) && (mode != SERIAL_TERMINATION)) { printUsage(); } if (mode == SERIAL_MODE) { if ((value != SERIAL_MODE_RS232) && (value != SERIAL_MODE_RS485)) { printUsage(); } if (value == SERIAL_MODE_RS232) { store(CONTROL_PIN_SERIAL_MODE, "0"); } if (value == SERIAL_MODE_RS485) { store(CONTROL_PIN_SERIAL_MODE, "1"); } } else if (mode == SERIAL_TERMINATION) { store(CONTROL_PIN_TERMINATION, value); } else { printError("Unknown mode: %s", mode.c_str()); exitHandler(98); } exitHandler(0); } void Device::showSerialModesMTR3(const std::string ¶meter) { std::vector parts = MTS::Text::split(parameter, '/'); if (parts.size() < 2) { // it is not a serial things, return and compare to other options // serial things look like `serial1/mode' return; } const std::string &serialName = parts[0]; const std::string &mode = parts[1]; if (serialName != "serial1") { printUsage(); } if ((mode != SERIAL_MODE) && (mode != SERIAL_TERMINATION)) { printUsage(); } if (mode == SERIAL_MODE) { if (getPinValue(CONTROL_PIN_SERIAL_MODE,NULL) == 1) { std::cout << SERIAL_MODE_RS485 << std::endl; } else { std::cout << SERIAL_MODE_RS232 << std::endl; } } else if (mode == SERIAL_TERMINATION) { std::cout << getPinValue(CONTROL_PIN_TERMINATION,NULL) << std::endl; } else { printError("Unknown mode: %s", mode.c_str()); exitHandler(98); } exitHandler(0); } void Device::setProgramName(const std::string &name){ program_name = name; } void Device::setHWVersion(){ std::ifstream is(HW_VERSION_PATH); if (!is.is_open()) { printDebug("Can't read to %s: %s", HW_VERSION_PATH, strerror(errno)); return; } std::getline(is, hw_version); }