From 48026bb824fd2d9cfb00ecd040db6ef3a416bae9 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Fri, 22 Jan 2021 21:43:36 -0500 Subject: upload initial port --- ChibiOS_20.3.2/os/oslib/src/chdelegates.c | 239 +++++++++ ChibiOS_20.3.2/os/oslib/src/chfactory.c | 811 ++++++++++++++++++++++++++++++ ChibiOS_20.3.2/os/oslib/src/chmboxes.c | 522 +++++++++++++++++++ ChibiOS_20.3.2/os/oslib/src/chmemcore.c | 227 +++++++++ ChibiOS_20.3.2/os/oslib/src/chmemheaps.c | 399 +++++++++++++++ ChibiOS_20.3.2/os/oslib/src/chmempools.c | 336 +++++++++++++ ChibiOS_20.3.2/os/oslib/src/chobjcaches.c | 491 ++++++++++++++++++ ChibiOS_20.3.2/os/oslib/src/chpipes.c | 388 ++++++++++++++ 8 files changed, 3413 insertions(+) create mode 100644 ChibiOS_20.3.2/os/oslib/src/chdelegates.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chfactory.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chmboxes.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chmemcore.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chmemheaps.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chmempools.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chobjcaches.c create mode 100644 ChibiOS_20.3.2/os/oslib/src/chpipes.c (limited to 'ChibiOS_20.3.2/os/oslib/src') diff --git a/ChibiOS_20.3.2/os/oslib/src/chdelegates.c b/ChibiOS_20.3.2/os/oslib/src/chdelegates.c new file mode 100644 index 0000000..d1c2453 --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chdelegates.c @@ -0,0 +1,239 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chdelegates.c + * @brief Delegate threads code. + * @details Delegate threads. + *

Operation mode

+ * A delegate thread is a thread performing function calls triggered + * by other threads. This functionality is especially useful when + * encapsulating a library not designed for threading into a + * delegate thread. Other threads have access to the library without + * having to worry about mutual exclusion. + * @pre In order to use the pipes APIs the @p CH_CFG_USE_DELEGATES + * option must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * + * @addtogroup oslib_delegates + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_DELEGATES == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing a delegate call. + */ +typedef struct { + /** + * @brief The delegate veneer function. + */ + delegate_veneer_t veneer; + /** + * @brief Pointer to the caller @p va_list object. + */ + va_list *argsp; +} call_message_t; + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/*lint -save -e586 [17.1] Required by design.*/ + +/** + * @brief Veneer for functions with no parameters. + * + * @param[in] argsp the list of arguments + * @return The function return value. + */ +msg_t __ch_delegate_fn0(va_list *argsp) { + delegate_fn0_t fn0 = (delegate_fn0_t)va_arg(*argsp, delegate_fn0_t); + return fn0(); +} + +/** + * @brief Veneer for functions with one parameter. + * + * @param[in] argsp the list of arguments + * @return The function return value. + */ +msg_t __ch_delegate_fn1(va_list *argsp) { + delegate_fn1_t fn1 = (delegate_fn1_t)va_arg(*argsp, delegate_fn1_t); + msg_t p1 = (msg_t)va_arg(*argsp, msg_t); + return fn1(p1); +} + +/** + * @brief Veneer for functions with two parameters. + * + * @param[in] argsp the list of arguments + * @return The function return value. + */ +msg_t __ch_delegate_fn2(va_list *argsp) { + delegate_fn2_t fn2 = (delegate_fn2_t)va_arg(*argsp, delegate_fn2_t); + msg_t p1 = (msg_t)va_arg(*argsp, msg_t); + msg_t p2 = (msg_t)va_arg(*argsp, msg_t); + return fn2(p1, p2); +} + +/** + * @brief Veneer for functions with three parameters. + * + * @param[in] argsp the list of arguments + * @return The function return value. + */ +msg_t __ch_delegate_fn3(va_list *argsp) { + delegate_fn3_t fn3 = (delegate_fn3_t)va_arg(*argsp, delegate_fn3_t); + msg_t p1 = (msg_t)va_arg(*argsp, msg_t); + msg_t p2 = (msg_t)va_arg(*argsp, msg_t); + msg_t p3 = (msg_t)va_arg(*argsp, msg_t); + return fn3(p1, p2, p3); +} + +/** + * @brief Veneer for functions with four parameters. + * + * @param[in] argsp the list of arguments + * @return The function return value. + */ +msg_t __ch_delegate_fn4(va_list *argsp) { + delegate_fn4_t fn4 = (delegate_fn4_t)va_arg(*argsp, delegate_fn4_t); + msg_t p1 = (msg_t)va_arg(*argsp, msg_t); + msg_t p2 = (msg_t)va_arg(*argsp, msg_t); + msg_t p3 = (msg_t)va_arg(*argsp, msg_t); + msg_t p4 = (msg_t)va_arg(*argsp, msg_t); + return fn4(p1, p2, p3, p4); +} + +/** + * @brief Triggers a function call on a delegate thread. + * @note The thread must be executing @p chDelegateDispatchTimeout() in + * order to have the functions called. + * + * @param[in] tp pointer to the delegate thread + * @param[in] veneer pointer to the veneer function to be called + * @param[in] ... variable number of parameters + * @return The function return value casted to msg_t. It is + * garbage for functions returning @p void. + */ +msg_t chDelegateCallVeneer(thread_t *tp, delegate_veneer_t veneer, ...) { + va_list args; + call_message_t cm; + msg_t msg; + + va_start(args, veneer); + + /* Preparing the call message.*/ + cm.veneer = veneer; + cm.argsp = &args; + (void)cm; /* Suppresses a lint warning.*/ + + /* Sending the message to the dispatcher thread, the return value is + contained in the returned message.*/ + msg = chMsgSend(tp, (msg_t)&cm); + + va_end(args); + + return msg; +} + +/*lint -restore*/ + +/** + * @brief Call messages dispatching. + * @details The function awaits for an incoming call messages and calls the + * specified functions, then it returns. In case multiple threads + * are sending messages then the requests are served in priority + * order. + * + * @api + */ +void chDelegateDispatch(void) { + thread_t *tp; + const call_message_t *cmp; + msg_t ret; + + tp = chMsgWait(); + cmp = (const call_message_t *)chMsgGet(tp); + ret = cmp->veneer(cmp->argsp); + + chMsgRelease(tp, ret); +} + +/** + * @brief Call messages dispatching with timeout. + * @details The function awaits for an incoming call messages and calls the + * specified functions, then it returns. In case multiple threads + * are sending messages then the requests are served in priority + * order. + * + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The function outcome. + * @retval MSG_OK if a function has been called. + * @retval MSG_TIMEOUT if a timeout occurred. + * + * @api + */ +msg_t chDelegateDispatchTimeout(sysinterval_t timeout) { + thread_t *tp; + const call_message_t *cmp; + msg_t ret; + + tp = chMsgWaitTimeout(timeout); + if (tp == NULL) { + return MSG_TIMEOUT; + } + + cmp = (const call_message_t *)chMsgGet(tp); + ret = cmp->veneer(cmp->argsp); + + chMsgRelease(tp, ret); + + return MSG_OK; +} + +#endif /* CH_CFG_USE_DELEGATES == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chfactory.c b/ChibiOS_20.3.2/os/oslib/src/chfactory.c new file mode 100644 index 0000000..aaad90b --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chfactory.c @@ -0,0 +1,811 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chfactory.c + * @brief ChibiOS objects factory and registry code. + * + * @addtogroup oslib_objects_factory + * @details The object factory is a subsystem that allows to: + * - Register static objects by name. + * - Dynamically create objects and assign them a name. + * - Retrieve existing objects by name. + * - Free objects by reference. + * . + * Allocated OS objects are handled using a reference counter, only + * when all references have been released then the object memory is + * freed in a pool.
+ * @pre This subsystem requires the @p CH_CFG_USE_MEMCORE and + * @p CH_CFG_USE_MEMPOOLS options to be set to @p TRUE. The + * option @p CH_CFG_USE_HEAP is also required if the support + * for variable length objects is enabled. + * @note Compatible with RT and NIL. + * @{ + */ + +#include + +#include "ch.h" + +#if (CH_CFG_USE_FACTORY == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module local definitions. */ +/*===========================================================================*/ + +/* + * Defaults on the best synchronization mechanism available. + */ +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) +#define F_LOCK() chMtxLock(&ch_factory.mtx) +#define F_UNLOCK() chMtxUnlock(&ch_factory.mtx) +#else +#define F_LOCK() (void) chSemWait(&ch_factory.sem) +#define F_UNLOCK() chSemSignal(&ch_factory.sem) +#endif + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/** + * @brief Factory object static instance. + * @note It is a global object because it could be accessed through + * a specific debugger plugin. + */ +objects_factory_t ch_factory; + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +static inline void dyn_list_init(dyn_list_t *dlp) { + + dlp->next = (dyn_element_t *)dlp; +} + +static dyn_element_t *dyn_list_find(const char *name, dyn_list_t *dlp) { + dyn_element_t *p = dlp->next; + + while (p != (dyn_element_t *)dlp) { + if (strncmp(p->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH) == 0) { + return p; + } + p = p->next; + } + + return NULL; +} + +static dyn_element_t *dyn_list_unlink(dyn_element_t *element, + dyn_list_t *dlp) { + dyn_element_t *prev = (dyn_element_t *)dlp; + + /* Scanning the list.*/ + while (prev->next != (dyn_element_t *)dlp) { + if (prev->next == element) { + /* Found.*/ + prev->next = element->next; + return element; + } + + /* Next element in the list.*/ + prev = prev->next; + } + + return NULL; +} + +#if CH_FACTORY_REQUIRES_HEAP || defined(__DOXYGEN__) +static dyn_element_t *dyn_create_object_heap(const char *name, + dyn_list_t *dlp, + size_t size) { + dyn_element_t *dep; + + chDbgCheck(name != NULL); + + /* Checking if an object with this name has already been created.*/ + dep = dyn_list_find(name, dlp); + if (dep != NULL) { + return NULL; + } + + /* Allocating space for the new buffer object.*/ + /*lint -save -e668 [] Lint is confused by the above chDbgCheck() and + incorrectly assumes that strncpy() could receive a NULL pointer.*/ + dep = (dyn_element_t *)chHeapAlloc(NULL, size); + if (dep == NULL) { + return NULL; + } + + /* Initializing object list element.*/ + strncpy(dep->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH); + /*lint -restore*/ + dep->refs = (ucnt_t)1; + dep->next = dlp->next; + + /* Updating factory list.*/ + dlp->next = dep; + + return dep; +} + +static void dyn_release_object_heap(dyn_element_t *dep, + dyn_list_t *dlp) { + + chDbgCheck(dep != NULL); + chDbgAssert(dep->refs > (ucnt_t)0, "invalid references number"); + + dep->refs--; + if (dep->refs == (ucnt_t)0) { + dep = dyn_list_unlink(dep, dlp); + chHeapFree((void *)dep); + } +} +#endif /* CH_FACTORY_REQUIRES_HEAP */ + +#if CH_FACTORY_REQUIRES_POOLS || defined(__DOXYGEN__) +static dyn_element_t *dyn_create_object_pool(const char *name, + dyn_list_t *dlp, + memory_pool_t *mp) { + dyn_element_t *dep; + + chDbgCheck(name != NULL); + + /* Checking if an object object with this name has already been created.*/ + dep = dyn_list_find(name, dlp); + if (dep != NULL) { + return NULL; + } + + /* Allocating space for the new object.*/ + dep = (dyn_element_t *)chPoolAlloc(mp); + if (dep == NULL) { + return NULL; + } + + /* Initializing object list element.*/ + /*lint -save -e668 [] Lint is confused by the above chDbgCheck() and + incorrectly assumes that strncpy() could receive a NULL pointer.*/ + strncpy(dep->name, name, CH_CFG_FACTORY_MAX_NAMES_LENGTH); + /*lint -restore*/ + dep->refs = (ucnt_t)1; + dep->next = dlp->next; + + /* Updating factory list.*/ + dlp->next = (dyn_element_t *)dep; + + return dep; +} + +static void dyn_release_object_pool(dyn_element_t *dep, + dyn_list_t *dlp, + memory_pool_t *mp) { + + chDbgCheck(dep != NULL); + chDbgAssert(dep->refs > (ucnt_t)0, "invalid references number"); + + dep->refs--; + if (dep->refs == (ucnt_t)0) { + dep = dyn_list_unlink(dep, dlp); + chPoolFree(mp, (void *)dep); + } +} +#endif /* CH_FACTORY_REQUIRES_POOLS */ + +static dyn_element_t *dyn_find_object(const char *name, dyn_list_t *dlp) { + dyn_element_t *dep; + + chDbgCheck(name != NULL); + + /* Checking if an object with this name has already been created.*/ + dep = dyn_list_find(name, dlp); + if (dep != NULL) { + /* Increasing references counter.*/ + dep->refs++; + } + + return dep; +} + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes the objects factory. + * + * @init + */ +void _factory_init(void) { + +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) + chMtxObjectInit(&ch_factory.mtx); +#else + chSemObjectInit(&ch_factory.sem, (cnt_t)1); +#endif + +#if CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE + dyn_list_init(&ch_factory.obj_list); + chPoolObjectInit(&ch_factory.obj_pool, + sizeof (registered_object_t), + chCoreAllocAlignedI); +#endif +#if CH_CFG_FACTORY_GENERIC_BUFFERS == TRUE + dyn_list_init(&ch_factory.buf_list); +#endif +#if CH_CFG_FACTORY_SEMAPHORES == TRUE + dyn_list_init(&ch_factory.sem_list); + chPoolObjectInit(&ch_factory.sem_pool, + sizeof (dyn_semaphore_t), + chCoreAllocAlignedI); +#endif +#if CH_CFG_FACTORY_MAILBOXES == TRUE + dyn_list_init(&ch_factory.mbx_list); +#endif +#if CH_CFG_FACTORY_OBJ_FIFOS == TRUE + dyn_list_init(&ch_factory.fifo_list); +#endif +#if CH_CFG_FACTORY_PIPES == TRUE + dyn_list_init(&ch_factory.pipe_list); +#endif +} + +#if (CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE) || defined(__DOXIGEN__) +/** + * @brief Registers a generic object. + * @post A reference to the registered object is returned and the + * reference counter is initialized to one. + * + * @param[in] name name to be assigned to the registered object + * @param[in] objp pointer to the object to be registered + * + * @return The reference to the registered object. + * @retval NULL if the object to be registered cannot be allocated or + * a registered object with the same name exists. + * + * @api + */ +registered_object_t *chFactoryRegisterObject(const char *name, + void *objp) { + registered_object_t *rop; + + F_LOCK(); + + rop = (registered_object_t *)dyn_create_object_pool(name, + &ch_factory.obj_list, + &ch_factory.obj_pool); + if (rop != NULL) { + /* Initializing registered object data.*/ + rop->objp = objp; + } + + F_UNLOCK(); + + return rop; +} + +/** + * @brief Retrieves a registered object. + * @post A reference to the registered object is returned with the + * reference counter increased by one. + * + * @param[in] name name of the registered object + * + * @return The reference to the found registered object. + * @retval NULL if a registered object with the specified name + * does not exist. + * + * @api + */ +registered_object_t *chFactoryFindObject(const char *name) { + registered_object_t *rop; + + F_LOCK(); + + rop = (registered_object_t *)dyn_find_object(name, &ch_factory.obj_list); + + F_UNLOCK(); + + return rop; +} + +/** + * @brief Retrieves a registered object by pointer. + * @post A reference to the registered object is returned with the + * reference counter increased by one. + * + * @param[in] objp pointer to the object to be retrieved + * + * @return The reference to the found registered object. + * @retval NULL if a registered object with the specified pointer + * does not exist. + * + * @api + */ +registered_object_t *chFactoryFindObjectByPointer(void *objp) { + registered_object_t *rop = (registered_object_t *)ch_factory.obj_list.next; + + F_LOCK(); + + while ((void *)rop != (void *)&ch_factory.obj_list) { + if (rop->objp == objp) { + rop->element.refs++; + + F_UNLOCK(); + + return rop; + } + rop = (registered_object_t *)rop->element.next; + } + + F_UNLOCK(); + + return NULL; +} + +/** + * @brief Releases a registered object. + * @details The reference counter of the registered object is decreased + * by one, if reaches zero then the registered object memory + * is freed. + * @note The object itself is not freed, it could be static, only the + * allocated list element is freed. + * + * @param[in] rop registered object reference + * + * @api + */ +void chFactoryReleaseObject(registered_object_t *rop) { + + F_LOCK(); + + dyn_release_object_pool(&rop->element, + &ch_factory.obj_list, + &ch_factory.obj_pool); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_OBJECTS_REGISTRY == TRUE */ + +#if (CH_CFG_FACTORY_GENERIC_BUFFERS == TRUE) || defined(__DOXIGEN__) +/** + * @brief Creates a generic dynamic buffer object. + * @post A reference to the dynamic buffer object is returned and the + * reference counter is initialized to one. + * @post The dynamic buffer object is filled with zeros. + * + * @param[in] name name to be assigned to the new dynamic buffer object + * @param[in] size payload size of the dynamic buffer object to be created + * + * @return The reference to the created dynamic buffer object. + * @retval NULL if the dynamic buffer object cannot be allocated or + * a dynamic buffer object with the same name exists. + * + * @api + */ +dyn_buffer_t *chFactoryCreateBuffer(const char *name, size_t size) { + dyn_buffer_t *dbp; + + F_LOCK(); + + dbp = (dyn_buffer_t *)dyn_create_object_heap(name, + &ch_factory.buf_list, + size); + if (dbp != NULL) { + /* Initializing buffer object data.*/ + memset((void *)(dbp + 1), 0, size); + } + + F_UNLOCK(); + + return dbp; +} + +/** + * @brief Retrieves a dynamic buffer object. + * @post A reference to the dynamic buffer object is returned with the + * reference counter increased by one. + * + * @param[in] name name of the dynamic buffer object + * + * @return The reference to the found dynamic buffer object. + * @retval NULL if a dynamic buffer object with the specified name + * does not exist. + * + * @api + */ +dyn_buffer_t *chFactoryFindBuffer(const char *name) { + dyn_buffer_t *dbp; + + F_LOCK(); + + dbp = (dyn_buffer_t *)dyn_find_object(name, &ch_factory.buf_list); + + F_UNLOCK(); + + return dbp; +} + +/** + * @brief Releases a dynamic buffer object. + * @details The reference counter of the dynamic buffer object is decreased + * by one, if reaches zero then the dynamic buffer object memory + * is freed. + * + * @param[in] dbp dynamic buffer object reference + * + * @api + */ +void chFactoryReleaseBuffer(dyn_buffer_t *dbp) { + + F_LOCK(); + + dyn_release_object_heap(&dbp->element, &ch_factory.buf_list); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_GENERIC_BUFFERS = TRUE */ + +#if (CH_CFG_FACTORY_SEMAPHORES == TRUE) || defined(__DOXIGEN__) +/** + * @brief Creates a dynamic semaphore object. + * @post A reference to the dynamic semaphore object is returned and the + * reference counter is initialized to one. + * @post The dynamic semaphore object is initialized and ready to use. + * + * @param[in] name name to be assigned to the new dynamic semaphore object + * @param[in] n dynamic semaphore object counter initialization value + * + * @return The reference to the created dynamic semaphore object. + * @retval NULL if the dynamic semaphore object cannot be allocated or + * a dynamic semaphore with the same name exists. + * + * @api + */ +dyn_semaphore_t *chFactoryCreateSemaphore(const char *name, cnt_t n) { + dyn_semaphore_t *dsp; + + F_LOCK(); + + dsp = (dyn_semaphore_t *)dyn_create_object_pool(name, + &ch_factory.sem_list, + &ch_factory.sem_pool); + if (dsp != NULL) { + /* Initializing semaphore object dataa.*/ + chSemObjectInit(&dsp->sem, n); + } + + F_UNLOCK(); + + return dsp; +} + +/** + * @brief Retrieves a dynamic semaphore object. + * @post A reference to the dynamic semaphore object is returned with the + * reference counter increased by one. + * + * @param[in] name name of the dynamic semaphore object + * + * @return The reference to the found dynamic semaphore object. + * @retval NULL if a dynamic semaphore object with the specified name + * does not exist. + * + * @api + */ +dyn_semaphore_t *chFactoryFindSemaphore(const char *name) { + dyn_semaphore_t *dsp; + + F_LOCK(); + + dsp = (dyn_semaphore_t *)dyn_find_object(name, &ch_factory.sem_list); + + F_UNLOCK(); + + return dsp; +} + +/** + * @brief Releases a dynamic semaphore object. + * @details The reference counter of the dynamic semaphore object is decreased + * by one, if reaches zero then the dynamic semaphore object memory + * is freed. + * + * @param[in] dsp dynamic semaphore object reference + * + * @api + */ +void chFactoryReleaseSemaphore(dyn_semaphore_t *dsp) { + + F_LOCK(); + + dyn_release_object_pool(&dsp->element, + &ch_factory.sem_list, + &ch_factory.sem_pool); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_SEMAPHORES = TRUE */ + +#if (CH_CFG_FACTORY_MAILBOXES == TRUE) || defined(__DOXIGEN__) +/** + * @brief Creates a dynamic mailbox object. + * @post A reference to the dynamic mailbox object is returned and the + * reference counter is initialized to one. + * @post The dynamic mailbox object is initialized and ready to use. + * + * @param[in] name name to be assigned to the new dynamic mailbox object + * @param[in] n mailbox buffer size as number of messages + * + * @return The reference to the created dynamic mailbox object. + * @retval NULL if the dynamic mailbox object cannot be allocated or + * a dynamic mailbox object with the same name exists. + * + * @api + */ +dyn_mailbox_t *chFactoryCreateMailbox(const char *name, size_t n) { + dyn_mailbox_t *dmp; + + F_LOCK(); + + dmp = (dyn_mailbox_t *)dyn_create_object_heap(name, + &ch_factory.mbx_list, + sizeof (dyn_mailbox_t) + + (n * sizeof (msg_t))); + if (dmp != NULL) { + /* Initializing mailbox object data.*/ + chMBObjectInit(&dmp->mbx, (msg_t *)(dmp + 1), n); + } + + F_UNLOCK(); + + return dmp; +} + +/** + * @brief Retrieves a dynamic mailbox object. + * @post A reference to the dynamic mailbox object is returned with the + * reference counter increased by one. + * + * @param[in] name name of the dynamic mailbox object + * + * @return The reference to the found dynamic mailbox object. + * @retval NULL if a dynamic mailbox object with the specified name + * does not exist. + * + * @api + */ +dyn_mailbox_t *chFactoryFindMailbox(const char *name) { + dyn_mailbox_t *dmp; + + F_LOCK(); + + dmp = (dyn_mailbox_t *)dyn_find_object(name, &ch_factory.mbx_list); + + F_UNLOCK(); + + return dmp; +} + +/** + * @brief Releases a dynamic mailbox object. + * @details The reference counter of the dynamic mailbox object is decreased + * by one, if reaches zero then the dynamic mailbox object memory + * is freed. + * + * @param[in] dmp dynamic mailbox object reference + * + * @api + */ +void chFactoryReleaseMailbox(dyn_mailbox_t *dmp) { + + F_LOCK(); + + dyn_release_object_heap(&dmp->element, &ch_factory.mbx_list); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_MAILBOXES = TRUE */ + +#if (CH_CFG_FACTORY_OBJ_FIFOS == TRUE) || defined(__DOXIGEN__) +/** + * @brief Creates a dynamic "objects FIFO" object. + * @post A reference to the dynamic "objects FIFO" object is returned and + * the reference counter is initialized to one. + * @post The dynamic "objects FIFO" object is initialized and ready to use. + * + * @param[in] name name to be assigned to the new dynamic "objects FIFO" + * object + * @param[in] objsize size of objects + * @param[in] objn number of objects available + * @param[in] objalign required objects alignment + * @return The reference to the created dynamic "objects FIFO" + * object. + * @retval NULL if the dynamic "objects FIFO" object cannot be + * allocated or a dynamic "objects FIFO" object with + * the same name exists. + * + * @api + */ +dyn_objects_fifo_t *chFactoryCreateObjectsFIFO(const char *name, + size_t objsize, + size_t objn, + unsigned objalign) { + dyn_objects_fifo_t *dofp; + + F_LOCK(); + + dofp = (dyn_objects_fifo_t *)dyn_create_object_heap(name, + &ch_factory.fifo_list, + sizeof (dyn_objects_fifo_t) + + (objn * sizeof (msg_t)) + + (objn * objsize)); + if (dofp != NULL) { + msg_t *msgbuf = (msg_t *)(dofp + 1); + + /* Initializing mailbox object data.*/ + chFifoObjectInitAligned(&dofp->fifo, objsize, objn, objalign, + (void *)&msgbuf[objn], msgbuf); + } + + F_UNLOCK(); + + return dofp; +} + +/** + * @brief Retrieves a dynamic "objects FIFO" object. + * @post A reference to the dynamic "objects FIFO" object is returned with + * the reference counter increased by one. + * + * @param[in] name name of the dynamic "objects FIFO" object + * + * @return The reference to the found dynamic "objects FIFO" + * object. + * @retval NULL if a dynamic "objects FIFO" object with the specified + * name does not exist. + * + * @api + */ +dyn_objects_fifo_t *chFactoryFindObjectsFIFO(const char *name) { + dyn_objects_fifo_t *dofp; + + F_LOCK(); + + dofp = (dyn_objects_fifo_t *)dyn_find_object(name, &ch_factory.fifo_list); + + F_UNLOCK(); + + return dofp; +} + +/** + * @brief Releases a dynamic "objects FIFO" object. + * @details The reference counter of the dynamic "objects FIFO" object is + * decreased by one, if reaches zero then the dynamic "objects FIFO" + * object memory is freed. + * + * @param[in] dofp dynamic "objects FIFO" object reference + * + * @api + */ +void chFactoryReleaseObjectsFIFO(dyn_objects_fifo_t *dofp) { + + F_LOCK(); + + dyn_release_object_heap(&dofp->element, &ch_factory.fifo_list); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_OBJ_FIFOS = TRUE */ + +#if (CH_CFG_FACTORY_PIPES == TRUE) || defined(__DOXIGEN__) +/** + * @brief Creates a dynamic pipe object. + * @post A reference to the dynamic pipe object is returned and + * the reference counter is initialized to one. + * @post The dynamic pipe object is initialized and ready to use. + * + * @param[in] name name to be assigned to the new dynamic pipe + * object + * @param[in] size pipe buffer size + * @return The reference to the created dynamic pipe + * object. + * @retval NULL if the dynamic pipe object cannot be + * allocated or a dynamic pipe object with + * the same name exists. + * + * @api + */ +dyn_pipe_t *chFactoryCreatePipe(const char *name, size_t size) { + dyn_pipe_t *dpp; + + F_LOCK(); + + dpp = (dyn_pipe_t *)dyn_create_object_heap(name, + &ch_factory.pipe_list, + sizeof (dyn_pipe_t) + size); + if (dpp != NULL) { + /* Initializing mailbox object data.*/ + chPipeObjectInit(&dpp->pipe, (uint8_t *)(dpp + 1), size); + } + + F_UNLOCK(); + + return dpp; +} + +/** + * @brief Retrieves a dynamic pipe object. + * @post A reference to the dynamic pipe object is returned with + * the reference counter increased by one. + * + * @param[in] name name of the pipe object + * + * @return The reference to the found dynamic pipe + * object. + * @retval NULL if a dynamic pipe object with the specified + * name does not exist. + * + * @api + */ +dyn_pipe_t *chFactoryFindPipe(const char *name) { + dyn_pipe_t *dpp; + + F_LOCK(); + + dpp = (dyn_pipe_t *)dyn_find_object(name, &ch_factory.pipe_list); + + F_UNLOCK(); + + return dpp; +} + +/** + * @brief Releases a dynamic pipe object. + * @details The reference counter of the dynamic pipe object is + * decreased by one, if reaches zero then the dynamic pipe + * object memory is freed. + * + * @param[in] dpp dynamic pipe object reference + * + * @api + */ +void chFactoryReleasePipe(dyn_pipe_t *dpp) { + + F_LOCK(); + + dyn_release_object_heap(&dpp->element, &ch_factory.pipe_list); + + F_UNLOCK(); +} +#endif /* CH_CFG_FACTORY_PIPES = TRUE */ + +#endif /* CH_CFG_USE_FACTORY == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chmboxes.c b/ChibiOS_20.3.2/os/oslib/src/chmboxes.c new file mode 100644 index 0000000..a96c07d --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chmboxes.c @@ -0,0 +1,522 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chmboxes.c + * @brief Mailboxes code. + * + * @addtogroup oslib_mailboxes + * @details Asynchronous messages. + *

Operation mode

+ * A mailbox is an asynchronous communication mechanism.
+ * Operations defined for mailboxes: + * - Post: Posts a message on the mailbox in FIFO order. + * - Post Ahead: Posts a message on the mailbox with urgent + * priority. + * - Fetch: A message is fetched from the mailbox and removed + * from the queue. + * - Reset: The mailbox is emptied and all the stored messages + * are lost. + * . + * A message is a variable of type msg_t that is guaranteed to have + * the same size of and be compatible with (data) pointers (anyway an + * explicit cast is needed). + * If larger messages need to be exchanged then a pointer to a + * structure can be posted in the mailbox but the posting side has + * no predefined way to know when the message has been processed. A + * possible approach is to allocate memory (from a memory pool for + * example) from the posting side and free it on the fetching side. + * Another approach is to set a "done" flag into the structure pointed + * by the message. + * @pre In order to use the mailboxes APIs the @p CH_CFG_USE_MAILBOXES + * option must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_MAILBOXES == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes a @p mailbox_t object. + * + * @param[out] mbp the pointer to the @p mailbox_t structure to be + * initialized + * @param[in] buf pointer to the messages buffer as an array of @p msg_t + * @param[in] n number of elements in the buffer array + * + * @init + */ +void chMBObjectInit(mailbox_t *mbp, msg_t *buf, size_t n) { + + chDbgCheck((mbp != NULL) && (buf != NULL) && (n > (size_t)0)); + + mbp->buffer = buf; + mbp->rdptr = buf; + mbp->wrptr = buf; + mbp->top = &buf[n]; + mbp->cnt = (size_t)0; + mbp->reset = false; + chThdQueueObjectInit(&mbp->qw); + chThdQueueObjectInit(&mbp->qr); +} + +/** + * @brief Resets a @p mailbox_t object. + * @details All the waiting threads are resumed with status @p MSG_RESET and + * the queued messages are lost. + * @post The mailbox is in reset state, all operations will fail and + * return @p MSG_RESET until the mailbox is enabled again using + * @p chMBResumeX(). + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * + * @api + */ +void chMBReset(mailbox_t *mbp) { + + chSysLock(); + chMBResetI(mbp); + chSchRescheduleS(); + chSysUnlock(); +} + +/** + * @brief Resets a @p mailbox_t object. + * @details All the waiting threads are resumed with status @p MSG_RESET and + * the queued messages are lost. + * @post The mailbox is in reset state, all operations will fail and + * return @p MSG_RESET until the mailbox is enabled again using + * @p chMBResumeX(). + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * + * @api + */ +void chMBResetI(mailbox_t *mbp) { + + chDbgCheckClassI(); + chDbgCheck(mbp != NULL); + + mbp->wrptr = mbp->buffer; + mbp->rdptr = mbp->buffer; + mbp->cnt = (size_t)0; + mbp->reset = true; + chThdDequeueAllI(&mbp->qw, MSG_RESET); + chThdDequeueAllI(&mbp->qr, MSG_RESET); +} + +/** + * @brief Posts a message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @api + */ +msg_t chMBPostTimeout(mailbox_t *mbp, msg_t msg, sysinterval_t timeout) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBPostTimeoutS(mbp, msg, timeout); + chSysUnlock(); + + return rdymsg; +} + +/** + * @brief Posts a message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @sclass + */ +msg_t chMBPostTimeoutS(mailbox_t *mbp, msg_t msg, sysinterval_t timeout) { + msg_t rdymsg; + + chDbgCheckClassS(); + chDbgCheck(mbp != NULL); + + do { + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a free message slot in queue? if so then post.*/ + if (chMBGetFreeCountI(mbp) > (size_t)0) { + *mbp->wrptr++ = msg; + if (mbp->wrptr >= mbp->top) { + mbp->wrptr = mbp->buffer; + } + mbp->cnt++; + + /* If there is a reader waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qr, MSG_OK); + chSchRescheduleS(); + + return MSG_OK; + } + + /* No space in the queue, waiting for a slot to become available.*/ + rdymsg = chThdEnqueueTimeoutS(&mbp->qw, timeout); + } while (rdymsg == MSG_OK); + + return rdymsg; +} + +/** + * @brief Posts a message into a mailbox. + * @details This variant is non-blocking, the function returns a timeout + * condition if the queue is full. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the mailbox is full and the message cannot be + * posted. + * + * @iclass + */ +msg_t chMBPostI(mailbox_t *mbp, msg_t msg) { + + chDbgCheckClassI(); + chDbgCheck(mbp != NULL); + + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a free message slot in queue? if so then post.*/ + if (chMBGetFreeCountI(mbp) > (size_t)0) { + *mbp->wrptr++ = msg; + if (mbp->wrptr >= mbp->top) { + mbp->wrptr = mbp->buffer; + } + mbp->cnt++; + + /* If there is a reader waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qr, MSG_OK); + + return MSG_OK; + } + + /* No space, immediate timeout.*/ + return MSG_TIMEOUT; +} + +/** + * @brief Posts an high priority message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @api + */ +msg_t chMBPostAheadTimeout(mailbox_t *mbp, msg_t msg, sysinterval_t timeout) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBPostAheadTimeoutS(mbp, msg, timeout); + chSysUnlock(); + + return rdymsg; +} + +/** + * @brief Posts an high priority message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @sclass + */ +msg_t chMBPostAheadTimeoutS(mailbox_t *mbp, msg_t msg, sysinterval_t timeout) { + msg_t rdymsg; + + chDbgCheckClassS(); + chDbgCheck(mbp != NULL); + + do { + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a free message slot in queue? if so then post.*/ + if (chMBGetFreeCountI(mbp) > (size_t)0) { + if (--mbp->rdptr < mbp->buffer) { + mbp->rdptr = mbp->top - 1; + } + *mbp->rdptr = msg; + mbp->cnt++; + + /* If there is a reader waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qr, MSG_OK); + chSchRescheduleS(); + + return MSG_OK; + } + + /* No space in the queue, waiting for a slot to become available.*/ + rdymsg = chThdEnqueueTimeoutS(&mbp->qw, timeout); + } while (rdymsg == MSG_OK); + + return rdymsg; +} + +/** + * @brief Posts an high priority message into a mailbox. + * @details This variant is non-blocking, the function returns a timeout + * condition if the queue is full. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[in] msg the message to be posted on the mailbox + * @return The operation status. + * @retval MSG_OK if a message has been correctly posted. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the mailbox is full and the message cannot be + * posted. + * + * @iclass + */ +msg_t chMBPostAheadI(mailbox_t *mbp, msg_t msg) { + + chDbgCheckClassI(); + chDbgCheck(mbp != NULL); + + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a free message slot in queue? if so then post.*/ + if (chMBGetFreeCountI(mbp) > (size_t)0) { + if (--mbp->rdptr < mbp->buffer) { + mbp->rdptr = mbp->top - 1; + } + *mbp->rdptr = msg; + mbp->cnt++; + + /* If there is a reader waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qr, MSG_OK); + + return MSG_OK; + } + + /* No space, immediate timeout.*/ + return MSG_TIMEOUT; +} + +/** + * @brief Retrieves a message from a mailbox. + * @details The invoking thread waits until a message is posted in the mailbox + * or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[out] msgp pointer to a message variable for the received message + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly fetched. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @api + */ +msg_t chMBFetchTimeout(mailbox_t *mbp, msg_t *msgp, sysinterval_t timeout) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBFetchTimeoutS(mbp, msgp, timeout); + chSysUnlock(); + + return rdymsg; +} + +/** + * @brief Retrieves a message from a mailbox. + * @details The invoking thread waits until a message is posted in the mailbox + * or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[out] msgp pointer to a message variable for the received message + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if a message has been correctly fetched. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the operation has timed out. + * + * @sclass + */ +msg_t chMBFetchTimeoutS(mailbox_t *mbp, msg_t *msgp, sysinterval_t timeout) { + msg_t rdymsg; + + chDbgCheckClassS(); + chDbgCheck((mbp != NULL) && (msgp != NULL)); + + do { + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a message in queue? if so then fetch.*/ + if (chMBGetUsedCountI(mbp) > (size_t)0) { + *msgp = *mbp->rdptr++; + if (mbp->rdptr >= mbp->top) { + mbp->rdptr = mbp->buffer; + } + mbp->cnt--; + + /* If there is a writer waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qw, MSG_OK); + chSchRescheduleS(); + + return MSG_OK; + } + + /* No message in the queue, waiting for a message to become available.*/ + rdymsg = chThdEnqueueTimeoutS(&mbp->qr, timeout); + } while (rdymsg == MSG_OK); + + return rdymsg; +} + +/** + * @brief Retrieves a message from a mailbox. + * @details This variant is non-blocking, the function returns a timeout + * condition if the queue is empty. + * + * @param[in] mbp the pointer to an initialized @p mailbox_t object + * @param[out] msgp pointer to a message variable for the received message + * @return The operation status. + * @retval MSG_OK if a message has been correctly fetched. + * @retval MSG_RESET if the mailbox has been reset. + * @retval MSG_TIMEOUT if the mailbox is empty and a message cannot be + * fetched. + * + * @iclass + */ +msg_t chMBFetchI(mailbox_t *mbp, msg_t *msgp) { + + chDbgCheckClassI(); + chDbgCheck((mbp != NULL) && (msgp != NULL)); + + /* If the mailbox is in reset state then returns immediately.*/ + if (mbp->reset) { + return MSG_RESET; + } + + /* Is there a message in queue? if so then fetch.*/ + if (chMBGetUsedCountI(mbp) > (size_t)0) { + *msgp = *mbp->rdptr++; + if (mbp->rdptr >= mbp->top) { + mbp->rdptr = mbp->buffer; + } + mbp->cnt--; + + /* If there is a writer waiting then makes it ready.*/ + chThdDequeueNextI(&mbp->qw, MSG_OK); + + return MSG_OK; + } + + /* No message, immediate timeout.*/ + return MSG_TIMEOUT; +} +#endif /* CH_CFG_USE_MAILBOXES == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chmemcore.c b/ChibiOS_20.3.2/os/oslib/src/chmemcore.c new file mode 100644 index 0000000..3a61955 --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chmemcore.c @@ -0,0 +1,227 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chmemcore.c + * @brief Core memory manager code. + * + * @addtogroup oslib_memcore + * @details Core Memory Manager related APIs and services. + *

Operation mode

+ * The core memory manager is a simplified allocator that only + * allows to allocate memory blocks without the possibility to + * free them.
+ * This allocator is meant as a memory blocks provider for the + * other allocators such as: + * - C-Runtime allocator (through a compiler specific adapter module). + * - Heap allocator (see @ref oslib_memheaps). + * - Memory pools allocator (see @ref oslib_mempools). + * . + * By having a centralized memory provider the various allocators + * can coexist and share the main memory.
+ * This allocator, alone, is also useful for very simple + * applications that just require a simple way to get memory + * blocks. + * @pre In order to use the core memory manager APIs the @p CH_CFG_USE_MEMCORE + * option must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_MEMCORE == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/** + * @brief Memory core descriptor. + */ +memcore_t ch_memcore; + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level memory manager initialization. + * + * @notapi + */ +void _core_init(void) { +#if CH_CFG_MEMCORE_SIZE == 0 + extern uint8_t __heap_base__[]; + extern uint8_t __heap_end__[]; + + /*lint -save -e9033 [10.8] Required cast operations.*/ + ch_memcore.basemem = __heap_base__; + ch_memcore.topmem = __heap_end__; + /*lint restore*/ +#else + static uint8_t static_heap[CH_CFG_MEMCORE_SIZE]; + + ch_memcore.basemem = &static_heap[0]; + ch_memcore.topmem = &static_heap[CH_CFG_MEMCORE_SIZE]; +#endif +} + +/** + * @brief Allocates a memory block starting from the lowest address upward. + * @details This function allocates a block of @p offset + @p size bytes. The + * returned pointer has @p offset bytes before its address and + * @p size bytes after. + * + * @param[in] size the size of the block to be allocated. + * @param[in] align desired memory alignment + * @param[in] offset aligned pointer offset + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + * + * @iclass + */ +void *chCoreAllocFromBaseI(size_t size, unsigned align, size_t offset) { + uint8_t *p, *next; + + chDbgCheckClassI(); + chDbgCheck(MEM_IS_VALID_ALIGNMENT(align)); + + p = (uint8_t *)MEM_ALIGN_NEXT(ch_memcore.basemem + offset, align); + next = p + size; + + /* Considering also the case where there is numeric overflow.*/ + if ((next > ch_memcore.topmem) || (next < ch_memcore.basemem)) { + return NULL; + } + + ch_memcore.basemem = next; + + return p; +} + +/** + * @brief Allocates a memory block starting from the top address downward. + * @details This function allocates a block of @p offset + @p size bytes. The + * returned pointer has @p offset bytes before its address and + * @p size bytes after. + * + * @param[in] size the size of the block to be allocated. + * @param[in] align desired memory alignment + * @param[in] offset aligned pointer offset + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + * + * @iclass + */ +void *chCoreAllocFromTopI(size_t size, unsigned align, size_t offset) { + uint8_t *p, *prev; + + chDbgCheckClassI(); + chDbgCheck(MEM_IS_VALID_ALIGNMENT(align)); + + p = (uint8_t *)MEM_ALIGN_PREV(ch_memcore.topmem - size, align); + prev = p - offset; + + /* Considering also the case where there is numeric overflow.*/ + if ((prev < ch_memcore.basemem) || (prev > ch_memcore.topmem)) { + return NULL; + } + + ch_memcore.topmem = prev; + + return p; +} + +/** + * @brief Allocates a memory block starting from the lowest address upward. + * @details This function allocates a block of @p offset + @p size bytes. The + * returned pointer has @p offset bytes before its address and + * @p size bytes after. + * + * @param[in] size the size of the block to be allocated. + * @param[in] align desired memory alignment + * @param[in] offset aligned pointer offset + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + * + * @api + */ +void *chCoreAllocFromBase(size_t size, unsigned align, size_t offset) { + void *p; + + chSysLock(); + p = chCoreAllocFromBaseI(size, align, offset); + chSysUnlock(); + + return p; +} + +/** + * @brief Allocates a memory block starting from the top address downward. + * @details This function allocates a block of @p offset + @p size bytes. The + * returned pointer has @p offset bytes before its address and + * @p size bytes after. + * + * @param[in] size the size of the block to be allocated. + * @param[in] align desired memory alignment + * @param[in] offset aligned pointer offset + * @return A pointer to the allocated memory block. + * @retval NULL allocation failed, core memory exhausted. + * + * @api + */ +void *chCoreAllocFromTop(size_t size, unsigned align, size_t offset) { + void *p; + + chSysLock(); + p = chCoreAllocFromTopI(size, align, offset); + chSysUnlock(); + + return p; +} + +/** + * @brief Core memory status. + * + * @return The size, in bytes, of the free core memory. + * + * @xclass + */ +size_t chCoreGetStatusX(void) { + + /*lint -save -e9033 [10.8] The cast is safe.*/ + return (size_t)(ch_memcore.topmem - ch_memcore.basemem); + /*lint -restore*/ +} +#endif /* CH_CFG_USE_MEMCORE == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chmemheaps.c b/ChibiOS_20.3.2/os/oslib/src/chmemheaps.c new file mode 100644 index 0000000..2e1f03d --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chmemheaps.c @@ -0,0 +1,399 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chmemheaps.c + * @brief Memory heaps code. + * + * @addtogroup oslib_memheaps + * @details Heap Allocator related APIs. + *

Operation mode

+ * The heap allocator implements a first-fit strategy and its APIs + * are functionally equivalent to the usual @p malloc() and @p free() + * library functions. The main difference is that the OS heap APIs + * are guaranteed to be thread safe and there is the ability to + * return memory blocks aligned to arbitrary powers of two.
+ * @pre In order to use the heap APIs the @p CH_CFG_USE_HEAP option must + * be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_HEAP == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module local definitions. */ +/*===========================================================================*/ + +/* + * Defaults on the best synchronization mechanism available. + */ +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) +#define H_LOCK(h) chMtxLock(&(h)->mtx) +#define H_UNLOCK(h) chMtxUnlock(&(h)->mtx) +#else +#define H_LOCK(h) (void) chSemWait(&(h)->sem) +#define H_UNLOCK(h) chSemSignal(&(h)->sem) +#endif + +#define H_BLOCK(hp) ((hp) + 1U) + +#define H_LIMIT(hp) (H_BLOCK(hp) + H_PAGES(hp)) + +#define H_NEXT(hp) ((hp)->free.next) + +#define H_PAGES(hp) ((hp)->free.pages) + +#define H_HEAP(hp) ((hp)->used.heap) + +#define H_SIZE(hp) ((hp)->used.size) + +/* + * Number of pages between two pointers in a MISRA-compatible way. + */ +#define NPAGES(p1, p2) \ + /*lint -save -e9033 [10.8] The cast is safe.*/ \ + ((size_t)((p1) - (p2))) \ + /*lint -restore*/ + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/** + * @brief Default heap descriptor. + */ +static memory_heap_t default_heap; + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes the default heap. + * + * @notapi + */ +void _heap_init(void) { + + default_heap.provider = chCoreAllocAlignedWithOffset; + H_NEXT(&default_heap.header) = NULL; + H_PAGES(&default_heap.header) = 0; +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) + chMtxObjectInit(&default_heap.mtx); +#else + chSemObjectInit(&default_heap.sem, (cnt_t)1); +#endif +} + +/** + * @brief Initializes a memory heap from a static memory area. + * @note The heap buffer base and size are adjusted if the passed buffer + * is not aligned to @p CH_HEAP_ALIGNMENT. This mean that the + * effective heap size can be less than @p size. + * + * @param[out] heapp pointer to the memory heap descriptor to be initialized + * @param[in] buf heap buffer base + * @param[in] size heap size + * + * @init + */ +void chHeapObjectInit(memory_heap_t *heapp, void *buf, size_t size) { + heap_header_t *hp = (heap_header_t *)MEM_ALIGN_NEXT(buf, CH_HEAP_ALIGNMENT); + + chDbgCheck((heapp != NULL) && (size > 0U)); + + /* Adjusting the size in case the initial block was not correctly + aligned.*/ + /*lint -save -e9033 [10.8] Required cast operations.*/ + size -= (size_t)((uint8_t *)hp - (uint8_t *)buf); + /*lint restore*/ + + /* Initializing the heap header.*/ + heapp->provider = NULL; + H_NEXT(&heapp->header) = hp; + H_PAGES(&heapp->header) = 0; + H_NEXT(hp) = NULL; + H_PAGES(hp) = (size - sizeof (heap_header_t)) / CH_HEAP_ALIGNMENT; +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) + chMtxObjectInit(&heapp->mtx); +#else + chSemObjectInit(&heapp->sem, (cnt_t)1); +#endif +} + +/** + * @brief Allocates a block of memory from the heap by using the first-fit + * algorithm. + * @details The allocated block is guaranteed to be properly aligned to the + * specified alignment. + * + * @param[in] heapp pointer to a heap descriptor or @p NULL in order to + * access the default heap. + * @param[in] size the size of the block to be allocated. Note that the + * allocated block may be a bit bigger than the requested + * size for alignment and fragmentation reasons. + * @param[in] align desired memory alignment + * @return A pointer to the aligned allocated block. + * @retval NULL if the block cannot be allocated. + * + * @api + */ +void *chHeapAllocAligned(memory_heap_t *heapp, size_t size, unsigned align) { + heap_header_t *qp, *hp, *ahp; + size_t pages; + + chDbgCheck((size > 0U) && MEM_IS_VALID_ALIGNMENT(align)); + + /* If an heap is not specified then the default system header is used.*/ + if (heapp == NULL) { + heapp = &default_heap; + } + + /* Minimum alignment is constrained by the heap header structure size.*/ + if (align < CH_HEAP_ALIGNMENT) { + align = CH_HEAP_ALIGNMENT; + } + + /* Size is converted in number of elementary allocation units.*/ + pages = MEM_ALIGN_NEXT(size, CH_HEAP_ALIGNMENT) / CH_HEAP_ALIGNMENT; + + /* Taking heap mutex/semaphore.*/ + H_LOCK(heapp); + + /* Start of the free blocks list.*/ + qp = &heapp->header; + while (H_NEXT(qp) != NULL) { + + /* Next free block.*/ + hp = H_NEXT(qp); + + /* Pointer aligned to the requested alignment.*/ + ahp = (heap_header_t *)MEM_ALIGN_NEXT(H_BLOCK(hp), align) - 1U; + + if ((ahp < H_LIMIT(hp)) && (pages <= NPAGES(H_LIMIT(hp), ahp + 1U))) { + /* The block is large enough to contain a correctly aligned area + of sufficient size.*/ + + if (ahp > hp) { + /* The block is not properly aligned, must split it.*/ + size_t bpages; + + bpages = NPAGES(H_LIMIT(hp), H_BLOCK(ahp)); + H_PAGES(hp) = NPAGES(ahp, H_BLOCK(hp)); + if (bpages > pages) { + /* The block is bigger than required, must split the excess.*/ + heap_header_t *fp; + + /* Creating the excess block.*/ + fp = H_BLOCK(ahp) + pages; + H_PAGES(fp) = (bpages - pages) - 1U; + + /* Linking the excess block.*/ + H_NEXT(fp) = H_NEXT(hp); + H_NEXT(hp) = fp; + } + + hp = ahp; + } + else { + /* The block is already properly aligned.*/ + + if (H_PAGES(hp) == pages) { + /* Exact size, getting the whole block.*/ + H_NEXT(qp) = H_NEXT(hp); + } + else { + /* The block is bigger than required, must split the excess.*/ + heap_header_t *fp; + + fp = H_BLOCK(hp) + pages; + H_NEXT(fp) = H_NEXT(hp); + H_PAGES(fp) = NPAGES(H_LIMIT(hp), H_BLOCK(fp)); + H_NEXT(qp) = fp; + } + } + + /* Setting in the block owner heap and size.*/ + H_SIZE(hp) = size; + H_HEAP(hp) = heapp; + + /* Releasing heap mutex/semaphore.*/ + H_UNLOCK(heapp); + + /*lint -save -e9087 [11.3] Safe cast.*/ + return (void *)H_BLOCK(hp); + /*lint -restore*/ + } + + /* Next in the free blocks list.*/ + qp = hp; + } + + /* Releasing heap mutex/semaphore.*/ + H_UNLOCK(heapp); + + /* More memory is required, tries to get it from the associated provider + else fails.*/ + if (heapp->provider != NULL) { + ahp = heapp->provider(pages * CH_HEAP_ALIGNMENT, + align, + sizeof (heap_header_t)); + if (ahp != NULL) { + hp = ahp - 1U; + H_HEAP(hp) = heapp; + H_SIZE(hp) = size; + + /*lint -save -e9087 [11.3] Safe cast.*/ + return (void *)ahp; + /*lint -restore*/ + } + } + + return NULL; +} + +/** + * @brief Frees a previously allocated memory block. + * + * @param[in] p pointer to the memory block to be freed + * + * @api + */ +void chHeapFree(void *p) { + heap_header_t *qp, *hp; + memory_heap_t *heapp; + + chDbgCheck((p != NULL) && MEM_IS_ALIGNED(p, CH_HEAP_ALIGNMENT)); + + /*lint -save -e9087 [11.3] Safe cast.*/ + hp = (heap_header_t *)p - 1U; + /*lint -restore*/ + heapp = H_HEAP(hp); + qp = &heapp->header; + + /* Size is converted in number of elementary allocation units.*/ + H_PAGES(hp) = MEM_ALIGN_NEXT(H_SIZE(hp), + CH_HEAP_ALIGNMENT) / CH_HEAP_ALIGNMENT; + + /* Taking heap mutex/semaphore.*/ + H_LOCK(heapp); + + while (true) { + chDbgAssert((hp < qp) || (hp >= H_LIMIT(qp)), "within free block"); + + if (((qp == &heapp->header) || (hp > qp)) && + ((H_NEXT(qp) == NULL) || (hp < H_NEXT(qp)))) { + /* Insertion after qp.*/ + H_NEXT(hp) = H_NEXT(qp); + H_NEXT(qp) = hp; + /* Verifies if the newly inserted block should be merged.*/ + if (H_LIMIT(hp) == H_NEXT(hp)) { + /* Merge with the next block.*/ + H_PAGES(hp) += H_PAGES(H_NEXT(hp)) + 1U; + H_NEXT(hp) = H_NEXT(H_NEXT(hp)); + } + if ((H_LIMIT(qp) == hp)) { + /* Merge with the previous block.*/ + H_PAGES(qp) += H_PAGES(hp) + 1U; + H_NEXT(qp) = H_NEXT(hp); + } + break; + } + qp = H_NEXT(qp); + } + + /* Releasing heap mutex/semaphore.*/ + H_UNLOCK(heapp); + + return; +} + +/** + * @brief Reports the heap status. + * @note This function is meant to be used in the test suite, it should + * not be really useful for the application code. + * + * @param[in] heapp pointer to a heap descriptor or @p NULL in order to + * access the default heap. + * @param[in] totalp pointer to a variable that will receive the total + * fragmented free space or @p NULL + * @param[in] largestp pointer to a variable that will receive the largest + * free free block found space or @p NULL + * @return The number of fragments in the heap. + * + * @api + */ +size_t chHeapStatus(memory_heap_t *heapp, size_t *totalp, size_t *largestp) { + heap_header_t *qp; + size_t n, tpages, lpages; + + if (heapp == NULL) { + heapp = &default_heap; + } + + H_LOCK(heapp); + tpages = 0U; + lpages = 0U; + n = 0U; + qp = &heapp->header; + while (H_NEXT(qp) != NULL) { + size_t pages = H_PAGES(H_NEXT(qp)); + + /* Updating counters.*/ + n++; + tpages += pages; + if (pages > lpages) { + lpages = pages; + } + + qp = H_NEXT(qp); + } + + /* Writing out fragmented free memory.*/ + if (totalp != NULL) { + *totalp = tpages * CH_HEAP_ALIGNMENT; + } + + /* Writing out unfragmented free memory.*/ + if (largestp != NULL) { + *largestp = lpages * CH_HEAP_ALIGNMENT; + } + H_UNLOCK(heapp); + + return n; +} + +#endif /* CH_CFG_USE_HEAP == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chmempools.c b/ChibiOS_20.3.2/os/oslib/src/chmempools.c new file mode 100644 index 0000000..1144b0b --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chmempools.c @@ -0,0 +1,336 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chmempools.c + * @brief Memory Pools code. + * + * @addtogroup oslib_mempools + * @details Memory Pools related APIs and services. + *

Operation mode

+ * The Memory Pools APIs allow to allocate/free fixed size objects in + * constant time and reliably without memory fragmentation + * problems.
+ * Memory Pools do not enforce any alignment constraint on the + * contained object however the objects must be properly aligned + * to contain a pointer to void. + * @pre In order to use the memory pools APIs the @p CH_CFG_USE_MEMPOOLS option + * must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_MEMPOOLS == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes an empty memory pool. + * + * @param[out] mp pointer to a @p memory_pool_t structure + * @param[in] size the size of the objects contained in this memory pool, + * the minimum accepted size is the size of a pointer to + * void. + * @param[in] align required memory alignment + * @param[in] provider memory provider function for the memory pool or + * @p NULL if the pool is not allowed to grow + * automatically + * + * @init + */ +void chPoolObjectInitAligned(memory_pool_t *mp, size_t size, + unsigned align, memgetfunc_t provider) { + + chDbgCheck((mp != NULL) && + (size >= sizeof(void *)) && + (align >= PORT_NATURAL_ALIGN) && + MEM_IS_VALID_ALIGNMENT(align)); + + mp->next = NULL; + mp->object_size = size; + mp->align = align; + mp->provider = provider; +} + +/** + * @brief Loads a memory pool with an array of static objects. + * @pre The memory pool must already be initialized. + * @pre The array elements must be of the right size for the specified + * memory pool. + * @pre The array elements size must be a multiple of the alignment + * requirement for the pool. + * @post The memory pool contains the elements of the input array. + * + * @param[in] mp pointer to a @p memory_pool_t structure + * @param[in] p pointer to the array first element + * @param[in] n number of elements in the array + * + * @api + */ +void chPoolLoadArray(memory_pool_t *mp, void *p, size_t n) { + + chDbgCheck((mp != NULL) && (n != 0U)); + + while (n != 0U) { + chPoolAdd(mp, p); + /*lint -save -e9087 [11.3] Safe cast.*/ + p = (void *)(((uint8_t *)p) + mp->object_size); + /*lint -restore*/ + n--; + } +} + +/** + * @brief Allocates an object from a memory pool. + * @pre The memory pool must already be initialized. + * + * @param[in] mp pointer to a @p memory_pool_t structure + * @return The pointer to the allocated object. + * @retval NULL if pool is empty. + * + * @iclass + */ +void *chPoolAllocI(memory_pool_t *mp) { + void *objp; + + chDbgCheckClassI(); + chDbgCheck(mp != NULL); + + objp = mp->next; + /*lint -save -e9013 [15.7] There is no else because it is not needed.*/ + if (objp != NULL) { + mp->next = mp->next->next; + } + else if (mp->provider != NULL) { + objp = mp->provider(mp->object_size, mp->align); + + chDbgAssert(MEM_IS_ALIGNED(objp, mp->align), + "returned object not aligned"); + } + /*lint -restore*/ + + return objp; +} + +/** + * @brief Allocates an object from a memory pool. + * @pre The memory pool must already be initialized. + * + * @param[in] mp pointer to a @p memory_pool_t structure + * @return The pointer to the allocated object. + * @retval NULL if pool is empty. + * + * @api + */ +void *chPoolAlloc(memory_pool_t *mp) { + void *objp; + + chSysLock(); + objp = chPoolAllocI(mp); + chSysUnlock(); + + return objp; +} + +/** + * @brief Releases an object into a memory pool. + * @pre The memory pool must already be initialized. + * @pre The freed object must be of the right size for the specified + * memory pool. + * @pre The added object must be properly aligned. + * + * @param[in] mp pointer to a @p memory_pool_t structure + * @param[in] objp the pointer to the object to be released + * + * @iclass + */ +void chPoolFreeI(memory_pool_t *mp, void *objp) { + struct pool_header *php = objp; + + chDbgCheckClassI(); + chDbgCheck((mp != NULL) && + (objp != NULL) && + MEM_IS_ALIGNED(objp, mp->align)); + + php->next = mp->next; + mp->next = php; +} + +/** + * @brief Releases an object into a memory pool. + * @pre The memory pool must already be initialized. + * @pre The freed object must be of the right size for the specified + * memory pool. + * @pre The added object must be properly aligned. + * + * @param[in] mp pointer to a @p memory_pool_t structure + * @param[in] objp the pointer to the object to be released + * + * @api + */ +void chPoolFree(memory_pool_t *mp, void *objp) { + + chSysLock(); + chPoolFreeI(mp, objp); + chSysUnlock(); +} + +#if (CH_CFG_USE_SEMAPHORES == TRUE) || defined(__DOXYGEN__) +/** + * @brief Initializes an empty guarded memory pool. + * + * @param[out] gmp pointer to a @p guarded_memory_pool_t structure + * @param[in] size the size of the objects contained in this guarded + * memory pool, the minimum accepted size is the size + * of a pointer to void. + * @param[in] align required memory alignment + * + * @init + */ +void chGuardedPoolObjectInitAligned(guarded_memory_pool_t *gmp, + size_t size, + unsigned align) { + + chPoolObjectInitAligned(&gmp->pool, size, align, NULL); + chSemObjectInit(&gmp->sem, (cnt_t)0); +} + +/** + * @brief Loads a guarded memory pool with an array of static objects. + * @pre The guarded memory pool must already be initialized. + * @pre The array elements must be of the right size for the specified + * guarded memory pool. + * @post The guarded memory pool contains the elements of the input array. + * + * @param[in] gmp pointer to a @p guarded_memory_pool_t structure + * @param[in] p pointer to the array first element + * @param[in] n number of elements in the array + * + * @api + */ +void chGuardedPoolLoadArray(guarded_memory_pool_t *gmp, void *p, size_t n) { + + chDbgCheck((gmp != NULL) && (n != 0U)); + + while (n != 0U) { + chGuardedPoolAdd(gmp, p); + /*lint -save -e9087 [11.3] Safe cast.*/ + p = (void *)(((uint8_t *)p) + gmp->pool.object_size); + /*lint -restore*/ + n--; + } +} + +/** + * @brief Allocates an object from a guarded memory pool. + * @pre The guarded memory pool must already be initialized. + * + * @param[in] gmp pointer to a @p guarded_memory_pool_t structure + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The pointer to the allocated object. + * @retval NULL if the operation timed out. + * + * @sclass + */ +void *chGuardedPoolAllocTimeoutS(guarded_memory_pool_t *gmp, + sysinterval_t timeout) { + msg_t msg; + + msg = chSemWaitTimeoutS(&gmp->sem, timeout); + if (msg != MSG_OK) { + return NULL; + } + + return chPoolAllocI(&gmp->pool); +} + +/** + * @brief Allocates an object from a guarded memory pool. + * @pre The guarded memory pool must already be initialized. + * + * @param[in] gmp pointer to a @p guarded_memory_pool_t structure + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The pointer to the allocated object. + * @retval NULL if the operation timed out. + * + * @api + */ +void *chGuardedPoolAllocTimeout(guarded_memory_pool_t *gmp, + sysinterval_t timeout) { + void *p; + + chSysLock(); + p = chGuardedPoolAllocTimeoutS(gmp, timeout); + chSysUnlock(); + + return p; +} + +/** + * @brief Releases an object into a guarded memory pool. + * @pre The guarded memory pool must already be initialized. + * @pre The freed object must be of the right size for the specified + * guarded memory pool. + * @pre The added object must be properly aligned. + * + * @param[in] gmp pointer to a @p guarded_memory_pool_t structure + * @param[in] objp the pointer to the object to be released + * + * @api + */ +void chGuardedPoolFree(guarded_memory_pool_t *gmp, void *objp) { + + chSysLock(); + chGuardedPoolFreeI(gmp, objp); + chSchRescheduleS(); + chSysUnlock(); +} +#endif + +#endif /* CH_CFG_USE_MEMPOOLS == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chobjcaches.c b/ChibiOS_20.3.2/os/oslib/src/chobjcaches.c new file mode 100644 index 0000000..d08421b --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chobjcaches.c @@ -0,0 +1,491 @@ +/* + ChibiOS - Copyright (C) 2006..2019 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chobjcaches.c + * @brief Objects Caches code. + * @details Objects caches. + *

Operation mode

+ * An object cache allows to retrieve and release objects from a + * slow media, for example a disk or flash.
+ * The most recently used objects are kept in a series of RAM + * buffers making access faster. Objects are identified by a + * pair which could be mapped, for example, to a + * disk drive identifier and sector identifier.
+ * Read and write operations are performed using externally-supplied + * functions, the cache is device-agnostic.
+ * The cache uses internally an hash table, the size of the table + * should be dimensioned to minimize the risk of hash collisions, + * a factor of two is usually acceptable, it depends on the specific + * application requirements.
+ * Operations defined for caches: + * - Get Object: Retrieves an object from cache, if not + * present then an empty buffer is returned. + * - Read Object: Retrieves an object from cache, if not + * present a buffer is allocated and the object is read from the + * media. + * - Release Object: Releases an object to the cache handling + * the media update, if required. + * . + * @pre In order to use the pipes APIs the @p CH_CFG_USE_OBJ_CACHES + * option must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * + * @addtogroup oslib_objchaches + * @{ + */ + +#include "ch.h" + +#if (CH_CFG_USE_OBJ_CACHES == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module local definitions. */ +/*===========================================================================*/ + +/* Default hash function.*/ +#if !defined(OC_HASH_FUNCTION) || defined(__DOXYGEN__) +#define OC_HASH_FUNCTION(ocp, group, key) \ + (((unsigned)(group) + (unsigned)(key)) & ((unsigned)(ocp)->hashn - 1U)) +#endif + +/* Insertion into an hash slot list.*/ +#define HASH_INSERT(ocp, objp, group, key) { \ + oc_hash_header_t *hhp; \ + (hhp) = &(ocp)->hashp[OC_HASH_FUNCTION(ocp, group, key)]; \ + (objp)->hash_next = (hhp)->hash_next; \ + (objp)->hash_prev = (oc_object_t *)(hhp); \ + (hhp)->hash_next->hash_prev = (objp); \ + (hhp)->hash_next = (objp); \ +} + +/* Removal of an object from the hash.*/ +#define HASH_REMOVE(objp) { \ + (objp)->hash_prev->hash_next = (objp)->hash_next; \ + (objp)->hash_next->hash_prev = (objp)->hash_prev; \ +} + +/* Insertion on LRU list head (newer objects).*/ +#define LRU_INSERT_HEAD(ocp, objp) { \ + (objp)->lru_next = (ocp)->lru.lru_next; \ + (objp)->lru_prev = (oc_object_t *)&(ocp)->lru; \ + (ocp)->lru.lru_next->lru_prev = (objp); \ + (ocp)->lru.lru_next = (objp); \ +} + +/* Insertion on LRU list head (newer objects).*/ +#define LRU_INSERT_TAIL(ocp, objp) { \ + (objp)->lru_prev = (ocp)->lru.lru_prev; \ + (objp)->lru_next = (oc_object_t *)&(ocp)->lru; \ + (ocp)->lru.lru_prev->lru_next = (objp); \ + (ocp)->lru.lru_prev = (objp); \ +} + +/* Removal of an object from the LRU list.*/ +#define LRU_REMOVE(objp) { \ + (objp)->lru_prev->lru_next = (objp)->lru_next; \ + (objp)->lru_next->lru_prev = (objp)->lru_prev; \ +} + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/** + * @brief Returns an object pointer from the cache, if present. + * + * @param[out] ocp pointer to the @p objects_cache_t structure to be + * @param[in] group object group identifier + * @param[in] key object identifier within the group + * initialized + * @return The pointer to the retrieved object. + * @retval NULL if the object is not in cache. + * + * @notapi + */ +static oc_object_t *hash_get_s(objects_cache_t *ocp, + uint32_t group, + uint32_t key) { + oc_hash_header_t *hhp; + oc_object_t *objp; + + /* Hash slot where to search for an hit.*/ + hhp = &ocp->hashp[OC_HASH_FUNCTION(ocp, group, key)]; + objp = hhp->hash_next; + + /* Scanning the siblings collision list.*/ + while (objp != (oc_object_t *)hhp) { + if ((objp->obj_key == key) && (objp->obj_group == group)) { + + /* Cache hit.*/ + return objp; + } + objp = objp->hash_next; + } + + return NULL; +} + +/** + * @brief Gets the least recently used object buffer from the LRU list. + * + * @param[out] ocp pointer to the @p objects_cache_t structure to be + * @return The pointer to the retrieved object. + * + * @notapi + */ +static oc_object_t *lru_get_last_s(objects_cache_t *ocp) { + oc_object_t *objp; + + while (true) { + /* Waiting for an object buffer to become available in the LRU.*/ + (void) chSemWaitS(&ocp->lru_sem); + + /* Now an object buffer is in the LRU for sure, taking it from the + LRU tail.*/ + objp = ocp->lru.lru_prev; + + chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == OC_FLAG_INLRU, + "not in LRU"); + chDbgAssert(chSemGetCounterI(&objp->obj_sem) == (cnt_t)1, + "semaphore counter not 1"); + + LRU_REMOVE(objp); + objp->obj_flags &= ~OC_FLAG_INLRU; + + /* Getting the object semaphore, we know there is no wait so + using the "fast" variant.*/ + chSemFastWaitI(&objp->obj_sem); + + /* If it is a buffer not needing (lazy) write then it can be used + right away.*/ + if ((objp->obj_flags & OC_FLAG_LAZYWRITE) == 0U) { + + /* Removing from hash table if required.*/ + if ((objp->obj_flags & OC_FLAG_INHASH) != 0U) { + HASH_REMOVE(objp); + } + + /* Removing all flags, it is "new" now.*/ + objp->obj_flags = 0U; + + return objp; + } + + + /* Out of critical section.*/ + chSysUnlock(); + + /* Invoking the writer asynchronously, it will release the buffer once it + is written. It is responsibility of the write function to release + the buffer.*/ + objp->obj_flags = OC_FLAG_INHASH | OC_FLAG_FORGET; + (void) ocp->writef(ocp, objp, true); + + /* Critical section enter again.*/ + chSysLock(); + } +} + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes a @p objects_cache_t object. + * + * @param[out] ocp pointer to the @p objects_cache_t structure to be + * initialized + * @param[in] hashn number of elements in the hash table array, must be + * a power of two and not lower than @p objn + * @param[in] hashp pointer to the hash table as an array of + * @p oc_hash_header_t + * @param[in] objn number of elements in the objects table array + * @param[in] objsz size of elements in the objects table array, the + * minimum value is sizeof (oc_object_t). + * @param[in] objvp pointer to the hash objects as an array of structures + * starting with an @p oc_object_t + * @param[in] readf pointer to an object reader function + * @param[in] writef pointer to an object writer function + * + * @init + */ +void chCacheObjectInit(objects_cache_t *ocp, + ucnt_t hashn, + oc_hash_header_t *hashp, + ucnt_t objn, + size_t objsz, + void *objvp, + oc_readf_t readf, + oc_writef_t writef) { + + chDbgCheck((ocp != NULL) && (hashp != NULL) && (objvp != NULL) && + ((hashn & (hashn - (ucnt_t)1)) == (ucnt_t)0) && + (objn > (ucnt_t)0) && (hashn >= objn) && + (objsz >= sizeof (oc_object_t)) && + ((objsz & (PORT_NATURAL_ALIGN - 1U)) == 0U)); + + chSemObjectInit(&ocp->cache_sem, (cnt_t)1); + chSemObjectInit(&ocp->lru_sem, (cnt_t)objn); + ocp->hashn = hashn; + ocp->hashp = hashp; + ocp->objn = objn; + ocp->objvp = objvp; + ocp->readf = readf; + ocp->writef = writef; + ocp->lru.hash_next = NULL; + ocp->lru.hash_prev = NULL; + ocp->lru.lru_next = (oc_object_t *)&ocp->lru; + ocp->lru.lru_prev = (oc_object_t *)&ocp->lru; + + /* Hash headers initialization.*/ + do { + hashp->hash_next = (oc_object_t *)hashp; + hashp->hash_prev = (oc_object_t *)hashp; + hashp++; + } while (hashp < &ocp->hashp[ocp->hashn]); + + /* Object headers initialization.*/ + do { + oc_object_t *objp = (oc_object_t *)objvp; + + chSemObjectInit(&objp->obj_sem, (cnt_t)1); + LRU_INSERT_HEAD(ocp, objp); + objp->obj_group = 0U; + objp->obj_key = 0U; + objp->obj_flags = OC_FLAG_INLRU; + objp->dptr = NULL; + objvp = (void *)((uint8_t *)objvp + objsz); + objn--; + } while (objn > (ucnt_t)0); +} + +/** + * @brief Retrieves an object from the cache. + * @note If the object is not in cache then the returned object is marked + * as @p OC_FLAG_NOTSYNC meaning that its data contains garbage and + * must be initialized. + * + * @param[in] ocp pointer to the @p objects_cache_t structure + * @param[in] group object group identifier + * @param[in] key object identifier within the group + * @return The pointer to the retrieved object. + * + * @api + */ +oc_object_t *chCacheGetObject(objects_cache_t *ocp, + uint32_t group, + uint32_t key) { + oc_object_t *objp; + + /* Critical section enter, the hash check operation is fast.*/ + chSysLock(); + + /* Checking the cache for a hit.*/ + objp = hash_get_s(ocp, group, key); + if (objp != NULL) { + + chDbgAssert((objp->obj_flags & OC_FLAG_INHASH) == OC_FLAG_INHASH, + "not in hash"); + + /* Cache hit, checking if the buffer is owned by some + other thread.*/ + if (chSemGetCounterI(&objp->obj_sem) > (cnt_t)0) { + /* Not owned case, it is in the LRU list.*/ + + chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == OC_FLAG_INLRU, + "not in LRU"); + + /* Removing the object from LRU, now it is "owned".*/ + LRU_REMOVE(objp); + objp->obj_flags &= ~OC_FLAG_INLRU; + + /* Getting the object semaphore, we know there is no wait so + using the "fast" variant.*/ + chSemFastWaitI(&objp->obj_sem); + } + else { + /* Owned case, some other thread is playing with this object, we + need to wait.*/ + + chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == 0U, "in LRU"); + + /* Waiting on the buffer semaphore.*/ + (void) chSemWaitS(&objp->obj_sem); + } + } + else { + /* Cache miss, getting an object buffer from the LRU list.*/ + objp = lru_get_last_s(ocp); + + /* Naming this object and publishing it in the hash table.*/ + objp->obj_group = group; + objp->obj_key = key; + objp->obj_flags = OC_FLAG_INHASH | OC_FLAG_NOTSYNC; + HASH_INSERT(ocp, objp, group, key); + } + + /* Out of critical section and returning the object.*/ + chSysUnlock(); + + return objp; +} + +/** + * @brief Releases an object into the cache. + * @note This function gives a meaning to the following flags: + * - @p OC_FLAG_INLRU must be cleared. + * - @p OC_FLAG_INHASH must be set. + * - @p OC_FLAG_SHARED must be cleared. + * - @p OC_FLAG_NOTSYNC invalidates the object and queues it on + * the LRU tail. + * - @p OC_FLAG_LAZYWRITE is ignored and kept, a write will occur + * when the object is removed from the LRU list (lazy write). + * . + * + * @param[in] ocp pointer to the @p objects_cache_t structure + * @param[in] objp pointer to the @p oc_object_t structure + * + * @iclass + */ +void chCacheReleaseObjectI(objects_cache_t *ocp, + oc_object_t *objp) { + + /* Checking initial conditions of the object to be released.*/ + chDbgAssert((objp->obj_flags & (OC_FLAG_INLRU | + OC_FLAG_INHASH | + OC_FLAG_SHARED)) == OC_FLAG_INHASH, + "invalid object state"); + chDbgAssert(chSemGetCounterI(&objp->obj_sem) <= (cnt_t)0, + "semaphore counter greater than 0"); + + /* If some thread is waiting for this specific buffer then it is + handed directly without going through the LRU.*/ + if (chSemGetCounterI(&objp->obj_sem) < (cnt_t)0) { + /* Clearing all flags except those that are still meaningful, note, + OC_FLAG_NOTSYNC and OC_FLAG_LAZYWRITE are passed, the other thread + will handle them.*/ + objp->obj_flags &= OC_FLAG_INHASH | OC_FLAG_NOTSYNC | OC_FLAG_LAZYWRITE; + chSemSignalI(&objp->obj_sem); + return; + } + + /* If the object specifies OC_FLAG_NOTSYNC then it must be invalidated + and removed from the hash table.*/ + if ((objp->obj_flags & OC_FLAG_NOTSYNC) != 0U) { + HASH_REMOVE(objp); + LRU_INSERT_TAIL(ocp, objp); + objp->obj_group = 0U; + objp->obj_key = 0U; + objp->obj_flags = OC_FLAG_INLRU; + } + else { + /* LRU insertion point depends on the OC_FLAG_FORGET flag.*/ + if ((objp->obj_flags & OC_FLAG_FORGET) == 0U) { + /* Placing it on head.*/ + LRU_INSERT_HEAD(ocp, objp); + } + else { + /* Low priority data, placing it on tail.*/ + LRU_INSERT_TAIL(ocp, objp); + } + objp->obj_flags &= OC_FLAG_INHASH | OC_FLAG_LAZYWRITE; + objp->obj_flags |= OC_FLAG_INLRU; + } + + /* Increasing the LRU counter semaphore.*/ + chSemSignalI(&ocp->lru_sem); + + /* Releasing the object, we know there are no threads waiting so + using the "fast" signal variant.*/ + chSemFastSignalI(&objp->obj_sem); +} + +/** + * @brief Reads object data from the storage. + * @note In case of asynchronous operation an error condition is not + * reported by this function. + * + * @param[in] ocp pointer to the @p objects_cache_t structure + * @param[in] objp pointer to the @p oc_object_t structure + * @param[in] async requests an asynchronous operation if supported, the + * function is then responsible for releasing the + * object + * @return The operation status. In case of asynchronous + * operation @p false is always returned. + * @retval false if the operation succeeded. + * @retval true if the synchronous read operation failed. + * + * @api + */ +bool chCacheReadObject(objects_cache_t *ocp, + oc_object_t *objp, + bool async) { + + /* Marking it as OC_FLAG_NOTSYNC because the read operation is going + to corrupt it in case of failure. It is responsibility of the read + implementation to clear it if the operation succeeds.*/ + objp->obj_flags |= OC_FLAG_NOTSYNC; + + return ocp->readf(ocp, objp, async); +} + +/** + * @brief Writes the object data back to storage. + * @note In case of asynchronous operation an error condition is not + * reported by this function. + * + * @param[in] ocp pointer to the @p objects_cache_t structure + * @param[in] objp pointer to the @p oc_object_t structure + * @param[in] async requests an asynchronous operation if supported, the + * function is then responsible for releasing the + * object + * @return The operation status. In case of asynchronous + * operation @p false is always returned. + * @retval false if the operation succeeded. + * @retval true if the synchronous write operation failed. + * + * @api + */ +bool chCacheWriteObject(objects_cache_t *ocp, + oc_object_t *objp, + bool async) { + + /* Resetting the OC_FLAG_LAZYWRITE flag in order to prevent multiple + writes.*/ + objp->obj_flags &= ~OC_FLAG_LAZYWRITE; + + return ocp->writef(ocp, objp, async); +} + +#endif /* CH_CFG_USE_OBJ_CACHES == TRUE */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/oslib/src/chpipes.c b/ChibiOS_20.3.2/os/oslib/src/chpipes.c new file mode 100644 index 0000000..f3fb462 --- /dev/null +++ b/ChibiOS_20.3.2/os/oslib/src/chpipes.c @@ -0,0 +1,388 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file oslib/src/chpipes.c + * @brief Pipes code. + * @details Byte pipes. + *

Operation mode

+ * A pipe is an asynchronous communication mechanism.
+ * Operations defined for mailboxes: + * - Write: Writes a buffer of data in the pipe in FIFO order. + * - Read: A buffer of data is read from the read and removed. + * - Reset: The pipe is emptied and all the stored data + * is lost. + * . + * @pre In order to use the pipes APIs the @p CH_CFG_USE_PIPES + * option must be enabled in @p chconf.h. + * @note Compatible with RT and NIL. + * + * @addtogroup oslib_pipes + * @{ + */ + +#include + +#include "ch.h" + +#if (CH_CFG_USE_PIPES == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module local definitions. */ +/*===========================================================================*/ + +/* + * Defaults on the best synchronization mechanism available. + */ +#if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) +#define PC_INIT(p) chMtxObjectInit(&(p)->cmtx) +#define PC_LOCK(p) chMtxLock(&(p)->cmtx) +#define PC_UNLOCK(p) chMtxUnlock(&(p)->cmtx) +#define PW_INIT(p) chMtxObjectInit(&(p)->wmtx) +#define PW_LOCK(p) chMtxLock(&(p)->wmtx) +#define PW_UNLOCK(p) chMtxUnlock(&(p)->wmtx) +#define PR_INIT(p) chMtxObjectInit(&(p)->rmtx) +#define PR_LOCK(p) chMtxLock(&(p)->rmtx) +#define PR_UNLOCK(p) chMtxUnlock(&(p)->rmtx) +#else +#define PC_INIT(p) chSemObjectInit(&(p)->csem, (cnt_t)1) +#define PC_LOCK(p) (void) chSemWait(&(p)->csem) +#define PC_UNLOCK(p) chSemSignal(&(p)->csem) +#define PW_INIT(p) chSemObjectInit(&(p)->wsem, (cnt_t)1) +#define PW_LOCK(p) (void) chSemWait(&(p)->wsem) +#define PW_UNLOCK(p) chSemSignal(&(p)->wsem) +#define PR_INIT(p) chSemObjectInit(&(p)->rsem, (cnt_t)1) +#define PR_LOCK(p) (void) chSemWait(&(p)->rsem) +#define PR_UNLOCK(p) chSemSignal(&(p)->rsem) +#endif + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Module local functions. */ +/*===========================================================================*/ + +/** + * @brief Non-blocking pipe write. + * @details The function writes data from a buffer to a pipe. The + * operation completes when the specified amount of data has been + * transferred or when the pipe buffer has been filled. + * + * @param[in] pp the pointer to an initialized @p pipe_t object + * @param[in] bp pointer to the data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @return The number of bytes effectively transferred. + * + * @notapi + */ +static size_t pipe_write(pipe_t *pp, const uint8_t *bp, size_t n) { + size_t s1, s2; + + PC_LOCK(pp); + + /* Number of bytes that can be written in a single atomic operation.*/ + if (n > chPipeGetFreeCount(pp)) { + n = chPipeGetFreeCount(pp); + } + pp->cnt += n; + + /* Number of bytes before buffer limit.*/ + /*lint -save -e9033 [10.8] Checked to be safe.*/ + s1 = (size_t)(pp->top - pp->wrptr); + /*lint -restore*/ + + if (n < s1) { + memcpy((void *)pp->wrptr, (const void *)bp, n); + pp->wrptr += n; + } + else if (n > s1) { + memcpy((void *)pp->wrptr, (const void *)bp, s1); + bp += s1; + s2 = n - s1; + memcpy((void *)pp->buffer, (const void *)bp, s2); + pp->wrptr = pp->buffer + s2; + } + else { + memcpy((void *)pp->wrptr, (const void *)bp, n); + pp->wrptr = pp->buffer; + } + + PC_UNLOCK(pp); + + return n; +} + +/** + * @brief Non-blocking pipe read. + * @details The function reads data from a pipe into a buffer. The + * operation completes when the specified amount of data has been + * transferred or when the pipe buffer has been emptied. + * + * @param[in] pp the pointer to an initialized @p pipe_t object + * @param[out] bp pointer to the data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @return The number of bytes effectively transferred. + * + * @notapi + */ +static size_t pipe_read(pipe_t *pp, uint8_t *bp, size_t n) { + size_t s1, s2; + + PC_LOCK(pp); + + /* Number of bytes that can be read in a single atomic operation.*/ + if (n > chPipeGetUsedCount(pp)) { + n = chPipeGetUsedCount(pp); + } + pp->cnt -= n; + + /* Number of bytes before buffer limit.*/ + /*lint -save -e9033 [10.8] Checked to be safe.*/ + s1 = (size_t)(pp->top - pp->rdptr); + /*lint -restore*/ + + if (n < s1) { + memcpy((void *)bp, (void *)pp->rdptr, n); + pp->rdptr += n; + } + else if (n > s1) { + memcpy((void *)bp, (void *)pp->rdptr, s1); + bp += s1; + s2 = n - s1; + memcpy((void *)bp, (void *)pp->buffer, s2); + pp->rdptr = pp->buffer + s2; + } + else { + memcpy((void *)bp, (void *)pp->rdptr, n); + pp->rdptr = pp->buffer; + } + + PC_UNLOCK(pp); + + return n; +} + +/*===========================================================================*/ +/* Module exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes a @p mailbox_t object. + * + * @param[out] pp the pointer to the @p pipe_t structure to be + * initialized + * @param[in] buf pointer to the pipe buffer as an array of @p uint8_t + * @param[in] n number of elements in the buffer array + * + * @init + */ +void chPipeObjectInit(pipe_t *pp, uint8_t *buf, size_t n) { + + chDbgCheck((pp != NULL) && (buf != NULL) && (n > (size_t)0)); + + pp->buffer = buf; + pp->rdptr = buf; + pp->wrptr = buf; + pp->top = &buf[n]; + pp->cnt = (size_t)0; + pp->reset = false; + pp->wtr = NULL; + pp->rtr = NULL; + PC_INIT(pp); + PW_INIT(pp); + PR_INIT(pp); +} + +/** + * @brief Resets a @p pipe_t object. + * @details All the waiting threads are resumed with status @p MSG_RESET and + * the queued data is lost. + * @post The pipe is in reset state, all operations will fail and + * return @p MSG_RESET until the mailbox is enabled again using + * @p chPipeResumeX(). + * + * @param[in] pp the pointer to an initialized @p pipe_t object + * + * @api + */ +void chPipeReset(pipe_t *pp) { + + chDbgCheck(pp != NULL); + + PC_LOCK(pp); + + pp->wrptr = pp->buffer; + pp->rdptr = pp->buffer; + pp->cnt = (size_t)0; + pp->reset = true; + + chSysLock(); + chThdResumeI(&pp->wtr, MSG_RESET); + chThdResumeI(&pp->rtr, MSG_RESET); + chSchRescheduleS(); + chSysUnlock(); + + PC_UNLOCK(pp); +} + +/** + * @brief Pipe write with timeout. + * @details The function writes data from a buffer to a pipe. The + * operation completes when the specified amount of data has been + * transferred or after the specified timeout or if the pipe has + * been reset. + * + * @param[in] pp the pointer to an initialized @p pipe_t object + * @param[in] bp pointer to the data buffer + * @param[in] n the number of bytes to be written, the value 0 is + * reserved + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The number of bytes effectively transferred. A number + * lower than @p n means that a timeout occurred or the + * pipe went in reset state. + * + * @api + */ +size_t chPipeWriteTimeout(pipe_t *pp, const uint8_t *bp, + size_t n, sysinterval_t timeout) { + size_t max = n; + + chDbgCheck(n > 0U); + + /* If the pipe is in reset state then returns immediately.*/ + if (pp->reset) { + return (size_t)0; + } + + PW_LOCK(pp); + + while (n > 0U) { + size_t done; + + done = pipe_write(pp, bp, n); + if (done == (size_t)0) { + msg_t msg; + + chSysLock(); + msg = chThdSuspendTimeoutS(&pp->wtr, timeout); + chSysUnlock(); + + /* Anything except MSG_OK causes the operation to stop.*/ + if (msg != MSG_OK) { + break; + } + } + else { + n -= done; + bp += done; + + /* Resuming the reader, if present.*/ + chThdResume(&pp->rtr, MSG_OK); + } + } + + PW_UNLOCK(pp); + + return max - n; +} + +/** + * @brief Pipe read with timeout. + * @details The function reads data from a pipe into a buffer. The + * operation completes when the specified amount of data has been + * transferred or after the specified timeout or if the pipe has + * been reset. + * + * @param[in] pp the pointer to an initialized @p pipe_t object + * @param[out] bp pointer to the data buffer + * @param[in] n the number of bytes to be read, the value 0 is + * reserved + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The number of bytes effectively transferred. A number + * lower than @p n means that a timeout occurred or the + * pipe went in reset state. + * + * @api + */ +size_t chPipeReadTimeout(pipe_t *pp, uint8_t *bp, + size_t n, sysinterval_t timeout) { + size_t max = n; + + chDbgCheck(n > 0U); + + /* If the pipe is in reset state then returns immediately.*/ + if (pp->reset) { + return (size_t)0; + } + + PR_LOCK(pp); + + while (n > 0U) { + size_t done; + + done = pipe_read(pp, bp, n); + if (done == (size_t)0) { + msg_t msg; + + chSysLock(); + msg = chThdSuspendTimeoutS(&pp->rtr, timeout); + chSysUnlock(); + + /* Anything except MSG_OK causes the operation to stop.*/ + if (msg != MSG_OK) { + break; + } + } + else { + n -= done; + bp += done; + + /* Resuming the writer, if present.*/ + chThdResume(&pp->wtr, MSG_OK); + } + } + + PR_UNLOCK(pp); + + return max - n; +} + +#endif /* CH_CFG_USE_PIPES == TRUE */ + +/** @} */ -- cgit v1.2.3