diff options
Diffstat (limited to 'net/mac802154')
-rw-r--r-- | net/mac802154/Kconfig | 2 | ||||
-rw-r--r-- | net/mac802154/Makefile | 4 | ||||
-rw-r--r-- | net/mac802154/cfg.c | 210 | ||||
-rw-r--r-- | net/mac802154/cfg.h | 9 | ||||
-rw-r--r-- | net/mac802154/driver-ops.h | 222 | ||||
-rw-r--r-- | net/mac802154/ieee802154_dev.c | 415 | ||||
-rw-r--r-- | net/mac802154/ieee802154_i.h (renamed from net/mac802154/mac802154.h) | 132 | ||||
-rw-r--r-- | net/mac802154/iface.c | 586 | ||||
-rw-r--r-- | net/mac802154/llsec.c | 23 | ||||
-rw-r--r-- | net/mac802154/mac_cmd.c | 88 | ||||
-rw-r--r-- | net/mac802154/main.c | 217 | ||||
-rw-r--r-- | net/mac802154/mib.c | 271 | ||||
-rw-r--r-- | net/mac802154/monitor.c | 117 | ||||
-rw-r--r-- | net/mac802154/rx.c | 310 | ||||
-rw-r--r-- | net/mac802154/tx.c | 154 | ||||
-rw-r--r-- | net/mac802154/util.c | 84 | ||||
-rw-r--r-- | net/mac802154/wpan.c | 599 |
17 files changed, 1885 insertions, 1558 deletions
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig index 1818a99b3081..aa462b480a39 100644 --- a/net/mac802154/Kconfig +++ b/net/mac802154/Kconfig @@ -16,5 +16,5 @@ config MAC802154 been tested yet! If you plan to use HardMAC IEEE 802.15.4 devices, you can - say N here. Alternatievly you can say M to compile it as + say N here. Alternatively you can say M to compile it as module. diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 9723d6f3f3e5..702d8b466317 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_MAC802154) += mac802154.o -mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \ - monitor.o wpan.o llsec.o +mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \ + iface.o llsec.o util.o cfg.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c new file mode 100644 index 000000000000..c035708ada16 --- /dev/null +++ b/net/mac802154/cfg.c @@ -0,0 +1,210 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/mac80211/cfg.c + */ + +#include <net/rtnetlink.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "driver-ops.h" +#include "cfg.h" + +static struct net_device * +ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy, + const char *name, int type) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + struct net_device *dev; + + rtnl_lock(); + dev = ieee802154_if_add(local, name, type, + cpu_to_le64(0x0000000000000000ULL)); + rtnl_unlock(); + + return dev; +} + +static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy, + struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + ieee802154_if_remove(sdata); +} + +static int +ieee802154_add_iface(struct wpan_phy *phy, const char *name, + enum nl802154_iftype type, __le64 extended_addr) +{ + struct ieee802154_local *local = wpan_phy_priv(phy); + struct net_device *err; + + err = ieee802154_if_add(local, name, type, extended_addr); + if (IS_ERR(err)) + return PTR_ERR(err); + + return 0; +} + +static int +ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev) +{ + ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev)); + + return 0; +} + +static int +ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + int ret; + + ASSERT_RTNL(); + + /* check if phy support this setting */ + if (!(wpan_phy->channels_supported[page] & BIT(channel))) + return -EINVAL; + + ret = drv_set_channel(local, page, channel); + if (!ret) { + wpan_phy->current_page = page; + wpan_phy->current_channel = channel; + } + + return ret; +} + +static int +ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le16 pan_id) +{ + ASSERT_RTNL(); + + /* TODO + * I am not sure about to check here on broadcast pan_id. + * Broadcast is a valid setting, comment from 802.15.4: + * If this value is 0xffff, the device is not associated. + * + * This could useful to simple deassociate an device. + */ + if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST)) + return -EINVAL; + + wpan_dev->pan_id = pan_id; + return 0; +} + +static int +ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + u8 min_be, u8 max_be) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) + return -EOPNOTSUPP; + + wpan_dev->min_be = min_be; + wpan_dev->max_be = max_be; + return 0; +} + +static int +ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le16 short_addr) +{ + ASSERT_RTNL(); + + /* TODO + * I am not sure about to check here on broadcast short_addr. + * Broadcast is a valid setting, comment from 802.15.4: + * A value of 0xfffe indicates that the device has + * associated but has not been allocated an address. A + * value of 0xffff indicates that the device does not + * have a short address. + * + * I think we should allow to set these settings but + * don't allow to allow socket communication with it. + */ + if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) || + short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST)) + return -EINVAL; + + wpan_dev->short_addr = short_addr; + return 0; +} + +static int +ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + u8 max_csma_backoffs) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) + return -EOPNOTSUPP; + + wpan_dev->csma_retries = max_csma_backoffs; + return 0; +} + +static int +ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + s8 max_frame_retries) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES)) + return -EOPNOTSUPP; + + wpan_dev->frame_retries = max_frame_retries; + return 0; +} + +static int +ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + bool mode) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_LBT)) + return -EOPNOTSUPP; + + wpan_dev->lbt = mode; + return 0; +} + +const struct cfg802154_ops mac802154_config_ops = { + .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated, + .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated, + .add_virtual_intf = ieee802154_add_iface, + .del_virtual_intf = ieee802154_del_iface, + .set_channel = ieee802154_set_channel, + .set_pan_id = ieee802154_set_pan_id, + .set_short_addr = ieee802154_set_short_addr, + .set_backoff_exponent = ieee802154_set_backoff_exponent, + .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs, + .set_max_frame_retries = ieee802154_set_max_frame_retries, + .set_lbt_mode = ieee802154_set_lbt_mode, +}; diff --git a/net/mac802154/cfg.h b/net/mac802154/cfg.h new file mode 100644 index 000000000000..e2718f981e82 --- /dev/null +++ b/net/mac802154/cfg.h @@ -0,0 +1,9 @@ +/* mac802154 configuration hooks for cfg802154 + */ + +#ifndef __CFG_H +#define __CFG_H + +extern const struct cfg802154_ops mac802154_config_ops; + +#endif /* __CFG_H */ diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h new file mode 100644 index 000000000000..f21e864613d0 --- /dev/null +++ b/net/mac802154/driver-ops.h @@ -0,0 +1,222 @@ +#ifndef __MAC802154_DRVIER_OPS +#define __MAC802154_DRIVER_OPS + +#include <linux/types.h> +#include <linux/rtnetlink.h> + +#include <net/mac802154.h> + +#include "ieee802154_i.h" + +static inline int +drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb) +{ + return local->ops->xmit_async(&local->hw, skb); +} + +static inline int +drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb) +{ + /* don't allow other operations while sync xmit */ + ASSERT_RTNL(); + + might_sleep(); + + return local->ops->xmit_sync(&local->hw, skb); +} + +static inline int drv_start(struct ieee802154_local *local) +{ + might_sleep(); + + local->started = true; + smp_mb(); + + return local->ops->start(&local->hw); +} + +static inline void drv_stop(struct ieee802154_local *local) +{ + might_sleep(); + + local->ops->stop(&local->hw); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; +} + +static inline int +drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel) +{ + might_sleep(); + + return local->ops->set_channel(&local->hw, page, channel); +} + +static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm) +{ + might_sleep(); + + if (!local->ops->set_txpower) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_txpower(&local->hw, dbm); +} + +static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode) +{ + might_sleep(); + + if (!local->ops->set_cca_mode) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_cca_mode(&local->hw, cca_mode); +} + +static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode) +{ + might_sleep(); + + if (!local->ops->set_lbt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_lbt(&local->hw, mode); +} + +static inline int +drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level) +{ + might_sleep(); + + if (!local->ops->set_cca_ed_level) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_cca_ed_level(&local->hw, ed_level); +} + +static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_id = pan_id; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANID_CHANGED); +} + +static inline int +drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.ieee_addr = extended_addr; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_IEEEADDR_CHANGED); +} + +static inline int +drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.short_addr = short_addr; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_SADDR_CHANGED); +} + +static inline int +drv_set_pan_coord(struct ieee802154_local *local, bool is_coord) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_coord = is_coord; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANC_CHANGED); +} + +static inline int +drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be, + u8 max_csma_backoffs) +{ + might_sleep(); + + if (!local->ops->set_csma_params) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_csma_params(&local->hw, min_be, max_be, + max_csma_backoffs); +} + +static inline int +drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries) +{ + might_sleep(); + + if (!local->ops->set_frame_retries) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_frame_retries(&local->hw, max_frame_retries); +} + +static inline int +drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) +{ + might_sleep(); + + if (!local->ops->set_promiscuous_mode) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_promiscuous_mode(&local->hw, on); +} + +#endif /* __MAC802154_DRVIER_OPS */ diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c deleted file mode 100644 index b36b2b996578..000000000000 --- a/net/mac802154/ieee802154_dev.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2007-2012 Siemens AG - * - * Written by: - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - * - * Based on the code from 'linux-zigbee.sourceforge.net' project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/netdevice.h> - -#include <net/netlink.h> -#include <linux/nl802154.h> -#include <net/mac802154.h> -#include <net/ieee802154_netdev.h> -#include <net/route.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -int mac802154_slave_open(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_sub_if_data *subif; - struct mac802154_priv *ipriv = priv->hw; - int res = 0; - - ASSERT_RTNL(); - - if (priv->type == IEEE802154_DEV_WPAN) { - mutex_lock(&priv->hw->slaves_mtx); - list_for_each_entry(subif, &priv->hw->slaves, list) { - if (subif != priv && subif->type == priv->type && - subif->running) { - mutex_unlock(&priv->hw->slaves_mtx); - return -EBUSY; - } - } - mutex_unlock(&priv->hw->slaves_mtx); - } - - mutex_lock(&priv->hw->slaves_mtx); - priv->running = true; - mutex_unlock(&priv->hw->slaves_mtx); - - if (ipriv->open_count++ == 0) { - res = ipriv->ops->start(&ipriv->hw); - WARN_ON(res); - if (res) - goto err; - } - - if (ipriv->ops->ieee_addr) { - __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - res = ipriv->ops->ieee_addr(&ipriv->hw, addr); - WARN_ON(res); - if (res) - goto err; - mac802154_dev_set_ieee_addr(dev); - } - - netif_start_queue(dev); - return 0; -err: - priv->hw->open_count--; - - return res; -} - -int mac802154_slave_close(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_priv *ipriv = priv->hw; - - ASSERT_RTNL(); - - netif_stop_queue(dev); - - mutex_lock(&priv->hw->slaves_mtx); - priv->running = false; - mutex_unlock(&priv->hw->slaves_mtx); - - if (!--ipriv->open_count) - ipriv->ops->stop(&ipriv->hw); - - return 0; -} - -static int -mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - struct mac802154_priv *ipriv; - int err; - - ipriv = wpan_phy_priv(phy); - - priv = netdev_priv(dev); - priv->dev = dev; - priv->hw = ipriv; - - dev->needed_headroom = ipriv->hw.extra_tx_headroom; - - SET_NETDEV_DEV(dev, &ipriv->phy->dev); - - mutex_lock(&ipriv->slaves_mtx); - if (!ipriv->running) { - mutex_unlock(&ipriv->slaves_mtx); - return -ENODEV; - } - mutex_unlock(&ipriv->slaves_mtx); - - err = register_netdev(dev); - if (err < 0) - return err; - - rtnl_lock(); - mutex_lock(&ipriv->slaves_mtx); - list_add_tail_rcu(&priv->list, &ipriv->slaves); - mutex_unlock(&ipriv->slaves_mtx); - rtnl_unlock(); - - return 0; -} - -static void -mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev) -{ - struct mac802154_sub_if_data *sdata; - - ASSERT_RTNL(); - - sdata = netdev_priv(dev); - - BUG_ON(sdata->hw->phy != phy); - - mutex_lock(&sdata->hw->slaves_mtx); - list_del_rcu(&sdata->list); - mutex_unlock(&sdata->hw->slaves_mtx); - - synchronize_rcu(); - unregister_netdevice(sdata->dev); -} - -static struct net_device * -mac802154_add_iface(struct wpan_phy *phy, const char *name, int type) -{ - struct net_device *dev; - int err = -ENOMEM; - - switch (type) { - case IEEE802154_DEV_MONITOR: - dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, NET_NAME_UNKNOWN, - mac802154_monitor_setup); - break; - case IEEE802154_DEV_WPAN: - dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, NET_NAME_UNKNOWN, - mac802154_wpan_setup); - break; - default: - dev = NULL; - err = -EINVAL; - break; - } - if (!dev) - goto err; - - err = mac802154_netdev_register(phy, dev); - if (err) - goto err_free; - - dev_hold(dev); /* we return an incremented device refcount */ - return dev; - -err_free: - free_netdev(dev); -err: - return ERR_PTR(err); -} - -static int mac802154_set_txpower(struct wpan_phy *phy, int db) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_txpower(&priv->hw, db); -} - -static int mac802154_set_lbt(struct wpan_phy *phy, bool on) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_lbt(&priv->hw, on); -} - -static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_cca_mode(&priv->hw, mode); -} - -static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_cca_ed_level(&priv->hw, level); -} - -static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be, - u8 max_be, u8 retries) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries); -} - -static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_frame_retries(&priv->hw, retries); -} - -struct ieee802154_dev * -ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) -{ - struct wpan_phy *phy; - struct mac802154_priv *priv; - size_t priv_size; - - if (!ops || !ops->xmit || !ops->ed || !ops->start || - !ops->stop || !ops->set_channel) { - pr_err("undefined IEEE802.15.4 device operations\n"); - return NULL; - } - - /* Ensure 32-byte alignment of our private data and hw private data. - * We use the wpan_phy priv data for both our mac802154_priv and for - * the driver's private data - * - * in memory it'll be like this: - * - * +-----------------------+ - * | struct wpan_phy | - * +-----------------------+ - * | struct mac802154_priv | - * +-----------------------+ - * | driver's private data | - * +-----------------------+ - * - * Due to ieee802154 layer isn't aware of driver and MAC structures, - * so lets allign them here. - */ - - priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len; - - phy = wpan_phy_alloc(priv_size); - if (!phy) { - pr_err("failure to allocate master IEEE802.15.4 device\n"); - return NULL; - } - - priv = wpan_phy_priv(phy); - priv->phy = phy; - priv->hw.phy = priv->phy; - priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN); - priv->ops = ops; - - INIT_LIST_HEAD(&priv->slaves); - mutex_init(&priv->slaves_mtx); - - return &priv->hw; -} -EXPORT_SYMBOL(ieee802154_alloc_device); - -void ieee802154_free_device(struct ieee802154_dev *hw) -{ - struct mac802154_priv *priv = mac802154_to_priv(hw); - - BUG_ON(!list_empty(&priv->slaves)); - - mutex_destroy(&priv->slaves_mtx); - - wpan_phy_free(priv->phy); -} -EXPORT_SYMBOL(ieee802154_free_device); - -int ieee802154_register_device(struct ieee802154_dev *dev) -{ - struct mac802154_priv *priv = mac802154_to_priv(dev); - int rc = -ENOSYS; - - if (dev->flags & IEEE802154_HW_TXPOWER) { - if (!priv->ops->set_txpower) - goto out; - - priv->phy->set_txpower = mac802154_set_txpower; - } - - if (dev->flags & IEEE802154_HW_LBT) { - if (!priv->ops->set_lbt) - goto out; - - priv->phy->set_lbt = mac802154_set_lbt; - } - - if (dev->flags & IEEE802154_HW_CCA_MODE) { - if (!priv->ops->set_cca_mode) - goto out; - - priv->phy->set_cca_mode = mac802154_set_cca_mode; - } - - if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) { - if (!priv->ops->set_cca_ed_level) - goto out; - - priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; - } - - if (dev->flags & IEEE802154_HW_CSMA_PARAMS) { - if (!priv->ops->set_csma_params) - goto out; - - priv->phy->set_csma_params = mac802154_set_csma_params; - } - - if (dev->flags & IEEE802154_HW_FRAME_RETRIES) { - if (!priv->ops->set_frame_retries) - goto out; - - priv->phy->set_frame_retries = mac802154_set_frame_retries; - } - - priv->dev_workqueue = - create_singlethread_workqueue(wpan_phy_name(priv->phy)); - if (!priv->dev_workqueue) { - rc = -ENOMEM; - goto out; - } - - wpan_phy_set_dev(priv->phy, priv->hw.parent); - - priv->phy->add_iface = mac802154_add_iface; - priv->phy->del_iface = mac802154_del_iface; - - rc = wpan_phy_register(priv->phy); - if (rc < 0) - goto out_wq; - - rtnl_lock(); - - mutex_lock(&priv->slaves_mtx); - priv->running = MAC802154_DEVICE_RUN; - mutex_unlock(&priv->slaves_mtx); - - rtnl_unlock(); - - return 0; - -out_wq: - destroy_workqueue(priv->dev_workqueue); -out: - return rc; -} -EXPORT_SYMBOL(ieee802154_register_device); - -void ieee802154_unregister_device(struct ieee802154_dev *dev) -{ - struct mac802154_priv *priv = mac802154_to_priv(dev); - struct mac802154_sub_if_data *sdata, *next; - - flush_workqueue(priv->dev_workqueue); - destroy_workqueue(priv->dev_workqueue); - - rtnl_lock(); - - mutex_lock(&priv->slaves_mtx); - priv->running = MAC802154_DEVICE_STOPPED; - mutex_unlock(&priv->slaves_mtx); - - list_for_each_entry_safe(sdata, next, &priv->slaves, list) { - mutex_lock(&sdata->hw->slaves_mtx); - list_del(&sdata->list); - mutex_unlock(&sdata->hw->slaves_mtx); - - unregister_netdevice(sdata->dev); - } - - rtnl_unlock(); - - wpan_phy_unregister(priv->phy); -} -EXPORT_SYMBOL(ieee802154_unregister_device); - -MODULE_DESCRIPTION("IEEE 802.15.4 implementation"); -MODULE_LICENSE("GPL v2"); diff --git a/net/mac802154/mac802154.h b/net/mac802154/ieee802154_i.h index 762a6f849c6b..bebd70ffc7a3 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/ieee802154_i.h @@ -10,29 +10,28 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> */ -#ifndef MAC802154_H -#define MAC802154_H +#ifndef __IEEE802154_I_H +#define __IEEE802154_I_H #include <linux/mutex.h> +#include <linux/hrtimer.h> +#include <net/cfg802154.h> #include <net/mac802154.h> +#include <net/nl802154.h> #include <net/ieee802154_netdev.h> #include "llsec.h" /* mac802154 device private data */ -struct mac802154_priv { - struct ieee802154_dev hw; - struct ieee802154_ops *ops; +struct ieee802154_local { + struct ieee802154_hw hw; + const struct ieee802154_ops *ops; /* ieee802154 phy */ struct wpan_phy *phy; @@ -46,23 +45,29 @@ struct mac802154_priv { * * So atomic readers can use any of this protection methods. */ - struct list_head slaves; - struct mutex slaves_mtx; + struct list_head interfaces; + struct mutex iflist_mtx; /* This one is used for scanning and other jobs not to be interfered * with serial driver. */ - struct workqueue_struct *dev_workqueue; + struct workqueue_struct *workqueue; - /* SoftMAC device is registered and running. One can add subinterfaces. - * This flag should be modified under slaves_mtx and RTNL, so you can - * read them using any of protection methods. - */ - bool running; + struct hrtimer ifs_timer; + + bool started; + + struct tasklet_struct tasklet; + struct sk_buff_head skb_queue; +}; + +enum { + IEEE802154_RX_MSG = 1, }; -#define MAC802154_DEVICE_STOPPED 0x00 -#define MAC802154_DEVICE_RUN 0x01 +enum ieee802154_sdata_state_bits { + SDATA_STATE_RUNNING, +}; /* Slave interface definition. * @@ -70,72 +75,74 @@ struct mac802154_priv { * Each ieee802154 device/transceiver may have several slaves and able * to be associated with several networks at the same time. */ -struct mac802154_sub_if_data { +struct ieee802154_sub_if_data { struct list_head list; /* the ieee802154_priv->slaves list */ - struct mac802154_priv *hw; + struct wpan_dev wpan_dev; + + struct ieee802154_local *local; struct net_device *dev; - int type; - bool running; + unsigned long state; + char name[IFNAMSIZ]; spinlock_t mib_lock; - __le16 pan_id; - __le16 short_addr; - __le64 extended_addr; - - u8 chan; - u8 page; - - struct ieee802154_mac_params mac_params; - - /* MAC BSN field */ - u8 bsn; - /* MAC DSN field */ - u8 dsn; - /* protects sec from concurrent access by netlink. access by * encrypt/decrypt/header_create safe without additional protection. */ struct mutex sec_mtx; struct mac802154_llsec sec; + /* must be last, dynamically sized area in this! */ + struct ieee802154_vif vif; }; -#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) - #define MAC802154_CHAN_NONE 0xff /* No channel is assigned */ -extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced; -extern struct ieee802154_mlme_ops mac802154_mlme_wpan; - -int mac802154_slave_open(struct net_device *dev); -int mac802154_slave_close(struct net_device *dev); +/* utility functions/constants */ +extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */ + +static inline struct ieee802154_local * +hw_to_local(struct ieee802154_hw *hw) +{ + return container_of(hw, struct ieee802154_local, hw); +} + +static inline struct ieee802154_sub_if_data * +IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev) +{ + return netdev_priv(dev); +} + +static inline struct ieee802154_sub_if_data * +IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev) +{ + return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev); +} + +static inline bool +ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata) +{ + return test_bit(SDATA_STATE_RUNNING, &sdata->state); +} -void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb); -void mac802154_monitor_setup(struct net_device *dev); - -void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb); -void mac802154_wpan_setup(struct net_device *dev); +extern struct ieee802154_mlme_ops mac802154_mlme_wpan; -netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, - u8 page, u8 chan); +netdev_tx_t +ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t +ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer); /* MIB callbacks */ void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val); __le16 mac802154_dev_get_short_addr(const struct net_device *dev); -void mac802154_dev_set_ieee_addr(struct net_device *dev); __le16 mac802154_dev_get_pan_id(const struct net_device *dev); void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); u8 mac802154_dev_get_dsn(const struct net_device *dev); -int mac802154_set_mac_params(struct net_device *dev, - const struct ieee802154_mac_params *params); -void mac802154_get_mac_params(struct net_device *dev, - struct ieee802154_mac_params *params); - int mac802154_get_params(struct net_device *dev, struct ieee802154_llsec_params *params); int mac802154_set_params(struct net_device *dev, @@ -169,4 +176,13 @@ void mac802154_get_table(struct net_device *dev, struct ieee802154_llsec_table **t); void mac802154_unlock_table(struct net_device *dev); -#endif /* MAC802154_H */ +/* interface handling */ +int ieee802154_iface_init(void); +void ieee802154_iface_exit(void); +void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata); +struct net_device * +ieee802154_if_add(struct ieee802154_local *local, const char *name, + enum nl802154_iftype type, __le64 extended_addr); +void ieee802154_remove_interfaces(struct ieee802154_local *local); + +#endif /* __IEEE802154_I_H */ diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c new file mode 100644 index 000000000000..9ae893057dd7 --- /dev/null +++ b/net/mac802154/iface.c @@ -0,0 +1,586 @@ +/* + * Copyright 2007-2012 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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. + * + * Written by: + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> + * Sergey Lapin <slapin@ossfans.org> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com> + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +#include <linux/netdevice.h> +#include <linux/module.h> +#include <linux/if_arp.h> +#include <linux/ieee802154.h> + +#include <net/nl802154.h> +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "driver-ops.h" + +static int mac802154_wpan_update_llsec(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + int rc = 0; + + if (ops->llsec) { + struct ieee802154_llsec_params params; + int changed = 0; + + params.pan_id = wpan_dev->pan_id; + changed |= IEEE802154_LLSEC_PARAM_PAN_ID; + + params.hwaddr = wpan_dev->extended_addr; + changed |= IEEE802154_LLSEC_PARAM_HWADDR; + + rc = ops->llsec->set_params(dev, ¶ms, changed); + } + + return rc; +} + +static int +mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct sockaddr_ieee802154 *sa = + (struct sockaddr_ieee802154 *)&ifr->ifr_addr; + int err = -ENOIOCTLCMD; + + ASSERT_RTNL(); + + spin_lock_bh(&sdata->mib_lock); + + switch (cmd) { + case SIOCGIFADDR: + { + u16 pan_id, short_addr; + + pan_id = le16_to_cpu(wpan_dev->pan_id); + short_addr = le16_to_cpu(wpan_dev->short_addr); + if (pan_id == IEEE802154_PANID_BROADCAST || + short_addr == IEEE802154_ADDR_BROADCAST) { + err = -EADDRNOTAVAIL; + break; + } + + sa->family = AF_IEEE802154; + sa->addr.addr_type = IEEE802154_ADDR_SHORT; + sa->addr.pan_id = pan_id; + sa->addr.short_addr = short_addr; + + err = 0; + break; + } + case SIOCSIFADDR: + if (netif_running(dev)) { + spin_unlock_bh(&sdata->mib_lock); + return -EBUSY; + } + + dev_warn(&dev->dev, + "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n"); + if (sa->family != AF_IEEE802154 || + sa->addr.addr_type != IEEE802154_ADDR_SHORT || + sa->addr.pan_id == IEEE802154_PANID_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { + err = -EINVAL; + break; + } + + wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id); + wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr); + + err = mac802154_wpan_update_llsec(dev); + break; + } + + spin_unlock_bh(&sdata->mib_lock); + return err; +} + +static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct sockaddr *addr = p; + __le64 extended_addr; + + if (netif_running(dev)) + return -EBUSY; + + ieee802154_be64_to_le64(&extended_addr, addr->sa_data); + if (!ieee802154_is_valid_extended_addr(extended_addr)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + sdata->wpan_dev.extended_addr = extended_addr; + + return mac802154_wpan_update_llsec(dev); +} + +static int mac802154_slave_open(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_sub_if_data *subif; + struct ieee802154_local *local = sdata->local; + int res = 0; + + ASSERT_RTNL(); + + if (sdata->vif.type == NL802154_IFTYPE_NODE) { + mutex_lock(&sdata->local->iflist_mtx); + list_for_each_entry(subif, &sdata->local->interfaces, list) { + if (subif != sdata && + subif->vif.type == sdata->vif.type && + ieee802154_sdata_running(subif)) { + mutex_unlock(&sdata->local->iflist_mtx); + return -EBUSY; + } + } + mutex_unlock(&sdata->local->iflist_mtx); + } + + set_bit(SDATA_STATE_RUNNING, &sdata->state); + + if (!local->open_count) { + res = drv_start(local); + WARN_ON(res); + if (res) + goto err; + } + + local->open_count++; + netif_start_queue(dev); + return 0; +err: + /* might already be clear but that doesn't matter */ + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + + return res; +} + +static int mac802154_wpan_open(struct net_device *dev) +{ + int rc; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct wpan_phy *phy = sdata->local->phy; + + rc = mac802154_slave_open(dev); + if (rc < 0) + return rc; + + mutex_lock(&phy->pib_lock); + + if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { + rc = drv_set_promiscuous_mode(local, + wpan_dev->promiscuous_mode); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_AFILT) { + rc = drv_set_pan_id(local, wpan_dev->pan_id); + if (rc < 0) + goto out; + + rc = drv_set_extended_addr(local, wpan_dev->extended_addr); + if (rc < 0) + goto out; + + rc = drv_set_short_addr(local, wpan_dev->short_addr); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_LBT) { + rc = drv_set_lbt_mode(local, wpan_dev->lbt); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) { + rc = drv_set_csma_params(local, wpan_dev->min_be, + wpan_dev->max_be, + wpan_dev->csma_retries); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) { + rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries); + if (rc < 0) + goto out; + } + + mutex_unlock(&phy->pib_lock); + return 0; + +out: + mutex_unlock(&phy->pib_lock); + return rc; +} + +static int mac802154_slave_close(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + + ASSERT_RTNL(); + + hrtimer_cancel(&local->ifs_timer); + + netif_stop_queue(dev); + local->open_count--; + + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + + if (!local->open_count) + drv_stop(local); + + return 0; +} + +static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata, + struct ieee802154_hdr *hdr, + const struct ieee802154_mac_cb *cb) +{ + struct ieee802154_llsec_params params; + u8 level; + + mac802154_llsec_get_params(&sdata->sec, ¶ms); + + if (!params.enabled && cb->secen_override && cb->secen) + return -EINVAL; + if (!params.enabled || + (cb->secen_override && !cb->secen) || + !params.out_level) + return 0; + if (cb->seclevel_override && !cb->seclevel) + return -EINVAL; + + level = cb->seclevel_override ? cb->seclevel : params.out_level; + + hdr->fc.security_enabled = 1; + hdr->sec.level = level; + hdr->sec.key_id_mode = params.out_key.mode; + if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) + hdr->sec.short_src = params.out_key.short_source; + else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) + hdr->sec.extended_src = params.out_key.extended_source; + hdr->sec.key_id = params.out_key.id; + + return 0; +} + +static int mac802154_header_create(struct sk_buff *skb, + struct net_device *dev, + unsigned short type, + const void *daddr, + const void *saddr, + unsigned len) +{ + struct ieee802154_hdr hdr; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct ieee802154_mac_cb *cb = mac_cb(skb); + int hlen; + + if (!daddr) + return -EINVAL; + + memset(&hdr.fc, 0, sizeof(hdr.fc)); + hdr.fc.type = cb->type; + hdr.fc.security_enabled = cb->secen; + hdr.fc.ack_request = cb->ackreq; + hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + + if (mac802154_set_header_security(sdata, &hdr, cb) < 0) + return -EINVAL; + + if (!saddr) { + spin_lock_bh(&sdata->mib_lock); + + if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || + wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || + wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { + hdr.source.mode = IEEE802154_ADDR_LONG; + hdr.source.extended_addr = wpan_dev->extended_addr; + } else { + hdr.source.mode = IEEE802154_ADDR_SHORT; + hdr.source.short_addr = wpan_dev->short_addr; + } + + hdr.source.pan_id = wpan_dev->pan_id; + + spin_unlock_bh(&sdata->mib_lock); + } else { + hdr.source = *(const struct ieee802154_addr *)saddr; + } + + hdr.dest = *(const struct ieee802154_addr *)daddr; + + hlen = ieee802154_hdr_push(skb, &hdr); + if (hlen < 0) + return -EINVAL; + + skb_reset_mac_header(skb); + skb->mac_len = hlen; + + if (len > ieee802154_max_payload(&hdr)) + return -EMSGSIZE; + + return hlen; +} + +static int +mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + struct ieee802154_hdr hdr; + struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; + + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { + pr_debug("malformed packet\n"); + return 0; + } + + *addr = hdr.source; + return sizeof(*addr); +} + +static struct header_ops mac802154_header_ops = { + .create = mac802154_header_create, + .parse = mac802154_header_parse, +}; + +static const struct net_device_ops mac802154_wpan_ops = { + .ndo_open = mac802154_wpan_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = ieee802154_subif_start_xmit, + .ndo_do_ioctl = mac802154_wpan_ioctl, + .ndo_set_mac_address = mac802154_wpan_mac_addr, +}; + +static const struct net_device_ops mac802154_monitor_ops = { + .ndo_open = mac802154_wpan_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = ieee802154_monitor_start_xmit, +}; + +static void mac802154_wpan_free(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + mac802154_llsec_destroy(&sdata->sec); + + free_netdev(dev); +} + +static void ieee802154_if_setup(struct net_device *dev) +{ + dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN); + + dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; + dev->needed_tailroom = 2 + 16; /* FCS + MIC */ + dev->mtu = IEEE802154_MTU; + dev->tx_queue_len = 300; + dev->flags = IFF_NOARP | IFF_BROADCAST; +} + +static int +ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, + enum nl802154_iftype type) +{ + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + + /* set some type-dependent values */ + sdata->vif.type = type; + sdata->wpan_dev.iftype = type; + + get_random_bytes(&wpan_dev->bsn, 1); + get_random_bytes(&wpan_dev->dsn, 1); + + /* defaults per 802.15.4-2011 */ + wpan_dev->min_be = 3; + wpan_dev->max_be = 5; + wpan_dev->csma_retries = 4; + /* for compatibility, actual default is 3 */ + wpan_dev->frame_retries = -1; + + wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); + wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + + switch (type) { + case NL802154_IFTYPE_NODE: + ieee802154_be64_to_le64(&wpan_dev->extended_addr, + sdata->dev->dev_addr); + + sdata->dev->header_ops = &mac802154_header_ops; + sdata->dev->destructor = mac802154_wpan_free; + sdata->dev->netdev_ops = &mac802154_wpan_ops; + sdata->dev->ml_priv = &mac802154_mlme_wpan; + wpan_dev->promiscuous_mode = false; + + spin_lock_init(&sdata->mib_lock); + mutex_init(&sdata->sec_mtx); + + mac802154_llsec_init(&sdata->sec); + break; + case NL802154_IFTYPE_MONITOR: + sdata->dev->destructor = free_netdev; + sdata->dev->netdev_ops = &mac802154_monitor_ops; + wpan_dev->promiscuous_mode = true; + break; + default: + BUG(); + } + + return 0; +} + +struct net_device * +ieee802154_if_add(struct ieee802154_local *local, const char *name, + enum nl802154_iftype type, __le64 extended_addr) +{ + struct net_device *ndev = NULL; + struct ieee802154_sub_if_data *sdata = NULL; + int ret = -ENOMEM; + + ASSERT_RTNL(); + + ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name, + NET_NAME_UNKNOWN, ieee802154_if_setup); + if (!ndev) + return ERR_PTR(-ENOMEM); + + ndev->needed_headroom = local->hw.extra_tx_headroom; + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) + goto err; + + ieee802154_le64_to_be64(ndev->perm_addr, + &local->hw.phy->perm_extended_addr); + switch (type) { + case NL802154_IFTYPE_NODE: + ndev->type = ARPHRD_IEEE802154; + if (ieee802154_is_valid_extended_addr(extended_addr)) + ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr); + else + memcpy(ndev->dev_addr, ndev->perm_addr, + IEEE802154_EXTENDED_ADDR_LEN); + break; + case NL802154_IFTYPE_MONITOR: + ndev->type = ARPHRD_IEEE802154_MONITOR; + break; + default: + ret = -EINVAL; + goto err; + } + + /* TODO check this */ + SET_NETDEV_DEV(ndev, &local->phy->dev); + sdata = netdev_priv(ndev); + ndev->ieee802154_ptr = &sdata->wpan_dev; + memcpy(sdata->name, ndev->name, IFNAMSIZ); + sdata->dev = ndev; + sdata->wpan_dev.wpan_phy = local->hw.phy; + sdata->local = local; + + /* setup type-dependent data */ + ret = ieee802154_setup_sdata(sdata, type); + if (ret) + goto err; + + ret = register_netdevice(ndev); + if (ret < 0) + goto err; + + mutex_lock(&local->iflist_mtx); + list_add_tail_rcu(&sdata->list, &local->interfaces); + mutex_unlock(&local->iflist_mtx); + + return ndev; + +err: + free_netdev(ndev); + return ERR_PTR(ret); +} + +void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata) +{ + ASSERT_RTNL(); + + mutex_lock(&sdata->local->iflist_mtx); + list_del_rcu(&sdata->list); + mutex_unlock(&sdata->local->iflist_mtx); + + synchronize_rcu(); + unregister_netdevice(sdata->dev); +} + +void ieee802154_remove_interfaces(struct ieee802154_local *local) +{ + struct ieee802154_sub_if_data *sdata, *tmp; + + mutex_lock(&local->iflist_mtx); + list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { + list_del(&sdata->list); + + unregister_netdevice(sdata->dev); + } + mutex_unlock(&local->iflist_mtx); +} + +static int netdev_notify(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct ieee802154_sub_if_data *sdata; + + if (state != NETDEV_CHANGENAME) + return NOTIFY_DONE; + + if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy) + return NOTIFY_DONE; + + if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid) + return NOTIFY_DONE; + + sdata = IEEE802154_DEV_TO_SUB_IF(dev); + memcpy(sdata->name, dev->name, IFNAMSIZ); + + return NOTIFY_OK; +} + +static struct notifier_block mac802154_netdev_notifier = { + .notifier_call = netdev_notify, +}; + +int ieee802154_iface_init(void) +{ + return register_netdevice_notifier(&mac802154_netdev_notifier); +} + +void ieee802154_iface_exit(void) +{ + unregister_netdevice_notifier(&mac802154_netdev_notifier); +} diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 457058142098..dcf73958133a 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -17,10 +17,10 @@ #include <linux/err.h> #include <linux/bug.h> #include <linux/completion.h> -#include <net/ieee802154.h> +#include <linux/ieee802154.h> #include <crypto/algapi.h> -#include "mac802154.h" +#include "ieee802154_i.h" #include "llsec.h" static void llsec_key_put(struct mac802154_llsec_key *key); @@ -75,8 +75,6 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec) } } - - int mac802154_llsec_get_params(struct mac802154_llsec *sec, struct ieee802154_llsec_params *params) { @@ -117,8 +115,6 @@ int mac802154_llsec_set_params(struct mac802154_llsec *sec, return 0; } - - static struct mac802154_llsec_key* llsec_key_alloc(const struct ieee802154_llsec_key *template) { @@ -294,8 +290,6 @@ int mac802154_llsec_key_del(struct mac802154_llsec *sec, return -ENOENT; } - - static bool llsec_dev_use_shortaddr(__le16 short_addr) { return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) && @@ -304,12 +298,12 @@ static bool llsec_dev_use_shortaddr(__le16 short_addr) static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id) { - return ((__force u16) short_addr) << 16 | (__force u16) pan_id; + return ((__force u16)short_addr) << 16 | (__force u16)pan_id; } static u64 llsec_dev_hash_long(__le64 hwaddr) { - return (__force u64) hwaddr; + return (__force u64)hwaddr; } static struct mac802154_llsec_device* @@ -411,8 +405,6 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr) return 0; } - - static struct mac802154_llsec_device_key* llsec_devkey_find(struct mac802154_llsec_device *dev, const struct ieee802154_llsec_key_id *key) @@ -475,8 +467,6 @@ int mac802154_llsec_devkey_del(struct mac802154_llsec *sec, return 0; } - - static struct mac802154_llsec_seclevel* llsec_find_seclevel(const struct mac802154_llsec *sec, const struct ieee802154_llsec_seclevel *sl) @@ -532,8 +522,6 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, return 0; } - - static int llsec_recover_addr(struct mac802154_llsec *sec, struct ieee802154_addr *addr) { @@ -609,7 +597,6 @@ found: return llsec_key_get(key); } - static void llsec_geniv(u8 iv[16], __le64 addr, const struct ieee802154_sechdr *sec) { @@ -786,8 +773,6 @@ fail: return rc; } - - static struct mac802154_llsec_device* llsec_lookup_dev(struct mac802154_llsec *sec, const struct ieee802154_addr *addr) diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index bf809131eef7..6aacb1816889 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -12,10 +12,6 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -24,14 +20,14 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> +#include <linux/ieee802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> #include <net/mac802154.h> -#include <net/nl802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" +#include "driver-ops.h" static int mac802154_mlme_start_req(struct net_device *dev, struct ieee802154_addr *addr, @@ -43,11 +39,12 @@ static int mac802154_mlme_start_req(struct net_device *dev, struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); int rc = 0; + ASSERT_RTNL(); + BUG_ON(addr->mode != IEEE802154_ADDR_SHORT); mac802154_dev_set_pan_id(dev, addr->pan_id); mac802154_dev_set_short_addr(dev, addr->short_addr); - mac802154_dev_set_ieee_addr(dev); mac802154_dev_set_page_channel(dev, page, channel); if (ops->llsec) { @@ -69,21 +66,71 @@ static int mac802154_mlme_start_req(struct net_device *dev, rc = ops->llsec->set_params(dev, ¶ms, changed); } - /* FIXME: add validation for unused parameters to be sane - * for SoftMAC - */ - ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS); - return rc; } -static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) +static int mac802154_set_mac_params(struct net_device *dev, + const struct ieee802154_mac_params *params) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + int ret; + + ASSERT_RTNL(); + + /* PHY */ + wpan_dev->wpan_phy->transmit_power = params->transmit_power; + wpan_dev->wpan_phy->cca_mode = params->cca_mode; + wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level; + + /* MAC */ + wpan_dev->min_be = params->min_be; + wpan_dev->max_be = params->max_be; + wpan_dev->csma_retries = params->csma_retries; + wpan_dev->frame_retries = params->frame_retries; + wpan_dev->lbt = params->lbt; + + if (local->hw.flags & IEEE802154_HW_TXPOWER) { + ret = drv_set_tx_power(local, params->transmit_power); + if (ret < 0) + return ret; + } + + if (local->hw.flags & IEEE802154_HW_CCA_MODE) { + ret = drv_set_cca_mode(local, params->cca_mode); + if (ret < 0) + return ret; + } - BUG_ON(dev->type != ARPHRD_IEEE802154); + if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) { + ret = drv_set_cca_ed_level(local, params->cca_ed_level); + if (ret < 0) + return ret; + } + + return 0; +} - return to_phy(get_device(&priv->hw->phy->dev)); +static void mac802154_get_mac_params(struct net_device *dev, + struct ieee802154_mac_params *params) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + + ASSERT_RTNL(); + + /* PHY */ + params->transmit_power = wpan_dev->wpan_phy->transmit_power; + params->cca_mode = wpan_dev->wpan_phy->cca_mode; + params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level; + + /* MAC */ + params->min_be = wpan_dev->min_be; + params->max_be = wpan_dev->max_be; + params->csma_retries = wpan_dev->csma_retries; + params->frame_retries = wpan_dev->frame_retries; + params->lbt = wpan_dev->lbt; } static struct ieee802154_llsec_ops mac802154_llsec_ops = { @@ -102,12 +149,7 @@ static struct ieee802154_llsec_ops mac802154_llsec_ops = { .unlock_table = mac802154_unlock_table, }; -struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { - .get_phy = mac802154_get_phy, -}; - struct ieee802154_mlme_ops mac802154_mlme_wpan = { - .get_phy = mac802154_get_phy, .start_req = mac802154_mlme_start_req, .get_pan_id = mac802154_dev_get_pan_id, .get_short_addr = mac802154_dev_get_short_addr, diff --git a/net/mac802154/main.c b/net/mac802154/main.c new file mode 100644 index 000000000000..8500378c8318 --- /dev/null +++ b/net/mac802154/main.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2007-2012 Siemens AG + * + * Written by: + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> + +#include <net/netlink.h> +#include <net/nl802154.h> +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> +#include <net/route.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "cfg.h" + +static void ieee802154_tasklet_handler(unsigned long data) +{ + struct ieee802154_local *local = (struct ieee802154_local *)data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->skb_queue))) { + switch (skb->pkt_type) { + case IEEE802154_RX_MSG: + /* Clear skb->pkt_type in order to not confuse kernel + * netstack. + */ + skb->pkt_type = 0; + ieee802154_rx(&local->hw, skb); + break; + default: + WARN(1, "mac802154: Packet is of unknown type %d\n", + skb->pkt_type); + kfree_skb(skb); + break; + } + } +} + +struct ieee802154_hw * +ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) +{ + struct wpan_phy *phy; + struct ieee802154_local *local; + size_t priv_size; + + if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed || + !ops->start || !ops->stop || !ops->set_channel) { + pr_err("undefined IEEE802.15.4 device operations\n"); + return NULL; + } + + /* Ensure 32-byte alignment of our private data and hw private data. + * We use the wpan_phy priv data for both our ieee802154_local and for + * the driver's private data + * + * in memory it'll be like this: + * + * +-------------------------+ + * | struct wpan_phy | + * +-------------------------+ + * | struct ieee802154_local | + * +-------------------------+ + * | driver's private data | + * +-------------------------+ + * + * Due to ieee802154 layer isn't aware of driver and MAC structures, + * so lets align them here. + */ + + priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; + + phy = wpan_phy_new(&mac802154_config_ops, priv_size); + if (!phy) { + pr_err("failure to allocate master IEEE802.15.4 device\n"); + return NULL; + } + + phy->privid = mac802154_wpan_phy_privid; + + local = wpan_phy_priv(phy); + local->phy = phy; + local->hw.phy = local->phy; + local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); + local->ops = ops; + + INIT_LIST_HEAD(&local->interfaces); + mutex_init(&local->iflist_mtx); + + tasklet_init(&local->tasklet, + ieee802154_tasklet_handler, + (unsigned long)local); + + skb_queue_head_init(&local->skb_queue); + + return &local->hw; +} +EXPORT_SYMBOL(ieee802154_alloc_hw); + +void ieee802154_free_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + + BUG_ON(!list_empty(&local->interfaces)); + + mutex_destroy(&local->iflist_mtx); + + wpan_phy_free(local->phy); +} +EXPORT_SYMBOL(ieee802154_free_hw); + +static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy) +{ + /* TODO warn on empty symbol_duration + * Should be done when all drivers sets this value. + */ + + wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD * + wpan_phy->symbol_duration; + wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD * + wpan_phy->symbol_duration; +} + +int ieee802154_register_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct net_device *dev; + int rc = -ENOSYS; + + local->workqueue = + create_singlethread_workqueue(wpan_phy_name(local->phy)); + if (!local->workqueue) { + rc = -ENOMEM; + goto out; + } + + hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + local->ifs_timer.function = ieee802154_xmit_ifs_timer; + + wpan_phy_set_dev(local->phy, local->hw.parent); + + ieee802154_setup_wpan_phy_pib(local->phy); + + rc = wpan_phy_register(local->phy); + if (rc < 0) + goto out_wq; + + rtnl_lock(); + + dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE, + cpu_to_le64(0x0000000000000000ULL)); + if (IS_ERR(dev)) { + rtnl_unlock(); + rc = PTR_ERR(dev); + goto out_wq; + } + + rtnl_unlock(); + + return 0; + +out_wq: + destroy_workqueue(local->workqueue); +out: + return rc; +} +EXPORT_SYMBOL(ieee802154_register_hw); + +void ieee802154_unregister_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + + tasklet_kill(&local->tasklet); + flush_workqueue(local->workqueue); + destroy_workqueue(local->workqueue); + + rtnl_lock(); + + ieee802154_remove_interfaces(local); + + rtnl_unlock(); + + wpan_phy_unregister(local->phy); +} +EXPORT_SYMBOL(ieee802154_unregister_hw); + +static int __init ieee802154_init(void) +{ + return ieee802154_iface_init(); +} + +static void __exit ieee802154_exit(void) +{ + ieee802154_iface_exit(); + + rcu_barrier(); +} + +subsys_initcall(ieee802154_init); +module_exit(ieee802154_exit); + +MODULE_DESCRIPTION("IEEE 802.15.4 subsystem"); +MODULE_LICENSE("GPL v2"); diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index 868a040fd422..5cf019a57fd7 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -10,10 +10,6 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Sergey Lapin <slapin@ossfans.org> @@ -25,208 +21,100 @@ #include <net/mac802154.h> #include <net/ieee802154_netdev.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -struct phy_chan_notify_work { - struct work_struct work; - struct net_device *dev; -}; - -struct hw_addr_filt_notify_work { - struct work_struct work; - struct net_device *dev; - unsigned long changed; -}; - -static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - BUG_ON(dev->type != ARPHRD_IEEE802154); - - return priv->hw; -} - -static void hw_addr_notify(struct work_struct *work) -{ - struct hw_addr_filt_notify_work *nw = container_of(work, - struct hw_addr_filt_notify_work, work); - struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); - int res; +#include <net/cfg802154.h> - res = hw->ops->set_hw_addr_filt(&hw->hw, - &hw->hw.hw_filt, - nw->changed); - if (res) - pr_debug("failed changed mask %lx\n", nw->changed); - - kfree(nw); -} - -static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct hw_addr_filt_notify_work *work; - - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return; - - INIT_WORK(&work->work, hw_addr_notify); - work->dev = dev; - work->changed = changed; - queue_work(priv->hw->dev_workqueue, &work->work); -} +#include "ieee802154_i.h" +#include "driver-ops.h" void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->short_addr = val; - spin_unlock_bh(&priv->mib_lock); - - if ((priv->hw->ops->set_hw_addr_filt) && - (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) { - priv->hw->hw.hw_filt.short_addr = priv->short_addr; - set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED); - } + spin_lock_bh(&sdata->mib_lock); + sdata->wpan_dev.short_addr = val; + spin_unlock_bh(&sdata->mib_lock); } __le16 mac802154_dev_get_short_addr(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - ret = priv->short_addr; - spin_unlock_bh(&priv->mib_lock); + spin_lock_bh(&sdata->mib_lock); + ret = sdata->wpan_dev.short_addr; + spin_unlock_bh(&sdata->mib_lock); return ret; } -void mac802154_dev_set_ieee_addr(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_priv *mac = priv->hw; - - priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - if (mac->ops->set_hw_addr_filt && - mac->hw.hw_filt.ieee_addr != priv->extended_addr) { - mac->hw.hw_filt.ieee_addr = priv->extended_addr; - set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); - } -} - __le16 mac802154_dev_get_pan_id(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - ret = priv->pan_id; - spin_unlock_bh(&priv->mib_lock); + spin_lock_bh(&sdata->mib_lock); + ret = sdata->wpan_dev.pan_id; + spin_unlock_bh(&sdata->mib_lock); return ret; } void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->pan_id = val; - spin_unlock_bh(&priv->mib_lock); - - if ((priv->hw->ops->set_hw_addr_filt) && - (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) { - priv->hw->hw.hw_filt.pan_id = priv->pan_id; - set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED); - } + spin_lock_bh(&sdata->mib_lock); + sdata->wpan_dev.pan_id = val; + spin_unlock_bh(&sdata->mib_lock); } u8 mac802154_dev_get_dsn(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - return priv->dsn++; -} - -static void phy_chan_notify(struct work_struct *work) -{ - struct phy_chan_notify_work *nw = container_of(work, - struct phy_chan_notify_work, work); - struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); - struct mac802154_sub_if_data *priv = netdev_priv(nw->dev); - int res; - - mutex_lock(&priv->hw->phy->pib_lock); - res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan); - if (res) { - pr_debug("set_channel failed\n"); - } else { - priv->hw->phy->current_channel = priv->chan; - priv->hw->phy->current_page = priv->page; - } - mutex_unlock(&priv->hw->phy->pib_lock); - - kfree(nw); + return sdata->wpan_dev.dsn++; } void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct phy_chan_notify_work *work; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->page = page; - priv->chan = chan; - spin_unlock_bh(&priv->mib_lock); - - mutex_lock(&priv->hw->phy->pib_lock); - if (priv->hw->phy->current_channel != priv->chan || - priv->hw->phy->current_page != priv->page) { - mutex_unlock(&priv->hw->phy->pib_lock); - - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return; - - INIT_WORK(&work->work, phy_chan_notify); - work->dev = dev; - queue_work(priv->hw->dev_workqueue, &work->work); + res = drv_set_channel(local, page, chan); + if (res) { + pr_debug("set_channel failed\n"); } else { - mutex_unlock(&priv->hw->phy->pib_lock); + mutex_lock(&local->phy->pib_lock); + local->phy->current_channel = chan; + local->phy->current_page = page; + mutex_unlock(&local->phy->pib_lock); } } - int mac802154_get_params(struct net_device *dev, struct ieee802154_llsec_params *params) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_get_params(&priv->sec, params); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_get_params(&sdata->sec, params); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -235,31 +123,30 @@ int mac802154_set_params(struct net_device *dev, const struct ieee802154_llsec_params *params, int changed) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_set_params(&priv->sec, params, changed); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_set_params(&sdata->sec, params, changed); + mutex_unlock(&sdata->sec_mtx); return res; } - int mac802154_add_key(struct net_device *dev, const struct ieee802154_llsec_key_id *id, const struct ieee802154_llsec_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_key_add(&priv->sec, id, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_add(&sdata->sec, id, key); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -267,61 +154,59 @@ int mac802154_add_key(struct net_device *dev, int mac802154_del_key(struct net_device *dev, const struct ieee802154_llsec_key_id *id) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_key_del(&priv->sec, id); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_del(&sdata->sec, id); + mutex_unlock(&sdata->sec_mtx); return res; } - int mac802154_add_dev(struct net_device *dev, const struct ieee802154_llsec_device *llsec_dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_dev_add(&priv->sec, llsec_dev); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev); + mutex_unlock(&sdata->sec_mtx); return res; } int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_dev_del(&priv->sec, dev_addr); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_del(&sdata->sec, dev_addr); + mutex_unlock(&sdata->sec_mtx); return res; } - int mac802154_add_devkey(struct net_device *dev, __le64 device_addr, const struct ieee802154_llsec_device_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -330,30 +215,29 @@ int mac802154_del_devkey(struct net_device *dev, __le64 device_addr, const struct ieee802154_llsec_device_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key); + mutex_unlock(&sdata->sec_mtx); return res; } - int mac802154_add_seclevel(struct net_device *dev, const struct ieee802154_llsec_seclevel *sl) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_seclevel_add(&priv->sec, sl); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_add(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -361,43 +245,42 @@ int mac802154_add_seclevel(struct net_device *dev, int mac802154_del_seclevel(struct net_device *dev, const struct ieee802154_llsec_seclevel *sl) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_seclevel_del(&priv->sec, sl); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_del(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); return res; } - void mac802154_lock_table(struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); } void mac802154_get_table(struct net_device *dev, struct ieee802154_llsec_table **t) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - *t = &priv->sec.table; + *t = &sdata->sec.table; } void mac802154_unlock_table(struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_unlock(&priv->sec_mtx); + mutex_unlock(&sdata->sec_mtx); } diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c deleted file mode 100644 index a68230e2b25f..000000000000 --- a/net/mac802154/monitor.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007, 2008, 2009 Siemens AG - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Written by: - * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> - * Sergey Lapin <slapin@ossfans.org> - * Maxim Gorbachyov <maxim.gorbachev@siemens.com> - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/crc-ccitt.h> - -#include <net/ieee802154.h> -#include <net/mac802154.h> -#include <net/netlink.h> -#include <net/wpan-phy.h> -#include <linux/nl802154.h> - -#include "mac802154.h" - -static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - u8 chan, page; - - priv = netdev_priv(dev); - - /* FIXME: locking */ - chan = priv->hw->phy->current_channel; - page = priv->hw->phy->current_page; - - if (chan == MAC802154_CHAN_NONE) /* not initialized */ - return NETDEV_TX_OK; - - if (WARN_ON(page >= WPAN_NUM_PAGES) || - WARN_ON(chan >= WPAN_NUM_CHANNELS)) - return NETDEV_TX_OK; - - skb->skb_iif = dev->ifindex; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - return mac802154_tx(priv->hw, skb, page, chan); -} - - -void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb) -{ - struct sk_buff *skb2; - struct mac802154_sub_if_data *sdata; - u16 crc = crc_ccitt(0, skb->data, skb->len); - u8 *data; - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) { - if (sdata->type != IEEE802154_DEV_MONITOR || - !netif_running(sdata->dev)) - continue; - - skb2 = skb_clone(skb, GFP_ATOMIC); - skb2->dev = sdata->dev; - skb2->pkt_type = PACKET_HOST; - data = skb_put(skb2, 2); - data[0] = crc & 0xff; - data[1] = crc >> 8; - - netif_rx_ni(skb2); - } - rcu_read_unlock(); -} - -static const struct net_device_ops mac802154_monitor_ops = { - .ndo_open = mac802154_slave_open, - .ndo_stop = mac802154_slave_close, - .ndo_start_xmit = mac802154_monitor_xmit, -}; - -void mac802154_monitor_setup(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - - dev->addr_len = 0; - dev->hard_header_len = 0; - dev->needed_tailroom = 2; /* room for FCS */ - dev->mtu = IEEE802154_MTU; - dev->tx_queue_len = 10; - dev->type = ARPHRD_IEEE802154_MONITOR; - dev->flags = IFF_NOARP | IFF_BROADCAST; - dev->watchdog_timeo = 0; - - dev->destructor = free_netdev; - dev->netdev_ops = &mac802154_monitor_ops; - dev->ml_priv = &mac802154_mlme_reduced; - - priv = netdev_priv(dev); - priv->type = IEEE802154_DEV_MONITOR; - - priv->chan = MAC802154_CHAN_NONE; /* not initialized */ - priv->page = 0; -} diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index a14cf9ede171..c0d67b2b4132 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -10,10 +10,6 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com> @@ -23,92 +19,284 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/workqueue.h> #include <linux/netdevice.h> #include <linux/crc-ccitt.h> +#include <asm/unaligned.h> #include <net/mac802154.h> #include <net/ieee802154_netdev.h> +#include <net/nl802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" -/* The IEEE 802.15.4 standard defines 4 MAC packet types: - * - beacon frame - * - MAC command frame - * - acknowledgement frame - * - data frame - * - * and only the data frame should be pushed to the upper layers, other types - * are just internal MAC layer management information. So only data packets - * are going to be sent to the networking queue, all other will be processed - * right here by using the device workqueue. - */ -struct rx_work { - struct sk_buff *skb; - struct work_struct work; - struct ieee802154_dev *dev; - u8 lqi; -}; +static int ieee802154_deliver_skb(struct sk_buff *skb) +{ + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = htons(ETH_P_IEEE802154); -static void -mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) + return netif_receive_skb(skb); +} + +static int +ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb, const struct ieee802154_hdr *hdr) { - struct mac802154_priv *priv = mac802154_to_priv(hw); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + __le16 span, sshort; + int rc; - mac_cb(skb)->lqi = lqi; - skb->protocol = htons(ETH_P_IEEE802154); - skb_reset_mac_header(skb); + pr_debug("getting packet via slave interface %s\n", sdata->dev->name); - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { - u16 crc; + spin_lock_bh(&sdata->mib_lock); - if (skb->len < 2) { - pr_debug("got invalid frame\n"); - goto fail; - } - crc = crc_ccitt(0, skb->data, skb->len); - if (crc) { - pr_debug("CRC mismatch\n"); - goto fail; - } - skb_trim(skb, skb->len - 2); /* CRC */ + span = wpan_dev->pan_id; + sshort = wpan_dev->short_addr; + + switch (mac_cb(skb)->dest.mode) { + case IEEE802154_ADDR_NONE: + if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) + /* FIXME: check if we are PAN coordinator */ + skb->pkt_type = PACKET_OTHERHOST; + else + /* ACK comes with both addresses empty */ + skb->pkt_type = PACKET_HOST; + break; + case IEEE802154_ADDR_LONG: + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr) + skb->pkt_type = PACKET_HOST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + case IEEE802154_ADDR_SHORT: + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->dest.short_addr == sshort) + skb->pkt_type = PACKET_HOST; + else if (mac_cb(skb)->dest.short_addr == + cpu_to_le16(IEEE802154_ADDR_BROADCAST)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + default: + spin_unlock_bh(&sdata->mib_lock); + pr_debug("invalid dest mode\n"); + goto fail; } - mac802154_monitors_rx(priv, skb); - mac802154_wpans_rx(priv, skb); + spin_unlock_bh(&sdata->mib_lock); - return; + skb->dev = sdata->dev; + + rc = mac802154_llsec_decrypt(&sdata->sec, skb); + if (rc) { + pr_debug("decryption failed: %i\n", rc); + goto fail; + } + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + + switch (mac_cb(skb)->type) { + case IEEE802154_FC_TYPE_DATA: + return ieee802154_deliver_skb(skb); + default: + pr_warn("ieee802154: bad frame received (type = %d)\n", + mac_cb(skb)->type); + goto fail; + } fail: kfree_skb(skb); + return NET_RX_DROP; } -static void mac802154_rx_worker(struct work_struct *work) +static void +ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr) { - struct rx_work *rw = container_of(work, struct rx_work, work); + if (addr->mode == IEEE802154_ADDR_NONE) + pr_debug("%s not present\n", name); + + pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); + if (addr->mode == IEEE802154_ADDR_SHORT) { + pr_debug("%s is short: %04x\n", name, + le16_to_cpu(addr->short_addr)); + } else { + u64 hw = swab64((__force u64)addr->extended_addr); - mac802154_subif_rx(rw->dev, rw->skb, rw->lqi); - kfree(rw); + pr_debug("%s is hardware: %8phC\n", name, &hw); + } } -void -ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) +static int +ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr) { - struct mac802154_priv *priv = mac802154_to_priv(dev); - struct rx_work *work; + int hlen; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); - if (!skb) - return; + skb_reset_mac_header(skb); + + hlen = ieee802154_hdr_pull(skb, hdr); + if (hlen < 0) + return -EINVAL; + + skb->mac_len = hlen; + + pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), + hdr->seq); + + cb->type = hdr->fc.type; + cb->ackreq = hdr->fc.ack_request; + cb->secen = hdr->fc.security_enabled; + + ieee802154_print_addr("destination", &hdr->dest); + ieee802154_print_addr("source", &hdr->source); + + cb->source = hdr->source; + cb->dest = hdr->dest; + + if (hdr->fc.security_enabled) { + u64 key; + + pr_debug("seclevel %i\n", hdr->sec.level); + + switch (hdr->sec.key_id_mode) { + case IEEE802154_SCF_KEY_IMPLICIT: + pr_debug("implicit key\n"); + break; + + case IEEE802154_SCF_KEY_INDEX: + pr_debug("key %02x\n", hdr->sec.key_id); + break; + + case IEEE802154_SCF_KEY_SHORT_INDEX: + pr_debug("key %04x:%04x %02x\n", + le32_to_cpu(hdr->sec.short_src) >> 16, + le32_to_cpu(hdr->sec.short_src) & 0xffff, + hdr->sec.key_id); + break; + + case IEEE802154_SCF_KEY_HW_INDEX: + key = swab64((__force u64)hdr->sec.extended_src); + pr_debug("key source %8phC %02x\n", &key, + hdr->sec.key_id); + break; + } + } + + return 0; +} + +static void +__ieee802154_rx_handle_packet(struct ieee802154_local *local, + struct sk_buff *skb) +{ + int ret; + struct ieee802154_sub_if_data *sdata; + struct ieee802154_hdr hdr; - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) + ret = ieee802154_parse_frame_start(skb, &hdr); + if (ret) { + pr_debug("got invalid frame\n"); + kfree_skb(skb); return; + } + + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL802154_IFTYPE_NODE || + !netif_running(sdata->dev)) + continue; + + ieee802154_subif_frame(sdata, skb, &hdr); + skb = NULL; + break; + } + + if (skb) + kfree_skb(skb); +} + +static void +ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb) +{ + struct sk_buff *skb2; + struct ieee802154_sub_if_data *sdata; + + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_IEEE802154); + + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL802154_IFTYPE_MONITOR) + continue; + + if (!ieee802154_sdata_running(sdata)) + continue; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + skb2->dev = sdata->dev; + ieee802154_deliver_skb(skb2); + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + } + } +} + +void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct ieee802154_local *local = hw_to_local(hw); + u16 crc; - INIT_WORK(&work->work, mac802154_rx_worker); - work->skb = skb; - work->dev = dev; - work->lqi = lqi; + WARN_ON_ONCE(softirq_count() == 0); - queue_work(priv->dev_workqueue, &work->work); + /* TODO: When a transceiver omits the checksum here, we + * add an own calculated one. This is currently an ugly + * solution because the monitor needs a crc here. + */ + if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) { + crc = crc_ccitt(0, skb->data, skb->len); + put_unaligned_le16(crc, skb_put(skb, 2)); + } + + rcu_read_lock(); + + ieee802154_monitors_rx(local, skb); + + /* Check if transceiver doesn't validate the checksum. + * If not we validate the checksum here. + */ + if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) { + crc = crc_ccitt(0, skb->data, skb->len); + if (crc) { + rcu_read_unlock(); + kfree_skb(skb); + return; + } + } + /* remove crc */ + skb_trim(skb, skb->len - 2); + + __ieee802154_rx_handle_packet(local, skb); + + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_rx); + +void +ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi) +{ + struct ieee802154_local *local = hw_to_local(hw); + + mac_cb(skb)->lqi = lqi; + skb->pkt_type = IEEE802154_RX_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); } EXPORT_SYMBOL(ieee802154_rx_irqsafe); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index fdf4c0e67259..c62e95695c78 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -10,10 +10,6 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Sergey Lapin <slapin@ossfans.org> @@ -24,106 +20,98 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/crc-ccitt.h> +#include <asm/unaligned.h> +#include <net/rtnetlink.h> #include <net/ieee802154_netdev.h> #include <net/mac802154.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" +#include "driver-ops.h" /* IEEE 802.15.4 transceivers can sleep during the xmit session, so process * packets through the workqueue. */ -struct xmit_work { +struct ieee802154_xmit_cb { struct sk_buff *skb; struct work_struct work; - struct mac802154_priv *priv; - u8 chan; - u8 page; + struct ieee802154_local *local; }; -static void mac802154_xmit_worker(struct work_struct *work) +static struct ieee802154_xmit_cb ieee802154_xmit_cb; + +static void ieee802154_xmit_worker(struct work_struct *work) { - struct xmit_work *xw = container_of(work, struct xmit_work, work); - struct mac802154_sub_if_data *sdata; + struct ieee802154_xmit_cb *cb = + container_of(work, struct ieee802154_xmit_cb, work); + struct ieee802154_local *local = cb->local; + struct sk_buff *skb = cb->skb; + struct net_device *dev = skb->dev; int res; - mutex_lock(&xw->priv->phy->pib_lock); - if (xw->priv->phy->current_channel != xw->chan || - xw->priv->phy->current_page != xw->page) { - res = xw->priv->ops->set_channel(&xw->priv->hw, - xw->page, - xw->chan); - if (res) { - pr_debug("set_channel failed\n"); - goto out; - } + rtnl_lock(); - xw->priv->phy->current_channel = xw->chan; - xw->priv->phy->current_page = xw->page; - } + /* check if ifdown occurred while schedule */ + if (!netif_running(dev)) + goto err_tx; - res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb); + res = drv_xmit_sync(local, skb); if (res) - pr_debug("transmission failed\n"); + goto err_tx; -out: - mutex_unlock(&xw->priv->phy->pib_lock); + ieee802154_xmit_complete(&local->hw, skb, false); - /* Restart the netif queue on each sub_if_data object. */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &xw->priv->slaves, list) - netif_wake_queue(sdata->dev); - rcu_read_unlock(); + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; - dev_kfree_skb(xw->skb); + rtnl_unlock(); - kfree(xw); + return; + +err_tx: + /* Restart the netif queue on each sub_if_data object. */ + ieee802154_wake_queue(&local->hw); + rtnl_unlock(); + kfree_skb(skb); + netdev_dbg(dev, "transmission failed\n"); } -netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, - u8 page, u8 chan) +static netdev_tx_t +ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) { - struct xmit_work *work; - struct mac802154_sub_if_data *sdata; - - if (!(priv->phy->channels_supported[page] & (1 << chan))) { - WARN_ON(1); - goto err_tx; - } - - mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb); + struct net_device *dev = skb->dev; + int ret; - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { + if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) { u16 crc = crc_ccitt(0, skb->data, skb->len); - u8 *data = skb_put(skb, 2); - data[0] = crc & 0xff; - data[1] = crc >> 8; + put_unaligned_le16(crc, skb_put(skb, 2)); } - if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) + if (skb_cow_head(skb, local->hw.extra_tx_headroom)) goto err_tx; - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - kfree_skb(skb); - return NETDEV_TX_BUSY; - } - /* Stop the netif queue on each sub_if_data object. */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) - netif_stop_queue(sdata->dev); - rcu_read_unlock(); + ieee802154_stop_queue(&local->hw); + + /* async is priority, otherwise sync is fallback */ + if (local->ops->xmit_async) { + ret = drv_xmit_async(local, skb); + if (ret) { + ieee802154_wake_queue(&local->hw); + goto err_tx; + } - INIT_WORK(&work->work, mac802154_xmit_worker); - work->skb = skb; - work->priv = priv; - work->page = page; - work->chan = chan; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + } else { + INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker); + ieee802154_xmit_cb.skb = skb; + ieee802154_xmit_cb.local = local; - queue_work(priv->dev_workqueue, &work->work); + queue_work(local->workqueue, &ieee802154_xmit_cb.work); + } return NETDEV_TX_OK; @@ -131,3 +119,31 @@ err_tx: kfree_skb(skb); return NETDEV_TX_OK; } + +netdev_tx_t +ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + skb->skb_iif = dev->ifindex; + + return ieee802154_tx(sdata->local, skb); +} + +netdev_tx_t +ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int rc; + + rc = mac802154_llsec_encrypt(&sdata->sec, skb); + if (rc) { + netdev_warn(dev, "encryption failed: %i\n", rc); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + skb->skb_iif = dev->ifindex; + + return ieee802154_tx(sdata->local, skb); +} diff --git a/net/mac802154/util.c b/net/mac802154/util.c new file mode 100644 index 000000000000..5fc979027919 --- /dev/null +++ b/net/mac802154/util.c @@ -0,0 +1,84 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/mac80211/util.c + */ + +#include "ieee802154_i.h" + +/* privid for wpan_phys to determine whether they belong to us or not */ +const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid; + +void ieee802154_wake_queue(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct ieee802154_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + netif_wake_queue(sdata->dev); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_wake_queue); + +void ieee802154_stop_queue(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct ieee802154_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + netif_stop_queue(sdata->dev); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_stop_queue); + +enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer) +{ + struct ieee802154_local *local = + container_of(timer, struct ieee802154_local, ifs_timer); + + ieee802154_wake_queue(&local->hw); + + return HRTIMER_NORESTART; +} + +void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, + bool ifs_handling) +{ + if (ifs_handling) { + struct ieee802154_local *local = hw_to_local(hw); + + if (skb->len > 18) + hrtimer_start(&local->ifs_timer, + ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC), + HRTIMER_MODE_REL); + else + hrtimer_start(&local->ifs_timer, + ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC), + HRTIMER_MODE_REL); + + consume_skb(skb); + } else { + ieee802154_wake_queue(hw); + consume_skb(skb); + } +} +EXPORT_SYMBOL(ieee802154_xmit_complete); diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c deleted file mode 100644 index 4ab86a57dca5..000000000000 --- a/net/mac802154/wpan.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright 2007-2012 Siemens AG - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Written by: - * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> - * Sergey Lapin <slapin@ossfans.org> - * Maxim Gorbachyov <maxim.gorbachev@siemens.com> - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -#include <linux/netdevice.h> -#include <linux/module.h> -#include <linux/if_arp.h> - -#include <net/rtnetlink.h> -#include <linux/nl802154.h> -#include <net/af_ieee802154.h> -#include <net/mac802154.h> -#include <net/ieee802154_netdev.h> -#include <net/ieee802154.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -static int mac802154_wpan_update_llsec(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); - int rc = 0; - - if (ops->llsec) { - struct ieee802154_llsec_params params; - int changed = 0; - - params.pan_id = priv->pan_id; - changed |= IEEE802154_LLSEC_PARAM_PAN_ID; - - params.hwaddr = priv->extended_addr; - changed |= IEEE802154_LLSEC_PARAM_HWADDR; - - rc = ops->llsec->set_params(dev, ¶ms, changed); - } - - return rc; -} - -static int -mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct sockaddr_ieee802154 *sa = - (struct sockaddr_ieee802154 *)&ifr->ifr_addr; - int err = -ENOIOCTLCMD; - - spin_lock_bh(&priv->mib_lock); - - switch (cmd) { - case SIOCGIFADDR: - { - u16 pan_id, short_addr; - - pan_id = le16_to_cpu(priv->pan_id); - short_addr = le16_to_cpu(priv->short_addr); - if (pan_id == IEEE802154_PANID_BROADCAST || - short_addr == IEEE802154_ADDR_BROADCAST) { - err = -EADDRNOTAVAIL; - break; - } - - sa->family = AF_IEEE802154; - sa->addr.addr_type = IEEE802154_ADDR_SHORT; - sa->addr.pan_id = pan_id; - sa->addr.short_addr = short_addr; - - err = 0; - break; - } - case SIOCSIFADDR: - dev_warn(&dev->dev, - "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n"); - if (sa->family != AF_IEEE802154 || - sa->addr.addr_type != IEEE802154_ADDR_SHORT || - sa->addr.pan_id == IEEE802154_PANID_BROADCAST || - sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || - sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { - err = -EINVAL; - break; - } - - priv->pan_id = cpu_to_le16(sa->addr.pan_id); - priv->short_addr = cpu_to_le16(sa->addr.short_addr); - - err = mac802154_wpan_update_llsec(dev); - break; - } - - spin_unlock_bh(&priv->mib_lock); - return err; -} - -static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - if (netif_running(dev)) - return -EBUSY; - - /* FIXME: validate addr */ - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - mac802154_dev_set_ieee_addr(dev); - return mac802154_wpan_update_llsec(dev); -} - -int mac802154_set_mac_params(struct net_device *dev, - const struct ieee802154_mac_params *params) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mutex_lock(&priv->hw->slaves_mtx); - priv->mac_params = *params; - mutex_unlock(&priv->hw->slaves_mtx); - - return 0; -} - -void mac802154_get_mac_params(struct net_device *dev, - struct ieee802154_mac_params *params) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mutex_lock(&priv->hw->slaves_mtx); - *params = priv->mac_params; - mutex_unlock(&priv->hw->slaves_mtx); -} - -static int mac802154_wpan_open(struct net_device *dev) -{ - int rc; - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct wpan_phy *phy = priv->hw->phy; - - rc = mac802154_slave_open(dev); - if (rc < 0) - return rc; - - mutex_lock(&phy->pib_lock); - - if (phy->set_txpower) { - rc = phy->set_txpower(phy, priv->mac_params.transmit_power); - if (rc < 0) - goto out; - } - - if (phy->set_lbt) { - rc = phy->set_lbt(phy, priv->mac_params.lbt); - if (rc < 0) - goto out; - } - - if (phy->set_cca_mode) { - rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode); - if (rc < 0) - goto out; - } - - if (phy->set_cca_ed_level) { - rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level); - if (rc < 0) - goto out; - } - - if (phy->set_csma_params) { - rc = phy->set_csma_params(phy, priv->mac_params.min_be, - priv->mac_params.max_be, - priv->mac_params.csma_retries); - if (rc < 0) - goto out; - } - - if (phy->set_frame_retries) { - rc = phy->set_frame_retries(phy, - priv->mac_params.frame_retries); - if (rc < 0) - goto out; - } - - mutex_unlock(&phy->pib_lock); - return 0; - -out: - mutex_unlock(&phy->pib_lock); - return rc; -} - -static int mac802154_set_header_security(struct mac802154_sub_if_data *priv, - struct ieee802154_hdr *hdr, - const struct ieee802154_mac_cb *cb) -{ - struct ieee802154_llsec_params params; - u8 level; - - mac802154_llsec_get_params(&priv->sec, ¶ms); - - if (!params.enabled && cb->secen_override && cb->secen) - return -EINVAL; - if (!params.enabled || - (cb->secen_override && !cb->secen) || - !params.out_level) - return 0; - if (cb->seclevel_override && !cb->seclevel) - return -EINVAL; - - level = cb->seclevel_override ? cb->seclevel : params.out_level; - - hdr->fc.security_enabled = 1; - hdr->sec.level = level; - hdr->sec.key_id_mode = params.out_key.mode; - if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) - hdr->sec.short_src = params.out_key.short_source; - else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) - hdr->sec.extended_src = params.out_key.extended_source; - hdr->sec.key_id = params.out_key.id; - - return 0; -} - -static int mac802154_header_create(struct sk_buff *skb, - struct net_device *dev, - unsigned short type, - const void *daddr, - const void *saddr, - unsigned len) -{ - struct ieee802154_hdr hdr; - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct ieee802154_mac_cb *cb = mac_cb(skb); - int hlen; - - if (!daddr) - return -EINVAL; - - memset(&hdr.fc, 0, sizeof(hdr.fc)); - hdr.fc.type = cb->type; - hdr.fc.security_enabled = cb->secen; - hdr.fc.ack_request = cb->ackreq; - hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); - - if (mac802154_set_header_security(priv, &hdr, cb) < 0) - return -EINVAL; - - if (!saddr) { - spin_lock_bh(&priv->mib_lock); - - if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || - priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || - priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { - hdr.source.mode = IEEE802154_ADDR_LONG; - hdr.source.extended_addr = priv->extended_addr; - } else { - hdr.source.mode = IEEE802154_ADDR_SHORT; - hdr.source.short_addr = priv->short_addr; - } - - hdr.source.pan_id = priv->pan_id; - - spin_unlock_bh(&priv->mib_lock); - } else { - hdr.source = *(const struct ieee802154_addr *)saddr; - } - - hdr.dest = *(const struct ieee802154_addr *)daddr; - - hlen = ieee802154_hdr_push(skb, &hdr); - if (hlen < 0) - return -EINVAL; - - skb_reset_mac_header(skb); - skb->mac_len = hlen; - - if (len > ieee802154_max_payload(&hdr)) - return -EMSGSIZE; - - return hlen; -} - -static int -mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) -{ - struct ieee802154_hdr hdr; - struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; - - if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { - pr_debug("malformed packet\n"); - return 0; - } - - *addr = hdr.source; - return sizeof(*addr); -} - -static netdev_tx_t -mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - u8 chan, page; - int rc; - - priv = netdev_priv(dev); - - spin_lock_bh(&priv->mib_lock); - chan = priv->chan; - page = priv->page; - spin_unlock_bh(&priv->mib_lock); - - if (chan == MAC802154_CHAN_NONE || - page >= WPAN_NUM_PAGES || - chan >= WPAN_NUM_CHANNELS) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - rc = mac802154_llsec_encrypt(&priv->sec, skb); - if (rc) { - pr_warn("encryption failed: %i\n", rc); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb->skb_iif = dev->ifindex; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - return mac802154_tx(priv->hw, skb, page, chan); -} - -static struct header_ops mac802154_header_ops = { - .create = mac802154_header_create, - .parse = mac802154_header_parse, -}; - -static const struct net_device_ops mac802154_wpan_ops = { - .ndo_open = mac802154_wpan_open, - .ndo_stop = mac802154_slave_close, - .ndo_start_xmit = mac802154_wpan_xmit, - .ndo_do_ioctl = mac802154_wpan_ioctl, - .ndo_set_mac_address = mac802154_wpan_mac_addr, -}; - -static void mac802154_wpan_free(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mac802154_llsec_destroy(&priv->sec); - - free_netdev(dev); -} - -void mac802154_wpan_setup(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - - dev->addr_len = IEEE802154_ADDR_LEN; - memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); - - dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; - dev->header_ops = &mac802154_header_ops; - dev->needed_tailroom = 2 + 16; /* FCS + MIC */ - dev->mtu = IEEE802154_MTU; - dev->tx_queue_len = 300; - dev->type = ARPHRD_IEEE802154; - dev->flags = IFF_NOARP | IFF_BROADCAST; - dev->watchdog_timeo = 0; - - dev->destructor = mac802154_wpan_free; - dev->netdev_ops = &mac802154_wpan_ops; - dev->ml_priv = &mac802154_mlme_wpan; - - priv = netdev_priv(dev); - priv->type = IEEE802154_DEV_WPAN; - - priv->chan = MAC802154_CHAN_NONE; - priv->page = 0; - - spin_lock_init(&priv->mib_lock); - mutex_init(&priv->sec_mtx); - - get_random_bytes(&priv->bsn, 1); - get_random_bytes(&priv->dsn, 1); - - /* defaults per 802.15.4-2011 */ - priv->mac_params.min_be = 3; - priv->mac_params.max_be = 5; - priv->mac_params.csma_retries = 4; - priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */ - - priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); - priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); - - mac802154_llsec_init(&priv->sec); -} - -static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) -{ - return netif_rx_ni(skb); -} - -static int -mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, - const struct ieee802154_hdr *hdr) -{ - __le16 span, sshort; - int rc; - - pr_debug("getting packet via slave interface %s\n", sdata->dev->name); - - spin_lock_bh(&sdata->mib_lock); - - span = sdata->pan_id; - sshort = sdata->short_addr; - - switch (mac_cb(skb)->dest.mode) { - case IEEE802154_ADDR_NONE: - if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) - /* FIXME: check if we are PAN coordinator */ - skb->pkt_type = PACKET_OTHERHOST; - else - /* ACK comes with both addresses empty */ - skb->pkt_type = PACKET_HOST; - break; - case IEEE802154_ADDR_LONG: - if (mac_cb(skb)->dest.pan_id != span && - mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) - skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr) - skb->pkt_type = PACKET_HOST; - else - skb->pkt_type = PACKET_OTHERHOST; - break; - case IEEE802154_ADDR_SHORT: - if (mac_cb(skb)->dest.pan_id != span && - mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) - skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->dest.short_addr == sshort) - skb->pkt_type = PACKET_HOST; - else if (mac_cb(skb)->dest.short_addr == - cpu_to_le16(IEEE802154_ADDR_BROADCAST)) - skb->pkt_type = PACKET_BROADCAST; - else - skb->pkt_type = PACKET_OTHERHOST; - break; - default: - spin_unlock_bh(&sdata->mib_lock); - pr_debug("invalid dest mode\n"); - kfree_skb(skb); - return NET_RX_DROP; - } - - spin_unlock_bh(&sdata->mib_lock); - - skb->dev = sdata->dev; - - rc = mac802154_llsec_decrypt(&sdata->sec, skb); - if (rc) { - pr_debug("decryption failed: %i\n", rc); - goto fail; - } - - sdata->dev->stats.rx_packets++; - sdata->dev->stats.rx_bytes += skb->len; - - switch (mac_cb(skb)->type) { - case IEEE802154_FC_TYPE_DATA: - return mac802154_process_data(sdata->dev, skb); - default: - pr_warn("ieee802154: bad frame received (type = %d)\n", - mac_cb(skb)->type); - goto fail; - } - -fail: - kfree_skb(skb); - return NET_RX_DROP; -} - -static void mac802154_print_addr(const char *name, - const struct ieee802154_addr *addr) -{ - if (addr->mode == IEEE802154_ADDR_NONE) - pr_debug("%s not present\n", name); - - pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); - if (addr->mode == IEEE802154_ADDR_SHORT) { - pr_debug("%s is short: %04x\n", name, - le16_to_cpu(addr->short_addr)); - } else { - u64 hw = swab64((__force u64) addr->extended_addr); - - pr_debug("%s is hardware: %8phC\n", name, &hw); - } -} - -static int mac802154_parse_frame_start(struct sk_buff *skb, - struct ieee802154_hdr *hdr) -{ - int hlen; - struct ieee802154_mac_cb *cb = mac_cb_init(skb); - - hlen = ieee802154_hdr_pull(skb, hdr); - if (hlen < 0) - return -EINVAL; - - skb->mac_len = hlen; - - pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), - hdr->seq); - - cb->type = hdr->fc.type; - cb->ackreq = hdr->fc.ack_request; - cb->secen = hdr->fc.security_enabled; - - mac802154_print_addr("destination", &hdr->dest); - mac802154_print_addr("source", &hdr->source); - - cb->source = hdr->source; - cb->dest = hdr->dest; - - if (hdr->fc.security_enabled) { - u64 key; - - pr_debug("seclevel %i\n", hdr->sec.level); - - switch (hdr->sec.key_id_mode) { - case IEEE802154_SCF_KEY_IMPLICIT: - pr_debug("implicit key\n"); - break; - - case IEEE802154_SCF_KEY_INDEX: - pr_debug("key %02x\n", hdr->sec.key_id); - break; - - case IEEE802154_SCF_KEY_SHORT_INDEX: - pr_debug("key %04x:%04x %02x\n", - le32_to_cpu(hdr->sec.short_src) >> 16, - le32_to_cpu(hdr->sec.short_src) & 0xffff, - hdr->sec.key_id); - break; - - case IEEE802154_SCF_KEY_HW_INDEX: - key = swab64((__force u64) hdr->sec.extended_src); - pr_debug("key source %8phC %02x\n", &key, - hdr->sec.key_id); - break; - } - } - - return 0; -} - -void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) -{ - int ret; - struct mac802154_sub_if_data *sdata; - struct ieee802154_hdr hdr; - - ret = mac802154_parse_frame_start(skb, &hdr); - if (ret) { - pr_debug("got invalid frame\n"); - kfree_skb(skb); - return; - } - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) { - if (sdata->type != IEEE802154_DEV_WPAN || - !netif_running(sdata->dev)) - continue; - - mac802154_subif_frame(sdata, skb, &hdr); - skb = NULL; - break; - } - rcu_read_unlock(); - - if (skb) - kfree_skb(skb); -} |