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