PX4 Firmware
PX4 Autopilot Software http://px4.io
parameters.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file param.c
36  *
37  * Global parameter store.
38  *
39  * Note that it might make sense to convert this into a driver. That would
40  * offer some interesting options regarding state for e.g. ORB advertisements
41  * and background parameter saving.
42  */
43 
44 #include "param.h"
45 #include <parameters/px4_parameters.h>
46 #include "tinybson/tinybson.h"
47 
48 #include <crc32.h>
49 #include <float.h>
50 #include <math.h>
51 
52 #include <drivers/drv_hrt.h>
53 #include <lib/perf/perf_counter.h>
54 #include <px4_platform_common/px4_config.h>
55 #include <px4_platform_common/defines.h>
56 #include <px4_platform_common/posix.h>
57 #include <px4_platform_common/sem.h>
58 #include <px4_platform_common/shutdown.h>
60 
61 using namespace time_literals;
62 
63 //#define PARAM_NO_ORB ///< if defined, avoid uorb dependency. This disables publication of parameter_update on param change
64 //#define PARAM_NO_AUTOSAVE ///< if defined, do not autosave (avoids LP work queue dependency)
65 
66 #if !defined(PARAM_NO_ORB)
67 # include "uORB/uORB.h"
70 # include <uORB/Subscription.hpp>
71 #endif
72 
73 #if defined(FLASH_BASED_PARAMS)
75 static const char *param_default_file = nullptr; // nullptr means to store to FLASH
76 #else
77 inline static int flash_param_save(bool only_unsaved) { return -1; }
78 inline static int flash_param_load() { return -1; }
79 inline static int flash_param_import() { return -1; }
80 static const char *param_default_file = PX4_ROOTFSDIR"/eeprom/parameters";
81 #endif
82 
83 static char *param_user_file = nullptr;
84 
85 #ifdef __PX4_QURT
86 #define PARAM_OPEN px4_open
87 #define PARAM_CLOSE px4_close
88 #else
89 #define PARAM_OPEN open
90 #define PARAM_CLOSE close
91 #endif
92 
93 #ifndef PARAM_NO_AUTOSAVE
94 #include <px4_platform_common/workqueue.h>
95 /* autosaving variables */
97 static struct work_s autosave_work {};
98 static volatile bool autosave_scheduled = false;
99 static bool autosave_disabled = false;
100 #endif /* PARAM_NO_AUTOSAVE */
101 
102 /**
103  * Array of static parameter info.
104  */
105 static const param_info_s *param_info_base = (const param_info_s *) &px4_parameters;
106 #define param_info_count px4_parameters.param_count
107 
108 /**
109  * Storage for modified parameters.
110  */
111 struct param_wbuf_s {
112  union param_value_u val;
113  param_t param;
114  bool unsaved;
115 };
116 
117 
118 uint8_t *param_changed_storage = nullptr;
120 const int bits_per_allocation_unit = (sizeof(*param_changed_storage) * 8);
121 
122 
123 static unsigned
125 {
126  /* Singleton creation of and array of bits to track changed values */
127  if (!param_changed_storage) {
128  /* Note that we have a (highly unlikely) race condition here: in the worst case the allocation is done twice */
131 
132  /* If the allocation fails we need to indicate failure in the
133  * API by returning PARAM_INVALID
134  */
135  if (param_changed_storage == nullptr) {
136  return 0;
137  }
138  }
139 
140  return param_info_count;
141 }
142 
143 /** flexible array holding modified parameter values */
145 
146 /** array info for the modified parameters array */
147 const UT_icd param_icd = {sizeof(param_wbuf_s), nullptr, nullptr, nullptr};
148 
149 #if !defined(PARAM_NO_ORB)
150 /** parameter update topic handle */
151 static orb_advert_t param_topic = nullptr;
152 static unsigned int param_instance = 0;
153 #endif
154 
155 static void param_set_used_internal(param_t param);
156 
157 static param_t param_find_internal(const char *name, bool notification);
158 
159 // the following implements an RW-lock using 2 semaphores (used as mutexes). It gives
160 // priority to readers, meaning a writer could suffer from starvation, but in our use-case
161 // we only have short periods of reads and writes are rare.
162 static px4_sem_t param_sem; ///< this protects against concurrent access to param_values
163 static int reader_lock_holders = 0;
164 static px4_sem_t reader_lock_holders_lock; ///< this protects against concurrent access to reader_lock_holders
165 
170 
171 static px4_sem_t param_sem_save; ///< this protects against concurrent param saves (file or flash access).
172 ///< we use a separate lock to allow concurrent param reads and saves.
173 ///< a param_set could still be blocked by a param save, because it
174 ///< needs to take the reader lock
175 
176 /** lock the parameter store for read access */
177 static void
179 {
180  do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0);
181 
183 
184  if (reader_lock_holders == 1) {
185  // the first reader takes the lock, the next ones are allowed to just continue
186  do {} while (px4_sem_wait(&param_sem) != 0);
187  }
188 
189  px4_sem_post(&reader_lock_holders_lock);
190 }
191 
192 /** lock the parameter store for write access */
193 static void
195 {
196  do {} while (px4_sem_wait(&param_sem) != 0);
197 }
198 
199 /** unlock the parameter store */
200 static void
202 {
203  do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0);
204 
206 
207  if (reader_lock_holders == 0) {
208  // the last reader releases the lock
209  px4_sem_post(&param_sem);
210  }
211 
212  px4_sem_post(&reader_lock_holders_lock);
213 }
214 
215 /** unlock the parameter store */
216 static void
218 {
219  px4_sem_post(&param_sem);
220 }
221 
222 /** assert that the parameter store is locked */
223 static void
225 {
226  /* XXX */
227 }
228 
229 void
231 {
232  px4_sem_init(&param_sem, 0, 1);
233  px4_sem_init(&param_sem_save, 0, 1);
234  px4_sem_init(&reader_lock_holders_lock, 0, 1);
235 
236  param_export_perf = perf_alloc(PC_ELAPSED, "param_export");
237  param_find_perf = perf_alloc(PC_ELAPSED, "param_find");
238  param_get_perf = perf_alloc(PC_ELAPSED, "param_get");
239  param_set_perf = perf_alloc(PC_ELAPSED, "param_set");
240 }
241 
242 /**
243  * Test whether a param_t is value.
244  *
245  * @param param The parameter handle to test.
246  * @return True if the handle is valid.
247  */
248 static bool
250 {
251  unsigned count = get_param_info_count();
252  return (count && param < count);
253 }
254 
255 /**
256  * Compare two modifid parameter structures to determine ordering.
257  *
258  * This function is suitable for passing to qsort or bsearch.
259  */
260 static int
261 param_compare_values(const void *a, const void *b)
262 {
263  struct param_wbuf_s *pa = (struct param_wbuf_s *)a;
264  struct param_wbuf_s *pb = (struct param_wbuf_s *)b;
265 
266  if (pa->param < pb->param) {
267  return -1;
268  }
269 
270  if (pa->param > pb->param) {
271  return 1;
272  }
273 
274  return 0;
275 }
276 
277 /**
278  * Locate the modified parameter structure for a parameter, if it exists.
279  *
280  * @param param The parameter being searched.
281  * @return The structure holding the modified value, or
282  * nullptr if the parameter has not been modified.
283  */
284 static param_wbuf_s *
286 {
287  param_wbuf_s *s = nullptr;
288 
290 
291  if (param_values != nullptr) {
292  param_wbuf_s key{};
293  key.param = param;
295  }
296 
297  return s;
298 }
299 
300 static void
302 {
303 #if !defined(PARAM_NO_ORB)
304  parameter_update_s pup = {};
306  pup.instance = param_instance++;
307 
308  /*
309  * If we don't have a handle to our topic, create one now; otherwise
310  * just publish.
311  */
312  if (param_topic == nullptr) {
313  param_topic = orb_advertise(ORB_ID(parameter_update), &pup);
314 
315  } else {
316  orb_publish(ORB_ID(parameter_update), param_topic, &pup);
317  }
318 
319 #endif
320 }
321 
322 void
324 {
326 }
327 
328 param_t
329 param_find_internal(const char *name, bool notification)
330 {
331  perf_begin(param_find_perf);
332 
333  param_t middle;
334  param_t front = 0;
335  param_t last = get_param_info_count();
336 
337  /* perform a binary search of the known parameters */
338 
339  while (front <= last) {
340  middle = front + (last - front) / 2;
341  int ret = strcmp(name, param_info_base[middle].name);
342 
343  if (ret == 0) {
344  if (notification) {
345  param_set_used_internal(middle);
346  }
347 
348  perf_end(param_find_perf);
349  return middle;
350 
351  } else if (middle == front) {
352  /* An end point has been hit, but there has been no match */
353  break;
354 
355  } else if (ret < 0) {
356  last = middle;
357 
358  } else {
359  front = middle;
360  }
361  }
362 
363  perf_end(param_find_perf);
364 
365  /* not found */
366  return PARAM_INVALID;
367 }
368 
369 param_t
370 param_find(const char *name)
371 {
372  return param_find_internal(name, true);
373 }
374 
375 param_t
377 {
378  return param_find_internal(name, false);
379 }
380 
381 unsigned
383 {
384  return get_param_info_count();
385 }
386 
387 unsigned
389 {
390  unsigned count = 0;
391 
392  // ensure the allocation has been done
393  if (get_param_info_count()) {
394 
395  for (int i = 0; i < size_param_changed_storage_bytes; i++) {
396  for (int j = 0; j < bits_per_allocation_unit; j++) {
397  if (param_changed_storage[i] & (1 << j)) {
398  count++;
399  }
400  }
401  }
402  }
403 
404  return count;
405 }
406 
407 param_t
408 param_for_index(unsigned index)
409 {
410  unsigned count = get_param_info_count();
411 
412  if (count && index < count) {
413  return (param_t)index;
414  }
415 
416  return PARAM_INVALID;
417 }
418 
419 param_t
420 param_for_used_index(unsigned index)
421 {
422  int count = get_param_info_count();
423 
424  if (count && (int)index < count) {
425  /* walk all params and count used params */
426  unsigned used_count = 0;
427 
428  for (int i = 0; i < size_param_changed_storage_bytes; i++) {
429  for (int j = 0; j < bits_per_allocation_unit; j++) {
430  if (param_changed_storage[i] & (1 << j)) {
431 
432  /* we found the right used count,
433  * return the param value
434  */
435  if (index == used_count) {
436  return (param_t)(i * bits_per_allocation_unit + j);
437  }
438 
439  used_count++;
440  }
441  }
442  }
443  }
444 
445  return PARAM_INVALID;
446 }
447 
448 int
450 {
451  if (handle_in_range(param)) {
452  return (unsigned)param;
453  }
454 
455  return -1;
456 }
457 
458 int
460 {
461  /* this tests for out of bounds and does a constant time lookup */
462  if (!param_used(param)) {
463  return -1;
464  }
465 
466  /* walk all params and count, now knowing that it has a valid index */
467  int used_count = 0;
468 
469  for (int i = 0; i < size_param_changed_storage_bytes; i++) {
470  for (int j = 0; j < bits_per_allocation_unit; j++) {
471  if (param_changed_storage[i] & (1 << j)) {
472 
473  if ((int)param == i * bits_per_allocation_unit + j) {
474  return used_count;
475  }
476 
477  used_count++;
478  }
479  }
480  }
481 
482  return -1;
483 }
484 
485 const char *
487 {
488  return handle_in_range(param) ? param_info_base[param].name : nullptr;
489 }
490 
491 bool
493 {
494  return handle_in_range(param) ? param_info_base[param].volatile_param : false;
495 }
496 
497 bool
499 {
500  struct param_wbuf_s *s;
502  s = param_find_changed(param);
504  return s == nullptr;
505 }
506 
507 bool
509 {
510  struct param_wbuf_s *s;
512  s = param_find_changed(param);
513  bool ret = s && s->unsaved;
515  return ret;
516 }
517 
520 {
521  return handle_in_range(param) ? param_info_base[param].type : PARAM_TYPE_UNKNOWN;
522 }
523 
524 size_t
526 {
527  if (handle_in_range(param)) {
528 
529  switch (param_type(param)) {
530 
531  case PARAM_TYPE_INT32:
532  case PARAM_TYPE_FLOAT:
533  return 4;
534 
536  /* decode structure size from type value */
537  return param_type(param) - PARAM_TYPE_STRUCT;
538 
539  default:
540  return 0;
541  }
542  }
543 
544  return 0;
545 }
546 
547 /**
548  * Obtain a pointer to the storage allocated for a parameter.
549  *
550  * @param param The parameter whose storage is sought.
551  * @return A pointer to the parameter value, or nullptr
552  * if the parameter does not exist.
553  */
554 static const void *
556 {
557  const void *result = nullptr;
558 
560 
561  if (handle_in_range(param)) {
562 
563  const union param_value_u *v;
564 
565  /* work out whether we're fetching the default or a written value */
566  struct param_wbuf_s *s = param_find_changed(param);
567 
568  if (s != nullptr) {
569  v = &s->val;
570 
571  } else {
572  v = &param_info_base[param].val;
573  }
574 
575  if (param_type(param) >= PARAM_TYPE_STRUCT &&
576  param_type(param) <= PARAM_TYPE_STRUCT_MAX) {
577 
578  result = v->p;
579 
580  } else {
581  result = v;
582  }
583  }
584 
585  return result;
586 }
587 
588 int
590 {
591  int result = -1;
592 
594  perf_begin(param_get_perf);
595 
596  const void *v = param_get_value_ptr(param);
597 
598  if (val && v) {
599  memcpy(val, v, param_size(param));
600  result = 0;
601  }
602 
603  perf_end(param_get_perf);
605 
606  return result;
607 }
608 
609 #ifndef PARAM_NO_AUTOSAVE
610 /**
611  * worker callback method to save the parameters
612  * @param arg unused
613  */
614 static void
615 autosave_worker(void *arg)
616 {
617  bool disabled = false;
618 
619 #if !defined(PARAM_NO_ORB)
620 
621  if (!param_get_default_file()) {
622  // In case we save to FLASH, defer param writes until disarmed,
623  // as writing to FLASH can stall the entire CPU (in rare cases around 300ms on STM32F7)
624  uORB::SubscriptionData<actuator_armed_s> armed_sub{ORB_ID(actuator_armed)};
625 
626  if (armed_sub.get().armed) {
627  work_queue(LPWORK, &autosave_work, (worker_t)&autosave_worker, nullptr, USEC2TICK(1_s));
628  return;
629  }
630  }
631 
632 #endif
633 
636  autosave_scheduled = false;
637  disabled = autosave_disabled;
639 
640  if (disabled) {
641  return;
642  }
643 
644  PX4_DEBUG("Autosaving params");
645  int ret = param_save_default();
646 
647  if (ret != 0) {
648  PX4_ERR("param auto save failed (%i)", ret);
649  }
650 }
651 #endif /* PARAM_NO_AUTOSAVE */
652 
653 /**
654  * Automatically save the parameters after a timeout and limited rate.
655  *
656  * This needs to be called with the writer lock held (it's not necessary that it's the writer lock, but it
657  * needs to be the same lock as autosave_worker() and param_control_autosave() use).
658  */
659 static void
661 {
662 #ifndef PARAM_NO_AUTOSAVE
663 
665  return;
666  }
667 
668  // wait at least 300ms before saving, because:
669  // - tasks often call param_set() for multiple params, so this avoids unnecessary save calls
670  // - the logger stores changed params. He gets notified on a param change via uORB and then
671  // looks at all unsaved params.
672  hrt_abstime delay = 300_ms;
673 
674  static constexpr const hrt_abstime rate_limit = 2_s; // rate-limit saving to 2 seconds
675  const hrt_abstime last_save_elapsed = hrt_elapsed_time(&last_autosave_timestamp);
676 
677  if (last_save_elapsed < rate_limit && rate_limit > last_save_elapsed + delay) {
678  delay = rate_limit - last_save_elapsed;
679  }
680 
681  autosave_scheduled = true;
682  work_queue(LPWORK, &autosave_work, (worker_t)&autosave_worker, nullptr, USEC2TICK(delay));
683 #endif /* PARAM_NO_AUTOSAVE */
684 }
685 
686 void
688 {
689 #ifndef PARAM_NO_AUTOSAVE
691 
692  if (!enable && autosave_scheduled) {
693  work_cancel(LPWORK, &autosave_work);
694  autosave_scheduled = false;
695  }
696 
697  autosave_disabled = !enable;
699 #endif /* PARAM_NO_AUTOSAVE */
700 }
701 
702 static int
703 param_set_internal(param_t param, const void *val, bool mark_saved, bool notify_changes)
704 {
705  int result = -1;
706  bool params_changed = false;
707 
709  perf_begin(param_set_perf);
710 
711  if (param_values == nullptr) {
712  utarray_new(param_values, &param_icd);
713  }
714 
715  if (param_values == nullptr) {
716  PX4_ERR("failed to allocate modified values array");
717  goto out;
718  }
719 
720  if (handle_in_range(param)) {
721 
722  param_wbuf_s *s = param_find_changed(param);
723 
724  if (s == nullptr) {
725 
726  /* construct a new parameter */
727  param_wbuf_s buf = {};
728  buf.param = param;
729 
730  params_changed = true;
731 
732  /* add it to the array and sort */
735 
736  /* find it after sorting */
737  s = param_find_changed(param);
738  }
739 
740  /* update the changed value */
741  switch (param_type(param)) {
742 
743  case PARAM_TYPE_INT32:
744  params_changed = params_changed || s->val.i != *(int32_t *)val;
745  s->val.i = *(int32_t *)val;
746  break;
747 
748  case PARAM_TYPE_FLOAT:
749  params_changed = params_changed || fabsf(s->val.f - * (float *)val) > FLT_EPSILON;
750  s->val.f = *(float *)val;
751  break;
752 
754  if (s->val.p == nullptr) {
755  size_t psize = param_size(param);
756 
757  if (psize > 0) {
758  s->val.p = malloc(psize);
759 
760  } else {
761  s->val.p = nullptr;
762  }
763 
764  if (s->val.p == nullptr) {
765  PX4_ERR("failed to allocate parameter storage");
766  goto out;
767  }
768  }
769 
770  memcpy(s->val.p, val, param_size(param));
771  params_changed = true;
772  break;
773 
774  default:
775  goto out;
776  }
777 
778  s->unsaved = !mark_saved;
779  result = 0;
780 
781  if (!mark_saved) { // this is false when importing parameters
782  param_autosave();
783  }
784  }
785 
786 out:
787  perf_end(param_set_perf);
789 
790  /*
791  * If we set something, now that we have unlocked, go ahead and advertise that
792  * a thing has been set.
793  */
794  if (params_changed && notify_changes) {
796  }
797 
798  return result;
799 }
800 
801 #if defined(FLASH_BASED_PARAMS)
802 int param_set_external(param_t param, const void *val, bool mark_saved, bool notify_changes)
803 {
804  return param_set_internal(param, val, mark_saved, notify_changes);
805 }
806 
808 {
809  return param_get_value_ptr(param);
810 }
811 #endif
812 
813 int
814 param_set(param_t param, const void *val)
815 {
816  return param_set_internal(param, val, false, true);
817 }
818 
819 int
821 {
822  return param_set_internal(param, val, false, false);
823 }
824 
825 bool
827 {
828  int param_index = param_get_index(param);
829 
830  if (param_index < 0) {
831  return false;
832  }
833 
834  return param_changed_storage[param_index / bits_per_allocation_unit] &
835  (1 << param_index % bits_per_allocation_unit);
836 }
837 
839 {
841 }
842 
844 {
845  int param_index = param_get_index(param);
846 
847  if (param_index < 0) {
848  return;
849  }
850 
851  // FIXME: this needs locking too
853  (1 << param_index % bits_per_allocation_unit);
854 }
855 
856 int
858 {
859  param_wbuf_s *s = nullptr;
860  bool param_found = false;
861 
863 
864  if (handle_in_range(param)) {
865 
866  /* look for a saved value */
867  s = param_find_changed(param);
868 
869  /* if we found one, erase it */
870  if (s != nullptr) {
871  int pos = utarray_eltidx(param_values, s);
872  utarray_erase(param_values, pos, 1);
873  }
874 
875  param_found = true;
876  }
877 
878  param_autosave();
879 
881 
882  if (s != nullptr) {
884  }
885 
886  return (!param_found);
887 }
888 static void
890 {
892 
893  if (param_values != nullptr) {
895  }
896 
897  /* mark as reset / deleted */
898  param_values = nullptr;
899 
900  if (auto_save) {
901  param_autosave();
902  }
903 
905 
907 }
908 
909 void
911 {
913 }
914 
915 void
916 param_reset_excludes(const char *excludes[], int num_excludes)
917 {
918  param_t param;
919 
920  for (param = 0; handle_in_range(param); param++) {
921  const char *name = param_name(param);
922  bool exclude = false;
923 
924  for (int index = 0; index < num_excludes; index ++) {
925  int len = strlen(excludes[index]);
926 
927  if ((excludes[index][len - 1] == '*'
928  && strncmp(name, excludes[index], len - 1) == 0)
929  || strcmp(name, excludes[index]) == 0) {
930 
931  exclude = true;
932  break;
933  }
934  }
935 
936  if (!exclude) {
937  param_reset(param);
938  }
939  }
940 
942 }
943 
944 int
945 param_set_default_file(const char *filename)
946 {
947 #ifdef FLASH_BASED_PARAMS
948  // the default for flash-based params is always the FLASH
949  (void)filename;
950 #else
951 
952  if (param_user_file != nullptr) {
953  // we assume this is not in use by some other thread
954  free(param_user_file);
955  param_user_file = nullptr;
956  }
957 
958  if (filename) {
959  param_user_file = strdup(filename);
960  }
961 
962 #endif /* FLASH_BASED_PARAMS */
963 
964  return 0;
965 }
966 
967 const char *
969 {
970  return (param_user_file != nullptr) ? param_user_file : param_default_file;
971 }
972 
973 int
975 {
976  int res = PX4_ERROR;
977 
978  const char *filename = param_get_default_file();
979 
980  if (!filename) {
981  perf_begin(param_export_perf);
983  res = flash_param_save(false);
985  perf_end(param_export_perf);
986  return res;
987  }
988 
989  /* write parameters to temp file */
990  int fd = PARAM_OPEN(filename, O_WRONLY | O_CREAT, PX4_O_MODE_666);
991 
992  if (fd < 0) {
993  PX4_ERR("failed to open param file: %s", filename);
994  return PX4_ERROR;
995  }
996 
997  int attempts = 5;
998 
999  while (res != OK && attempts > 0) {
1000  res = param_export(fd, false);
1001  attempts--;
1002 
1003  if (res != PX4_OK) {
1004  PX4_ERR("param_export failed, retrying %d", attempts);
1005  lseek(fd, 0, SEEK_SET); // jump back to the beginning of the file
1006  }
1007  }
1008 
1009  if (res != OK) {
1010  PX4_ERR("failed to write parameters to file: %s", filename);
1011  }
1012 
1013  PARAM_CLOSE(fd);
1014 
1015  return res;
1016 }
1017 
1018 /**
1019  * @return 0 on success, 1 if all params have not yet been stored, -1 if device open failed, -2 if writing parameters failed
1020  */
1021 int
1023 {
1024  int res = 0;
1025  const char *filename = param_get_default_file();
1026 
1027  if (!filename) {
1028  return flash_param_load();
1029  }
1030 
1031  int fd_load = PARAM_OPEN(filename, O_RDONLY);
1032 
1033  if (fd_load < 0) {
1034  /* no parameter file is OK, otherwise this is an error */
1035  if (errno != ENOENT) {
1036  PX4_ERR("open '%s' for reading failed", filename);
1037  return -1;
1038  }
1039 
1040  return 1;
1041  }
1042 
1043  int result = param_load(fd_load);
1044  PARAM_CLOSE(fd_load);
1045 
1046  if (result != 0) {
1047  PX4_ERR("error reading parameters from '%s'", filename);
1048  return -2;
1049  }
1050 
1051  return res;
1052 }
1053 
1054 int
1055 param_export(int fd, bool only_unsaved)
1056 {
1057  int result = -1;
1058  perf_begin(param_export_perf);
1059 
1060  if (fd < 0) {
1062  // flash_param_save() will take the shutdown lock
1063  result = flash_param_save(only_unsaved);
1065  perf_end(param_export_perf);
1066  return result;
1067  }
1068 
1069  param_wbuf_s *s = nullptr;
1070  struct bson_encoder_s encoder;
1071 
1072  int shutdown_lock_ret = px4_shutdown_lock();
1073 
1074  if (shutdown_lock_ret) {
1075  PX4_ERR("px4_shutdown_lock() failed (%i)", shutdown_lock_ret);
1076  }
1077 
1078  // take the file lock
1079  do {} while (px4_sem_wait(&param_sem_save) != 0);
1080 
1082 
1083  uint8_t bson_buffer[256];
1084  bson_encoder_init_buf_file(&encoder, fd, &bson_buffer, sizeof(bson_buffer));
1085 
1086  /* no modified parameters -> we are done */
1087  if (param_values == nullptr) {
1088  result = 0;
1089  goto out;
1090  }
1091 
1092  while ((s = (struct param_wbuf_s *)utarray_next(param_values, s)) != nullptr) {
1093  /*
1094  * If we are only saving values changed since last save, and this
1095  * one hasn't, then skip it
1096  */
1097  if (only_unsaved && !s->unsaved) {
1098  continue;
1099  }
1100 
1101  s->unsaved = false;
1102 
1103  const char *name = param_name(s->param);
1104  const size_t size = param_size(s->param);
1105 
1106  /* append the appropriate BSON type object */
1107  switch (param_type(s->param)) {
1108 
1109  case PARAM_TYPE_INT32: {
1110  const int32_t i = s->val.i;
1111 
1112  PX4_DEBUG("exporting: %s (%d) size: %d val: %d", name, s->param, size, i);
1113 
1114  if (bson_encoder_append_int(&encoder, name, i)) {
1115  PX4_ERR("BSON append failed for '%s'", name);
1116  goto out;
1117  }
1118  }
1119  break;
1120 
1121  case PARAM_TYPE_FLOAT: {
1122  const double f = (double)s->val.f;
1123 
1124  PX4_DEBUG("exporting: %s (%d) size: %d val: %.3f", name, s->param, size, (double)f);
1125 
1126  if (bson_encoder_append_double(&encoder, name, f)) {
1127  PX4_ERR("BSON append failed for '%s'", name);
1128  goto out;
1129  }
1130  }
1131  break;
1132 
1134  const void *value_ptr = param_get_value_ptr(s->param);
1135 
1136  /* lock as short as possible */
1137  if (bson_encoder_append_binary(&encoder,
1138  name,
1140  size,
1141  value_ptr)) {
1142 
1143  PX4_ERR("BSON append failed for '%s'", name);
1144  goto out;
1145  }
1146  }
1147  break;
1148 
1149  default:
1150  PX4_ERR("unrecognized parameter type");
1151  goto out;
1152  }
1153  }
1154 
1155  result = 0;
1156 
1157 out:
1158 
1159  if (result == 0) {
1160  if (bson_encoder_fini(&encoder) != PX4_OK) {
1161  PX4_ERR("bson encoder finish failed");
1162  }
1163  }
1164 
1166 
1167  px4_sem_post(&param_sem_save);
1168 
1169  if (shutdown_lock_ret == 0) {
1170  px4_shutdown_unlock();
1171  }
1172 
1173  perf_end(param_export_perf);
1174 
1175  return result;
1176 }
1177 
1178 struct param_import_state {
1179  bool mark_saved;
1180 };
1181 
1182 static int
1184 {
1185  float f = 0.0f;
1186  int32_t i = 0;
1187  void *tmp = nullptr;
1188  void *v = nullptr;
1189  int result = -1;
1191 
1192  /*
1193  * EOO means the end of the parameter object. (Currently not supporting
1194  * nested BSON objects).
1195  */
1196  if (node->type == BSON_EOO) {
1197  PX4_DEBUG("end of parameters");
1198  return 0;
1199  }
1200 
1201  /*
1202  * Find the parameter this node represents. If we don't know it,
1203  * ignore the node.
1204  */
1205  param_t param = param_find_no_notification(node->name);
1206 
1207  if (param == PARAM_INVALID) {
1208  PX4_ERR("ignoring unrecognised parameter '%s'", node->name);
1209  return 1;
1210  }
1211 
1212  /*
1213  * Handle setting the parameter from the node
1214  */
1215 
1216  switch (node->type) {
1217  case BSON_INT32: {
1218  if (param_type(param) != PARAM_TYPE_INT32) {
1219  PX4_WARN("unexpected type for %s", node->name);
1220  result = 1; // just skip this entry
1221  goto out;
1222  }
1223 
1224  i = node->i;
1225  v = &i;
1226 
1227  PX4_DEBUG("Imported %s with value %d", param_name(param), i);
1228  }
1229  break;
1230 
1231  case BSON_DOUBLE: {
1232  if (param_type(param) != PARAM_TYPE_FLOAT) {
1233  PX4_WARN("unexpected type for %s", node->name);
1234  result = 1; // just skip this entry
1235  goto out;
1236  }
1237 
1238  f = node->d;
1239  v = &f;
1240 
1241  PX4_DEBUG("Imported %s with value %f", param_name(param), (double)f);
1242  }
1243  break;
1244 
1245  case BSON_BINDATA: {
1246  if (node->subtype != BSON_BIN_BINARY) {
1247  PX4_WARN("unexpected subtype for %s", node->name);
1248  result = 1; // just skip this entry
1249  goto out;
1250  }
1251 
1252  if (bson_decoder_data_pending(decoder) != param_size(param)) {
1253  PX4_WARN("bad size for '%s'", node->name);
1254  result = 1; // just skip this entry
1255  goto out;
1256  }
1257 
1258  /* XXX check actual file data size? */
1259  size_t psize = param_size(param);
1260 
1261  if (psize > 0) {
1262  tmp = malloc(psize);
1263 
1264  } else {
1265  tmp = nullptr;
1266  }
1267 
1268  if (tmp == nullptr) {
1269  PX4_ERR("failed allocating for '%s'", node->name);
1270  goto out;
1271  }
1272 
1273  if (bson_decoder_copy_data(decoder, tmp)) {
1274  PX4_ERR("failed copying data for '%s'", node->name);
1275  goto out;
1276  }
1277 
1278  v = tmp;
1279  }
1280  break;
1281 
1282  default:
1283  PX4_DEBUG("unrecognised node type");
1284  goto out;
1285  }
1286 
1287  if (param_set_internal(param, v, state->mark_saved, true)) {
1288  PX4_DEBUG("error setting value for '%s'", node->name);
1289  goto out;
1290  }
1291 
1292  if (tmp != nullptr) {
1293  free(tmp);
1294  tmp = nullptr;
1295  }
1296 
1297  /* don't return zero, that means EOF */
1298  result = 1;
1299 
1300 out:
1301 
1302  if (tmp != nullptr) {
1303  free(tmp);
1304  }
1305 
1306  return result;
1307 }
1308 
1309 static int
1310 param_import_internal(int fd, bool mark_saved)
1311 {
1312  bson_decoder_s decoder;
1314  int result = -1;
1315 
1316  if (bson_decoder_init_file(&decoder, fd, param_import_callback, &state)) {
1317  PX4_ERR("decoder init failed");
1318  return PX4_ERROR;
1319  }
1320 
1321  state.mark_saved = mark_saved;
1322 
1323  do {
1324  result = bson_decoder_next(&decoder);
1325 
1326  } while (result > 0);
1327 
1328  return result;
1329 }
1330 
1331 int
1333 {
1334  if (fd < 0) {
1335  return flash_param_import();
1336  }
1337 
1338  return param_import_internal(fd, false);
1339 }
1340 
1341 int
1343 {
1344  if (fd < 0) {
1345  return flash_param_load();
1346  }
1347 
1348  param_reset_all_internal(false);
1349  return param_import_internal(fd, true);
1350 }
1351 
1352 void
1353 param_foreach(void (*func)(void *arg, param_t param), void *arg, bool only_changed, bool only_used)
1354 {
1355  param_t param;
1356 
1357  for (param = 0; handle_in_range(param); param++) {
1358 
1359  /* if requested, skip unchanged values */
1360  if (only_changed && (param_find_changed(param) == nullptr)) {
1361  continue;
1362  }
1363 
1364  if (only_used && !param_used(param)) {
1365  continue;
1366  }
1367 
1368  func(arg, param);
1369  }
1370 }
1371 
1373 {
1374  uint32_t param_hash = 0;
1375 
1377 
1378  /* compute the CRC32 over all string param names and 4 byte values */
1379  for (param_t param = 0; handle_in_range(param); param++) {
1380  if (!param_used(param) || param_is_volatile(param)) {
1381  continue;
1382  }
1383 
1384  const char *name = param_name(param);
1385  const void *val = param_get_value_ptr(param);
1386  param_hash = crc32part((const uint8_t *)name, strlen(name), param_hash);
1387  param_hash = crc32part((const uint8_t *)val, param_size(param), param_hash);
1388  }
1389 
1391 
1392  return param_hash;
1393 }
1394 
1396 {
1397  PX4_INFO("summary: %d/%d (used/total)", param_count_used(), param_count());
1398 
1399 #ifndef FLASH_BASED_PARAMS
1400  const char *filename = param_get_default_file();
1401 
1402  if (filename != nullptr) {
1403  PX4_INFO("file: %s", param_get_default_file());
1404  }
1405 
1406 #endif /* FLASH_BASED_PARAMS */
1407 
1408  if (param_values != nullptr) {
1409  PX4_INFO("storage array: %d/%d elements (%zu bytes total)",
1411  }
1412 
1413 #ifndef PARAM_NO_AUTOSAVE
1414  PX4_INFO("auto save: %s", autosave_disabled ? "off" : "on");
1415 
1417  PX4_INFO("last auto save: %.3f seconds ago", hrt_elapsed_time(&last_autosave_timestamp) * 1e-6);
1418  }
1419 
1420 #endif /* PARAM_NO_AUTOSAVE */
1421 
1422  perf_print_counter(param_export_perf);
1423  perf_print_counter(param_find_perf);
1424  perf_print_counter(param_get_perf);
1425  perf_print_counter(param_set_perf);
1426 }
param_t param_for_index(unsigned index)
Look up a parameter by index.
Definition: parameters.cpp:408
void param_set_used(param_t param)
Mark a parameter as used.
Definition: parameters.cpp:838
static px4_sem_t param_sem_save
this protects against concurrent param saves (file or flash access).
Definition: parameters.cpp:171
#define PARAM_INVALID
Handle returned when a parameter cannot be found.
Definition: param.h:103
#define utarray_find(a, v, cmp)
Definition: utarray.h:212
int bson_encoder_init_buf_file(bson_encoder_t encoder, int fd, void *buf, unsigned bufsize)
Initialze the encoder for writing to a file.
Definition: tinybson.cpp:443
param_type_t type
Definition: param.h:446
char name[BSON_MAXNAME]
Definition: tinybson.h:85
unsigned param_count()
Return the total number of parameters.
Definition: parameters.cpp:382
int param_export(int fd, bool only_unsaved)
Export changed parameters to a file.
int param_save_default()
Save parameters to the default file.
Definition: parameters.cpp:974
param_t param_find_no_notification(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:376
static void param_unlock_reader()
unlock the parameter store
Definition: parameters.cpp:201
int bson_decoder_next(bson_decoder_t decoder)
Process the next node from the stream and invoke the callback.
Definition: tinybson.cpp:180
#define PARAM_TYPE_UNKNOWN
Definition: param.h:64
measure the time elapsed performing an event
Definition: perf_counter.h:56
uint16_t volatile_param
Definition: param.h:447
static enum @74 state
#define PARAM_TYPE_INT32
Parameter types.
Definition: param.h:60
int bson_encoder_append_binary(bson_encoder_t encoder, const char *name, bson_binary_subtype_t subtype, size_t size, const void *data)
Append a binary blob to the encoded stream.
Definition: tinybson.cpp:614
static char * param_user_file
Definition: parameters.cpp:83
const char * name
Definition: param.h:444
API for the uORB lightweight object broker.
int bson_decoder_init_file(bson_decoder_t decoder, int fd, bson_decoder_callback callback, void *priv)
Initialise the decoder to read from a file.
Definition: tinybson.cpp:114
void param_init()
Initialize the param backend.
Definition: parameters.cpp:230
int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
#define PARAM_TYPE_STRUCT
Definition: param.h:62
uint16_t param_type_t
Definition: param.h:66
Storage for modified parameters.
Definition: flashparams.cpp:71
static volatile bool autosave_scheduled
Definition: parameters.cpp:98
static int param_import_internal(int fd, bool mark_saved)
bson_binary_subtype_t subtype
Definition: tinybson.h:87
static void param_set_used_internal(param_t param)
Definition: parameters.cpp:843
__EXPORT int param_set_external(param_t param, const void *val, bool mark_saved, bool notify_changes)
#define utarray_sort(a, cmp)
Definition: utarray.h:208
Encoder state structure.
Definition: tinybson.h:173
union param_value_u val
Definition: flashparams.cpp:72
static bool handle_in_range(param_t param)
Test whether a param_t is value.
Definition: parameters.cpp:249
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
Definition: uORB.cpp:43
static const param_info_s * param_info_base
Array of static parameter info.
Definition: parameters.cpp:105
bool param_is_volatile(param_t param)
Obtain the volatile state of a parameter.
Definition: parameters.cpp:492
#define utarray_free(a)
Definition: utarray.h:84
static orb_advert_t param_topic
parameter update topic handle
Definition: parameters.cpp:151
__EXPORT const void * param_get_value_ptr_external(param_t param)
int bson_decoder_copy_data(bson_decoder_t decoder, void *buf)
Copy node data.
Definition: tinybson.cpp:312
static perf_counter_t param_find_perf
Definition: parameters.cpp:167
#define FLT_EPSILON
High-resolution timer with callouts and timekeeping.
int32_t i
Definition: param.h:418
Node structure passed to the callback.
Definition: tinybson.h:84
#define param_info_count
Definition: parameters.cpp:106
param_t param_for_used_index(unsigned index)
Look up an used parameter by index.
Definition: parameters.cpp:420
void * p
Definition: param.h:417
A simple subset SAX-style BSON parser and generator.
static perf_counter_t param_export_perf
Definition: parameters.cpp:166
Global flash based parameter store.
unsigned param_count_used()
Return the actually used number of parameters.
Definition: parameters.cpp:388
static void param_reset_all_internal(bool auto_save)
Definition: parameters.cpp:889
int bson_encoder_append_double(bson_encoder_t encoder, const char *name, double value)
Append a double to the encoded stream.
Definition: tinybson.cpp:580
static void autosave_worker(void *arg)
worker callback method to save the parameters
Definition: parameters.cpp:615
Static parameter definition structure.
Definition: param.h:428
const char * param_get_default_file()
Get the default parameter file name.
Definition: parameters.cpp:968
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
int param_reset(param_t param)
Reset a parameter to its default value.
Definition: parameters.cpp:857
uint32_t param_hash_check()
Generate the hash of all parameters and their values.
static void param_unlock_writer()
unlock the parameter store
Definition: parameters.cpp:217
Parameter value union.
Definition: param.h:416
Header common to all counters.
const int bits_per_allocation_unit
Definition: parameters.cpp:120
const UT_icd param_icd
array info for the modified parameters array
Definition: parameters.cpp:147
static int reader_lock_holders
Definition: parameters.cpp:163
static param_wbuf_s * param_find_changed(param_t param)
Locate the modified parameter structure for a parameter, if it exists.
Definition: parameters.cpp:285
#define perf_alloc(a, b)
Definition: px4io.h:59
param_type_t param_type(param_t param)
Obtain the type of a parameter.
Definition: parameters.cpp:519
static bool autosave_disabled
Definition: parameters.cpp:99
#define utarray_erase(a, pos, len)
Definition: utarray.h:177
void param_reset_excludes(const char *excludes[], int num_excludes)
Reset all parameters to their default values except for excluded parameters.
Definition: parameters.cpp:916
#define utarray_new(a, _icd)
Definition: utarray.h:79
static px4_sem_t param_sem
this protects against concurrent access to param_values
Definition: parameters.cpp:162
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
static int param_compare_values(const void *a, const void *b)
Compare two modifid parameter structures to determine ordering.
Definition: parameters.cpp:261
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
size_t bson_decoder_data_pending(bson_decoder_t decoder)
Report copyable data size.
Definition: tinybson.cpp:331
static int flash_param_load()
Definition: parameters.cpp:78
#define PARAM_TYPE_STRUCT_MAX
Definition: param.h:63
param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
void param_print_status()
Print the status of the param system.
static void param_lock_reader()
lock the parameter store for read access
Definition: parameters.cpp:178
const char * param_name(param_t param)
Obtain the name of a parameter.
Definition: parameters.cpp:486
#define utarray_next(a, e)
Definition: utarray.h:215
static void param_lock_writer()
lock the parameter store for write access
Definition: parameters.cpp:194
void perf_end(perf_counter_t handle)
End a performance event.
unsigned n
Definition: utarray.h:56
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
bool param_value_is_default(param_t param)
Test whether a parameter&#39;s value has changed from the default.
Definition: parameters.cpp:498
__BEGIN_DECLS typedef void * orb_advert_t
ORB topic advertiser handle.
Definition: uORB.h:134
int fd
Definition: dataman.cpp:146
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
int bson_encoder_fini(bson_encoder_t encoder)
Finalise the encoded stream.
Definition: tinybson.cpp:484
bool param_value_unsaved(param_t param)
Test whether a parameter&#39;s value has been changed but not saved.
Definition: parameters.cpp:508
int param_set(param_t param, const void *val)
Set the value of a parameter.
Definition: parameters.cpp:814
static unsigned get_param_info_count()
Definition: parameters.cpp:124
int param_load_default()
Load parameters from the default parameter file.
bson_type_t type
Definition: tinybson.h:86
static void param_assert_locked()
assert that the parameter store is locked
Definition: parameters.cpp:224
#define PARAM_OPEN
Definition: parameters.cpp:89
static int param_set_internal(param_t param, const void *val, bool mark_saved, bool notify_changes)
Definition: parameters.cpp:703
const char * name
Definition: tests_main.c:58
void param_control_autosave(bool enable)
Enable/disable the param autosaving.
Definition: parameters.cpp:687
#define PARAM_TYPE_FLOAT
Definition: param.h:61
static perf_counter_t param_set_perf
Definition: parameters.cpp:169
static int flash_param_import()
Definition: parameters.cpp:79
int param_set_no_notification(param_t param, const void *val)
Set the value of a parameter, but do not notify the system about the change.
Definition: parameters.cpp:820
int param_set_default_file(const char *filename)
Set the default parameter file name.
Definition: parameters.cpp:945
uint8_t * param_changed_storage
Definition: parameters.cpp:118
bool param_used(param_t param)
Wether a parameter is in use in the system.
Definition: parameters.cpp:826
void param_foreach(void(*func)(void *arg, param_t param), void *arg, bool only_changed, bool only_used)
Apply a function to each parameter.
static param_t param_find_internal(const char *name, bool notification)
Definition: parameters.cpp:329
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
static unsigned int param_instance
Definition: parameters.cpp:152
static void _param_notify_changes()
Definition: parameters.cpp:301
float f
Definition: param.h:419
static perf_counter_t param_get_perf
Definition: parameters.cpp:168
void param_notify_changes()
Notify the system about parameter changes.
Definition: parameters.cpp:323
static void param_autosave()
Automatically save the parameters after a timeout and limited rate.
Definition: parameters.cpp:660
int param_get_index(param_t param)
Look up the index of a parameter.
Definition: parameters.cpp:449
union param_value_u val
Definition: param.h:448
static hrt_abstime last_autosave_timestamp
Definition: parameters.cpp:96
param_t param
Definition: flashparams.cpp:73
UT_array * param_values
flexible array holding modified parameter values
Definition: parameters.cpp:144
#define utarray_len(a)
Definition: utarray.h:114
size_t param_size(param_t param)
Determine the size of a parameter.
Definition: parameters.cpp:525
#define PARAM_CLOSE
Definition: parameters.cpp:90
static const void * param_get_value_ptr(param_t param)
Obtain a pointer to the storage allocated for a parameter.
Definition: parameters.cpp:555
int param_import(int fd)
Import parameters from a file, discarding any unrecognized parameters.
Definition: utarray.h:48
#define OK
Definition: uavcan_main.cpp:71
static const char * param_default_file
Definition: parameters.cpp:80
int64_t i
Definition: tinybson.h:89
int size_param_changed_storage_bytes
Definition: parameters.cpp:119
int param_get_used_index(param_t param)
Look up the index of an used parameter.
Definition: parameters.cpp:459
int param_load(int fd)
Load parameters from a file.
void param_reset_all()
Reset all parameters to their default values.
Definition: parameters.cpp:910
static px4_sem_t reader_lock_holders_lock
this protects against concurrent access to reader_lock_holders
Definition: parameters.cpp:164
#define utarray_eltidx(a, e)
Definition: utarray.h:218
void perf_begin(perf_counter_t handle)
Begin a performance event.
static int param_import_callback(bson_decoder_t decoder, void *priv, bson_node_t node)
double d
Definition: tinybson.h:90
int bson_encoder_append_int(bson_encoder_t encoder, const char *name, int64_t value)
Append an integer to the encoded stream.
Definition: tinybson.cpp:552
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uint32_t param_t
Parameter handle.
Definition: param.h:98
#define utarray_push_back(a, p)
Definition: utarray.h:96
static int flash_param_save(bool only_unsaved)
Definition: parameters.cpp:77
Performance measuring tools.