linux/net/mac802154/ieee802154_dev.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007-2012 Siemens AG
   3 *
   4 * Written by:
   5 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
   6 *
   7 * Based on the code from 'linux-zigbee.sourceforge.net' project.
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2
  11 * as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along
  19 * with this program; if not, write to the Free Software Foundation, Inc.,
  20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21 */
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/netdevice.h>
  26
  27#include <net/netlink.h>
  28#include <linux/nl802154.h>
  29#include <net/mac802154.h>
  30#include <net/route.h>
  31#include <net/wpan-phy.h>
  32
  33#include "mac802154.h"
  34
  35int mac802154_slave_open(struct net_device *dev)
  36{
  37        struct mac802154_sub_if_data *priv = netdev_priv(dev);
  38        struct mac802154_priv *ipriv = priv->hw;
  39        int res = 0;
  40
  41        if (ipriv->open_count++ == 0) {
  42                res = ipriv->ops->start(&ipriv->hw);
  43                WARN_ON(res);
  44                if (res)
  45                        goto err;
  46        }
  47
  48        if (ipriv->ops->ieee_addr) {
  49                res = ipriv->ops->ieee_addr(&ipriv->hw, dev->dev_addr);
  50                WARN_ON(res);
  51                if (res)
  52                        goto err;
  53                mac802154_dev_set_ieee_addr(dev);
  54        }
  55
  56        netif_start_queue(dev);
  57        return 0;
  58err:
  59        priv->hw->open_count--;
  60
  61        return res;
  62}
  63
  64int mac802154_slave_close(struct net_device *dev)
  65{
  66        struct mac802154_sub_if_data *priv = netdev_priv(dev);
  67        struct mac802154_priv *ipriv = priv->hw;
  68
  69        netif_stop_queue(dev);
  70
  71        if (!--ipriv->open_count)
  72                ipriv->ops->stop(&ipriv->hw);
  73
  74        return 0;
  75}
  76
  77static int
  78mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
  79{
  80        struct mac802154_sub_if_data *priv;
  81        struct mac802154_priv *ipriv;
  82        int err;
  83
  84        ipriv = wpan_phy_priv(phy);
  85
  86        priv = netdev_priv(dev);
  87        priv->dev = dev;
  88        priv->hw = ipriv;
  89
  90        dev->needed_headroom = ipriv->hw.extra_tx_headroom;
  91
  92        SET_NETDEV_DEV(dev, &ipriv->phy->dev);
  93
  94        mutex_lock(&ipriv->slaves_mtx);
  95        if (!ipriv->running) {
  96                mutex_unlock(&ipriv->slaves_mtx);
  97                return -ENODEV;
  98        }
  99        mutex_unlock(&ipriv->slaves_mtx);
 100
 101        err = register_netdev(dev);
 102        if (err < 0)
 103                return err;
 104
 105        rtnl_lock();
 106        mutex_lock(&ipriv->slaves_mtx);
 107        list_add_tail_rcu(&priv->list, &ipriv->slaves);
 108        mutex_unlock(&ipriv->slaves_mtx);
 109        rtnl_unlock();
 110
 111        return 0;
 112}
 113
 114static void
 115mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
 116{
 117        struct mac802154_sub_if_data *sdata;
 118        ASSERT_RTNL();
 119
 120        sdata = netdev_priv(dev);
 121
 122        BUG_ON(sdata->hw->phy != phy);
 123
 124        mutex_lock(&sdata->hw->slaves_mtx);
 125        list_del_rcu(&sdata->list);
 126        mutex_unlock(&sdata->hw->slaves_mtx);
 127
 128        synchronize_rcu();
 129        unregister_netdevice(sdata->dev);
 130}
 131
 132static struct net_device *
 133mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
 134{
 135        struct net_device *dev;
 136        int err = -ENOMEM;
 137
 138        switch (type) {
 139        case IEEE802154_DEV_MONITOR:
 140                dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
 141                                   name, mac802154_monitor_setup);
 142                break;
 143        case IEEE802154_DEV_WPAN:
 144                dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
 145                                   name, mac802154_wpan_setup);
 146                break;
 147        default:
 148                dev = NULL;
 149                err = -EINVAL;
 150                break;
 151        }
 152        if (!dev)
 153                goto err;
 154
 155        err = mac802154_netdev_register(phy, dev);
 156        if (err)
 157                goto err_free;
 158
 159        dev_hold(dev); /* we return an incremented device refcount */
 160        return dev;
 161
 162err_free:
 163        free_netdev(dev);
 164err:
 165        return ERR_PTR(err);
 166}
 167
 168struct ieee802154_dev *
 169ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 170{
 171        struct wpan_phy *phy;
 172        struct mac802154_priv *priv;
 173        size_t priv_size;
 174
 175        if (!ops || !ops->xmit || !ops->ed || !ops->start ||
 176            !ops->stop || !ops->set_channel) {
 177                printk(KERN_ERR
 178                       "undefined IEEE802.15.4 device operations\n");
 179                return NULL;
 180        }
 181
 182        /* Ensure 32-byte alignment of our private data and hw private data.
 183         * We use the wpan_phy priv data for both our mac802154_priv and for
 184         * the driver's private data
 185         *
 186         * in memory it'll be like this:
 187         *
 188         * +-----------------------+
 189         * | struct wpan_phy       |
 190         * +-----------------------+
 191         * | struct mac802154_priv |
 192         * +-----------------------+
 193         * | driver's private data |
 194         * +-----------------------+
 195         *
 196         * Due to ieee802154 layer isn't aware of driver and MAC structures,
 197         * so lets allign them here.
 198         */
 199
 200        priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len;
 201
 202        phy = wpan_phy_alloc(priv_size);
 203        if (!phy) {
 204                printk(KERN_ERR
 205                       "failure to allocate master IEEE802.15.4 device\n");
 206                return NULL;
 207        }
 208
 209        priv = wpan_phy_priv(phy);
 210        priv->hw.phy = priv->phy = phy;
 211        priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
 212        priv->ops = ops;
 213
 214        INIT_LIST_HEAD(&priv->slaves);
 215        mutex_init(&priv->slaves_mtx);
 216
 217        return &priv->hw;
 218}
 219EXPORT_SYMBOL(ieee802154_alloc_device);
 220
 221void ieee802154_free_device(struct ieee802154_dev *hw)
 222{
 223        struct mac802154_priv *priv = mac802154_to_priv(hw);
 224
 225        BUG_ON(!list_empty(&priv->slaves));
 226
 227        wpan_phy_free(priv->phy);
 228
 229        mutex_destroy(&priv->slaves_mtx);
 230}
 231EXPORT_SYMBOL(ieee802154_free_device);
 232
 233int ieee802154_register_device(struct ieee802154_dev *dev)
 234{
 235        struct mac802154_priv *priv = mac802154_to_priv(dev);
 236        int rc = -ENOMEM;
 237
 238        priv->dev_workqueue =
 239                create_singlethread_workqueue(wpan_phy_name(priv->phy));
 240        if (!priv->dev_workqueue)
 241                goto out;
 242
 243        wpan_phy_set_dev(priv->phy, priv->hw.parent);
 244
 245        priv->phy->add_iface = mac802154_add_iface;
 246        priv->phy->del_iface = mac802154_del_iface;
 247
 248        rc = wpan_phy_register(priv->phy);
 249        if (rc < 0)
 250                goto out_wq;
 251
 252        rtnl_lock();
 253
 254        mutex_lock(&priv->slaves_mtx);
 255        priv->running = MAC802154_DEVICE_RUN;
 256        mutex_unlock(&priv->slaves_mtx);
 257
 258        rtnl_unlock();
 259
 260        return 0;
 261
 262out_wq:
 263        destroy_workqueue(priv->dev_workqueue);
 264out:
 265        return rc;
 266}
 267EXPORT_SYMBOL(ieee802154_register_device);
 268
 269void ieee802154_unregister_device(struct ieee802154_dev *dev)
 270{
 271        struct mac802154_priv *priv = mac802154_to_priv(dev);
 272        struct mac802154_sub_if_data *sdata, *next;
 273
 274        flush_workqueue(priv->dev_workqueue);
 275        destroy_workqueue(priv->dev_workqueue);
 276
 277        rtnl_lock();
 278
 279        mutex_lock(&priv->slaves_mtx);
 280        priv->running = MAC802154_DEVICE_STOPPED;
 281        mutex_unlock(&priv->slaves_mtx);
 282
 283        list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
 284                mutex_lock(&sdata->hw->slaves_mtx);
 285                list_del(&sdata->list);
 286                mutex_unlock(&sdata->hw->slaves_mtx);
 287
 288                unregister_netdevice(sdata->dev);
 289        }
 290
 291        rtnl_unlock();
 292
 293        wpan_phy_unregister(priv->phy);
 294}
 295EXPORT_SYMBOL(ieee802154_unregister_device);
 296
 297MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
 298MODULE_LICENSE("GPL v2");
 299
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.