# Writing LIBMEM drivers

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 straight forward 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;
        }

        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;
        }