treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 174
[sfrench/cifs-2.6.git] / net / ieee802154 / core.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2007, 2008, 2009 Siemens AG
4  */
5
6 #include <linux/slab.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/device.h>
10
11 #include <net/cfg802154.h>
12 #include <net/rtnetlink.h>
13
14 #include "ieee802154.h"
15 #include "nl802154.h"
16 #include "sysfs.h"
17 #include "core.h"
18
19 /* name for sysfs, %d is appended */
20 #define PHY_NAME "phy"
21
22 /* RCU-protected (and RTNL for writers) */
23 LIST_HEAD(cfg802154_rdev_list);
24 int cfg802154_rdev_list_generation;
25
26 static int wpan_phy_match(struct device *dev, const void *data)
27 {
28         return !strcmp(dev_name(dev), (const char *)data);
29 }
30
31 struct wpan_phy *wpan_phy_find(const char *str)
32 {
33         struct device *dev;
34
35         if (WARN_ON(!str))
36                 return NULL;
37
38         dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
39         if (!dev)
40                 return NULL;
41
42         return container_of(dev, struct wpan_phy, dev);
43 }
44 EXPORT_SYMBOL(wpan_phy_find);
45
46 struct wpan_phy_iter_data {
47         int (*fn)(struct wpan_phy *phy, void *data);
48         void *data;
49 };
50
51 static int wpan_phy_iter(struct device *dev, void *_data)
52 {
53         struct wpan_phy_iter_data *wpid = _data;
54         struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
55
56         return wpid->fn(phy, wpid->data);
57 }
58
59 int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
60                       void *data)
61 {
62         struct wpan_phy_iter_data wpid = {
63                 .fn = fn,
64                 .data = data,
65         };
66
67         return class_for_each_device(&wpan_phy_class, NULL,
68                         &wpid, wpan_phy_iter);
69 }
70 EXPORT_SYMBOL(wpan_phy_for_each);
71
72 struct cfg802154_registered_device *
73 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
74 {
75         struct cfg802154_registered_device *result = NULL, *rdev;
76
77         ASSERT_RTNL();
78
79         list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
80                 if (rdev->wpan_phy_idx == wpan_phy_idx) {
81                         result = rdev;
82                         break;
83                 }
84         }
85
86         return result;
87 }
88
89 struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
90 {
91         struct cfg802154_registered_device *rdev;
92
93         ASSERT_RTNL();
94
95         rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
96         if (!rdev)
97                 return NULL;
98         return &rdev->wpan_phy;
99 }
100
101 struct wpan_phy *
102 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
103 {
104         static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
105         struct cfg802154_registered_device *rdev;
106         size_t alloc_size;
107
108         alloc_size = sizeof(*rdev) + priv_size;
109         rdev = kzalloc(alloc_size, GFP_KERNEL);
110         if (!rdev)
111                 return NULL;
112
113         rdev->ops = ops;
114
115         rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
116
117         if (unlikely(rdev->wpan_phy_idx < 0)) {
118                 /* ugh, wrapped! */
119                 atomic_dec(&wpan_phy_counter);
120                 kfree(rdev);
121                 return NULL;
122         }
123
124         /* atomic_inc_return makes it start at 1, make it start at 0 */
125         rdev->wpan_phy_idx--;
126
127         INIT_LIST_HEAD(&rdev->wpan_dev_list);
128         device_initialize(&rdev->wpan_phy.dev);
129         dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
130
131         rdev->wpan_phy.dev.class = &wpan_phy_class;
132         rdev->wpan_phy.dev.platform_data = rdev;
133
134         wpan_phy_net_set(&rdev->wpan_phy, &init_net);
135
136         init_waitqueue_head(&rdev->dev_wait);
137
138         return &rdev->wpan_phy;
139 }
140 EXPORT_SYMBOL(wpan_phy_new);
141
142 int wpan_phy_register(struct wpan_phy *phy)
143 {
144         struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
145         int ret;
146
147         rtnl_lock();
148         ret = device_add(&phy->dev);
149         if (ret) {
150                 rtnl_unlock();
151                 return ret;
152         }
153
154         list_add_rcu(&rdev->list, &cfg802154_rdev_list);
155         cfg802154_rdev_list_generation++;
156
157         /* TODO phy registered lock */
158         rtnl_unlock();
159
160         /* TODO nl802154 phy notify */
161
162         return 0;
163 }
164 EXPORT_SYMBOL(wpan_phy_register);
165
166 void wpan_phy_unregister(struct wpan_phy *phy)
167 {
168         struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
169
170         wait_event(rdev->dev_wait, ({
171                 int __count;
172                 rtnl_lock();
173                 __count = rdev->opencount;
174                 rtnl_unlock();
175                 __count == 0; }));
176
177         rtnl_lock();
178         /* TODO nl802154 phy notify */
179         /* TODO phy registered lock */
180
181         WARN_ON(!list_empty(&rdev->wpan_dev_list));
182
183         /* First remove the hardware from everywhere, this makes
184          * it impossible to find from userspace.
185          */
186         list_del_rcu(&rdev->list);
187         synchronize_rcu();
188
189         cfg802154_rdev_list_generation++;
190
191         device_del(&phy->dev);
192
193         rtnl_unlock();
194 }
195 EXPORT_SYMBOL(wpan_phy_unregister);
196
197 void wpan_phy_free(struct wpan_phy *phy)
198 {
199         put_device(&phy->dev);
200 }
201 EXPORT_SYMBOL(wpan_phy_free);
202
203 int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
204                            struct net *net)
205 {
206         struct wpan_dev *wpan_dev;
207         int err = 0;
208
209         list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
210                 if (!wpan_dev->netdev)
211                         continue;
212                 wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
213                 err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d");
214                 if (err)
215                         break;
216                 wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
217         }
218
219         if (err) {
220                 /* failed -- clean up to old netns */
221                 net = wpan_phy_net(&rdev->wpan_phy);
222
223                 list_for_each_entry_continue_reverse(wpan_dev,
224                                                      &rdev->wpan_dev_list,
225                                                      list) {
226                         if (!wpan_dev->netdev)
227                                 continue;
228                         wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
229                         err = dev_change_net_namespace(wpan_dev->netdev, net,
230                                                        "wpan%d");
231                         WARN_ON(err);
232                         wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
233                 }
234
235                 return err;
236         }
237
238         wpan_phy_net_set(&rdev->wpan_phy, net);
239
240         err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev));
241         WARN_ON(err);
242
243         return 0;
244 }
245
246 void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
247 {
248         kfree(rdev);
249 }
250
251 static void
252 cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
253                            int iftype, int num)
254 {
255         ASSERT_RTNL();
256
257         rdev->num_running_ifaces += num;
258 }
259
260 static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
261                                           unsigned long state, void *ptr)
262 {
263         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
264         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
265         struct cfg802154_registered_device *rdev;
266
267         if (!wpan_dev)
268                 return NOTIFY_DONE;
269
270         rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
271
272         /* TODO WARN_ON unspec type */
273
274         switch (state) {
275                 /* TODO NETDEV_DEVTYPE */
276         case NETDEV_REGISTER:
277                 dev->features |= NETIF_F_NETNS_LOCAL;
278                 wpan_dev->identifier = ++rdev->wpan_dev_id;
279                 list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
280                 rdev->devlist_generation++;
281
282                 wpan_dev->netdev = dev;
283                 break;
284         case NETDEV_DOWN:
285                 cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
286
287                 rdev->opencount--;
288                 wake_up(&rdev->dev_wait);
289                 break;
290         case NETDEV_UP:
291                 cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
292
293                 rdev->opencount++;
294                 break;
295         case NETDEV_UNREGISTER:
296                 /* It is possible to get NETDEV_UNREGISTER
297                  * multiple times. To detect that, check
298                  * that the interface is still on the list
299                  * of registered interfaces, and only then
300                  * remove and clean it up.
301                  */
302                 if (!list_empty(&wpan_dev->list)) {
303                         list_del_rcu(&wpan_dev->list);
304                         rdev->devlist_generation++;
305                 }
306                 /* synchronize (so that we won't find this netdev
307                  * from other code any more) and then clear the list
308                  * head so that the above code can safely check for
309                  * !list_empty() to avoid double-cleanup.
310                  */
311                 synchronize_rcu();
312                 INIT_LIST_HEAD(&wpan_dev->list);
313                 break;
314         default:
315                 return NOTIFY_DONE;
316         }
317
318         return NOTIFY_OK;
319 }
320
321 static struct notifier_block cfg802154_netdev_notifier = {
322         .notifier_call = cfg802154_netdev_notifier_call,
323 };
324
325 static void __net_exit cfg802154_pernet_exit(struct net *net)
326 {
327         struct cfg802154_registered_device *rdev;
328
329         rtnl_lock();
330         list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
331                 if (net_eq(wpan_phy_net(&rdev->wpan_phy), net))
332                         WARN_ON(cfg802154_switch_netns(rdev, &init_net));
333         }
334         rtnl_unlock();
335 }
336
337 static struct pernet_operations cfg802154_pernet_ops = {
338         .exit = cfg802154_pernet_exit,
339 };
340
341 static int __init wpan_phy_class_init(void)
342 {
343         int rc;
344
345         rc = register_pernet_device(&cfg802154_pernet_ops);
346         if (rc)
347                 goto err;
348
349         rc = wpan_phy_sysfs_init();
350         if (rc)
351                 goto err_sysfs;
352
353         rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
354         if (rc)
355                 goto err_nl;
356
357         rc = ieee802154_nl_init();
358         if (rc)
359                 goto err_notifier;
360
361         rc = nl802154_init();
362         if (rc)
363                 goto err_ieee802154_nl;
364
365         return 0;
366
367 err_ieee802154_nl:
368         ieee802154_nl_exit();
369
370 err_notifier:
371         unregister_netdevice_notifier(&cfg802154_netdev_notifier);
372 err_nl:
373         wpan_phy_sysfs_exit();
374 err_sysfs:
375         unregister_pernet_device(&cfg802154_pernet_ops);
376 err:
377         return rc;
378 }
379 subsys_initcall(wpan_phy_class_init);
380
381 static void __exit wpan_phy_class_exit(void)
382 {
383         nl802154_exit();
384         ieee802154_nl_exit();
385         unregister_netdevice_notifier(&cfg802154_netdev_notifier);
386         wpan_phy_sysfs_exit();
387         unregister_pernet_device(&cfg802154_pernet_ops);
388 }
389 module_exit(wpan_phy_class_exit);
390
391 MODULE_LICENSE("GPL v2");
392 MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
393 MODULE_AUTHOR("Dmitry Eremin-Solenikov");