linux/net/phonet/pn_dev.c
<<
>>
Prefs
   1/*
   2 * File: pn_dev.c
   3 *
   4 * Phonet network device
   5 *
   6 * Copyright (C) 2008 Nokia Corporation.
   7 *
   8 * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
   9 * Original author: Sakari Ailus <sakari.ailus@nokia.com>
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * version 2 as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  23 * 02110-1301 USA
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/net.h>
  28#include <linux/netdevice.h>
  29#include <linux/phonet.h>
  30#include <net/sock.h>
  31#include <net/phonet/pn_dev.h>
  32
  33/* when accessing, remember to lock with spin_lock(&pndevs.lock); */
  34struct phonet_device_list pndevs = {
  35        .list = LIST_HEAD_INIT(pndevs.list),
  36        .lock = __SPIN_LOCK_UNLOCKED(pndevs.lock),
  37};
  38
  39/* Allocate new Phonet device. */
  40static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
  41{
  42        struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC);
  43        if (pnd == NULL)
  44                return NULL;
  45        pnd->netdev = dev;
  46        bitmap_zero(pnd->addrs, 64);
  47
  48        list_add(&pnd->list, &pndevs.list);
  49        return pnd;
  50}
  51
  52static struct phonet_device *__phonet_get(struct net_device *dev)
  53{
  54        struct phonet_device *pnd;
  55
  56        list_for_each_entry(pnd, &pndevs.list, list) {
  57                if (pnd->netdev == dev)
  58                        return pnd;
  59        }
  60        return NULL;
  61}
  62
  63static void __phonet_device_free(struct phonet_device *pnd)
  64{
  65        list_del(&pnd->list);
  66        kfree(pnd);
  67}
  68
  69struct net_device *phonet_device_get(struct net *net)
  70{
  71        struct phonet_device *pnd;
  72        struct net_device *dev;
  73
  74        spin_lock_bh(&pndevs.lock);
  75        list_for_each_entry(pnd, &pndevs.list, list) {
  76                dev = pnd->netdev;
  77                BUG_ON(!dev);
  78
  79                if (dev_net(dev) == net &&
  80                        (dev->reg_state == NETREG_REGISTERED) &&
  81                        ((pnd->netdev->flags & IFF_UP)) == IFF_UP)
  82                        break;
  83                dev = NULL;
  84        }
  85        if (dev)
  86                dev_hold(dev);
  87        spin_unlock_bh(&pndevs.lock);
  88        return dev;
  89}
  90
  91int phonet_address_add(struct net_device *dev, u8 addr)
  92{
  93        struct phonet_device *pnd;
  94        int err = 0;
  95
  96        spin_lock_bh(&pndevs.lock);
  97        /* Find or create Phonet-specific device data */
  98        pnd = __phonet_get(dev);
  99        if (pnd == NULL)
 100                pnd = __phonet_device_alloc(dev);
 101        if (unlikely(pnd == NULL))
 102                err = -ENOMEM;
 103        else if (test_and_set_bit(addr >> 2, pnd->addrs))
 104                err = -EEXIST;
 105        spin_unlock_bh(&pndevs.lock);
 106        return err;
 107}
 108
 109int phonet_address_del(struct net_device *dev, u8 addr)
 110{
 111        struct phonet_device *pnd;
 112        int err = 0;
 113
 114        spin_lock_bh(&pndevs.lock);
 115        pnd = __phonet_get(dev);
 116        if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs))
 117                err = -EADDRNOTAVAIL;
 118        else if (bitmap_empty(pnd->addrs, 64))
 119                __phonet_device_free(pnd);
 120        spin_unlock_bh(&pndevs.lock);
 121        return err;
 122}
 123
 124/* Gets a source address toward a destination, through a interface. */
 125u8 phonet_address_get(struct net_device *dev, u8 addr)
 126{
 127        struct phonet_device *pnd;
 128
 129        spin_lock_bh(&pndevs.lock);
 130        pnd = __phonet_get(dev);
 131        if (pnd) {
 132                BUG_ON(bitmap_empty(pnd->addrs, 64));
 133
 134                /* Use same source address as destination, if possible */
 135                if (!test_bit(addr >> 2, pnd->addrs))
 136                        addr = find_first_bit(pnd->addrs, 64) << 2;
 137        } else
 138                addr = PN_NO_ADDR;
 139        spin_unlock_bh(&pndevs.lock);
 140        return addr;
 141}
 142
 143int phonet_address_lookup(u8 addr)
 144{
 145        struct phonet_device *pnd;
 146
 147        spin_lock_bh(&pndevs.lock);
 148        list_for_each_entry(pnd, &pndevs.list, list) {
 149                /* Don't allow unregistering devices! */
 150                if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
 151                                ((pnd->netdev->flags & IFF_UP)) != IFF_UP)
 152                        continue;
 153
 154                if (test_bit(addr >> 2, pnd->addrs)) {
 155                        spin_unlock_bh(&pndevs.lock);
 156                        return 0;
 157                }
 158        }
 159        spin_unlock_bh(&pndevs.lock);
 160        return -EADDRNOTAVAIL;
 161}
 162
 163/* notify Phonet of device events */
 164static int phonet_device_notify(struct notifier_block *me, unsigned long what,
 165                                void *arg)
 166{
 167        struct net_device *dev = arg;
 168
 169        if (what == NETDEV_UNREGISTER) {
 170                struct phonet_device *pnd;
 171
 172                /* Destroy phonet-specific device data */
 173                spin_lock_bh(&pndevs.lock);
 174                pnd = __phonet_get(dev);
 175                if (pnd)
 176                        __phonet_device_free(pnd);
 177                spin_unlock_bh(&pndevs.lock);
 178        }
 179        return 0;
 180
 181}
 182
 183static struct notifier_block phonet_device_notifier = {
 184        .notifier_call = phonet_device_notify,
 185        .priority = 0,
 186};
 187
 188/* Initialize Phonet devices list */
 189void phonet_device_init(void)
 190{
 191        register_netdevice_notifier(&phonet_device_notifier);
 192}
 193
 194void phonet_device_exit(void)
 195{
 196        struct phonet_device *pnd, *n;
 197
 198        rtnl_unregister_all(PF_PHONET);
 199        rtnl_lock();
 200        spin_lock_bh(&pndevs.lock);
 201
 202        list_for_each_entry_safe(pnd, n, &pndevs.list, list)
 203                __phonet_device_free(pnd);
 204
 205        spin_unlock_bh(&pndevs.lock);
 206        rtnl_unlock();
 207        unregister_netdevice_notifier(&phonet_device_notifier);
 208}
 209
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.