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 might_sleep(); 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 { 600739d643Salex.bluesman.smirnov@gmail.com pr_debug("set channel to %d\n", channel); 610739d643Salex.bluesman.smirnov@gmail.com 620739d643Salex.bluesman.smirnov@gmail.com might_sleep(); 635a504397SAlexander Aring hw->phy->current_page = page; 645a504397SAlexander Aring hw->phy->current_channel = channel; 650739d643Salex.bluesman.smirnov@gmail.com 660739d643Salex.bluesman.smirnov@gmail.com return 0; 670739d643Salex.bluesman.smirnov@gmail.com } 680739d643Salex.bluesman.smirnov@gmail.com 690739d643Salex.bluesman.smirnov@gmail.com static void 700739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(struct fakelb_dev_priv *priv, 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 spin_lock(&priv->lock); 750739d643Salex.bluesman.smirnov@gmail.com if (priv->working) { 760739d643Salex.bluesman.smirnov@gmail.com newskb = pskb_copy(skb, GFP_ATOMIC); 775a504397SAlexander Aring ieee802154_rx_irqsafe(priv->hw, newskb, 0xcc); 780739d643Salex.bluesman.smirnov@gmail.com } 790739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 800739d643Salex.bluesman.smirnov@gmail.com } 810739d643Salex.bluesman.smirnov@gmail.com 820739d643Salex.bluesman.smirnov@gmail.com static int 835a504397SAlexander Aring fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 840739d643Salex.bluesman.smirnov@gmail.com { 855a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 860739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *fake = priv->fake; 870739d643Salex.bluesman.smirnov@gmail.com 880739d643Salex.bluesman.smirnov@gmail.com might_sleep(); 890739d643Salex.bluesman.smirnov@gmail.com 900739d643Salex.bluesman.smirnov@gmail.com read_lock_bh(&fake->lock); 910739d643Salex.bluesman.smirnov@gmail.com if (priv->list.next == priv->list.prev) { 920739d643Salex.bluesman.smirnov@gmail.com /* we are the only one device */ 930739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(priv, skb); 940739d643Salex.bluesman.smirnov@gmail.com } else { 950739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp; 960739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry(dp, &priv->fake->list, list) { 970739d643Salex.bluesman.smirnov@gmail.com if (dp != priv && 985a504397SAlexander Aring (dp->hw->phy->current_channel == 995a504397SAlexander Aring priv->hw->phy->current_channel)) 1000739d643Salex.bluesman.smirnov@gmail.com fakelb_hw_deliver(dp, skb); 1010739d643Salex.bluesman.smirnov@gmail.com } 1020739d643Salex.bluesman.smirnov@gmail.com } 1030739d643Salex.bluesman.smirnov@gmail.com read_unlock_bh(&fake->lock); 1040739d643Salex.bluesman.smirnov@gmail.com 1050739d643Salex.bluesman.smirnov@gmail.com return 0; 1060739d643Salex.bluesman.smirnov@gmail.com } 1070739d643Salex.bluesman.smirnov@gmail.com 1080739d643Salex.bluesman.smirnov@gmail.com static int 1095a504397SAlexander Aring fakelb_hw_start(struct ieee802154_hw *hw) { 1105a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 1110739d643Salex.bluesman.smirnov@gmail.com int ret = 0; 1120739d643Salex.bluesman.smirnov@gmail.com 1130739d643Salex.bluesman.smirnov@gmail.com spin_lock(&priv->lock); 1140739d643Salex.bluesman.smirnov@gmail.com if (priv->working) 1150739d643Salex.bluesman.smirnov@gmail.com ret = -EBUSY; 1160739d643Salex.bluesman.smirnov@gmail.com else 1170739d643Salex.bluesman.smirnov@gmail.com priv->working = 1; 1180739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 1190739d643Salex.bluesman.smirnov@gmail.com 1200739d643Salex.bluesman.smirnov@gmail.com return ret; 1210739d643Salex.bluesman.smirnov@gmail.com } 1220739d643Salex.bluesman.smirnov@gmail.com 1230739d643Salex.bluesman.smirnov@gmail.com static void 1245a504397SAlexander Aring fakelb_hw_stop(struct ieee802154_hw *hw) { 1255a504397SAlexander Aring struct fakelb_dev_priv *priv = hw->priv; 1260739d643Salex.bluesman.smirnov@gmail.com 1270739d643Salex.bluesman.smirnov@gmail.com spin_lock(&priv->lock); 1280739d643Salex.bluesman.smirnov@gmail.com priv->working = 0; 1290739d643Salex.bluesman.smirnov@gmail.com spin_unlock(&priv->lock); 1300739d643Salex.bluesman.smirnov@gmail.com } 1310739d643Salex.bluesman.smirnov@gmail.com 13216301861SAlexander Aring static const struct ieee802154_ops fakelb_ops = { 1330739d643Salex.bluesman.smirnov@gmail.com .owner = THIS_MODULE, 134ed0a5dceSAlexander Aring .xmit_sync = fakelb_hw_xmit, 1350739d643Salex.bluesman.smirnov@gmail.com .ed = fakelb_hw_ed, 1360739d643Salex.bluesman.smirnov@gmail.com .set_channel = fakelb_hw_channel, 1370739d643Salex.bluesman.smirnov@gmail.com .start = fakelb_hw_start, 1380739d643Salex.bluesman.smirnov@gmail.com .stop = fakelb_hw_stop, 1390739d643Salex.bluesman.smirnov@gmail.com }; 1400739d643Salex.bluesman.smirnov@gmail.com 1410739d643Salex.bluesman.smirnov@gmail.com /* Number of dummy devices to be set up by this module. */ 1420739d643Salex.bluesman.smirnov@gmail.com module_param(numlbs, int, 0); 1430739d643Salex.bluesman.smirnov@gmail.com MODULE_PARM_DESC(numlbs, " number of pseudo devices"); 1440739d643Salex.bluesman.smirnov@gmail.com 1450739d643Salex.bluesman.smirnov@gmail.com static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake) 1460739d643Salex.bluesman.smirnov@gmail.com { 1470739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *priv; 1480739d643Salex.bluesman.smirnov@gmail.com int err; 1495a504397SAlexander Aring struct ieee802154_hw *hw; 1500739d643Salex.bluesman.smirnov@gmail.com 1515a504397SAlexander Aring hw = ieee802154_alloc_hw(sizeof(*priv), &fakelb_ops); 1525a504397SAlexander Aring if (!hw) 1530739d643Salex.bluesman.smirnov@gmail.com return -ENOMEM; 1540739d643Salex.bluesman.smirnov@gmail.com 1555a504397SAlexander Aring priv = hw->priv; 1565a504397SAlexander Aring priv->hw = hw; 1570739d643Salex.bluesman.smirnov@gmail.com 1580739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz BPSK 802.15.4-2003 */ 1595a504397SAlexander Aring hw->phy->channels_supported[0] |= 1; 1600739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz BPSK 802.15.4-2003 */ 1615a504397SAlexander Aring hw->phy->channels_supported[0] |= 0x7fe; 1620739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz O-QPSK 802.15.4-2003 */ 1635a504397SAlexander Aring hw->phy->channels_supported[0] |= 0x7FFF800; 1640739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz ASK 802.15.4-2006 */ 1655a504397SAlexander Aring hw->phy->channels_supported[1] |= 1; 1660739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz ASK 802.15.4-2006 */ 1675a504397SAlexander Aring hw->phy->channels_supported[1] |= 0x7fe; 1680739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz O-QPSK 802.15.4-2006 */ 1695a504397SAlexander Aring hw->phy->channels_supported[2] |= 1; 1700739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz O-QPSK 802.15.4-2006 */ 1715a504397SAlexander Aring hw->phy->channels_supported[2] |= 0x7fe; 1720739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz CSS 802.15.4a-2007 */ 1735a504397SAlexander Aring hw->phy->channels_supported[3] |= 0x3fff; 1740739d643Salex.bluesman.smirnov@gmail.com /* UWB Sub-gigahertz 802.15.4a-2007 */ 1755a504397SAlexander Aring hw->phy->channels_supported[4] |= 1; 1760739d643Salex.bluesman.smirnov@gmail.com /* UWB Low band 802.15.4a-2007 */ 1775a504397SAlexander Aring hw->phy->channels_supported[4] |= 0x1e; 1780739d643Salex.bluesman.smirnov@gmail.com /* UWB High band 802.15.4a-2007 */ 1795a504397SAlexander Aring hw->phy->channels_supported[4] |= 0xffe0; 1800739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz O-QPSK 802.15.4c-2009 */ 1815a504397SAlexander Aring hw->phy->channels_supported[5] |= 0xf; 1820739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz MPSK 802.15.4c-2009 */ 1835a504397SAlexander Aring hw->phy->channels_supported[5] |= 0xf0; 1840739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz BPSK 802.15.4d-2009 */ 1855a504397SAlexander Aring hw->phy->channels_supported[6] |= 0x3ff; 1860739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz GFSK 802.15.4d-2009 */ 1875a504397SAlexander Aring hw->phy->channels_supported[6] |= 0x3ffc00; 1880739d643Salex.bluesman.smirnov@gmail.com 1890739d643Salex.bluesman.smirnov@gmail.com INIT_LIST_HEAD(&priv->list); 1900739d643Salex.bluesman.smirnov@gmail.com priv->fake = fake; 1910739d643Salex.bluesman.smirnov@gmail.com 1920739d643Salex.bluesman.smirnov@gmail.com spin_lock_init(&priv->lock); 1930739d643Salex.bluesman.smirnov@gmail.com 1945a504397SAlexander Aring hw->parent = dev; 1950739d643Salex.bluesman.smirnov@gmail.com 1965a504397SAlexander Aring err = ieee802154_register_hw(hw); 1970739d643Salex.bluesman.smirnov@gmail.com if (err) 1980739d643Salex.bluesman.smirnov@gmail.com goto err_reg; 1990739d643Salex.bluesman.smirnov@gmail.com 2000739d643Salex.bluesman.smirnov@gmail.com write_lock_bh(&fake->lock); 2010739d643Salex.bluesman.smirnov@gmail.com list_add_tail(&priv->list, &fake->list); 2020739d643Salex.bluesman.smirnov@gmail.com write_unlock_bh(&fake->lock); 2030739d643Salex.bluesman.smirnov@gmail.com 2040739d643Salex.bluesman.smirnov@gmail.com return 0; 2050739d643Salex.bluesman.smirnov@gmail.com 2060739d643Salex.bluesman.smirnov@gmail.com err_reg: 2075a504397SAlexander Aring ieee802154_free_hw(priv->hw); 2080739d643Salex.bluesman.smirnov@gmail.com return err; 2090739d643Salex.bluesman.smirnov@gmail.com } 2100739d643Salex.bluesman.smirnov@gmail.com 2110739d643Salex.bluesman.smirnov@gmail.com static void fakelb_del(struct fakelb_dev_priv *priv) 2120739d643Salex.bluesman.smirnov@gmail.com { 2130739d643Salex.bluesman.smirnov@gmail.com write_lock_bh(&priv->fake->lock); 2140739d643Salex.bluesman.smirnov@gmail.com list_del(&priv->list); 2150739d643Salex.bluesman.smirnov@gmail.com write_unlock_bh(&priv->fake->lock); 2160739d643Salex.bluesman.smirnov@gmail.com 2175a504397SAlexander Aring ieee802154_unregister_hw(priv->hw); 2185a504397SAlexander Aring ieee802154_free_hw(priv->hw); 2190739d643Salex.bluesman.smirnov@gmail.com } 2200739d643Salex.bluesman.smirnov@gmail.com 221bb1f4606SBill Pemberton static int fakelb_probe(struct platform_device *pdev) 2220739d643Salex.bluesman.smirnov@gmail.com { 2230739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *priv; 2240739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp; 2250739d643Salex.bluesman.smirnov@gmail.com int err = -ENOMEM; 2260739d643Salex.bluesman.smirnov@gmail.com int i; 2270739d643Salex.bluesman.smirnov@gmail.com 22812b5c38fSHimangi Saraogi priv = devm_kzalloc(&pdev->dev, sizeof(struct fakelb_priv), 22912b5c38fSHimangi Saraogi GFP_KERNEL); 2300739d643Salex.bluesman.smirnov@gmail.com if (!priv) 2310739d643Salex.bluesman.smirnov@gmail.com goto err_alloc; 2320739d643Salex.bluesman.smirnov@gmail.com 2330739d643Salex.bluesman.smirnov@gmail.com INIT_LIST_HEAD(&priv->list); 2340739d643Salex.bluesman.smirnov@gmail.com rwlock_init(&priv->lock); 2350739d643Salex.bluesman.smirnov@gmail.com 2360739d643Salex.bluesman.smirnov@gmail.com for (i = 0; i < numlbs; i++) { 2370739d643Salex.bluesman.smirnov@gmail.com err = fakelb_add_one(&pdev->dev, priv); 2380739d643Salex.bluesman.smirnov@gmail.com if (err < 0) 2390739d643Salex.bluesman.smirnov@gmail.com goto err_slave; 2400739d643Salex.bluesman.smirnov@gmail.com } 2410739d643Salex.bluesman.smirnov@gmail.com 2420739d643Salex.bluesman.smirnov@gmail.com platform_set_drvdata(pdev, priv); 2430739d643Salex.bluesman.smirnov@gmail.com dev_info(&pdev->dev, "added ieee802154 hardware\n"); 2440739d643Salex.bluesman.smirnov@gmail.com return 0; 2450739d643Salex.bluesman.smirnov@gmail.com 2460739d643Salex.bluesman.smirnov@gmail.com err_slave: 2470739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry(dp, &priv->list, list) 2480739d643Salex.bluesman.smirnov@gmail.com fakelb_del(dp); 2490739d643Salex.bluesman.smirnov@gmail.com err_alloc: 2500739d643Salex.bluesman.smirnov@gmail.com return err; 2510739d643Salex.bluesman.smirnov@gmail.com } 2520739d643Salex.bluesman.smirnov@gmail.com 253bb1f4606SBill Pemberton static int fakelb_remove(struct platform_device *pdev) 2540739d643Salex.bluesman.smirnov@gmail.com { 2550739d643Salex.bluesman.smirnov@gmail.com struct fakelb_priv *priv = platform_get_drvdata(pdev); 2560739d643Salex.bluesman.smirnov@gmail.com struct fakelb_dev_priv *dp, *temp; 2570739d643Salex.bluesman.smirnov@gmail.com 2580739d643Salex.bluesman.smirnov@gmail.com list_for_each_entry_safe(dp, temp, &priv->list, list) 2590739d643Salex.bluesman.smirnov@gmail.com fakelb_del(dp); 2600739d643Salex.bluesman.smirnov@gmail.com 2610739d643Salex.bluesman.smirnov@gmail.com return 0; 2620739d643Salex.bluesman.smirnov@gmail.com } 2630739d643Salex.bluesman.smirnov@gmail.com 2640739d643Salex.bluesman.smirnov@gmail.com static struct platform_device *ieee802154fake_dev; 2650739d643Salex.bluesman.smirnov@gmail.com 2660739d643Salex.bluesman.smirnov@gmail.com static struct platform_driver ieee802154fake_driver = { 2670739d643Salex.bluesman.smirnov@gmail.com .probe = fakelb_probe, 268bb1f4606SBill Pemberton .remove = fakelb_remove, 2690739d643Salex.bluesman.smirnov@gmail.com .driver = { 2700739d643Salex.bluesman.smirnov@gmail.com .name = "ieee802154fakelb", 2710739d643Salex.bluesman.smirnov@gmail.com .owner = THIS_MODULE, 2720739d643Salex.bluesman.smirnov@gmail.com }, 2730739d643Salex.bluesman.smirnov@gmail.com }; 2740739d643Salex.bluesman.smirnov@gmail.com 2750739d643Salex.bluesman.smirnov@gmail.com static __init int fakelb_init_module(void) 2760739d643Salex.bluesman.smirnov@gmail.com { 2770739d643Salex.bluesman.smirnov@gmail.com ieee802154fake_dev = platform_device_register_simple( 2780739d643Salex.bluesman.smirnov@gmail.com "ieee802154fakelb", -1, NULL, 0); 2790739d643Salex.bluesman.smirnov@gmail.com return platform_driver_register(&ieee802154fake_driver); 2800739d643Salex.bluesman.smirnov@gmail.com } 2810739d643Salex.bluesman.smirnov@gmail.com 2820739d643Salex.bluesman.smirnov@gmail.com static __exit void fake_remove_module(void) 2830739d643Salex.bluesman.smirnov@gmail.com { 2840739d643Salex.bluesman.smirnov@gmail.com platform_driver_unregister(&ieee802154fake_driver); 2850739d643Salex.bluesman.smirnov@gmail.com platform_device_unregister(ieee802154fake_dev); 2860739d643Salex.bluesman.smirnov@gmail.com } 2870739d643Salex.bluesman.smirnov@gmail.com 2880739d643Salex.bluesman.smirnov@gmail.com module_init(fakelb_init_module); 2890739d643Salex.bluesman.smirnov@gmail.com module_exit(fake_remove_module); 2900739d643Salex.bluesman.smirnov@gmail.com MODULE_LICENSE("GPL"); 291