diff --git a/include/nuttx/wireless/ieee802154/ieee802154_mac.h b/include/nuttx/wireless/ieee802154/ieee802154_mac.h index 10f691a9b2..19af457bf6 100644 --- a/include/nuttx/wireless/ieee802154/ieee802154_mac.h +++ b/include/nuttx/wireless/ieee802154/ieee802154_mac.h @@ -724,35 +724,52 @@ extern "C" ****************************************************************************/ /**************************************************************************** - * Name: mac802154_register + * Name: mac802154_create * * Description: * Create a 802.15.4 MAC device from a 802.15.4 compatible radio device. - * To create a 802.15.4 MAC, you need to pass: * - * - an instance of a radio driver in radiodev - * - a pointer to a structure that contains MAC callback routines to - * handle confirmations and indications. NULL entries indicate no - * callback. + * The returned MAC structure should be passed to either the next highest + * layer in the network stack, or registered with a mac802154dev character + * driver. In either of these scenarios, the next highest layer should + * register a set of callbacks with the MAC layer by setting the mac->cbs + * member. * - * In return you get a mac structure that has pointers to MAC operations - * and responses. - * - * This API does not create any device accessible to userspace. If you + * NOTE: This API does not create any device accessible to userspace. If you * want to call these APIs from userspace, you have to wrap your mac in a * character device via mac802154_device.c. * + * Input Parameters: + * radiodev - an instance of an IEEE 802.15.4 radio + * + * Returned Value: + * A MAC structure that has pointers to MAC operations + * and responses. + * ****************************************************************************/ -#if 0 /* REVISIT: This form is not currently used by the driver */ FAR struct ieee802154_mac_s * - mac802154_register(FAR struct ieee802154_radio_s *radiodev, - FAR struct ieee802154_maccb_s *callbacks); -#else /* This is the form used by the driver */ -FAR struct ieee802154_mac_s * - mac802154_register(FAR struct ieee802154_radio_s *radiodev, - unsigned int minor); -#endif + mac802154_create(FAR struct ieee802154_radio_s *radiodev); + +/**************************************************************************** + * Name: mac802154dev_register + * + * Description: + * Register a character driver to access the IEEE 802.15.4 MAC layer from + * user-space + * + * Input Parameters: + * mac - Pointer to the mac layer struct to be registerd. + * minor - The device minor number. The IEEE802.15.4 MAC character device + * will be registered as /dev/ieeeN where N is the minor number + * + * Returned Values: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int mac802154dev_register(FAR struct ieee802154_mac_s *mac, int minor); #undef EXTERN #ifdef __cplusplus diff --git a/wireless/ieee802154/Kconfig b/wireless/ieee802154/Kconfig index fa8793af3d..5470894a7d 100644 --- a/wireless/ieee802154/Kconfig +++ b/wireless/ieee802154/Kconfig @@ -21,6 +21,14 @@ config IEEE802154_MAC such as 6lowpan. It is not required to use 802.15.4 radios, but is strongly suggested to ensure exchange of valid frames. +config IEEE802154_MAC_DEV + bool "Character driver for IEEE 802.15.4 MAC layer" + default n + depends on IEEE802154_MAC + ---help--- + Enable the device driver to expose the IEEE 802.15.4 MAC layer + access to user space as IOCTLs + config IEEE802154_DEV bool "Debug character driver for ieee802.15.4 radio interfaces" default n diff --git a/wireless/ieee802154/Make.defs b/wireless/ieee802154/Make.defs index e2f205aabc..4a69b06511 100644 --- a/wireless/ieee802154/Make.defs +++ b/wireless/ieee802154/Make.defs @@ -45,6 +45,10 @@ ifeq ($(CONFIG_IEEE802154_MAC),y) CSRCS += mac802154.c endif +ifeq ($(CONFIG_IEEE802154_MAC_DEV),y) +CSRCS += mac802154_device.c +endif + ifeq ($(CONFIG_IEEE802154_DEV),y) CSRCS += radio802154_device.c endif diff --git a/wireless/ieee802154/mac802154.c b/wireless/ieee802154/mac802154.c index 59d5f57d79..d0a0a3ce50 100644 --- a/wireless/ieee802154/mac802154.c +++ b/wireless/ieee802154/mac802154.c @@ -506,29 +506,32 @@ static int mac802154_rsporphan(FAR struct ieee802154_mac_s *mac, ****************************************************************************/ /**************************************************************************** - * Name: mac802154_register + * Name: mac802154_create * * Description: * Create a 802.15.4 MAC device from a 802.15.4 compatible radio device. - * To create a 802.15.4 MAC, you need to pass: * - * - an instance of a radio driver in radiodev - * - a pointer to a structure that contains MAC callback routines to - * handle confirmations and indications. NULL entries indicate no - * callback. + * The returned MAC structure should be passed to either the next highest + * layer in the network stack, or registered with a mac802154dev character + * driver. In either of these scenarios, the next highest layer should + * register a set of callbacks with the MAC layer by setting the mac->cbs + * member. * - * In return you get a mac structure that has pointers to MAC operations - * and responses. - * - * This API does not create any device accessible to userspace. If you + * NOTE: This API does not create any device accessible to userspace. If you * want to call these APIs from userspace, you have to wrap your mac in a * character device via mac802154_device.c. * + * Input Parameters: + * radiodev - an instance of an IEEE 802.15.4 radio + * + * Returned Value: + * A MAC structure that has pointers to MAC operations + * and responses. + * ****************************************************************************/ FAR struct ieee802154_mac_s * - mac802154_register(FAR struct ieee802154_radio_s *radiodev, - unsigned int minor) + mac802154_create(FAR struct ieee802154_radio_s *radiodev) { FAR struct ieee802154_privmac_s *mac; diff --git a/wireless/ieee802154/mac802154_device.c b/wireless/ieee802154/mac802154_device.c new file mode 100644 index 0000000000..88f36ee514 --- /dev/null +++ b/wireless/ieee802154/mac802154_device.c @@ -0,0 +1,503 @@ +/**************************************************************************** + * drivers/ieee802154/mac802154_device.c + * + * Copyright (C) 2017 Verge Inc. All rights reserved. + * Author: Anthony Merlino + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device naming ************************************************************/ + +#define DEVNAME_FMT "/dev/ieee%d" +#define DEVNAME_FMTLEN (9 + 3 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mac802154_devwrapper_s +{ + FAR struct ieee802154_mac_s *md_mac; /* Saved binding to the mac layer */ + sem_t md_exclsem; /* Exclusive device access */ + + /* The following is a singly linked list of open references to the + * MAC device. + */ + + FAR struct mac802154_open_s *md_open; +}; + +/* This structure describes the state of one open mac driver instance */ + +struct mac802154_open_s +{ + /* Supports a singly linked list */ + + FAR struct mac802154_open_s *md_flink; + + /* The following will be true if we are closing */ + + volatile bool md_closing; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + + /* Semaphore helpers */ + +static inline int mac802154dev_takesem(sem_t *sem); +#define mac802154dev_givesem(s) sem_post(s); + +static int mac802154dev_open(FAR struct file *filep); +static int mac802154dev_close(FAR struct file *filep); +static ssize_t mac802154dev_read(FAR struct file *filep, FAR char *buffer, + size_t len); +static ssize_t mac802154dev_write(FAR struct file *filep, + FAR const char *buffer, size_t len); +static int mac802154dev_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations mac802154dev_fops = +{ + mac802154dev_open , /* open */ + mac802154dev_close, /* close */ + mac802154dev_read , /* read */ + mac802154dev_write, /* write */ + NULL, /* seek */ + mac802154dev_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , NULL /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mac802154dev_semtake + * + * Description: + * Acquire the semaphore used for access serialization. + * + ****************************************************************************/ + +static inline int mac802154dev_takesem(sem_t *sem) +{ + /* Take a count from the semaphore, possibly waiting */ + + if (sem_wait(sem) < 0) + { + /* EINTR is the only error that we expect */ + + int errcode = get_errno(); + DEBUGASSERT(errcode == EINTR); + return -errcode; + } + + return OK; +} + +/**************************************************************************** + * Name: mac802154dev_open + * + * Description: + * Open the 802.15.4 MAC character device. + * + ****************************************************************************/ + +static int mac802154dev_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct mac802154_devwrapper_s *dev; + FAR struct mac802154_open_s *opriv; + int ret; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + + dev = inode->i_private; + DEBUGASSERT(dev != NULL); + + /* Get exclusive access to the MAC driver data structure */ + + ret = mac802154dev_takesem(&dev->md_exclsem); + if (ret < 0) + { + wlerr("ERROR: mac802154dev_takesem failed: %d\n", ret); + return ret; + } + + /* Allocate a new open struct */ + + opriv = (FAR struct mac802154_open_s *)kmm_zalloc(sizeof(struct mac802154_open_s)); + if (!opriv) + { + wlerr("ERROR: Failed to allocate new open struct\n"); + ret = -ENOMEM; + goto errout_with_sem; + } + + /* Attach the open struct to the device */ + + opriv->md_flink = dev->md_open; + dev->md_open = opriv; + + /* Attach the open struct to the file structure */ + + filep->f_priv = (FAR void *)opriv; + ret = OK; + +errout_with_sem: + mac802154dev_givesem(&dev->md_exclsem); + return ret; +} + +/**************************************************************************** + * Name: mac802154dev_close + * + * Description: + * Close the 802.15.4 MAC character device. + * + ****************************************************************************/ + +static int mac802154dev_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct mac802154_devwrapper_s *dev; + FAR struct mac802154_open_s *opriv; + FAR struct mac802154_open_s *curr; + FAR struct mac802154_open_s *prev; + irqstate_t flags; + bool closing; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + dev = (FAR struct mac802154_devwrapper_s *)inode->i_private; + + /* Handle an improbable race conditions with the following atomic test + * and set. + * + * This is actually a pretty feeble attempt to handle this. The + * improbable race condition occurs if two different threads try to + * close the joystick driver at the same time. The rule: don't do + * that! It is feeble because we do not really enforce stale pointer + * detection anyway. + */ + + flags = enter_critical_section(); + closing = opriv->md_closing; + opriv->md_closing = true; + leave_critical_section(flags); + + if (closing) + { + /* Another thread is doing the close */ + + return OK; + } + + /* Get exclusive access to the driver structure */ + + ret = mac802154dev_takesem(&dev->md_exclsem); + if (ret < 0) + { + wlerr("ERROR: mac802154_takesem failed: %d\n", ret); + return ret; + } + + /* Find the open structure in the list of open structures for the device */ + + for (prev = NULL, curr = dev->md_open; + curr && curr != opriv; + prev = curr, curr = curr->md_flink); + + DEBUGASSERT(curr); + if (!curr) + { + wlerr("ERROR: Failed to find open entry\n"); + ret = -ENOENT; + goto errout_with_exclsem; + } + + /* Remove the structure from the device */ + + if (prev) + { + prev->md_flink = opriv->md_flink; + } + else + { + dev->md_open = opriv->md_flink; + } + + /* And free the open structure */ + + kmm_free(opriv); + + ret = OK; + +errout_with_exclsem: + mac802154dev_givesem(&dev->md_exclsem); + return ret; +} + +/**************************************************************************** + * Name: mac802154dev_read + * + * Description: + * Return the last received packet. + * + ****************************************************************************/ + +static ssize_t mac802154dev_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + FAR struct inode *inode; + FAR struct mac802154_devwrapper_s *dev; + int ret; + + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + dev = (FAR struct mac802154_devwrapper_s *)inode->i_private; + + /* Check to make sure that the buffer is big enough to hold at least one + * packet. + */ + + if (len < sizeof(struct ieee802154_frame_s)) + { + wlerr("ERROR: buffer too small: %lu\n", (unsigned long)len); + return -EINVAL; + } + + /* Get exclusive access to the driver structure */ + + ret = mac802154dev_takesem(&dev->md_exclsem); + if (ret < 0) + { + wlerr("ERROR: mac802154dev_takesem failed: %d\n", ret); + return ret; + } + + /* TODO: Add code to read a packet and return it */ + ret = -ENOTSUP; + + mac802154dev_givesem(&dev->md_exclsem); + return ret; +} + +/**************************************************************************** + * Name: mac802154dev_write + * + * Description: + * Send a packet over the IEEE802.15.4 network. + * + ****************************************************************************/ + +static ssize_t mac802154dev_write(FAR struct file *filep, + FAR const char *buffer, size_t len) +{ + FAR struct inode *inode; + FAR struct mac802154_devwrapper_s *dev; + struct ieee802154_frame_s frame; + int ret; + + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + dev = (FAR struct mac802154_devwrapper_s *)inode->i_private; + + /* Check to make sure that the buffer is big enough to hold at least one + * packet. + */ + + if (len < sizeof(struct ieee802154_frame_s)) + { + wlerr("ERROR: buffer too small: %lu\n", (unsigned long)len); + return -EINVAL; + } + + DEBUGASSERT(buffer != NULL); + frame = *(FAR struct ieee802154_frame_s *)buffer; + + /* Get exclusive access to the driver structure */ + + ret = mac802154dev_takesem(&dev->md_exclsem); + if (ret < 0) + { + wlerr("ERROR: mac802154dev_takesem failed: %d\n", ret); + return ret; + } + + /* TODO: Send the frame out */ + ret = -ENOTSUP; + + mac802154dev_givesem(&dev->md_exclsem); + return ret; +} + +/**************************************************************************** + * Name: mac802154dev_ioctl + * + * Description: + * Control the MAC layer via MLME IOCTL commands. + * + ****************************************************************************/ + +static int mac802154dev_ioctl(FAR struct file *filep, int cmd, + unsigned long arg) +{ + FAR struct inode *inode; + FAR struct mac802154_devwrapper_s *dev; + FAR struct ieee802154_mac_s *mac; + int ret; + + DEBUGASSERT(filep != NULL && filep->f_priv != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + dev = (FAR struct mac802154_devwrapper_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = mac802154dev_takesem(&dev->md_exclsem); + if (ret < 0) + { + wlerr("ERROR: mac802154dev_takesem failed: %d\n", ret); + return ret; + } + + /* Handle the ioctl command */ + + switch (cmd) + { + default: + wlerr("ERROR: Unrecognized command %ld\n", cmd); + ret = -EINVAL; + break; + } + + mac802154dev_givesem(&dev->md_exclsem); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mac802154dev_register + * + * Description: + * Register a character driver to access the IEEE 802.15.4 MAC layer from + * user-space + * + * Input Parameters: + * mac - Pointer to the mac layer struct to be registerd. + * minor - The device minor number. The IEEE802.15.4 MAC character device + * will be registered as /dev/ieeeN where N is the minor number + * + * Returned Values: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int mac802154dev_register(FAR struct ieee802154_mac_s *mac, int minor) +{ + FAR struct mac802154_devwrapper_s *dev; + char devname[DEVNAME_FMTLEN]; + int ret; + + dev = kmm_zalloc(sizeof(struct mac802154_devwrapper_s)); + if (!dev) + { + wlerr("ERROR: Failed to allocate device structure\n"); + return -ENOMEM; + } + + /* Initialize the new mac driver instance */ + + dev->md_mac = mac; + sem_init(&dev->md_exclsem, 0, 1); /* Allow the device to be opened once before blocking */ + + /* Create the character device name */ + + snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, minor); + + /* Register the mac character driver */ + + ret = register_driver(devname, &mac802154dev_fops, 0666, dev); + if (ret < 0) + { + wlerr("ERROR: register_driver failed: %d\n", ret); + goto errout_with_priv; + } + + return OK; + +errout_with_priv: + sem_destroy(&dev->md_exclsem); + kmm_free(dev); + return ret; +}