fakelb: move lock out of iteration
[deliverable/linux.git] / drivers / net / ieee802154 / fakelb.c
CommitLineData
e1e49b64 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 *
e1e49b64 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>
12b5c38f 25#include <linux/device.h>
e1e49b64 26#include <linux/spinlock.h>
27#include <net/mac802154.h>
5ad60d36 28#include <net/cfg802154.h>
e1e49b64 29
7e57905b 30static int numlbs = 2;
3335d98c 31static DEFINE_RWLOCK(fakelb_lock);
b82b99f1 32static LIST_HEAD(fakelb_phys);
e1e49b64 33
db3f5d0d 34struct fakelb_phy {
5a504397 35 struct ieee802154_hw *hw;
e1e49b64 36
37 struct list_head list;
e1e49b64 38
39 spinlock_t lock;
40 bool working;
41};
42
e1e49b64 43static int
5a504397 44fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level)
e1e49b64 45{
e1e49b64 46 BUG_ON(!level);
47 *level = 0xbe;
48
49 return 0;
50}
51
52static int
e37d2ec8 53fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
e1e49b64 54{
55 pr_debug("set channel to %d\n", channel);
56
e1e49b64 57 return 0;
58}
59
60static void
db3f5d0d 61fakelb_hw_deliver(struct fakelb_phy *phy, struct sk_buff *skb)
e1e49b64 62{
63 struct sk_buff *newskb;
64
db3f5d0d
AA
65 spin_lock(&phy->lock);
66 if (phy->working) {
e1e49b64 67 newskb = pskb_copy(skb, GFP_ATOMIC);
cb97d9a3 68 if (newskb)
db3f5d0d 69 ieee802154_rx_irqsafe(phy->hw, newskb, 0xcc);
e1e49b64 70 }
db3f5d0d 71 spin_unlock(&phy->lock);
e1e49b64 72}
73
74static int
5a504397 75fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
e1e49b64 76{
db3f5d0d 77 struct fakelb_phy *current_phy = hw->priv;
9f8b97f8 78 struct fakelb_phy *phy;
e1e49b64 79
3335d98c 80 read_lock_bh(&fakelb_lock);
b82b99f1 81 list_for_each_entry(phy, &fakelb_phys, list) {
9f8b97f8
AA
82 if (current_phy == phy)
83 continue;
84
85 if (phy->hw->phy->current_channel ==
86 current_phy->hw->phy->current_channel)
87 fakelb_hw_deliver(phy, skb);
e1e49b64 88 }
3335d98c 89 read_unlock_bh(&fakelb_lock);
e1e49b64 90
91 return 0;
92}
93
94static int
5a504397 95fakelb_hw_start(struct ieee802154_hw *hw) {
db3f5d0d 96 struct fakelb_phy *phy = hw->priv;
e1e49b64 97 int ret = 0;
98
db3f5d0d
AA
99 spin_lock(&phy->lock);
100 if (phy->working)
e1e49b64 101 ret = -EBUSY;
102 else
db3f5d0d
AA
103 phy->working = 1;
104 spin_unlock(&phy->lock);
e1e49b64 105
106 return ret;
107}
108
109static void
5a504397 110fakelb_hw_stop(struct ieee802154_hw *hw) {
db3f5d0d 111 struct fakelb_phy *phy = hw->priv;
e1e49b64 112
db3f5d0d
AA
113 spin_lock(&phy->lock);
114 phy->working = 0;
115 spin_unlock(&phy->lock);
e1e49b64 116}
117
16301861 118static const struct ieee802154_ops fakelb_ops = {
e1e49b64 119 .owner = THIS_MODULE,
ed0a5dce 120 .xmit_sync = fakelb_hw_xmit,
e1e49b64 121 .ed = fakelb_hw_ed,
122 .set_channel = fakelb_hw_channel,
123 .start = fakelb_hw_start,
124 .stop = fakelb_hw_stop,
125};
126
127/* Number of dummy devices to be set up by this module. */
128module_param(numlbs, int, 0);
129MODULE_PARM_DESC(numlbs, " number of pseudo devices");
130
b82b99f1 131static int fakelb_add_one(struct device *dev)
e1e49b64 132{
db3f5d0d 133 struct fakelb_phy *phy;
e1e49b64 134 int err;
5a504397 135 struct ieee802154_hw *hw;
e1e49b64 136
db3f5d0d 137 hw = ieee802154_alloc_hw(sizeof(*phy), &fakelb_ops);
5a504397 138 if (!hw)
e1e49b64 139 return -ENOMEM;
140
db3f5d0d
AA
141 phy = hw->priv;
142 phy->hw = hw;
e1e49b64 143
144 /* 868 MHz BPSK 802.15.4-2003 */
72f655e4 145 hw->phy->supported.channels[0] |= 1;
e1e49b64 146 /* 915 MHz BPSK 802.15.4-2003 */
72f655e4 147 hw->phy->supported.channels[0] |= 0x7fe;
e1e49b64 148 /* 2.4 GHz O-QPSK 802.15.4-2003 */
72f655e4 149 hw->phy->supported.channels[0] |= 0x7FFF800;
e1e49b64 150 /* 868 MHz ASK 802.15.4-2006 */
72f655e4 151 hw->phy->supported.channels[1] |= 1;
e1e49b64 152 /* 915 MHz ASK 802.15.4-2006 */
72f655e4 153 hw->phy->supported.channels[1] |= 0x7fe;
e1e49b64 154 /* 868 MHz O-QPSK 802.15.4-2006 */
72f655e4 155 hw->phy->supported.channels[2] |= 1;
e1e49b64 156 /* 915 MHz O-QPSK 802.15.4-2006 */
72f655e4 157 hw->phy->supported.channels[2] |= 0x7fe;
e1e49b64 158 /* 2.4 GHz CSS 802.15.4a-2007 */
72f655e4 159 hw->phy->supported.channels[3] |= 0x3fff;
e1e49b64 160 /* UWB Sub-gigahertz 802.15.4a-2007 */
72f655e4 161 hw->phy->supported.channels[4] |= 1;
e1e49b64 162 /* UWB Low band 802.15.4a-2007 */
72f655e4 163 hw->phy->supported.channels[4] |= 0x1e;
e1e49b64 164 /* UWB High band 802.15.4a-2007 */
72f655e4 165 hw->phy->supported.channels[4] |= 0xffe0;
e1e49b64 166 /* 750 MHz O-QPSK 802.15.4c-2009 */
72f655e4 167 hw->phy->supported.channels[5] |= 0xf;
e1e49b64 168 /* 750 MHz MPSK 802.15.4c-2009 */
72f655e4 169 hw->phy->supported.channels[5] |= 0xf0;
e1e49b64 170 /* 950 MHz BPSK 802.15.4d-2009 */
72f655e4 171 hw->phy->supported.channels[6] |= 0x3ff;
e1e49b64 172 /* 950 MHz GFSK 802.15.4d-2009 */
72f655e4 173 hw->phy->supported.channels[6] |= 0x3ffc00;
e1e49b64 174
db3f5d0d 175 spin_lock_init(&phy->lock);
e1e49b64 176
5a504397 177 hw->parent = dev;
e1e49b64 178
5a504397 179 err = ieee802154_register_hw(hw);
e1e49b64 180 if (err)
181 goto err_reg;
182
3335d98c 183 write_lock_bh(&fakelb_lock);
b82b99f1 184 list_add_tail(&phy->list, &fakelb_phys);
3335d98c 185 write_unlock_bh(&fakelb_lock);
e1e49b64 186
187 return 0;
188
189err_reg:
db3f5d0d 190 ieee802154_free_hw(phy->hw);
e1e49b64 191 return err;
192}
193
db3f5d0d 194static void fakelb_del(struct fakelb_phy *phy)
e1e49b64 195{
db3f5d0d 196 list_del(&phy->list);
e1e49b64 197
db3f5d0d
AA
198 ieee802154_unregister_hw(phy->hw);
199 ieee802154_free_hw(phy->hw);
e1e49b64 200}
201
bb1f4606 202static int fakelb_probe(struct platform_device *pdev)
e1e49b64 203{
db3f5d0d 204 struct fakelb_phy *phy, *tmp;
e1e49b64 205 int err = -ENOMEM;
206 int i;
207
e1e49b64 208 for (i = 0; i < numlbs; i++) {
b82b99f1 209 err = fakelb_add_one(&pdev->dev);
e1e49b64 210 if (err < 0)
211 goto err_slave;
212 }
213
e1e49b64 214 dev_info(&pdev->dev, "added ieee802154 hardware\n");
215 return 0;
216
217err_slave:
6fa2cffe 218 write_lock_bh(&fakelb_lock);
b82b99f1 219 list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
db3f5d0d 220 fakelb_del(phy);
6fa2cffe 221 write_unlock_bh(&fakelb_lock);
e1e49b64 222 return err;
223}
224
bb1f4606 225static int fakelb_remove(struct platform_device *pdev)
e1e49b64 226{
db3f5d0d 227 struct fakelb_phy *phy, *temp;
e1e49b64 228
6fa2cffe 229 write_lock_bh(&fakelb_lock);
b82b99f1 230 list_for_each_entry_safe(phy, temp, &fakelb_phys, list)
db3f5d0d 231 fakelb_del(phy);
6fa2cffe 232 write_unlock_bh(&fakelb_lock);
e1e49b64 233 return 0;
234}
235
236static struct platform_device *ieee802154fake_dev;
237
238static struct platform_driver ieee802154fake_driver = {
239 .probe = fakelb_probe,
bb1f4606 240 .remove = fakelb_remove,
e1e49b64 241 .driver = {
242 .name = "ieee802154fakelb",
e1e49b64 243 },
244};
245
246static __init int fakelb_init_module(void)
247{
248 ieee802154fake_dev = platform_device_register_simple(
249 "ieee802154fakelb", -1, NULL, 0);
250 return platform_driver_register(&ieee802154fake_driver);
251}
252
253static __exit void fake_remove_module(void)
254{
255 platform_driver_unregister(&ieee802154fake_driver);
256 platform_device_unregister(ieee802154fake_dev);
257}
258
259module_init(fakelb_init_module);
260module_exit(fake_remove_module);
261MODULE_LICENSE("GPL");
This page took 0.222398 seconds and 5 git commands to generate.