1 /**
2   ******************************************************************************
3   * @file    EEPROM_Emul/Core/eeprom_emul.c
4   * @author  MCD Application Team
5   * @brief   This file provides all the EEPROM emulation firmware functions.
6   @verbatim
7   ==============================================================================
8                         ##### How to use this driver #####
9   ==============================================================================
10     [..]
11       This driver provides functions to initialize EEPROM emulation, to read and
12       write EEPROM variables, and to cleanup FLASH pages used by EEPROM emulation.
13 
14       (#) EEPROM emulation initialization functions:
15            (++) Format the FLASH pages used by EEPROM emulation using EE_Format().
16                 This function is optionally used, it can be called the very first
17                 time EEPROM emulation is used, to prepare FLASH pages for EEPROM
18                 emulation with empty EEPROM variables. It can also be called at
19                 any time, to flush all EEPROM variables.
20            (++) Initialize EEPROM emulation, and restore the FLASH pages used by
21                 EEPROM emulation to a known good state in case of power loss
22                 using EE_Init(). It must be performed at system start up.
23 
24       (#) EEPROM variables access functions:
25            (++) Write EEPROM variable using EE_WriteVariableXbits() functions
26                 A Clean Up request can be raised as return parameter in case
27                 FLASH pages used by EEPROM emulation, are full.
28            (++) Read EEPROM variable using EE_ReadVariableXbits() functions
29 
30       (#) Clean up functions of FLASH pages, used by EEPROM emulation:
31            (++) There Two modes of erasing:
32             (+++) Polling mode using EE_CleanUp() function
33             (+++) Interrupt mode using EE_CleanUp_IT() function
34            (++) Callback function called when the clean up operation in interrupt
35                 mode, is finished: EE_EndOfCleanup_UserCallback()
36 
37   @endverbatim
38   ******************************************************************************
39   * @attention
40   *
41   * <h2><center>&copy; Copyright (c) 2017 STMicroelectronics International N.V.
42   * All rights reserved.</center></h2>
43   *
44   * Redistribution and use in source and binary forms, with or without
45   * modification, are permitted, provided that the following conditions are met:
46   *
47   * 1. Redistribution of source code must retain the above copyright notice,
48   *    this list of conditions and the following disclaimer.
49   * 2. Redistributions in binary form must reproduce the above copyright notice,
50   *    this list of conditions and the following disclaimer in the documentation
51   *    and/or other materials provided with the distribution.
52   * 3. Neither the name of STMicroelectronics nor the names of other
53   *    contributors to this software may be used to endorse or promote products
54   *    derived from this software without specific written permission.
55   * 4. This software, including modifications and/or derivative works of this
56   *    software, must execute solely and exclusively on microcontroller or
57   *    microprocessor devices manufactured by or for STMicroelectronics.
58   * 5. Redistribution and use of this software other than as permitted under
59   *    this license is void and will automatically terminate your rights under
60   *    this license.
61   *
62   * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
63   * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
64   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
65   * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
66   * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
67   * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
68   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
69   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
70   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
71   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
72   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
73   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74   *
75   ******************************************************************************
76   */
77 
78 /* Includes ------------------------------------------------------------------*/
79 #include "eeprom_emul.h"
80 
81 /** @defgroup EEPROM_Emulation EEPROM_Emulation
82   * @{
83   */
84 
85 /* Private typedef -----------------------------------------------------------*/
86 /** @defgroup EEPROM_Private_Structures EEPROM Private Structures
87   * @{
88   */
89 
90 /**
91   * @brief  EE Find Type structure definition.
92   */
93 /* Type of find requested :
94        READ  --> page in active state
95        WRITE --> page in receive state or active state
96        ERASE --> page in erased state */
97 typedef enum {
98    FIND_READ_PAGE,
99    FIND_WRITE_PAGE,
100    FIND_ERASE_PAGE
101 } EE_Find_type;
102 
103 /**
104   * @brief  EE State Type structure definition.
105   */
106 /* Type of state requested :
107        ERASED  --> page is erased
108        RECEIVE --> page used during data transfer when no more space available in the system
109        ACTIVE  --> page contains valid data and is not full
110        VALID   --> page contains valid data and is full
111        ERASING --> page used during transfer, should be erased after transfer
112        INVALID --> page invalid state */
113 typedef enum {
114    STATE_PAGE_ERASED,
115    STATE_PAGE_RECEIVE,
116    STATE_PAGE_ACTIVE,
117    STATE_PAGE_VALID,
118    STATE_PAGE_ERASING,
119    STATE_PAGE_INVALID
120 } EE_State_type;
121 
122 /**
123   * @brief  EE Transfer Type structure definition.
124   */
125 /* Definition of the different type of page transfer
126         NORMAL  -> copy data page source to page destination
127         RECOVER -> resume page transfer that has been interrupted */
128 typedef enum {
129   EE_TRANSFER_NORMAL,
130   EE_TRANSFER_RECOVER
131 } EE_Transfer_type;
132 
133 /**
134   * @brief  EE State Reliability structure definition.
135   */
136 /* Reliability of page state:
137         RELIABLE  -> header of page is not corrupted, state is reliable
138         CORRUPTED -> header of page is corrupted, state is corrupted */
139 typedef enum {
140   STATE_RELIABLE,
141   STATE_CORRUPTED
142 } EE_State_Reliability;
143 
144 /**
145   * @}
146   */
147 
148 /* Private variables ---------------------------------------------------------*/
149 /** @defgroup EEPROM_Private_Variables EEPROM Private Variables
150   * @{
151   */
152 
153 /* Global variables used to store eeprom status */
154 uint16_t* puhVirtAdd = NULL;                        /*!< Pointer to Virtual addresses Table defined by user */
155 uint16_t uhNbWrittenElements = 0U;                  /*!< Nb of elements written in valid and active pages */
156 uint8_t ubCurrentActivePage = 0U;                   /*!< Current active page (can be active or receive state) */
157 uint32_t uwAddressNextWrite = PAGE_HEADER_SIZE;     /*!< Initialize write position just after page header */
158 
159 /**
160   * @}
161   */
162 
163 /* Private function prototypes -----------------------------------------------*/
164 /** @defgroup EEPROM_Private_Functions EEPROM Private Functions
165   * @{
166   */
167 
168 static EE_Status ReadVariable(uint16_t VirtAddress, EE_DATA_TYPE* pData);
169 static EE_Status WriteVariable(uint16_t VirtAddress, EE_DATA_TYPE Data);
170 static EE_Status VerifyPageFullyErased(uint32_t Address, uint32_t PageSize);
171 static uint32_t FindPage(EE_Find_type Operation);
172 static EE_Status PagesTransfer(uint16_t VirtAddress, EE_DATA_TYPE Data, EE_Transfer_type type);
173 static EE_Status VerifyPagesFullWriteVariable(uint16_t VirtAddress, EE_DATA_TYPE Data);
174 static EE_Status SetPageState(uint32_t Page, EE_State_type State);
175 static EE_State_type GetPageState(uint32_t Address);
176 #if defined(RECOVERY_TEST)
177 static void VerifyStateReset(uint32_t TriggerState);
178 #endif
179 void ConfigureCrc(void);
180 uint16_t CalculateCrc(EE_DATA_TYPE Data, uint16_t VirtAddress);
181 
182 /**
183   * @}
184   */
185 
186 /* Exported functions -------------------------------------------------------*/
187 /** @addtogroup EEPROM_Exported_Functions
188   * @{
189   */
190 
191 /**
192   * @brief  Restore the pages to a known good state in case of power loss.
193   *         If a page is in RECEIVE state, resume transfer.
194   *         Then if some pages are ERASING state, erase these pages.
195   * @param  VirtAddTab Table of virtual addresses defined by user.
196   *           0xFFFF value is prohibited as virtual address.
197   * @param  EraseType: Type of erase to apply on page requiring to be erased.
198   *         This parameter can be one of the following values:
199   *          @arg @ref EE_FORCED_ERASE      pages to erase are erased unconditionnally
200   *          @arg @ref EE_CONDITIONAL_ERASE pages to erase are erased only if not fully erased
201   * @retval EE_Status
202   *           - EE_OK in case of success
203   *           - EE error code in case of error
204   */
EE_Init(uint16_t * VirtAddTab,EE_Erase_type EraseType)205 EE_Status EE_Init(uint16_t* VirtAddTab, EE_Erase_type EraseType)
206 {
207   EE_State_type pagestatus = STATE_PAGE_INVALID;
208   uint32_t page = 0U, pageaddress = 0U, varidx = 0U,
209            nbactivepage = 0U, nbactivereceivepage = 0U, nbvalidpage = 0U,
210            lastvalidpage = 0U, firstvalidpage = 0U,
211            recoverytransfer = 0U;
212   EE_ELEMENT_TYPE addressvalue = 0U;
213   EE_State_Reliability pagestate = STATE_RELIABLE;
214   EE_Status status = EE_OK;
215 
216   /* Check if the configuration is 128-bits bank or 2*64-bits bank */
217   if (CheckBankConfig() != EE_OK)
218   {
219     return EE_INVALID_BANK_CFG;
220   }
221 
222   /***************************************************************************/
223   /* Step 0: Check parameters validity and perform initial configuration     */
224   /***************************************************************************/
225   /* Configure CRC peripheral for eeprom emulation usage */
226   ConfigureCrc();
227 
228   /* Check validity of Table of Virtual addresses */
229   if (VirtAddTab == NULL)
230   {
231     return EE_INVALID_VIRTUALADDRESS;
232   }
233 
234   /* Store Table of Virtual addressess */
235   puhVirtAdd = VirtAddTab;
236 
237   /* Check the variables definitions: 0x0000 and 0xFFFF value are prohibited */
238   for (varidx = 0U; varidx < NB_OF_VARIABLES; varidx++)
239   {
240     if ((puhVirtAdd[varidx] == 0x0000U) || (puhVirtAdd[varidx] == 0xFFFFU))
241     {
242       return EE_INVALID_VIRTUALADDRESS;
243     }
244   }
245 
246   /***************************************************************************/
247   /* Step 1: Read all lines of the flash pages of eeprom emulation to        */
248   /*         delete corrupted lines detectable through NMI                   */
249   /***************************************************************************/
250   for (page = START_PAGE; page < (START_PAGE + PAGES_NUMBER); page++)
251   {
252     pageaddress = PAGE_ADDRESS(page);
253     for (varidx = 0U; varidx < PAGE_SIZE; varidx += EE_ELEMENT_SIZE)
254     {
255       addressvalue = (*(__IO EE_ELEMENT_TYPE*)(pageaddress + varidx));
256     }
257   }
258 
259   /***************************************************************************/
260   /* Step 2: Handle case of reset during transfer with no receive page       */
261   /*         present, by setting missing receive page state                  */
262   /***************************************************************************/
263   /* Check if no active page and no receive page present */
264   /* Browse all pages */
265   for (page = START_PAGE; page < (START_PAGE + PAGES_NUMBER); page++)
266   {
267     pageaddress = PAGE_ADDRESS(page);
268     pagestatus = GetPageState(pageaddress);
269 
270     /* Search for active and receive page */
271     if ((pagestatus == STATE_PAGE_ACTIVE) || (pagestatus == STATE_PAGE_RECEIVE))
272     {
273       nbactivereceivepage++;
274     }
275     /* Keep index of first valid page, and last valid page */
276     else if (pagestatus == STATE_PAGE_VALID)
277     {
278       if (nbvalidpage == 0U)
279       {
280         firstvalidpage = page;
281       }
282       lastvalidpage = page;
283       nbvalidpage++;
284     }
285   }
286 
287   /* Check if no active and no receive page have been detected */
288   if (nbactivereceivepage == 0U)
289   {
290     /* Check if valid pages have been detected */
291     if (nbvalidpage > 0U)
292     {
293       /* Check state of page just before first valid page.
294       If it is erasing page, then page after last valid page shall be set
295       to receiving state */
296       if (GetPageState(PAGE_ADDRESS(PREVIOUS_PAGE(firstvalidpage))) == STATE_PAGE_ERASING)
297       {
298         if (SetPageState(FOLLOWING_PAGE(lastvalidpage), STATE_PAGE_RECEIVE) != EE_OK)
299         {
300           return EE_WRITE_ERROR;
301         }
302       }
303     }
304     /* Format flash pages used for eeprom emulation in case no active, no receive, no valid pages are found */
305     else
306     {
307       return EE_Format(EE_FORCED_ERASE);
308     }
309   }
310 
311   /*********************************************************************/
312   /* Step 3: Handle case of reset during transfer, by performing       */
313   /*         transfer recovery                                         */
314   /*********************************************************************/
315   /* Browse all pages */
316   for (page = START_PAGE; page < (START_PAGE + PAGES_NUMBER); page++)
317   {
318     pageaddress = PAGE_ADDRESS(page);
319     pagestatus = GetPageState(pageaddress);
320 
321     /* Check if there is receive page, meaning transfer has been interrupted */
322     if (pagestatus == STATE_PAGE_RECEIVE)
323     {
324       /* Verify that receive page is a true one, not a corrupted page state */
325       /* Check if page is not the first page of a bloc */
326       if ((page != START_PAGE) && (page != (uint32_t)(START_PAGE + (PAGES_NUMBER / 2U))))
327       {
328         /* Check that previous page is valid state */
329         if (GetPageState(PAGE_ADDRESS(PREVIOUS_PAGE(page))) == STATE_PAGE_VALID)
330         {
331           /* The receive page is a true receive page */
332           pagestate = STATE_RELIABLE;
333         }
334         else /* Previous page is not valid state */
335         {
336           /* The receive page is false receive page due to header corruption */
337           pagestate = STATE_CORRUPTED;
338         }
339       }
340       else /* The receive page is the first page of a bloc */
341       {
342         /* Check that following page is erased state */
343         if (GetPageState(PAGE_ADDRESS(FOLLOWING_PAGE(page))) == STATE_PAGE_ERASED)
344         {
345           /* The receive page is a true receive page */
346           pagestate = STATE_RELIABLE;
347         }
348         else /* Following page is not erased state */
349         {
350           /* The receive page is false receive page due to header corruption */
351           pagestate = STATE_CORRUPTED;
352         }
353       }
354 
355       /* If the receive page is a true receive page, resume pages transfer */
356       if (pagestate == STATE_RELIABLE)
357       {
358         /* Initialize current active page */
359         ubCurrentActivePage = page;
360 
361         /* Resume the interrupted page transfer, using dummy new data */
362         if (PagesTransfer(0U, 0U, EE_TRANSFER_RECOVER) != EE_CLEANUP_REQUIRED)
363         {
364           return EE_TRANSFER_ERROR;
365         }
366 
367         /* Memorize transfer recovery occured */
368         recoverytransfer = 1U;
369 
370         /* transfer recovery is done, then stop searching receive page */
371         break;
372       }
373     }
374   }
375 
376   /*********************************************************************/
377   /* Step 4: Verify presence of one unique active page                 */
378   /*         If more than one active page, raise error                 */
379   /*         If no active page present, set missing active page        */
380   /*********************************************************************/
381   /* Browse all pages to search for active pages */
382   nbactivepage = 0U;
383   for (page = START_PAGE; page < (START_PAGE + PAGES_NUMBER); page++)
384   {
385     pageaddress = PAGE_ADDRESS(page);
386     pagestatus = GetPageState(pageaddress);
387 
388     /* Search for active page */
389     if (pagestatus == STATE_PAGE_ACTIVE)
390     {
391       /* Verify that active page is a true one, not a corrupted page state */
392       /* Check if page is not the first page of a bloc */
393       if ((page != START_PAGE) && (page != (uint32_t)(START_PAGE + (PAGES_NUMBER / 2U))))
394       {
395         /* Check that previous page is valid state */
396         if (GetPageState(PAGE_ADDRESS(PREVIOUS_PAGE(page))) == STATE_PAGE_VALID)
397         {
398           /* The active page is a true active page */
399           pagestate = STATE_RELIABLE;
400         }
401         else /* Previous page is not valid state */
402         {
403           /* The active page is false active page due to header corruption */
404           pagestate = STATE_CORRUPTED;
405         }
406       }
407       else /* The active page is the first page of a bloc */
408       {
409         /* Check that following page is erased state */
410         if (GetPageState(PAGE_ADDRESS(FOLLOWING_PAGE(page))) == STATE_PAGE_ERASED)
411         {
412           /* The active page is a true active page */
413           pagestate = STATE_RELIABLE;
414         }
415         else /* Following page is not erased state */
416         {
417           /* The active page is false active page due to header corruption */
418           pagestate = STATE_CORRUPTED;
419         }
420       }
421 
422       /* If the active page is a true active page, initialize global variables */
423       if (pagestate == STATE_RELIABLE)
424       {
425         if (nbactivepage == 0U)
426         {
427           ubCurrentActivePage = page;
428           nbactivepage++;
429         }
430         else
431         {
432           /* Error: More than one reliable active page is present */
433           return EE_INVALID_PAGE_SEQUENCE;
434         }
435       }
436     }
437     /* Keep index of last valid page, will be required in case no active page is found */
438     else if (pagestatus == STATE_PAGE_VALID)
439     {
440       lastvalidpage = page;
441     }
442   }
443 
444   /* In case no active page is found, set page after last valid page to active state */
445   if (nbactivepage == 0U)
446   {
447     ubCurrentActivePage = FOLLOWING_PAGE(lastvalidpage);
448     if (SetPageState(ubCurrentActivePage, STATE_PAGE_ACTIVE) != EE_OK)
449     {
450       return EE_WRITE_ERROR;
451     }
452   }
453 
454   /*********************************************************************/
455   /* Step 5: Initialize eeprom emulation global variables relative     */
456   /*         to active page                                            */
457   /*********************************************************************/
458   /* Initialize global variables, with elements detected in active page */
459   uhNbWrittenElements = 0U;
460   uwAddressNextWrite = PAGE_HEADER_SIZE;
461 
462   for (varidx = PAGE_HEADER_SIZE; varidx < PAGE_SIZE; varidx += EE_ELEMENT_SIZE)
463   {
464     /* Check elements present in active page */
465     addressvalue = (*(__IO EE_ELEMENT_TYPE*)(PAGE_ADDRESS(ubCurrentActivePage) + varidx));
466     if (addressvalue != EE_MASK_FULL)
467     {
468       /* Then increment uhNbWrittenElements and uwAddressNextWrite */
469       uhNbWrittenElements++;
470       uwAddressNextWrite += EE_ELEMENT_SIZE;
471     }
472     else /* no more element in the page */
473     {
474       break;
475     }
476   }
477 
478   /*********************************************************************/
479   /* Step 6: Finalize eeprom emulation global variables relative       */
480   /*         to valid pages, and check consistency of pages sequence   */
481   /*********************************************************************/
482   /* Check consistency of pages sequence: one active page, optionnally some valid pages before */
483   /* Update global variable uhNbWrittenElements if valid pages are found */
484   page = ubCurrentActivePage;
485   firstvalidpage = ubCurrentActivePage;
486   while ((page != START_PAGE) && (page != (uint32_t)(START_PAGE + (PAGES_NUMBER / 2U))))
487   {
488     /* Decrement page index among circular pages list */
489     page = PREVIOUS_PAGE(page);
490     pagestatus = GetPageState(PAGE_ADDRESS(page));
491 
492     /* Check if page is valid state */
493     if (pagestatus == STATE_PAGE_VALID)
494     {
495       /* Update uhNbWrittenElements with number of elements in full page */
496       uhNbWrittenElements += NB_MAX_ELEMENTS_BY_PAGE;
497 
498       /* Keep index of first valid page */
499       firstvalidpage = page;
500     }
501     else
502     {
503       /* Error: Pages sequence is not consistent */
504       return EE_INVALID_PAGE_SEQUENCE;
505     }
506   }
507 
508   /*********************************************************************/
509   /* Step 7: Ensure empty pages are erased                             */
510   /*********************************************************************/
511   /* Ensure all pages after active page, until first valid page, are erased */
512   page = FOLLOWING_PAGE(ubCurrentActivePage);
513   pageaddress = PAGE_ADDRESS(page);
514 
515   while (page != firstvalidpage)
516   {
517     /* Check if page erase has to be forced unconditionally (default case) */
518     if (EraseType == EE_FORCED_ERASE)
519     {
520       /* Force page erase independently of its content */
521       if (PageErase(page, 1U) != EE_OK)
522       {
523         return EE_ERASE_ERROR;
524       }
525     }
526     else /* EraseType == EE_CONDITIONAL_ERASE */
527     {
528       /* Check if page is fully erased */
529       if (VerifyPageFullyErased(pageaddress, PAGE_SIZE) == EE_PAGE_NOTERASED)
530       {
531         /* Erase pages if not fully erased */
532         if (PageErase(page, 1U) != EE_OK)
533         {
534           return EE_ERASE_ERROR;
535         }
536       }
537     }
538 
539     /* Increment page index among circular pages list, to get first page to erased */
540     page = FOLLOWING_PAGE(page);
541     pageaddress = PAGE_ADDRESS(page);
542   }
543 
544   /*********************************************************************/
545   /* Step 8: Perform dummy write '0' to get rid of potential           */
546   /*         instability of line value 0xFFFFFFFF consecutive to a     */
547   /*         reset during write here                                   */
548   /*         Only needed if recovery transfer did not occured          */
549   /*********************************************************************/
550   if (recoverytransfer == 0U)
551   {
552     status = VerifyPagesFullWriteVariable(0U, 0U);
553 
554     /* The dummy write can be skipped in case pages are full
555        because in this case potential instability can not happen */
556     if ((status != EE_OK) && (status != EE_PAGE_FULL))
557     {
558       return EE_WRITE_ERROR;
559     }
560   }
561 
562   return EE_OK;
563 }
564 
565 /**
566   * @brief  Erases all flash pages of eeprom emulation, and set first page
567   *         header as ACTIVE.
568   * @note   This function can be called the very first time eeprom emulation is
569   *         used, to prepare flash pages for eeprom emulation with empty eeprom
570             variables. It can also be called at any time, to flush all eeprom
571   *         variables.
572   * @param  EraseType: Type of erase to apply on page requiring to be erased.
573   *         This parameter can be one of the following values:
574   *          @arg @ref EE_FORCED_ERASE      pages to erase are erased unconditionnally
575   *          @arg @ref EE_CONDITIONAL_ERASE pages to erase are erased only if not fully erased
576   * @retval EE_Status
577   *           - EE_OK: on success
578   *           - EE error code: if an error occurs
579   */
EE_Format(EE_Erase_type EraseType)580 EE_Status EE_Format(EE_Erase_type EraseType)
581 {
582   uint32_t page = 0U;
583 
584   /* Check if the configuration is 128-bits bank or 2*64-bits bank */
585   if (CheckBankConfig() != EE_OK)
586   {
587     return EE_INVALID_BANK_CFG;
588   }
589 
590   /* Erase All Pages */
591   for (page = START_PAGE; page < (START_PAGE + PAGES_NUMBER); page++)
592   {
593     /* Check if page erase has to be forced unconditionally (default case) */
594     if (EraseType == EE_FORCED_ERASE)
595     {
596       /* Force page erase independently of its content */
597       if (PageErase(page, 1U) != EE_OK)
598       {
599         return EE_ERASE_ERROR;
600       }
601     }
602     else /* EraseType == EE_CONDITIONAL_ERASE */
603     {
604       /* Check if Page is not yet fully erased */
605       if (VerifyPageFullyErased(PAGE_ADDRESS(page), PAGE_SIZE) == EE_PAGE_NOTERASED)
606       {
607         /* Erase the page */
608         /* If Erase operation was failed, a Flash error code is returned */
609         if (PageErase(page, 1U) != EE_OK)
610         {
611           return EE_ERASE_ERROR;
612         }
613       }
614     }
615   }
616 
617   /* Set first Page in Active State */
618   /* If program operation was failed, a Flash error code is returned */
619   if (SetPageState(START_PAGE, STATE_PAGE_ACTIVE) != EE_OK)
620   {
621     return EE_WRITE_ERROR;
622   }
623 
624   /* Reset global variables */
625   uhNbWrittenElements = (uint16_t)0U;
626   ubCurrentActivePage = START_PAGE;
627   uwAddressNextWrite = PAGE_HEADER_SIZE; /* Initialize write position just after page header */
628 
629   return EE_OK;
630 }
631 
632 #if defined(EE_ACCESS_32BITS)
633 /**
634   * @brief  Returns the last stored variable data, if found, which correspond to
635   *         the passed virtual address
636   * @param  VirtAddress Variable virtual address
637   * @param  pData Variable containing the 32bits read variable value
638   * @retval EE_Status
639   *           - EE_OK: if variable was found
640   *           - EE error code: if an error occurs
641   */
EE_ReadVariable32bits(uint16_t VirtAddress,uint32_t * pData)642 EE_Status EE_ReadVariable32bits(uint16_t VirtAddress, uint32_t* pData)
643 {
644   EE_DATA_TYPE datatmp = 0U;
645   EE_Status status = EE_OK;
646 
647   /* Read variable of size EE_DATA_TYPE, then cast it to 32bits */
648   status = ReadVariable(VirtAddress, &datatmp);
649   *pData = (uint32_t) datatmp;
650 
651   return status;
652 }
653 #endif
654 
655 /**
656   * @brief  Returns the last stored variable data, if found, which correspond to
657   *         the passed virtual address
658   * @param  VirtAddress Variable virtual address
659   * @param  pData Variable containing the 16bits read variable value
660   * @retval EE_Status
661   *           - EE_OK: if variable was found
662   *           - EE error code: if an error occurs
663   */
EE_ReadVariable16bits(uint16_t VirtAddress,uint16_t * pData)664 EE_Status EE_ReadVariable16bits(uint16_t VirtAddress, uint16_t* pData)
665 {
666   EE_DATA_TYPE datatmp = 0U;
667   EE_Status status = EE_OK;
668 
669   /* Read variable of size EE_DATA_TYPE, then cast it to 16bits */
670   status = ReadVariable(VirtAddress, &datatmp);
671   *pData = (uint16_t) datatmp;
672 
673   return status;
674 }
675 
676 /**
677   * @brief  Returns the last stored variable data, if found, which correspond to
678   *         the passed virtual address
679   * @param  VirtAddress Variable virtual address
680   * @param  pData Variable containing the 8bits read variable value
681   * @retval EE_Status
682   *           - EE_OK: if variable was found
683   *           - EE error code: if an error occurs
684   */
EE_ReadVariable8bits(uint16_t VirtAddress,uint8_t * pData)685 EE_Status EE_ReadVariable8bits(uint16_t VirtAddress, uint8_t* pData)
686 {
687   EE_DATA_TYPE datatmp = 0U;
688   EE_Status status = EE_OK;
689 
690   /* Read variable of size EE_DATA_TYPE, then cast it to 8bits */
691   status = ReadVariable(VirtAddress, &datatmp);
692   *pData = (uint8_t) datatmp;
693 
694   return status;
695 }
696 
697 #if defined(EE_ACCESS_32BITS)
698 /**
699   * @brief  Writes/updates variable data in EEPROM.
700   *         Trig internal Pages transfer if half of the pages are full.
701   * @warning This function is not reentrant
702   * @param  VirtAddress Variable virtual address
703   * @param  Data 32bits data to be written
704   * @retval EE_Status
705   *           - EE_OK: on success
706   *           - EE_CLEANUP_REQUIRED: success and user has to trig flash pages cleanup
707   *           - EE error code: if an error occurs
708   */
EE_WriteVariable32bits(uint16_t VirtAddress,uint32_t Data)709 EE_Status EE_WriteVariable32bits(uint16_t VirtAddress, uint32_t Data)
710 {
711   return WriteVariable(VirtAddress, (EE_DATA_TYPE) Data);
712 }
713 #endif
714 
715 /**
716   * @brief  Writes/updates variable data in EEPROM.
717   *         Trig internal Pages transfer if half of the pages are full.
718   * @warning This function is not reentrant
719   * @param  VirtAddress Variable virtual address
720   * @param  Data 16bits data to be written
721   * @retval EE_Status
722   *           - EE_OK: on success
723   *           - EE_CLEANUP_REQUIRED: success and user has to trig flash pages cleanup
724   *           - EE error code: if an error occurs
725   */
EE_WriteVariable16bits(uint16_t VirtAddress,uint16_t Data)726 EE_Status EE_WriteVariable16bits(uint16_t VirtAddress, uint16_t Data)
727 {
728   return WriteVariable(VirtAddress, (EE_DATA_TYPE) Data);
729 }
730 
731 /**
732   * @brief  Writes/updates variable data in EEPROM.
733   *         Trig internal Pages transfer if half of the pages are full.
734   * @warning This function is not reentrant
735   * @param  VirtAddress Variable virtual address
736   * @param  Data 8bits data to be written
737   * @retval EE_Status
738   *           - EE_OK: on success
739   *           - EE_CLEANUP_REQUIRED: success and user has to trig flash pages cleanup
740   *           - EE error code: if an error occurs
741   */
EE_WriteVariable8bits(uint16_t VirtAddress,uint8_t Data)742 EE_Status EE_WriteVariable8bits(uint16_t VirtAddress, uint8_t Data)
743 {
744   return WriteVariable(VirtAddress, (EE_DATA_TYPE) Data);
745 }
746 
747 /**
748   * @brief  Erase group of pages which are erasing state, in polling mode.
749   *         Could be either first half or second half of total pages number.
750   * @note   This function should be called when EE_WriteVariableXXbits has
751   *         returned EE_CLEANUP_REQUIRED status (and only in that case)
752   * @retval EE_Status
753   *           - EE_OK: in case of success
754   *           - EE error code: if an error occurs
755   */
EE_CleanUp(void)756 EE_Status EE_CleanUp(void)
757 {
758   uint32_t firstpage = 0U, page = 0U;
759   uint32_t firstpageaddress = 0U, pageaddress = 0U;
760   EE_State_type firstpagestatus = STATE_PAGE_INVALID, pagestatus = STATE_PAGE_INVALID;
761 
762   /* Check first half and second half page group */
763   for (firstpage = START_PAGE; firstpage < (START_PAGE + PAGES_NUMBER); firstpage += (PAGES_NUMBER / 2U))
764   {
765     /* Check status of first page of the group */
766     firstpageaddress = PAGE_ADDRESS(firstpage);
767     firstpagestatus = GetPageState(firstpageaddress);
768 
769     /* If first page of the group is erasing state, check that all other pages
770     of the group are also erasing state */
771     if (firstpagestatus == STATE_PAGE_ERASING)
772     {
773       for (page = (firstpage + 1U); page < (firstpage + (PAGES_NUMBER / 2U)); page++)
774       {
775         pageaddress = PAGE_ADDRESS(page);
776         pagestatus = GetPageState(pageaddress);
777 
778         /* If page is not erasing, return error */
779         if (pagestatus != STATE_PAGE_ERASING)
780         {
781           return EE_ERROR_NOERASING_PAGE;
782         }
783       }
784 
785       /* Erase all the pages of the group */
786       /* If erase operation fails, a Flash error code is returned */
787       if (PageErase(firstpage, PAGES_NUMBER / 2U) != EE_OK)
788       {
789         return EE_ERASE_ERROR;
790       }
791       else
792       {
793         return EE_OK;
794       }
795     }
796   }
797 
798   /* Error if no erasing pages group is found */
799   return EE_ERROR_NOERASING_PAGE;
800 }
801 
802 /**
803   * @brief  Erase group of pages which are erasing state, in IT mode.
804   *         Could be either first half or second half of total pages number.
805   * @note   This function should be called when EE_WriteVariableXXbits has
806   *         returned EE_CLEANUP_REQUIRED status (and only in that case)
807   * @retval EE_Status
808   *           - EE_OK: in case of success
809   *           - EE error code: if an error occurs
810   */
EE_CleanUp_IT(void)811 EE_Status EE_CleanUp_IT(void)
812 {
813   uint32_t firstpage = 0U, page = 0U;
814   uint32_t firstpageaddress = 0U, pageaddress = 0U;
815   EE_State_type firstpagestatus = STATE_PAGE_INVALID, pagestatus = STATE_PAGE_INVALID;
816 
817   /* Check first half and second half page group */
818   for (firstpage = START_PAGE; firstpage < (START_PAGE + PAGES_NUMBER); firstpage += (PAGES_NUMBER / 2U))
819   {
820     /* Check status of first page of the group */
821     firstpageaddress = PAGE_ADDRESS(firstpage);
822     firstpagestatus = GetPageState(firstpageaddress);
823 
824     /* If first page of the group is erasing state, check that all other pages
825     of the group are also erasing state */
826     if (firstpagestatus == STATE_PAGE_ERASING)
827     {
828       for (page = (firstpage + 1U); page < (firstpage + (PAGES_NUMBER / 2U)); page++)
829       {
830         pageaddress = PAGE_ADDRESS(page);
831         pagestatus = GetPageState(pageaddress);
832 
833         /* If page is not erasing, return error */
834         if (pagestatus != STATE_PAGE_ERASING)
835         {
836           return EE_ERROR_NOERASING_PAGE;
837         }
838       }
839 
840       /* Erase all the pages of the group */
841       /* If erase operation fails, a Flash error code is returned */
842       if (PageErase_IT(firstpage, PAGES_NUMBER / 2U) != EE_OK)
843       {
844         return EE_ERASE_ERROR;
845       }
846       else
847       {
848         return EE_OK;
849       }
850     }
851   }
852 
853   /* Error if no erasing pages group is found */
854   return EE_ERROR_NOERASING_PAGE;
855 }
856 
857 /**
858   * @brief  Delete corrupted Flash address, can be called under NMI.
859   * @param  Address Address of the FLASH Memory to delete
860   * @retval EE_Status
861   *           - EE_OK: on success
862   *           - EE error code: if an error occurs
863   */
EE_DeleteCorruptedFlashAddress(uint32_t Address)864 EE_Status EE_DeleteCorruptedFlashAddress(uint32_t Address)
865 {
866   return DeleteCorruptedFlashAddress(Address);
867 }
868 
869 /**
870   * @brief  Clean Up end of operation interrupt callback.
871   * @retval None
872   */
EE_EndOfCleanup_UserCallback(void)873 __weak void EE_EndOfCleanup_UserCallback(void)
874 {
875   /* NOTE : This function should not be modified, when the callback is needed,
876             the EE_EndOfCleanup_UserCallback could be implemented in the user file
877    */
878 }
879 
880 /**
881   * @}
882   */
883 
884 /* Private functions ---------------------------------------------------------*/
885 /** @addtogroup EEPROM_Private_Functions
886   * @{
887   */
888 
889 /**
890   * @brief  Returns the last stored variable data, if found, which correspond to
891   *         the passed virtual address
892   * @param  VirtAddress Variable virtual address
893   * @param  pData Variable containing the EE_DATA_TYPE read variable value
894   * @retval EE_Status
895   *           - EE_OK: if variable was found
896   *           - EE error code: if an error occurs
897   */
ReadVariable(uint16_t VirtAddress,EE_DATA_TYPE * pData)898 static EE_Status ReadVariable(uint16_t VirtAddress, EE_DATA_TYPE* pData)
899 {
900   EE_ELEMENT_TYPE addressvalue = 0U;
901   uint32_t page = 0U, pageaddress = 0U, counter = 0U, crc = 0U;
902   EE_State_type pagestate = STATE_PAGE_INVALID;
903 
904   /* Get active Page for read operation */
905   page = FindPage(FIND_READ_PAGE);
906 
907   /* Check if there is no active page */
908   if (page == EE_NO_PAGE_FOUND)
909   {
910     return EE_ERROR_NOACTIVE_PAGE;
911   }
912   pageaddress = PAGE_ADDRESS(page);
913   pagestate = GetPageState(pageaddress);
914 
915   /* Search variable in active page and valid pages until erased page is found
916      or in erasing pages until erased page is found */
917   while ((pagestate == STATE_PAGE_ACTIVE) || (pagestate == STATE_PAGE_VALID) || (pagestate == STATE_PAGE_ERASING))
918   {
919     /* Set counter index to last element position in the page */
920     counter = PAGE_SIZE - EE_ELEMENT_SIZE;
921 
922     /* Check each page address starting from end */
923     while (counter >= PAGE_HEADER_SIZE)
924     {
925       /* Get the current location content to be compared with virtual address */
926       addressvalue = (*(__IO EE_ELEMENT_TYPE*)(pageaddress + counter));
927       if (addressvalue != EE_PAGESTAT_ERASED)
928       {
929         /* Compare the read address with the virtual address */
930         if (EE_VIRTUALADDRESS_VALUE(addressvalue) == VirtAddress)
931         {
932           /* Calculate crc of variable data and virtual address */
933           crc = CalculateCrc(EE_DATA_VALUE(addressvalue), EE_VIRTUALADDRESS_VALUE(addressvalue));
934 
935           /* if crc verification pass, data is correct and is returned.
936              if crc verification fails, data is corrupted and has to be skip */
937           if (crc == EE_CRC_VALUE(addressvalue))
938           {
939             /* Get content of variable value */
940             *pData = EE_DATA_VALUE(addressvalue);
941 
942             return EE_OK;
943           }
944         }
945       }
946       /* Next address location */
947       counter -= EE_ELEMENT_SIZE;
948     }
949 
950     /* Decrement page index circularly, among pages allocated to eeprom emulation */
951     page = PREVIOUS_PAGE(page);
952     pageaddress = PAGE_ADDRESS(page);
953     pagestate = GetPageState(pageaddress);
954   }
955 
956   /* Variable is not found */
957   return EE_NO_DATA;
958 }
959 
960 /**
961   * @brief  Writes/updates variable data in EEPROM
962   *         Trig internal Pages transfer if half of the pages are full
963   * @param  VirtAddress Variable virtual address
964   * @param  Data EE_DATA_TYPE data to be written
965   * @retval EE_Status
966   *           - EE_OK: on success, without page transfer
967   *           - EE_CLEANUP_REQUIRED: on success, with page transfer occured
968   *           - EE error code: if an error occurs
969   */
WriteVariable(uint16_t VirtAddress,EE_DATA_TYPE Data)970 static EE_Status WriteVariable(uint16_t VirtAddress, EE_DATA_TYPE Data)
971 {
972   EE_Status status = EE_OK;
973 
974   /* Write the variable virtual address and value in the EEPROM, if not full */
975   status = VerifyPagesFullWriteVariable(VirtAddress, Data);
976   if (status == EE_PAGE_FULL)
977   {
978     /* In case the EEPROM pages are full, perform Pages transfer */
979     return PagesTransfer(VirtAddress, Data, EE_TRANSFER_NORMAL);
980   }
981 
982   /* Return last operation status */
983   return status;
984 }
985 
986 /**
987   * @brief  Verify if specified page is fully erased.
988   * @param  Address page address
989   * @param  PageSize page size
990   * @retval EE_Status
991   *           - EE_PAGE_NOTERASED : if Page not erased
992   *           - EE_PAGE_ERASED    : if Page erased
993   */
VerifyPageFullyErased(uint32_t Address,uint32_t PageSize)994 static EE_Status VerifyPageFullyErased(uint32_t Address, uint32_t PageSize)
995 {
996   EE_Status readstatus = EE_PAGE_ERASED;
997   uint32_t counter = 0U;
998 
999   /* Check each element in the page */
1000   while (counter < PageSize)
1001   {
1002     /* Compare the read address with the virtual address */
1003     if ((*(__IO EE_ELEMENT_TYPE*)(Address+counter)) != EE_PAGESTAT_ERASED)
1004     {
1005       /* In case one element is not erased, reset readstatus flag */
1006       readstatus = EE_PAGE_NOTERASED;
1007     }
1008     /* Next address location */
1009     counter = counter + EE_ELEMENT_SIZE;
1010   }
1011 
1012   /* Return readstatus value */
1013   return readstatus;
1014 }
1015 
1016 /**
1017   * @brief  Find suitable page for read/write/erase operation.
1018   *   It also update pages state if current page is full.
1019   *   And it force cleanup if all pages are full.
1020   * @param  Operation Type of page to be requested.
1021   *   This parameter can be one of the following values:
1022   *     @arg @ref FIND_READ_PAGE return the active page index
1023   *     @arg @ref FIND_WRITE_PAGE return the write page index
1024   *     @arg @ref FIND_ERASE_PAGE return the erase page index
1025   * @retval Page_Index
1026   *           - Page Index: on success
1027   *           - @ref EE_NO_PAGE_FOUND : if an error occurs
1028   */
FindPage(EE_Find_type Operation)1029 static uint32_t FindPage(EE_Find_type Operation)
1030 {
1031   EE_State_type currentpagestatus = STATE_PAGE_INVALID, followingpagestatus = STATE_PAGE_INVALID;
1032   uint32_t currentpage = 0U, followingpage = 0U, previouspage = 0U;
1033 
1034   /* Get currentpage status */
1035   currentpage = ubCurrentActivePage;
1036   currentpagestatus = GetPageState(PAGE_ADDRESS(currentpage));
1037 
1038   /* Get followingpage status */
1039   followingpage = FOLLOWING_PAGE(currentpage);
1040   followingpagestatus = GetPageState(PAGE_ADDRESS(followingpage));
1041 
1042   /* Get previouspage status */
1043   previouspage = PREVIOUS_PAGE(currentpage);
1044 
1045   /* Write, read or erase operation */
1046   switch (Operation)
1047   {
1048     case FIND_WRITE_PAGE:   /* ---- Write operation ---- */
1049       /* Normal operation, no page transfer on going */
1050       if (currentpagestatus == STATE_PAGE_ACTIVE)
1051       {
1052         /* Check if active page is not full */
1053         if (uwAddressNextWrite < PAGE_SIZE)
1054         {
1055           /* Return current Active page */
1056           return currentpage;
1057         }
1058         else
1059         /* No more space in current active page */
1060         {
1061           /* Check if following page is erasing state */
1062           if (followingpagestatus == STATE_PAGE_ERASING)
1063           {
1064             /* Force Cleanup, as not yet performed by user */
1065             if (EE_CleanUp() != EE_OK)
1066             {
1067               return EE_NO_PAGE_FOUND;
1068             }
1069           }
1070 
1071           /* Set current active page in valid state */
1072           if (SetPageState(currentpage, STATE_PAGE_VALID) != EE_OK)
1073           {
1074             return EE_NO_PAGE_FOUND;
1075           }
1076 
1077 #if defined(RECOVERY_TEST)
1078           VerifyStateReset(8U);
1079 #endif
1080 
1081           /* Set following page as active */
1082           if (SetPageState(followingpage, STATE_PAGE_ACTIVE) != EE_OK)
1083           {
1084             return EE_NO_PAGE_FOUND;
1085           }
1086           uwAddressNextWrite = PAGE_HEADER_SIZE;   /* Skip page header */
1087           return followingpage;         /* Following page is now active one */
1088         }
1089       }
1090       /* Transfer is on going, page receiving data */
1091       else
1092       {
1093         if (currentpagestatus == STATE_PAGE_RECEIVE)
1094         {
1095           /* Check if receive page is not full */
1096           if (uwAddressNextWrite < PAGE_SIZE)
1097           {
1098             /* Return current receive page */
1099             return currentpage;
1100           }
1101           else
1102           /* No more space in current receive page */
1103           {
1104             /* Check if following page is erasing state */
1105             if (followingpagestatus == STATE_PAGE_ERASING)
1106             {
1107               /* Force Cleanup, as not yet performed by user */
1108               if (EE_CleanUp() != EE_OK)
1109               {
1110                 return EE_NO_PAGE_FOUND;
1111               }
1112             }
1113 
1114             /* Set current receive page in valid state */
1115             if (SetPageState(currentpage, STATE_PAGE_VALID) != EE_OK)
1116             {
1117               return EE_NO_PAGE_FOUND;
1118             }
1119 
1120             /* Set following page as receive */
1121             if (SetPageState(followingpage, STATE_PAGE_RECEIVE) != EE_OK)
1122             {
1123               return EE_NO_PAGE_FOUND;
1124             }
1125             uwAddressNextWrite = PAGE_HEADER_SIZE;   /* Skip page header */
1126             return followingpage;         /* Following page is now active one */
1127           }
1128         }
1129         else
1130         {
1131           return EE_NO_PAGE_FOUND;   /* No active Page */
1132         }
1133       }
1134 
1135     case FIND_READ_PAGE:  /* ---- Read operation ---- */
1136       if (currentpagestatus == STATE_PAGE_ACTIVE)
1137       {
1138         return currentpage;
1139       }
1140       else
1141       {
1142         if (currentpagestatus == STATE_PAGE_RECEIVE)
1143         {
1144           return previouspage;
1145         }
1146         else
1147         {
1148           return EE_NO_PAGE_FOUND;   /* No active Page */
1149         }
1150       }
1151 
1152     case FIND_ERASE_PAGE: /* ---- Return the erased page */
1153       if (followingpagestatus == STATE_PAGE_ERASED)
1154       {
1155         return followingpage;
1156       }
1157       else
1158       {
1159         return EE_NO_PAGE_FOUND;  /* No erased Page */
1160       }
1161 
1162     default:
1163       ;
1164   }
1165 
1166   return EE_NO_PAGE_FOUND;
1167 }
1168 
1169 /**
1170   * @brief  Writes a new variable data in fresh new page in case of normal
1171   *         transfer, and transfers last updated elements from full pages to
1172   *         empty pages in any cases.
1173   * @param  VirtAddress 16 bit virtual address of the new variable data
1174   * @param  Data @ref EE_DATA_TYPE data value of the new variable data
1175   * @param  Type Type of transfer.
1176   *         This parameter can be one of the EE_Transfer_type enum values.
1177   *            @arg @ref EE_TRANSFER_NORMAL Pages transfer during normal processing
1178   *            @arg @ref EE_TRANSFER_RECOVER Recovering pages transfer at Init
1179   * @retval EE_Status
1180   *           - EE_CLEANUP_REQUIRED: on success
1181   *           - EE error code: if an error occurs
1182   */
PagesTransfer(uint16_t VirtAddress,EE_DATA_TYPE Data,EE_Transfer_type Type)1183 static EE_Status PagesTransfer(uint16_t VirtAddress, EE_DATA_TYPE Data, EE_Transfer_type Type)
1184 {
1185   EE_State_type pagestatus = STATE_PAGE_INVALID;
1186   uint32_t pageaddress = 0U;
1187   uint32_t page = 0U;
1188   uint32_t varidx = 0U;
1189   EE_ELEMENT_TYPE addressvalue = 0U;
1190   EE_Status status = EE_OK;
1191   EE_DATA_TYPE DataValue = 0U;
1192 
1193   /* Get receive Page for transfer operation */
1194   page = FindPage((Type == EE_TRANSFER_NORMAL?FIND_ERASE_PAGE:FIND_WRITE_PAGE));
1195   if (page == EE_NO_PAGE_FOUND)
1196   {
1197     return EE_ERROR_NOERASE_PAGE;
1198   }
1199 
1200   /* Reinitialize number of data written in the pages, and current active page */
1201   uhNbWrittenElements = 0U;
1202   ubCurrentActivePage = page;
1203   uwAddressNextWrite = PAGE_HEADER_SIZE;
1204 
1205   /* Mark the erased page at receive state in case of normal transfer */
1206   /* It is already the case in recover transfer case */
1207   /* If program operation was failed, a Flash error code is returned */
1208   if (Type == EE_TRANSFER_NORMAL)
1209   {
1210     if (SetPageState(page, STATE_PAGE_RECEIVE) != EE_OK)
1211     {
1212       return EE_WRITE_ERROR;
1213     }
1214   }
1215 
1216 #if defined(RECOVERY_TEST)
1217   if (Type == EE_TRANSFER_NORMAL)
1218   {
1219     VerifyStateReset(1U);
1220   }
1221 #endif
1222 
1223   /* Set the previous active page and all previous valid pages to erasing state */
1224   /* In case of recover transfer, some of these pages may already be marked erasing */
1225   page = PREVIOUS_PAGE(page);
1226   pageaddress = PAGE_ADDRESS(page);
1227   pagestatus = GetPageState(pageaddress);
1228 
1229   if ((pagestatus == STATE_PAGE_ACTIVE) || (pagestatus == STATE_PAGE_ERASING))
1230   {
1231     /* Set active page to erasing */
1232     if (pagestatus == STATE_PAGE_ACTIVE)
1233     {
1234       if (SetPageState(page, STATE_PAGE_ERASING) != EE_OK)
1235       {
1236         return EE_WRITE_ERROR;
1237       }
1238     }
1239 
1240 #if defined(RECOVERY_TEST)
1241     if (Type == EE_TRANSFER_NORMAL)
1242     {
1243       VerifyStateReset(2U);
1244     }
1245 #endif
1246 
1247     /* Inspect the previous pages to set all valid pages to erasing state */
1248     /* In case of recover, some valid pages may be already erasing state */
1249     page = PREVIOUS_PAGE(page);
1250     pageaddress = PAGE_ADDRESS(page);
1251     pagestatus = GetPageState(pageaddress);
1252 
1253     while ((pagestatus == STATE_PAGE_VALID) || (pagestatus == STATE_PAGE_ERASING))
1254     {
1255       /* Set valid page to erasing */
1256       if (pagestatus == STATE_PAGE_VALID)
1257       {
1258         if (SetPageState(page, STATE_PAGE_ERASING) != EE_OK)
1259         {
1260           return EE_WRITE_ERROR;
1261         }
1262       }
1263 
1264 #if defined(RECOVERY_TEST)
1265       if (Type == EE_TRANSFER_NORMAL)
1266       {
1267         VerifyStateReset(3U);
1268       }
1269 #endif
1270 
1271       /* decrement page index */
1272       page = PREVIOUS_PAGE(page);
1273       pageaddress = PAGE_ADDRESS(page);
1274       pagestatus = GetPageState(pageaddress);
1275     }
1276   }
1277   else
1278   {
1279     if ((Type == EE_TRANSFER_RECOVER) && (pagestatus == STATE_PAGE_VALID))
1280     {
1281       /* This can happen in case of recover transfer. It indicates that previous */
1282       /* transfer goes far enough to fill a complete receive page at least */
1283       /* (valid state). Then erasing state marking was already completed */
1284     }
1285     else
1286     {
1287       /* Inconsistent previous page state */
1288       return EE_INVALID_PAGE_SEQUENCE;
1289     }
1290   }
1291 
1292 #if defined(RECOVERY_TEST)
1293   if (Type == EE_TRANSFER_NORMAL)
1294   {
1295     VerifyStateReset(4U);
1296   }
1297 #endif
1298 
1299   /* In case of recover transfer, transfer must be resumed where it has been stopped */
1300   /* Update global variables to reflect current transfer status */
1301   if (Type == EE_TRANSFER_RECOVER)
1302   {
1303     /* Count number of elements already transferred in current receive page */
1304     for (varidx = PAGE_HEADER_SIZE; varidx < PAGE_SIZE; varidx += EE_ELEMENT_SIZE)
1305     {
1306       /* Get next element in receive page */
1307       addressvalue = (*(__IO EE_ELEMENT_TYPE*)(PAGE_ADDRESS(ubCurrentActivePage) + varidx));
1308 
1309       /* Check if element is valid */
1310       if (addressvalue != EE_PAGESTAT_ERASED)
1311       {
1312         /* Update global variables accordingly */
1313         uhNbWrittenElements++;
1314         uwAddressNextWrite += EE_ELEMENT_SIZE;
1315       }
1316       else
1317       {
1318         break;
1319       }
1320     }
1321 
1322     /* Count number of elements already transferred in previous valid pages */
1323     page = ubCurrentActivePage;
1324     for (varidx = 0U; varidx < PAGES_NUMBER; varidx++)
1325     {
1326       /* Decrement page index among circular pages list */
1327       page = PREVIOUS_PAGE(page);
1328       pagestatus = GetPageState(PAGE_ADDRESS(page));
1329 
1330       /* Check if page is valid state */
1331       if (pagestatus == STATE_PAGE_VALID)
1332       {
1333         /* Update uhNbWrittenElements with number of elements in page */
1334         uhNbWrittenElements += NB_MAX_ELEMENTS_BY_PAGE;
1335       }
1336       else
1337       {
1338         break;
1339       }
1340     }
1341   }
1342 
1343   /* Write the variable passed as parameter in the new active page */
1344   /* If program operation was failed, a Flash error code is returned */
1345   if (VerifyPagesFullWriteVariable(VirtAddress, Data) != EE_OK)
1346   {
1347     return EE_WRITE_ERROR;
1348   }
1349 
1350 #if defined(RECOVERY_TEST)
1351   if (Type == EE_TRANSFER_NORMAL)
1352   {
1353     VerifyStateReset(5U);
1354   }
1355 #endif
1356 
1357   /* Transfer process: transfer variables from old to the new active page */
1358   /* First element in receive page can be any one, the following elements are */
1359   /* ordered from the beginning. */
1360   /* In case of recovery, Pre-Last element in receive page could be */
1361   /* corrupted if reset occured during write of this element, */
1362   /* and last element is dummy value that we have just written. */
1363   /* Transfer shall then resume from (uhNbWrittenElements-3) variable index */
1364   for (varidx = (uhNbWrittenElements >= 3U?uhNbWrittenElements-3U:0U); varidx < NB_OF_VARIABLES; varidx++)
1365   {
1366     /* Check each variable except the one passed as parameter */
1367     if (puhVirtAdd[varidx] != VirtAddress)
1368     {
1369       /* Read the last variable updates */
1370       status = ReadVariable(puhVirtAdd[varidx], &DataValue);
1371       if (status == EE_OK)
1372       {
1373         /* In case variable corresponding to the virtual address was found */
1374         /* Transfer the variable to the new active page */
1375         /* If program operation was failed, a Flash error code is returned */
1376         status = VerifyPagesFullWriteVariable(puhVirtAdd[varidx], DataValue);
1377         if (status != EE_OK)
1378         {
1379           return status;
1380         }
1381       }
1382       else
1383       {
1384         if (status != EE_NO_DATA)
1385         {
1386           /* In case variable is not found , do nothing */
1387           /* Any other status is error code occurs during variable read */
1388           return status;
1389         }
1390       }
1391     }
1392   }
1393 
1394 #if defined(RECOVERY_TEST)
1395   if (Type == EE_TRANSFER_NORMAL)
1396   {
1397     VerifyStateReset(6U);
1398   }
1399 #endif
1400 
1401   /* Transfer is now done, mark the receive state page as active */
1402   if (SetPageState(ubCurrentActivePage, STATE_PAGE_ACTIVE) != EE_OK)
1403   {
1404     return EE_WRITE_ERROR;
1405   }
1406 
1407 #if defined(RECOVERY_TEST)
1408   if (Type == EE_TRANSFER_NORMAL)
1409   {
1410     VerifyStateReset(7U);
1411   }
1412 #endif
1413 
1414   /* Return last operation flash status */
1415   return EE_CLEANUP_REQUIRED;
1416 }
1417 
1418 /**
1419   * @brief  Verify if pages are full
1420   *   then if not the case, writes variable in EEPROM.
1421   * @param  VirtAddress 16 bit virtual address of the variable
1422   * @param  Data @ref EE_DATA_TYPE data to be written as variable value
1423   * @retval EE_Status
1424   *           - EE_OK: on success
1425   *           - EE_FULL: if half pages are full
1426   *           - EE error code: if an error occurs
1427   */
VerifyPagesFullWriteVariable(uint16_t VirtAddress,EE_DATA_TYPE Data)1428 static EE_Status VerifyPagesFullWriteVariable(uint16_t VirtAddress, EE_DATA_TYPE Data)
1429 {
1430   uint32_t crc = 0U;
1431 
1432   /* Check if pages are full, i.e. max number of written elements achieved */
1433   if (uhNbWrittenElements >= NB_MAX_WRITTEN_ELEMENTS)
1434   {
1435     return EE_PAGE_FULL;
1436   }
1437 
1438   /* Get active Page for write operation */
1439   uint32_t activepage = FindPage(FIND_WRITE_PAGE);
1440   uint32_t activepageaddress = 0U;
1441 
1442   /* Check if there is no active page */
1443   if (activepage == EE_NO_PAGE_FOUND)
1444   {
1445     return EE_ERROR_NOACTIVE_PAGE;
1446   }
1447 
1448   activepageaddress = PAGE_ADDRESS(activepage);
1449 
1450   /* Force crc to 0 in case of Data/VirtAddress are 0*/
1451   if ((Data == 0U) && (VirtAddress == 0U))
1452   {
1453     crc = 0U;
1454   }
1455   else
1456   {
1457     /* Calculate crc of variable data and virtual address */
1458     crc = CalculateCrc(Data, VirtAddress);
1459   }
1460 
1461   /* Program variable data + virtual address + crc */
1462   /* If program operation was failed, a Flash error code is returned */
1463   if (EE_FLASH_PROGRAM(activepageaddress+uwAddressNextWrite, EE_ELEMENT_VALUE(VirtAddress,Data,crc)) != HAL_OK)
1464   {
1465     return EE_WRITE_ERROR;
1466   }
1467 
1468 #if defined(RECOVERY_TEST)
1469   uint32_t index = 0U;
1470 
1471   /* Write variable values in backup registers 0 to 30 */
1472   for (index = 0U; index < 31U; index++)
1473   {
1474     if (puhVirtAdd[index] == VirtAddress)
1475     {
1476       LL_RTC_BAK_SetRegister(RTC, index, Data);
1477     }
1478   }
1479 #endif
1480 
1481   /* Increment global variables relative to write operation done*/
1482   uwAddressNextWrite += EE_ELEMENT_SIZE;
1483   uhNbWrittenElements++;
1484 
1485   return EE_OK;
1486 }
1487 
1488 /**
1489   * @brief  Set page state in page header
1490   * @param  Page Index of the page
1491   * @param  State State of the page
1492   * @retval EE_Status
1493   *           - EE_OK: on success
1494   *           - EE error code: if an error occurs
1495   */
SetPageState(uint32_t Page,EE_State_type State)1496 static EE_Status SetPageState(uint32_t Page, EE_State_type State)
1497 {
1498   uint32_t header1 = 0U, header2 = 0U, header3 = 0U, header4 = 0U;
1499 
1500   header1 = PAGE_ADDRESS(Page);
1501   header2 = PAGE_ADDRESS(Page) + EE_ELEMENT_SIZE;
1502   header3 = PAGE_ADDRESS(Page) + (EE_ELEMENT_SIZE*2U);
1503   header4 = PAGE_ADDRESS(Page) + (EE_ELEMENT_SIZE*3U);
1504 
1505   switch(State)
1506   {
1507   case STATE_PAGE_RECEIVE:
1508     {
1509       /* Set new Page status to STATE_PAGE_RECEIVE status */
1510       if (EE_FLASH_PROGRAM(header1, EE_PAGESTAT_RECEIVE) != HAL_OK)
1511       {
1512         return EE_WRITE_ERROR;
1513       }
1514       ubCurrentActivePage = Page;
1515     }
1516     break;
1517   case STATE_PAGE_ACTIVE:
1518     {
1519       /* Set new Page status to STATE_PAGE_ACTIVE status */
1520       if (EE_FLASH_PROGRAM(header2, EE_PAGESTAT_ACTIVE) != HAL_OK)
1521       {
1522         return EE_WRITE_ERROR;
1523       }
1524       ubCurrentActivePage = Page;
1525     }
1526     break;
1527   case STATE_PAGE_VALID:
1528     {
1529       /* Set new Page status to STATE_PAGE_VALID status */
1530       if (EE_FLASH_PROGRAM(header3, EE_PAGESTAT_VALID) != HAL_OK)
1531       {
1532         return EE_WRITE_ERROR;
1533       }
1534     }
1535     break;
1536   case STATE_PAGE_ERASING:
1537     {
1538       /* Set new Page status to STATE_PAGE_ERASING status */
1539       if (EE_FLASH_PROGRAM(header4, EE_PAGESTAT_ERASING) != HAL_OK)
1540       {
1541         return EE_WRITE_ERROR;
1542       }
1543     }
1544     break;
1545   default:
1546     break;
1547   }
1548 
1549   /* Return last operation flash status */
1550   return EE_OK;
1551 }
1552 
1553 /**
1554   * @brief  Get page state in page header
1555   * @param  Address Address of the FLASH Memory page
1556   * @retval State State of the page
1557   */
GetPageState(uint32_t Address)1558 static EE_State_type GetPageState(uint32_t Address)
1559 {
1560   EE_ELEMENT_TYPE status1 = 0U, status2 = 0U, status3 = 0U, status4 = 0U;
1561 
1562   /* Get page state information from page header (3 first elements) */
1563   status1 = (*(__IO EE_ELEMENT_TYPE*)Address);
1564   status2 = (*(__IO EE_ELEMENT_TYPE*)(Address + EE_ELEMENT_SIZE));
1565   status3 = (*(__IO EE_ELEMENT_TYPE*)(Address + (EE_ELEMENT_SIZE*2U)));
1566   status4 = (*(__IO EE_ELEMENT_TYPE*)(Address + (EE_ELEMENT_SIZE*3U)));
1567 
1568   /* Return erasing status, if element4 is not EE_PAGESTAT_ERASED value */
1569   if (status4 != EE_PAGESTAT_ERASED)
1570   {
1571     return STATE_PAGE_ERASING;
1572   }
1573 
1574   /* Return valid status, if element3 is not EE_PAGESTAT_ERASED value */
1575   if (status3 != EE_PAGESTAT_ERASED)
1576   {
1577     return STATE_PAGE_VALID;
1578   }
1579 
1580   /* Return active status, if element2 is not EE_PAGESTAT_ERASED value */
1581   if (status2 != EE_PAGESTAT_ERASED)
1582   {
1583     return STATE_PAGE_ACTIVE;
1584   }
1585 
1586   /* Return receive status, if element1 is not EE_PAGESTAT_ERASED value */
1587   if (status1 != EE_PAGESTAT_ERASED)
1588   {
1589     return STATE_PAGE_RECEIVE;
1590   }
1591 
1592   /* Return erased status, if 4 first elements are EE_PAGESTAT_ERASED value */
1593   return STATE_PAGE_ERASED;
1594 }
1595 
1596 #if defined(RECOVERY_TEST)
1597 /**
1598   * @brief  Check reset state in backup registers, then increment
1599   *         it and reset system if it fits trigger value
1600   * @param  TriggerState Value of state triggering system reset
1601   * @retval None
1602   */
VerifyStateReset(uint32_t TriggerState)1603 static void VerifyStateReset(uint32_t TriggerState)
1604 {
1605   uint32_t state = 0U;
1606 
1607   /* Read state in backup registers N�31 */
1608   state = LL_RTC_BAK_GetRegister(RTC, 31U);
1609 
1610   /* Trig System Reset, if state reach trigger state */
1611   if (state == TriggerState)
1612   {
1613     /* Increment state */
1614     state++;
1615 
1616     /* Save state in backup register N�31 */
1617     LL_RTC_BAK_SetRegister(RTC, 31U, state);
1618 
1619     /* System Reset */
1620     HAL_NVIC_SystemReset();
1621   }
1622 }
1623 #endif
1624 
1625 /**
1626   * @brief  This function configures CRC Instance.
1627   * @note   This function is used to :
1628   *         -1- Enable peripheral clock for CRC.
1629   *         -2- Configure CRC functional parameters.
1630   * @note   Peripheral configuration is minimal configuration from reset values.
1631   *         Thus, some useless LL unitary functions calls below are provided as
1632   *         commented examples - setting is default configuration from reset.
1633   * @param  None
1634   * @retval None
1635   */
ConfigureCrc(void)1636 void ConfigureCrc(void)
1637 {
1638   /* (1) Enable peripheral clock for CRC */
1639   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_CRC);
1640 
1641   /* (2) Configure CRC functional parameters */
1642 
1643   /* Configure CRC calculation unit with user defined polynomial */
1644   LL_CRC_SetPolynomialCoef(CRC, CRC_POLYNOMIAL_VALUE);
1645   LL_CRC_SetPolynomialSize(CRC, CRC_POLYNOMIAL_LENGTH);
1646 
1647   /* Initialize default CRC initial value */
1648   /* Reset value is LL_CRC_DEFAULT_CRC_INITVALUE */
1649   /* LL_CRC_SetInitialData(CRC, LL_CRC_DEFAULT_CRC_INITVALUE);*/
1650 
1651   /* Set input data inversion mode : No inversion*/
1652   /* Reset value is LL_CRC_INDATA_REVERSE_NONE */
1653   /* LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_NONE); */
1654 
1655   /* Set output data inversion mode : No inversion */
1656   /* Reset value is LL_CRC_OUTDATA_REVERSE_NONE */
1657   /* LL_CRC_SetOutputDataReverseMode(CRC, LL_CRC_OUTDATA_REVERSE_NONE); */
1658 }
1659 
1660 /**
1661   * @brief  This function performs CRC calculation on Data and Virtual Address.
1662   * @param  Data value of  the eeprom variable.
1663   * @param  VirtAddress address of the eeprom variable.
1664   * @retval 16-bit CRC value computed on Data and Virtual Address.
1665   */
CalculateCrc(EE_DATA_TYPE Data,uint16_t VirtAddress)1666 uint16_t CalculateCrc(EE_DATA_TYPE Data, uint16_t VirtAddress)
1667 {
1668   /* Reset CRC calculation unit */
1669   LL_CRC_ResetCRCCalculationUnit(CRC);
1670 
1671   /* Feed Data and Virtual Address */
1672   LL_CRC_FeedData32(CRC, Data);
1673   LL_CRC_FeedData16(CRC, VirtAddress);
1674 
1675   /* Return computed CRC value */
1676   return(LL_CRC_ReadData16(CRC));
1677 }
1678 
1679 /**
1680   * @}
1681   */
1682 
1683 /**
1684   * @}
1685   */
1686 
1687 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
1688