summaryrefslogtreecommitdiff
path: root/src/Device/Device.cpp.orig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Device/Device.cpp.orig')
-rw-r--r--src/Device/Device.cpp.orig630
1 files changed, 630 insertions, 0 deletions
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 &ethSwitchAlloc = 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();
+ }
+}