LCOV - code coverage report
Current view: top level - AP_HAL_Linux - Storage.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 52 116 44.8 %
Date: 2026-09-09 11:02:08 Functions: 7 9 77.8 %

          Line data    Source code
       1             : #include "Storage.h"
       2             : 
       3             : #include <assert.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <stdio.h>
       7             : #include <sys/stat.h>
       8             : #include <sys/types.h>
       9             : #include <unistd.h>
      10             : 
      11             : #include <AP_HAL/AP_HAL.h>
      12             : 
      13             : using namespace Linux;
      14             : 
      15             : /*
      16             :   This stores 'eeprom' data on the SD card, with a 4k size, and a
      17             :   in-memory buffer. This keeps the latency down.
      18             :  */
      19             : 
      20             : // name the storage file after the sketch so you can use the same board
      21             : // card for ArduCopter and ArduPlane
      22             : #define STORAGE_FILE AP_BUILD_TARGET_NAME ".stg"
      23             : 
      24             : extern const AP_HAL::HAL& hal;
      25             : 
      26           2 : static inline int is_dir(const char *path)
      27             : {
      28             :     struct stat st;
      29             : 
      30           2 :     if (stat(path, &st) < 0) {
      31           0 :         return -errno;
      32             :     }
      33             : 
      34           2 :     return S_ISDIR(st.st_mode);
      35             : }
      36             : 
      37           2 : static int mkdir_p(const char *path, int len, mode_t mode)
      38             : {
      39             :     char *start, *end;
      40             : 
      41           2 :     start = strndupa(path, len);
      42           2 :     end = start + len;
      43             : 
      44             :     /*
      45             :      * scan backwards, replacing '/' with '\0' while the component doesn't
      46             :      * exist
      47             :      */
      48             :     for (;;) {
      49           2 :         int r = is_dir(start);
      50           2 :         if (r > 0) {
      51           2 :             end += strlen(end);
      52             : 
      53           2 :             if (end == start + len) {
      54           2 :                 return 0;
      55             :             }
      56             : 
      57             :             /* end != start, since it would be caught on the first
      58             :              * iteration */
      59           0 :             *end = '/';
      60           0 :             break;
      61           0 :         } else if (r == 0) {
      62           0 :             return -ENOTDIR;
      63             :         }
      64             : 
      65           0 :         if (end == start) {
      66           0 :             break;
      67             :         }
      68             : 
      69           0 :         *end = '\0';
      70             : 
      71             :         /* Find the next component, backwards, discarding extra '/'*/
      72           0 :         while (end > start && *end != '/') {
      73           0 :             end--;
      74             :         }
      75             : 
      76           0 :         while (end > start && *(end - 1) == '/') {
      77           0 :             end--;
      78             :         }
      79           0 :     }
      80             : 
      81           0 :     while (end < start + len) {
      82           0 :         if (mkdir(start, mode) < 0 && errno != EEXIST) {
      83           0 :             return -errno;
      84             :         }
      85             : 
      86           0 :         end += strlen(end);
      87           0 :         *end = '/';
      88             :     }
      89             : 
      90           0 :     return 0;
      91             : }
      92             : 
      93           2 : int Storage::_storage_create(const char *dpath)
      94             : {
      95           2 :     int dfd = -1;
      96             : 
      97           2 :     mkdir_p(dpath, strlen(dpath), 0777);
      98           2 :     dfd = open(dpath, O_RDONLY|O_CLOEXEC);
      99           2 :     if (dfd == -1) {
     100           0 :         fprintf(stderr, "Failed to open storage directory: %s (%m)\n", dpath);
     101           0 :         return -1;
     102             :     }
     103             : 
     104           2 :     int fd = openat(dfd, STORAGE_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0666);
     105             : 
     106           2 :     if (fd == -1) {
     107           0 :         fprintf(stderr, "Failed to create storage file %s/%s\n", dpath,
     108             :                 STORAGE_FILE);
     109           0 :         goto fail;
     110             :     }
     111             : 
     112             :     // take up all needed space
     113           2 :     if (ftruncate(fd, sizeof(_buffer)) == -1) {
     114           0 :         fprintf(stderr, "Failed to set file size to %u kB (%m)\n",
     115             :                 unsigned(sizeof(_buffer) / 1024));
     116           0 :         close(fd);
     117           0 :         goto fail;
     118             :     }
     119             : 
     120             :     // ensure the directory is updated with the new size
     121           2 :     fsync(fd);
     122           2 :     fsync(dfd);
     123             : 
     124           2 :     close(dfd);
     125             : 
     126           2 :     return fd;
     127             : 
     128           0 : fail:
     129           0 :     close(dfd);
     130           0 :     return -1;
     131             : }
     132             : 
     133          61 : void Storage::init()
     134             : {
     135             :     const char *dpath;
     136             : 
     137          61 :     if (_initialised) {
     138          59 :         return;
     139             :     }
     140             : 
     141           2 :     _dirty_mask = 0;
     142             : 
     143           2 :     dpath = hal.util->get_custom_storage_directory();
     144           2 :     if (!dpath) {
     145           2 :         dpath = HAL_BOARD_STORAGE_DIRECTORY;
     146             :     }
     147             : 
     148           2 :     int fd = _storage_create(dpath);
     149           2 :     if (fd == -1) {
     150           0 :         AP_HAL::panic("Cannot create storage %s (%m)", dpath);
     151             :     }
     152             : 
     153           2 :     ssize_t ret = read(fd, _buffer, sizeof(_buffer));
     154             : 
     155           2 :     if (ret != sizeof(_buffer)) {
     156           0 :         close(fd);
     157           0 :         AP_HAL::panic("Failed to read %s (%m)", dpath);
     158             :     }
     159             : 
     160           2 :     _fd = fd;
     161           2 :     _initialised = true;
     162             : }
     163             : 
     164             : /*
     165             :   mark some lines as dirty. Note that there is no attempt to avoid
     166             :   the race condition between this code and the _timer_tick() code
     167             :   below, which both update _dirty_mask. If we lose the race then the
     168             :   result is that a line is written more than once, but it won't result
     169             :   in a line not being written.
     170             :  */
     171          17 : void Storage::_mark_dirty(uint16_t loc, uint16_t length)
     172             : {
     173          17 :     if (length == 0) {
     174           0 :         return;
     175             :     }
     176          17 :     uint16_t end = loc + length - 1;
     177          17 :     for (uint8_t line=loc>>LINUX_STORAGE_LINE_SHIFT;
     178          34 :          line <= end>>LINUX_STORAGE_LINE_SHIFT;
     179             :          line++) {
     180          17 :         _dirty_mask |= 1U << line;
     181             :     }
     182             : }
     183             : 
     184          44 : void Storage::read_block(void *dst, uint16_t loc, size_t n)
     185             : {
     186          44 :     if (loc >= sizeof(_buffer)-(n-1)) {
     187           0 :         return;
     188             :     }
     189          44 :     init();
     190          44 :     memcpy(dst, &_buffer[loc], n);
     191             : }
     192             : 
     193          22 : void Storage::write_block(uint16_t loc, const void *src, size_t n)
     194             : {
     195          22 :     if (loc >= sizeof(_buffer)-(n-1)) {
     196           0 :         return;
     197             :     }
     198          22 :     if (memcmp(src, &_buffer[loc], n) != 0) {
     199          17 :         init();
     200          17 :         memcpy(&_buffer[loc], src, n);
     201          17 :         _mark_dirty(loc, n);
     202             :     }
     203             : }
     204             : 
     205           0 : void Storage::_timer_tick(void)
     206             : {
     207           0 :     if (!_initialised || _dirty_mask == 0 || _fd == -1) {
     208           0 :         return;
     209             :     }
     210             : 
     211             :     // write out the first dirty set of lines. We don't write more
     212             :     // than one to keep the latency of this call to a minimum
     213             :     uint8_t i, n;
     214           0 :     for (i=0; i<LINUX_STORAGE_NUM_LINES; i++) {
     215           0 :         if (_dirty_mask & (1<<i)) {
     216           0 :             break;
     217             :         }
     218             :     }
     219           0 :     if (i == LINUX_STORAGE_NUM_LINES) {
     220             :         // this shouldn't be possible
     221           0 :         return;
     222             :     }
     223           0 :     uint32_t write_mask = (1U<<i);
     224             :     // see how many lines to write
     225           0 :     for (n=1; (i+n) < LINUX_STORAGE_NUM_LINES &&
     226             :              n < (LINUX_STORAGE_MAX_WRITE>>LINUX_STORAGE_LINE_SHIFT); n++) {
     227           0 :         if (!(_dirty_mask & (1<<(n+i)))) {
     228           0 :             break;
     229             :         }
     230             :         // mark that line clean
     231           0 :         write_mask |= (1<<(n+i));
     232             :     }
     233             : 
     234             :     /*
     235             :       write the lines. This also updates _dirty_mask. Note that
     236             :       because this is a SCHED_FIFO thread it will not be preempted
     237             :       by the main task except during blocking calls. This means we
     238             :       don't need a semaphore around the _dirty_mask updates.
     239             :      */
     240           0 :     if (lseek(_fd, i<<LINUX_STORAGE_LINE_SHIFT, SEEK_SET) == (i<<LINUX_STORAGE_LINE_SHIFT)) {
     241           0 :         _dirty_mask &= ~write_mask;
     242           0 :         if (write(_fd, &_buffer[i<<LINUX_STORAGE_LINE_SHIFT], n<<LINUX_STORAGE_LINE_SHIFT) != n<<LINUX_STORAGE_LINE_SHIFT) {
     243             :             // write error - likely EINTR
     244           0 :             _dirty_mask |= write_mask;
     245           0 :             close(_fd);
     246           0 :             _fd = -1;
     247             :         }
     248           0 :         if (_dirty_mask == 0) {
     249           0 :             if (fsync(_fd) != 0) {
     250           0 :                 close(_fd);
     251           0 :                 _fd = -1;
     252             :             }
     253             :         }
     254             :     }
     255             : }
     256             : 
     257             : /*
     258             :   get storage size and ptr
     259             :  */
     260           0 : bool Storage::get_storage_ptr(void *&ptr, size_t &size)
     261             : {
     262           0 :     if (!_initialised) {
     263           0 :         return false;
     264             :     }
     265           0 :     ptr = _buffer;
     266           0 :     size = sizeof(_buffer);
     267           0 :     return true;
     268             : }

Generated by: LCOV version 1.14