AP_Logger_Block.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. /*
  2. block based logging, for boards with flash logging
  3. */
  4. #include "AP_Logger_Block.h"
  5. #include <AP_HAL/AP_HAL.h>
  6. #include <stdio.h>
  7. extern AP_HAL::HAL& hal;
  8. // the last page holds the log format in first 4 bytes. Please change
  9. // this if (and only if!) the low level format changes
  10. #define DF_LOGGING_FORMAT 0x1901201A
  11. AP_Logger_Block::AP_Logger_Block(AP_Logger &front, LoggerMessageWriter_DFLogStart *writer) :
  12. writebuf(0),
  13. AP_Logger_Backend(front, writer)
  14. {
  15. buffer = (uint8_t *)hal.util->malloc_type(page_size_max, AP_HAL::Util::MEM_DMA_SAFE);
  16. if (buffer == nullptr) {
  17. AP_HAL::panic("Out of DMA memory for logging");
  18. }
  19. }
  20. // init is called after backend init
  21. void AP_Logger_Block::Init(void)
  22. {
  23. if (CardInserted()) {
  24. // reserve space for version in last sector
  25. df_NumPages -= df_PagePerSector;
  26. // determine and limit file backend buffersize
  27. uint32_t bufsize = _front._params.file_bufsize;
  28. if (bufsize > 64) {
  29. bufsize = 64;
  30. }
  31. bufsize *= 1024;
  32. // If we can't allocate the full size, try to reduce it until we can allocate it
  33. while (!writebuf.set_size(bufsize) && bufsize >= df_PageSize * df_PagePerSector) {
  34. hal.console->printf("AP_Logger_Block: Couldn't set buffer size to=%u\n", (unsigned)bufsize);
  35. bufsize >>= 1;
  36. }
  37. if (!writebuf.get_size()) {
  38. hal.console->printf("Out of memory for logging\n");
  39. return;
  40. }
  41. hal.console->printf("AP_Logger_Block: buffer size=%u\n", (unsigned)bufsize);
  42. _initialised = true;
  43. }
  44. WITH_SEMAPHORE(sem);
  45. hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_Logger_Block::io_timer, void));
  46. AP_Logger_Backend::Init();
  47. }
  48. uint32_t AP_Logger_Block::bufferspace_available()
  49. {
  50. // because AP_Logger_Block devices are ring buffers, we *always*
  51. // have room...
  52. return df_NumPages * df_PageSize;
  53. }
  54. // *** LOGGER PUBLIC FUNCTIONS ***
  55. void AP_Logger_Block::StartWrite(uint32_t PageAdr)
  56. {
  57. df_PageAdr = PageAdr;
  58. log_write_started = true;
  59. }
  60. void AP_Logger_Block::FinishWrite(void)
  61. {
  62. // Write Buffer to flash
  63. BufferToPage(df_PageAdr);
  64. df_PageAdr++;
  65. // If we reach the end of the memory, start from the beginning
  66. if (df_PageAdr > df_NumPages) {
  67. df_PageAdr = 1;
  68. }
  69. // when starting a new sector, erase it
  70. if ((df_PageAdr-1) % df_PagePerSector == 0) {
  71. SectorErase(df_PageAdr / df_PagePerSector);
  72. }
  73. }
  74. bool AP_Logger_Block::WritesOK() const
  75. {
  76. if (!CardInserted()) {
  77. return false;
  78. }
  79. return true;
  80. }
  81. bool AP_Logger_Block::_WritePrioritisedBlock(const void *pBuffer, uint16_t size, bool is_critical)
  82. {
  83. // is_critical is ignored - we're a ring buffer and never run out
  84. // of space. possibly if we do more complicated bandwidth
  85. // limiting we can reserve bandwidth based on is_critical
  86. if (!WritesOK()) {
  87. return false;
  88. }
  89. if (! WriteBlockCheckStartupMessages()) {
  90. return false;
  91. }
  92. if (writebuf.space() < size) {
  93. // no room in buffer
  94. return false;
  95. }
  96. writebuf.write((uint8_t*)pBuffer, size);
  97. return true;
  98. }
  99. void AP_Logger_Block::StartRead(uint32_t PageAdr)
  100. {
  101. df_Read_PageAdr = PageAdr;
  102. // copy flash page to buffer
  103. if (erase_started) {
  104. memset(buffer, 0xff, df_PageSize);
  105. } else {
  106. PageToBuffer(df_Read_PageAdr);
  107. }
  108. // We are starting a new page - read FileNumber and FilePage
  109. struct PageHeader ph;
  110. BlockRead(0, &ph, sizeof(ph));
  111. df_FileNumber = ph.FileNumber;
  112. df_FilePage = ph.FilePage;
  113. df_Read_BufferIdx = sizeof(ph);
  114. }
  115. bool AP_Logger_Block::ReadBlock(void *pBuffer, uint16_t size)
  116. {
  117. if (erase_started) {
  118. return false;
  119. }
  120. while (size > 0) {
  121. uint16_t n = df_PageSize - df_Read_BufferIdx;
  122. if (n > size) {
  123. n = size;
  124. }
  125. if (!BlockRead(df_Read_BufferIdx, pBuffer, n)) {
  126. return false;
  127. }
  128. size -= n;
  129. pBuffer = (void *)(n + (uintptr_t)pBuffer);
  130. df_Read_BufferIdx += n;
  131. if (df_Read_BufferIdx == df_PageSize) {
  132. df_Read_PageAdr++;
  133. if (df_Read_PageAdr > df_NumPages) {
  134. df_Read_PageAdr = 1;
  135. }
  136. if (erase_started) {
  137. memset(buffer, 0xff, df_PageSize);
  138. } else {
  139. PageToBuffer(df_Read_PageAdr);
  140. }
  141. // We are starting a new page - read FileNumber and FilePage
  142. struct PageHeader ph;
  143. if (!BlockRead(0, &ph, sizeof(ph))) {
  144. return false;
  145. }
  146. df_FileNumber = ph.FileNumber;
  147. df_FilePage = ph.FilePage;
  148. df_Read_BufferIdx = sizeof(ph);
  149. }
  150. }
  151. return true;
  152. }
  153. void AP_Logger_Block::SetFileNumber(uint16_t FileNumber)
  154. {
  155. df_FileNumber = FileNumber;
  156. df_FilePage = 1;
  157. }
  158. uint16_t AP_Logger_Block::GetFileNumber()
  159. {
  160. return df_FileNumber;
  161. }
  162. void AP_Logger_Block::EraseAll()
  163. {
  164. WITH_SEMAPHORE(sem);
  165. if (erase_started) {
  166. // already erasing
  167. return;
  168. }
  169. log_write_started = false;
  170. StartErase();
  171. erase_started = true;
  172. }
  173. bool AP_Logger_Block::NeedPrep(void)
  174. {
  175. return NeedErase();
  176. }
  177. void AP_Logger_Block::Prep()
  178. {
  179. WITH_SEMAPHORE(sem);
  180. if (hal.util->get_soft_armed()) {
  181. // do not want to do any filesystem operations while we are e.g. flying
  182. return;
  183. }
  184. if (NeedErase()) {
  185. EraseAll();
  186. }
  187. }
  188. /*
  189. * we need to erase if the logging format has changed
  190. */
  191. bool AP_Logger_Block::NeedErase(void)
  192. {
  193. uint32_t version = 0;
  194. StartRead(df_NumPages+1); // last page
  195. BlockRead(0, &version, sizeof(version));
  196. StartRead(1);
  197. if (version == DF_LOGGING_FORMAT) {
  198. return false;
  199. }
  200. return true;
  201. }
  202. /**
  203. get raw data from a log
  204. */
  205. int16_t AP_Logger_Block::get_log_data_raw(uint16_t log_num, uint32_t page, uint32_t offset, uint16_t len, uint8_t *data)
  206. {
  207. WITH_SEMAPHORE(sem);
  208. uint16_t data_page_size = df_PageSize - sizeof(struct PageHeader);
  209. if (offset >= data_page_size) {
  210. page += offset / data_page_size;
  211. offset = offset % data_page_size;
  212. if (page > df_NumPages) {
  213. // pages are one based, not zero
  214. page = 1 + page - df_NumPages;
  215. }
  216. }
  217. if (log_write_started || df_Read_PageAdr != page) {
  218. StartRead(page);
  219. }
  220. df_Read_BufferIdx = offset + sizeof(struct PageHeader);
  221. if (!ReadBlock(data, len)) {
  222. return -1;
  223. }
  224. return (int16_t)len;
  225. }
  226. /**
  227. get data from a log, accounting for adding FMT headers
  228. */
  229. int16_t AP_Logger_Block::get_log_data(uint16_t log_num, uint16_t page, uint32_t offset, uint16_t len, uint8_t *data)
  230. {
  231. WITH_SEMAPHORE(sem);
  232. if (offset == 0) {
  233. uint8_t header[3];
  234. if (get_log_data_raw(log_num, page, 0, 3, header) == -1) {
  235. return -1;
  236. }
  237. adding_fmt_headers = (header[0] != HEAD_BYTE1 || header[1] != HEAD_BYTE2 || header[2] != LOG_FORMAT_MSG);
  238. }
  239. uint16_t ret = 0;
  240. if (adding_fmt_headers) {
  241. // the log doesn't start with a FMT message, we need to add
  242. // them
  243. const uint16_t fmt_header_size = num_types() * sizeof(struct log_Format);
  244. while (offset < fmt_header_size && len > 0) {
  245. struct log_Format pkt;
  246. uint8_t t = offset / sizeof(pkt);
  247. uint8_t ofs = offset % sizeof(pkt);
  248. Fill_Format(structure(t), pkt);
  249. uint8_t n = sizeof(pkt) - ofs;
  250. if (n > len) {
  251. n = len;
  252. }
  253. memcpy(data, ofs + (uint8_t *)&pkt, n);
  254. data += n;
  255. offset += n;
  256. len -= n;
  257. ret += n;
  258. }
  259. offset -= fmt_header_size;
  260. }
  261. if (len > 0) {
  262. const int16_t bytes = get_log_data_raw(log_num, page, offset, len, data);
  263. if (bytes == -1) {
  264. return ret == 0 ? -1 : ret;
  265. }
  266. ret += bytes;
  267. }
  268. return ret;
  269. }
  270. // This function determines the number of whole or partial log files in the AP_Logger
  271. // Wholly overwritten files are (of course) lost.
  272. uint16_t AP_Logger_Block::get_num_logs(void)
  273. {
  274. WITH_SEMAPHORE(sem);
  275. uint32_t lastpage;
  276. uint32_t last;
  277. uint32_t first;
  278. if (!CardInserted() || find_last_page() == 1) {
  279. return 0;
  280. }
  281. StartRead(1);
  282. if (GetFileNumber() == 0xFFFF) {
  283. return 0;
  284. }
  285. lastpage = find_last_page();
  286. StartRead(lastpage);
  287. last = GetFileNumber();
  288. StartRead(lastpage + 2);
  289. if (GetFileNumber() == 0xFFFF) {
  290. StartRead(((((lastpage-1)>>8)+1)<<8)+1); // next sector
  291. }
  292. first = GetFileNumber();
  293. if (first > last) {
  294. StartRead(1);
  295. first = GetFileNumber();
  296. }
  297. if (last == first) {
  298. return 1;
  299. }
  300. return (last - first + 1);
  301. }
  302. // This function starts a new log file in the AP_Logger
  303. uint16_t AP_Logger_Block::start_new_log(void)
  304. {
  305. WITH_SEMAPHORE(sem);
  306. uint32_t last_page = find_last_page();
  307. StartRead(last_page);
  308. if (find_last_log() == 0 || GetFileNumber() == 0xFFFF) {
  309. SetFileNumber(1);
  310. StartWrite(1);
  311. return 1;
  312. }
  313. uint16_t new_log_num;
  314. // Check for log of length 1 page and suppress
  315. if (df_FilePage <= 1) {
  316. new_log_num = GetFileNumber();
  317. // Last log too short, reuse its number
  318. // and overwrite it
  319. SetFileNumber(new_log_num);
  320. StartWrite(last_page);
  321. } else {
  322. new_log_num = GetFileNumber()+1;
  323. if (last_page == 0xFFFF) {
  324. last_page=0;
  325. }
  326. SetFileNumber(new_log_num);
  327. StartWrite(last_page + 1);
  328. }
  329. return new_log_num;
  330. }
  331. // This function finds the first and last pages of a log file
  332. // The first page may be greater than the last page if the AP_Logger has been filled and partially overwritten.
  333. void AP_Logger_Block::get_log_boundaries(uint16_t log_num, uint32_t & start_page, uint32_t & end_page)
  334. {
  335. WITH_SEMAPHORE(sem);
  336. uint16_t num = get_num_logs();
  337. uint32_t look;
  338. if (num == 1) {
  339. StartRead(df_NumPages);
  340. if (GetFileNumber() == 0xFFFF) {
  341. start_page = 1;
  342. end_page = find_last_page_of_log((uint16_t)log_num);
  343. } else {
  344. end_page = find_last_page_of_log((uint16_t)log_num);
  345. start_page = end_page + 1;
  346. }
  347. } else {
  348. if (log_num==1) {
  349. StartRead(df_NumPages);
  350. if (GetFileNumber() == 0xFFFF) {
  351. start_page = 1;
  352. } else {
  353. start_page = find_last_page() + 1;
  354. }
  355. } else {
  356. if (log_num == find_last_log() - num + 1) {
  357. start_page = find_last_page() + 1;
  358. } else {
  359. look = log_num-1;
  360. do {
  361. start_page = find_last_page_of_log(look) + 1;
  362. look--;
  363. } while (start_page <= 0 && look >=1);
  364. }
  365. }
  366. }
  367. if (start_page == df_NumPages+1 || start_page == 0) {
  368. start_page = 1;
  369. }
  370. end_page = find_last_page_of_log(log_num);
  371. if (end_page == 0) {
  372. end_page = start_page;
  373. }
  374. }
  375. bool AP_Logger_Block::check_wrapped(void)
  376. {
  377. StartRead(df_NumPages);
  378. return GetFileNumber() != 0xFFFF;
  379. }
  380. // This funciton finds the last log number
  381. uint16_t AP_Logger_Block::find_last_log(void)
  382. {
  383. WITH_SEMAPHORE(sem);
  384. uint32_t last_page = find_last_page();
  385. StartRead(last_page);
  386. return GetFileNumber();
  387. }
  388. // This function finds the last page of the last file
  389. uint32_t AP_Logger_Block::find_last_page(void)
  390. {
  391. uint32_t look;
  392. uint32_t bottom = 1;
  393. uint32_t top = df_NumPages;
  394. uint64_t look_hash;
  395. uint64_t bottom_hash;
  396. uint64_t top_hash;
  397. WITH_SEMAPHORE(sem);
  398. StartRead(bottom);
  399. bottom_hash = ((int64_t)GetFileNumber()<<32) | df_FilePage;
  400. while (top-bottom > 1) {
  401. look = (top+bottom)/2;
  402. StartRead(look);
  403. look_hash = (int64_t)GetFileNumber()<<32 | df_FilePage;
  404. if (look_hash >= 0xFFFF00000000) {
  405. look_hash = 0;
  406. }
  407. if (look_hash < bottom_hash) {
  408. // move down
  409. top = look;
  410. } else {
  411. // move up
  412. bottom = look;
  413. bottom_hash = look_hash;
  414. }
  415. }
  416. StartRead(top);
  417. top_hash = ((int64_t)GetFileNumber()<<32) | df_FilePage;
  418. if (top_hash >= 0xFFFF00000000) {
  419. top_hash = 0;
  420. }
  421. if (top_hash > bottom_hash) {
  422. return top;
  423. }
  424. return bottom;
  425. }
  426. // This function finds the last page of a particular log file
  427. uint32_t AP_Logger_Block::find_last_page_of_log(uint16_t log_number)
  428. {
  429. uint32_t look;
  430. uint32_t bottom;
  431. uint32_t top;
  432. uint64_t look_hash;
  433. uint64_t check_hash;
  434. WITH_SEMAPHORE(sem);
  435. if (check_wrapped()) {
  436. StartRead(1);
  437. bottom = GetFileNumber();
  438. if (bottom > log_number) {
  439. bottom = find_last_page();
  440. top = df_NumPages;
  441. } else {
  442. bottom = 1;
  443. top = find_last_page();
  444. }
  445. } else {
  446. bottom = 1;
  447. top = find_last_page();
  448. }
  449. check_hash = (int64_t)log_number<<32 | 0xFFFFFFFF;
  450. while (top-bottom > 1) {
  451. look = (top+bottom)/2;
  452. StartRead(look);
  453. look_hash = (int64_t)GetFileNumber()<<32 | df_FilePage;
  454. if (look_hash >= 0xFFFF00000000) {
  455. look_hash = 0;
  456. }
  457. if (look_hash > check_hash) {
  458. // move down
  459. top = look;
  460. } else {
  461. // move up
  462. bottom = look;
  463. }
  464. }
  465. StartRead(top);
  466. if (GetFileNumber() == log_number) {
  467. return top;
  468. }
  469. StartRead(bottom);
  470. if (GetFileNumber() == log_number) {
  471. return bottom;
  472. }
  473. return 0;
  474. }
  475. void AP_Logger_Block::get_log_info(uint16_t log_num, uint32_t &size, uint32_t &time_utc)
  476. {
  477. uint32_t start, end;
  478. WITH_SEMAPHORE(sem);
  479. get_log_boundaries(log_num, start, end);
  480. if (end >= start) {
  481. size = (end + 1 - start) * (uint32_t)df_PageSize;
  482. } else {
  483. size = (df_NumPages + end - start) * (uint32_t)df_PageSize;
  484. }
  485. time_utc = 0;
  486. }
  487. void AP_Logger_Block::PrepForArming()
  488. {
  489. if (logging_started()) {
  490. return;
  491. }
  492. start_new_log();
  493. }
  494. // read size bytes of data from the buffer
  495. bool AP_Logger_Block::BlockRead(uint16_t IntPageAdr, void *pBuffer, uint16_t size)
  496. {
  497. memcpy(pBuffer, &buffer[IntPageAdr], size);
  498. return true;
  499. }
  500. /*
  501. IO timer running on IO thread
  502. */
  503. void AP_Logger_Block::io_timer(void)
  504. {
  505. if (!_initialised) {
  506. return;
  507. }
  508. if (erase_started) {
  509. if (InErase()) {
  510. return;
  511. }
  512. // write the logging format in the last page
  513. StartWrite(df_NumPages+1);
  514. uint32_t version = DF_LOGGING_FORMAT;
  515. memset(buffer, 0, df_PageSize);
  516. memcpy(buffer, &version, sizeof(version));
  517. FinishWrite();
  518. erase_started = false;
  519. }
  520. if (!CardInserted() || !log_write_started) {
  521. return;
  522. }
  523. while (writebuf.available() >= df_PageSize - sizeof(struct PageHeader)) {
  524. WITH_SEMAPHORE(sem);
  525. struct PageHeader ph;
  526. ph.FileNumber = df_FileNumber;
  527. ph.FilePage = df_FilePage;
  528. memcpy(buffer, &ph, sizeof(ph));
  529. writebuf.read(&buffer[sizeof(ph)], df_PageSize - sizeof(ph));
  530. FinishWrite();
  531. df_FilePage++;
  532. }
  533. }