| /**************************************************************************** |
| * wireless/ieee802154/mac802154_device.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 <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <time.h> |
| #include <fcntl.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/signal.h> |
| #include <nuttx/mm/iob.h> |
| |
| #include <nuttx/wireless/ieee802154/ieee802154_device.h> |
| #include <nuttx/wireless/ieee802154/ieee802154_mac.h> |
| |
| #include "mac802154.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Device naming ************************************************************/ |
| |
| #define DEVNAME_FMT "/dev/ieee%d" |
| #define DEVNAME_FMTLEN (9 + 3 + 1) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This structure describes the state of one open mac driver instance */ |
| |
| struct mac802154dev_open_s |
| { |
| /* Supports a singly linked list */ |
| |
| FAR struct mac802154dev_open_s *md_flink; |
| |
| /* The following will be true if we are closing */ |
| |
| volatile bool md_closing; |
| }; |
| |
| struct mac802154dev_callback_s |
| { |
| /* This holds the information visible to the MAC layer */ |
| |
| struct mac802154_maccb_s mc_cb; /* Interface understood by |
| * the MAC layer |
| */ |
| FAR struct mac802154_chardevice_s *mc_priv; /* Our priv data */ |
| }; |
| |
| struct mac802154_chardevice_s |
| { |
| MACHANDLE md_mac; /* Saved binding to the mac layer */ |
| struct mac802154dev_callback_s md_cb; /* Callback information */ |
| mutex_t md_lock; /* Exclusive device access */ |
| |
| /* Hold a list of events */ |
| |
| bool enableevents : 1; /* Are events enabled? */ |
| bool geteventpending : 1; /* Is there a get event using the semaphore? */ |
| sem_t geteventsem; /* Signaling semaphore for waiting get event */ |
| sq_queue_t primitive_queue; /* For holding primitives to pass along */ |
| |
| /* The following is a singly linked list of open references to the |
| * MAC device. |
| */ |
| |
| FAR struct mac802154dev_open_s *md_open; |
| |
| /* Hold a list of transactions as a "readahead" buffer */ |
| |
| bool readpending; /* Is there a read using the semaphore? */ |
| sem_t readsem; /* Signaling semaphore for waiting read */ |
| sq_queue_t dataind_queue; |
| |
| /* MAC Service notification information */ |
| |
| bool md_notify_registered; |
| pid_t md_notify_pid; |
| struct sigevent md_notify_event; |
| struct sigwork_s md_notify_work; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int mac802154dev_notify(FAR struct mac802154_maccb_s *maccb, |
| FAR struct ieee802154_primitive_s *primitive); |
| static int mac802154dev_rxframe(FAR struct mac802154_chardevice_s *dev, |
| FAR struct ieee802154_data_ind_s *ind); |
| |
| 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 g_mac802154dev_fops = |
| { |
| mac802154dev_open, /* open */ |
| mac802154dev_close, /* close */ |
| mac802154dev_read, /* read */ |
| mac802154dev_write, /* write */ |
| NULL, /* seek */ |
| mac802154dev_ioctl, /* ioctl */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * 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_chardevice_s *dev; |
| FAR struct mac802154dev_open_s *opriv; |
| int ret; |
| |
| inode = filep->f_inode; |
| |
| dev = inode->i_private; |
| DEBUGASSERT(dev != NULL); |
| |
| /* Get exclusive access to the MAC driver data structure */ |
| |
| ret = nxmutex_lock(&dev->md_lock); |
| if (ret < 0) |
| { |
| wlerr("ERROR: nxsem_wait failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* Allocate a new open struct */ |
| |
| opriv = (FAR struct mac802154dev_open_s *) |
| kmm_zalloc(sizeof(struct mac802154dev_open_s)); |
| |
| if (!opriv) |
| { |
| wlerr("ERROR: Failed to allocate new open struct\n"); |
| ret = -ENOMEM; |
| goto errout_with_lock; |
| } |
| |
| /* 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_lock: |
| nxmutex_unlock(&dev->md_lock); |
| 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_chardevice_s *dev; |
| FAR struct mac802154dev_open_s *opriv; |
| FAR struct mac802154dev_open_s *curr; |
| FAR struct mac802154dev_open_s *prev; |
| irqstate_t flags; |
| bool closing; |
| int ret; |
| |
| DEBUGASSERT(filep->f_priv); |
| opriv = filep->f_priv; |
| inode = filep->f_inode; |
| DEBUGASSERT(inode->i_private); |
| dev = 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 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 = nxmutex_lock(&dev->md_lock); |
| if (ret < 0) |
| { |
| wlerr("ERROR: nxsem_wait 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_lock; |
| } |
| |
| /* 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); |
| |
| /* If there are now no open instances of the driver and a signal handler is |
| * not registered, purge the list of events. |
| */ |
| |
| if (dev->md_open) |
| { |
| FAR struct ieee802154_primitive_s *primitive; |
| |
| primitive = |
| (FAR struct ieee802154_primitive_s *) |
| sq_remfirst(&dev->primitive_queue); |
| |
| while (primitive) |
| { |
| ieee802154_primitive_free(primitive); |
| primitive = |
| (FAR struct ieee802154_primitive_s *) |
| sq_remfirst(&dev->primitive_queue); |
| } |
| } |
| |
| ret = OK; |
| |
| errout_with_lock: |
| nxmutex_unlock(&dev->md_lock); |
| 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_chardevice_s *dev; |
| FAR struct mac802154dev_rxframe_s *rx; |
| FAR struct ieee802154_data_ind_s *ind; |
| struct ieee802154_get_req_s req; |
| int ret; |
| |
| inode = filep->f_inode; |
| DEBUGASSERT(inode->i_private); |
| dev = inode->i_private; |
| |
| /* Check to make sure the buffer is the right size for the struct */ |
| |
| if (len != sizeof(struct mac802154dev_rxframe_s)) |
| { |
| wlerr("ERROR: buffer isn't a mac802154dev_rxframe_s: %lu\n", |
| (unsigned long)len); |
| return -EINVAL; |
| } |
| |
| DEBUGASSERT(buffer != NULL); |
| rx = (FAR struct mac802154dev_rxframe_s *)buffer; |
| |
| while (1) |
| { |
| /* Get exclusive access to the driver structure */ |
| |
| ret = nxmutex_lock(&dev->md_lock); |
| if (ret < 0) |
| { |
| wlerr("ERROR: nxsem_wait failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* Try popping a data indication off the list */ |
| |
| ind = (FAR struct ieee802154_data_ind_s *) |
| sq_remfirst(&dev->dataind_queue); |
| |
| /* If the indication is not null, we can exit the loop and copy the |
| * data |
| */ |
| |
| if (ind != NULL) |
| { |
| nxmutex_unlock(&dev->md_lock); |
| break; |
| } |
| |
| /* If this is a non-blocking call, or if there is another read |
| * operation already pending, don't block. This driver returns EAGAIN |
| * even when configured as non-blocking if another read operation is |
| * already pending. This situation should be rare. |
| * It will only occur when there are 2 calls to read from separate |
| * threads and there was no data in the rx list. |
| */ |
| |
| if ((filep->f_oflags & O_NONBLOCK) || dev->readpending) |
| { |
| nxmutex_unlock(&dev->md_lock); |
| return -EAGAIN; |
| } |
| |
| dev->readpending = true; |
| nxmutex_unlock(&dev->md_lock); |
| |
| /* Wait to be signaled when a frame is added to the list */ |
| |
| ret = nxsem_wait(&dev->readsem); |
| if (ret < 0) |
| { |
| dev->readpending = false; |
| return ret; |
| } |
| |
| /* Let the loop wrap back around, we will then pop a indication and |
| * this time, it should have a data indication |
| */ |
| } |
| |
| /* Check if the MAC layer is in promiscuous mode. */ |
| |
| req.attr = IEEE802154_ATTR_MAC_PROMISCUOUS_MODE; |
| |
| ret = mac802154_ioctl(dev->md_mac, MAC802154IOC_MLME_GET_REQUEST, |
| (unsigned long)&req); |
| |
| if (ret == 0 && req.attrval.mac.promisc_mode) |
| { |
| /* If it is, add the FCS back into the frame by increasing the length |
| * by the PHY layer's FCS length. |
| */ |
| |
| req.attr = IEEE802154_ATTR_PHY_FCS_LEN; |
| ret = mac802154_ioctl(dev->md_mac, MAC802154IOC_MLME_GET_REQUEST, |
| (unsigned long)&req); |
| if (ret == OK) |
| { |
| rx->length = ind->frame->io_len + req.attrval.phy.fcslen; |
| rx->offset = ind->frame->io_offset; |
| } |
| |
| /* Copy the entire frame from the IOB to the user supplied struct */ |
| |
| memcpy(&rx->payload[0], &ind->frame->io_data[0], rx->length); |
| } |
| else |
| { |
| rx->length = (ind->frame->io_len - ind->frame->io_offset); |
| rx->offset = 0; |
| |
| /* Copy just the payload from the IOB to the user supplied struct */ |
| |
| memcpy(&rx->payload[0], &ind->frame->io_data[ind->frame->io_offset], |
| rx->length); |
| } |
| |
| memcpy(&rx->meta, ind, sizeof(struct ieee802154_data_ind_s)); |
| |
| /* Zero out the forward link and IOB reference */ |
| |
| rx->meta.flink = NULL; |
| rx->meta.frame = NULL; |
| |
| /* Free the IOB */ |
| |
| iob_free(ind->frame); |
| |
| /* Deallocate the data indication */ |
| |
| ieee802154_primitive_free((FAR struct ieee802154_primitive_s *)ind); |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * 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_chardevice_s *dev; |
| FAR struct mac802154dev_txframe_s *tx; |
| FAR struct iob_s *iob; |
| int ret; |
| |
| inode = filep->f_inode; |
| DEBUGASSERT(inode->i_private); |
| dev = inode->i_private; |
| |
| /* Check if the struct is the correct size */ |
| |
| if (len != sizeof(struct mac802154dev_txframe_s)) |
| { |
| wlerr("ERROR: buffer isn't a mac802154dev_txframe_s: %lu\n", |
| (unsigned long)len); |
| |
| return -EINVAL; |
| } |
| |
| DEBUGASSERT(buffer != NULL); |
| tx = (FAR struct mac802154dev_txframe_s *)buffer; |
| |
| /* Allocate an IOB to put the frame in */ |
| |
| iob = iob_alloc(false); |
| DEBUGASSERT(iob != NULL); |
| |
| /* Get the MAC header length */ |
| |
| ret = mac802154_get_mhrlen(dev->md_mac, &tx->meta); |
| if (ret < 0) |
| { |
| wlerr("ERROR: TX meta-data is invalid"); |
| return ret; |
| } |
| |
| iob->io_offset = ret; |
| iob->io_len = iob->io_offset; |
| |
| memcpy(&iob->io_data[iob->io_offset], tx->payload, tx->length); |
| |
| iob->io_len += tx->length; |
| |
| /* Pass the request to the MAC layer */ |
| |
| ret = mac802154_req_data(dev->md_mac, &tx->meta, iob); |
| if (ret < 0) |
| { |
| iob_free(iob); |
| wlerr("ERROR: req_data failed %d\n", ret); |
| return ret; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: mac802154dev_ioctl |
| * |
| * Description: |
| * Control the MAC layer via IOCTL commands. |
| * |
| ****************************************************************************/ |
| |
| static int mac802154dev_ioctl(FAR struct file *filep, int cmd, |
| unsigned long arg) |
| { |
| FAR struct inode *inode; |
| FAR struct mac802154_chardevice_s *dev; |
| FAR union ieee802154_macarg_u *macarg = |
| (FAR union ieee802154_macarg_u *)((uintptr_t)arg); |
| int ret; |
| |
| DEBUGASSERT(filep->f_priv != NULL && |
| filep->f_inode != NULL); |
| inode = filep->f_inode; |
| DEBUGASSERT(inode->i_private); |
| dev = inode->i_private; |
| |
| /* Get exclusive access to the driver structure */ |
| |
| ret = nxmutex_lock(&dev->md_lock); |
| if (ret < 0) |
| { |
| wlerr("ERROR: nxsem_wait failed: %d\n", ret); |
| return ret; |
| } |
| |
| switch (cmd) |
| { |
| /* Command: MAC802154IOC_NOTIFY_REGISTER |
| * Description: Register to receive a signal whenever there is a |
| * event primitive sent from the MAC layer. |
| * Argument: The signal number to use. |
| * Return: Zero (OK) on success. Minus one will be returned on |
| * failure with the errno value set appropriately. |
| */ |
| |
| case MAC802154IOC_NOTIFY_REGISTER: |
| { |
| /* Save the notification events */ |
| |
| dev->md_notify_event = macarg->event; |
| dev->md_notify_pid = nxsched_getpid(); |
| dev->md_notify_registered = true; |
| |
| ret = OK; |
| } |
| break; |
| |
| case MAC802154IOC_GET_EVENT: |
| { |
| FAR struct ieee802154_primitive_s *primitive; |
| |
| while (1) |
| { |
| /* Try popping an event off the queue */ |
| |
| primitive = (FAR struct ieee802154_primitive_s *) |
| sq_remfirst(&dev->primitive_queue); |
| |
| /* If there was an event to pop off, copy it into the user data |
| * and free it from the MAC layer's memory. |
| */ |
| |
| if (primitive != NULL) |
| { |
| memcpy(&macarg->primitive, |
| primitive, |
| sizeof(struct ieee802154_primitive_s)); |
| |
| /* Free the notification */ |
| |
| ieee802154_primitive_free(primitive); |
| ret = OK; |
| break; |
| } |
| |
| /* If this is a non-blocking call, or if there is another |
| * getevent operation already pending, don't block. This driver |
| * returns EAGAIN even when configured as non-blocking if |
| * another getevent operation is already pending This situation |
| * should be rare. |
| * It will only occur when there are 2 calls from separate |
| * threads and there was no events in the queue. |
| */ |
| |
| if ((filep->f_oflags & O_NONBLOCK) || dev->geteventpending) |
| { |
| ret = -EAGAIN; |
| break; |
| } |
| |
| dev->geteventpending = true; |
| nxmutex_unlock(&dev->md_lock); |
| |
| /* Wait to be signaled when an event is queued */ |
| |
| ret = nxsem_wait(&dev->geteventsem); |
| if (ret < 0) |
| { |
| dev->geteventpending = false; |
| return ret; |
| } |
| |
| /* Get exclusive access again, then loop back around and try |
| * and pop an event off the queue |
| */ |
| |
| ret = nxmutex_lock(&dev->md_lock); |
| if (ret < 0) |
| { |
| wlerr("ERROR: nxsem_wait failed: %d\n", ret); |
| return ret; |
| } |
| } |
| } |
| break; |
| |
| case MAC802154IOC_ENABLE_EVENTS: |
| { |
| dev->enableevents = macarg->enable; |
| ret = OK; |
| } |
| break; |
| |
| default: |
| { |
| /* Forward any unrecognized commands to the MAC layer */ |
| |
| ret = mac802154_ioctl(dev->md_mac, cmd, arg); |
| } |
| break; |
| } |
| |
| nxmutex_unlock(&dev->md_lock); |
| return ret; |
| } |
| |
| static int mac802154dev_notify(FAR struct mac802154_maccb_s *maccb, |
| FAR struct ieee802154_primitive_s *primitive) |
| { |
| FAR struct mac802154dev_callback_s *cb = |
| (FAR struct mac802154dev_callback_s *)maccb; |
| FAR struct mac802154_chardevice_s *dev; |
| |
| DEBUGASSERT(cb != NULL && cb->mc_priv != NULL); |
| dev = cb->mc_priv; |
| |
| /* Handle the special case for data indications or "incoming frames" */ |
| |
| if (primitive->type == IEEE802154_PRIMITIVE_IND_DATA) |
| { |
| return mac802154dev_rxframe(dev, &primitive->u.dataind); |
| } |
| |
| /* If there is a registered notification receiver, queue the event and |
| * signal the receiver. Events should be popped from the queue from the |
| * application at a reasonable rate in order for the MAC layer to be able |
| * to allocate new notifications. |
| */ |
| |
| if (dev->enableevents && |
| (dev->md_open != NULL || dev->md_notify_registered)) |
| { |
| /* Get exclusive access to the driver structure. We don't care about |
| * any signals so if we see one, just go back to trying to get access |
| * again |
| */ |
| |
| while (nxmutex_lock(&dev->md_lock) != 0); |
| |
| sq_addlast((FAR sq_entry_t *)primitive, &dev->primitive_queue); |
| |
| /* Check if there is a read waiting for data */ |
| |
| if (dev->geteventpending) |
| { |
| /* Wake the thread waiting for the data transmission */ |
| |
| dev->geteventpending = false; |
| nxsem_post(&dev->geteventsem); |
| } |
| |
| if (dev->md_notify_registered) |
| { |
| dev->md_notify_event.sigev_value.sival_int = primitive->type; |
| nxsig_notification(dev->md_notify_pid, &dev->md_notify_event, |
| SI_QUEUE, &dev->md_notify_work); |
| } |
| |
| nxmutex_unlock(&dev->md_lock); |
| return OK; |
| } |
| |
| /* By returning a negative value, we let the MAC know that we don't want |
| * the primitive and it will free it for us |
| */ |
| |
| return -1; |
| } |
| |
| /**************************************************************************** |
| * Name: mac802154dev_rxframe |
| * |
| * Description: |
| * Handle received frames forward by the IEEE 802.15.4 MAC. |
| * |
| * Returned Value: |
| * any failure. On success, the ind and its contained iob will be freed. |
| * The ind will be intact if this function returns a failure. |
| * |
| ****************************************************************************/ |
| |
| static int mac802154dev_rxframe(FAR struct mac802154_chardevice_s *dev, |
| FAR struct ieee802154_data_ind_s *ind) |
| { |
| /* Get exclusive access to the driver structure. We don't care about any |
| * signals so if we see one, just go back to trying to get access again |
| */ |
| |
| while (nxmutex_lock(&dev->md_lock) != 0); |
| |
| /* Push the indication onto the list */ |
| |
| sq_addlast((FAR sq_entry_t *)ind, &dev->dataind_queue); |
| |
| /* Check if there is a read waiting for data */ |
| |
| if (dev->readpending) |
| { |
| /* Wake the thread waiting for the data transmission */ |
| |
| dev->readpending = false; |
| nxsem_post(&dev->readsem); |
| } |
| |
| /* Release the driver */ |
| |
| nxmutex_unlock(&dev->md_lock); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * 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 registered. |
| * 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 Value: |
| * Zero (OK) is returned on success. Otherwise a negated errno value is |
| * returned to indicate the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| int mac802154dev_register(MACHANDLE mac, int minor) |
| { |
| FAR struct mac802154_chardevice_s *dev; |
| FAR struct mac802154_maccb_s *maccb; |
| char devname[DEVNAME_FMTLEN]; |
| int ret; |
| |
| dev = kmm_zalloc(sizeof(struct mac802154_chardevice_s)); |
| if (!dev) |
| { |
| wlerr("ERROR: Failed to allocate device structure\n"); |
| return -ENOMEM; |
| } |
| |
| /* Initialize the new mac driver instance */ |
| |
| dev->md_mac = mac; |
| nxmutex_init(&dev->md_lock); /* Allow the device to be opened once |
| * before blocking */ |
| |
| nxsem_init(&dev->readsem, 0, 0); |
| dev->readpending = false; |
| |
| sq_init(&dev->dataind_queue); |
| |
| dev->geteventpending = false; |
| nxsem_init(&dev->geteventsem, 0, 0); |
| |
| sq_init(&dev->primitive_queue); |
| |
| dev->enableevents = true; |
| dev->md_notify_registered = false; |
| |
| /* Initialize the MAC callbacks */ |
| |
| dev->md_cb.mc_priv = dev; |
| |
| maccb = &dev->md_cb.mc_cb; |
| maccb->flink = NULL; |
| maccb->prio = CONFIG_IEEE802154_MACDEV_RECVRPRIO; |
| maccb->notify = mac802154dev_notify; |
| |
| /* Bind the callback structure */ |
| |
| ret = mac802154_bind(mac, maccb); |
| if (ret < 0) |
| { |
| nerr("ERROR: Failed to bind the MAC callbacks: %d\n", ret); |
| goto errout_with_priv; |
| } |
| |
| /* Create the character device name */ |
| |
| snprintf(devname, sizeof(devname), DEVNAME_FMT, minor); |
| |
| /* Register the mac character driver */ |
| |
| ret = register_driver(devname, &g_mac802154dev_fops, 0666, dev); |
| if (ret < 0) |
| { |
| wlerr("ERROR: register_driver failed: %d\n", ret); |
| goto errout_with_priv; |
| } |
| |
| return OK; |
| |
| errout_with_priv: |
| nxmutex_destroy(&dev->md_lock); |
| kmm_free(dev); |
| return ret; |
| } |