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