Logo
UNICENS V2.1.0-3491
User Manual and API Reference
Low-Level Driver

Introduction

The low-level driver (LLD) is responsible to forward Tx messages from the UNICENS library to the INIC and Rx messages from the INIC to the UNICENS library. The UNICENS library currently provides one interface to exchange control messages with the INIC. Therefore, the control messages will be wrapped in a so called Port Message. The low-level driver is responsible to transmit and receive the Port Messages over the interface that is adjusted in the INIC Configuration String.

Initialization

The following example shows how to assign low-level driver functions which are invoked by the UNICENS library.

bool Initialize(void)
{
Ucs_InitData_t init_data;
/* platform specific initialization of the low-level driver */
Lld_SysInitialize(/*system specific parameters*/);
/* assign init data */
Ucs_SetDefaultConfig(&init_data);
init_data.lld.start_fptr = &Lld_Start;
init_data.lld.stop_fptr = &Lld_Stop;
init_data.lld.tx_transmit_fptr = &Lld_TxTransmit;
init_data.lld.rx_available_fptr = &Lld_RxMsgAvailable;
/* ... further assignment and initialization ... */
/* create one instance of UCS */
inst_ptr = Ucs_CreateInstance();
if (inst_ptr != NULL)
{
if (Ucs_Init(inst_ptr, &init_data, &App_OnInitResult) == UCS_RET_SUCCESS)
{
return true;
}
}
return false;
}
 See also Getting Started, section Initialization.

Local Data

The exemplary implementation of callback functions refers the following local data. In order to call UNICENS API functions the low-level driver has to store a set of callback functions and an internal handle which is required to invoke a callback function.

#define MAX_DATA_LEN 72 /* messages can be up to 72 bytes */
Ucs_Lld_Api_t *_cb_ptr; /* reference to UCS callback functions */
void *_h_ptr; /* reference to nun-public handler, which */
/* is necessary to invoke callback functions */
bool _rx_event; /* labels if the driver may read an available Rx message */
uint8_t _tx_buffer[MAX_DATA_LEN];
uint8_t _rx_buffer[MAX_DATA_LEN];

Start and Stop

The UNICENS library invokes the mandatory callback functions start_fptr() and stop_fptr() to signal when internal data is valid and the LLD may access UNICENS callback functions and message objects for transmission and reception.

The function Lld_SysInitialize() is not called by the UNICENS library. This example function is system specific and might be necessary to set local data to definite values and to initialize the device driver before UNICENS library will start to transmit and receive.

The low-level driver is allowed to initialize and run the communication with the INIC before the UNICENS Library calls Lld_Start(). While the UNICENS callback functions are invalid, it is recommended to discard incoming Rx messages.

Before leaving the function Lld_Stop() the LLD must ensure not accessing UCS memory and callback functions any longer.

void Lld_SysInitialize(void)
{
/* Initialization: system specific setup of the device driver, trace, queues, interrupts, etc.
Initialize local data, discard incoming messages from the INIC until Lld_Start() is called.
*/
_rx_event = false;
_api_ptr = NULL;
_h_ptr = NULL;
}
void Lld_Start(Ucs_Lld_Api_t* api_ptr, void *h_ptr, void *lld_user_ptr)
{
/* UNICENS is ready to transmit and receive.
Now, remember the callbacks and the handle which is
required to invoke a callback function.
*/
_api_ptr = api_ptr;
_h_ptr = inst_ptr;
}
void Lld_Stop(void *lld_user_ptr)
{
/* IMPORTANT: Do no longer invoke any UNICENS callback function and
immediately stop accessing UNICENS message objects.
I.e., ensure stopping asynchronous transmission and reception using UNICENS memory
before leaving this function (e.g., stop/wait DMA copy operations to/from UNICENS memory).
Incoming messages from the INIC should be discarded until Lld_Start() is called again.
*/
_api_ptr = NULL;
_h_ptr = NULL;
}

Message Transmission

The low-level driver must implement a transmit function which requires a reference to Ucs_Lld_TxMsg_t. The code section below shows the declaration of the Ucs_Lld_TxMsg_t and the referred Ucs_Mem_Buffer_t type. The Ucs_Mem_Buffer_t structure contains values to describe the pointer and the size of the buffer. Furthermore it is possible that one buffer refers a second buffer if next_buffer_ptr is set. This means that it is possible to concatenate multiple buffers. Furthermore, one message structure refers a buffer structure. The custom_next_msg_ptr is not set by UNICENS library. It can be used by the Low Level Driver to concatenate messages, e.g. for asynchronous transmission.

typedef struct Ucs_Mem_Buffer_
{
struct Ucs_Mem_Buffer_ *next_buffer_ptr;
uint8_t *data_ptr;
uint16_t data_size;
uint16_t total_size;
typedef struct Ucs_Lld_TxMsg_
{
struct Ucs_Lld_TxMsg_ *custom_next_msg_ptr;
Ucs_Mem_Buffer_t *memory_ptr;

The transmit function shall copy the data from the memory buffers to the target buffer. Therefore, the low-level driver has to iterate over all buffers. After the low-level driver does no longer refer the message object, it has to call tx_release_fptr. It is important to call tx_release_fptr in the same order as the messages are passed to the transmit function.

void Lld_TxTransmit(Ucs_Lld_TxMsg_t *msg_ptr, void *lld_user_ptr)
{
Ucs_Mem_Buffer_t * buf_ptr;
uint16_t i = 0U; /* index of target buffer*/
if ((msg_ptr != NULL) && _api_ptr)
{
for (buf_ptr = msg_ptr->memory_ptr; buf_ptr != NULL; buf_ptr = buf_ptr->next_buffer_ptr)
{
memcpy(&_tx_buffer[i], buf_ptr->data_ptr, buf_ptr->data_size);
i += buf_ptr->data_size;
}
/* call synchronous transmit function of driver */
MY_TX(&_tx_buffer[0], msg_ptr->memory_ptr->total_size);
/* release the message object */
_api_ptr->tx_release_fptr(_h_ptr, msg_ptr);
}
}
Note
The transmit function has no return value. This means that the low-level driver itself has to handle asynchronous re-transmission if the interface is busy. Therefore, the Low Level Driver is able to enqueue multiple message objects by using custom_next_msg_ptr. If the LLD detects an unrecoverable transmission error the LLD shall abort the transmission and call tx_release_fptr().
 See also API Reference, sections Low-Level Driver and Ucs_Lld_Api_t.

Message Reception

The low-level driver has to forward messages to the UNICENS library by calling rx_allocate_fptr() and rx_receive_fptr(). The structure of the provided Rx message object is shown in the code section below.

typedef struct
{
uint8_t* data_ptr;
uint16_t data_size;

If rx_allocate_fptr() returns a valid reference to a Ucs_Lld_RxMsg_t structure, the low-level driver has to copy the received message to the data_ptr attribute. The value of data_size is already set to the same value as initially stated to rx_allocate_fptr(). After the copy operation, the low-level driver shall call rx_receive_fptr() which hands the Rx message over to the UNICENS library.

static void My_RxHandler(void)
{
if (_rx_event != false)
{
bool success = false;
uint16_t size = 0;
/* receive to LLD provided buffer */
success = MY_RX(&_rx_buffer[0], &size);
/* check if UNICENS is running and message is ok */
if ((_api_ptr != NULL) && (success != false) && (size < MAX_DATA_LEN))
{
Ucs_Lld_RxMsg_t *msg_ptr;
/* allocate message object from UCS */
msg_ptr = _api_ptr->rx_allocate_fptr(_h_ptr, size);
if (msg_ptr != NULL)
{
uint16_t i;
for (i=0; i<size; i++)
{ /* copy data to UCS message object */
msg_ptr->data_ptr[i] = _rx_buffer[i];
}
/* pass message object to UCS */
_api_ptr->rx_receive_fptr(_h_ptr, msg_ptr);
}
else
{
/* wait until Lld_RxMsgAvailable() is invoked */
}
}
else
{
/* discard Rx message - since UCS is not running */
}
}
}
 See also API Reference, sections Low-Level Driver and Ucs_Lld_Api_t.