/* USER CODE BEGIN Header */
/**
 ******************************************************************************
  * @file    user_diskio.c
  * @brief   This file includes a diskio driver skeleton to be completed by the user.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
 /* USER CODE END Header */

#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
 * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
 * To be suppressed in the future.
 * Kept to ensure backward compatibility with previous CubeMx versions when
 * migrating projects.
 * User code previously added there should be copied in the new user sections before
 * the section contents can be deleted.
 */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif

/* USER CODE BEGIN DECL */

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>

#include "ff_gen_drv.h"

#include "eeprom_diskio.h"
#include "eeprom_conf.h"
#include "m24256e.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;

uint8_t M24256E_CDA_DevSelCode = 0xB0;
uint8_t M24256E_IDPage_DevSelCode = 0xB0;
uint8_t M24256E_Memory_DevSelCode = 0xA0;

static M24256E_EEPROM_CommonDrv_t *M24256EDrv[EEICA1_M24_INSTANCES_NBR];
void *M24256ECompObj[EEICA1_M24_INSTANCES_NBR];

/* USER CODE END DECL */

static int32_t M24256E_Probe(void);
int32_t M24256E_IsDeviceReady(uint32_t Instance, const uint32_t Trials);
int32_t M24256E_ReadPage(uint32_t Instance, uint8_t * const pData, const uint32_t TarAddr, const uint16_t Size);

/* Private function prototypes -----------------------------------------------*/
DSTATUS EEPROM_initialize (BYTE pdrv);
DSTATUS EEPROM_status (BYTE pdrv);
DRESULT EEPROM_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
  DRESULT EEPROM_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
  DRESULT EEPROM_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */

Diskio_drvTypeDef  EEPROM_Driver =
{
  EEPROM_initialize,
  EEPROM_status,
  EEPROM_read,
#if  _USE_WRITE
  EEPROM_write,
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1
  EEPROM_ioctl,
#endif /* _USE_IOCTL == 1 */
};

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Initializes a Drive
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS EEPROM_initialize (BYTE pdrv /* Physical drive nmuber to identify the drive */
									  )
{
	/* USER CODE BEGIN INIT */
	UNUSED(pdrv);																// We have only one EEPROM on the bus

    Stat = STA_NOINIT;

	if (M24256E_Probe() == BSP_ERROR_NONE)
		Stat = 0;

    return Stat;
	/* USER CODE END INIT */
}

/**
  * @brief  Gets Disk Status
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS EEPROM_status (
	BYTE pdrv       /* Physical drive number to identify the drive */
)
{
	/* USER CODE BEGIN STATUS */
	UNUSED(pdrv);																// We have only one EEPROM on the bus
    Stat = STA_NOINIT;

	if (M24256E_IsDeviceReady(EEICA1_M24256E, MAX_TRIALS) == BSP_ERROR_NONE)
		Stat = 0;

    return Stat;
	/* USER CODE END STATUS */
}

/**
  * @brief  Reads Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data buffer to store read data
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to read (1..128)
  * @retval DRESULT: Operation result
  */
DRESULT EEPROM_read (
	BYTE pdrv,      /* Physical drive nmuber to identify the drive */
	BYTE *buff,     /* Data buffer to store read data */
	DWORD sector,   /* Sector address in LBA */
	UINT count      /* Number of sectors to read */
)
{
	/* USER CODE BEGIN READ */
	UNUSED(pdrv);
	printf("Requested %u sectors starting at %lu.\n", count, sector);

	if (M24256E_ReadPage(EEICA1_M24256E, buff, sector * _MIN_SS, count * _MIN_SS) != BSP_ERROR_NONE)
		return RES_ERROR;

    return RES_OK;
	/* USER CODE END READ */
}

/**
  * @brief  Writes Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
#if _USE_WRITE == 1
DRESULT EEPROM_write (
	BYTE pdrv,          /* Physical drive nmuber to identify the drive */
	const BYTE *buff,   /* Data to be written */
	DWORD sector,       /* Sector address in LBA */
	UINT count          /* Number of sectors to write */
)
{
  /* USER CODE BEGIN WRITE */
  /* USER CODE HERE */
    return RES_OK;
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */

/**
  * @brief  I/O control operation
  * @param  pdrv: Physical drive number (0..)
  * @param  cmd: Control code
  * @param  *buff: Buffer to send/receive control data
  * @retval DRESULT: Operation result
  */
#if _USE_IOCTL == 1
DRESULT EEPROM_ioctl (
	BYTE pdrv,      /* Physical drive nmuber (0..) */
	BYTE cmd,       /* Control code */
	void *buff      /* Buffer to send/receive control data */
)
{
	/* USER CODE BEGIN IOCTL */
	UNUSED(pdrv);

    DRESULT res = RES_ERROR;

    if (Stat & STA_NOINIT) return RES_NOTRDY;

    switch (cmd)
    {
		case CTRL_SYNC:	  // Make sure that no pending write process
			res = RES_OK;
			break;

		case GET_SECTOR_COUNT:	  // Get number of sectors on the disk (DWORD)
			*(DWORD*)buff = M24256E_MEMORYSIZE / _MIN_SS;
			res = RES_OK;
			break;

		case GET_SECTOR_SIZE:	// Get R/W sector size (WORD)
			*(WORD*)buff = _MIN_SS;
			res = RES_OK;
			break;


		case GET_BLOCK_SIZE:	// Get erase block size in unit of sector (DWORD)
			*(DWORD*)buff = 1;
			res = RES_OK;
			break;

		default:
			res = RES_PARERR;
    }

    return res;
	/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

//------------------------------------------------------------------------------

/**
* @brief  Reads complete page from the memory at page start address
* @param  Instance : I2C EEPROM instance to be used
* @param  pData   : pointer to the data to read
* @param  TarAddr : starting page address to read
* @param  Size    : Size in bytes of the value to be written
* @retval BSP status
*/
int32_t M24256E_ReadPage(uint32_t Instance, uint8_t * const pData, const uint32_t TarAddr, const uint16_t Size)
{
  int32_t ret = BSP_ERROR_NONE;

  if ((TarAddr + Size)> M24256E_MEMORYSIZE)
    return BSP_ERROR_WRONG_PARAM;

  uint32_t iNumberOfPage = (TarAddr + Size) / M24256E_PAGESIZE;
  uint32_t iRemainder = (TarAddr + Size) % M24256E_PAGESIZE;

  uint32_t PageAddress = TarAddr * M24256E_PAGESIZE;
  uint32_t iPageNumber = TarAddr;
  if (iRemainder != 0U)
  {
    iNumberOfPage += 1U;
  }

  if (iNumberOfPage <= 1U)
  {
    if (M24256EDrv[Instance]->ReadPage(M24256ECompObj[Instance], pData, PageAddress, M24256E_PAGESIZE) != BSP_ERROR_NONE)
    {
      ret = BSP_ERROR_COMPONENT_FAILURE;
    }
    else
    {
      ret = BSP_ERROR_NONE;
    }
  }
  else
  {
    for (uint32_t iCounter=0; iCounter<iNumberOfPage; iCounter++)
    {
      uint32_t iPageAddress = iPageNumber * M24256E_PAGESIZE;
      ret = M24256EDrv[Instance]->ReadPage(M24256ECompObj[Instance], &pData[0U + (iCounter*M24256E_PAGESIZE)], iPageAddress, M24256E_PAGESIZE);
      iPageNumber++;
      HAL_Delay(5);
    }
  }

  return ret;
}

//------------------------------------------------------------------------------

/**
* @brief  Checks if the memory is available
* @param  Instance : I2C EEPROM instance to be used
* @param  Trials : Number of trials
* @retval BSP status
*/
int32_t M24256E_IsDeviceReady(uint32_t Instance, const uint32_t Trials)
{
    int32_t ret;

    if (M24256EDrv[Instance]->IsReady(M24256ECompObj[Instance], Trials) != BSP_ERROR_NONE)
    {
      ret = BSP_ERROR_COMPONENT_FAILURE;
    }
    else
    {
      ret = BSP_ERROR_NONE;
    }
    return ret;
}

//------------------------------------------------------------------------------

/**
* @brief  Register Bus IOs for instance M24256E
* @retval BSP status
*/
static int32_t M24256E_Probe(void)
{
    M24256E_IO_t io_ctx256;
    int32_t ret = BSP_ERROR_NONE;
    static M24256E_Object_t M24256_obj_0;

    io_ctx256.Address     = M24256E_CDA_DevSelCode;
    io_ctx256.Init        = EEICA1_I2C_INIT;
    io_ctx256.DeInit      = EEICA1_I2C_DEINIT;
    io_ctx256.ReadReg     = EEICA1_I2C_READREG;
    io_ctx256.WriteReg    = EEICA1_I2C_WRITEREG;
    io_ctx256.ReadReg16   = EEICA1_I2C_READREG16;
    io_ctx256.WriteReg16  = EEICA1_I2C_WRITEREG16;
    io_ctx256.Transmit    = EEICA1_I2C_SEND;
    io_ctx256.IsReady     = EEICA1_I2C_ISREADY;
    io_ctx256.Delay       = EEICA1_M24_DELAY;

    if (M24256E_RegisterBusIO(&M24256_obj_0, &io_ctx256) != M24_OK)
    {
		ret = BSP_ERROR_UNKNOWN_COMPONENT;
    }

    M24256ECompObj[EEICA1_M24256E] = &M24256_obj_0;
    M24256EDrv[EEICA1_M24256E] = (M24256E_EEPROM_CommonDrv_t *)(void *)&M24256E_i2c_Drv;

    if (M24256EDrv[EEICA1_M24256E]->Init(M24256ECompObj[EEICA1_M24256E]) != M24_OK)
    {
		ret = BSP_ERROR_COMPONENT_FAILURE;
    }
    else
    {
		ret = BSP_ERROR_NONE;
    }
    return ret;
}

//------------------------------------------------------------------------------
