blob: 785abb1aafb440e5f8f3ac56e1e8c2aa87ec17ed [file] [log] [blame]
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +00001/*
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.
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +000017 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/netdevice.h>
22
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000023#include <net/netlink.h>
24#include <linux/nl802154.h>
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +000025#include <net/mac802154.h>
Phoebe Buckheisterb70ab2e2014-03-14 21:23:59 +010026#include <net/ieee802154_netdev.h>
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +000027#include <net/route.h>
Alexander Aring5ad60d32014-10-25 09:41:02 +020028#include <net/cfg802154.h>
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +000029
Alexander Aring0f1556b2014-10-25 09:41:00 +020030#include "ieee802154_i.h"
Alexander Aring1201cd22014-11-02 04:18:36 +010031#include "cfg.h"
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +000032
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000033static int
34mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
35{
Alexander Aring59d19cd2014-10-25 17:16:40 +020036 struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
Alexander Aringa5e1ec52014-10-25 17:16:35 +020037 struct ieee802154_local *local;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000038 int err;
39
Alexander Aringa5e1ec52014-10-25 17:16:35 +020040 local = wpan_phy_priv(phy);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000041
Alexander Aring036562f2014-10-25 17:16:36 +020042 sdata->dev = dev;
Alexander Aring04e850f2014-10-25 17:16:37 +020043 sdata->local = local;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000044
Alexander Aringa5e1ec52014-10-25 17:16:35 +020045 dev->needed_headroom = local->hw.extra_tx_headroom;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000046
Alexander Aringa5e1ec52014-10-25 17:16:35 +020047 SET_NETDEV_DEV(dev, &local->phy->dev);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000048
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000049 err = register_netdev(dev);
50 if (err < 0)
51 return err;
52
53 rtnl_lock();
Alexander Aringd98be452014-10-25 17:16:38 +020054 mutex_lock(&local->iflist_mtx);
55 list_add_tail_rcu(&sdata->list, &local->interfaces);
56 mutex_unlock(&local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000057 rtnl_unlock();
58
59 return 0;
60}
61
62static void
63mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
64{
Alexander Aring59d19cd2014-10-25 17:16:40 +020065 struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
Varka Bhadram4710d802014-07-02 09:01:09 +053066
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000067 ASSERT_RTNL();
68
Alexander Aring04e850f2014-10-25 17:16:37 +020069 BUG_ON(sdata->local->phy != phy);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000070
Alexander Aringd98be452014-10-25 17:16:38 +020071 mutex_lock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000072 list_del_rcu(&sdata->list);
Alexander Aringd98be452014-10-25 17:16:38 +020073 mutex_unlock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000074
75 synchronize_rcu();
76 unregister_netdevice(sdata->dev);
77}
78
79static struct net_device *
80mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
81{
82 struct net_device *dev;
83 int err = -ENOMEM;
84
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000085 switch (type) {
alex.bluesman.smirnov@gmail.com06060692012-05-15 20:50:29 +000086 case IEEE802154_DEV_MONITOR:
Alexander Aring036562f2014-10-25 17:16:36 +020087 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
Tom Gundersenc835a672014-07-14 16:37:24 +020088 name, NET_NAME_UNKNOWN,
89 mac802154_monitor_setup);
alex.bluesman.smirnov@gmail.com06060692012-05-15 20:50:29 +000090 break;
alex.bluesman.smirnov@gmail.com32bad7e2012-06-25 23:24:48 +000091 case IEEE802154_DEV_WPAN:
Alexander Aring036562f2014-10-25 17:16:36 +020092 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
Tom Gundersenc835a672014-07-14 16:37:24 +020093 name, NET_NAME_UNKNOWN,
94 mac802154_wpan_setup);
alex.bluesman.smirnov@gmail.com32bad7e2012-06-25 23:24:48 +000095 break;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000096 default:
97 dev = NULL;
98 err = -EINVAL;
99 break;
100 }
101 if (!dev)
102 goto err;
103
104 err = mac802154_netdev_register(phy, dev);
105 if (err)
106 goto err_free;
107
108 dev_hold(dev); /* we return an incremented device refcount */
109 return dev;
110
111err_free:
112 free_netdev(dev);
113err:
114 return ERR_PTR(err);
115}
116
Alexander Aringc5c47e62014-10-27 17:13:30 +0100117static void ieee802154_tasklet_handler(unsigned long data)
118{
119 struct ieee802154_local *local = (struct ieee802154_local *)data;
120 struct sk_buff *skb;
121
122 while ((skb = skb_dequeue(&local->skb_queue))) {
123 switch (skb->pkt_type) {
124 case IEEE802154_RX_MSG:
125 /* Clear skb->pkt_type in order to not confuse kernel
126 * netstack.
127 */
128 skb->pkt_type = 0;
129 ieee802154_rx(&local->hw, skb);
130 break;
131 default:
132 WARN(1, "mac802154: Packet is of unknown type %d\n",
133 skb->pkt_type);
134 kfree_skb(skb);
135 break;
136 }
137 }
138}
139
Alexander Aring5a504392014-10-25 17:16:34 +0200140struct ieee802154_hw *
Alexander Aring16301862014-10-28 18:21:18 +0100141ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000142{
143 struct wpan_phy *phy;
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200144 struct ieee802154_local *local;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000145 size_t priv_size;
146
Alexander Aringed0a5dc2014-10-26 09:37:08 +0100147 if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
148 !ops->start || !ops->stop || !ops->set_channel) {
Chen Weilong83a1a7c2013-10-30 15:28:07 +0800149 pr_err("undefined IEEE802.15.4 device operations\n");
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000150 return NULL;
151 }
152
153 /* Ensure 32-byte alignment of our private data and hw private data.
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200154 * We use the wpan_phy priv data for both our ieee802154_local and for
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000155 * the driver's private data
156 *
157 * in memory it'll be like this:
158 *
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200159 * +-------------------------+
160 * | struct wpan_phy |
161 * +-------------------------+
162 * | struct ieee802154_local |
163 * +-------------------------+
164 * | driver's private data |
165 * +-------------------------+
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000166 *
167 * Due to ieee802154 layer isn't aware of driver and MAC structures,
Alexander Aring139f14a2014-10-25 05:25:08 +0200168 * so lets align them here.
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000169 */
170
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200171 priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000172
Alexander Aring1201cd22014-11-02 04:18:36 +0100173 phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000174 if (!phy) {
Chen Weilong83a1a7c2013-10-30 15:28:07 +0800175 pr_err("failure to allocate master IEEE802.15.4 device\n");
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000176 return NULL;
177 }
178
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200179 local = wpan_phy_priv(phy);
180 local->phy = phy;
181 local->hw.phy = local->phy;
182 local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
183 local->ops = ops;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000184
Alexander Aringd98be452014-10-25 17:16:38 +0200185 INIT_LIST_HEAD(&local->interfaces);
186 mutex_init(&local->iflist_mtx);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000187
Alexander Aringc5c47e62014-10-27 17:13:30 +0100188 tasklet_init(&local->tasklet,
189 ieee802154_tasklet_handler,
190 (unsigned long)local);
191
192 skb_queue_head_init(&local->skb_queue);
193
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200194 return &local->hw;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000195}
Alexander Aring5a504392014-10-25 17:16:34 +0200196EXPORT_SYMBOL(ieee802154_alloc_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000197
Alexander Aring5a504392014-10-25 17:16:34 +0200198void ieee802154_free_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000199{
Alexander Aring60741362014-10-25 17:16:39 +0200200 struct ieee802154_local *local = hw_to_local(hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000201
Alexander Aringd98be452014-10-25 17:16:38 +0200202 BUG_ON(!list_empty(&local->interfaces));
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000203
Alexander Aringd98be452014-10-25 17:16:38 +0200204 mutex_destroy(&local->iflist_mtx);
Konstantin Khlebnikov1e9f9542012-12-14 01:03:03 +0000205
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200206 wpan_phy_free(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000207}
Alexander Aring5a504392014-10-25 17:16:34 +0200208EXPORT_SYMBOL(ieee802154_free_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000209
Alexander Aring5a504392014-10-25 17:16:34 +0200210int ieee802154_register_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000211{
Alexander Aring60741362014-10-25 17:16:39 +0200212 struct ieee802154_local *local = hw_to_local(hw);
Alexander Aring640985e2014-07-03 00:20:43 +0200213 int rc = -ENOSYS;
214
Alexander Aringf7730542014-10-25 17:16:41 +0200215 local->workqueue =
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200216 create_singlethread_workqueue(wpan_phy_name(local->phy));
Alexander Aringf7730542014-10-25 17:16:41 +0200217 if (!local->workqueue) {
Alexander Aring640985e2014-07-03 00:20:43 +0200218 rc = -ENOMEM;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000219 goto out;
Alexander Aring640985e2014-07-03 00:20:43 +0200220 }
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000221
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200222 wpan_phy_set_dev(local->phy, local->hw.parent);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000223
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200224 local->phy->add_iface = mac802154_add_iface;
225 local->phy->del_iface = mac802154_del_iface;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000226
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200227 rc = wpan_phy_register(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000228 if (rc < 0)
229 goto out_wq;
230
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000231 return 0;
232
233out_wq:
Alexander Aringf7730542014-10-25 17:16:41 +0200234 destroy_workqueue(local->workqueue);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000235out:
236 return rc;
237}
Alexander Aring5a504392014-10-25 17:16:34 +0200238EXPORT_SYMBOL(ieee802154_register_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000239
Alexander Aring5a504392014-10-25 17:16:34 +0200240void ieee802154_unregister_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000241{
Alexander Aring60741362014-10-25 17:16:39 +0200242 struct ieee802154_local *local = hw_to_local(hw);
Alexander Aring036562f2014-10-25 17:16:36 +0200243 struct ieee802154_sub_if_data *sdata, *next;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000244
Alexander Aringc5c47e62014-10-27 17:13:30 +0100245 tasklet_kill(&local->tasklet);
Alexander Aringf7730542014-10-25 17:16:41 +0200246 flush_workqueue(local->workqueue);
247 destroy_workqueue(local->workqueue);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000248
249 rtnl_lock();
250
Alexander Aringd98be452014-10-25 17:16:38 +0200251 list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
252 mutex_lock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000253 list_del(&sdata->list);
Alexander Aringd98be452014-10-25 17:16:38 +0200254 mutex_unlock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000255
256 unregister_netdevice(sdata->dev);
257 }
258
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000259 rtnl_unlock();
260
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200261 wpan_phy_unregister(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000262}
Alexander Aring5a504392014-10-25 17:16:34 +0200263EXPORT_SYMBOL(ieee802154_unregister_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000264
265MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
266MODULE_LICENSE("GPL v2");