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 300739d643Salex.bluesman.smirnov@gmail.com static int numlbs = 1; 310739d643Salex.bluesman.smirnov@gmail.com 320739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv { 335a504397SAlexander Aring struct ieee802154_hw *hw; 340739d643Salex.bluesman.smirnov@gmail.com 350739d643Salex.bluesman.smirnov@gmail.com struct list_head list; 360739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *fake; 370739d643Salex.bluesman.smirnov@gmail.com 380739d643Salex.bluesman.smirnov@gmail.com spinlock_t lock; 390739d643Salex.bluesman.smirnov@gmail.com bool working; 400739d643Salex.bluesman.smirnov@gmail.com }; 410739d643Salex.bluesman.smirnov@gmail.com 420739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv { 430739d643Salex.bluesman.smirnov@gmail.com struct list_head list; 440739d643Salex.bluesman.smirnov@gmail.com rwlock_t lock; 450739d643Salex.bluesman.smirnov@gmail.com }; 460739d643Salex.bluesman.smirnov@gmail.com 470739d643Salex.bluesman.smirnov@gmail.com static int 485a504397SAlexander Aring fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 490739d643Salex.bluesman.smirnov@gmail.com { 500739d643Salex.bluesman.smirnov@gmail.com BUG_ON(!level); 510739d643Salex.bluesman.smirnov@gmail.com *level = 0xbe; 520739d643Salex.bluesman.smirnov@gmail.com 530739d643Salex.bluesman.smirnov@gmail.com return 0; 540739d643Salex.bluesman.smirnov@gmail.com } 550739d643Salex.bluesman.smirnov@gmail.com 560739d643Salex.bluesman.smirnov@gmail.com static int 57e37d2ec8SAlexander Aring fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 580739d643Salex.bluesman.smirnov@gmail.com { 590739d643Salex.bluesman.smirnov@gmail.com pr_debug("set channel to %d\n", channel); 600739d643Salex.bluesman.smirnov@gmail.com 610739d643Salex.bluesman.smirnov@gmail.com return 0; 620739d643Salex.bluesman.smirnov@gmail.com } 630739d643Salex.bluesman.smirnov@gmail.com 640739d643Salex.bluesman.smirnov@gmail.com static void 650739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(struct fakelb_dev_priv *priv, struct sk_buff *skb) 660739d643Salex.bluesman.smirnov@gmail.com { 670739d643Salex.bluesman.smirnov@gmail.com struct sk_buff *newskb; 680739d643Salex.bluesman.smirnov@gmail.com 690739d643Salex.bluesman.smirnov@gmail.com spin_lock(&priv->lock); 700739d643Salex.bluesman.smirnov@gmail.com if (priv->working) { 710739d643Salex.bluesman.smirnov@gmail.com newskb = pskb_copy(skb, GFP_ATOMIC); 72cb97d9a3SMartin Townsend if (newskb) 735a504397SAlexander Aring ieee802154_rx_irqsafe(priv->hw, newskb, 0xcc); 740739d643Salex.bluesman.smirnov@gmail.com } 750739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 760739d643Salex.bluesman.smirnov@gmail.com } 770739d643Salex.bluesman.smirnov@gmail.com 780739d643Salex.bluesman.smirnov@gmail.com static int 795a504397SAlexander Aring fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 800739d643Salex.bluesman.smirnov@gmail.com { 815a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 820739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *fake = priv->fake; 830739d643Salex.bluesman.smirnov@gmail.com 840739d643Salex.bluesman.smirnov@gmail.com read_lock_bh(&fake->lock); 850739d643Salex.bluesman.smirnov@gmail.com if (priv->list.next == priv->list.prev) { 860739d643Salex.bluesman.smirnov@gmail.com /* we are the only one device */ 870739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(priv, skb); 880739d643Salex.bluesman.smirnov@gmail.com } else { 890739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp; 900739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry(dp, &priv->fake->list, list) { 910739d643Salex.bluesman.smirnov@gmail.com if (dp != priv && 925a504397SAlexander Aring (dp->hw->phy->current_channel == 935a504397SAlexander Aring priv->hw->phy->current_channel)) 940739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(dp, skb); 950739d643Salex.bluesman.smirnov@gmail.com } 960739d643Salex.bluesman.smirnov@gmail.com } 970739d643Salex.bluesman.smirnov@gmail.com read_unlock_bh(&fake->lock); 980739d643Salex.bluesman.smirnov@gmail.com 990739d643Salex.bluesman.smirnov@gmail.com return 0; 1000739d643Salex.bluesman.smirnov@gmail.com } 1010739d643Salex.bluesman.smirnov@gmail.com 1020739d643Salex.bluesman.smirnov@gmail.com static int 1035a504397SAlexander Aring fakelb_hw_start(struct ieee802154_hw *hw) { 1045a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 1050739d643Salex.bluesman.smirnov@gmail.com int ret = 0; 1060739d643Salex.bluesman.smirnov@gmail.com 1070739d643Salex.bluesman.smirnov@gmail.com spin_lock(&priv->lock); 1080739d643Salex.bluesman.smirnov@gmail.com if (priv->working) 1090739d643Salex.bluesman.smirnov@gmail.com ret = -EBUSY; 1100739d643Salex.bluesman.smirnov@gmail.com else 1110739d643Salex.bluesman.smirnov@gmail.com priv->working = 1; 1120739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 1130739d643Salex.bluesman.smirnov@gmail.com 1140739d643Salex.bluesman.smirnov@gmail.com return ret; 1150739d643Salex.bluesman.smirnov@gmail.com } 1160739d643Salex.bluesman.smirnov@gmail.com 1170739d643Salex.bluesman.smirnov@gmail.com static void 1185a504397SAlexander Aring fakelb_hw_stop(struct ieee802154_hw *hw) { 1195a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 1200739d643Salex.bluesman.smirnov@gmail.com 1210739d643Salex.bluesman.smirnov@gmail.com spin_lock(&priv->lock); 1220739d643Salex.bluesman.smirnov@gmail.com priv->working = 0; 1230739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 1240739d643Salex.bluesman.smirnov@gmail.com } 1250739d643Salex.bluesman.smirnov@gmail.com 12616301861SAlexander Aring static const struct ieee802154_ops fakelb_ops = { 1270739d643Salex.bluesman.smirnov@gmail.com .owner = THIS_MODULE, 128ed0a5dceSAlexander Aring .xmit_sync = fakelb_hw_xmit, 1290739d643Salex.bluesman.smirnov@gmail.com .ed = fakelb_hw_ed, 1300739d643Salex.bluesman.smirnov@gmail.com .set_channel = fakelb_hw_channel, 1310739d643Salex.bluesman.smirnov@gmail.com .start = fakelb_hw_start, 1320739d643Salex.bluesman.smirnov@gmail.com .stop = fakelb_hw_stop, 1330739d643Salex.bluesman.smirnov@gmail.com }; 1340739d643Salex.bluesman.smirnov@gmail.com 1350739d643Salex.bluesman.smirnov@gmail.com /* Number of dummy devices to be set up by this module. */ 1360739d643Salex.bluesman.smirnov@gmail.com module_param(numlbs, int, 0); 1370739d643Salex.bluesman.smirnov@gmail.com MODULE_PARM_DESC(numlbs, " number of pseudo devices"); 1380739d643Salex.bluesman.smirnov@gmail.com 1390739d643Salex.bluesman.smirnov@gmail.com static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake) 1400739d643Salex.bluesman.smirnov@gmail.com { 1410739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *priv; 1420739d643Salex.bluesman.smirnov@gmail.com int err; 1435a504397SAlexander Aring struct ieee802154_hw *hw; 1440739d643Salex.bluesman.smirnov@gmail.com 1455a504397SAlexander Aring hw = ieee802154_alloc_hw(sizeof(*priv), &fakelb_ops); 1465a504397SAlexander Aring if (!hw) 1470739d643Salex.bluesman.smirnov@gmail.com return -ENOMEM; 1480739d643Salex.bluesman.smirnov@gmail.com 1495a504397SAlexander Aring priv = hw->priv; 1505a504397SAlexander Aring priv->hw = hw; 1510739d643Salex.bluesman.smirnov@gmail.com 1520739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz BPSK 802.15.4-2003 */ 15372f655e4SAlexander Aring hw->phy->supported.channels[0] |= 1; 1540739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz BPSK 802.15.4-2003 */ 15572f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7fe; 1560739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz O-QPSK 802.15.4-2003 */ 15772f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7FFF800; 1580739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz ASK 802.15.4-2006 */ 15972f655e4SAlexander Aring hw->phy->supported.channels[1] |= 1; 1600739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz ASK 802.15.4-2006 */ 16172f655e4SAlexander Aring hw->phy->supported.channels[1] |= 0x7fe; 1620739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz O-QPSK 802.15.4-2006 */ 16372f655e4SAlexander Aring hw->phy->supported.channels[2] |= 1; 1640739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz O-QPSK 802.15.4-2006 */ 16572f655e4SAlexander Aring hw->phy->supported.channels[2] |= 0x7fe; 1660739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz CSS 802.15.4a-2007 */ 16772f655e4SAlexander Aring hw->phy->supported.channels[3] |= 0x3fff; 1680739d643Salex.bluesman.smirnov@gmail.com /* UWB Sub-gigahertz 802.15.4a-2007 */ 16972f655e4SAlexander Aring hw->phy->supported.channels[4] |= 1; 1700739d643Salex.bluesman.smirnov@gmail.com /* UWB Low band 802.15.4a-2007 */ 17172f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0x1e; 1720739d643Salex.bluesman.smirnov@gmail.com /* UWB High band 802.15.4a-2007 */ 17372f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0xffe0; 1740739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz O-QPSK 802.15.4c-2009 */ 17572f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf; 1760739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz MPSK 802.15.4c-2009 */ 17772f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf0; 1780739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz BPSK 802.15.4d-2009 */ 17972f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ff; 1800739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz GFSK 802.15.4d-2009 */ 18172f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ffc00; 1820739d643Salex.bluesman.smirnov@gmail.com 1830739d643Salex.bluesman.smirnov@gmail.com INIT_LIST_HEAD(&priv->list); 1840739d643Salex.bluesman.smirnov@gmail.com priv->fake = fake; 1850739d643Salex.bluesman.smirnov@gmail.com 1860739d643Salex.bluesman.smirnov@gmail.com spin_lock_init(&priv->lock); 1870739d643Salex.bluesman.smirnov@gmail.com 1885a504397SAlexander Aring hw->parent = dev; 1890739d643Salex.bluesman.smirnov@gmail.com 1905a504397SAlexander Aring err = ieee802154_register_hw(hw); 1910739d643Salex.bluesman.smirnov@gmail.com if (err) 1920739d643Salex.bluesman.smirnov@gmail.com goto err_reg; 1930739d643Salex.bluesman.smirnov@gmail.com 1940739d643Salex.bluesman.smirnov@gmail.com write_lock_bh(&fake->lock); 1950739d643Salex.bluesman.smirnov@gmail.com list_add_tail(&priv->list, &fake->list); 1960739d643Salex.bluesman.smirnov@gmail.com write_unlock_bh(&fake->lock); 1970739d643Salex.bluesman.smirnov@gmail.com 1980739d643Salex.bluesman.smirnov@gmail.com return 0; 1990739d643Salex.bluesman.smirnov@gmail.com 2000739d643Salex.bluesman.smirnov@gmail.com err_reg: 2015a504397SAlexander Aring ieee802154_free_hw(priv->hw); 2020739d643Salex.bluesman.smirnov@gmail.com return err; 2030739d643Salex.bluesman.smirnov@gmail.com } 2040739d643Salex.bluesman.smirnov@gmail.com 2050739d643Salex.bluesman.smirnov@gmail.com static void fakelb_del(struct fakelb_dev_priv *priv) 2060739d643Salex.bluesman.smirnov@gmail.com { 2070739d643Salex.bluesman.smirnov@gmail.com write_lock_bh(&priv->fake->lock); 2080739d643Salex.bluesman.smirnov@gmail.com list_del(&priv->list); 2090739d643Salex.bluesman.smirnov@gmail.com write_unlock_bh(&priv->fake->lock); 2100739d643Salex.bluesman.smirnov@gmail.com 2115a504397SAlexander Aring ieee802154_unregister_hw(priv->hw); 2125a504397SAlexander Aring ieee802154_free_hw(priv->hw); 2130739d643Salex.bluesman.smirnov@gmail.com } 2140739d643Salex.bluesman.smirnov@gmail.com 215bb1f4606SBill Pemberton static int fakelb_probe(struct platform_device *pdev) 2160739d643Salex.bluesman.smirnov@gmail.com { 2170739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *priv; 2180739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp; 2190739d643Salex.bluesman.smirnov@gmail.com int err = -ENOMEM; 2200739d643Salex.bluesman.smirnov@gmail.com int i; 2210739d643Salex.bluesman.smirnov@gmail.com 22212b5c38fSHimangi Saraogi priv = devm_kzalloc(&pdev->dev, sizeof(struct fakelb_priv), 22312b5c38fSHimangi Saraogi GFP_KERNEL); 2240739d643Salex.bluesman.smirnov@gmail.com if (!priv) 2250739d643Salex.bluesman.smirnov@gmail.com goto err_alloc; 2260739d643Salex.bluesman.smirnov@gmail.com 2270739d643Salex.bluesman.smirnov@gmail.com INIT_LIST_HEAD(&priv->list); 2280739d643Salex.bluesman.smirnov@gmail.com rwlock_init(&priv->lock); 2290739d643Salex.bluesman.smirnov@gmail.com 2300739d643Salex.bluesman.smirnov@gmail.com for (i = 0; i < numlbs; i++) { 2310739d643Salex.bluesman.smirnov@gmail.com err = fakelb_add_one(&pdev->dev, priv); 2320739d643Salex.bluesman.smirnov@gmail.com if (err < 0) 2330739d643Salex.bluesman.smirnov@gmail.com goto err_slave; 2340739d643Salex.bluesman.smirnov@gmail.com } 2350739d643Salex.bluesman.smirnov@gmail.com 2360739d643Salex.bluesman.smirnov@gmail.com platform_set_drvdata(pdev, priv); 2370739d643Salex.bluesman.smirnov@gmail.com dev_info(&pdev->dev, "added ieee802154 hardware\n"); 2380739d643Salex.bluesman.smirnov@gmail.com return 0; 2390739d643Salex.bluesman.smirnov@gmail.com 2400739d643Salex.bluesman.smirnov@gmail.com err_slave: 2410739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry(dp, &priv->list, list) 2420739d643Salex.bluesman.smirnov@gmail.com fakelb_del(dp); 2430739d643Salex.bluesman.smirnov@gmail.com err_alloc: 2440739d643Salex.bluesman.smirnov@gmail.com return err; 2450739d643Salex.bluesman.smirnov@gmail.com } 2460739d643Salex.bluesman.smirnov@gmail.com 247bb1f4606SBill Pemberton static int fakelb_remove(struct platform_device *pdev) 2480739d643Salex.bluesman.smirnov@gmail.com { 2490739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *priv = platform_get_drvdata(pdev); 2500739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp, *temp; 2510739d643Salex.bluesman.smirnov@gmail.com 2520739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry_safe(dp, temp, &priv->list, list) 2530739d643Salex.bluesman.smirnov@gmail.com fakelb_del(dp); 2540739d643Salex.bluesman.smirnov@gmail.com 2550739d643Salex.bluesman.smirnov@gmail.com return 0; 2560739d643Salex.bluesman.smirnov@gmail.com } 2570739d643Salex.bluesman.smirnov@gmail.com 2580739d643Salex.bluesman.smirnov@gmail.com static struct platform_device *ieee802154fake_dev; 2590739d643Salex.bluesman.smirnov@gmail.com 2600739d643Salex.bluesman.smirnov@gmail.com static struct platform_driver ieee802154fake_driver = { 2610739d643Salex.bluesman.smirnov@gmail.com .probe = fakelb_probe, 262bb1f4606SBill Pemberton .remove = fakelb_remove, 2630739d643Salex.bluesman.smirnov@gmail.com .driver = { 2640739d643Salex.bluesman.smirnov@gmail.com .name = "ieee802154fakelb", 2650739d643Salex.bluesman.smirnov@gmail.com }, 2660739d643Salex.bluesman.smirnov@gmail.com }; 2670739d643Salex.bluesman.smirnov@gmail.com 2680739d643Salex.bluesman.smirnov@gmail.com static __init int fakelb_init_module(void) 2690739d643Salex.bluesman.smirnov@gmail.com { 2700739d643Salex.bluesman.smirnov@gmail.com ieee802154fake_dev = platform_device_register_simple( 2710739d643Salex.bluesman.smirnov@gmail.com "ieee802154fakelb", -1, NULL, 0); 2720739d643Salex.bluesman.smirnov@gmail.com return platform_driver_register(&ieee802154fake_driver); 2730739d643Salex.bluesman.smirnov@gmail.com } 2740739d643Salex.bluesman.smirnov@gmail.com 2750739d643Salex.bluesman.smirnov@gmail.com static __exit void fake_remove_module(void) 2760739d643Salex.bluesman.smirnov@gmail.com { 2770739d643Salex.bluesman.smirnov@gmail.com platform_driver_unregister(&ieee802154fake_driver); 2780739d643Salex.bluesman.smirnov@gmail.com platform_device_unregister(ieee802154fake_dev); 2790739d643Salex.bluesman.smirnov@gmail.com } 2800739d643Salex.bluesman.smirnov@gmail.com 2810739d643Salex.bluesman.smirnov@gmail.com module_init(fakelb_init_module); 2820739d643Salex.bluesman.smirnov@gmail.com module_exit(fake_remove_module); 2830739d643Salex.bluesman.smirnov@gmail.com MODULE_LICENSE("GPL"); 284