Soletta™ Framework
|
This page will explain the common design patterns employed by Soletta.
Soletta has its own pattern for dealing with input/output streams. This section will describe how the soletta pattern is implemented and how to use it.
The aim of this section is to guide the developer to provide a unified API for all kind of streams, making it easier for a third party developer to migrate its application to any stream related IO, if necessary.
The pattern is divided in three categories: The stream configuration, the write API and the read API.
The stream should be configured using a struct during its creation, this struct should provide the following fields.
feed_size:
The write buffer size - If 0
means that there is no limit (Data for this buffer is not necessary allocated). data_buffer_size:
The read buffer size. This buffer is always allocated and the on_data
may be called with at most data_buffer_size
bytes - If 0
means that there is not limit. on_feed_done:
The write callback - It will be called when data was fully written. on_data:
The read callback - It will be called when there's data available to read. user_data:
Data to on_data
and on_feed_done
. Below there's a example of a configuration struct.
Write operations must be async and the data that will be transferred must be provided as a pointer to sol_blob. One should follow the API below:
Every blob requested to be written, must be queued in order to be sent. If there's no more space to queue more blobs (sum of all queued blobs >= feed_size
) the function write should return -ENOSPC
.
Every time a blob is fully written the on_feed_done
must be called in order to inform the user that the write operation was completed. The on_feed_done
should have the following signature:
One should warn the user that it's not necessary to use sol_blob_unref(), because the stream API will already do that. The status
variable should be 0 on success and -errno
on error. The user should be able to cancel the stream operation from the on_feed_done
.
Below there's an example of a stream device write implementation.
Since this is a stream, there's no direct way for a third party developer to request a read operation. In order to be able to read from the stream, the third party developer must provide the on_data
duration the stream configuration/creation. The on_data
is provided during the stream creation, one must inform the user every time data is avaible to read. The on_data
has the following signature:
Note that the on_data
returns a ssize_t
, the returned value must be -errno
if an error happened or the number of bytes consumed from buf
(0 is valid). The consumed bytes will be removed from the buf
. It's important to note that the buf
should not be modified and any references to its data should not be kept after on_data
returns. Also just like on_feed_done
, the user should be able to close/delete the stream handle inside on_data
. Extra care must be taken in order to do not crash.
Below there's an example of the stream device read implementation.
In the following example, it will be demonstrated how one can correctly use a stream API. The example is consists in an UART producer producing more data than an UART consumer can read. By doing so, it will be demonstrated how flow control can be employed.
First we start configuring the streams and creating the streams.
The producer will create its data every 10 ms ticks and try to send it. Since the producer buffer is limited sol_uart_feed() will start to return -ENOSPC, because the sum of all blobs are limited to feed_size
.
Below we can see how the data is produced and sent.
It's important to note that every time sol_uart_feed() returns -ENOSPC
the data production is ceased and the blob that could not be sent is stored at the pending_blob
variable.
Every time a blob is completely written, the on_feed_done
is called, in this case producer_data_written
function. In this function the pending_blob
variable is checked to see if it's not NULL
. In case it's not NULL, we try to send it.
By stopping the data production when sol_uart_feed() returns -ENOSPC
we control the data input flow. After sol_uart_feed() starts to accept blobs, the data production can start once again.
Now it's time to take a look at our consumer. It's a very naive consumer, it will just print the data to stdout
every time the nul byte is found. In addiction it will close the UART input if it receives the "close" string. Note that is completely safe to close the UART from the on_data
(the same applies to on_feed_done!
)
Now that you have the stream usage basics, you can start coding your apps!
Soletta currently implements streams for the following modules: