Storage model

Introduction

Dooba offers a generic storage model to represent any storage device implementations.

Whether it's a MicroSD card, an EEPROM, some Flash memory or any other fancy device you can think of, it is easy to expose its functionality through this generic model. Doing so will have the advantage of allowing anyone (and anything) to access that device without having to care about the underlying mechanisms.

This page will present how to make use of this storage model.

How to use

First, let's add the 'storage' library to our dfe.conf:

# ...
deps:
  - storage

Using a storage device

Whenever we get a pointer to a storage object (struct storage *), we can access it simply by calling the following functions:

  • uint8_t storage_read(struct storage *s, uint64_t addr, void *data, uint16_t size);
  • uint8_t storage_write(struct storage *s, uint64_t addr, void *data, uint16_t size);
  • uint8_t storage_sync(struct storage *s);

Example:

 1 #include <storage/storage.h>
 2 
 3 void main()
 4 {
 5     struct storage *stor;
 6     uint8_t buffer[10];
 7 
 8     // ...
 9 
10     // Read some data into our buffer (9 bytes @ offset 0x32)
11     if(storage_read(stor, 0x32, buffer, 9))    { /* An error occurred while reading! */ }
12 
13     // Write some data from our buffer (12 bytes @ offset 0x28)
14     if(storage_write(stor, 0x28, buffer, 12))  { /* An error occurred while writing! */ }
15 
16     // Synchronize our device (flush any buffers)
17     if(storage_sync(stor))                     { /* An error occurred while syncing! */ }
18 
19     // ...
20 }

Providing a storage device

To provide a new device, all we need to do is implement some access functions:

  • uint8_t read(struct storage *s, void *user, uint64_t addr, void *data, uint16_t size)
  • uint8_t write(struct storage *s, void *user, uint64_t addr, void *data, uint16_t size)
  • uint8_t sync(struct storage *s, void *user)

Then, we can provide a storage device by declaring a 'struct storage' object and initializing it with the storage_init function:

  • void storage_init(struct storage *s, void *user, uint64_t size, storage_io_t read, storage_io_t write, storage_sync_t sync, char *name_fmt, ...)
 1 #include <storage/storage.h>
 2 
 3 // Our storage device
 4 struct storage example_storage;
 5 
 6 uint8_t example_read(struct storage *s, void *user, uint64_t addr, void *data, uint16_t size)
 7 {
 8     // ToDo: do something (read from the device)
 9 }
10 
11 uint8_t example_write(struct storage *s, void *user, uint64_t addr, void *data, uint16_t size)
12 {
13     // ToDo: do something (write to the device)
14 }
15 
16 uint8_t example_sync(struct storage *s, void *user)
17 {
18     // ToDo: do something (sync the device)
19 }
20 
21 void main()
22 {
23     void *internal_reference;
24 
25     // ...
26 
27     // Initialize our storage device ('internal_reference' pointer will be passed back to read/write/sync)
28     storage_init(&example_storage, internal_reference, 1024, example_read, example_write, example_sync, "exmpl0");
29 
30     // ...
31 }

The 'user' pointer passed to 'storage_init' will be passed back to the read/write/sync functions. It can serve as an internal reference to the underlying storage implementation.

The 'size' parameter is the total storage size (in bytes). In the example above, we're assuming our device can store 1024 bytes of data.