162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Loopback IEEE 802.15.4 interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007-2012 Siemens AG 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Written by: 862306a36Sopenharmony_ci * Sergey Lapin <slapin@ossfans.org> 962306a36Sopenharmony_ci * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 1062306a36Sopenharmony_ci * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/timer.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <net/mac802154.h> 2062306a36Sopenharmony_ci#include <net/cfg802154.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int numlbs = 2; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic LIST_HEAD(fakelb_phys); 2562306a36Sopenharmony_cistatic DEFINE_MUTEX(fakelb_phys_lock); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic LIST_HEAD(fakelb_ifup_phys); 2862306a36Sopenharmony_cistatic DEFINE_RWLOCK(fakelb_ifup_phys_lock); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct fakelb_phy { 3162306a36Sopenharmony_ci struct ieee802154_hw *hw; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci u8 page; 3462306a36Sopenharmony_ci u8 channel; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci bool suspended; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci struct list_head list; 3962306a36Sopenharmony_ci struct list_head list_ifup; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci WARN_ON(!level); 4562306a36Sopenharmony_ci *level = 0xbe; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 5562306a36Sopenharmony_ci phy->page = page; 5662306a36Sopenharmony_ci phy->channel = channel; 5762306a36Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct fakelb_phy *current_phy = hw->priv, *phy; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci read_lock_bh(&fakelb_ifup_phys_lock); 6662306a36Sopenharmony_ci WARN_ON(current_phy->suspended); 6762306a36Sopenharmony_ci list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) { 6862306a36Sopenharmony_ci if (current_phy == phy) 6962306a36Sopenharmony_ci continue; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (current_phy->page == phy->page && 7262306a36Sopenharmony_ci current_phy->channel == phy->channel) { 7362306a36Sopenharmony_ci struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (newskb) 7662306a36Sopenharmony_ci ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci read_unlock_bh(&fakelb_ifup_phys_lock); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ieee802154_xmit_complete(hw, skb, false); 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int fakelb_hw_start(struct ieee802154_hw *hw) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 9062306a36Sopenharmony_ci phy->suspended = false; 9162306a36Sopenharmony_ci list_add(&phy->list_ifup, &fakelb_ifup_phys); 9262306a36Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void fakelb_hw_stop(struct ieee802154_hw *hw) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct fakelb_phy *phy = hw->priv; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci write_lock_bh(&fakelb_ifup_phys_lock); 10262306a36Sopenharmony_ci phy->suspended = true; 10362306a36Sopenharmony_ci list_del(&phy->list_ifup); 10462306a36Sopenharmony_ci write_unlock_bh(&fakelb_ifup_phys_lock); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int 10862306a36Sopenharmony_cifakelb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct ieee802154_ops fakelb_ops = { 11462306a36Sopenharmony_ci .owner = THIS_MODULE, 11562306a36Sopenharmony_ci .xmit_async = fakelb_hw_xmit, 11662306a36Sopenharmony_ci .ed = fakelb_hw_ed, 11762306a36Sopenharmony_ci .set_channel = fakelb_hw_channel, 11862306a36Sopenharmony_ci .start = fakelb_hw_start, 11962306a36Sopenharmony_ci .stop = fakelb_hw_stop, 12062306a36Sopenharmony_ci .set_promiscuous_mode = fakelb_set_promiscuous_mode, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Number of dummy devices to be set up by this module. */ 12462306a36Sopenharmony_cimodule_param(numlbs, int, 0); 12562306a36Sopenharmony_ciMODULE_PARM_DESC(numlbs, " number of pseudo devices"); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int fakelb_add_one(struct device *dev) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct ieee802154_hw *hw; 13062306a36Sopenharmony_ci struct fakelb_phy *phy; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops); 13462306a36Sopenharmony_ci if (!hw) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci phy = hw->priv; 13862306a36Sopenharmony_ci phy->hw = hw; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 868 MHz BPSK 802.15.4-2003 */ 14162306a36Sopenharmony_ci hw->phy->supported.channels[0] |= 1; 14262306a36Sopenharmony_ci /* 915 MHz BPSK 802.15.4-2003 */ 14362306a36Sopenharmony_ci hw->phy->supported.channels[0] |= 0x7fe; 14462306a36Sopenharmony_ci /* 2.4 GHz O-QPSK 802.15.4-2003 */ 14562306a36Sopenharmony_ci hw->phy->supported.channels[0] |= 0x7FFF800; 14662306a36Sopenharmony_ci /* 868 MHz ASK 802.15.4-2006 */ 14762306a36Sopenharmony_ci hw->phy->supported.channels[1] |= 1; 14862306a36Sopenharmony_ci /* 915 MHz ASK 802.15.4-2006 */ 14962306a36Sopenharmony_ci hw->phy->supported.channels[1] |= 0x7fe; 15062306a36Sopenharmony_ci /* 868 MHz O-QPSK 802.15.4-2006 */ 15162306a36Sopenharmony_ci hw->phy->supported.channels[2] |= 1; 15262306a36Sopenharmony_ci /* 915 MHz O-QPSK 802.15.4-2006 */ 15362306a36Sopenharmony_ci hw->phy->supported.channels[2] |= 0x7fe; 15462306a36Sopenharmony_ci /* 2.4 GHz CSS 802.15.4a-2007 */ 15562306a36Sopenharmony_ci hw->phy->supported.channels[3] |= 0x3fff; 15662306a36Sopenharmony_ci /* UWB Sub-gigahertz 802.15.4a-2007 */ 15762306a36Sopenharmony_ci hw->phy->supported.channels[4] |= 1; 15862306a36Sopenharmony_ci /* UWB Low band 802.15.4a-2007 */ 15962306a36Sopenharmony_ci hw->phy->supported.channels[4] |= 0x1e; 16062306a36Sopenharmony_ci /* UWB High band 802.15.4a-2007 */ 16162306a36Sopenharmony_ci hw->phy->supported.channels[4] |= 0xffe0; 16262306a36Sopenharmony_ci /* 750 MHz O-QPSK 802.15.4c-2009 */ 16362306a36Sopenharmony_ci hw->phy->supported.channels[5] |= 0xf; 16462306a36Sopenharmony_ci /* 750 MHz MPSK 802.15.4c-2009 */ 16562306a36Sopenharmony_ci hw->phy->supported.channels[5] |= 0xf0; 16662306a36Sopenharmony_ci /* 950 MHz BPSK 802.15.4d-2009 */ 16762306a36Sopenharmony_ci hw->phy->supported.channels[6] |= 0x3ff; 16862306a36Sopenharmony_ci /* 950 MHz GFSK 802.15.4d-2009 */ 16962306a36Sopenharmony_ci hw->phy->supported.channels[6] |= 0x3ffc00; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); 17262306a36Sopenharmony_ci /* fake phy channel 13 as default */ 17362306a36Sopenharmony_ci hw->phy->current_channel = 13; 17462306a36Sopenharmony_ci phy->channel = hw->phy->current_channel; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci hw->flags = IEEE802154_HW_PROMISCUOUS; 17762306a36Sopenharmony_ci hw->parent = dev; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci err = ieee802154_register_hw(hw); 18062306a36Sopenharmony_ci if (err) 18162306a36Sopenharmony_ci goto err_reg; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 18462306a36Sopenharmony_ci list_add_tail(&phy->list, &fakelb_phys); 18562306a36Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cierr_reg: 19062306a36Sopenharmony_ci ieee802154_free_hw(phy->hw); 19162306a36Sopenharmony_ci return err; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void fakelb_del(struct fakelb_phy *phy) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci list_del(&phy->list); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ieee802154_unregister_hw(phy->hw); 19962306a36Sopenharmony_ci ieee802154_free_hw(phy->hw); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int fakelb_probe(struct platform_device *pdev) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct fakelb_phy *phy, *tmp; 20562306a36Sopenharmony_ci int err, i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < numlbs; i++) { 20862306a36Sopenharmony_ci err = fakelb_add_one(&pdev->dev); 20962306a36Sopenharmony_ci if (err < 0) 21062306a36Sopenharmony_ci goto err_slave; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dev_info(&pdev->dev, "added %i fake ieee802154 hardware devices\n", numlbs); 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cierr_slave: 21762306a36Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 21862306a36Sopenharmony_ci list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 21962306a36Sopenharmony_ci fakelb_del(phy); 22062306a36Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int fakelb_remove(struct platform_device *pdev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct fakelb_phy *phy, *tmp; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mutex_lock(&fakelb_phys_lock); 22962306a36Sopenharmony_ci list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 23062306a36Sopenharmony_ci fakelb_del(phy); 23162306a36Sopenharmony_ci mutex_unlock(&fakelb_phys_lock); 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic struct platform_device *ieee802154fake_dev; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct platform_driver ieee802154fake_driver = { 23862306a36Sopenharmony_ci .probe = fakelb_probe, 23962306a36Sopenharmony_ci .remove = fakelb_remove, 24062306a36Sopenharmony_ci .driver = { 24162306a36Sopenharmony_ci .name = "ieee802154fakelb", 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic __init int fakelb_init_module(void) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci ieee802154fake_dev = platform_device_register_simple( 24862306a36Sopenharmony_ci "ieee802154fakelb", -1, NULL, 0); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci pr_warn("fakelb driver is marked as deprecated, please use mac802154_hwsim!\n"); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return platform_driver_register(&ieee802154fake_driver); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic __exit void fake_remove_module(void) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci platform_driver_unregister(&ieee802154fake_driver); 25862306a36Sopenharmony_ci platform_device_unregister(ieee802154fake_dev); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cimodule_init(fakelb_init_module); 26262306a36Sopenharmony_cimodule_exit(fake_remove_module); 26362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 264