Example #1
0
//! 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;
}
Example #2
0
//! @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;
}
Example #3
0
//! 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
Example #4
0
File: nf.c Project: InSoonPark/asf
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");
   }
}