Configurator.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
  2. #include "Configurator.h"
  3. #include "esp_log.h"
  4. #include "esp_system.h"
  5. #include "pb_common.h" // Nanopb header for encoding (serialization)
  6. #include "pb_decode.h" // Nanopb header for decoding (deserialization)
  7. #include "pb_encode.h" // Nanopb header for encoding (serialization)
  8. // #include "sys_options.h"
  9. #include "tools.h"
  10. #include <string.h>
  11. #include <algorithm>
  12. static const char* TAG = "Configurator";
  13. static const char* targets_folder = "targets";
  14. static const char* config_file_name = "settings.bin";
  15. static const char* state_file_name = "state.bin";
  16. __attribute__((section(".ext_ram.bss"))) PlatformConfig::Configurator configurator;
  17. sys_Config* platform = NULL;
  18. sys_State* sys_state = NULL;
  19. bool set_pb_string_from_mac(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) {
  20. if (!stream) {
  21. // This is a size calculation pass, return true to indicate field presence
  22. return true;
  23. }
  24. // Generate the string based on MAC and prefix
  25. const char* prefix = reinterpret_cast<const char*>(*arg);
  26. char* value = alloc_get_string_with_mac(prefix && strlen(prefix) > 0 ? prefix : "squeezelite-");
  27. // Write the string to the stream
  28. if (!pb_encode_string(stream, (uint8_t*)value, strlen(value))) {
  29. free(value); // Free memory if encoding fails
  30. return false;
  31. }
  32. free(value); // Free memory after encoding
  33. return true;
  34. }
  35. namespace PlatformConfig {
  36. EXT_RAM_ATTR static const int NO_COMMIT_PENDING = BIT0;
  37. EXT_RAM_ATTR static const int LOAD_BIT = BIT1;
  38. EXT_RAM_ATTR static const int NO_STATE_COMMIT_PENDING = BIT2;
  39. const int Configurator::MaxDelay = 1000;
  40. const int Configurator::LockMaxWait = 20 * Configurator::MaxDelay;
  41. EXT_RAM_ATTR TimerHandle_t Configurator::_timer;
  42. EXT_RAM_ATTR SemaphoreHandle_t Configurator::_mutex;
  43. EXT_RAM_ATTR SemaphoreHandle_t Configurator::_state_mutex;
  44. EXT_RAM_ATTR EventGroupHandle_t Configurator::_group;
  45. static void ConfiguratorCallback(TimerHandle_t xTimer) {
  46. static int cnt = 0, scnt = 0;
  47. if (configurator.HasChanges()) {
  48. ESP_LOGI(TAG, "Configuration has some uncommitted entries");
  49. configurator.CommitChanges();
  50. } else {
  51. if (++cnt >= 15) {
  52. ESP_LOGV(TAG, "commit timer: commit flag not set");
  53. cnt = 0;
  54. }
  55. }
  56. if (configurator.HasStateChanges()) {
  57. ESP_LOGI(TAG, "State has some uncommitted changes");
  58. configurator.CommitState();
  59. } else {
  60. if (++scnt >= 15) {
  61. ESP_LOGV(TAG, "commit timer: commit flag not set");
  62. cnt = 0;
  63. }
  64. }
  65. xTimerReset(xTimer, 10);
  66. }
  67. void Configurator::RaiseStateModified() { SetGroupBit(NO_STATE_COMMIT_PENDING, false); }
  68. void Configurator::RaiseModified() { SetGroupBit(NO_COMMIT_PENDING, false); }
  69. void Configurator::ResetModified() {
  70. ESP_LOGV(TAG, "Resetting the global commit flag.");
  71. SetGroupBit(NO_COMMIT_PENDING, false);
  72. }
  73. void Configurator::ResetStateModified() {
  74. ESP_LOGV(TAG, "Resetting the state commit flag.");
  75. SetGroupBit(NO_STATE_COMMIT_PENDING, false);
  76. }
  77. bool Configurator::SetGroupBit(int bit_num, bool flag) {
  78. bool result = true;
  79. int curFlags = xEventGroupGetBits(_group);
  80. if ((curFlags & LOAD_BIT) && bit_num == NO_COMMIT_PENDING) {
  81. ESP_LOGD(TAG, "Loading config, ignoring changes");
  82. result = false;
  83. }
  84. if (result) {
  85. bool curBit = (xEventGroupGetBits(_group) & bit_num);
  86. if (curBit == flag) {
  87. ESP_LOGV(TAG, "Flag %d already %s", bit_num, flag ? "Set" : "Cleared");
  88. result = false;
  89. }
  90. }
  91. if (result) {
  92. ESP_LOGV(TAG, "%s Flag %d ", flag ? "Setting" : "Clearing", bit_num);
  93. if (!flag) {
  94. xEventGroupClearBits(_group, bit_num);
  95. } else {
  96. xEventGroupSetBits(_group, bit_num);
  97. }
  98. }
  99. return result;
  100. }
  101. bool Configurator::Lock() {
  102. ESP_LOGV(TAG, "Locking Configurator");
  103. if (xSemaphoreTake(_mutex, LockMaxWait) == pdTRUE) {
  104. ESP_LOGV(TAG, "Configurator locked!");
  105. return true;
  106. } else {
  107. ESP_LOGE(TAG, "Semaphore take failed. Unable to lock Configurator");
  108. return false;
  109. }
  110. }
  111. bool Configurator::LockState() {
  112. ESP_LOGV(TAG, "Locking State");
  113. if (xSemaphoreTake(_state_mutex, LockMaxWait) == pdTRUE) {
  114. ESP_LOGV(TAG, "State locked!");
  115. return true;
  116. } else {
  117. ESP_LOGE(TAG, "Semaphore take failed. Unable to lock State");
  118. return false;
  119. }
  120. }
  121. void* Configurator::AllocGetConfigBuffer(size_t* sz, sys_Config* config) {
  122. size_t datasz;
  123. pb_byte_t* data = NULL;
  124. if (!pb_get_encoded_size(&datasz, sys_Config_fields, (const void*)platform) || datasz <= 0) {
  125. return data;
  126. }
  127. data = (pb_byte_t*)malloc_init_external(datasz * sizeof(pb_byte_t));
  128. pb_ostream_t stream = pb_ostream_from_buffer(data, datasz);
  129. pb_encode(&stream, sys_Config_fields, (const void*)platform);
  130. if (sz) {
  131. *sz = datasz * sizeof(pb_byte_t);
  132. }
  133. return data;
  134. }
  135. void* Configurator::AllocGetConfigBuffer(size_t* sz) {
  136. return AllocGetConfigBuffer(sz, &this->_root);
  137. }
  138. bool Configurator::WaitForCommit() {
  139. bool commit_pending = (xEventGroupGetBits(_group) & NO_COMMIT_PENDING) == 0;
  140. while (commit_pending) {
  141. ESP_LOGW(TAG, "Waiting for config commit ...");
  142. commit_pending = (xEventGroupWaitBits(_group, NO_COMMIT_PENDING | NO_STATE_COMMIT_PENDING, pdFALSE, pdTRUE,
  143. (MaxDelay * 2) / portTICK_PERIOD_MS) &
  144. ( NO_COMMIT_PENDING | NO_STATE_COMMIT_PENDING)) == 0;
  145. if (commit_pending) {
  146. ESP_LOGW(TAG, "Timeout waiting for config commit.");
  147. } else {
  148. ESP_LOGI(TAG, "Config committed!");
  149. }
  150. }
  151. return !commit_pending;
  152. }
  153. void Configurator::CommitChanges() {
  154. esp_err_t err = ESP_OK;
  155. ESP_LOGI(TAG, "Committing configuration to flash. Locking config object.");
  156. if (!Lock()) {
  157. ESP_LOGE(TAG, "Unable to lock config for commit ");
  158. return;
  159. }
  160. ESP_LOGV(TAG, "Config Locked. Committing");
  161. Commit(&_root);
  162. ResetModified();
  163. Unlock();
  164. ESP_LOGI(TAG, "Done Committing configuration to flash.");
  165. }
  166. bool Configurator::CommitState() {
  167. esp_err_t err = ESP_OK;
  168. ESP_LOGI(TAG, "Committing configuration to flash. Locking config object.");
  169. if (!LockState()) {
  170. ESP_LOGE(TAG, "Unable to lock config for commit ");
  171. return false;
  172. }
  173. ESP_LOGV(TAG, "Config Locked. Committing");
  174. CommitState(&_sys_state);
  175. ResetStateModified();
  176. Unlock();
  177. ESP_LOGI(TAG, "Done Committing configuration to flash.");
  178. return true;
  179. }
  180. bool Configurator::HasChanges() { return (xEventGroupGetBits(_group) & NO_COMMIT_PENDING); }
  181. bool Configurator::HasStateChanges() { return (xEventGroupGetBits(_group) & NO_STATE_COMMIT_PENDING); }
  182. void Configurator::Unlock() {
  183. ESP_LOGV(TAG, "Unlocking Configurator!");
  184. xSemaphoreGive(_mutex);
  185. }
  186. void Configurator::UnlockState() {
  187. ESP_LOGV(TAG, "Unlocking State!");
  188. xSemaphoreGive(_state_mutex);
  189. }
  190. void Configurator::ResetStructure(sys_Config* config) {
  191. if (!config) {
  192. return;
  193. }
  194. sys_Config blankconfig = sys_Config_init_default;
  195. memcpy(config, &blankconfig, sizeof(blankconfig));
  196. }
  197. bool Configurator::LoadDecodeBuffer(void* buffer, size_t buffer_size) {
  198. size_t msgsize = 0;
  199. size_t newsize = 0;
  200. sys_Config config = sys_Config_init_default;
  201. bool result = Configurator::LoadDecode(buffer, buffer_size, &config);
  202. if (result) {
  203. Configurator::ApplyTargetSettings(&config);
  204. }
  205. if (result) {
  206. void* currentbuffer = AllocGetConfigBuffer(&msgsize);
  207. void* newbuffer = AllocGetConfigBuffer(&newsize, &config);
  208. if (msgsize != newsize || !memcmp(currentbuffer, newbuffer, msgsize)) {
  209. ESP_LOGI(TAG, "Config change detected.");
  210. // todo: here we are assuming that all strings and repeated elements have fixed size
  211. // and therefore size should always be the same.
  212. result = Configurator::LoadDecode(buffer, buffer_size, &this->_root);
  213. RaiseModified();
  214. }
  215. free(currentbuffer);
  216. free(newbuffer);
  217. }
  218. return result;
  219. }
  220. bool Configurator::LoadDecodeState() {
  221. bool result = true;
  222. sys_State blank_state = sys_State_init_default;
  223. FILE* file = open_file("rb", state_file_name);
  224. if (file == nullptr) {
  225. ESP_LOGD(TAG,"No state file found. Initializing ");
  226. pb_release(&sys_State_msg,(void *)&_sys_state);
  227. memcpy(&_sys_state, &blank_state, sizeof(sys_State));
  228. ESP_LOGD(TAG,"Done Initializing state");
  229. return true;
  230. }
  231. ESP_LOGD(TAG, "Creating binding");
  232. pb_istream_t filestream = {&in_file_binding,NULL,0};
  233. ESP_LOGD(TAG, "Starting encode");
  234. if (!pb_decode(&filestream, &sys_State_msg, (void*)&_sys_state)) {
  235. ESP_LOGE(TAG, "Decoding failed: %s\n", PB_GET_ERROR(&filestream));
  236. result = false;
  237. }
  238. fclose(file);
  239. configurator_raise_state_changed();
  240. ESP_LOGD(TAG, "State loaded");
  241. return true;
  242. }
  243. bool Configurator::LoadDecode(
  244. void* buffer, size_t buffer_size, sys_Config* conf_root, bool noinit) {
  245. if (!conf_root || !buffer) {
  246. ESP_LOGE(TAG, "Invalid arguments passed to Load");
  247. }
  248. bool result = true;
  249. // Prepare to read the data into the 'config' structure
  250. pb_istream_t stream = pb_istream_from_buffer((uint8_t*)buffer, buffer_size);
  251. // Decode the Protocol Buffers message
  252. if (noinit) {
  253. ESP_LOGD(TAG, "Decoding WITHOUT initialization");
  254. result = pb_decode_noinit(&stream, &sys_Config_msg, conf_root);
  255. } else {
  256. ESP_LOGD(TAG, "Decoding WITH initialization");
  257. result = pb_decode(&stream, &sys_Config_msg, conf_root);
  258. }
  259. if (!result) {
  260. ESP_LOGE(TAG, "Failed to decode settings: %s", PB_GET_ERROR(&stream));
  261. return false;
  262. }
  263. ESP_LOGD(TAG, "Settings decoded");
  264. return true;
  265. }
  266. bool Configurator::Commit(sys_Config* config) {
  267. if (!config) {
  268. ESP_LOGE(TAG, "Invalid configuration structure!");
  269. return false;
  270. }
  271. FILE* file = open_file("wb", config_file_name);
  272. bool result = true;
  273. if (file == nullptr) {
  274. return false;
  275. }
  276. ESP_LOGD(TAG, "Creating binding");
  277. pb_ostream_t filestream = {&out_file_binding, file, SIZE_MAX, 0};
  278. ESP_LOGD(TAG, "Starting encode");
  279. if (!pb_encode(&filestream, sys_Config_fields, (void*)config)) {
  280. ESP_LOGE(TAG, "Encoding failed: %s\n", PB_GET_ERROR(&filestream));
  281. result = false;
  282. }
  283. ESP_LOGD(TAG, "Encoded size: %d", filestream.bytes_written);
  284. if (filestream.bytes_written == 0) {
  285. ESP_LOGE(TAG, "Empty configuration!");
  286. ESP_LOGD(TAG, "Device name: %s", config->names.device);
  287. }
  288. fclose(file);
  289. return result;
  290. }
  291. bool Configurator::CommitState(sys_State* state) {
  292. if (!state) {
  293. ESP_LOGE(TAG, "Invalid state structure!");
  294. return false;
  295. }
  296. FILE* file = open_file("wb", state_file_name);
  297. bool result = true;
  298. if (file == nullptr) {
  299. return false;
  300. }
  301. ESP_LOGD(TAG, "Creating binding for state commit");
  302. pb_ostream_t filestream = {&out_file_binding, file, SIZE_MAX, 0};
  303. ESP_LOGD(TAG, "Starting state encode");
  304. if (!pb_encode(&filestream, sys_Config_fields, (void*)state)) {
  305. ESP_LOGE(TAG, "Encoding failed: %s\n", PB_GET_ERROR(&filestream));
  306. result = false;
  307. }
  308. ESP_LOGD(TAG, "Encoded size: %d", filestream.bytes_written);
  309. if (filestream.bytes_written == 0) {
  310. ESP_LOGE(TAG, "Empty state!");
  311. }
  312. fclose(file);
  313. return result;
  314. }
  315. void Configurator::InitLoadConfig(const char* filename) {
  316. return Configurator::InitLoadConfig(filename, &this->_root);
  317. }
  318. void Configurator::InitLoadConfig(const char* filename, sys_Config* conf_root, bool noinit) {
  319. esp_err_t err = ESP_OK;
  320. size_t data_length = 0;
  321. bool result = false;
  322. ESP_LOGI(TAG, "Loading settings from %s", filename);
  323. void* data = load_file(&data_length, filename);
  324. if (!data) {
  325. ESP_LOGW(TAG, "Config file %s was empty. ", filename);
  326. return;
  327. } else {
  328. result = LoadDecode(data, data_length, conf_root, noinit);
  329. free(data);
  330. }
  331. if (ApplyTargetSettings(conf_root)) {
  332. result = true;
  333. }
  334. if (result) {
  335. _timer = xTimerCreate(
  336. "configTimer", MaxDelay / portTICK_RATE_MS, pdFALSE, NULL, ConfiguratorCallback);
  337. if (xTimerStart(_timer, MaxDelay / portTICK_RATE_MS) != pdPASS) {
  338. ESP_LOGE(TAG, "config commitment timer failed to start.");
  339. }
  340. }
  341. return;
  342. }
  343. bool Configurator::ApplyTargetSettings() { return ApplyTargetSettings(&this->_root); }
  344. bool Configurator::ApplyTargetSettings(sys_Config* conf_root) {
  345. size_t data_length = 0;
  346. bool result = false;
  347. std::string target_name = conf_root->target;
  348. std::string target_file;
  349. #ifdef CONFIG_FW_PLATFORM_NAME
  350. if( target_name.empty()){
  351. target_name = CONFIG_FW_PLATFORM_NAME;
  352. }
  353. #endif
  354. target_file = target_name+ std::string(".bin");
  355. std::transform(target_file.begin(), target_file.end(), target_file.begin(),
  356. [](unsigned char c){ return std::tolower(c); });
  357. if (target_file.empty() || !get_file_info(NULL, targets_folder, target_file.c_str())) {
  358. ESP_LOGD(TAG, "Platform settings file not found: %s", target_file.c_str());
  359. return result;
  360. }
  361. ESP_LOGI(TAG, "Applying target %s settings", target_name.c_str());
  362. void* data = load_file(&data_length, targets_folder, target_file.c_str());
  363. if (!data) {
  364. ESP_LOGE(TAG, "File read fail");
  365. return false;
  366. } else {
  367. result = LoadDecode(data, data_length, conf_root, true);
  368. if (result) {
  369. ESP_LOGI(TAG, "Target %s settings loaded", target_name.c_str());
  370. }
  371. free(data);
  372. }
  373. return result;
  374. }
  375. }; // namespace PlatformConfig
  376. void configurator_reset_configuration() {
  377. ESP_LOGI(TAG, "Creating default configuration file. ");
  378. sys_Config config = sys_Config_init_default;
  379. ESP_LOGD(TAG, "Device name before target settings: %s", config.names.device);
  380. PlatformConfig::Configurator::ApplyTargetSettings(&config);
  381. ESP_LOGD(TAG, "Device name after target settings: %s", config.names.device);
  382. ESP_LOGD(TAG, "Committing new structure");
  383. PlatformConfig::Configurator::Commit(&config);
  384. }
  385. void configurator_load() {
  386. struct stat fileInformation;
  387. ESP_LOGI(TAG, "Loading system settings file");
  388. ESP_LOGD(TAG, "Checking if file %s exists", config_file_name);
  389. bool found = get_file_info(&fileInformation, config_file_name);
  390. if (!found || fileInformation.st_size == 0) {
  391. ESP_LOGI(TAG, "Configuration file not found or is empty. ");
  392. configurator_reset_configuration();
  393. }
  394. configurator.InitLoadConfig(config_file_name);
  395. ESP_LOGD(TAG, "Assigning global config pointer");
  396. platform = configurator.Root();
  397. configurator.LoadDecodeState();
  398. sys_state = configurator.RootState();
  399. }
  400. bool configurator_lock() { return configurator.Lock(); }
  401. void configurator_unlock() { configurator.Unlock(); }
  402. void configurator_raise_changed() { configurator.RaiseModified(); }
  403. void configurator_raise_state_changed() { configurator.RaiseStateModified(); }
  404. bool configurator_has_changes() { return configurator.HasChanges(); }
  405. bool configurator_waitcommit() { return configurator.WaitForCommit(); }
  406. void* configurator_alloc_get_config(size_t* sz) { return configurator.AllocGetConfigBuffer(sz); }
  407. bool configurator_parse_config(void* buffer, size_t buffer_size) {
  408. // Load and decode buffer. The method also applies any overlay if needed.
  409. return configurator.LoadDecodeBuffer(buffer, buffer_size);
  410. }
  411. pb_type_t configurator_get_field_type(const pb_msgdesc_t* desc, uint32_t tag) {
  412. pb_field_iter_t iter;
  413. if (pb_field_iter_begin(&iter, desc, NULL) && pb_field_iter_find(&iter, tag)) {
  414. /* Found our field. */
  415. return iter.type;
  416. }
  417. return 0;
  418. }
  419. bool configurator_set_string(
  420. const pb_msgdesc_t* desc, uint32_t field_tag, void* message, const char* value) {
  421. pb_field_iter_t iter;
  422. const char * newval = STR_OR_BLANK(value);
  423. ESP_LOGD(TAG, "Setting value [%s] in message field tag %d",newval , field_tag);
  424. if (pb_field_iter_begin(&iter, desc, message) && pb_field_iter_find(&iter, field_tag)) {
  425. if (iter.pData && !strcmp((char*)iter.pData, newval)) {
  426. ESP_LOGW(TAG, "No change, from and to values are the same: [%s]", STR_OR_BLANK(newval));
  427. return false;
  428. }
  429. if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER) {
  430. ESP_LOGD(TAG, "Field is a pointer. Freeing previous value if any");
  431. FREE_AND_NULL(iter.pData);
  432. ESP_LOGD(TAG, "Field is a pointer. Setting new value ");
  433. if(newval && strlen(newval)>0){
  434. iter.pData = strdup_psram(newval);
  435. }
  436. } else if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC) {
  437. ESP_LOGD(TAG, "Static string. Setting new value");
  438. memset(iter.pData,0x00,iter.data_size);
  439. if(newval && strlen(newval)>0){
  440. strncpy((char*)iter.pData, newval, iter.data_size);
  441. }
  442. }
  443. ESP_LOGD(TAG, "Done setting value ");
  444. }
  445. return true;
  446. }