18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Loopback IEEE 802.15.4 interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007-2012 Siemens AG 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Written by: 88c2ecf20Sopenharmony_ci * Sergey Lapin <slapin@ossfans.org> 98c2ecf20Sopenharmony_ci * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 108c2ecf20Sopenharmony_ci * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/timer.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <net/mac802154.h> 208c2ecf20Sopenharmony_ci#include <net/cfg802154.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int numlbs = 2; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic LIST_HEAD(fakelb_phys); 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fakelb_phys_lock); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic LIST_HEAD(fakelb_ifup_phys); 288c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(fakelb_ifup_phys_lock); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct fakelb_phy { 318c2ecf20Sopenharmony_ci struct ieee802154_hw *hw; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci u8 page; 348c2ecf20Sopenharmony_ci u8 channel; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci bool suspended; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci struct list_head list; 398c2ecf20Sopenharmony_ci struct list_head list_ifup; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci WARN_ON(!level); 458c2ecf20Sopenharmony_ci *level = 0xbe; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 558c2ecf20Sopenharmony_ci phy->page = page; 568c2ecf20Sopenharmony_ci phy->channel = channel; 578c2ecf20Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct fakelb_phy *current_phy = hw->priv, *phy; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci read_lock_bh(&fakelb_ifup_phys_lock); 668c2ecf20Sopenharmony_ci WARN_ON(current_phy->suspended); 678c2ecf20Sopenharmony_ci list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) { 688c2ecf20Sopenharmony_ci if (current_phy == phy) 698c2ecf20Sopenharmony_ci continue; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (current_phy->page == phy->page && 728c2ecf20Sopenharmony_ci current_phy->channel == phy->channel) { 738c2ecf20Sopenharmony_ci struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (newskb) 768c2ecf20Sopenharmony_ci ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci read_unlock_bh(&fakelb_ifup_phys_lock); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ieee802154_xmit_complete(hw, skb, false); 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int fakelb_hw_start(struct ieee802154_hw *hw) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 908c2ecf20Sopenharmony_ci phy->suspended = false; 918c2ecf20Sopenharmony_ci list_add(&phy->list_ifup, &fakelb_ifup_phys); 928c2ecf20Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void fakelb_hw_stop(struct ieee802154_hw *hw) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 1028c2ecf20Sopenharmony_ci phy->suspended = true; 1038c2ecf20Sopenharmony_ci list_del(&phy->list_ifup); 1048c2ecf20Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int 1088c2ecf20Sopenharmony_cifakelb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct ieee802154_ops fakelb_ops = { 1148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1158c2ecf20Sopenharmony_ci .xmit_async = fakelb_hw_xmit, 1168c2ecf20Sopenharmony_ci .ed = fakelb_hw_ed, 1178c2ecf20Sopenharmony_ci .set_channel = fakelb_hw_channel, 1188c2ecf20Sopenharmony_ci .start = fakelb_hw_start, 1198c2ecf20Sopenharmony_ci .stop = fakelb_hw_stop, 1208c2ecf20Sopenharmony_ci .set_promiscuous_mode = fakelb_set_promiscuous_mode, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Number of dummy devices to be set up by this module. */ 1248c2ecf20Sopenharmony_cimodule_param(numlbs, int, 0); 1258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(numlbs, " number of pseudo devices"); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int fakelb_add_one(struct device *dev) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct ieee802154_hw *hw; 1308c2ecf20Sopenharmony_ci struct fakelb_phy *phy; 1318c2ecf20Sopenharmony_ci int err; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops); 1348c2ecf20Sopenharmony_ci if (!hw) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci phy = hw->priv; 1388c2ecf20Sopenharmony_ci phy->hw = hw; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 868 MHz BPSK 802.15.4-2003 */ 1418c2ecf20Sopenharmony_ci hw->phy->supported.channels[0] |= 1; 1428c2ecf20Sopenharmony_ci /* 915 MHz BPSK 802.15.4-2003 */ 1438c2ecf20Sopenharmony_ci hw->phy->supported.channels[0] |= 0x7fe; 1448c2ecf20Sopenharmony_ci /* 2.4 GHz O-QPSK 802.15.4-2003 */ 1458c2ecf20Sopenharmony_ci hw->phy->supported.channels[0] |= 0x7FFF800; 1468c2ecf20Sopenharmony_ci /* 868 MHz ASK 802.15.4-2006 */ 1478c2ecf20Sopenharmony_ci hw->phy->supported.channels[1] |= 1; 1488c2ecf20Sopenharmony_ci /* 915 MHz ASK 802.15.4-2006 */ 1498c2ecf20Sopenharmony_ci hw->phy->supported.channels[1] |= 0x7fe; 1508c2ecf20Sopenharmony_ci /* 868 MHz O-QPSK 802.15.4-2006 */ 1518c2ecf20Sopenharmony_ci hw->phy->supported.channels[2] |= 1; 1528c2ecf20Sopenharmony_ci /* 915 MHz O-QPSK 802.15.4-2006 */ 1538c2ecf20Sopenharmony_ci hw->phy->supported.channels[2] |= 0x7fe; 1548c2ecf20Sopenharmony_ci /* 2.4 GHz CSS 802.15.4a-2007 */ 1558c2ecf20Sopenharmony_ci hw->phy->supported.channels[3] |= 0x3fff; 1568c2ecf20Sopenharmony_ci /* UWB Sub-gigahertz 802.15.4a-2007 */ 1578c2ecf20Sopenharmony_ci hw->phy->supported.channels[4] |= 1; 1588c2ecf20Sopenharmony_ci /* UWB Low band 802.15.4a-2007 */ 1598c2ecf20Sopenharmony_ci hw->phy->supported.channels[4] |= 0x1e; 1608c2ecf20Sopenharmony_ci /* UWB High band 802.15.4a-2007 */ 1618c2ecf20Sopenharmony_ci hw->phy->supported.channels[4] |= 0xffe0; 1628c2ecf20Sopenharmony_ci /* 750 MHz O-QPSK 802.15.4c-2009 */ 1638c2ecf20Sopenharmony_ci hw->phy->supported.channels[5] |= 0xf; 1648c2ecf20Sopenharmony_ci /* 750 MHz MPSK 802.15.4c-2009 */ 1658c2ecf20Sopenharmony_ci hw->phy->supported.channels[5] |= 0xf0; 1668c2ecf20Sopenharmony_ci /* 950 MHz BPSK 802.15.4d-2009 */ 1678c2ecf20Sopenharmony_ci hw->phy->supported.channels[6] |= 0x3ff; 1688c2ecf20Sopenharmony_ci /* 950 MHz GFSK 802.15.4d-2009 */ 1698c2ecf20Sopenharmony_ci hw->phy->supported.channels[6] |= 0x3ffc00; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); 1728c2ecf20Sopenharmony_ci /* fake phy channel 13 as default */ 1738c2ecf20Sopenharmony_ci hw->phy->current_channel = 13; 1748c2ecf20Sopenharmony_ci phy->channel = hw->phy->current_channel; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci hw->flags = IEEE802154_HW_PROMISCUOUS; 1778c2ecf20Sopenharmony_ci hw->parent = dev; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci err = ieee802154_register_hw(hw); 1808c2ecf20Sopenharmony_ci if (err) 1818c2ecf20Sopenharmony_ci goto err_reg; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 1848c2ecf20Sopenharmony_ci list_add_tail(&phy->list, &fakelb_phys); 1858c2ecf20Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cierr_reg: 1908c2ecf20Sopenharmony_ci ieee802154_free_hw(phy->hw); 1918c2ecf20Sopenharmony_ci return err; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void fakelb_del(struct fakelb_phy *phy) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci list_del(&phy->list); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ieee802154_unregister_hw(phy->hw); 1998c2ecf20Sopenharmony_ci ieee802154_free_hw(phy->hw); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int fakelb_probe(struct platform_device *pdev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct fakelb_phy *phy, *tmp; 2058c2ecf20Sopenharmony_ci int err, i; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < numlbs; i++) { 2088c2ecf20Sopenharmony_ci err = fakelb_add_one(&pdev->dev); 2098c2ecf20Sopenharmony_ci if (err < 0) 2108c2ecf20Sopenharmony_ci goto err_slave; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "added %i fake ieee802154 hardware devices\n", numlbs); 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cierr_slave: 2178c2ecf20Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 2188c2ecf20Sopenharmony_ci list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 2198c2ecf20Sopenharmony_ci fakelb_del(phy); 2208c2ecf20Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 2218c2ecf20Sopenharmony_ci return err; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int fakelb_remove(struct platform_device *pdev) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct fakelb_phy *phy, *tmp; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 2298c2ecf20Sopenharmony_ci list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 2308c2ecf20Sopenharmony_ci fakelb_del(phy); 2318c2ecf20Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct platform_device *ieee802154fake_dev; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct platform_driver ieee802154fake_driver = { 2388c2ecf20Sopenharmony_ci .probe = fakelb_probe, 2398c2ecf20Sopenharmony_ci .remove = fakelb_remove, 2408c2ecf20Sopenharmony_ci .driver = { 2418c2ecf20Sopenharmony_ci .name = "ieee802154fakelb", 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic __init int fakelb_init_module(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci ieee802154fake_dev = platform_device_register_simple( 2488c2ecf20Sopenharmony_ci "ieee802154fakelb", -1, NULL, 0); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci pr_warn("fakelb driver is marked as deprecated, please use mac802154_hwsim!\n"); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return platform_driver_register(&ieee802154fake_driver); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic __exit void fake_remove_module(void) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci platform_driver_unregister(&ieee802154fake_driver); 2588c2ecf20Sopenharmony_ci platform_device_unregister(ieee802154fake_dev); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cimodule_init(fakelb_init_module); 2628c2ecf20Sopenharmony_cimodule_exit(fake_remove_module); 2638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 264