Merge branch 'iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[cascardo/linux.git] / net / ieee802154 / core.c
1 /*
2  * Copyright (C) 2007, 2008, 2009 Siemens AG
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14
15 #include <linux/slab.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/device.h>
19
20 #include <net/cfg802154.h>
21 #include <net/rtnetlink.h>
22
23 #include "ieee802154.h"
24 #include "nl802154.h"
25 #include "sysfs.h"
26 #include "core.h"
27
28 /* RCU-protected (and RTNL for writers) */
29 LIST_HEAD(cfg802154_rdev_list);
30 int cfg802154_rdev_list_generation;
31
32 static int wpan_phy_match(struct device *dev, const void *data)
33 {
34         return !strcmp(dev_name(dev), (const char *)data);
35 }
36
37 struct wpan_phy *wpan_phy_find(const char *str)
38 {
39         struct device *dev;
40
41         if (WARN_ON(!str))
42                 return NULL;
43
44         dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
45         if (!dev)
46                 return NULL;
47
48         return container_of(dev, struct wpan_phy, dev);
49 }
50 EXPORT_SYMBOL(wpan_phy_find);
51
52 struct wpan_phy_iter_data {
53         int (*fn)(struct wpan_phy *phy, void *data);
54         void *data;
55 };
56
57 static int wpan_phy_iter(struct device *dev, void *_data)
58 {
59         struct wpan_phy_iter_data *wpid = _data;
60         struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
61
62         return wpid->fn(phy, wpid->data);
63 }
64
65 int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
66                       void *data)
67 {
68         struct wpan_phy_iter_data wpid = {
69                 .fn = fn,
70                 .data = data,
71         };
72
73         return class_for_each_device(&wpan_phy_class, NULL,
74                         &wpid, wpan_phy_iter);
75 }
76 EXPORT_SYMBOL(wpan_phy_for_each);
77
78 struct cfg802154_registered_device *
79 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
80 {
81         struct cfg802154_registered_device *result = NULL, *rdev;
82
83         ASSERT_RTNL();
84
85         list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
86                 if (rdev->wpan_phy_idx == wpan_phy_idx) {
87                         result = rdev;
88                         break;
89                 }
90         }
91
92         return result;
93 }
94
95 struct wpan_phy *
96 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
97 {
98         static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
99         struct cfg802154_registered_device *rdev;
100         size_t alloc_size;
101
102         alloc_size = sizeof(*rdev) + priv_size;
103         rdev = kzalloc(alloc_size, GFP_KERNEL);
104         if (!rdev)
105                 return NULL;
106
107         rdev->ops = ops;
108
109         rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
110
111         if (unlikely(rdev->wpan_phy_idx < 0)) {
112                 /* ugh, wrapped! */
113                 atomic_dec(&wpan_phy_counter);
114                 kfree(rdev);
115                 return NULL;
116         }
117
118         /* atomic_inc_return makes it start at 1, make it start at 0 */
119         rdev->wpan_phy_idx--;
120
121         mutex_init(&rdev->wpan_phy.pib_lock);
122
123         INIT_LIST_HEAD(&rdev->wpan_dev_list);
124         device_initialize(&rdev->wpan_phy.dev);
125         dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
126
127         rdev->wpan_phy.dev.class = &wpan_phy_class;
128         rdev->wpan_phy.dev.platform_data = rdev;
129
130         init_waitqueue_head(&rdev->dev_wait);
131
132         return &rdev->wpan_phy;
133 }
134 EXPORT_SYMBOL(wpan_phy_new);
135
136 int wpan_phy_register(struct wpan_phy *phy)
137 {
138         struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
139         int ret;
140
141         rtnl_lock();
142         ret = device_add(&phy->dev);
143         if (ret) {
144                 rtnl_unlock();
145                 return ret;
146         }
147
148         list_add_rcu(&rdev->list, &cfg802154_rdev_list);
149         cfg802154_rdev_list_generation++;
150
151         /* TODO phy registered lock */
152         rtnl_unlock();
153
154         /* TODO nl802154 phy notify */
155
156         return 0;
157 }
158 EXPORT_SYMBOL(wpan_phy_register);
159
160 void wpan_phy_unregister(struct wpan_phy *phy)
161 {
162         struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
163
164         wait_event(rdev->dev_wait, ({
165                 int __count;
166                 rtnl_lock();
167                 __count = rdev->opencount;
168                 rtnl_unlock();
169                 __count == 0; }));
170
171         rtnl_lock();
172         /* TODO nl802154 phy notify */
173         /* TODO phy registered lock */
174
175         WARN_ON(!list_empty(&rdev->wpan_dev_list));
176
177         /* First remove the hardware from everywhere, this makes
178          * it impossible to find from userspace.
179          */
180         list_del_rcu(&rdev->list);
181         synchronize_rcu();
182
183         cfg802154_rdev_list_generation++;
184
185         device_del(&phy->dev);
186
187         rtnl_unlock();
188 }
189 EXPORT_SYMBOL(wpan_phy_unregister);
190
191 void wpan_phy_free(struct wpan_phy *phy)
192 {
193         put_device(&phy->dev);
194 }
195 EXPORT_SYMBOL(wpan_phy_free);
196
197 void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
198 {
199         kfree(rdev);
200 }
201
202 static void
203 cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
204                            int iftype, int num)
205 {
206         ASSERT_RTNL();
207
208         rdev->num_running_ifaces += num;
209 }
210
211 static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
212                                           unsigned long state, void *ptr)
213 {
214         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
215         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
216         struct cfg802154_registered_device *rdev;
217
218         if (!wpan_dev)
219                 return NOTIFY_DONE;
220
221         rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
222
223         /* TODO WARN_ON unspec type */
224
225         switch (state) {
226                 /* TODO NETDEV_DEVTYPE */
227         case NETDEV_REGISTER:
228                 wpan_dev->identifier = ++rdev->wpan_dev_id;
229                 list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
230                 rdev->devlist_generation++;
231
232                 wpan_dev->netdev = dev;
233                 break;
234         case NETDEV_DOWN:
235                 cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
236
237                 rdev->opencount--;
238                 wake_up(&rdev->dev_wait);
239                 break;
240         case NETDEV_UP:
241                 cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
242
243                 rdev->opencount++;
244                 break;
245         case NETDEV_UNREGISTER:
246                 /* It is possible to get NETDEV_UNREGISTER
247                  * multiple times. To detect that, check
248                  * that the interface is still on the list
249                  * of registered interfaces, and only then
250                  * remove and clean it up.
251                  */
252                 if (!list_empty(&wpan_dev->list)) {
253                         list_del_rcu(&wpan_dev->list);
254                         rdev->devlist_generation++;
255                 }
256                 /* synchronize (so that we won't find this netdev
257                  * from other code any more) and then clear the list
258                  * head so that the above code can safely check for
259                  * !list_empty() to avoid double-cleanup.
260                  */
261                 synchronize_rcu();
262                 INIT_LIST_HEAD(&wpan_dev->list);
263                 break;
264         default:
265                 return NOTIFY_DONE;
266         }
267
268         return NOTIFY_OK;
269 }
270
271 static struct notifier_block cfg802154_netdev_notifier = {
272         .notifier_call = cfg802154_netdev_notifier_call,
273 };
274
275 static int __init wpan_phy_class_init(void)
276 {
277         int rc;
278
279         rc = wpan_phy_sysfs_init();
280         if (rc)
281                 goto err;
282
283         rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
284         if (rc)
285                 goto err_nl;
286
287         rc = ieee802154_nl_init();
288         if (rc)
289                 goto err_notifier;
290
291         rc = nl802154_init();
292         if (rc)
293                 goto err_ieee802154_nl;
294
295         return 0;
296
297 err_ieee802154_nl:
298         ieee802154_nl_exit();
299
300 err_notifier:
301         unregister_netdevice_notifier(&cfg802154_netdev_notifier);
302 err_nl:
303         wpan_phy_sysfs_exit();
304 err:
305         return rc;
306 }
307 subsys_initcall(wpan_phy_class_init);
308
309 static void __exit wpan_phy_class_exit(void)
310 {
311         nl802154_exit();
312         ieee802154_nl_exit();
313         unregister_netdevice_notifier(&cfg802154_netdev_notifier);
314         wpan_phy_sysfs_exit();
315 }
316 module_exit(wpan_phy_class_exit);
317
318 MODULE_LICENSE("GPL v2");
319 MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
320 MODULE_AUTHOR("Dmitry Eremin-Solenikov");
321