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