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