5656#include < uavcan_posix/dynamic_node_id_server/file_storage_backend.hpp>
5757#include < uavcan_posix/firmware_version_checker.hpp>
5858
59+ static constexpr size_t FW_DB_LINE_SIZE = 256 ; // key(~15) + '=' + filename(NAME_MAX) + '\n'
60+ static const char FW_DB_PATH[] = UAVCAN_FIRMWARE_PATH " /FW.db" ;
61+ static const char FW_DB_TMP_PATH[] = UAVCAN_FIRMWARE_PATH " /FW.db.tmp" ;
62+
5963/* *
6064 * @file uavcan_servers.cpp
6165 *
6569 * David Sidrane <david_s5@nscdg.com>
6670 */
6771
68- UavcanServers::UavcanServers (uavcan::INode &node, uavcan::NodeInfoRetriever &node_info_retriever) :
72+ UavcanServers::UavcanServers (uavcan::INode &node, uavcan::NodeInfoRetriever &node_info_retriever,
73+ UavcanNode &uavcan_node) :
6974 _server_instance(node, _storage_backend, _tracer),
7075 _fileserver_backend(node),
7176 _fw_upgrade_trigger(node, _fw_version_checker),
7277 _fw_server(node, _fileserver_backend),
73- _node_info_retriever(node_info_retriever)
78+ _node_info_retriever(node_info_retriever),
79+ _uavcan_node(uavcan_node)
7480{
7581}
7682
@@ -135,6 +141,7 @@ int UavcanServers::init()
135141 Check for firmware in the root directory, move it to appropriate location on
136142 the SD card, as defined by the APDesc.
137143 */
144+ validateFwDatabase ();
138145 migrateFWFromRoot (UAVCAN_FIRMWARE_PATH, UAVCAN_SD_ROOT_PATH);
139146
140147 /* Start the Node */
@@ -159,6 +166,7 @@ void UavcanServers::migrateFWFromRoot(const char *sd_path, const char *sd_root_p
159166 int rv;
160167 char srcpath[maxlen + 1 ];
161168 char dstpath[maxlen + 1 ];
169+ uavcan_posix::FirmwareVersionChecker::AppDescriptor descriptor{};
162170
163171 if (stat (sd_path, &sb) != 0 || !S_ISDIR (sb.st_mode )) {
164172 rv = mkdir (sd_path, S_IRWXU | S_IRWXG | S_IRWXO);
@@ -169,11 +177,6 @@ void UavcanServers::migrateFWFromRoot(const char *sd_path, const char *sd_root_p
169177 }
170178 }
171179
172- // Phase 1: collect .bin filenames with directory open, then close
173- static constexpr int MaxBinFiles = 10 ;
174- char bin_names[MaxBinFiles][NAME_MAX + 1 ];
175- int bin_count = 0 ;
176-
177180 DIR *const sd_root_dir = opendir (sd_root_path);
178181
179182 if (!sd_root_dir) {
@@ -182,7 +185,7 @@ void UavcanServers::migrateFWFromRoot(const char *sd_path, const char *sd_root_p
182185
183186 struct dirent *dev_dirent = NULL ;
184187
185- while ((dev_dirent = readdir (sd_root_dir)) != nullptr && bin_count < MaxBinFiles ) {
188+ while ((dev_dirent = readdir (sd_root_dir)) != nullptr ) {
186189 if (DIRENT_ISFILE (dev_dirent->d_type ) && strstr (dev_dirent->d_name , " .bin" ) != nullptr ) {
187190 size_t filename_len = strlen (dev_dirent->d_name );
188191 size_t srcpath_len = sd_root_path_len + 1 + filename_len;
@@ -192,34 +195,27 @@ void UavcanServers::migrateFWFromRoot(const char *sd_path, const char *sd_root_p
192195 continue ;
193196 }
194197
195- strncpy (bin_names[bin_count], dev_dirent->d_name , NAME_MAX);
196- bin_names[bin_count][NAME_MAX] = ' \0 ' ;
197- bin_count++;
198- }
199- }
200-
201- (void )closedir (sd_root_dir);
202-
203- // Phase 2: process collected files with directory closed
204- for (int i = 0 ; i < bin_count; i++) {
205- uavcan_posix::FirmwareVersionChecker::AppDescriptor descriptor{0 };
198+ snprintf (srcpath, sizeof (srcpath), " %s%s" , sd_root_path, dev_dirent->d_name );
206199
207- snprintf (srcpath, sizeof (srcpath), " %s%s" , sd_root_path, bin_names[i]);
208-
209- if (uavcan_posix::FirmwareVersionChecker::getFileInfo (srcpath, descriptor, 1024 ) != 0 ) {
210- continue ;
211- }
200+ if (uavcan_posix::FirmwareVersionChecker::getFileInfo (srcpath, descriptor, 1024 ) != 0 ) {
201+ continue ;
202+ }
212203
213- if (descriptor.image_crc == 0 ) {
214- continue ;
215- }
204+ if (descriptor.image_crc == 0 ) {
205+ continue ;
206+ }
216207
217- snprintf (dstpath, sizeof (dstpath), " %s/%d.bin" , sd_path, descriptor.board_id );
208+ snprintf (dstpath, sizeof (dstpath), " %s/%d.bin" , sd_path, descriptor.board_id );
218209
219- if (copyFw (dstpath, srcpath) >= 0 ) {
220- unlink (srcpath);
210+ if (copyFw (dstpath, srcpath) >= 0 ) {
211+ updateFwDatabase (dstpath, dev_dirent->d_name );
212+ unlink (srcpath);
213+ _new_firmware = true ;
214+ }
221215 }
222216 }
217+
218+ (void )closedir (sd_root_dir);
223219}
224220
225221int UavcanServers::copyFw (const char *dst, const char *src)
@@ -277,3 +273,183 @@ int UavcanServers::copyFw(const char *dst, const char *src)
277273
278274 return rv;
279275}
276+
277+ void UavcanServers::updateFwDatabase (const char *dst, const char *src_filename)
278+ {
279+ const char *key = dst + strlen (UAVCAN_FIRMWARE_PATH);
280+
281+ if (*key == ' /' ) {
282+ key++;
283+ }
284+
285+ char new_line[FW_DB_LINE_SIZE];
286+ snprintf (new_line, sizeof (new_line), " %s=%s\n " , key, src_filename);
287+ const size_t new_line_len = strlen (new_line);
288+
289+ char key_prefix[64 ];
290+ snprintf (key_prefix, sizeof (key_prefix), " %s=" , key);
291+ const size_t key_prefix_len = strlen (key_prefix);
292+
293+ int src_fd = open (FW_DB_PATH, O_RDONLY);
294+ int dst_fd = open (FW_DB_TMP_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0666 );
295+
296+ if (dst_fd < 0 ) {
297+ if (src_fd >= 0 ) { close (src_fd); }
298+
299+ PX4_WARN (" updateFwDatabase: couldn't open tmp file" );
300+ return ;
301+ }
302+
303+ bool found = false ;
304+ char line[FW_DB_LINE_SIZE];
305+ int pos = 0 ;
306+ char c;
307+
308+ if (src_fd >= 0 ) {
309+ while (read (src_fd, &c, 1 ) == 1 ) {
310+ if (c == ' \n ' || pos >= (int )sizeof (line) - 2 ) {
311+ if (c == ' \n ' ) { line[pos++] = c; }
312+
313+ line[pos] = ' \0 ' ;
314+
315+ if (strncmp (line, key_prefix, key_prefix_len) == 0 ) {
316+ write (dst_fd, new_line, new_line_len);
317+ found = true ;
318+
319+ } else {
320+ write (dst_fd, line, pos);
321+ }
322+
323+ pos = 0 ;
324+
325+ } else {
326+ line[pos++] = c;
327+ }
328+ }
329+
330+ // flush any trailing line without newline
331+ if (pos > 0 ) {
332+ line[pos] = ' \0 ' ;
333+
334+ if (strncmp (line, key_prefix, key_prefix_len) == 0 ) {
335+ write (dst_fd, new_line, new_line_len);
336+ found = true ;
337+
338+ } else {
339+ write (dst_fd, line, pos);
340+ }
341+ }
342+
343+ close (src_fd);
344+ }
345+
346+ if (!found) {
347+ write (dst_fd, new_line, new_line_len);
348+ }
349+
350+ close (dst_fd);
351+ rename (FW_DB_TMP_PATH, FW_DB_PATH);
352+ }
353+
354+ void UavcanServers::validateFwDatabase ()
355+ {
356+ int src_fd = open (FW_DB_PATH, O_RDONLY);
357+
358+ if (src_fd < 0 ) {
359+ return ; // no DB yet, nothing to validate
360+ }
361+
362+ int dst_fd = open (FW_DB_TMP_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0666 );
363+
364+ if (dst_fd < 0 ) {
365+ close (src_fd);
366+ PX4_WARN (" validateFwDatabase: couldn't open tmp file" );
367+ return ;
368+ }
369+
370+ bool changed = false ;
371+ char line[FW_DB_LINE_SIZE];
372+ int pos = 0 ;
373+ char c;
374+
375+ auto process_line = [&](int len) {
376+ line[len] = ' \0 ' ;
377+ const char *eq = strchr (line, ' =' );
378+
379+ if (eq != nullptr ) {
380+ char key[64 ];
381+ const size_t key_len = (size_t )(eq - line);
382+
383+ if (key_len < sizeof (key)) {
384+ memcpy (key, line, key_len);
385+ key[key_len] = ' \0 ' ;
386+
387+ char full_path[UAVCAN_MAX_PATH_LENGTH + 1 ];
388+ snprintf (full_path, sizeof (full_path), " %s/%s" , UAVCAN_FIRMWARE_PATH, key);
389+
390+ struct stat sb;
391+
392+ if (stat (full_path, &sb) == 0 && S_ISREG (sb.st_mode )) {
393+ write (dst_fd, line, len);
394+
395+ } else {
396+ PX4_INFO (" validateFwDatabase: removing stale entry for '%s'" , key);
397+ changed = true ;
398+ }
399+ }
400+
401+ } else {
402+ changed = true ; // malformed — drop it
403+ }
404+ };
405+
406+ while (read (src_fd, &c, 1 ) == 1 ) {
407+ if (c == ' \n ' || pos >= (int )sizeof (line) - 2 ) {
408+ if (c == ' \n ' ) { line[pos++] = c; }
409+
410+ process_line (pos);
411+ pos = 0 ;
412+
413+ } else {
414+ line[pos++] = c;
415+ }
416+ }
417+
418+ if (pos > 0 ) {
419+ process_line (pos);
420+ }
421+
422+ close (src_fd);
423+ close (dst_fd);
424+
425+ if (changed) {
426+ rename (FW_DB_TMP_PATH, FW_DB_PATH);
427+
428+ } else {
429+ unlink (FW_DB_TMP_PATH);
430+ }
431+ }
432+
433+ void UavcanServers::onFirmwareCurrent (uavcan::NodeID node_id, uint16_t board_id)
434+ {
435+ const uint8_t nid = node_id.get ();
436+
437+ if (nid >= sizeof (_configured_nodes) || _configured_nodes[nid]) {
438+ return ;
439+ }
440+
441+ _configured_nodes[nid] = true ;
442+ }
443+
444+ bool UavcanServers::checkForNewFirmware ()
445+ {
446+ if (hrt_elapsed_time (&_last_fw_migration_check) < 30_s) {
447+ return false ;
448+ }
449+
450+ _last_fw_migration_check = hrt_absolute_time ();
451+
452+ _new_firmware = false ;
453+ migrateFWFromRoot (UAVCAN_FIRMWARE_PATH, UAVCAN_SD_ROOT_PATH);
454+ return _new_firmware;
455+ }
0 commit comments