10739d643Salex.bluesman.smirnov@gmail.com /* 20739d643Salex.bluesman.smirnov@gmail.com * Loopback IEEE 802.15.4 interface 30739d643Salex.bluesman.smirnov@gmail.com * 40739d643Salex.bluesman.smirnov@gmail.com * Copyright 2007-2012 Siemens AG 50739d643Salex.bluesman.smirnov@gmail.com * 60739d643Salex.bluesman.smirnov@gmail.com * This program is free software; you can redistribute it and/or modify 70739d643Salex.bluesman.smirnov@gmail.com * it under the terms of the GNU General Public License version 2 80739d643Salex.bluesman.smirnov@gmail.com * as published by the Free Software Foundation. 90739d643Salex.bluesman.smirnov@gmail.com * 100739d643Salex.bluesman.smirnov@gmail.com * This program is distributed in the hope that it will be useful, 110739d643Salex.bluesman.smirnov@gmail.com * but WITHOUT ANY WARRANTY; without even the implied warranty of 120739d643Salex.bluesman.smirnov@gmail.com * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 130739d643Salex.bluesman.smirnov@gmail.com * GNU General Public License for more details. 140739d643Salex.bluesman.smirnov@gmail.com * 150739d643Salex.bluesman.smirnov@gmail.com * Written by: 160739d643Salex.bluesman.smirnov@gmail.com * Sergey Lapin <slapin@ossfans.org> 170739d643Salex.bluesman.smirnov@gmail.com * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 180739d643Salex.bluesman.smirnov@gmail.com * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 190739d643Salex.bluesman.smirnov@gmail.com */ 200739d643Salex.bluesman.smirnov@gmail.com 210739d643Salex.bluesman.smirnov@gmail.com #include <linux/module.h> 220739d643Salex.bluesman.smirnov@gmail.com #include <linux/timer.h> 230739d643Salex.bluesman.smirnov@gmail.com #include <linux/platform_device.h> 240739d643Salex.bluesman.smirnov@gmail.com #include <linux/netdevice.h> 2512b5c38fSHimangi Saraogi #include <linux/device.h> 260739d643Salex.bluesman.smirnov@gmail.com #include <linux/spinlock.h> 270739d643Salex.bluesman.smirnov@gmail.com #include <net/mac802154.h> 285ad60d36SAlexander Aring #include <net/cfg802154.h> 290739d643Salex.bluesman.smirnov@gmail.com 307e57905bSAlexander Aring static int numlbs = 2; 31e369dc8fSAlexander Aring 32b82b99f1SAlexander Aring static LIST_HEAD(fakelb_phys); 33e369dc8fSAlexander Aring static DEFINE_SPINLOCK(fakelb_phys_lock); 34e369dc8fSAlexander Aring 35e369dc8fSAlexander Aring static LIST_HEAD(fakelb_ifup_phys); 36e369dc8fSAlexander Aring static DEFINE_RWLOCK(fakelb_ifup_phys_lock); 370739d643Salex.bluesman.smirnov@gmail.com 38db3f5d0dSAlexander Aring struct fakelb_phy { 395a504397SAlexander Aring struct ieee802154_hw *hw; 400739d643Salex.bluesman.smirnov@gmail.com 4112da8d97SAlexander Aring u8 page; 4212da8d97SAlexander Aring u8 channel; 4312da8d97SAlexander Aring 440739d643Salex.bluesman.smirnov@gmail.com struct list_head list; 45e369dc8fSAlexander Aring struct list_head list_ifup; 460739d643Salex.bluesman.smirnov@gmail.com }; 470739d643Salex.bluesman.smirnov@gmail.com 480739d643Salex.bluesman.smirnov@gmail.com static int 495a504397SAlexander Aring fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 500739d643Salex.bluesman.smirnov@gmail.com { 510739d643Salex.bluesman.smirnov@gmail.com BUG_ON(!level); 520739d643Salex.bluesman.smirnov@gmail.com *level = 0xbe; 530739d643Salex.bluesman.smirnov@gmail.com 540739d643Salex.bluesman.smirnov@gmail.com return 0; 550739d643Salex.bluesman.smirnov@gmail.com } 560739d643Salex.bluesman.smirnov@gmail.com 570739d643Salex.bluesman.smirnov@gmail.com static int 58e37d2ec8SAlexander Aring fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 590739d643Salex.bluesman.smirnov@gmail.com { 6012da8d97SAlexander Aring struct fakelb_phy *phy = hw->priv; 610739d643Salex.bluesman.smirnov@gmail.com 6212da8d97SAlexander Aring write_lock_bh(&fakelb_ifup_phys_lock); 6312da8d97SAlexander Aring phy->page = page; 6412da8d97SAlexander Aring phy->channel = channel; 6512da8d97SAlexander Aring write_unlock_bh(&fakelb_ifup_phys_lock); 660739d643Salex.bluesman.smirnov@gmail.com return 0; 670739d643Salex.bluesman.smirnov@gmail.com } 680739d643Salex.bluesman.smirnov@gmail.com 690739d643Salex.bluesman.smirnov@gmail.com static void 70db3f5d0dSAlexander Aring fakelb_hw_deliver(struct fakelb_phy *phy, struct sk_buff *skb) 710739d643Salex.bluesman.smirnov@gmail.com { 720739d643Salex.bluesman.smirnov@gmail.com struct sk_buff *newskb; 730739d643Salex.bluesman.smirnov@gmail.com 740739d643Salex.bluesman.smirnov@gmail.com newskb = pskb_copy(skb, GFP_ATOMIC); 75cb97d9a3SMartin Townsend if (newskb) 76db3f5d0dSAlexander Aring ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc); 770739d643Salex.bluesman.smirnov@gmail.com } 780739d643Salex.bluesman.smirnov@gmail.com 790739d643Salex.bluesman.smirnov@gmail.com static int 805a504397SAlexander Aring fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 810739d643Salex.bluesman.smirnov@gmail.com { 82db3f5d0dSAlexander Aring struct fakelb_phy *current_phy = hw->priv; 83db3f5d0dSAlexander Aring struct fakelb_phy *phy; 84db3f5d0dSAlexander Aring 85e369dc8fSAlexander Aring read_lock_bh(&fakelb_ifup_phys_lock); 86e369dc8fSAlexander Aring list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) { 879f8b97f8SAlexander Aring if (current_phy == phy) 889f8b97f8SAlexander Aring continue; 899f8b97f8SAlexander Aring 9012da8d97SAlexander Aring if (current_phy->page == phy->page && 9112da8d97SAlexander Aring current_phy->channel == phy->channel) 92db3f5d0dSAlexander Aring fakelb_hw_deliver(phy, skb); 930739d643Salex.bluesman.smirnov@gmail.com } 94e369dc8fSAlexander Aring read_unlock_bh(&fakelb_ifup_phys_lock); 950739d643Salex.bluesman.smirnov@gmail.com 960739d643Salex.bluesman.smirnov@gmail.com return 0; 970739d643Salex.bluesman.smirnov@gmail.com } 980739d643Salex.bluesman.smirnov@gmail.com 990739d643Salex.bluesman.smirnov@gmail.com static int 1005a504397SAlexander Aring fakelb_hw_start(struct ieee802154_hw *hw) { 101db3f5d0dSAlexander Aring struct fakelb_phy *phy = hw->priv; 1020739d643Salex.bluesman.smirnov@gmail.com 103e369dc8fSAlexander Aring write_lock_bh(&fakelb_ifup_phys_lock); 104e369dc8fSAlexander Aring list_add(&phy->list_ifup, &fakelb_ifup_phys); 105e369dc8fSAlexander Aring write_unlock_bh(&fakelb_ifup_phys_lock); 1060739d643Salex.bluesman.smirnov@gmail.com 107e369dc8fSAlexander Aring return 0; 1080739d643Salex.bluesman.smirnov@gmail.com } 1090739d643Salex.bluesman.smirnov@gmail.com 1100739d643Salex.bluesman.smirnov@gmail.com static void 1115a504397SAlexander Aring fakelb_hw_stop(struct ieee802154_hw *hw) { 112db3f5d0dSAlexander Aring struct fakelb_phy *phy = hw->priv; 1130739d643Salex.bluesman.smirnov@gmail.com 114e369dc8fSAlexander Aring write_lock_bh(&fakelb_ifup_phys_lock); 115e369dc8fSAlexander Aring list_del(&phy->list_ifup); 116e369dc8fSAlexander Aring write_unlock_bh(&fakelb_ifup_phys_lock); 1170739d643Salex.bluesman.smirnov@gmail.com } 1180739d643Salex.bluesman.smirnov@gmail.com 11916301861SAlexander Aring static const struct ieee802154_ops fakelb_ops = { 1200739d643Salex.bluesman.smirnov@gmail.com .owner = THIS_MODULE, 121ed0a5dceSAlexander Aring .xmit_sync = fakelb_hw_xmit, 1220739d643Salex.bluesman.smirnov@gmail.com .ed = fakelb_hw_ed, 1230739d643Salex.bluesman.smirnov@gmail.com .set_channel = fakelb_hw_channel, 1240739d643Salex.bluesman.smirnov@gmail.com .start = fakelb_hw_start, 1250739d643Salex.bluesman.smirnov@gmail.com .stop = fakelb_hw_stop, 1260739d643Salex.bluesman.smirnov@gmail.com }; 1270739d643Salex.bluesman.smirnov@gmail.com 1280739d643Salex.bluesman.smirnov@gmail.com /* Number of dummy devices to be set up by this module. */ 1290739d643Salex.bluesman.smirnov@gmail.com module_param(numlbs, int, 0); 1300739d643Salex.bluesman.smirnov@gmail.com MODULE_PARM_DESC(numlbs, " number of pseudo devices"); 1310739d643Salex.bluesman.smirnov@gmail.com 132b82b99f1SAlexander Aring static int fakelb_add_one(struct device *dev) 1330739d643Salex.bluesman.smirnov@gmail.com { 134db3f5d0dSAlexander Aring struct fakelb_phy *phy; 1350739d643Salex.bluesman.smirnov@gmail.com int err; 1365a504397SAlexander Aring struct ieee802154_hw *hw; 1370739d643Salex.bluesman.smirnov@gmail.com 138db3f5d0dSAlexander Aring hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops); 1395a504397SAlexander Aring if (!hw) 1400739d643Salex.bluesman.smirnov@gmail.com return -ENOMEM; 1410739d643Salex.bluesman.smirnov@gmail.com 142db3f5d0dSAlexander Aring phy = hw->priv; 143db3f5d0dSAlexander Aring phy->hw = hw; 1440739d643Salex.bluesman.smirnov@gmail.com 1450739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz BPSK 802.15.4-2003 */ 14672f655e4SAlexander Aring hw->phy->supported.channels[0] |= 1; 1470739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz BPSK 802.15.4-2003 */ 14872f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7fe; 1490739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz O-QPSK 802.15.4-2003 */ 15072f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7FFF800; 1510739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz ASK 802.15.4-2006 */ 15272f655e4SAlexander Aring hw->phy->supported.channels[1] |= 1; 1530739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz ASK 802.15.4-2006 */ 15472f655e4SAlexander Aring hw->phy->supported.channels[1] |= 0x7fe; 1550739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz O-QPSK 802.15.4-2006 */ 15672f655e4SAlexander Aring hw->phy->supported.channels[2] |= 1; 1570739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz O-QPSK 802.15.4-2006 */ 15872f655e4SAlexander Aring hw->phy->supported.channels[2] |= 0x7fe; 1590739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz CSS 802.15.4a-2007 */ 16072f655e4SAlexander Aring hw->phy->supported.channels[3] |= 0x3fff; 1610739d643Salex.bluesman.smirnov@gmail.com /* UWB Sub-gigahertz 802.15.4a-2007 */ 16272f655e4SAlexander Aring hw->phy->supported.channels[4] |= 1; 1630739d643Salex.bluesman.smirnov@gmail.com /* UWB Low band 802.15.4a-2007 */ 16472f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0x1e; 1650739d643Salex.bluesman.smirnov@gmail.com /* UWB High band 802.15.4a-2007 */ 16672f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0xffe0; 1670739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz O-QPSK 802.15.4c-2009 */ 16872f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf; 1690739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz MPSK 802.15.4c-2009 */ 17072f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf0; 1710739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz BPSK 802.15.4d-2009 */ 17272f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ff; 1730739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz GFSK 802.15.4d-2009 */ 17472f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ffc00; 1750739d643Salex.bluesman.smirnov@gmail.com 1765a504397SAlexander Aring hw->parent = dev; 1770739d643Salex.bluesman.smirnov@gmail.com 1785a504397SAlexander Aring err = ieee802154_register_hw(hw); 1790739d643Salex.bluesman.smirnov@gmail.com if (err) 1800739d643Salex.bluesman.smirnov@gmail.com goto err_reg; 1810739d643Salex.bluesman.smirnov@gmail.com 182e369dc8fSAlexander Aring spin_lock(&fakelb_phys_lock); 183b82b99f1SAlexander Aring list_add_tail(&phy->list, &fakelb_phys); 184e369dc8fSAlexander Aring spin_unlock(&fakelb_phys_lock); 1850739d643Salex.bluesman.smirnov@gmail.com 1860739d643Salex.bluesman.smirnov@gmail.com return 0; 1870739d643Salex.bluesman.smirnov@gmail.com 1880739d643Salex.bluesman.smirnov@gmail.com err_reg: 189db3f5d0dSAlexander Aring ieee802154_free_hw(phy->hw); 1900739d643Salex.bluesman.smirnov@gmail.com return err; 1910739d643Salex.bluesman.smirnov@gmail.com } 1920739d643Salex.bluesman.smirnov@gmail.com 193db3f5d0dSAlexander Aring static void fakelb_del(struct fakelb_phy *phy) 1940739d643Salex.bluesman.smirnov@gmail.com { 195db3f5d0dSAlexander Aring list_del(&phy->list); 1960739d643Salex.bluesman.smirnov@gmail.com 197db3f5d0dSAlexander Aring ieee802154_unregister_hw(phy->hw); 198db3f5d0dSAlexander Aring ieee802154_free_hw(phy->hw); 1990739d643Salex.bluesman.smirnov@gmail.com } 2000739d643Salex.bluesman.smirnov@gmail.com 201bb1f4606SBill Pemberton static int fakelb_probe(struct platform_device *pdev) 2020739d643Salex.bluesman.smirnov@gmail.com { 203db3f5d0dSAlexander Aring struct fakelb_phy *phy, *tmp; 2040739d643Salex.bluesman.smirnov@gmail.com int err = -ENOMEM; 2050739d643Salex.bluesman.smirnov@gmail.com int i; 2060739d643Salex.bluesman.smirnov@gmail.com 2070739d643Salex.bluesman.smirnov@gmail.com for (i = 0; i < numlbs; i++) { 208b82b99f1SAlexander Aring err = fakelb_add_one(&pdev->dev); 2090739d643Salex.bluesman.smirnov@gmail.com if (err < 0) 2100739d643Salex.bluesman.smirnov@gmail.com goto err_slave; 2110739d643Salex.bluesman.smirnov@gmail.com } 2120739d643Salex.bluesman.smirnov@gmail.com 2130739d643Salex.bluesman.smirnov@gmail.com dev_info(&pdev->dev, "added ieee802154 hardware\n"); 2140739d643Salex.bluesman.smirnov@gmail.com return 0; 2150739d643Salex.bluesman.smirnov@gmail.com 2160739d643Salex.bluesman.smirnov@gmail.com err_slave: 217e369dc8fSAlexander Aring spin_lock(&fakelb_phys_lock); 218b82b99f1SAlexander Aring list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 219db3f5d0dSAlexander Aring fakelb_del(phy); 220e369dc8fSAlexander Aring spin_unlock(&fakelb_phys_lock); 2210739d643Salex.bluesman.smirnov@gmail.com return err; 2220739d643Salex.bluesman.smirnov@gmail.com } 2230739d643Salex.bluesman.smirnov@gmail.com 224bb1f4606SBill Pemberton static int fakelb_remove(struct platform_device *pdev) 2250739d643Salex.bluesman.smirnov@gmail.com { 226db3f5d0dSAlexander Aring struct fakelb_phy *phy, *temp; 2270739d643Salex.bluesman.smirnov@gmail.com 228e369dc8fSAlexander Aring spin_lock(&fakelb_phys_lock); 229b82b99f1SAlexander Aring list_for_each_entry_safe(phy, temp, &fakelb_phys, list) 230db3f5d0dSAlexander Aring fakelb_del(phy); 231e369dc8fSAlexander Aring spin_unlock(&fakelb_phys_lock); 2320739d643Salex.bluesman.smirnov@gmail.com return 0; 2330739d643Salex.bluesman.smirnov@gmail.com } 2340739d643Salex.bluesman.smirnov@gmail.com 2350739d643Salex.bluesman.smirnov@gmail.com static struct platform_device *ieee802154fake_dev; 2360739d643Salex.bluesman.smirnov@gmail.com 2370739d643Salex.bluesman.smirnov@gmail.com static struct platform_driver ieee802154fake_driver = { 2380739d643Salex.bluesman.smirnov@gmail.com .probe = fakelb_probe, 239bb1f4606SBill Pemberton .remove = fakelb_remove, 2400739d643Salex.bluesman.smirnov@gmail.com .driver = { 2410739d643Salex.bluesman.smirnov@gmail.com .name = "ieee802154fakelb", 2420739d643Salex.bluesman.smirnov@gmail.com }, 2430739d643Salex.bluesman.smirnov@gmail.com }; 2440739d643Salex.bluesman.smirnov@gmail.com 2450739d643Salex.bluesman.smirnov@gmail.com static __init int fakelb_init_module(void) 2460739d643Salex.bluesman.smirnov@gmail.com { 2470739d643Salex.bluesman.smirnov@gmail.com ieee802154fake_dev = platform_device_register_simple( 2480739d643Salex.bluesman.smirnov@gmail.com "ieee802154fakelb", -1, NULL, 0); 2490739d643Salex.bluesman.smirnov@gmail.com return platform_driver_register(&ieee802154fake_driver); 2500739d643Salex.bluesman.smirnov@gmail.com } 2510739d643Salex.bluesman.smirnov@gmail.com 2520739d643Salex.bluesman.smirnov@gmail.com static __exit void fake_remove_module(void) 2530739d643Salex.bluesman.smirnov@gmail.com { 2540739d643Salex.bluesman.smirnov@gmail.com platform_driver_unregister(&ieee802154fake_driver); 2550739d643Salex.bluesman.smirnov@gmail.com platform_device_unregister(ieee802154fake_dev); 2560739d643Salex.bluesman.smirnov@gmail.com } 2570739d643Salex.bluesman.smirnov@gmail.com 2580739d643Salex.bluesman.smirnov@gmail.com module_init(fakelb_init_module); 2590739d643Salex.bluesman.smirnov@gmail.com module_exit(fake_remove_module); 2600739d643Salex.bluesman.smirnov@gmail.com MODULE_LICENSE("GPL"); 261