LIBMEM includes a set of memory drivers for common memory devices which means in most cases you probably won't need to write a LIBMEM driver. If however you wish to use LIBMEM to drive other unsupported memory devices you will need to write your own LIBMEM driver.

It is fairly straightforward to implement a LIBMEM driver, the following example demonstrates the implementation of a minimal LIBMEM driver:

#include <libmem.h>

static int
libmem_write_impl(libmem_driver_handle_t *h, uint8_t *dest, const uint8_t *src, size_t size)
{
  // TODO: Implement memory write operation.
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_fill_impl(libmem_driver_handle_t *h, uint8_t *dest, uint8_t c, size_t size)
{
  // TODO: Implement memory fill operation.
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_erase_impl(libmem_driver_handle_t *h, uint8_t *start, size_t size, 
                  uint8_t **erase_start, size_t *erase_size)
{
  // TODO: Implement memory erase operation.
  if (erase_start)
    {
      // TODO: Set erase_start to point to the start of the memory block that
      //       has been erased. For now we'll just return the requested start in
      //       order to keep the caller happy.
      *erase_start = start;
    }
  if (erase_size)
    {
      // TODO: Set erase_size to the size of the memory block that has been
      //       erased. For now we'll just return the requested size in order to
      //       keep the caller happy.
      *erase_size = size;
    }
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_lock_impl(libmem_driver_handle_t *h, uint8_t *dest, size_t size)
{
  // TODO: Implement memory lock operation
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_unlock_impl(libmem_driver_handle_t *h, uint8_t *dest, size_t size)
{
  // TODO: Implement memory unlock operation.
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_flush_impl(libmem_driver_handle_t *h)
{
  // TODO: Implement memory flush operation.
  return LIBMEM_STATUS_SUCCESS;
}

static const libmem_driver_functions_t driver_functions =
{
  libmem_write_impl,
  libmem_fill_impl,
  libmem_erase_impl,
  libmem_lock_impl,
  libmem_unlock_impl,
  libmem_flush_impl
};

int
libmem_register_example_driver_1(libmem_driver_handle_t *h, uint8_t *start, size_t size)
{
  libmem_register_driver(h, start, size, 0, 0, &driver_functions, 0);
  return LIBMEM_STATUS_SUCCESS;
}

For some types of memory it is necessary to carry out operations on a per-sector basis, in this case it can be useful to register a geometry with the driver and use the geometry helper functions. For example the following code demonstrates how you might implement a driver that can only erase the entire memory or individual sectors.

static int
driver_erase_sector(libmem_driver_handle_t *h, libmem_sector_info_t *si)
{
  // TODO: Implement sector erase for sector starting at si->start
  return LIBMEM_STATUS_SUCCESS;
}

static int
driver_erase_chip(libmem_driver_handle_t *h)
{
  // TODO: Implement chip erase
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_erase_impl(libmem_driver_handle_t *h, uint8_t *start, size_t size,
                  uint8_t **erase_start, size_t *erase_size)
{ 
  int res;
  if (LIBMEM_RANGE_WITHIN_RANGE(h->start, h->start + h->size - 1, start, start + size - 1))
    {
      res = driver_erase_chip(h);
      if (erase_start)
        *erase_start = h->start;
      if (erase_size)
        *erase_size = h->size;
    }
  else
    res = libmem_foreach_sector_in_range(h, start, size, driver_erase_sector, erase_start, erase_size);
  return res;
} 

static const libmem_geometry_t geometry[] =
{ 
  { 8,  0x00002000 }, // 8 x 8KB sectors
  { 31, 0x00010000 }, // 31 x 64KB sectors
  { 0, 0 }            // NULL terminator
};

int
libmem_register_example_driver_2(libmem_driver_handle_t *h, uint8_t *start, size_t size)
{ 
  libmem_register_driver(h, start, size, geometry, 0, &driver_functions, 0);
  return LIBMEM_STATUS_SUCCESS;
}

There are two sets of driver entry point functions, the standard set that include functions common to most LIBMEM drivers which have been described above and the extended set which provide extra functionality for less common types of driver. The following example demonstrates how you would also register a set of extended LIBMEM driver functions in your driver:

static int
libmem_inrange_impl(libmem_driver_handle_t *h, const uint8_t *dest)
{
  // TODO: Implement inrange function (return non-zero if dest is within range 
  //       handled by driver).
  return 0;
}

static int
libmem_read_impl(libmem_driver_handle_t *h, uint8_t *dest, const uint8_t *src, size_t size)
{
  // TODO: Implement memory read operation
  return LIBMEM_STATUS_SUCCESS;
}

static uint32_t
libmem_crc32_impl(libmem_driver_handle_t *h, const uint8_t *start, size_t size, uint32_t crc)
{
  // TODO: Implement CRC-32 operation.
  return crc;
}

static const libmem_ext_driver_functions_t ext_driver_functions = 
{
  libmem_inrange_impl,
  libmem_read_impl,
  libmem_crc32_impl
};

int
libmem_register_example_driver_3(libmem_driver_handle_t *h, uint8_t *start, size_t size)
{
  libmem_register_driver(h, start, size, geometry, 0, &driver_functions, &ext_driver_functions);
  return LIBMEM_STATUS_SUCCESS;
}

Some types of memory require you to carry out paged writes. The paged write driver helper functions have been provided to simplify the writing of drivers of this type.

To use these functions, you need to call libmem_driver_paged_write_init supplying a paged write control block, a page buffer, the page size, a pointer to a function that will carry out the actual page write operation and the byte alignment of the source data required by the page write function. You can then use the libmem_driver_paged_write, libmem_driver_paged_write_fill and libmem_driver_paged_write_flush functions to implement your driver's write, fill and flush functions.

For example, the following code demonstrates how you might implement a driver for a device with a page size of 256 bytes:

static uint8_t page_buffer[256];
static libmem_driver_paged_write_ctrlblk_t paged_write_ctrlblk;

static int
flash_write_page(libmem_driver_handle_t *h, uint8_t *dest, const uint8_t *src)
{
  // TODO: Implement function that writes a page of data from src to page
  //       starting at dest.
  return LIBMEM_STATUS_SUCCESS;
}

static int
libmem_write_impl(libmem_driver_handle_t *h, uint8_t *dest, const uint8_t *src, size_t size)
{
  return libmem_driver_paged_write(h, dest, src, size, &paged_write_ctrlblk);
}

static int
libmem_fill_impl(libmem_driver_handle_t *h, uint8_t *dest, uint8_t c, size_t size)
{
  return libmem_driver_paged_write_fill(h, dest, c, size, &paged_write_ctrlblk);
}

static int
libmem_flush_impl(libmem_driver_handle_t *h)
{
  return libmem_driver_paged_write_flush(h, &paged_write_ctrlblk);
}

int
libmem_register_example_driver_4(libmem_driver_handle_t *h, uint8_t *start, size_t size)
{
  libmem_register_driver(h, start, size, 0, 0, &driver_functions, 0);
  libmem_driver_paged_write_init(&paged_write_ctrlblk, 
                                 page_buffer, sizeof(page_buffer),
                                 flash_write_page, 4,
                                 0);
  return LIBMEM_STATUS_SUCCESS;
}