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> 30421d20a3SDavid S. Miller #include <linux/proc_fs.h> 31f5bb1c55SRémi Denis-Courmont #include <linux/if_arp.h> 32f8ff6028SRemi Denis-Courmont #include <net/sock.h> 339a3b7a42Sremi.denis-courmont@nokia #include <net/netns/generic.h> 34f8ff6028SRemi Denis-Courmont #include <net/phonet/pn_dev.h> 35f8ff6028SRemi Denis-Courmont 36*55748ac0SRémi Denis-Courmont struct phonet_routes { 37*55748ac0SRémi Denis-Courmont spinlock_t lock; 38*55748ac0SRémi Denis-Courmont struct net_device *table[64]; 39*55748ac0SRémi Denis-Courmont }; 40*55748ac0SRémi Denis-Courmont 419a3b7a42Sremi.denis-courmont@nokia struct phonet_net { 429a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list pndevs; 43*55748ac0SRémi Denis-Courmont struct phonet_routes routes; 44f8ff6028SRemi Denis-Courmont }; 45f8ff6028SRemi Denis-Courmont 469a3b7a42Sremi.denis-courmont@nokia int phonet_net_id; 479a3b7a42Sremi.denis-courmont@nokia 489a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *phonet_device_list(struct net *net) 499a3b7a42Sremi.denis-courmont@nokia { 509a3b7a42Sremi.denis-courmont@nokia struct phonet_net *pnn = net_generic(net, phonet_net_id); 519a3b7a42Sremi.denis-courmont@nokia return &pnn->pndevs; 529a3b7a42Sremi.denis-courmont@nokia } 539a3b7a42Sremi.denis-courmont@nokia 54f8ff6028SRemi Denis-Courmont /* Allocate new Phonet device. */ 55f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_device_alloc(struct net_device *dev) 56f8ff6028SRemi Denis-Courmont { 579a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 58f8ff6028SRemi Denis-Courmont struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); 59f8ff6028SRemi Denis-Courmont if (pnd == NULL) 60f8ff6028SRemi Denis-Courmont return NULL; 61f8ff6028SRemi Denis-Courmont pnd->netdev = dev; 62f8ff6028SRemi Denis-Courmont bitmap_zero(pnd->addrs, 64); 63f8ff6028SRemi Denis-Courmont 649a3b7a42Sremi.denis-courmont@nokia list_add(&pnd->list, &pndevs->list); 65f8ff6028SRemi Denis-Courmont return pnd; 66f8ff6028SRemi Denis-Courmont } 67f8ff6028SRemi Denis-Courmont 68f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_get(struct net_device *dev) 69f8ff6028SRemi Denis-Courmont { 709a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 71f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 72f8ff6028SRemi Denis-Courmont 739a3b7a42Sremi.denis-courmont@nokia list_for_each_entry(pnd, &pndevs->list, list) { 74f8ff6028SRemi Denis-Courmont if (pnd->netdev == dev) 75f8ff6028SRemi Denis-Courmont return pnd; 76f8ff6028SRemi Denis-Courmont } 77f8ff6028SRemi Denis-Courmont return NULL; 78f8ff6028SRemi Denis-Courmont } 79f8ff6028SRemi Denis-Courmont 802be6fa4cSRémi Denis-Courmont static void phonet_device_destroy(struct net_device *dev) 81f8ff6028SRemi Denis-Courmont { 822be6fa4cSRémi Denis-Courmont struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 832be6fa4cSRémi Denis-Courmont struct phonet_device *pnd; 842be6fa4cSRémi Denis-Courmont 852be6fa4cSRémi Denis-Courmont ASSERT_RTNL(); 862be6fa4cSRémi Denis-Courmont 872be6fa4cSRémi Denis-Courmont spin_lock_bh(&pndevs->lock); 882be6fa4cSRémi Denis-Courmont pnd = __phonet_get(dev); 892be6fa4cSRémi Denis-Courmont if (pnd) 90f8ff6028SRemi Denis-Courmont list_del(&pnd->list); 912be6fa4cSRémi Denis-Courmont spin_unlock_bh(&pndevs->lock); 922be6fa4cSRémi Denis-Courmont 932be6fa4cSRémi Denis-Courmont if (pnd) { 942be6fa4cSRémi Denis-Courmont u8 addr; 952be6fa4cSRémi Denis-Courmont 962be6fa4cSRémi Denis-Courmont for (addr = find_first_bit(pnd->addrs, 64); addr < 64; 972be6fa4cSRémi Denis-Courmont addr = find_next_bit(pnd->addrs, 64, 1+addr)) 982be6fa4cSRémi Denis-Courmont phonet_address_notify(RTM_DELADDR, dev, addr); 99f8ff6028SRemi Denis-Courmont kfree(pnd); 100f8ff6028SRemi Denis-Courmont } 1012be6fa4cSRémi Denis-Courmont } 102f8ff6028SRemi Denis-Courmont 103f8ff6028SRemi Denis-Courmont struct net_device *phonet_device_get(struct net *net) 104f8ff6028SRemi Denis-Courmont { 1059a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(net); 106f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 10759e57f44SEric Dumazet struct net_device *dev = NULL; 108f8ff6028SRemi Denis-Courmont 1099a3b7a42Sremi.denis-courmont@nokia spin_lock_bh(&pndevs->lock); 1109a3b7a42Sremi.denis-courmont@nokia list_for_each_entry(pnd, &pndevs->list, list) { 111f8ff6028SRemi Denis-Courmont dev = pnd->netdev; 112f8ff6028SRemi Denis-Courmont BUG_ON(!dev); 113f8ff6028SRemi Denis-Courmont 1149a3b7a42Sremi.denis-courmont@nokia if ((dev->reg_state == NETREG_REGISTERED) && 115f8ff6028SRemi Denis-Courmont ((pnd->netdev->flags & IFF_UP)) == IFF_UP) 116f8ff6028SRemi Denis-Courmont break; 117f8ff6028SRemi Denis-Courmont dev = NULL; 118f8ff6028SRemi Denis-Courmont } 119f8ff6028SRemi Denis-Courmont if (dev) 120f8ff6028SRemi Denis-Courmont dev_hold(dev); 1219a3b7a42Sremi.denis-courmont@nokia spin_unlock_bh(&pndevs->lock); 122f8ff6028SRemi Denis-Courmont return dev; 123f8ff6028SRemi Denis-Courmont } 124f8ff6028SRemi Denis-Courmont 125f8ff6028SRemi Denis-Courmont int phonet_address_add(struct net_device *dev, u8 addr) 126f8ff6028SRemi Denis-Courmont { 1279a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 128f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 129f8ff6028SRemi Denis-Courmont int err = 0; 130f8ff6028SRemi Denis-Courmont 1319a3b7a42Sremi.denis-courmont@nokia spin_lock_bh(&pndevs->lock); 132f8ff6028SRemi Denis-Courmont /* Find or create Phonet-specific device data */ 133f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev); 134f8ff6028SRemi Denis-Courmont if (pnd == NULL) 135f8ff6028SRemi Denis-Courmont pnd = __phonet_device_alloc(dev); 136f8ff6028SRemi Denis-Courmont if (unlikely(pnd == NULL)) 137f8ff6028SRemi Denis-Courmont err = -ENOMEM; 138f8ff6028SRemi Denis-Courmont else if (test_and_set_bit(addr >> 2, pnd->addrs)) 139f8ff6028SRemi Denis-Courmont err = -EEXIST; 1409a3b7a42Sremi.denis-courmont@nokia spin_unlock_bh(&pndevs->lock); 141f8ff6028SRemi Denis-Courmont return err; 142f8ff6028SRemi Denis-Courmont } 143f8ff6028SRemi Denis-Courmont 144f8ff6028SRemi Denis-Courmont int phonet_address_del(struct net_device *dev, u8 addr) 145f8ff6028SRemi Denis-Courmont { 1469a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 147f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 148f8ff6028SRemi Denis-Courmont int err = 0; 149f8ff6028SRemi Denis-Courmont 1509a3b7a42Sremi.denis-courmont@nokia spin_lock_bh(&pndevs->lock); 151f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev); 152f8ff6028SRemi Denis-Courmont if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) 153f8ff6028SRemi Denis-Courmont err = -EADDRNOTAVAIL; 1542be6fa4cSRémi Denis-Courmont else if (bitmap_empty(pnd->addrs, 64)) { 1552be6fa4cSRémi Denis-Courmont list_del(&pnd->list); 1562be6fa4cSRémi Denis-Courmont kfree(pnd); 1572be6fa4cSRémi Denis-Courmont } 1589a3b7a42Sremi.denis-courmont@nokia spin_unlock_bh(&pndevs->lock); 159f8ff6028SRemi Denis-Courmont return err; 160f8ff6028SRemi Denis-Courmont } 161f8ff6028SRemi Denis-Courmont 162f8ff6028SRemi Denis-Courmont /* Gets a source address toward a destination, through a interface. */ 163*55748ac0SRémi Denis-Courmont u8 phonet_address_get(struct net_device *dev, u8 daddr) 164f8ff6028SRemi Denis-Courmont { 1659a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 166f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 167*55748ac0SRémi Denis-Courmont u8 saddr; 168f8ff6028SRemi Denis-Courmont 1699a3b7a42Sremi.denis-courmont@nokia spin_lock_bh(&pndevs->lock); 170f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev); 171f8ff6028SRemi Denis-Courmont if (pnd) { 172f8ff6028SRemi Denis-Courmont BUG_ON(bitmap_empty(pnd->addrs, 64)); 173f8ff6028SRemi Denis-Courmont 174f8ff6028SRemi Denis-Courmont /* Use same source address as destination, if possible */ 175*55748ac0SRémi Denis-Courmont if (test_bit(daddr >> 2, pnd->addrs)) 176*55748ac0SRémi Denis-Courmont saddr = daddr; 177*55748ac0SRémi Denis-Courmont else 178*55748ac0SRémi Denis-Courmont saddr = find_first_bit(pnd->addrs, 64) << 2; 179f8ff6028SRemi Denis-Courmont } else 180*55748ac0SRémi Denis-Courmont saddr = PN_NO_ADDR; 1819a3b7a42Sremi.denis-courmont@nokia spin_unlock_bh(&pndevs->lock); 182*55748ac0SRémi Denis-Courmont 183*55748ac0SRémi Denis-Courmont if (saddr == PN_NO_ADDR) { 184*55748ac0SRémi Denis-Courmont /* Fallback to another device */ 185*55748ac0SRémi Denis-Courmont struct net_device *def_dev; 186*55748ac0SRémi Denis-Courmont 187*55748ac0SRémi Denis-Courmont def_dev = phonet_device_get(dev_net(dev)); 188*55748ac0SRémi Denis-Courmont if (def_dev) { 189*55748ac0SRémi Denis-Courmont if (def_dev != dev) 190*55748ac0SRémi Denis-Courmont saddr = phonet_address_get(def_dev, daddr); 191*55748ac0SRémi Denis-Courmont dev_put(def_dev); 192*55748ac0SRémi Denis-Courmont } 193*55748ac0SRémi Denis-Courmont } 194*55748ac0SRémi Denis-Courmont return saddr; 195f8ff6028SRemi Denis-Courmont } 196f8ff6028SRemi Denis-Courmont 19752404881SRémi Denis-Courmont int phonet_address_lookup(struct net *net, u8 addr) 198f8ff6028SRemi Denis-Courmont { 1999a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(net); 200f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 2019a3b7a42Sremi.denis-courmont@nokia int err = -EADDRNOTAVAIL; 202f8ff6028SRemi Denis-Courmont 2039a3b7a42Sremi.denis-courmont@nokia spin_lock_bh(&pndevs->lock); 2049a3b7a42Sremi.denis-courmont@nokia list_for_each_entry(pnd, &pndevs->list, list) { 205f8ff6028SRemi Denis-Courmont /* Don't allow unregistering devices! */ 206f8ff6028SRemi Denis-Courmont if ((pnd->netdev->reg_state != NETREG_REGISTERED) || 207f8ff6028SRemi Denis-Courmont ((pnd->netdev->flags & IFF_UP)) != IFF_UP) 208f8ff6028SRemi Denis-Courmont continue; 209f8ff6028SRemi Denis-Courmont 210f8ff6028SRemi Denis-Courmont if (test_bit(addr >> 2, pnd->addrs)) { 2119a3b7a42Sremi.denis-courmont@nokia err = 0; 2129a3b7a42Sremi.denis-courmont@nokia goto found; 213f8ff6028SRemi Denis-Courmont } 214f8ff6028SRemi Denis-Courmont } 2159a3b7a42Sremi.denis-courmont@nokia found: 2169a3b7a42Sremi.denis-courmont@nokia spin_unlock_bh(&pndevs->lock); 2179a3b7a42Sremi.denis-courmont@nokia return err; 218f8ff6028SRemi Denis-Courmont } 219f8ff6028SRemi Denis-Courmont 220f5bb1c55SRémi Denis-Courmont /* automatically configure a Phonet device, if supported */ 221f5bb1c55SRémi Denis-Courmont static int phonet_device_autoconf(struct net_device *dev) 222f5bb1c55SRémi Denis-Courmont { 223f5bb1c55SRémi Denis-Courmont struct if_phonet_req req; 224f5bb1c55SRémi Denis-Courmont int ret; 225f5bb1c55SRémi Denis-Courmont 226f5bb1c55SRémi Denis-Courmont if (!dev->netdev_ops->ndo_do_ioctl) 227f5bb1c55SRémi Denis-Courmont return -EOPNOTSUPP; 228f5bb1c55SRémi Denis-Courmont 229f5bb1c55SRémi Denis-Courmont ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, 230f5bb1c55SRémi Denis-Courmont SIOCPNGAUTOCONF); 231f5bb1c55SRémi Denis-Courmont if (ret < 0) 232f5bb1c55SRémi Denis-Courmont return ret; 233b11b5165SRémi Denis-Courmont 234b11b5165SRémi Denis-Courmont ASSERT_RTNL(); 235b11b5165SRémi Denis-Courmont ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device); 236b11b5165SRémi Denis-Courmont if (ret) 237b11b5165SRémi Denis-Courmont return ret; 238b11b5165SRémi Denis-Courmont phonet_address_notify(RTM_NEWADDR, dev, 239b11b5165SRémi Denis-Courmont req.ifr_phonet_autoconf.device); 240b11b5165SRémi Denis-Courmont return 0; 241f5bb1c55SRémi Denis-Courmont } 242f5bb1c55SRémi Denis-Courmont 243f8ff6028SRemi Denis-Courmont /* notify Phonet of device events */ 244f8ff6028SRemi Denis-Courmont static int phonet_device_notify(struct notifier_block *me, unsigned long what, 245f8ff6028SRemi Denis-Courmont void *arg) 246f8ff6028SRemi Denis-Courmont { 247f8ff6028SRemi Denis-Courmont struct net_device *dev = arg; 248f8ff6028SRemi Denis-Courmont 249f5bb1c55SRémi Denis-Courmont switch (what) { 250f5bb1c55SRémi Denis-Courmont case NETDEV_REGISTER: 251f5bb1c55SRémi Denis-Courmont if (dev->type == ARPHRD_PHONET) 252f5bb1c55SRémi Denis-Courmont phonet_device_autoconf(dev); 253f5bb1c55SRémi Denis-Courmont break; 254f5bb1c55SRémi Denis-Courmont case NETDEV_UNREGISTER: 2552be6fa4cSRémi Denis-Courmont phonet_device_destroy(dev); 256f5bb1c55SRémi Denis-Courmont break; 257f5bb1c55SRémi Denis-Courmont } 258f8ff6028SRemi Denis-Courmont return 0; 259f8ff6028SRemi Denis-Courmont 260f8ff6028SRemi Denis-Courmont } 261f8ff6028SRemi Denis-Courmont 262f8ff6028SRemi Denis-Courmont static struct notifier_block phonet_device_notifier = { 263f8ff6028SRemi Denis-Courmont .notifier_call = phonet_device_notify, 264f8ff6028SRemi Denis-Courmont .priority = 0, 265f8ff6028SRemi Denis-Courmont }; 266f8ff6028SRemi Denis-Courmont 2679a3b7a42Sremi.denis-courmont@nokia /* Per-namespace Phonet devices handling */ 2689a3b7a42Sremi.denis-courmont@nokia static int phonet_init_net(struct net *net) 2699a3b7a42Sremi.denis-courmont@nokia { 270*55748ac0SRémi Denis-Courmont struct phonet_net *pnn = kzalloc(sizeof(*pnn), GFP_KERNEL); 2719a3b7a42Sremi.denis-courmont@nokia if (!pnn) 2729a3b7a42Sremi.denis-courmont@nokia return -ENOMEM; 2739a3b7a42Sremi.denis-courmont@nokia 274c1dc13e9SRémi Denis-Courmont if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) { 275c1dc13e9SRémi Denis-Courmont kfree(pnn); 276c1dc13e9SRémi Denis-Courmont return -ENOMEM; 277c1dc13e9SRémi Denis-Courmont } 278c1dc13e9SRémi Denis-Courmont 2799a3b7a42Sremi.denis-courmont@nokia INIT_LIST_HEAD(&pnn->pndevs.list); 2809a3b7a42Sremi.denis-courmont@nokia spin_lock_init(&pnn->pndevs.lock); 281*55748ac0SRémi Denis-Courmont spin_lock_init(&pnn->routes.lock); 2829a3b7a42Sremi.denis-courmont@nokia net_assign_generic(net, phonet_net_id, pnn); 2839a3b7a42Sremi.denis-courmont@nokia return 0; 2849a3b7a42Sremi.denis-courmont@nokia } 2859a3b7a42Sremi.denis-courmont@nokia 2869a3b7a42Sremi.denis-courmont@nokia static void phonet_exit_net(struct net *net) 2879a3b7a42Sremi.denis-courmont@nokia { 2889a3b7a42Sremi.denis-courmont@nokia struct phonet_net *pnn = net_generic(net, phonet_net_id); 2892be6fa4cSRémi Denis-Courmont struct net_device *dev; 2909a3b7a42Sremi.denis-courmont@nokia 2912be6fa4cSRémi Denis-Courmont rtnl_lock(); 2922be6fa4cSRémi Denis-Courmont for_each_netdev(net, dev) 2932be6fa4cSRémi Denis-Courmont phonet_device_destroy(dev); 2942be6fa4cSRémi Denis-Courmont rtnl_unlock(); 295c1dc13e9SRémi Denis-Courmont 296c1dc13e9SRémi Denis-Courmont proc_net_remove(net, "phonet"); 2979a3b7a42Sremi.denis-courmont@nokia kfree(pnn); 2989a3b7a42Sremi.denis-courmont@nokia } 2999a3b7a42Sremi.denis-courmont@nokia 3009a3b7a42Sremi.denis-courmont@nokia static struct pernet_operations phonet_net_ops = { 3019a3b7a42Sremi.denis-courmont@nokia .init = phonet_init_net, 3029a3b7a42Sremi.denis-courmont@nokia .exit = phonet_exit_net, 3039a3b7a42Sremi.denis-courmont@nokia }; 3049a3b7a42Sremi.denis-courmont@nokia 305f8ff6028SRemi Denis-Courmont /* Initialize Phonet devices list */ 30676e02cf6Sremi.denis-courmont@nokia int __init phonet_device_init(void) 307f8ff6028SRemi Denis-Courmont { 3089a3b7a42Sremi.denis-courmont@nokia int err = register_pernet_gen_device(&phonet_net_id, &phonet_net_ops); 3099a3b7a42Sremi.denis-courmont@nokia if (err) 3109a3b7a42Sremi.denis-courmont@nokia return err; 311660f706dSremi.denis-courmont@nokia 312f8ff6028SRemi Denis-Courmont register_netdevice_notifier(&phonet_device_notifier); 313660f706dSremi.denis-courmont@nokia err = phonet_netlink_register(); 314660f706dSremi.denis-courmont@nokia if (err) 315660f706dSremi.denis-courmont@nokia phonet_device_exit(); 316660f706dSremi.denis-courmont@nokia return err; 317f8ff6028SRemi Denis-Courmont } 318f8ff6028SRemi Denis-Courmont 319f8ff6028SRemi Denis-Courmont void phonet_device_exit(void) 320f8ff6028SRemi Denis-Courmont { 321f8ff6028SRemi Denis-Courmont rtnl_unregister_all(PF_PHONET); 3226530e0feSremi.denis-courmont@nokia unregister_netdevice_notifier(&phonet_device_notifier); 3239a3b7a42Sremi.denis-courmont@nokia unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops); 324f8ff6028SRemi Denis-Courmont } 325*55748ac0SRémi Denis-Courmont 326*55748ac0SRémi Denis-Courmont int phonet_route_add(struct net_device *dev, u8 daddr) 327*55748ac0SRémi Denis-Courmont { 328*55748ac0SRémi Denis-Courmont struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id); 329*55748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 330*55748ac0SRémi Denis-Courmont int err = -EEXIST; 331*55748ac0SRémi Denis-Courmont 332*55748ac0SRémi Denis-Courmont daddr = daddr >> 2; 333*55748ac0SRémi Denis-Courmont spin_lock_bh(&routes->lock); 334*55748ac0SRémi Denis-Courmont if (routes->table[daddr] == NULL) { 335*55748ac0SRémi Denis-Courmont routes->table[daddr] = dev; 336*55748ac0SRémi Denis-Courmont dev_hold(dev); 337*55748ac0SRémi Denis-Courmont err = 0; 338*55748ac0SRémi Denis-Courmont } 339*55748ac0SRémi Denis-Courmont spin_unlock_bh(&routes->lock); 340*55748ac0SRémi Denis-Courmont return err; 341*55748ac0SRémi Denis-Courmont } 342*55748ac0SRémi Denis-Courmont 343*55748ac0SRémi Denis-Courmont int phonet_route_del(struct net_device *dev, u8 daddr) 344*55748ac0SRémi Denis-Courmont { 345*55748ac0SRémi Denis-Courmont struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id); 346*55748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 347*55748ac0SRémi Denis-Courmont int err = -ENOENT; 348*55748ac0SRémi Denis-Courmont 349*55748ac0SRémi Denis-Courmont daddr = daddr >> 2; 350*55748ac0SRémi Denis-Courmont spin_lock_bh(&routes->lock); 351*55748ac0SRémi Denis-Courmont if (dev == routes->table[daddr]) { 352*55748ac0SRémi Denis-Courmont routes->table[daddr] = NULL; 353*55748ac0SRémi Denis-Courmont dev_put(dev); 354*55748ac0SRémi Denis-Courmont err = 0; 355*55748ac0SRémi Denis-Courmont } 356*55748ac0SRémi Denis-Courmont spin_unlock_bh(&routes->lock); 357*55748ac0SRémi Denis-Courmont return err; 358*55748ac0SRémi Denis-Courmont } 359*55748ac0SRémi Denis-Courmont 360*55748ac0SRémi Denis-Courmont struct net_device *phonet_route_get(struct net *net, u8 daddr) 361*55748ac0SRémi Denis-Courmont { 362*55748ac0SRémi Denis-Courmont struct phonet_net *pnn = net_generic(net, phonet_net_id); 363*55748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 364*55748ac0SRémi Denis-Courmont struct net_device *dev; 365*55748ac0SRémi Denis-Courmont 366*55748ac0SRémi Denis-Courmont ASSERT_RTNL(); /* no need to hold the device */ 367*55748ac0SRémi Denis-Courmont 368*55748ac0SRémi Denis-Courmont daddr >>= 2; 369*55748ac0SRémi Denis-Courmont spin_lock_bh(&routes->lock); 370*55748ac0SRémi Denis-Courmont dev = routes->table[daddr]; 371*55748ac0SRémi Denis-Courmont spin_unlock_bh(&routes->lock); 372*55748ac0SRémi Denis-Courmont return dev; 373*55748ac0SRémi Denis-Courmont } 374*55748ac0SRémi Denis-Courmont 375*55748ac0SRémi Denis-Courmont struct net_device *phonet_route_output(struct net *net, u8 daddr) 376*55748ac0SRémi Denis-Courmont { 377*55748ac0SRémi Denis-Courmont struct phonet_net *pnn = net_generic(net, phonet_net_id); 378*55748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 379*55748ac0SRémi Denis-Courmont struct net_device *dev; 380*55748ac0SRémi Denis-Courmont 381*55748ac0SRémi Denis-Courmont spin_lock_bh(&routes->lock); 382*55748ac0SRémi Denis-Courmont dev = routes->table[daddr >> 2]; 383*55748ac0SRémi Denis-Courmont if (dev) 384*55748ac0SRémi Denis-Courmont dev_hold(dev); 385*55748ac0SRémi Denis-Courmont spin_unlock_bh(&routes->lock); 386*55748ac0SRémi Denis-Courmont 387*55748ac0SRémi Denis-Courmont if (!dev) 388*55748ac0SRémi Denis-Courmont dev = phonet_device_get(net); /* Default route */ 389*55748ac0SRémi Denis-Courmont return dev; 390*55748ac0SRémi Denis-Courmont } 391