1 /* 2 * Loopback IEEE 802.15.4 interface 3 * 4 * Copyright 2007-2012 Siemens AG 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 8 * as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * Written by: 16 * Sergey Lapin <slapin@ossfans.org> 17 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 18 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 19 */ 20 21 #include <linux/module.h> 22 #include <linux/timer.h> 23 #include <linux/platform_device.h> 24 #include <linux/netdevice.h> 25 #include <linux/device.h> 26 #include <linux/spinlock.h> 27 #include <net/mac802154.h> 28 #include <net/cfg802154.h> 29 30 static int numlbs = 2; 31 32 static LIST_HEAD(fakelb_phys); 33 static DEFINE_SPINLOCK(fakelb_phys_lock); 34 35 static LIST_HEAD(fakelb_ifup_phys); 36 static DEFINE_RWLOCK(fakelb_ifup_phys_lock); 37 38 struct fakelb_phy { 39 struct ieee802154_hw *hw; 40 41 struct list_head list; 42 struct list_head list_ifup; 43 }; 44 45 static int 46 fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level) 47 { 48 BUG_ON(!level); 49 *level = 0xbe; 50 51 return 0; 52 } 53 54 static int 55 fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 56 { 57 pr_debug("set channel to %d\n", channel); 58 59 return 0; 60 } 61 62 static void 63 fakelb_hw_deliver(struct fakelb_phy *phy, struct sk_buff *skb) 64 { 65 struct sk_buff *newskb; 66 67 newskb = pskb_copy(skb, GFP_ATOMIC); 68 if (newskb) 69 ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc); 70 } 71 72 static int 73 fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) 74 { 75 struct fakelb_phy *current_phy = hw->priv; 76 struct fakelb_phy *phy; 77 78 read_lock_bh(&fakelb_ifup_phys_lock); 79 list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) { 80 if (current_phy == phy) 81 continue; 82 83 if (phy->hw->phy->current_channel == 84 current_phy->hw->phy->current_channel) 85 fakelb_hw_deliver(phy, skb); 86 } 87 read_unlock_bh(&fakelb_ifup_phys_lock); 88 89 return 0; 90 } 91 92 static int 93 fakelb_hw_start(struct ieee802154_hw *hw) { 94 struct fakelb_phy *phy = hw->priv; 95 96 write_lock_bh(&fakelb_ifup_phys_lock); 97 list_add(&phy->list_ifup, &fakelb_ifup_phys); 98 write_unlock_bh(&fakelb_ifup_phys_lock); 99 100 return 0; 101 } 102 103 static void 104 fakelb_hw_stop(struct ieee802154_hw *hw) { 105 struct fakelb_phy *phy = hw->priv; 106 107 write_lock_bh(&fakelb_ifup_phys_lock); 108 list_del(&phy->list_ifup); 109 write_unlock_bh(&fakelb_ifup_phys_lock); 110 } 111 112 static const struct ieee802154_ops fakelb_ops = { 113 .owner = THIS_MODULE, 114 .xmit_sync = fakelb_hw_xmit, 115 .ed = fakelb_hw_ed, 116 .set_channel = fakelb_hw_channel, 117 .start = fakelb_hw_start, 118 .stop = fakelb_hw_stop, 119 }; 120 121 /* Number of dummy devices to be set up by this module. */ 122 module_param(numlbs, int, 0); 123 MODULE_PARM_DESC(numlbs, " number of pseudo devices"); 124 125 static int fakelb_add_one(struct device *dev) 126 { 127 struct fakelb_phy *phy; 128 int err; 129 struct ieee802154_hw *hw; 130 131 hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops); 132 if (!hw) 133 return -ENOMEM; 134 135 phy = hw->priv; 136 phy->hw = hw; 137 138 /* 868 MHz BPSK 802.15.4-2003 */ 139 hw->phy->supported.channels[0] |= 1; 140 /* 915 MHz BPSK 802.15.4-2003 */ 141 hw->phy->supported.channels[0] |= 0x7fe; 142 /* 2.4 GHz O-QPSK 802.15.4-2003 */ 143 hw->phy->supported.channels[0] |= 0x7FFF800; 144 /* 868 MHz ASK 802.15.4-2006 */ 145 hw->phy->supported.channels[1] |= 1; 146 /* 915 MHz ASK 802.15.4-2006 */ 147 hw->phy->supported.channels[1] |= 0x7fe; 148 /* 868 MHz O-QPSK 802.15.4-2006 */ 149 hw->phy->supported.channels[2] |= 1; 150 /* 915 MHz O-QPSK 802.15.4-2006 */ 151 hw->phy->supported.channels[2] |= 0x7fe; 152 /* 2.4 GHz CSS 802.15.4a-2007 */ 153 hw->phy->supported.channels[3] |= 0x3fff; 154 /* UWB Sub-gigahertz 802.15.4a-2007 */ 155 hw->phy->supported.channels[4] |= 1; 156 /* UWB Low band 802.15.4a-2007 */ 157 hw->phy->supported.channels[4] |= 0x1e; 158 /* UWB High band 802.15.4a-2007 */ 159 hw->phy->supported.channels[4] |= 0xffe0; 160 /* 750 MHz O-QPSK 802.15.4c-2009 */ 161 hw->phy->supported.channels[5] |= 0xf; 162 /* 750 MHz MPSK 802.15.4c-2009 */ 163 hw->phy->supported.channels[5] |= 0xf0; 164 /* 950 MHz BPSK 802.15.4d-2009 */ 165 hw->phy->supported.channels[6] |= 0x3ff; 166 /* 950 MHz GFSK 802.15.4d-2009 */ 167 hw->phy->supported.channels[6] |= 0x3ffc00; 168 169 hw->parent = dev; 170 171 err = ieee802154_register_hw(hw); 172 if (err) 173 goto err_reg; 174 175 spin_lock(&fakelb_phys_lock); 176 list_add_tail(&phy->list, &fakelb_phys); 177 spin_unlock(&fakelb_phys_lock); 178 179 return 0; 180 181 err_reg: 182 ieee802154_free_hw(phy->hw); 183 return err; 184 } 185 186 static void fakelb_del(struct fakelb_phy *phy) 187 { 188 list_del(&phy->list); 189 190 ieee802154_unregister_hw(phy->hw); 191 ieee802154_free_hw(phy->hw); 192 } 193 194 static int fakelb_probe(struct platform_device *pdev) 195 { 196 struct fakelb_phy *phy, *tmp; 197 int err = -ENOMEM; 198 int i; 199 200 for (i = 0; i < numlbs; i++) { 201 err = fakelb_add_one(&pdev->dev); 202 if (err < 0) 203 goto err_slave; 204 } 205 206 dev_info(&pdev->dev, "added ieee802154 hardware\n"); 207 return 0; 208 209 err_slave: 210 spin_lock(&fakelb_phys_lock); 211 list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) 212 fakelb_del(phy); 213 spin_unlock(&fakelb_phys_lock); 214 return err; 215 } 216 217 static int fakelb_remove(struct platform_device *pdev) 218 { 219 struct fakelb_phy *phy, *temp; 220 221 spin_lock(&fakelb_phys_lock); 222 list_for_each_entry_safe(phy, temp, &fakelb_phys, list) 223 fakelb_del(phy); 224 spin_unlock(&fakelb_phys_lock); 225 return 0; 226 } 227 228 static struct platform_device *ieee802154fake_dev; 229 230 static struct platform_driver ieee802154fake_driver = { 231 .probe = fakelb_probe, 232 .remove = fakelb_remove, 233 .driver = { 234 .name = "ieee802154fakelb", 235 }, 236 }; 237 238 static __init int fakelb_init_module(void) 239 { 240 ieee802154fake_dev = platform_device_register_simple( 241 "ieee802154fakelb", -1, NULL, 0); 242 return platform_driver_register(&ieee802154fake_driver); 243 } 244 245 static __exit void fake_remove_module(void) 246 { 247 platform_driver_unregister(&ieee802154fake_driver); 248 platform_device_unregister(ieee802154fake_dev); 249 } 250 251 module_init(fakelb_init_module); 252 module_exit(fake_remove_module); 253 MODULE_LICENSE("GPL"); 254