device: Provide generic API to handle synchronous calls in drivers

Introduce the device_sync_call_t object type and its API.

This object type allows one thread to perform synchronous calls into a
driver. Only one thread can do such calls per instance of the
device_sync_call_t object since it makes a global record of what type of
thread is waiting on it.

Based on an idea from Dmitriy Korovkin and Peter Mitsis, moving their
proposal to a more generic API provides the solution for all device
drivers that exposes synchronous API in an interrupt based
implementation.

Change-Id: I793fac76645396bf4eb6be38b5a130ac6bde8f73
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2015-11-24 13:29:11 +01:00 committed by Anas Nashif
commit 55fdd1fd33
3 changed files with 163 additions and 0 deletions

View file

@ -85,4 +85,109 @@ struct device {
void _sys_device_do_config_level(int level);
struct device* device_get_binding(char *name);
/**
* Synchronous calls API
*/
#include <stdbool.h>
#include <nanokernel.h>
#ifdef CONFIG_MICROKERNEL
#include <microkernel.h>
#endif
/**
* Specific type for synchronizing calls among the 2 possible contexts
*/
typedef struct {
/** Nanokernel semaphore used for fiber context */
struct nano_sem *f_sem;
#ifdef CONFIG_MICROKERNEL
/** Microkernel semaphore used for task context */
struct _k_sem_struct _t_sem;
ksem_t t_sem;
bool caller_is_task;
#endif
} device_sync_call_t;
/**
* @brief Initialize the context-dependent synchronization data
*
* @param sync A pointer to a valid devic_sync_call_t
*/
static inline void synchronous_call_init(device_sync_call_t *sync)
{
nano_sem_init(sync->f_sem);
#ifdef CONFIG_MICROKERNEL
sync->_t_sem.waiters = NULL;
sync->_t_sem.level = sync->_t_sem.count = 0;
sync->t_sem = (ksem_t)&sync->_t_sem;
sync->caller_is_task = false;
#endif
}
#ifdef CONFIG_MICROKERNEL
/**
* @brief Wait for the isr to complete the synchronous call
* Note: In a microkernel built this function will take care of the caller
* context and thus use the right attribute to handle the synchronization.
*
* @param sync A pointer to a valid devic_sync_call_t
*/
static inline void synchronous_call_wait(device_sync_call_t *sync)
{
if ((sys_execution_context_type_get() == NANO_CTX_TASK) &&
(task_priority_get() < CONFIG_NUM_TASK_PRIORITIES - 1)) {
sync->caller_is_task = true;
task_sem_take_wait(sync->t_sem);
} else {
sync->caller_is_task = false;
nano_sem_take_wait(sync->f_sem);
}
}
/**
* @brief Signal the caller about synchronization completion
* Note: In a microkernel built this function will take care of the caller
* context and thus use the right attribute to signale the completion.
*
* @param sync A pointer to a valid devic_sync_call_t
*/
static inline void synchronous_call_complete(device_sync_call_t *sync)
{
if (sync->caller_is_task) {
task_sem_give(sync->t_sem);
} else {
nano_isr_sem_give(sync->f_sem);
}
}
#else
/**
* @brief Wait for the isr to complete the synchronous call
* Note: It will simply wait on the internal semaphore.
*
* @param sync A pointer to a valid devic_sync_call_t
*/
static inline void synchronous_call_wait(device_sync_call_t *sync)
{
nano_sem_take_wait(sync->f_sem);
}
/**
* @brief Signal the caller about synchronization completion
* Note: It will simply release the internal semaphore
*
* @param sync A pointer to a valid devic_sync_call_t
*/
static inline void synchronous_call_complete(device_sync_call_t *sync)
{
nano_isr_sem_give(sync->f_sem);
}
#endif /* CONFIG_MICROKERNEL || CONFIG_NANOKERNEL */
#endif /* _DEVICE_H_ */