//! Ensure that the memory is in a good state before starting to use it //! //! The function will scan the memory, test if the memory is valid, clean it //! if it has to and rebuild all the management blocks. This function shall be //! called prior to any use of the memory after a power-up. //! //! @param none //! //! @return a status: //! PASS if the command has been succesfully executed; //! FAIL else //! Status_bool nf_verify_resume( void ) { U8 u8_nb_loop; Bool status_bool; trace("nf_verify_resume"); trace_nl(); #if (ERASING_ALL==ENABLE) ut_nfc_erase_all(); #endif status_bool = nf_scan(); if(( PASS!=status_bool ) || ( is_nf_invalid() ) // The NF is not cleanly built ) { // The NF seems not cleanly built, or not built at all. // u8_nb_loop = 0; while( 1 ) { u8_nb_loop++; if( u8_nb_loop > 2 ) { status_bool=FAIL; break; // Error NF access or control } nf_cleanup_memory(); if( PASS != nf_scan() ) continue; if( PASS != nf_rebuild() ) continue; status_bool = PASS; break; } } if (status_bool==PASS) { g_nf_init = TRUE; Nf_check_lut(); Nf_check_fbb( FALSE ); } return status_bool; }
//! @brief Rebuild the memory and create LUT, Recovery and Free-blocks blocks. //! //! TBD //! //! It uses s_n_invalid_blocks (number of invalid blocks) and //! g_curr_block_addr (current block address of each device). //! //! @return a status: //! PASS if there are no error; //! FAIL a programmation error occured: the block is marked as bad. The //! function must be recall. //! static Status_bool nf_rebuild ( void ) { Status_bool status_bool=PASS; Bool b_duplicate; U8 i_sub_lut; U8 i_dev =0; _MEM_TYPE_SLOW_ U16 i_block=0; _MEM_TYPE_SLOW_ U16 u16_tmp; _MEM_TYPE_SLOW_ U16 sub_lut_log_sz; _MEM_TYPE_SLOW_ U16 log_block_addr; _MEM_TYPE_SLOW_ U16 log_block_addr_min; _MEM_TYPE_SLOW_ U16 log_block_addr_max; // Refine the computation // s_n_invalid_blocks[S_MNGT_DEV] += 1 // Need a block for the Free-blocks block + (G_N_BLOCKS*NF_N_DEVICES)/NF_SUBLUT_SIZE // and one for each sub-LUT ; // Take the max number of invalid blocks of each devices // u16_tmp=s_n_invalid_blocks[0] ; for ( i_dev=1 ; i_dev<NF_N_DEVICES ; i_dev++ ) { u16_tmp=Max( u16_tmp, s_n_invalid_blocks[i_dev] ); } // Take the max number of quarantine blocks of each devices // i_sub_lut=s_n_quarantine_blocks[0] ; for ( i_dev=1 ; i_dev<NF_N_DEVICES ; i_dev++ ) { i_sub_lut=Max( i_sub_lut, s_n_quarantine_blocks[i_dev] ); } sub_lut_log_sz = (U16)NF_N_DEVICES*(G_N_BLOCKS -g_nf_first_block -u16_tmp); // Finally compute the number of exportable physical blocks and free blocks // Assert( u16_tmp<(G_N_BLOCKS -g_nf_first_block) ); g_n_export_blocks= (U16)( ((U32)( (U32)sub_lut_log_sz ) * 1000) / 1024); g_n_export_blocks= Align_down( g_n_export_blocks, NF_N_DEVICES); g_n_free_blocks = (U16)sub_lut_log_sz - g_n_export_blocks; g_n_free_blocks -= (U16)NF_N_DEVICES*i_sub_lut; if( g_n_free_blocks<=NF_LOW_N_FREE_THRESHOLD ) { while(1); // TBD } Assert( g_n_free_blocks>0 ); Assert( g_n_free_blocks<(1L<<NF_SHIFT_PAGE_BYTE) ); // limit the free blocks in order to fit in 1 page // Compute the number of needed sub-LUT // Affect to each management block a free block address // Nfc_action(NFC_ACT_DEV_SELECT, S_MNGT_DEV); g_fbb_block_addr = nf_fetch_free_block(S_MNGT_DEV); nfc_erase_block( nf_block_2_page( g_fbb_block_addr ), TRUE ); g_n_sub_lut= 0; u16_tmp = g_n_export_blocks; //#error il faut positionner les index(LUT, FBB, RCV...) while(1) { Assert( g_n_sub_lut<N_SUBLUT ); g_lut_block_addr [g_n_sub_lut]=nf_fetch_free_block(S_MNGT_DEV); g_lut_block_index[g_n_sub_lut]=0; nfc_erase_block( nf_block_2_page( g_lut_block_addr [g_n_sub_lut] ), TRUE ); g_n_sub_lut++; if( u16_tmp>NF_SUBLUT_SIZE ) u16_tmp-=NF_SUBLUT_SIZE; else break; } g_last_sub_lut_log_sz=u16_tmp/NF_N_DEVICES; // Build the sub-LUTs // for ( i_sub_lut=0 ; i_sub_lut<g_n_sub_lut ; ) { U8 n_sublut_in_buf = g_n_sub_lut - i_sub_lut; // Count remaining sublut to build log_block_addr_max = log_block_addr_min = (U16)i_sub_lut<<(NF_SHIFT_SUBLUT_PHYS-NF_SHIFT_N_DEVICES); // first included if( n_sublut_in_buf>(NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE)) ) { n_sublut_in_buf = NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE); log_block_addr_max += ((U16)n_sublut_in_buf)*g_sub_lut_log_sz; // last not included } else { log_block_addr_max += ((U16)n_sublut_in_buf-1)*g_sub_lut_log_sz +g_last_sub_lut_log_sz; // last not included } nf_init_buffer(); // Report affected logical blocks // u16_tmp=g_n_export_blocks/NF_N_DEVICES; // Number of logical blocks used for the mass storage b_duplicate=FALSE; for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); g_block_to_kill[i_dev]=0xFFFF; for ( i_block=g_nf_first_block ; i_block<G_N_BLOCKS ; i_block++ ) { nfc_read_spare_byte( g_byte, 8, nf_block_2_page(i_block) ); if(( 0xFF !=g_byte[G_OFST_BLK_STATUS ] ) // The block is bad || ( NFC_BLK_ID_DATA!=g_byte[NFC_SPARE_OFST_1_BLK_ID ] ) // or is not a data block || ( ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA ] ) // or is not affected && ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA+1 ] ) ) ) { continue; } MSB(log_block_addr) = g_byte[NFC_SPARE_OFST_6_LBA ]; LSB(log_block_addr) = g_byte[NFC_SPARE_OFST_6_LBA+1]; if( log_block_addr>=u16_tmp ) { // The LBA seems bad: it does not fit in any LUT. This happens when unplugging the player. // Block is erased. // Anyway, stay in the loop to track similar problems. nfc_erase_block( nf_block_2_page(i_block), TRUE ); status_bool=FAIL; } if(( log_block_addr>=log_block_addr_min ) && ( log_block_addr< log_block_addr_max )) { U16 ofst=2*((U16)i_dev + (log_block_addr%((U16)NF_PAGE_BUFFER_SIZE/2/NF_N_DEVICES))*NF_N_DEVICES) ; if( ( 0xFF==g_page_buffer[ ofst ] ) && ( 0xFF==g_page_buffer[ ofst +1 ] ) ) { // no redundant phys blocks Assert( ( ofst +1 ) < NF_PAGE_BUFFER_SIZE ); g_page_buffer[ ofst ] = MSB(i_block); g_page_buffer[ ofst +1 ] = LSB(i_block); } else { // A duplicated logical block is detected. This happens when unplugging the player. // Anyway, stay in the loop to track any other redundant blocks, for that sub-LUT. _MEM_TYPE_SLOW_ U16 tmp_addr; MSB(tmp_addr)=g_page_buffer[ ofst ]; LSB(tmp_addr)=g_page_buffer[ ofst +1 ]; //trace("Dupl "); trace_hex32(tmp_addr); trace("-"); trace_hex32(i_block);; trace("\n\r"); if(0xFFFF!=g_block_to_kill[i_dev]) { // !!! There are more than 1 duplicated block on the device. This should never happen... nfc_erase_block( nf_block_2_page(g_block_to_kill[i_dev]), TRUE ); return FAIL; } b_duplicate=TRUE; g_log_block_id=log_block_addr; nfc_open_page_read( nf_block_2_page(i_block) , NF_SPARE_POS+NFC_SPARE_OFST_3_BYTE_3 ); if( NFC_OFST_3_DATA_DST!=Nfc_rd_data_fetch_next() ) { trace("1. Src block="); trace_hex16(i_block); trace_nl(); trace("1. Dst block="); trace_hex16(tmp_addr); trace_nl(); //nfc_print_block(i_block, 0); //nfc_print_block(tmp_addr, 0); //while(1); g_block_to_kill[i_dev]=i_block; // source block g_phys_page_addr[i_dev] = nf_block_2_page( tmp_addr ); // recipient block } else { trace("2. Src block="); trace_hex16(tmp_addr); trace_nl(); trace("2. Dst block="); trace_hex16(i_block); trace_nl(); //nfc_print_block(tmp_addr, 0); //nfc_print_block(i_block, 0); //while(1); g_block_to_kill[i_dev]= tmp_addr ; // source block g_page_buffer[ ofst ]=MSB(i_block); g_page_buffer[ ofst +1 ]=LSB(i_block); g_phys_page_addr[i_dev] = nf_block_2_page( i_block ); // recipient block } } } } // for ( i_block ../.. } // for ( i_dev ../.. if( b_duplicate ) { U8 i_page; U8 i_sect; trace("recovery\n\r"); // Test that recovery can be done for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { if( 0xFFFF==g_block_to_kill[i_dev] ) { // !Ooops... we can not recover from that case since there are duplication // only on on device for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { if( 0xFFFF!=g_block_to_kill[i_dev] ) { nfc_erase_block( nf_block_2_page(g_block_to_kill[i_dev]), TRUE ); } } return FAIL; } } // Initialize variable for nf_copy_tail g_curr_dev_id=0; g_last_log_sector= ((U32)g_log_block_id) << S_SHIFT_LOG_BLOCK_SECTOR; // Look for last written sector for( i_page=0 ; i_page<SIZE_BLOCK_PAGE ; i_page++ ) { Nfc_action(NFC_ACT_DEV_SELECT, g_curr_dev_id); // open the current device for( i_sect=0 ; i_sect<SIZE_PAGE_SECTOR ; i_sect++ ) { nfc_open_page_read( g_phys_page_addr[g_curr_dev_id] , NF_SPARE_POS + (((U16)i_sect)*16) + NFC_SPARE_OFST_6_LBA ); if(( 0xFF==Nfc_rd_data_fetch_next() ) && ( 0xFF==Nfc_rd_data_fetch_next() )) goto recovery_exit; else { g_last_log_sector++; trace("g_last_log_sector="); trace_hex32(g_last_log_sector); trace_nl(); } } g_phys_page_addr[g_curr_dev_id]++; // update the current physical page of the current device g_curr_dev_id++; // update the current device if( g_curr_dev_id==NF_N_DEVICES ) { g_curr_dev_id=0; } trace("g_curr_dev_id="); trace_hex(g_curr_dev_id); trace_nl(); trace("g_phys_page_addr="); trace_hex32(g_phys_page_addr[g_curr_dev_id]); trace_nl(); } recovery_exit: trace("recovery stop on g_last_log_sector="); trace_hex32(g_last_log_sector); trace_nl(); trace("g_curr_dev_id="); trace_hex(g_curr_dev_id); trace_nl(); trace("g_phys_page_addr="); trace_hex32(g_phys_page_addr[g_curr_dev_id]); trace_nl(); //while(1); nf_copy_tail(); return FAIL; } // At least one redundant have been found: the LUT must be rebuilt since the fetch of free block // may not have seen that affected block (redundant) are in fact free. if( PASS!=status_bool ) { return FAIL; } // Affect a free physical block to the logical block // for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for(u16_tmp=0 ; u16_tmp<(log_block_addr_max-log_block_addr_min) ; u16_tmp++ ) { U16 ofst=2*((U16)i_dev + u16_tmp*NF_N_DEVICES); if(( 0xFF==g_page_buffer[ofst ] ) && ( 0xFF==g_page_buffer[ofst+1] )) { i_block=nf_fetch_free_block(i_dev); Assert( ofst+1<NF_PAGE_BUFFER_SIZE); g_page_buffer[ofst ] = MSB(i_block); g_page_buffer[ofst+1] = LSB(i_block); } } } // for ( i_dev ../.. // Each sub-LUT will fit in a physical page and will be of the same size // except the last one which contains less // for( ; n_sublut_in_buf!=0 ; n_sublut_in_buf--, i_sub_lut++ ) { sub_lut_log_sz= ( i_sub_lut==(g_n_sub_lut-1) ) ? g_last_sub_lut_log_sz : g_sub_lut_log_sz ; // Write the sub-LUT in the page // status_bool = nf_write_lut(i_sub_lut%(NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE)), i_sub_lut, sub_lut_log_sz); if ( PASS!=status_bool ) { nfc_mark_bad_block( nf_block_2_page( g_lut_block_addr[i_sub_lut] ) ); return FAIL; } } } //#error: si recovery, il faut effacer la lut en question. Il faut donc la reconstruire. // Pour cela, il faut trouver des blocs libres. // 1ere methode: effacer aussi le free-blocks block et le reconstruire, ainsi que la sub-LUT // 2eme methode: marquer les free block pour les reconnaitre et reconstruire la sub lut // Build the free-blocks block // First, fill the internal buffer with the free blocks // for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for ( u16_tmp=0 ; u16_tmp<(g_n_free_blocks/NF_N_DEVICES) ; u16_tmp++ ) { // This define is better than using a variable that holds the expression... #define OFST (2*(i_dev + u16_tmp*NF_N_DEVICES)) i_block=nf_fetch_free_block(i_dev); nfc_erase_block( nf_block_2_page(i_block), TRUE ); Assert( OFST <NF_PAGE_BUFFER_SIZE); Assert( OFST +1<NF_PAGE_BUFFER_SIZE); Assert( i_block>=g_nf_first_block ); Assert( i_block< G_N_BLOCKS ); g_page_buffer[OFST ] = MSB(i_block); g_page_buffer[OFST +1] = LSB(i_block); #undef OFST } } // Then write the buffer in the free-blocks block // Note that the list of free-blocks holds on one page only; the // algo is thus made for both 512B and 2kB pages. // g_fbb_block_index=0; status_bool = nf_write_fbb(); if ( PASS!=status_bool ) { nfc_mark_bad_block( nf_block_2_page( g_fbb_block_addr ) ); return FAIL; } //#error Effacer les free blocks !!!!!! //#error il faut determiner s_lut_index[all] pour les sub-lut existantes //#error si il existe un bloc de recovery, alors la lut associée n'est plus valide //#error rendre parametrable la taille du buffer (actuellement 2k). Si <512 et no partial prog: fatal error //nf_init_buffer(); // Cleanup the buffer return PASS; }
//! Scan the memory and looks for sub-LUT, free-blocks block and recovery blocks //! //! @param none //! //! @return a status: //! PASS if the scan has been succesfully executed; //! FAIL if the scan encountered any problem //! static Status_bool nf_scan( void ) { U8 i_dev =0; U16 i_block=0; U8 n_quarantine_blocks=0; U16 n_invalid_blocks=0; g_last_sub_lut_log_sz =(U16)-1; g_sub_lut_log_sz =(U16)NF_SUBLUT_SIZE/NF_N_DEVICES; // Initialize the recovery structure. This should be done by the startup ! // g_is_found_lut =FALSE; g_is_found_fbb =FALSE; g_fatal =FALSE; g_n_real_sub_lut=0; trace("nf_scan"); trace_nl(); // Scan all the devices and looks for: // - the sub-LUT blocks // - the recovery block // - the free-blocks block // for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { n_invalid_blocks = 0; n_quarantine_blocks= 0; g_curr_block_addr[i_dev]= G_N_BLOCKS -1; // points on the last block trace("Device "); trace_hex(i_dev); trace("\n\r"); Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for( i_block=g_nf_first_block ; i_block<G_N_BLOCKS ; i_block++ ) { nfc_read_spare_byte( g_byte, 16, nf_block_2_page(i_block) ); if ( g_byte[G_OFST_BLK_STATUS]!=0xFF ) { n_invalid_blocks +=1 ; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): bad Block\n\r"); continue; // The block is bad } if(( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_SUBLUT ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_FBB ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_QUARANTINE ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_DATA )) { n_invalid_blocks +=1; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): Unknown\n\r"); continue; } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_QUARANTINE ) { n_quarantine_blocks +=1; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): Quarantine\n\r"); continue; } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_SUBLUT ) { n_invalid_blocks +=1; if ( i_dev==S_MNGT_DEV ) { U8 sub_lut_id = g_byte[NFC_SPARE_OFST_2_BYTE_2]; g_n_sub_lut = g_byte[NFC_SPARE_OFST_4_BYTE_4]; g_is_found_lut = TRUE; g_n_real_sub_lut++; g_lut_block_addr[sub_lut_id] = i_block; if ( sub_lut_id==(g_n_sub_lut-1) ) { MSB(g_last_sub_lut_log_sz)= g_byte[NFC_SPARE_OFST_6_LBA ]; LSB(g_last_sub_lut_log_sz)= g_byte[NFC_SPARE_OFST_6_LBA+1]; } g_lut_block_index[sub_lut_id]= nf_refine_index(i_block, 1, NFC_BLK_ID_SUBLUT); trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): SUB-LUT (id ");trace_hex(sub_lut_id);trace(" ofst "); trace_hex(g_lut_block_index[sub_lut_id]); trace(")\n\r"); continue ; } else { // LUT found on bad NF g_fatal=TRUE; break; } } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_FBB ) { n_invalid_blocks +=1; if ( i_dev==S_MNGT_DEV ) { if ( TRUE==g_is_found_fbb ) { g_fatal=TRUE; // already found break; } g_fbb_block_addr = i_block; g_fbb_block_index = nf_refine_index(i_block, 1, NFC_BLK_ID_FBB); nfc_read_spare_byte( g_byte, 16, nf_block_2_page(i_block) + (U32)g_fbb_block_index ); // Reload if( NFC_OFST_6_FBB_VALID!=g_byte[NFC_SPARE_OFST_6_LBA] ) { g_fatal=TRUE; // FBB not valid. Force rebuild break; } MSB(g_n_free_blocks) = g_byte[NFC_SPARE_OFST_2_BYTE_2]; LSB(g_n_free_blocks) = g_byte[NFC_SPARE_OFST_3_BYTE_3]; s_nfd_rev = g_byte[NFC_SPARE_OFST_4_BYTE_4]; MSB(g_n_export_blocks) = g_byte[NFC_SPARE_OFST_EXPORT]; LSB(g_n_export_blocks) = g_byte[NFC_SPARE_OFST_EXPORT+1]; trace(" g_n_free_blocks="); trace_hex16(g_n_free_blocks); trace_nl(); trace(" g_n_export_blocks="); trace_hex16(g_n_export_blocks); trace_nl(); g_is_found_fbb=TRUE; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): FBB (ofst "); trace_hex( g_fbb_block_index ); trace(")\n\r"); continue ; } else { g_fatal=TRUE; break; } } } // for ( i_block // A fatal error on one device is enough to cleanup all the devices ! // s_n_invalid_blocks[ i_dev]= n_invalid_blocks; s_n_quarantine_blocks[i_dev]= n_quarantine_blocks; if ( TRUE==g_fatal ) { break; } } // for ( i_dev return (g_fatal==TRUE) ? FAIL: PASS; } // nf_scan
void nfc_print_block(U16 block_addr, U8 dev_id) { _MEM_TYPE_SLOW_ U32 page_addr=(U32)block_addr*((U8)1<<G_SHIFT_BLOCK_PAGE); _MEM_TYPE_SLOW_ U16 n_bytes; _MEM_TYPE_SLOW_ U16 i_byte; _MEM_TYPE_SLOW_ U8 i_page; trace("\n\rDisplay block 0x"); trace_hex( MSB(block_addr) ); trace_hex( LSB(block_addr) ); n_bytes= ( Is_nf_512() ) ? 512 // 512B page access : 2048 // 2KB page access ; nf_select( dev_id ); //for ( i_page=(U8)1<<G_SHIFT_BLOCK_PAGE ; i_page!=0 ; i_page--, page_addr++ ) for( i_page=0 ; i_page<64 ; i_page++, page_addr++ ) { trace("\n\rOpening page 0x"); trace_hex( MSB0(page_addr) ); trace_hex( MSB1(page_addr) ); trace_hex( MSB2(page_addr) ); trace_hex( MSB3(page_addr) ); #if 0 nf_open_page_read( page_addr, 0 ); for( i_byte=0 ; i_byte<n_bytes ; ) { if( !(i_byte%32) ) { trace("\n\r0x"); trace_hex( MSB(i_byte) ); trace_hex( LSB(i_byte) ); trace(" 0x"); } else if( !(i_byte%16) ) trace(" "); else if( !(i_byte% 8) ) trace(" "); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); i_byte+=4; } #else nf_open_page_read( page_addr, n_bytes ); #endif trace("\n\rSpare zone: 0x"); for( i_byte=4*4 ; i_byte!=0 ; i_byte-- ) { // discard spare zone if( i_byte%4==0 ) trace_nl(); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); trace_hex( nf_rd_data() ); } trace("\n\r"); } trace("\n\rOther way to access spare zone: 0x"); page_addr=(U32)block_addr*((U8)1<<G_SHIFT_BLOCK_PAGE); nf_open_page_read( page_addr, NF_SPARE_POS ); i_byte=Nfc_rd_data_fetch_next(); { for ( i_byte=4*4 ; i_byte!=0 ; i_byte-- ) { // discard spare zone trace_hex( Nfc_rd_data_fetch_next() ); trace_hex( Nfc_rd_data_fetch_next() ); trace_hex( Nfc_rd_data_fetch_next() ); trace_hex( Nfc_rd_data_fetch_next() ); } trace("\n\r"); } }