blob: b34ddbf43c3d9cf053d1762d5794748af0cc909c [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
Alexander Aring4a9a8162014-11-02 04:18:38 +010062void mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000063{
Alexander Aring59d19cd2014-10-25 17:16:40 +020064 struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
Varka Bhadram4710d802014-07-02 09:01:09 +053065
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000066 ASSERT_RTNL();
67
Alexander Aring04e850f2014-10-25 17:16:37 +020068 BUG_ON(sdata->local->phy != phy);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000069
Alexander Aringd98be452014-10-25 17:16:38 +020070 mutex_lock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000071 list_del_rcu(&sdata->list);
Alexander Aringd98be452014-10-25 17:16:38 +020072 mutex_unlock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000073
74 synchronize_rcu();
75 unregister_netdevice(sdata->dev);
76}
77
Alexander Aring4a9a8162014-11-02 04:18:38 +010078struct net_device *
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000079mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
80{
81 struct net_device *dev;
82 int err = -ENOMEM;
83
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000084 switch (type) {
alex.bluesman.smirnov@gmail.com06060692012-05-15 20:50:29 +000085 case IEEE802154_DEV_MONITOR:
Alexander Aring036562f2014-10-25 17:16:36 +020086 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
Tom Gundersenc835a672014-07-14 16:37:24 +020087 name, NET_NAME_UNKNOWN,
88 mac802154_monitor_setup);
alex.bluesman.smirnov@gmail.com06060692012-05-15 20:50:29 +000089 break;
alex.bluesman.smirnov@gmail.com32bad7e2012-06-25 23:24:48 +000090 case IEEE802154_DEV_WPAN:
Alexander Aring036562f2014-10-25 17:16:36 +020091 dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
Tom Gundersenc835a672014-07-14 16:37:24 +020092 name, NET_NAME_UNKNOWN,
93 mac802154_wpan_setup);
alex.bluesman.smirnov@gmail.com32bad7e2012-06-25 23:24:48 +000094 break;
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +000095 default:
96 dev = NULL;
97 err = -EINVAL;
98 break;
99 }
100 if (!dev)
101 goto err;
102
103 err = mac802154_netdev_register(phy, dev);
104 if (err)
105 goto err_free;
106
107 dev_hold(dev); /* we return an incremented device refcount */
108 return dev;
109
110err_free:
111 free_netdev(dev);
112err:
113 return ERR_PTR(err);
114}
115
Alexander Aringc5c47e62014-10-27 17:13:30 +0100116static void ieee802154_tasklet_handler(unsigned long data)
117{
118 struct ieee802154_local *local = (struct ieee802154_local *)data;
119 struct sk_buff *skb;
120
121 while ((skb = skb_dequeue(&local->skb_queue))) {
122 switch (skb->pkt_type) {
123 case IEEE802154_RX_MSG:
124 /* Clear skb->pkt_type in order to not confuse kernel
125 * netstack.
126 */
127 skb->pkt_type = 0;
128 ieee802154_rx(&local->hw, skb);
129 break;
130 default:
131 WARN(1, "mac802154: Packet is of unknown type %d\n",
132 skb->pkt_type);
133 kfree_skb(skb);
134 break;
135 }
136 }
137}
138
Alexander Aring5a504392014-10-25 17:16:34 +0200139struct ieee802154_hw *
Alexander Aring16301862014-10-28 18:21:18 +0100140ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000141{
142 struct wpan_phy *phy;
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200143 struct ieee802154_local *local;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000144 size_t priv_size;
145
Alexander Aringed0a5dc2014-10-26 09:37:08 +0100146 if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
147 !ops->start || !ops->stop || !ops->set_channel) {
Chen Weilong83a1a7c2013-10-30 15:28:07 +0800148 pr_err("undefined IEEE802.15.4 device operations\n");
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000149 return NULL;
150 }
151
152 /* Ensure 32-byte alignment of our private data and hw private data.
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200153 * We use the wpan_phy priv data for both our ieee802154_local and for
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000154 * the driver's private data
155 *
156 * in memory it'll be like this:
157 *
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200158 * +-------------------------+
159 * | struct wpan_phy |
160 * +-------------------------+
161 * | struct ieee802154_local |
162 * +-------------------------+
163 * | driver's private data |
164 * +-------------------------+
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000165 *
166 * Due to ieee802154 layer isn't aware of driver and MAC structures,
Alexander Aring139f14a2014-10-25 05:25:08 +0200167 * so lets align them here.
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000168 */
169
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200170 priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000171
Alexander Aring1201cd22014-11-02 04:18:36 +0100172 phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000173 if (!phy) {
Chen Weilong83a1a7c2013-10-30 15:28:07 +0800174 pr_err("failure to allocate master IEEE802.15.4 device\n");
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000175 return NULL;
176 }
177
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200178 local = wpan_phy_priv(phy);
179 local->phy = phy;
180 local->hw.phy = local->phy;
181 local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
182 local->ops = ops;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000183
Alexander Aringd98be452014-10-25 17:16:38 +0200184 INIT_LIST_HEAD(&local->interfaces);
185 mutex_init(&local->iflist_mtx);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000186
Alexander Aringc5c47e62014-10-27 17:13:30 +0100187 tasklet_init(&local->tasklet,
188 ieee802154_tasklet_handler,
189 (unsigned long)local);
190
191 skb_queue_head_init(&local->skb_queue);
192
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200193 return &local->hw;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000194}
Alexander Aring5a504392014-10-25 17:16:34 +0200195EXPORT_SYMBOL(ieee802154_alloc_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000196
Alexander Aring5a504392014-10-25 17:16:34 +0200197void ieee802154_free_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000198{
Alexander Aring60741362014-10-25 17:16:39 +0200199 struct ieee802154_local *local = hw_to_local(hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000200
Alexander Aringd98be452014-10-25 17:16:38 +0200201 BUG_ON(!list_empty(&local->interfaces));
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000202
Alexander Aringd98be452014-10-25 17:16:38 +0200203 mutex_destroy(&local->iflist_mtx);
Konstantin Khlebnikov1e9f9542012-12-14 01:03:03 +0000204
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200205 wpan_phy_free(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000206}
Alexander Aring5a504392014-10-25 17:16:34 +0200207EXPORT_SYMBOL(ieee802154_free_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000208
Alexander Aring5a504392014-10-25 17:16:34 +0200209int ieee802154_register_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000210{
Alexander Aring60741362014-10-25 17:16:39 +0200211 struct ieee802154_local *local = hw_to_local(hw);
Alexander Aring640985e2014-07-03 00:20:43 +0200212 int rc = -ENOSYS;
213
Alexander Aringf7730542014-10-25 17:16:41 +0200214 local->workqueue =
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200215 create_singlethread_workqueue(wpan_phy_name(local->phy));
Alexander Aringf7730542014-10-25 17:16:41 +0200216 if (!local->workqueue) {
Alexander Aring640985e2014-07-03 00:20:43 +0200217 rc = -ENOMEM;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000218 goto out;
Alexander Aring640985e2014-07-03 00:20:43 +0200219 }
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000220
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200221 wpan_phy_set_dev(local->phy, local->hw.parent);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000222
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200223 rc = wpan_phy_register(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000224 if (rc < 0)
225 goto out_wq;
226
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000227 return 0;
228
229out_wq:
Alexander Aringf7730542014-10-25 17:16:41 +0200230 destroy_workqueue(local->workqueue);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000231out:
232 return rc;
233}
Alexander Aring5a504392014-10-25 17:16:34 +0200234EXPORT_SYMBOL(ieee802154_register_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000235
Alexander Aring5a504392014-10-25 17:16:34 +0200236void ieee802154_unregister_hw(struct ieee802154_hw *hw)
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000237{
Alexander Aring60741362014-10-25 17:16:39 +0200238 struct ieee802154_local *local = hw_to_local(hw);
Alexander Aring036562f2014-10-25 17:16:36 +0200239 struct ieee802154_sub_if_data *sdata, *next;
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000240
Alexander Aringc5c47e62014-10-27 17:13:30 +0100241 tasklet_kill(&local->tasklet);
Alexander Aringf7730542014-10-25 17:16:41 +0200242 flush_workqueue(local->workqueue);
243 destroy_workqueue(local->workqueue);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000244
245 rtnl_lock();
246
Alexander Aringd98be452014-10-25 17:16:38 +0200247 list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
248 mutex_lock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000249 list_del(&sdata->list);
Alexander Aringd98be452014-10-25 17:16:38 +0200250 mutex_unlock(&sdata->local->iflist_mtx);
alex.bluesman.smirnov@gmail.com62610ad2012-05-15 20:50:28 +0000251
252 unregister_netdevice(sdata->dev);
253 }
254
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000255 rtnl_unlock();
256
Alexander Aringa5e1ec52014-10-25 17:16:35 +0200257 wpan_phy_unregister(local->phy);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000258}
Alexander Aring5a504392014-10-25 17:16:34 +0200259EXPORT_SYMBOL(ieee802154_unregister_hw);
alex.bluesman.smirnov@gmail.com1010f542012-05-15 20:50:20 +0000260
261MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
262MODULE_LICENSE("GPL v2");