]>
Commit | Line | Data |
---|---|---|
2bfb1070 DES |
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 | * | |
2bfb1070 DES |
13 | */ |
14 | ||
5a0e3ad6 | 15 | #include <linux/slab.h> |
2bfb1070 DES |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | |
18 | #include <linux/device.h> | |
19 | ||
5ad60d36 | 20 | #include <net/cfg802154.h> |
f3ada640 | 21 | #include <net/rtnetlink.h> |
2bfb1070 | 22 | |
cb6b3763 | 23 | #include "ieee802154.h" |
e23e9ec1 | 24 | #include "sysfs.h" |
a5dd1d72 | 25 | #include "core.h" |
2bfb1070 | 26 | |
f3ada640 AA |
27 | /* RCU-protected (and RTNL for writers) */ |
28 | static LIST_HEAD(cfg802154_rdev_list); | |
29 | static int cfg802154_rdev_list_generation; | |
30 | ||
9f3b795a | 31 | static int wpan_phy_match(struct device *dev, const void *data) |
2bfb1070 DES |
32 | { |
33 | return !strcmp(dev_name(dev), (const char *)data); | |
34 | } | |
35 | ||
36 | struct wpan_phy *wpan_phy_find(const char *str) | |
37 | { | |
38 | struct device *dev; | |
39 | ||
40 | if (WARN_ON(!str)) | |
41 | return NULL; | |
42 | ||
9f3b795a | 43 | dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match); |
2bfb1070 DES |
44 | if (!dev) |
45 | return NULL; | |
46 | ||
47 | return container_of(dev, struct wpan_phy, dev); | |
48 | } | |
49 | EXPORT_SYMBOL(wpan_phy_find); | |
50 | ||
1c889f4d DES |
51 | struct wpan_phy_iter_data { |
52 | int (*fn)(struct wpan_phy *phy, void *data); | |
53 | void *data; | |
54 | }; | |
55 | ||
56 | static int wpan_phy_iter(struct device *dev, void *_data) | |
57 | { | |
58 | struct wpan_phy_iter_data *wpid = _data; | |
59 | struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); | |
4710d806 | 60 | |
1c889f4d DES |
61 | return wpid->fn(phy, wpid->data); |
62 | } | |
63 | ||
64 | int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), | |
4710d806 | 65 | void *data) |
1c889f4d DES |
66 | { |
67 | struct wpan_phy_iter_data wpid = { | |
68 | .fn = fn, | |
69 | .data = data, | |
70 | }; | |
71 | ||
72 | return class_for_each_device(&wpan_phy_class, NULL, | |
73 | &wpid, wpan_phy_iter); | |
74 | } | |
75 | EXPORT_SYMBOL(wpan_phy_for_each); | |
76 | ||
a5dd1d72 | 77 | struct wpan_phy * |
f601379f | 78 | wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) |
2bfb1070 | 79 | { |
53f9ee61 | 80 | static atomic_t wpan_phy_counter = ATOMIC_INIT(0); |
a5dd1d72 AA |
81 | struct cfg802154_registered_device *rdev; |
82 | size_t alloc_size; | |
83 | ||
84 | alloc_size = sizeof(*rdev) + priv_size; | |
85 | rdev = kzalloc(alloc_size, GFP_KERNEL); | |
86 | if (!rdev) | |
87 | return NULL; | |
88 | ||
89 | rdev->ops = ops; | |
2bfb1070 | 90 | |
53f9ee61 AA |
91 | rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter); |
92 | ||
93 | if (unlikely(rdev->wpan_phy_idx < 0)) { | |
94 | /* ugh, wrapped! */ | |
95 | atomic_dec(&wpan_phy_counter); | |
a5dd1d72 | 96 | kfree(rdev); |
53f9ee61 | 97 | return NULL; |
2bfb1070 | 98 | } |
53f9ee61 AA |
99 | |
100 | /* atomic_inc_return makes it start at 1, make it start at 0 */ | |
101 | rdev->wpan_phy_idx--; | |
2bfb1070 | 102 | |
a5dd1d72 | 103 | mutex_init(&rdev->wpan_phy.pib_lock); |
2bfb1070 | 104 | |
a5dd1d72 | 105 | device_initialize(&rdev->wpan_phy.dev); |
53f9ee61 | 106 | dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx); |
2bfb1070 | 107 | |
a5dd1d72 AA |
108 | rdev->wpan_phy.dev.class = &wpan_phy_class; |
109 | rdev->wpan_phy.dev.platform_data = rdev; | |
2bfb1070 | 110 | |
a5dd1d72 | 111 | return &rdev->wpan_phy; |
2bfb1070 | 112 | } |
f601379f | 113 | EXPORT_SYMBOL(wpan_phy_new); |
2bfb1070 | 114 | |
e9cf356c | 115 | int wpan_phy_register(struct wpan_phy *phy) |
2bfb1070 | 116 | { |
f3ada640 AA |
117 | struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); |
118 | int ret; | |
119 | ||
120 | rtnl_lock(); | |
121 | ret = device_add(&phy->dev); | |
122 | if (ret) { | |
123 | rtnl_unlock(); | |
124 | return ret; | |
125 | } | |
126 | ||
127 | list_add_rcu(&rdev->list, &cfg802154_rdev_list); | |
128 | cfg802154_rdev_list_generation++; | |
129 | ||
130 | /* TODO phy registered lock */ | |
131 | rtnl_unlock(); | |
132 | ||
133 | /* TODO nl802154 phy notify */ | |
134 | ||
135 | return 0; | |
2bfb1070 DES |
136 | } |
137 | EXPORT_SYMBOL(wpan_phy_register); | |
138 | ||
139 | void wpan_phy_unregister(struct wpan_phy *phy) | |
140 | { | |
f3ada640 AA |
141 | struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); |
142 | ||
143 | /* TODO open count */ | |
144 | ||
145 | rtnl_lock(); | |
146 | /* TODO nl802154 phy notify */ | |
147 | /* TODO phy registered lock */ | |
148 | ||
149 | /* TODO WARN_ON wpan_dev_list */ | |
150 | ||
151 | /* First remove the hardware from everywhere, this makes | |
152 | * it impossible to find from userspace. | |
153 | */ | |
154 | list_del_rcu(&rdev->list); | |
155 | synchronize_rcu(); | |
156 | ||
157 | cfg802154_rdev_list_generation++; | |
158 | ||
2bfb1070 | 159 | device_del(&phy->dev); |
f3ada640 AA |
160 | |
161 | rtnl_unlock(); | |
2bfb1070 DES |
162 | } |
163 | EXPORT_SYMBOL(wpan_phy_unregister); | |
164 | ||
165 | void wpan_phy_free(struct wpan_phy *phy) | |
166 | { | |
167 | put_device(&phy->dev); | |
168 | } | |
169 | EXPORT_SYMBOL(wpan_phy_free); | |
170 | ||
a5dd1d72 AA |
171 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev) |
172 | { | |
173 | kfree(rdev); | |
174 | } | |
175 | ||
2bfb1070 DES |
176 | static int __init wpan_phy_class_init(void) |
177 | { | |
cb6b3763 | 178 | int rc; |
4710d806 | 179 | |
e23e9ec1 | 180 | rc = wpan_phy_sysfs_init(); |
cb6b3763 DES |
181 | if (rc) |
182 | goto err; | |
183 | ||
184 | rc = ieee802154_nl_init(); | |
185 | if (rc) | |
186 | goto err_nl; | |
187 | ||
188 | return 0; | |
189 | err_nl: | |
e23e9ec1 | 190 | wpan_phy_sysfs_exit(); |
cb6b3763 DES |
191 | err: |
192 | return rc; | |
2bfb1070 | 193 | } |
282a3954 | 194 | subsys_initcall(wpan_phy_class_init); |
2bfb1070 DES |
195 | |
196 | static void __exit wpan_phy_class_exit(void) | |
197 | { | |
cb6b3763 | 198 | ieee802154_nl_exit(); |
e23e9ec1 | 199 | wpan_phy_sysfs_exit(); |
2bfb1070 DES |
200 | } |
201 | module_exit(wpan_phy_class_exit); | |
202 | ||
2bfb1070 | 203 | MODULE_LICENSE("GPL v2"); |
cb6b3763 DES |
204 | MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface"); |
205 | MODULE_AUTHOR("Dmitry Eremin-Solenikov"); | |
2bfb1070 | 206 |