| /**************************************************************************** |
| * wireless/ieee802154/ieee802154_primitive.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/wireless/ieee802154/ieee802154_mac.h> |
| |
| #include "mac802154.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* NOTE: The CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE options is marked as |
| * marked 'experimental' and with the default 0 zero because there are no |
| * interrupt level allocations performed by the current IEEE 802.15.4 MAC |
| * code. |
| */ |
| |
| #if !defined(CONFIG_IEEE802154_PRIMITIVE_PREALLOC) || \ |
| CONFIG_IEEE802154_PRIMITIVE_PREALLOC < 0 |
| # undef CONFIG_IEEE802154_PRIMITIVE_PREALLOC |
| # define CONFIG_IEEE802154_PRIMITIVE_PREALLOC 20 |
| #endif |
| |
| #if !defined(CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE) || \ |
| CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE < 0 |
| # undef CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| # define CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE 0 |
| #endif |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE > CONFIG_IEEE802154_PRIMITIVE_PREALLOC |
| # undef CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| # define CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE CONFIG_IEEE802154_PRIMITIVE_PREALLOC |
| #endif |
| |
| /* Memory Pools */ |
| |
| #define POOL_PRIMITIVE_GENERAL 0 |
| #define POOL_PRIMITIVE_IRQ 1 |
| #define POOL_PRIMITIVE_DYNAMIC 2 |
| |
| /**************************************************************************** |
| * Private Data Types |
| ****************************************************************************/ |
| |
| /* Private data type that extends the ieee802154_primitive_s struct */ |
| |
| struct ieee802154_priv_primitive_s |
| { |
| /* Must be first member so we can cast to/from */ |
| |
| struct ieee802154_primitive_s pub; |
| FAR struct ieee802154_priv_primitive_s *flink; |
| uint8_t pool; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| /* The g_primfree is a list of primitive structures that are available for |
| * general use. The number of messages in this list is a system |
| * configuration item. |
| */ |
| |
| static struct ieee802154_priv_primitive_s *g_primfree; |
| #endif |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE > 0 |
| /* The g_primfree_irq is a list of primitive structures that are reserved for |
| * use by only by interrupt handlers. |
| */ |
| |
| static struct ieee802154_priv_primitive_s *g_primfree_irq; |
| #endif |
| |
| /* Pool of pre-allocated primitive structures */ |
| |
| static struct ieee802154_priv_primitive_s |
| g_primpool[CONFIG_IEEE802154_PRIMITIVE_PREALLOC]; |
| #endif /* CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 */ |
| |
| static bool g_poolinit = false; |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ieee802154_primitivepool_initialize |
| * |
| * Description: |
| * This function initializes the primitive allocator. This function must |
| * be called early in the initialization sequence before any radios |
| * begin operation. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void ieee802154_primitivepool_initialize(void) |
| { |
| /* Only allow the pool to be initialized once */ |
| |
| if (g_poolinit) |
| { |
| return; |
| } |
| |
| g_poolinit = true; |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 |
| FAR struct ieee802154_priv_primitive_s *pool = g_primpool; |
| int remaining = CONFIG_IEEE802154_PRIMITIVE_PREALLOC; |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| /* Initialize g_primfree, the list of primitive structures that are |
| * available for general use. |
| */ |
| |
| g_primfree = NULL; |
| while (remaining > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE) |
| { |
| FAR struct ieee802154_priv_primitive_s *prim = pool; |
| |
| /* Add the next meta data structure from the pool to the list of |
| * general structures. |
| */ |
| |
| prim->flink = g_primfree; |
| g_primfree = prim; |
| |
| /* Set up for the next structure from the pool */ |
| |
| pool++; |
| remaining--; |
| } |
| #endif |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE > 0 |
| /* Initialize g_primfree_irq is a list of primitive structures reserved for |
| * use by only by interrupt handlers. |
| */ |
| |
| g_primfree_irq = NULL; |
| while (remaining > 0) |
| { |
| FAR struct ieee802154_priv_primitive_s *prim = pool; |
| |
| /* Add the next meta data structure from the pool to the list of |
| * general structures. |
| */ |
| |
| prim->flink = g_primfree_irq; |
| g_primfree_irq = prim; |
| |
| /* Set up for the next structure from the pool */ |
| |
| pool++; |
| remaining--; |
| } |
| #endif |
| #endif /* CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 */ |
| } |
| |
| /**************************************************************************** |
| * Name: ieee802154_primitive_allocate |
| * |
| * Description: |
| * The ieee802154_primitive_allocate function will get a free primitive |
| * structure for use by the IEEE 802.15.4 MAC. |
| * |
| * Interrupt handling logic will first attempt to allocate from the |
| * g_primfree list. If that list is empty, it will attempt to allocate |
| * from its reserve, g_primfree_irq. If that list is empty, then the |
| * allocation fails (NULL is returned). |
| * |
| * Non-interrupt handler logic will attempt to allocate from g_primfree |
| * list. If that the list is empty, then the primitive structure will be |
| * allocated from the dynamic memory pool. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * A reference to the allocated primitive structure. |
| * All user fields in this structure have been zeroed. |
| * On a failure to allocate, NULL is returned. |
| * |
| ****************************************************************************/ |
| |
| FAR struct ieee802154_primitive_s *ieee802154_primitive_allocate(void) |
| { |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 |
| FAR struct ieee802154_priv_primitive_s *prim; |
| irqstate_t flags; |
| uint8_t pool; |
| |
| /* If we were called from an interrupt handler, then try to get the meta- |
| * data structure from generally available list of messages. If this fails, |
| * then try the list of messages reserved for interrupt handlers |
| */ |
| |
| flags = enter_critical_section(); /* Always necessary in SMP mode */ |
| if (up_interrupt_context()) |
| { |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| /* Try the general free list */ |
| |
| if (g_primfree != NULL) |
| { |
| prim = g_primfree; |
| g_primfree = prim->flink; |
| |
| leave_critical_section(flags); |
| pool = POOL_PRIMITIVE_GENERAL; |
| } |
| else |
| #endif |
| #if CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE > 0 |
| /* Try the list list reserved for interrupt handlers */ |
| |
| if (g_primfree_irq != NULL) |
| { |
| prim = g_primfree_irq; |
| g_primfree_irq = prim->flink; |
| |
| leave_critical_section(flags); |
| pool = POOL_PRIMITIVE_IRQ; |
| } |
| else |
| #endif |
| { |
| leave_critical_section(flags); |
| return NULL; |
| } |
| } |
| |
| /* We were not called from an interrupt handler. */ |
| |
| else |
| { |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| /* Try the general free list */ |
| |
| if (g_primfree != NULL) |
| { |
| prim = g_primfree; |
| g_primfree = prim->flink; |
| |
| leave_critical_section(flags); |
| pool = POOL_PRIMITIVE_GENERAL; |
| } |
| else |
| #endif |
| { |
| /* If we cannot a primitive structure from the free list, then we |
| * will have to allocate one from the kernel memory pool. |
| */ |
| |
| leave_critical_section(flags); |
| prim = (FAR struct ieee802154_priv_primitive_s *) |
| kmm_malloc((sizeof (struct ieee802154_priv_primitive_s))); |
| |
| /* Check if we allocated the primitive structure */ |
| |
| if (prim == NULL) |
| { |
| /* No.. memory not available */ |
| |
| wlerr("ERROR: Failed to allocate primitive.\n"); |
| return NULL; |
| } |
| |
| /* Remember that this primitive structure |
| * was dynamically allocated |
| */ |
| |
| pool = POOL_PRIMITIVE_DYNAMIC; |
| } |
| } |
| |
| /* We have successfully allocated memory from some source. |
| * Zero and tag the allocated primitive structure. |
| */ |
| |
| prim->pool = pool; |
| memset(&prim->pub, 0, sizeof(struct ieee802154_primitive_s)); |
| |
| wlinfo("Primitive allocated: %p\n", prim); |
| return &prim->pub; |
| #else |
| return NULL; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: ieee802154_primitive_free |
| * |
| * Description: |
| * The ieee802154_primitive_free function will return a primitive structure |
| * to the free pool of messages if it was a pre-allocated primitive |
| * structure. If the primitive structure was allocated dynamically it will |
| * be deallocated. |
| * |
| * Input Parameters: |
| * prim - primitive structure to free |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void ieee802154_primitive_free(FAR struct ieee802154_primitive_s *prim) |
| { |
| if (--prim->nclients > 0) |
| { |
| wlinfo("Remaining Clients: %d\n", prim->nclients); |
| return; |
| } |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > 0 |
| irqstate_t flags; |
| FAR struct ieee802154_priv_primitive_s *priv = |
| (FAR struct ieee802154_priv_primitive_s *)prim; |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_PREALLOC > CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE |
| /* If this is a generally available pre-allocated primitive structure, |
| * then just put it back in the free list. |
| */ |
| |
| if (priv->pool == POOL_PRIMITIVE_GENERAL) |
| { |
| /* Make sure we avoid concurrent access to the free |
| * list from interrupt handlers. |
| */ |
| |
| flags = enter_critical_section(); |
| priv->flink = g_primfree; |
| g_primfree = priv; |
| leave_critical_section(flags); |
| } |
| else |
| #endif |
| |
| #if CONFIG_IEEE802154_PRIMITIVE_IRQRESERVE > 0 |
| /* If this is a primitive structure pre-allocated for interrupts, |
| * then put it back in the correct free list. |
| */ |
| |
| if (priv->pool == POOL_PRIMITIVE_IRQ) |
| { |
| /* Make sure we avoid concurrent access to the free |
| * list from interrupt handlers. |
| */ |
| |
| flags = enter_critical_section(); |
| priv->flink = g_primfree_irq; |
| g_primfree_irq = priv; |
| leave_critical_section(flags); |
| } |
| else |
| #endif |
| |
| { |
| /* Otherwise, deallocate it. */ |
| |
| DEBUGASSERT(priv->pool == POOL_PRIMITIVE_DYNAMIC); |
| kmm_free(priv); |
| } |
| #endif |
| |
| wlinfo("Primitive freed: %p\n", prim); |
| } |