Flash API

API Reference


This file contains data structures and prototypes definitions for high level Flash APIs.

The goal is to provide an API to update an image recorded:

  • in a flash partition in RAW mode at the block layer, using a Memory Technology Device (MTD).
  • in an Unsorted Block Images (UBI) volume, using a MTD and updating UBI information and headers.

IPC interfaces binding

All the functions of this API are provided by the fwupdateService.

Here's a code sample binding to the flash service:

bindings:
{
   clientExe.clientComponent.le_flash -> fwupdateService.le_flash
}
Warning
These APIs are not available for all platforms. Please refer to the Product Technical Specification document of your platform for further details. Please refer to Flash for details.

Bad image detection

This functionality allows the user to be notified when an image becomes bad.

Flash access protection

To address race conditions, le_flash_RequestAccess() has to be called first before trying to access to the flash. When all flash modifications are done, le_flash_ReleaseAccess() is required.

Open a partition

Depending on how the read/write operations have to be done, the correct open API has to be used:

A sample code showing how to open, retrieve information and close can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Retrieve information about an open partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_Info
(
char **args
)
{
const char *partNameStr = args[0];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize;
 
// Open the given MTD partition in R/O
res = le_flash_OpenMtd(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Retrieve MTD flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the MTD
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
 
return res;
}

A sample code showing how to open UBI, retrieve UBI information and close UBI can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Retrieve information about an UBI volume
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_InfoUbi
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize;
uint32_t freeBlock, volBlock, volSize;
 
// Open the given UBI partition in R/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Retrieve UBI flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Open an UBI volume belonging to this UBI partition
res = le_flash_OpenUbiVolume(partRef, ubiVolStr, LE_FLASH_UBI_VOL_NO_SIZE);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
LE_INFO("Free Block %u, Allocated Block to Volume %u, Volume Size %u",
freeBlock, volBlock, volSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI volume
res = le_flash_CloseUbiVolume(partRef);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

Close a partition

When the read/write operation are over, the application has to call le_flash_Close() API to release resources and make the partition usable.

Erase a block inside a partition

When a block needs to be erased; the application has to call le_flash_EraseBlock() API to perform the erase operation on the given block index. If the erase fails, the block is marked "bad".

Read a data chunk

To read data, le_flash_Read() has to be called. The data are read at the specified block index. Note that this index is not the index of the block into the partition, but a logical block index:

  • the bad blocks are skipped.
  • the blocks are not into a consecutive order for UBI volume.

The data length to be read can't be higher than:

  • an erase block size for MTD usage partition
  • an erase block size minus 2 pages for UBI partitions (UBI uses 2 pages for its internal need)

A sample code showing how to read a whole partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Dump all blocks from a MTD partition into a file
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_Dump
(
char **args
)
{
const char *partNameStr = args[0];
const char *toFile = args[1];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize, blockIdx, size;
int writeSize;
int toFd;
uint8_t rData[LE_FLASH_MAX_READ_SIZE];
 
toFd = open(toFile, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if (-1 == toFd)
{
LE_ERROR("Failed to open '%s': %m", toFile);
return LE_FAULT;
}
 
// Open the given MTD partition in R/O
res = le_flash_OpenMtd(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
close(toFd);
return res;
}
 
// Retrieve MTD flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
close(toFd);
le_flash_Close(partRef);
return res;
}
 
// Loop for all blocks of the partition, try to read it and dump it to the file
for(blockIdx = 0; blockIdx < numBlock; blockIdx++)
{
// Read the whole erase block size
// As we read in RAW, the whole erase block is read by once
size = sizeof(rData);
res = le_flash_Read(partRef, blockIdx, rData, &size);
if (LE_OK != res)
{
LE_ERROR("le_flash_Read failed: %d", res);
break;
}
LE_DEBUG("Read blockIdx %u size %u", blockIdx, size);
 
// Write to file
writeSize = write(toFd, rData, size);
if (-1 == writeSize)
{
LE_ERROR("Write to file failed: %m");
break;
}
}
if (LE_OK == res)
{
LE_INFO("Read %u blocks from partition \"%s\"", blockIdx, partNameStr);
}
else
{
close(toFd);
le_flash_Close(partRef);
return res;
}
 
// Close the MTD
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
close(toFd);
 
return res;
}

A sample code showing how to read a whole UBI volume inside an UBI partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Dump a whole UBI volume from an UBI partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_DumpUbi
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
const char *toFile = args[2];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize, blockIdx, size;
uint32_t freeBlock, volBlock, volSize, readVolSize = 0;
int writeSize;
int toFd;
uint8_t rData[LE_FLASH_MAX_READ_SIZE];
 
toFd = open(toFile, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if (-1 == toFd)
{
LE_ERROR("Failed to open '%s': %m", toFile);
return LE_FAULT;
}
 
// Open the given UBI partition in R/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
close(toFd);
return res;
}
 
// Open an UBI volume belonging to this UBI partition
// As the UBI is open is R/O, discard the volume size to adjust when le_flash_CloseUbiVolume()
// is called.
res = le_flash_OpenUbiVolume(partRef, ubiVolStr, LE_FLASH_UBI_VOL_NO_SIZE);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
close(toFd);
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
close(toFd);
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
LE_INFO("Free Block %u, Allocated Block to Volume %u, Volume Size %u",
freeBlock, volBlock, volSize);
if (LE_OK != res)
{
close(toFd);
le_flash_Close(partRef);
return res;
}
 
// Loop for all blocks of the UBI volume, try to read it and dump it to the file
for(blockIdx = 0; blockIdx < volBlock; blockIdx++)
{
// Read the whole erase block size
// As we read in UBI, the whole erase block is read by once minus some administrative
// pages. The size reported by the le_flash_Read() is real size read.
size = sizeof(rData);
res = le_flash_Read(partRef, blockIdx, rData, &size);
if (LE_OK != res)
{
LE_ERROR("le_flash_Read failed: %d", res);
break;
}
LE_DEBUG("Read blockIdx %u size %u", blockIdx, size);
readVolSize += size;
 
writeSize = write(toFd, rData, size);
if (-1 == writeSize)
{
LE_ERROR("Write to file failed: %m");
break;
}
}
close(toFd);
if (LE_OK == res)
{
LE_INFO("Read %u blocks from UBI partition \"%s\" volume \"%s\"",
blockIdx, partNameStr, ubiVolStr);
LE_INFO("Volume size read %u, expected volume size %u", readVolSize, volSize);
}
else
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI volume
res = le_flash_CloseUbiVolume(partRef);
LE_INFO("UBI volume \"%s\" close ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

Write a data chunk

To write some data, le_flash_Write() can be used. The data are written at a specified block index. This index is a logical block index (see Read a data chunk). The block is firstly erased, so no call to le_flash_EraseBlock() is needed. If the erase or the write reports an error, the block is marked "bad" and the write starts again at the next physical block. The data length to be written can't be higher than:

  • an erase block size for MTD usage partition
  • an erase block size minus 2 pages for UBI partitions (UBI uses 2 pages for its internal need)

A sample code showing how to write a whole partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Flash a file into a MTD partition and erase remaining blocks up to end of the partition if any
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_FlashErase
(
char **args
)
{
const char *partNameStr = args[0];
const char *fromFile = args[1];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize, blockIdx, size, readSize;
uint32_t writeBadBlock, eraseBadBlock;
int fromFd;
uint8_t rData[LE_FLASH_MAX_READ_SIZE];
 
fromFd = open(fromFile, O_RDONLY);
if (-1 == fromFd)
{
LE_ERROR("Failed to open '%s': %m", fromFile);
return LE_FAULT;
}
 
// Open the given MTD partition in W/O
res = le_flash_OpenMtd(partNameStr, LE_FLASH_WRITE_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
close(fromFd);
return res;
}
 
// Retrieve MTD flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
close(fromFd);
le_flash_Close(partRef);
return res;
}
 
// Loop for all blocks of the partition, try to read from the file and flash the erase block
// into the partition
for(blockIdx = 0; blockIdx < numBlock; blockIdx++)
{
// Read the whole erase block size
size = eraseBlockSize;
readSize = read(fromFd, rData, size);
if (-1 == readSize)
{
LE_ERROR("Read from file failed: %m");
res = LE_FAULT;
break;
}
// Nothing to read, file is complete
if (0 == readSize)
{
break;
}
 
// Write the whole erase block size
// As we write in RAW, the whole erase block is written by once
// The Flash layer will perform an erase before writing. We do not need to call it.
// If the write fails or the erase fails, the block will be marked bad and the write
// starts again at the next block.
res = le_flash_Write(partRef, blockIdx, rData, readSize);
if (LE_OK != res)
{
LE_ERROR("le_flash_Write failed: %d", res);
break;
}
// As blocks are marked bad, it may happen that we cannot write the whole file into
// the Flash partition if too many bad blocks are found.
LE_DEBUG("Write blockIdx %u size %u", blockIdx, size);
}
close(fromFd);
if (LE_OK == res)
{
LE_INFO("Written %u blocks to partition \"%s\"", blockIdx, partNameStr);
}
else
{
le_flash_Close(partRef);
return res;
}
 
// Retrieve MTD flash information to look for "new bad blocks"
writeBadBlock = 0;
&writeBadBlock, &numBlock, &eraseBlockSize, &pageSize);
if ((LE_OK != res) || (writeBadBlock > badBlock))
{
LE_ERROR("New bad blocks marked during write: %u (%u - %u)", writeBadBlock - badBlock,
writeBadBlock, badBlock);
}
 
LE_INFO("Erasing remaining blocks: blockIdx %u numBlock %u", blockIdx, numBlock);
for ( ; blockIdx < numBlock; blockIdx++)
{
// Erase the block. If the erase fails, the block is marked bad.
res = le_flash_EraseBlock(partRef, blockIdx);
if (LE_OK != res)
{
LE_ERROR("le_flash_EraseBlock %u failed: %d", blockIdx, res);
le_flash_Close(partRef);
return res;
}
LE_DEBUG("Erase blockIdx %u", blockIdx);
}
 
// Retrieve MTD flash information to look for "new bad blocks"
eraseBadBlock = 0;
&eraseBadBlock, &numBlock, &eraseBlockSize, &pageSize);
if ((LE_OK != res) || (eraseBadBlock > writeBadBlock))
{
LE_ERROR("New bad blocks marked during erase: %u (%u - %u)", eraseBadBlock - writeBadBlock,
eraseBadBlock, writeBadBlock);
}
 
// Close the MTD
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

A sample code showing how to write a whole UBI volume inside an UBI partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Flash a whole UBI volume from a file into an UBI partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_FlashUbi
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
const char *fromFile = args[2];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize, blockIdx, size;
uint32_t freeBlock, volBlock, volSize, writeVolSize = 0;
int readSize;
int fromFd;
uint8_t rData[LE_FLASH_MAX_READ_SIZE];
struct stat st;
 
fromFd = open(fromFile, O_RDONLY);
if (-1 == fromFd)
{
LE_ERROR("Failed to open '%s': %m", fromFile);
return LE_FAULT;
}
 
// Open the given UBI partition in W/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_WRITE_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
close(fromFd);
return res;
}
 
// Open an UBI volume belonging to this UBI partition
// Get the size of file. This will be needed to "adjust" the UBI volume size after it was
// fully written. This size is passed to le_flash_OpenUbiVolume() and the UBI volume will be
// resized when le_flash_CloseUbiVolume() is called. Using LE_FLASH_UBI_VOL_NO_SIZE instead
// will keep the volume size unchanged.
fstat(fromFd, &st);
res = le_flash_OpenUbiVolume(partRef, ubiVolStr, st.st_size);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
close(fromFd);
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
close(fromFd);
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
LE_INFO("Free Block %u, Allocated Block to Volume %u, Volume Size %u",
freeBlock, volBlock, volSize);
if (LE_OK != res)
{
close(fromFd);
le_flash_Close(partRef);
return res;
}
 
// Loop until the whole size of the file has been read.
for(blockIdx = 0; writeVolSize < st.st_size; blockIdx++)
{
// The erase block size contains all UBI header and data information. We need to
// remove the 2 write pages to get the whole data size.
size = eraseBlockSize - (2*pageSize);
readSize = read(fromFd, rData, size);
if (-1 == readSize)
{
LE_ERROR("Read from file failed: %m");
res = LE_FAULT;
break;
}
// Nothing to read, file is complete
if (0 == readSize)
{
break;
}
 
// Write the whole erase block size
// As we write in UBI, the whole erase block is read by once minus some administrative
// pages.
// The Flash layer will perform an erase before writing. We do not need to call it.
// If the write fails or the erase fails, the block will be marked bad and the write
// starts again at the next block.
// If a new block is required to store data into the volume, the Flash layer will allocate
// it to the volume and fill the administrative headers.
res = le_flash_Write(partRef, blockIdx, rData, readSize);
if (LE_OK != res)
{
LE_ERROR("le_flash_Write failed: %d", res);
break;
}
LE_DEBUG("Write blockIdx %u size %u", blockIdx, readSize);
writeVolSize += readSize;
}
close(fromFd);
if (LE_OK == res)
{
LE_INFO("Write %u blocks to UBI partition \"%s\" volume \"%s\"",
blockIdx, partNameStr, ubiVolStr);
LE_INFO("Volume size written %u, expected volume size %u",
writeVolSize, (uint32_t)st.st_size);
}
else
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI volume. If a specific volume size was passed to le_flash_OpenUbiVolume(), the
// volume size will be adjusted to it. Blocks over the volume size will be released and given
// back to the UBI partition.
res = le_flash_CloseUbiVolume(partRef);
LE_INFO("UBI volume \"%s\" close ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Just re-open the same volume without size to check that the UBI volume was resized correctly.
res = le_flash_OpenUbiVolume(partRef, ubiVolStr, LE_FLASH_UBI_VOL_NO_SIZE);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information and check that the volume size reports the good size.
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
LE_INFO("Volume size adjusted to %u", volSize);
if ((volSize != st.st_size) || (volSize != writeVolSize))
{
LE_ERROR("UBI voluma has bad size: %u, expected %u", volSize, writeVolSize);
}
 
// Close the UBI volume
res = le_flash_CloseUbiVolume(partRef);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

Retrieve information about blocks and pages for a

partition. To get information about blocks and pages, call le_flash_GetBlockInformation(). The API will return:

  • The number of bad blocks belonging to this partition
  • The number of erase blocks belonging to this partition
  • The erase block size
  • The page size The whole partition size in bytes can be computed by: number of erase blocks * erase block size The whole partition available size for read and write purpose can be computed by: (number of erase blocks - number of bad blocks) * erase block size

A sample code showing how to open, retrieve information and close a partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Retrieve information about an open partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_Info
(
char **args
)
{
const char *partNameStr = args[0];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize;
 
// Open the given MTD partition in R/O
res = le_flash_OpenMtd(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Retrieve MTD flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the MTD
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
 
return res;
}

Retrieve UBI information.

To get information about an UBI volume, call le_flash_GetUbiVolumeInformation(). The API will return:

  • The number of free blocks in the whole UBI partition
  • The currently number of blocks allocated to the UBI volume
  • The real size of the UBI volume This API must be called after the UBI volume has been open by le_flash_OpenUbiVolume().

A sample code showing how to open UBI, retrieve UBI information and close UBI can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Retrieve information about an UBI volume
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_InfoUbi
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize;
uint32_t freeBlock, volBlock, volSize;
 
// Open the given UBI partition in R/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_READ_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Retrieve UBI flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Open an UBI volume belonging to this UBI partition
res = le_flash_OpenUbiVolume(partRef, ubiVolStr, LE_FLASH_UBI_VOL_NO_SIZE);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
LE_INFO("Free Block %u, Allocated Block to Volume %u, Volume Size %u",
freeBlock, volBlock, volSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI volume
res = le_flash_CloseUbiVolume(partRef);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

Create an UBI partition and volume

le_flash_CreateUbi() is used to create an UBI partition. If the partition is already an UBI, an error LE_DUPLICATE will be reported. The overwrite of the partition can be forced by setting the flag isForcedCreate to true (all previous content will be lost).

When the creation succeeded, the UBI device is opened using le_flash_OpenUbi() with LE_FLASH_READWRITE mode.

To create a new volume in the created UBI partition, le_flash_CreateUbiVolume() has to be called. If a volume already exists with the same name or same ID, an error LE_DUPLICATE will be reported. The flag isForcedCreate permits to re-create the volume, but the previous content of the volume will be lost.

The created volume can be opened using the le_flash_OpenUbiVolume() API.

An existing volume can be deleted thanks to le_flash_DeleteUbiVolume().

Please refer to Flash for details.

A sample code showing how to create an UBI partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Create an UBI partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_CreateUbi
(
char **args
)
{
const char *partNameStr = args[0];
le_flash_PartitionRef_t partRef = NULL;
uint32_t badBlock, numBlock, eraseBlockSize, pageSize;
 
// Create and open the given UBI partition in W/O
res = le_flash_CreateUbi(partNameStr, true, &partRef);
LE_INFO("partition \"%s\" create ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Retrieve UBI flash information
res = le_flash_GetBlockInformation(partRef, &badBlock, &numBlock, &eraseBlockSize, &pageSize);
LE_INFO("Bad Block %u, Block %u, Erase Block Size %u, Page Size %u",
badBlock, numBlock, eraseBlockSize, pageSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
 
return res;
}

A sample code showing how to create an UBI volume inside an UBI partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Create an UBI volume into an UBI partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_CreateUbiVol
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
const char *ubiVolIdStr = args[2];
const char *ubiVolTypeStr = args[3];
const char *ubiVolSizeStr = args[4];
le_flash_PartitionRef_t partRef = NULL;
le_flash_UbiVolumeType_t ubiVolType;
int32_t ubiVolId, ubiVolSize;
uint32_t freeBlock, volBlock, volSize;
 
if (0 == strcmp(ubiVolTypeStr, "dynamic"))
{
ubiVolType = LE_FLASH_DYNAMIC;
}
else if (0 == strcmp(ubiVolTypeStr, "static"))
{
ubiVolType = LE_FLASH_STATIC;
}
else
{
LE_ERROR("Incorrect volume type '%s'. Must be dynamic or static.", ubiVolTypeStr);
}
 
if ((1 != sscanf( ubiVolIdStr, "%d", &ubiVolId )) || (ubiVolId > LE_FLASH_UBI_VOL_ID_MAX))
{
LE_ERROR("Invalid volume Id '%s'", ubiVolIdStr);
}
 
if (1 != sscanf( ubiVolSizeStr, "%d", &ubiVolSize ))
{
LE_ERROR("Invalid volume Size '%s'", ubiVolSizeStr);
}
 
// Open the given UBI partition in W/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_WRITE_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Create an UBI volume belonging to this UBI partition
res = le_flash_CreateUbiVolume(partRef, true, ubiVolId, ubiVolType, ubiVolStr, ubiVolSize);
LE_INFO("UBI volume \"%s\" id %u created ref %p, res %d", ubiVolStr, ubiVolId, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Retrieve UBI volume information
res = le_flash_GetUbiVolumeInformation(partRef, &freeBlock, &volBlock, &volSize);
LE_INFO("Free Block %u, Allocated Block to Volume %u, Volume Size %u",
freeBlock, volBlock, volSize);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI volume
res = le_flash_CloseUbiVolume(partRef);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
return res;
}

A sample code showing how to delete an UBI volume from an UBI partition can be seen below:

//--------------------------------------------------------------------------------------------------
/**
* Delete an UBI volume from a partition
*
*/
//--------------------------------------------------------------------------------------------------
static le_result_t FlashApiTest_DeleteUbiVol
(
char **args
)
{
const char *partNameStr = args[0];
const char *ubiVolStr = args[1];
le_flash_PartitionRef_t partRef = NULL;
 
// Open the given UBI partition in W/O
res = le_flash_OpenUbi(partNameStr, LE_FLASH_WRITE_ONLY, &partRef);
LE_INFO("partition \"%s\" open ref %p, res %d", partNameStr, partRef, res);
if (LE_OK != res)
{
return res;
}
 
// Delete the UBI volume from the UBI partition
res = le_flash_DeleteUbiVolume(partRef, ubiVolStr);
LE_INFO("UBI volume \"%s\" open ref %p, res %d", ubiVolStr, partRef, res);
if (LE_OK != res)
{
le_flash_Close(partRef);
return res;
}
 
// Close the UBI partition
res = le_flash_Close(partRef);
LE_INFO("partition \"%s\" close ref %p, res %d", partNameStr, partRef, res);
 
return res;
}

This API is synchronous: it is blocking until the write is over. After the end of the write, the data are read to ensure the data has been saved correctly.