mac802154: main: move open and close into iface
[linux-2.6-block.git] / net / mac802154 / main.c
CommitLineData
1010f540 1/*
2 * Copyright (C) 2007-2012 Siemens AG
3 *
4 * Written by:
5 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
6 *
7 * Based on the code from 'linux-zigbee.sourceforge.net' project.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
1010f540 17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/netdevice.h>
22
62610ad2 23#include <net/netlink.h>
24#include <linux/nl802154.h>
1010f540 25#include <net/mac802154.h>
b70ab2e8 26#include <net/ieee802154_netdev.h>
1010f540 27#include <net/route.h>
5ad60d36 28#include <net/cfg802154.h>
1010f540 29
0f1556bc 30#include "ieee802154_i.h"
1010f540 31
62610ad2 32static int
33mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
34{
59d19cd7 35 struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
a5e1ec53 36 struct ieee802154_local *local;
62610ad2 37 int err;
38
a5e1ec53 39 local = wpan_phy_priv(phy);
62610ad2 40
036562f9 41 sdata->dev = dev;
04e850fe 42 sdata->local = local;
62610ad2 43
a5e1ec53 44 dev->needed_headroom = local->hw.extra_tx_headroom;
62610ad2 45
a5e1ec53 46 SET_NETDEV_DEV(dev, &local->phy->dev);
62610ad2 47
d98be45b 48 mutex_lock(&local->iflist_mtx);
a5e1ec53 49 if (!local->running) {
d98be45b 50 mutex_unlock(&local->iflist_mtx);
62610ad2 51 return -ENODEV;
52 }
d98be45b 53 mutex_unlock(&local->iflist_mtx);
62610ad2 54
55 err = register_netdev(dev);
56 if (err < 0)
57 return err;
58
59 rtnl_lock();
d98be45b
AA
60 mutex_lock(&local->iflist_mtx);
61 list_add_tail_rcu(&sdata->list, &local->interfaces);
62 mutex_unlock(&local->iflist_mtx);
62610ad2 63 rtnl_unlock();
64
65 return 0;
66}
67
68static void
69mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
70{
59d19cd7 71 struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
4710d806 72
62610ad2 73 ASSERT_RTNL();
62610ad2 74
04e850fe 75 BUG_ON(sdata->local->phy != phy);
62610ad2 76
d98be45b 77 mutex_lock(&sdata->local->iflist_mtx);
62610ad2 78 list_del_rcu(&sdata->list);
d98be45b 79 mutex_unlock(&sdata->local->iflist_mtx);
62610ad2 80
81 synchronize_rcu();
82 unregister_netdevice(sdata->dev);
83}
84
85static struct net_device *
86mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
87{
88 struct net_device *dev;
89 int err = -ENOMEM;
90
62610ad2 91 switch (type) {
0606069d 92 case IEEE802154_DEV_MONITOR:
036562f9 93 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
c835a677
TG
94 name, NET_NAME_UNKNOWN,
95 mac802154_monitor_setup);
0606069d 96 break;
32bad7e3 97 case IEEE802154_DEV_WPAN:
036562f9 98 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
c835a677
TG
99 name, NET_NAME_UNKNOWN,
100 mac802154_wpan_setup);
32bad7e3 101 break;
62610ad2 102 default:
103 dev = NULL;
104 err = -EINVAL;
105 break;
106 }
107 if (!dev)
108 goto err;
109
110 err = mac802154_netdev_register(phy, dev);
111 if (err)
112 goto err_free;
113
114 dev_hold(dev); /* we return an incremented device refcount */
115 return dev;
116
117err_free:
118 free_netdev(dev);
119err:
120 return ERR_PTR(err);
121}
122
9b2777d6
PB
123static int mac802154_set_txpower(struct wpan_phy *phy, int db)
124{
a5e1ec53 125 struct ieee802154_local *local = wpan_phy_priv(phy);
9b2777d6 126
a5e1ec53 127 return local->ops->set_txpower(&local->hw, db);
9b2777d6
PB
128}
129
84dda3c6
PB
130static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
131{
a5e1ec53 132 struct ieee802154_local *local = wpan_phy_priv(phy);
84dda3c6 133
a5e1ec53 134 return local->ops->set_lbt(&local->hw, on);
84dda3c6
PB
135}
136
ba08fea5
PB
137static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
138{
a5e1ec53 139 struct ieee802154_local *local = wpan_phy_priv(phy);
ba08fea5 140
a5e1ec53 141 return local->ops->set_cca_mode(&local->hw, mode);
ba08fea5
PB
142}
143
6ca00197
PB
144static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
145{
a5e1ec53 146 struct ieee802154_local *local = wpan_phy_priv(phy);
6ca00197 147
a5e1ec53 148 return local->ops->set_cca_ed_level(&local->hw, level);
6ca00197
PB
149}
150
4244db1b
PB
151static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
152 u8 max_be, u8 retries)
153{
a5e1ec53 154 struct ieee802154_local *local = wpan_phy_priv(phy);
4244db1b 155
a5e1ec53 156 return local->ops->set_csma_params(&local->hw, min_be, max_be, retries);
4244db1b
PB
157}
158
159static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
160{
a5e1ec53 161 struct ieee802154_local *local = wpan_phy_priv(phy);
4244db1b 162
a5e1ec53 163 return local->ops->set_frame_retries(&local->hw, retries);
4244db1b
PB
164}
165
c5c47e67
AA
166static void ieee802154_tasklet_handler(unsigned long data)
167{
168 struct ieee802154_local *local = (struct ieee802154_local *)data;
169 struct sk_buff *skb;
170
171 while ((skb = skb_dequeue(&local->skb_queue))) {
172 switch (skb->pkt_type) {
173 case IEEE802154_RX_MSG:
174 /* Clear skb->pkt_type in order to not confuse kernel
175 * netstack.
176 */
177 skb->pkt_type = 0;
178 ieee802154_rx(&local->hw, skb);
179 break;
180 default:
181 WARN(1, "mac802154: Packet is of unknown type %d\n",
182 skb->pkt_type);
183 kfree_skb(skb);
184 break;
185 }
186 }
187}
188
5a504397
AA
189struct ieee802154_hw *
190ieee802154_alloc_hw(size_t priv_data_len, struct ieee802154_ops *ops)
1010f540 191{
192 struct wpan_phy *phy;
a5e1ec53 193 struct ieee802154_local *local;
1010f540 194 size_t priv_size;
195
ed0a5dce
AA
196 if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
197 !ops->start || !ops->stop || !ops->set_channel) {
83a1a7ce 198 pr_err("undefined IEEE802.15.4 device operations\n");
1010f540 199 return NULL;
200 }
201
202 /* Ensure 32-byte alignment of our private data and hw private data.
a5e1ec53 203 * We use the wpan_phy priv data for both our ieee802154_local and for
1010f540 204 * the driver's private data
205 *
206 * in memory it'll be like this:
207 *
a5e1ec53
AA
208 * +-------------------------+
209 * | struct wpan_phy |
210 * +-------------------------+
211 * | struct ieee802154_local |
212 * +-------------------------+
213 * | driver's private data |
214 * +-------------------------+
1010f540 215 *
216 * Due to ieee802154 layer isn't aware of driver and MAC structures,
139f14ad 217 * so lets align them here.
1010f540 218 */
219
a5e1ec53 220 priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
1010f540 221
222 phy = wpan_phy_alloc(priv_size);
223 if (!phy) {
83a1a7ce 224 pr_err("failure to allocate master IEEE802.15.4 device\n");
1010f540 225 return NULL;
226 }
227
a5e1ec53
AA
228 local = wpan_phy_priv(phy);
229 local->phy = phy;
230 local->hw.phy = local->phy;
231 local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
232 local->ops = ops;
1010f540 233
d98be45b
AA
234 INIT_LIST_HEAD(&local->interfaces);
235 mutex_init(&local->iflist_mtx);
1010f540 236
c5c47e67
AA
237 tasklet_init(&local->tasklet,
238 ieee802154_tasklet_handler,
239 (unsigned long)local);
240
241 skb_queue_head_init(&local->skb_queue);
242
a5e1ec53 243 return &local->hw;
1010f540 244}
5a504397 245EXPORT_SYMBOL(ieee802154_alloc_hw);
1010f540 246
5a504397 247void ieee802154_free_hw(struct ieee802154_hw *hw)
1010f540 248{
60741361 249 struct ieee802154_local *local = hw_to_local(hw);
1010f540 250
d98be45b 251 BUG_ON(!list_empty(&local->interfaces));
62610ad2 252
d98be45b 253 mutex_destroy(&local->iflist_mtx);
1e9f9545 254
a5e1ec53 255 wpan_phy_free(local->phy);
1010f540 256}
5a504397 257EXPORT_SYMBOL(ieee802154_free_hw);
1010f540 258
5a504397 259int ieee802154_register_hw(struct ieee802154_hw *hw)
1010f540 260{
60741361 261 struct ieee802154_local *local = hw_to_local(hw);
640985ec
AA
262 int rc = -ENOSYS;
263
5a504397 264 if (hw->flags & IEEE802154_HW_TXPOWER) {
a5e1ec53 265 if (!local->ops->set_txpower)
640985ec
AA
266 goto out;
267
a5e1ec53 268 local->phy->set_txpower = mac802154_set_txpower;
640985ec
AA
269 }
270
5a504397 271 if (hw->flags & IEEE802154_HW_LBT) {
a5e1ec53 272 if (!local->ops->set_lbt)
640985ec
AA
273 goto out;
274
a5e1ec53 275 local->phy->set_lbt = mac802154_set_lbt;
640985ec
AA
276 }
277
5a504397 278 if (hw->flags & IEEE802154_HW_CCA_MODE) {
a5e1ec53 279 if (!local->ops->set_cca_mode)
640985ec
AA
280 goto out;
281
a5e1ec53 282 local->phy->set_cca_mode = mac802154_set_cca_mode;
640985ec
AA
283 }
284
5a504397 285 if (hw->flags & IEEE802154_HW_CCA_ED_LEVEL) {
a5e1ec53 286 if (!local->ops->set_cca_ed_level)
640985ec
AA
287 goto out;
288
a5e1ec53 289 local->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
640985ec
AA
290 }
291
5a504397 292 if (hw->flags & IEEE802154_HW_CSMA_PARAMS) {
a5e1ec53 293 if (!local->ops->set_csma_params)
640985ec
AA
294 goto out;
295
a5e1ec53 296 local->phy->set_csma_params = mac802154_set_csma_params;
640985ec
AA
297 }
298
5a504397 299 if (hw->flags & IEEE802154_HW_FRAME_RETRIES) {
a5e1ec53 300 if (!local->ops->set_frame_retries)
640985ec
AA
301 goto out;
302
a5e1ec53 303 local->phy->set_frame_retries = mac802154_set_frame_retries;
640985ec 304 }
1010f540 305
f7730542 306 local->workqueue =
a5e1ec53 307 create_singlethread_workqueue(wpan_phy_name(local->phy));
f7730542 308 if (!local->workqueue) {
640985ec 309 rc = -ENOMEM;
1010f540 310 goto out;
640985ec 311 }
1010f540 312
a5e1ec53 313 wpan_phy_set_dev(local->phy, local->hw.parent);
1010f540 314
a5e1ec53
AA
315 local->phy->add_iface = mac802154_add_iface;
316 local->phy->del_iface = mac802154_del_iface;
62610ad2 317
a5e1ec53 318 rc = wpan_phy_register(local->phy);
1010f540 319 if (rc < 0)
320 goto out_wq;
321
322 rtnl_lock();
323
d98be45b 324 mutex_lock(&local->iflist_mtx);
a5e1ec53 325 local->running = MAC802154_DEVICE_RUN;
d98be45b 326 mutex_unlock(&local->iflist_mtx);
1010f540 327
328 rtnl_unlock();
329
330 return 0;
331
332out_wq:
f7730542 333 destroy_workqueue(local->workqueue);
1010f540 334out:
335 return rc;
336}
5a504397 337EXPORT_SYMBOL(ieee802154_register_hw);
1010f540 338
5a504397 339void ieee802154_unregister_hw(struct ieee802154_hw *hw)
1010f540 340{
60741361 341 struct ieee802154_local *local = hw_to_local(hw);
036562f9 342 struct ieee802154_sub_if_data *sdata, *next;
1010f540 343
c5c47e67 344 tasklet_kill(&local->tasklet);
f7730542
AA
345 flush_workqueue(local->workqueue);
346 destroy_workqueue(local->workqueue);
1010f540 347
348 rtnl_lock();
349
d98be45b 350 mutex_lock(&local->iflist_mtx);
a5e1ec53 351 local->running = MAC802154_DEVICE_STOPPED;
d98be45b 352 mutex_unlock(&local->iflist_mtx);
1010f540 353
d98be45b
AA
354 list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
355 mutex_lock(&sdata->local->iflist_mtx);
62610ad2 356 list_del(&sdata->list);
d98be45b 357 mutex_unlock(&sdata->local->iflist_mtx);
62610ad2 358
359 unregister_netdevice(sdata->dev);
360 }
361
1010f540 362 rtnl_unlock();
363
a5e1ec53 364 wpan_phy_unregister(local->phy);
1010f540 365}
5a504397 366EXPORT_SYMBOL(ieee802154_unregister_hw);
1010f540 367
368MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
369MODULE_LICENSE("GPL v2");