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); 33bdca1fd9SAlexander Aring static DEFINE_MUTEX(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 44789a99ecSAlexander Aring bool suspended; 45789a99ecSAlexander Aring 460739d643Salex.bluesman.smirnov@gmail.com struct list_head list; 47e369dc8fSAlexander Aring struct list_head list_ifup; 480739d643Salex.bluesman.smirnov@gmail.com }; 490739d643Salex.bluesman.smirnov@gmail.com 5039572ab3SAlexander Aring static int fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 510739d643Salex.bluesman.smirnov@gmail.com { 528f2fbc6cSStefan Schmidt WARN_ON(!level); 530739d643Salex.bluesman.smirnov@gmail.com *level = 0xbe; 540739d643Salex.bluesman.smirnov@gmail.com 550739d643Salex.bluesman.smirnov@gmail.com return 0; 560739d643Salex.bluesman.smirnov@gmail.com } 570739d643Salex.bluesman.smirnov@gmail.com 5839572ab3SAlexander Aring static int 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 6939572ab3SAlexander Aring static int fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 700739d643Salex.bluesman.smirnov@gmail.com { 7139572ab3SAlexander Aring struct fakelb_phy *current_phy = hw->priv, *phy; 72db3f5d0dSAlexander Aring 73e369dc8fSAlexander Aring read_lock_bh(&fakelb_ifup_phys_lock); 74789a99ecSAlexander Aring WARN_ON(current_phy->suspended); 75e369dc8fSAlexander Aring list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) { 769f8b97f8SAlexander Aring if (current_phy == phy) 779f8b97f8SAlexander Aring continue; 789f8b97f8SAlexander Aring 7912da8d97SAlexander Aring if (current_phy->page == phy->page && 8098be165bSAlexander Aring current_phy->channel == phy->channel) { 8198be165bSAlexander Aring struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC); 8298be165bSAlexander Aring 8398be165bSAlexander Aring if (newskb) 8498be165bSAlexander Aring ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc); 8598be165bSAlexander Aring } 860739d643Salex.bluesman.smirnov@gmail.com } 87e369dc8fSAlexander Aring read_unlock_bh(&fakelb_ifup_phys_lock); 880739d643Salex.bluesman.smirnov@gmail.com 89a09c07a8SAlexander Aring ieee802154_xmit_complete(hw, skb, false); 900739d643Salex.bluesman.smirnov@gmail.com return 0; 910739d643Salex.bluesman.smirnov@gmail.com } 920739d643Salex.bluesman.smirnov@gmail.com 9339572ab3SAlexander Aring static int fakelb_hw_start(struct ieee802154_hw *hw) 9439572ab3SAlexander Aring { 95db3f5d0dSAlexander Aring struct fakelb_phy *phy = hw->priv; 960739d643Salex.bluesman.smirnov@gmail.com 97e369dc8fSAlexander Aring write_lock_bh(&fakelb_ifup_phys_lock); 98789a99ecSAlexander Aring phy->suspended = false; 99e369dc8fSAlexander Aring list_add(&phy->list_ifup, &fakelb_ifup_phys); 100e369dc8fSAlexander Aring write_unlock_bh(&fakelb_ifup_phys_lock); 1010739d643Salex.bluesman.smirnov@gmail.com 102e369dc8fSAlexander Aring return 0; 1030739d643Salex.bluesman.smirnov@gmail.com } 1040739d643Salex.bluesman.smirnov@gmail.com 10539572ab3SAlexander Aring static void fakelb_hw_stop(struct ieee802154_hw *hw) 10639572ab3SAlexander Aring { 107db3f5d0dSAlexander Aring struct fakelb_phy *phy = hw->priv; 1080739d643Salex.bluesman.smirnov@gmail.com 109e369dc8fSAlexander Aring write_lock_bh(&fakelb_ifup_phys_lock); 110789a99ecSAlexander Aring phy->suspended = true; 111e369dc8fSAlexander Aring list_del(&phy->list_ifup); 112e369dc8fSAlexander Aring write_unlock_bh(&fakelb_ifup_phys_lock); 1130739d643Salex.bluesman.smirnov@gmail.com } 1140739d643Salex.bluesman.smirnov@gmail.com 1157c2b9bffSAlexander Aring static int 1167c2b9bffSAlexander Aring fakelb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) 1177c2b9bffSAlexander Aring { 1187c2b9bffSAlexander Aring return 0; 1197c2b9bffSAlexander Aring } 1207c2b9bffSAlexander Aring 12116301861SAlexander Aring static const struct ieee802154_ops fakelb_ops = { 1220739d643Salex.bluesman.smirnov@gmail.com .owner = THIS_MODULE, 123a09c07a8SAlexander Aring .xmit_async = fakelb_hw_xmit, 1240739d643Salex.bluesman.smirnov@gmail.com .ed = fakelb_hw_ed, 1250739d643Salex.bluesman.smirnov@gmail.com .set_channel = fakelb_hw_channel, 1260739d643Salex.bluesman.smirnov@gmail.com .start = fakelb_hw_start, 1270739d643Salex.bluesman.smirnov@gmail.com .stop = fakelb_hw_stop, 1287c2b9bffSAlexander Aring .set_promiscuous_mode = fakelb_set_promiscuous_mode, 1290739d643Salex.bluesman.smirnov@gmail.com }; 1300739d643Salex.bluesman.smirnov@gmail.com 1310739d643Salex.bluesman.smirnov@gmail.com /* Number of dummy devices to be set up by this module. */ 1320739d643Salex.bluesman.smirnov@gmail.com module_param(numlbs, int, 0); 1330739d643Salex.bluesman.smirnov@gmail.com MODULE_PARM_DESC(numlbs, " number of pseudo devices"); 1340739d643Salex.bluesman.smirnov@gmail.com 135b82b99f1SAlexander Aring static int fakelb_add_one(struct device *dev) 1360739d643Salex.bluesman.smirnov@gmail.com { 13739572ab3SAlexander Aring struct ieee802154_hw *hw; 138db3f5d0dSAlexander Aring struct fakelb_phy *phy; 1390739d643Salex.bluesman.smirnov@gmail.com int err; 1400739d643Salex.bluesman.smirnov@gmail.com 141db3f5d0dSAlexander Aring hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops); 1425a504397SAlexander Aring if (!hw) 1430739d643Salex.bluesman.smirnov@gmail.com return -ENOMEM; 1440739d643Salex.bluesman.smirnov@gmail.com 145db3f5d0dSAlexander Aring phy = hw->priv; 146db3f5d0dSAlexander Aring phy->hw = hw; 1470739d643Salex.bluesman.smirnov@gmail.com 1480739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz BPSK 802.15.4-2003 */ 14972f655e4SAlexander Aring hw->phy->supported.channels[0] |= 1; 1500739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz BPSK 802.15.4-2003 */ 15172f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7fe; 1520739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz O-QPSK 802.15.4-2003 */ 15372f655e4SAlexander Aring hw->phy->supported.channels[0] |= 0x7FFF800; 1540739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz ASK 802.15.4-2006 */ 15572f655e4SAlexander Aring hw->phy->supported.channels[1] |= 1; 1560739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz ASK 802.15.4-2006 */ 15772f655e4SAlexander Aring hw->phy->supported.channels[1] |= 0x7fe; 1580739d643Salex.bluesman.smirnov@gmail.com /* 868 MHz O-QPSK 802.15.4-2006 */ 15972f655e4SAlexander Aring hw->phy->supported.channels[2] |= 1; 1600739d643Salex.bluesman.smirnov@gmail.com /* 915 MHz O-QPSK 802.15.4-2006 */ 16172f655e4SAlexander Aring hw->phy->supported.channels[2] |= 0x7fe; 1620739d643Salex.bluesman.smirnov@gmail.com /* 2.4 GHz CSS 802.15.4a-2007 */ 16372f655e4SAlexander Aring hw->phy->supported.channels[3] |= 0x3fff; 1640739d643Salex.bluesman.smirnov@gmail.com /* UWB Sub-gigahertz 802.15.4a-2007 */ 16572f655e4SAlexander Aring hw->phy->supported.channels[4] |= 1; 1660739d643Salex.bluesman.smirnov@gmail.com /* UWB Low band 802.15.4a-2007 */ 16772f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0x1e; 1680739d643Salex.bluesman.smirnov@gmail.com /* UWB High band 802.15.4a-2007 */ 16972f655e4SAlexander Aring hw->phy->supported.channels[4] |= 0xffe0; 1700739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz O-QPSK 802.15.4c-2009 */ 17172f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf; 1720739d643Salex.bluesman.smirnov@gmail.com /* 750 MHz MPSK 802.15.4c-2009 */ 17372f655e4SAlexander Aring hw->phy->supported.channels[5] |= 0xf0; 1740739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz BPSK 802.15.4d-2009 */ 17572f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ff; 1760739d643Salex.bluesman.smirnov@gmail.com /* 950 MHz GFSK 802.15.4d-2009 */ 17772f655e4SAlexander Aring hw->phy->supported.channels[6] |= 0x3ffc00; 1780739d643Salex.bluesman.smirnov@gmail.com 179e214a904SAlexander Aring ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); 180e214a904SAlexander Aring /* fake phy channel 13 as default */ 181e214a904SAlexander Aring hw->phy->current_channel = 13; 182e214a904SAlexander Aring phy->channel = hw->phy->current_channel; 183e214a904SAlexander Aring 1847c2b9bffSAlexander Aring hw->flags = IEEE802154_HW_PROMISCUOUS; 1855a504397SAlexander Aring hw->parent = dev; 1860739d643Salex.bluesman.smirnov@gmail.com 1875a504397SAlexander Aring err = ieee802154_register_hw(hw); 1880739d643Salex.bluesman.smirnov@gmail.com if (err) 1890739d643Salex.bluesman.smirnov@gmail.com goto err_reg; 1900739d643Salex.bluesman.smirnov@gmail.com 191bdca1fd9SAlexander Aring mutex_lock(&fakelb_phys_lock); 192b82b99f1SAlexander Aring list_add_tail(&phy->list, &fakelb_phys); 193bdca1fd9SAlexander Aring mutex_unlock(&fakelb_phys_lock); 1940739d643Salex.bluesman.smirnov@gmail.com 1950739d643Salex.bluesman.smirnov@gmail.com return 0; 1960739d643Salex.bluesman.smirnov@gmail.com 1970739d643Salex.bluesman.smirnov@gmail.com err_reg: 198db3f5d0dSAlexander Aring ieee802154_free_hw(phy->hw); 1990739d643Salex.bluesman.smirnov@gmail.com return err; 2000739d643Salex.bluesman.smirnov@gmail.com } 2010739d643Salex.bluesman.smirnov@gmail.com 202db3f5d0dSAlexander Aring static void fakelb_del(struct fakelb_phy *phy) 2030739d643Salex.bluesman.smirnov@gmail.com { 204db3f5d0dSAlexander Aring list_del(&phy->list); 2050739d643Salex.bluesman.smirnov@gmail.com 206db3f5d0dSAlexander Aring ieee802154_unregister_hw(phy->hw); 207db3f5d0dSAlexander Aring ieee802154_free_hw(phy->hw); 2080739d643Salex.bluesman.smirnov@gmail.com } 2090739d643Salex.bluesman.smirnov@gmail.com 210bb1f4606SBill Pemberton static int fakelb_probe(struct platform_device *pdev) 2110739d643Salex.bluesman.smirnov@gmail.com { 212db3f5d0dSAlexander Aring struct fakelb_phy *phy, *tmp; 21339572ab3SAlexander Aring int err, i; 2140739d643Salex.bluesman.smirnov@gmail.com 2150739d643Salex.bluesman.smirnov@gmail.com for (i = 0; i < numlbs; i++) { 216b82b99f1SAlexander Aring err = fakelb_add_one(&pdev->dev); 2170739d643Salex.bluesman.smirnov@gmail.com if (err < 0) 2180739d643Salex.bluesman.smirnov@gmail.com goto err_slave; 2190739d643Salex.bluesman.smirnov@gmail.com } 2200739d643Salex.bluesman.smirnov@gmail.com 221e03551d7SStefan Schmidt dev_info(&pdev->dev, "added %i fake ieee802154 hardware devices\n", numlbs); 2220739d643Salex.bluesman.smirnov@gmail.com return 0; 2230739d643Salex.bluesman.smirnov@gmail.com 2240739d643Salex.bluesman.smirnov@gmail.com err_slave: 225bdca1fd9SAlexander Aring mutex_lock(&fakelb_phys_lock); 226b82b99f1SAlexander Aring list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 227db3f5d0dSAlexander Aring fakelb_del(phy); 228bdca1fd9SAlexander Aring mutex_unlock(&fakelb_phys_lock); 2290739d643Salex.bluesman.smirnov@gmail.com return err; 2300739d643Salex.bluesman.smirnov@gmail.com } 2310739d643Salex.bluesman.smirnov@gmail.com 232bb1f4606SBill Pemberton static int fakelb_remove(struct platform_device *pdev) 2330739d643Salex.bluesman.smirnov@gmail.com { 23439572ab3SAlexander Aring struct fakelb_phy *phy, *tmp; 2350739d643Salex.bluesman.smirnov@gmail.com 236bdca1fd9SAlexander Aring mutex_lock(&fakelb_phys_lock); 23739572ab3SAlexander Aring list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 238db3f5d0dSAlexander Aring fakelb_del(phy); 239bdca1fd9SAlexander Aring mutex_unlock(&fakelb_phys_lock); 2400739d643Salex.bluesman.smirnov@gmail.com return 0; 2410739d643Salex.bluesman.smirnov@gmail.com } 2420739d643Salex.bluesman.smirnov@gmail.com 2430739d643Salex.bluesman.smirnov@gmail.com static struct platform_device *ieee802154fake_dev; 2440739d643Salex.bluesman.smirnov@gmail.com 2450739d643Salex.bluesman.smirnov@gmail.com static struct platform_driver ieee802154fake_driver = { 2460739d643Salex.bluesman.smirnov@gmail.com .probe = fakelb_probe, 247bb1f4606SBill Pemberton .remove = fakelb_remove, 2480739d643Salex.bluesman.smirnov@gmail.com .driver = { 2490739d643Salex.bluesman.smirnov@gmail.com .name = "ieee802154fakelb", 2500739d643Salex.bluesman.smirnov@gmail.com }, 2510739d643Salex.bluesman.smirnov@gmail.com }; 2520739d643Salex.bluesman.smirnov@gmail.com 2530739d643Salex.bluesman.smirnov@gmail.com static __init int fakelb_init_module(void) 2540739d643Salex.bluesman.smirnov@gmail.com { 2550739d643Salex.bluesman.smirnov@gmail.com ieee802154fake_dev = platform_device_register_simple( 2560739d643Salex.bluesman.smirnov@gmail.com "ieee802154fakelb", -1, NULL, 0); 2570739d643Salex.bluesman.smirnov@gmail.com return platform_driver_register(&ieee802154fake_driver); 2580739d643Salex.bluesman.smirnov@gmail.com } 2590739d643Salex.bluesman.smirnov@gmail.com 2600739d643Salex.bluesman.smirnov@gmail.com static __exit void fake_remove_module(void) 2610739d643Salex.bluesman.smirnov@gmail.com { 2620739d643Salex.bluesman.smirnov@gmail.com platform_driver_unregister(&ieee802154fake_driver); 2630739d643Salex.bluesman.smirnov@gmail.com platform_device_unregister(ieee802154fake_dev); 2640739d643Salex.bluesman.smirnov@gmail.com } 2650739d643Salex.bluesman.smirnov@gmail.com 2660739d643Salex.bluesman.smirnov@gmail.com module_init(fakelb_init_module); 2670739d643Salex.bluesman.smirnov@gmail.com module_exit(fake_remove_module); 2680739d643Salex.bluesman.smirnov@gmail.com MODULE_LICENSE("GPL"); 269