00001
00045 #include <debug.h>
00046
00047 #include <block/journal.h>
00048 #include <checksum/crc8.h>
00049 #include <dmapool.h>
00050 #include <workqueue.h>
00051 #include <app/workqueue.h>
00052 #include <string.h>
00053 #include <status-codes.h>
00054
00055 #define JOURNAL_MAGIC0 0x42
00056 #define JOURNAL_CARD0_MAGIC 0x1234
00057 #define JOURNAL_CARD1_MAGIC 0x5678
00058
00059 enum journal_search_state {
00060 JOURNAL_SEARCH_STARTING,
00061 JOURNAL_SEARCH_FOR_BLOCK,
00062 JOURNAL_SEARCH_FOR_ENTRY,
00063 };
00064
00065 #define JOURNAL_ENTRY_FULL_LENGTH sizeof(struct journal_entry)
00066
00067
00068
00069 #define JOURNAL_ENTRY_START_LENGTH (sizeof(struct journal_entry) - 4)
00070
00084 static unsigned int journal_get_next_address(
00085 struct journal *media, unsigned int prev_addr)
00086 {
00087 unsigned int next_addr = prev_addr + JOURNAL_ENTRY_FULL_LENGTH;
00088
00089
00090
00091
00092 if (dataflash_get_page_offset(media->storage, next_addr)
00093 + JOURNAL_ENTRY_FULL_LENGTH
00094 > dataflash_get_page_size(media->storage)) {
00095 next_addr = (dataflash_get_page_index(media->storage,
00096 next_addr) + 1)
00097 * dataflash_get_page_size(media->storage);
00098 }
00099 return next_addr;
00100 }
00101
00102 static void journal_open_entry_done(struct dataflash_request *request,
00103 void *context)
00104 {
00105 struct journal *media = context;
00106
00107 if (request->status != STATUS_OK) {
00108 media->status = JOURNAL_ERROR;
00109 dbg_error("Journal ERROR: Dataflash error\n");
00110 } else if (media->current_entry) {
00111 media->status = JOURNAL_ENTRY_OPEN;
00112 } else {
00113 media->status = JOURNAL_IDLE;
00114 }
00115
00116 if (media->flags & JOURNAL_EMPTY) {
00117 media->flags &= ~JOURNAL_EMPTY;
00118 } else if (dataflash_get_block_index(media->storage,
00119 media->last_location)
00120 != dataflash_get_block_index(media->storage,
00121 media->current_location)) {
00122
00123 dbg_info("Journal: Done with block %x, erasing\n",
00124 dataflash_get_block_index(media->storage,
00125 media->last_location));
00126
00127 media->erase_request.address = dataflash_get_block_index(
00128 media->storage,
00129 media->last_location);
00130 dataflash_erase_blocks(&media->erase_request);
00131 }
00132
00133 media->last_location = media->current_location;
00134 media->current_location = journal_get_next_address(media,
00135 media->last_location);
00136
00137 if (media->current_location + JOURNAL_ENTRY_FULL_LENGTH >
00138 media->start + media->length) {
00139
00140
00141
00142
00143
00144 media->current_location = media->start;
00145 }
00146
00147 if (media->callback)
00148 media->callback(media, media->current_entry, media->context);
00149 }
00150
00164 void journal_open_entry(
00165 struct journal *media,
00166 struct journal_entry *entry,
00167 journal_callback_t entry_stored,
00168 void *context)
00169 {
00170 struct dataflash_request *request;
00171
00172 assert(media);
00173 assert(entry);
00174 assert(entry->magic0 != 0xffff);
00175 assert(media->status == JOURNAL_IDLE);
00176 media->status = JOURNAL_BUSY;
00177
00178 request = &media->request;
00179 media->current_entry = entry;
00180 media->callback = entry_stored;
00181 media->context = context;
00182
00183 buffer_init_tx(&media->entry_buffer, entry,
00184 JOURNAL_ENTRY_START_LENGTH);
00185
00186 request->address = media->current_location;
00187 request->request_done = journal_open_entry_done;
00188
00189 dataflash_write_array(request);
00190 }
00191
00192 static void journal_clean_entry_done(struct dataflash_request *request,
00193 void *context)
00194 {
00195 dma_free(request->data->addr.ptr, sizeof(struct journal_entry));
00196 journal_open_entry_done(request, context);
00197 }
00198
00217 void journal_clean_last_entry(
00218 struct journal *media,
00219 journal_callback_t entry_cleaned,
00220 void *context)
00221 {
00222 struct journal_entry *entry;
00223 struct dataflash_request *request;
00224 phys_addr_t entry_phys;
00225
00226 assert(media);
00227 assert(media->status == JOURNAL_IDLE);
00228
00229 entry = dma_alloc(&entry_phys, sizeof(struct journal_entry));
00230 if (!entry) {
00231 dbg_error("journal: failed to allocate dummy entry\n");
00232 if (entry_cleaned)
00233 entry_cleaned(media, NULL, context);
00234 return;
00235 }
00236
00237 media->status = JOURNAL_BUSY;
00238 buffer_init_tx_mapped(&media->entry_buffer, entry, entry_phys,
00239 sizeof(struct journal_entry));
00240
00241 journal_prepare_entry(entry, 0, 0, 0);
00242
00243 request = &media->request;
00244 media->current_entry = NULL;
00245 media->callback = entry_cleaned;
00246 media->context = context;
00247
00248 request->address = media->current_location;
00249 request->request_done = journal_clean_entry_done;
00250
00251 dataflash_write_array(request);
00252 }
00253
00254 static void journal_commit_card_done(
00255 struct dataflash_request *request,
00256 void *context)
00257 {
00258 struct journal *media = context;
00259
00260 if (request->status != STATUS_OK) {
00261 media->status = JOURNAL_ERROR;
00262 dbg_error("Journal ERROR: Dataflash error\n");
00263 } else if (media->flags & JOURNAL_CARD0_COMMITTED &&
00264 media->flags & JOURNAL_CARD1_COMMITTED) {
00265 media->flags &= ~(JOURNAL_CARD0_COMMITTED
00266 | JOURNAL_CARD1_COMMITTED);
00267 media->status = JOURNAL_IDLE;
00268 } else {
00269 media->status = JOURNAL_ENTRY_OPEN;
00270 }
00271
00272 if (media->callback)
00273 media->callback(media, media->current_entry, media->context);
00274 }
00275
00290 void journal_commit_card(
00291 struct journal *media,
00292 struct journal_entry *entry,
00293 unsigned int card_nr,
00294 journal_callback_t card_committed,
00295 void *context)
00296 {
00297 struct dataflash_request *request;
00298
00299 assert(media);
00300 assert(entry);
00301 assert(entry == media->current_entry);
00302 assert(card_nr <= 1);
00303 assert(media->status == JOURNAL_ENTRY_OPEN);
00304
00305 media->status = JOURNAL_BUSY;
00306 request = &media->request;
00307 media->callback = card_committed;
00308 media->context = context;
00309
00310 if (card_nr == 0) {
00311 buffer_init_tx(&media->entry_buffer,
00312 &entry->committed_card0_magic, 2);
00313 request->address = media->last_location +
00314 JOURNAL_ENTRY_START_LENGTH;
00315 media->flags |= JOURNAL_CARD0_COMMITTED;
00316 } else if (card_nr == 1) {
00317 buffer_init_tx(&media->entry_buffer,
00318 &entry->committed_card1_magic, 2);
00319 request->address = media->last_location +
00320 JOURNAL_ENTRY_START_LENGTH + 2;
00321 media->flags |= JOURNAL_CARD1_COMMITTED;
00322 }
00323
00324 request->request_done = journal_commit_card_done;
00325 dataflash_write_array(request);
00326 }
00327
00338 void journal_close_entry(struct journal *media)
00339 {
00340 assert(media);
00341
00342 media->flags &= ~(JOURNAL_CARD0_COMMITTED
00343 | JOURNAL_CARD1_COMMITTED);
00344 media->status = JOURNAL_IDLE;
00345 }
00346
00347 static void journal_get_last_entry_done(
00348 struct dataflash_request *request,
00349 void *context)
00350 {
00351 struct journal *media = context;
00352
00353 if (request->status != STATUS_OK) {
00354 media->status = JOURNAL_ERROR;
00355 dbg_error("Journal ERROR: Dataflash error\n");
00356 } else {
00357 media->status = JOURNAL_IDLE;
00358 }
00359
00360 if (media->callback) {
00361 if (media->flags & JOURNAL_EMPTY)
00362 media->callback(media, NULL, media->context);
00363 else
00364 media->callback(media, media->current_entry,
00365 media->context);
00366 }
00367 }
00368
00369 static void journal_read_last_entry(
00370 struct journal *media)
00371 {
00372 buffer_init_rx(&media->entry_buffer, media->current_entry,
00373 JOURNAL_ENTRY_FULL_LENGTH);
00374
00375 media->request.address = media->last_location;
00376 media->request.request_done = journal_get_last_entry_done;
00377 dataflash_read_array(&media->request);
00378 }
00379
00380 static void journal_run_find_last_entry(
00381 struct dataflash_request *request,
00382 void *context)
00383 {
00384 struct journal *media = context;
00385
00386 workqueue_add_item(&journal_workqueue, &media->workqueue_item);
00387 }
00388
00389 static void journal_reerase_previous_block(struct journal *media)
00390 {
00391 unsigned int current_block;
00392 unsigned int first_block;
00393 unsigned int last_block;
00394
00395
00396 if (dataflash_get_block_index(media->storage, media->last_location)
00397 == dataflash_get_block_index(media->storage,
00398 media->last_location - 1)) {
00399 return;
00400 }
00401
00402 if (media->current_entry->committed_card0_magic
00403 == JOURNAL_CARD0_MAGIC)
00404 return;
00405
00406
00407
00408
00409
00410 current_block = dataflash_get_block_index(
00411 media->storage,
00412 media->last_location);
00413 first_block = dataflash_get_block_index(
00414 media->storage,
00415 media->start);
00416 last_block = dataflash_get_block_index(
00417 media->storage,
00418 media->start + media->length - 1);
00419 if (current_block == first_block)
00420 media->erase_request.address = last_block;
00421 else
00422 media->erase_request.address = current_block - 1;
00423 dataflash_erase_blocks(&media->erase_request);
00424 }
00425
00438 static void journal_find_last_entry(void *data)
00439 {
00440 struct journal *media = data;
00441
00442
00443 switch (media->search_mode) {
00444 case JOURNAL_SEARCH_STARTING:
00445 media->current_location = media->start;
00446 media->search_mode = JOURNAL_SEARCH_FOR_BLOCK;
00447 break;
00448 case JOURNAL_SEARCH_FOR_BLOCK:
00449 if (media->magic_number == 0xffff) {
00450
00451 media->current_location += dataflash_get_block_size(
00452 media->storage);
00453 break;
00454 } else {
00455
00456 media->search_mode = JOURNAL_SEARCH_FOR_ENTRY;
00457 media->flags &= ~JOURNAL_EMPTY;
00458
00459 }
00460 case JOURNAL_SEARCH_FOR_ENTRY:
00461 if (media->magic_number == 0xffff) {
00462
00463 journal_read_last_entry(media);
00464 journal_reerase_previous_block(media);
00465 return;
00466 } else {
00467 media->last_location = media->current_location;
00468 media->current_location = journal_get_next_address(
00469 media, media->current_location);
00470 }
00471 break;
00472 default:
00473 unhandled_case(media->search_mode);
00474 }
00475
00476 if (media->current_location + JOURNAL_ENTRY_FULL_LENGTH
00477 > media->start + media->length) {
00478
00479
00480 media->current_location = media->start;
00481 journal_read_last_entry(media);
00482 return;
00483 }
00484
00485 buffer_init_rx(&media->entry_buffer, &media->magic_number, 2);
00486 media->request.address = media->current_location;
00487 dataflash_read_array(&media->request);
00488 }
00489
00503 void journal_get_last_entry(
00504 struct journal *media,
00505 struct journal_entry *entry,
00506 journal_callback_t entry_retrieved,
00507 void *context)
00508 {
00509 assert(media);
00510 assert(media->status == JOURNAL_IDLE
00511 || media->status == JOURNAL_END_NOT_LOCALIZED);
00512
00513 media->status = JOURNAL_BUSY;
00514 media->callback = entry_retrieved;
00515 media->context = context;
00516 media->current_entry = entry;
00517
00518 media->flags |= JOURNAL_EMPTY;
00519 media->search_mode = JOURNAL_SEARCH_STARTING;
00520
00521 media->request.request_done = journal_run_find_last_entry;
00522 journal_find_last_entry(media);
00523 }
00524
00538 void journal_prepare_entry(
00539 struct journal_entry *entry,
00540 uint32_t card_id,
00541 uint32_t block,
00542 uint16_t length)
00543 {
00544 uint8_t journal_checksum;
00545
00546 assert(entry);
00547
00548 entry->card_id = card_id;
00549 entry->block = block;
00550 entry->length = length;
00551
00552
00553 journal_checksum = checksum_crc8((uint8_t *)&entry->length, 10);
00554 entry->magic0 = JOURNAL_MAGIC0 << 8 | journal_checksum;
00555 entry->committed_card0_magic = JOURNAL_CARD0_MAGIC;
00556 entry->committed_card1_magic = JOURNAL_CARD1_MAGIC;
00557 }
00558
00574 bool journal_entry_card_is_intact(
00575 struct journal_entry *entry,
00576 unsigned int card_nr)
00577 {
00578 uint8_t checksum;
00579
00580 assert(entry);
00581 assert(card_nr <= 1);
00582
00583 checksum = checksum_crc8((uint8_t *)&entry->length, 10);
00584
00585 if (entry->magic0 != (JOURNAL_MAGIC0 << 8 | checksum)) {
00586
00587
00588
00589 return true;
00590 } else if (entry->committed_card0_magic != JOURNAL_CARD0_MAGIC) {
00591
00592
00593 if (card_nr == 0)
00594 return false;
00595 else
00596 return true;
00597 } else if (entry->committed_card1_magic != JOURNAL_CARD1_MAGIC) {
00598
00599
00600
00601 if (card_nr == 0)
00602 return true;
00603 else
00604 return false;
00605 } else {
00606
00607 return true;
00608 }
00609 }
00610
00623 bool journal_entry_is_consistent(struct journal_entry *entry)
00624 {
00625 uint8_t checksum;
00626
00627 assert(entry);
00628
00629 checksum = checksum_crc8((uint8_t *)&entry->length, 10);
00630
00631
00632
00633
00634
00635
00636 if (entry->magic0 != (JOURNAL_MAGIC0 << 8 | checksum)) {
00637 if (entry->committed_card0_magic != 0xffff ||
00638 entry->committed_card1_magic != 0xffff)
00639 return false;
00640 }
00641 return true;
00642 }
00643
00657 void journal_init(
00658 struct journal *media,
00659 struct dataflash_device *storage,
00660 unsigned int start_addr,
00661 unsigned int length)
00662 {
00663 assert(media);
00664 assert(storage);
00665
00666 memset(media, 0, sizeof(struct journal));
00667 media->status = JOURNAL_END_NOT_LOCALIZED;
00668
00669 if (dataflash_get_block_index(storage, start_addr - 1)
00670 == dataflash_get_block_index(storage, start_addr)) {
00671
00672 start_addr = dataflash_get_block_size(storage) *
00673 (dataflash_get_block_index(storage, start_addr) + 1);
00674 dbg_warning("Journal: Moving start address to %x to align "
00675 "with block boundary (block %x)\n",
00676 start_addr,
00677 dataflash_get_block_index(storage,
00678 start_addr));
00679 }
00680 assert(start_addr + length <= dataflash_get_device_size(storage));
00681
00682 media->start = start_addr;
00683 media->length = length;
00684 media->storage = storage;
00685 workqueue_init_item(&media->workqueue_item,
00686 journal_find_last_entry, media);
00687
00688
00689 media->request.data = &media->entry_buffer;
00690 media->request.device = media->storage;
00691 media->request.context = media;
00692
00693 media->erase_request.device = media->storage;
00694 media->erase_request.length = 1;
00695 }