Snap for 9115583 from 36896d304ede2125dba1c94eeff9b01b1c882d36 to gki13-boot-release

Change-Id: Ie0d5a0e9633f4db88294ce685ef41113d75e45da
diff --git a/st21nfc/hal/hal_fd.cc b/st21nfc/hal/hal_fd.cc
index 2e8fab0..810aca3 100644
--- a/st21nfc/hal/hal_fd.cc
+++ b/st21nfc/hal/hal_fd.cc
@@ -55,6 +55,51 @@
 
 static const uint8_t ApduExitLoadMode[] = {0x2F, 0x04, 0x06, 0x80, 0xA0,
                                            0x00, 0x00, 0x01, 0x01};
+
+// APDUs for ST54L
+const int UK_NB = 2;
+const int UK_SIZE = 12;
+static uint8_t UserKeys[UK_NB][UK_SIZE] = {
+    {0x00, 0x00, 0xFD, 0x0F, 0x87, 0x7D, 0x31, 0xE3, 0xCF, 0x0C, 0xD3,
+     0x68},  // Test
+    {0x00, 0x00, 0xFD, 0x00, 0x87, 0x7D, 0x31, 0xE3, 0xCF, 0x0C, 0xD3,
+     0x68}};  // Production
+
+static uint8_t ApduPutKeyUser1[UK_NB][50] = {
+    {0x2F, 0x04, 0x2F, 0x84, 0x11, 0x00, 0x00, 0x2A, 0x01, 0xB3,
+     0x56, 0x01,  // Test
+     0x00, 0x00, 0xFD, 0x20, 0x20, 0xEC, 0x7D, 0x47, 0xAE, 0xF3,
+     0x23, 0x2E, 0x00, 0x00, 0x34, 0x78, 0x82, 0xEC, 0x6b, 0xA5,
+     0x83, 0xAF, 0x68, 0xC7, 0x1F, 0x9F, 0xB0, 0xD7, 0x9D, 0x33,
+     0xB0, 0xDA, 0xC6, 0x2C, 0xAB, 0x8A, 0x10, 0xEA},
+    {0x2F, 0x04, 0x2F, 0x84, 0x11, 0x00, 0x00, 0x2A, 0x01, 0xB3,
+     0x56, 0x01,  // Production
+     0x00, 0x00, 0xFD, 0x20, 0x20, 0xEC, 0x7D, 0x47, 0xAE, 0xF3,
+     0x23, 0x2E, 0x00, 0x00, 0xE1, 0xA2, 0x78, 0xA9, 0x71, 0x14,
+     0x46, 0x6D, 0x73, 0x86, 0x4C, 0x3B, 0x0F, 0x51, 0x71, 0x8E,
+     0xE4, 0x1D, 0x54, 0x02, 0x3A, 0xE3, 0x18, 0x55}};
+
+static uint8_t ApduEraseUpgradeStart[] = {
+    0x2F, 0x04, 0x12, 0x84, 0x35, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0xB2, 0x51, 0x42, 0xB0, 0x27, 0x92, 0xAA, 0xAB};
+
+static uint8_t ApduEraseNfcArea[] = {0x2F, 0x04, 0x17, 0x84, 0x36, 0x00, 0x00,
+                                     0x12, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00,
+                                     0x00, 0x04, 0x5E, 0x00, 0x4D, 0x83, 0xE1,
+                                     0x59, 0x62, 0xDC, 0x14, 0x64};
+
+static uint8_t ApduEraseUpgradeStop[] = {0x2F, 0x04, 0x0F, 0x80, 0x33, 0x00,
+                                         0x00, 0x0A, 0x00, 0x02, 0x97, 0x22,
+                                         0xC2, 0x5A, 0x2D, 0xA4, 0x09, 0x1A};
+
+static uint8_t ApduSetVariousConfig[] = {
+    0x2F, 0x04, 0x11, 0x84, 0x74, 0x00, 0x00, 0x0C, 0x06, 0x02,
+    0x80, 0x80, 0xD4, 0x29, 0xEC, 0x9A, 0xFB, 0xC8, 0x4B, 0x2A};
+
+static uint8_t ApduSwitchToUser[] = {0x2F, 0x04, 0x0F, 0x84, 0xA0, 0x00,
+                                     0x00, 0x0A, 0x20, 0x01, 0xFC, 0x63,
+                                     0x2A, 0xE1, 0xFD, 0xAA, 0xD1, 0x9B};
+
 static const uint8_t nciHeaderPropSetUwbConfig[9] = {
     0x2F, 0x02, 0x00, 0x04, 0x00, 0x16, 0x01, 0x00, 0x00};
 static const uint8_t nciGetPropConfig[8] = {0x2F, 0x02, 0x05, 0x03,
@@ -64,12 +109,44 @@
 static uint8_t nciPropSetUwbConfig[128];
 static uint8_t nciPropSetConfig_CustomField[64];
 hal_fd_state_e mHalFDState = HAL_FD_STATE_AUTHENTICATE;
+hal_fd_st54l_state_e mHalFD54LState = HAL_FD_ST54L_STATE_PUY_KEYUSER;
 void SendExitLoadMode(HALHANDLE mmHalHandle);
+void SendSwitchToUserMode(HALHANDLE mmHalHandle);
 extern void hal_wrapper_update_complete();
 
 typedef size_t (*STLoadUwbParams)(void *out_buff,
                                   size_t buf_size);
 
+/***********************************************************************
+ * Determine UserKey
+ *
+ * @return mode: -1 : not supported
+ *                0 : Test sample
+ *                1 : Product sample
+ ***********************************************************************/
+static int GetProdType(uint8_t* UserKey) {
+  int i, j;
+  int status;
+
+  for (i = 0; i < UK_NB; i++) {
+    status = 1;
+    for (j = 0; j < UK_SIZE; j++) {
+      if (UserKey[j] != UserKeys[i][j]) {
+        STLOG_HAL_D(
+            "   No match between UserKey[%d]=0x%02X and \
+                UserKeys[%d][%d]=0x%02X",
+            j, UserKey[j], i, j, UserKeys[i][j]);
+        status = 0;
+        break;
+      }
+    }
+    if (1 == status) {
+      return i;
+    }
+  }
+  return (-1);
+}
+
 /**
  * Send a HW reset and decode NCI_CORE_RESET_NTF information
  * @param pHwVersion is used to return HW version, part of NCI_CORE_RESET_NTF
@@ -90,6 +167,7 @@
   char ConfPath[256];
   char fwBinName[256];
   char fwConfName[256];
+  int ret;
 
   STLOG_HAL_D("  %s - enter", __func__);
 
@@ -142,19 +220,56 @@
     STLOG_HAL_D("%s - %s not detected", __func__, fwBinName);
   } else {
     STLOG_HAL_D("%s - %s file detected\n", __func__, fwBinName);
-
     result |= FW_PATCH_AVAILABLE;
-    fread(mBinData, sizeof(uint8_t), 4, mFwFileBin);
-    mFWInfo->patchVersion =
+
+    ret = fread(mBinData, sizeof(uint8_t), 4, mFwFileBin);
+    if (ret != 4) {
+      STLOG_HAL_E("%s did not read 4 bytes \n", __func__);
+    }
+    mFWInfo->fileFwVersion =
         mBinData[0] << 24 | mBinData[1] << 16 | mBinData[2] << 8 | mBinData[3];
 
-    fread(mApduAuthent, sizeof(uint8_t), 24, mFwFileBin);
-
     fgetpos(mFwFileBin, &mPosInit);
+    ret = fread(mBinData, sizeof(uint8_t), 5, mFwFileBin);
+    if (ret != 5) {
+      STLOG_HAL_E("%s did not read 5 bytes \n", __func__);
+    }
+    fsetpos(mFwFileBin, &mPosInit);  // reset pos in stream
 
-    STLOG_HAL_D(
-        "%s --> st21nfc_fw integrates patch NFC FW version 0x%08X (%s)\n",
-        __func__, mFWInfo->patchVersion, FwType);
+    if (mBinData[4] == 0x35) {
+      mFWInfo->fileHwVersion = HW_ST54L;
+    } else {
+      ret = fread(mApduAuthent, sizeof(uint8_t), 24, mFwFileBin);
+      if (ret != 24) {
+        STLOG_HAL_E("%s Wrong read nb \n", __func__);
+      }
+
+      // We use the last byte of the auth command to discriminate at the moment.
+      // it can be extended in case of conflict later.
+      switch (mApduAuthent[23]) {
+        case 0x43:
+        case 0xC7:
+          mFWInfo->fileHwVersion = HW_NFCD;
+          break;
+
+        case 0xE9:
+          mFWInfo->fileHwVersion = HW_ST54J;
+          break;
+      }
+    }
+
+    if (mFWInfo->fileHwVersion == 0) {
+      STLOG_HAL_E("%s --> %s integrates unknown patch NFC FW -- rejected\n",
+                  __func__, FwPath);
+      fclose(mFwFileBin);
+      mFwFileBin = NULL;
+    } else {
+      fgetpos(mFwFileBin, &mPosInit);
+
+      STLOG_HAL_D("%s --> %s integrates patch NFC FW version 0x%08X (r:%d)\n",
+                  __func__, FwPath, mFWInfo->fileFwVersion,
+                  mFWInfo->fileHwVersion);
+    }
   }
 
   if ((mCustomFileBin = fopen((char *)ConfPath, "r")) == NULL) {
@@ -162,9 +277,9 @@
   } else {
     STLOG_HAL_D("%s - %s file detected\n", __func__, ConfPath);
     fread(mBinData, sizeof(uint8_t), 2, mCustomFileBin);
-    mFWInfo->confVersion = mBinData[0] << 8 | mBinData[1];
+    mFWInfo->fileCustVersion = mBinData[0] << 8 | mBinData[1];
     STLOG_HAL_D("%s --> st21nfc_custom configuration version 0x%04X \n",
-                __func__, mFWInfo->confVersion);
+                __func__, mFWInfo->fileCustVersion);
     result |= FW_CUSTOM_PARAM_AVAILABLE;
   }
 
@@ -215,25 +330,26 @@
     STLOG_HAL_D("-> Router Mode NCI_CORE_RESET_NTF received after HW Reset");
 
     /* retrieve HW Version from NCI_CORE_RESET_NTF */
-    mFWInfo->hwVersion = pdata[8];
-    STLOG_HAL_D("   HwVersion = 0x%02X", mFWInfo->hwVersion);
+    mFWInfo->chipHwVersion = pdata[8];
+    STLOG_HAL_D("   HwVersion = 0x%02X", mFWInfo->chipHwVersion);
 
     /* retrieve FW Version from NCI_CORE_RESET_NTF */
-    mFWInfo->fwVersion =
+    mFWInfo->chipFwVersion =
         (pdata[10] << 24) | (pdata[11] << 16) | (pdata[12] << 8) | pdata[13];
-    STLOG_HAL_D("   FwVersion = 0x%08X", mFWInfo->fwVersion);
+    STLOG_HAL_D("   FwVersion = 0x%08X", mFWInfo->chipFwVersion);
 
     /* retrieve Loader Version from NCI_CORE_RESET_NTF */
-    mFWInfo->loaderVersion = (pdata[14] << 16) | (pdata[15] << 8) | pdata[16];
-    STLOG_HAL_D("   LoaderVersion = 0x%06X", mFWInfo->loaderVersion);
+    mFWInfo->chipLoaderVersion =
+        (pdata[14] << 16) | (pdata[15] << 8) | pdata[16];
+    STLOG_HAL_D("   LoaderVersion = 0x%06X", mFWInfo->chipLoaderVersion);
 
     /* retrieve Customer Version from NCI_CORE_RESET_NTF */
-    mFWInfo->custVersion = (pdata[31] << 8) | pdata[32];
-    STLOG_HAL_D("   CustomerVersion = 0x%04X", mFWInfo->custVersion);
+    mFWInfo->chipCustVersion = (pdata[31] << 8) | pdata[32];
+    STLOG_HAL_D("   CustomerVersion = 0x%04X", mFWInfo->chipCustVersion);
 
     /* retrieve Uwb param Version from NCI_CORE_RESET_NTF */
-    mFWInfo->uwbFwVersion = (pdata[29] << 8) | pdata[30];
-    STLOG_HAL_D("   uwbVersion = 0x%04X", mFWInfo->uwbFwVersion);
+    mFWInfo->chipUwbVersion = (pdata[29] << 8) | pdata[30];
+    STLOG_HAL_D("   uwbVersion = 0x%04X", mFWInfo->chipUwbVersion);
 
     *clf_mode = FT_CLF_MODE_ROUTER;
   } else if ((pdata[2] == 0x39) && (pdata[3] == 0xA1)) {
@@ -241,32 +357,43 @@
 
     /* deduce HW Version from Factory Loader version */
     if (pdata[16] == 0x01) {
-      mFWInfo->hwVersion = 0x05;  // ST54J
+      mFWInfo->chipHwVersion = 0x05;  // ST54J
     } else if (pdata[16] == 0x02) {
-      mFWInfo->hwVersion = 0x04;  // ST21NFCD
+      mFWInfo->chipHwVersion = 0x04;  // ST21NFCD
     } else {
-      mFWInfo->hwVersion = 0x03;  // ST21NFCD
+      mFWInfo->chipHwVersion = 0x03;  // ST21NFCD
     }
-    STLOG_HAL_D("   HwVersion = 0x%02X", mFWInfo->hwVersion);
+    STLOG_HAL_D("   HwVersion = 0x%02X", mFWInfo->chipHwVersion);
 
     /* Identify the Active loader. Normally only one should be detected*/
     if (pdata[11] == 0xA0) {
-      mFWInfo->loaderVersion = (pdata[8] << 16) | (pdata[9] << 8) | pdata[10];
+      mFWInfo->chipLoaderVersion =
+          (pdata[8] << 16) | (pdata[9] << 8) | pdata[10];
       STLOG_HAL_D("         - Most recent loader activated, revision 0x%06X",
-                  mFWInfo->loaderVersion);
+                  mFWInfo->chipLoaderVersion);
     }
     if (pdata[15] == 0xA0) {
-      mFWInfo->loaderVersion = (pdata[12] << 16) | (pdata[13] << 8) | pdata[14];
+      mFWInfo->chipLoaderVersion =
+          (pdata[12] << 16) | (pdata[13] << 8) | pdata[14];
       STLOG_HAL_D("         - Least recent loader activated, revision 0x%06X",
-                  mFWInfo->loaderVersion);
+                  mFWInfo->chipLoaderVersion);
     }
     if (pdata[19] == 0xA0) {
-      mFWInfo->loaderVersion = (pdata[16] << 16) | (pdata[17] << 8) | pdata[18];
+      mFWInfo->chipLoaderVersion =
+          (pdata[16] << 16) | (pdata[17] << 8) | pdata[18];
       STLOG_HAL_D("         - Factory loader activated, revision 0x%06X",
-                  mFWInfo->loaderVersion);
+                  mFWInfo->chipLoaderVersion);
     }
 
     *clf_mode = FT_CLF_MODE_LOADER;
+  } else if ((pdata[2] == 0x41) && (pdata[3] == 0xA2)) {
+    STLOG_HAL_D("-> Loader V3 Mode NCI_CORE_RESET_NTF received after HW Reset");
+    mFWInfo->chipHwVersion = HW_ST54L;
+    STLOG_HAL_D("   HwVersion = 0x%02X", mFWInfo->chipHwVersion);
+    mFWInfo->chipFwVersion = 0;  // make sure FW will be updated.
+    /* retrieve Production type* from NCI_CORE_RESET_NTF */
+    mFWInfo->chipProdType = GetProdType(&pdata[44]);
+    *clf_mode = FT_CLF_MODE_LOADER;
   } else {
     STLOG_HAL_E(
         "%s --> ERROR: wrong NCI_CORE_RESET_NTF received after HW Reset",
@@ -274,17 +401,18 @@
     *clf_mode = FT_CLF_MODE_ERROR;
   }
 
-  // Allow update only for ST54J
-  if (mFWInfo->hwVersion == 0x05) {
-    if ((mFwFileBin != NULL) && (mFWInfo->patchVersion != mFWInfo->fwVersion)) {
+  if ((mFWInfo->chipHwVersion == HW_ST54J) ||
+      (mFWInfo->chipHwVersion == HW_ST54L)) {
+    if ((mFwFileBin != NULL) &&
+        (mFWInfo->fileFwVersion != mFWInfo->chipFwVersion)) {
       STLOG_HAL_D("---> Firmware update needed\n");
       result |= FW_UPDATE_NEEDED;
     } else {
       STLOG_HAL_D("---> No Firmware update needed\n");
     }
 
-    if ((mFWInfo->confVersion != 0) &&
-        (mFWInfo->custVersion != mFWInfo->confVersion)) {
+    if ((mFWInfo->fileCustVersion != 0) &&
+        (mFWInfo->chipCustVersion != mFWInfo->fileCustVersion)) {
       STLOG_HAL_D(
           "%s - Need to apply new st21nfc custom configuration settings\n",
           __func__);
@@ -294,8 +422,8 @@
                   __func__);
     }
   }
-  if ((mFWInfo->uwbVersion != 0) &&
-      (mFWInfo->uwbVersion != mFWInfo->uwbFwVersion)) {
+  if ((mFWInfo->fileUwbVersion != 0) &&
+      (mFWInfo->fileUwbVersion != mFWInfo->chipUwbVersion)) {
     result |= UWB_CONF_UPDATE_NEEDED;
     STLOG_HAL_D("%s - Need to apply new uwb param configuration \n", __func__);
     mUwbConfigNeeded = true;
@@ -401,10 +529,10 @@
       memcpy(nciPropSetUwbConfig, nciHeaderPropSetUwbConfig, 9);
       nciPropSetUwbConfig[2] = lengthOutput + 6;
       nciPropSetUwbConfig[8] = lengthOutput;
-      mFWInfo->uwbVersion =
+      mFWInfo->fileUwbVersion =
           nciPropSetUwbConfig[9] << 8 | nciPropSetUwbConfig[10];
       STLOG_HAL_D("%s --> uwb configuration version 0x%04X \n", __func__,
-                  mFWInfo->uwbVersion);
+                  mFWInfo->fileUwbVersion);
       return true;
     } else {
       STLOG_HAL_D("%s: lengthOutput null", __func__);
@@ -415,11 +543,34 @@
   }
   return false;
 }
+/*******************************************************************************
+**
+** Function         resetHandlerState
+**
+** Description      Reset FW update state.
+**
+** Parameters       void
+**
+**
+*******************************************************************************/
 void resetHandlerState() {
   STLOG_HAL_D("%s", __func__);
   mHalFDState = HAL_FD_STATE_AUTHENTICATE;
+  mHalFD54LState = HAL_FD_ST54L_STATE_PUY_KEYUSER;
 }
 
+/*******************************************************************************
+**
+** Function         UpdateHandler
+**
+** Description      Handler to update ST54J NFCC FW.
+**
+** Parameters       mHalHandle - HAL handle
+**                  data_len   - Buffer length
+**                  p_data     - Data buffer from NFCC
+**
+**
+*******************************************************************************/
 void UpdateHandler(HALHANDLE mHalHandle, uint16_t data_len, uint8_t *p_data) {
   HalSendDownstreamStopTimer(mHalHandle);
 
@@ -512,8 +663,9 @@
       STLOG_HAL_D("%s - mHalFDState = HAL_FD_STATE_EXIT_APDU", __func__);
       if ((p_data[data_len - 2] != 0x90) || (p_data[data_len - 1] != 0x00)) {
         STLOG_HAL_D(
-            "%s - Error exiting loader mode, i.e. a problem occured during FW "
-            "update", __func__);
+            "%s - Error exiting loader mode, i.e. a problem occurred"
+            "during FW update",
+            __func__);
       }
 
       I2cResetPulse();
@@ -529,6 +681,176 @@
   }
 }
 
+/*******************************************************************************
+**
+** Function         UpdateHandlerST54L
+**
+** Description      Handler to update ST54L NFCC FW.
+**
+** Parameters       mHalHandle - HAL handle
+**                  data_len   - Buffer length
+**                  p_data     - Data buffer from NFCC
+**
+**
+*******************************************************************************/
+static void UpdateHandlerST54L(HALHANDLE mHalHandle, uint16_t data_len,
+                               uint8_t* p_data) {
+  STLOG_HAL_D("%s : Enter state = %d", __func__, mHalFD54LState);
+
+  switch (mHalFD54LState) {
+    case HAL_FD_ST54L_STATE_PUY_KEYUSER:
+      if (!HalSendDownstreamTimer(
+              mHalHandle, (uint8_t*)ApduPutKeyUser1[mFWInfo->chipProdType],
+              sizeof(ApduPutKeyUser1[mFWInfo->chipProdType]),
+              FW_TIMER_DURATION)) {
+        STLOG_HAL_E("%s - SendDownstream failed", __func__);
+      }
+      mHalFD54LState = HAL_FD_ST54L_STATE_ERASE_UPGRADE_START;
+      break;
+
+    case HAL_FD_ST54L_STATE_ERASE_UPGRADE_START:
+      if ((p_data[data_len - 2] == 0x90) && (p_data[data_len - 1] == 0x00)) {
+        if (!HalSendDownstreamTimer(mHalHandle, (uint8_t*)ApduEraseUpgradeStart,
+                                    sizeof(ApduEraseUpgradeStart),
+                                    FW_TIMER_DURATION)) {
+          STLOG_HAL_E("%s - SendDownstream failed", __func__);
+        }
+        mHalFD54LState = HAL_FD_ST54L_STATE_ERASE_NFC_AREA;
+      } else {
+        STLOG_HAL_D("%s - FW flash not succeeded", __func__);
+        SendSwitchToUserMode(mHalHandle);
+      }
+      break;
+
+    case HAL_FD_ST54L_STATE_ERASE_NFC_AREA:
+      if ((p_data[data_len - 2] == 0x90) && (p_data[data_len - 1] == 0x00)) {
+        if (!HalSendDownstreamTimer(mHalHandle, (uint8_t*)ApduEraseNfcArea,
+                                    sizeof(ApduEraseNfcArea),
+                                    FW_TIMER_DURATION)) {
+          STLOG_HAL_E("%s - SendDownstream failed", __func__);
+        }
+        mHalFD54LState = HAL_FD_ST54L_STATE_ERASE_UPGRADE_STOP;
+      } else {
+        STLOG_HAL_D("%s - FW flash not succeeded", __func__);
+        SendSwitchToUserMode(mHalHandle);
+      }
+      break;
+
+    case HAL_FD_ST54L_STATE_ERASE_UPGRADE_STOP:
+      if ((p_data[data_len - 2] == 0x90) && (p_data[data_len - 1] == 0x00)) {
+        if (!HalSendDownstreamTimer(mHalHandle, (uint8_t*)ApduEraseUpgradeStop,
+                                    sizeof(ApduEraseUpgradeStop),
+                                    FW_TIMER_DURATION)) {
+          STLOG_HAL_E("%s - SendDownstream failed", __func__);
+        }
+        mHalFD54LState = HAL_FD_ST54L_STATE_SEND_RAW_APDU;
+      } else {
+        STLOG_HAL_D("%s - FW flash not succeeded", __func__);
+        SendSwitchToUserMode(mHalHandle);
+      }
+      break;
+
+    case HAL_FD_ST54L_STATE_SEND_RAW_APDU:
+      STLOG_HAL_D("%s - mHalFDState = HAL_FD_ST54L_STATE_SEND_RAW_APDU",
+                  __func__);
+      if ((p_data[0] == 0x4f) && (p_data[1] == 0x04)) {
+        if ((p_data[data_len - 2] == 0x90) && (p_data[data_len - 1] == 0x00)) {
+          mRetry = true;
+
+          fgetpos(mFwFileBin, &mPos);  // save current position in stream
+          if ((fread(mBinData, sizeof(uint8_t), 3, mFwFileBin) == 3) &&
+              (fread(mBinData + 3, sizeof(uint8_t), mBinData[2], mFwFileBin) ==
+               mBinData[2])) {
+            if (!HalSendDownstreamTimer(mHalHandle, mBinData, mBinData[2] + 3,
+                                        FW_TIMER_DURATION)) {
+              STLOG_HAL_E("%s - SendDownstream failed", __func__);
+            }
+          } else {
+            STLOG_HAL_D("%s - EOF of FW binary", __func__);
+            if (!HalSendDownstreamTimer(
+                    mHalHandle, (uint8_t*)ApduSetVariousConfig,
+                    sizeof(ApduSetVariousConfig), FW_TIMER_DURATION)) {
+              STLOG_HAL_E("%s - SendDownstream failed", __func__);
+            }
+            mHalFD54LState = HAL_FD_ST54L_STATE_SET_CONFIG;
+          }
+        } else if (mRetry == true) {
+          STLOG_HAL_D("%s - Last Tx was NOK. Retry", __func__);
+          mRetry = false;
+          fsetpos(mFwFileBin, &mPos);
+          if ((fread(mBinData, sizeof(uint8_t), 3, mFwFileBin) == 3) &&
+              (fread(mBinData + 3, sizeof(uint8_t), mBinData[2], mFwFileBin) ==
+               mBinData[2])) {
+            if (!HalSendDownstreamTimer(mHalHandle, mBinData, mBinData[2] + 3,
+                                        FW_TIMER_DURATION)) {
+              STLOG_HAL_E("%s - SendDownstream failed", __func__);
+            }
+            fgetpos(mFwFileBin, &mPos);  // save current position in stream
+          } else {
+            STLOG_HAL_D("%s - EOF of FW binary", __func__);
+            if (!HalSendDownstreamTimer(
+                    mHalHandle, (uint8_t*)ApduSetVariousConfig,
+                    sizeof(ApduSetVariousConfig), FW_TIMER_DURATION)) {
+              STLOG_HAL_E("%s - SendDownstream failed", __func__);
+            }
+            mHalFD54LState = HAL_FD_ST54L_STATE_SET_CONFIG;
+          }
+        } else {
+          STLOG_HAL_D("%s - FW flash not succeeded.", __func__);
+          I2cResetPulse();
+          SendSwitchToUserMode(mHalHandle);
+        }
+      }
+      break;
+
+    case HAL_FD_ST54L_STATE_SET_CONFIG:
+
+      if ((p_data[0] == 0x4f) && (p_data[1] == 0x04)) {
+        SendSwitchToUserMode(mHalHandle);
+      }
+      break;
+
+    case HAL_FD_ST54L_STATE_SWITCH_TO_USER:
+      if ((p_data[data_len - 2] != 0x90) || (p_data[data_len - 1] != 0x00)) {
+        STLOG_HAL_D(
+            "%s - Error exiting loader mode, i.e. a problem occurred during FW "
+            "update",
+            __func__);
+      }
+
+      I2cResetPulse();
+      hal_wrapper_set_state(HAL_WRAPPER_STATE_OPEN);
+      mHalFD54LState = HAL_FD_ST54L_STATE_PUY_KEYUSER;
+      break;
+
+    default:
+      STLOG_HAL_D("%s - mHalFD54LState = unknown", __func__);
+      STLOG_HAL_D("%s - FW flash not succeeded", __func__);
+      SendSwitchToUserMode(mHalHandle);
+      break;
+  }
+}
+
+/*******************************************************************************
+**
+** Function         FwUpdateHandler
+**
+** Description      Handler to update NFCC FW.
+**
+** Parameters       mHalHandle - HAL handle
+**                  data_len   - Buffer length
+**                  p_data     - Data buffer from NFCC
+**
+**
+*******************************************************************************/
+void FwUpdateHandler(HALHANDLE mHalHandle, uint16_t data_len, uint8_t* p_data) {
+  if (mFWInfo->chipHwVersion == HW_ST54L) {
+    UpdateHandlerST54L(mHalHandle, data_len, p_data);
+  } else {
+    UpdateHandler(mHalHandle, data_len, p_data);
+  }
+}
+
 void ApplyCustomParamHandler(HALHANDLE mHalHandle, uint16_t data_len,
                              uint8_t *p_data) {
   STLOG_HAL_D("%s - Enter ", __func__);
@@ -595,8 +917,8 @@
             nciPropSetConfig_CustomField[8] = p_data[6];
             nciPropSetConfig_CustomField[2] = p_data[6] + 6;
             memcpy(nciPropSetConfig_CustomField + 9, p_data + 7, p_data[6]);
-            nciPropSetConfig_CustomField[13] = mFWInfo->uwbFwVersion >> 8;
-            nciPropSetConfig_CustomField[14] = mFWInfo->uwbFwVersion;
+            nciPropSetConfig_CustomField[13] = mFWInfo->chipUwbVersion >> 8;
+            nciPropSetConfig_CustomField[14] = mFWInfo->chipUwbVersion;
 
             if (!HalSendDownstream(mHalHandle, nciPropSetConfig_CustomField,
                                    nciPropSetConfig_CustomField[2] + 3)) {
@@ -697,8 +1019,8 @@
           nciPropSetConfig_CustomField[8] = p_data[6];
           nciPropSetConfig_CustomField[2] = p_data[6] + 6;
           memcpy(nciPropSetConfig_CustomField + 9, p_data + 7, p_data[6]);
-          nciPropSetConfig_CustomField[13] = mFWInfo->uwbVersion >> 8;
-          nciPropSetConfig_CustomField[14] = mFWInfo->uwbVersion;
+          nciPropSetConfig_CustomField[13] = mFWInfo->fileUwbVersion >> 8;
+          nciPropSetConfig_CustomField[14] = mFWInfo->fileUwbVersion;
 
           if (!HalSendDownstream(mHalHandle, nciPropSetConfig_CustomField,
                                  nciPropSetConfig_CustomField[2] + 3)) {
@@ -739,3 +1061,13 @@
   }
   mHalFDState = HAL_FD_STATE_EXIT_APDU;
 }
+
+void SendSwitchToUserMode(HALHANDLE mmHalHandle) {
+  STLOG_HAL_D("%s: enter", __func__);
+
+  if (!HalSendDownstreamTimer(mmHalHandle, ApduSwitchToUser,
+                              sizeof(ApduSwitchToUser), FW_TIMER_DURATION)) {
+    STLOG_HAL_E("%s - SendDownstream failed", __func__);
+  }
+  mHalFD54LState = HAL_FD_ST54L_STATE_SWITCH_TO_USER;
+}
diff --git a/st21nfc/hal/hal_fd.h b/st21nfc/hal/hal_fd.h
index 3ee42e9..44cde09 100644
--- a/st21nfc/hal/hal_fd.h
+++ b/st21nfc/hal/hal_fd.h
@@ -28,14 +28,18 @@
  */
 typedef struct FWInfo {
   uint32_t patchVersion;
-  uint32_t fwVersion;
-  uint8_t hwVersion;
-  uint32_t loaderVersion;
-  uint16_t custVersion;
-  uint16_t confVersion;
-  uint16_t uwbFwVersion;
-  uint16_t uwbVersion;
+  uint32_t chipFwVersion;
+  uint8_t chipHwVersion;
+  uint32_t chipLoaderVersion;
+  uint16_t chipCustVersion;
+  uint16_t chipUwbVersion;
   bool hibernate_exited;
+
+  uint16_t fileUwbVersion;
+  uint8_t fileHwVersion;  // if 0, no FW patch available.
+  uint32_t fileFwVersion;
+  uint16_t fileCustVersion;  // if 0, no custom params available.
+  uint8_t chipProdType;
 } FWInfo;
 
 typedef enum {
@@ -46,6 +50,16 @@
   HAL_FD_STATE_EXIT_APDU,
 } hal_fd_state_e;
 
+typedef enum {
+  HAL_FD_ST54L_STATE_PUY_KEYUSER,
+  HAL_FD_ST54L_STATE_ERASE_UPGRADE_START,
+  HAL_FD_ST54L_STATE_ERASE_NFC_AREA,
+  HAL_FD_ST54L_STATE_ERASE_UPGRADE_STOP,
+  HAL_FD_ST54L_STATE_SEND_RAW_APDU,
+  HAL_FD_ST54L_STATE_SET_CONFIG,
+  HAL_FD_ST54L_STATE_SWITCH_TO_USER,
+} hal_fd_st54l_state_e;
+
 #define FT_CLF_MODE_ERROR 0
 #define FT_CLF_MODE_LOADER 1
 #define FT_CLF_MODE_ROUTER 2
@@ -62,13 +76,18 @@
 
 #define MAX_BUFFER_SIZE 300
 
+// HwVersion :
+#define HW_NFCD 0x04
+#define HW_ST54J 0x05
+#define HW_ST54L 0x06
+
 /* Function declarations */
 int hal_fd_init();
 void hal_fd_close();
 uint8_t ft_cmd_HwReset(uint8_t* pdata, uint8_t* clf_mode);
 void ExitHibernateHandler(HALHANDLE mHalHandle, uint16_t data_len,
                           uint8_t* p_data);
-void UpdateHandler(HALHANDLE mHalHandle, uint16_t data_len, uint8_t* p_data);
+void FwUpdateHandler(HALHANDLE mHalHandle, uint16_t data_len, uint8_t* p_data);
 void ApplyCustomParamHandler(HALHANDLE mHalHandle, uint16_t data_len,
                              uint8_t* p_data);
 void ApplyUwbParamHandler(HALHANDLE mHalHandle, uint16_t data_len,
diff --git a/st21nfc/hal_wrapper.cc b/st21nfc/hal_wrapper.cc
index 5c97179..2b5ecc7 100644
--- a/st21nfc/hal_wrapper.cc
+++ b/st21nfc/hal_wrapper.cc
@@ -240,12 +240,17 @@
             mHalWrapperCallback(HAL_NFC_OPEN_CPLT_EVT, HAL_NFC_STATUS_FAILED);
             I2cCloseLayer();
           } else {
-            STLOG_HAL_V("%s - Send APDU_GET_ATR_CMD", __func__);
-            mRetryFwDwl--;
-            if (!HalSendDownstreamTimer(mHalHandle, ApduGetAtr,
-                                        sizeof(ApduGetAtr),
-                                        FW_TIMER_DURATION)) {
-              STLOG_HAL_E("%s - SendDownstream failed", __func__);
+            if (((p_data[3] == 0x01) && (p_data[8] == HW_ST54L)) ||
+                ((p_data[2] == 0x41) && (p_data[3] == 0xA2))) {  // ST54L
+              FwUpdateHandler(mHalHandle, data_len, p_data);
+            } else {
+              STLOG_HAL_V("%s - Send APDU_GET_ATR_CMD", __func__);
+              mRetryFwDwl--;
+              if (!HalSendDownstreamTimer(mHalHandle, ApduGetAtr,
+                                          sizeof(ApduGetAtr),
+                                          FW_TIMER_DURATION)) {
+                STLOG_HAL_E("%s - SendDownstream failed", __func__);
+              }
             }
             mHalWrapperState = HAL_WRAPPER_STATE_UPDATE;
           }
@@ -556,7 +561,7 @@
 
     case HAL_WRAPPER_STATE_UPDATE:  // 7
       STLOG_HAL_V("%s - mHalWrapperState = HAL_WRAPPER_STATE_UPDATE", __func__);
-      UpdateHandler(mHalHandle, data_len, p_data);
+      FwUpdateHandler(mHalHandle, data_len, p_data);
       break;
     case HAL_WRAPPER_STATE_APPLY_CUSTOM_PARAM:  // 8
       STLOG_HAL_V(