diff options
author | Alexander Aring <alex.aring@gmail.com> | 2014-11-09 02:36:53 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-11-09 13:50:29 -0500 |
commit | 79fe1a2aa7b504c68642e510154f17e2de60da60 (patch) | |
tree | 8915b7af57b06dc0185ee5f41a3a010e6a864a06 | |
parent | 3ae75e02c34b5b8d521b0470522e540512ce24e3 (diff) |
ieee802154: add nl802154 framework
This patch adds a basic nl802154 framework. Most of this code was
grabbed from nl80211 framework.
Signed-off-by: Alexander Aring <alex.aring@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r-- | net/ieee802154/Makefile | 2 | ||||
-rw-r--r-- | net/ieee802154/core.c | 28 | ||||
-rw-r--r-- | net/ieee802154/core.h | 4 | ||||
-rw-r--r-- | net/ieee802154/ieee802154.h | 2 | ||||
-rw-r--r-- | net/ieee802154/netlink.c | 2 | ||||
-rw-r--r-- | net/ieee802154/nl802154.c | 309 | ||||
-rw-r--r-- | net/ieee802154/nl802154.h | 7 |
7 files changed, 350 insertions, 4 deletions
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 38354d4a70cb..9f6970f2a28b 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile | |||
@@ -3,7 +3,7 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o | |||
3 | 3 | ||
4 | ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o | 4 | ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o |
5 | ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ | 5 | ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ |
6 | header_ops.o sysfs.o | 6 | header_ops.o sysfs.o nl802154.o |
7 | af_802154-y := af_ieee802154.o raw.o dgram.o | 7 | af_802154-y := af_ieee802154.o raw.o dgram.o |
8 | 8 | ||
9 | ccflags-y += -D__CHECK_ENDIAN__ | 9 | ccflags-y += -D__CHECK_ENDIAN__ |
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 3ee00bf0e514..ae5ecbc2ca0a 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c | |||
@@ -21,11 +21,12 @@ | |||
21 | #include <net/rtnetlink.h> | 21 | #include <net/rtnetlink.h> |
22 | 22 | ||
23 | #include "ieee802154.h" | 23 | #include "ieee802154.h" |
24 | #include "nl802154.h" | ||
24 | #include "sysfs.h" | 25 | #include "sysfs.h" |
25 | #include "core.h" | 26 | #include "core.h" |
26 | 27 | ||
27 | /* RCU-protected (and RTNL for writers) */ | 28 | /* RCU-protected (and RTNL for writers) */ |
28 | static LIST_HEAD(cfg802154_rdev_list); | 29 | LIST_HEAD(cfg802154_rdev_list); |
29 | static int cfg802154_rdev_list_generation; | 30 | static int cfg802154_rdev_list_generation; |
30 | 31 | ||
31 | static int wpan_phy_match(struct device *dev, const void *data) | 32 | static int wpan_phy_match(struct device *dev, const void *data) |
@@ -74,6 +75,23 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), | |||
74 | } | 75 | } |
75 | EXPORT_SYMBOL(wpan_phy_for_each); | 76 | EXPORT_SYMBOL(wpan_phy_for_each); |
76 | 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 | |||
77 | struct wpan_phy * | 95 | struct wpan_phy * |
78 | wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) | 96 | wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) |
79 | { | 97 | { |
@@ -270,8 +288,15 @@ static int __init wpan_phy_class_init(void) | |||
270 | if (rc) | 288 | if (rc) |
271 | goto err_notifier; | 289 | goto err_notifier; |
272 | 290 | ||
291 | rc = nl802154_init(); | ||
292 | if (rc) | ||
293 | goto err_ieee802154_nl; | ||
294 | |||
273 | return 0; | 295 | return 0; |
274 | 296 | ||
297 | err_ieee802154_nl: | ||
298 | ieee802154_nl_exit(); | ||
299 | |||
275 | err_notifier: | 300 | err_notifier: |
276 | unregister_netdevice_notifier(&cfg802154_netdev_notifier); | 301 | unregister_netdevice_notifier(&cfg802154_netdev_notifier); |
277 | err_nl: | 302 | err_nl: |
@@ -283,6 +308,7 @@ subsys_initcall(wpan_phy_class_init); | |||
283 | 308 | ||
284 | static void __exit wpan_phy_class_exit(void) | 309 | static void __exit wpan_phy_class_exit(void) |
285 | { | 310 | { |
311 | nl802154_exit(); | ||
286 | ieee802154_nl_exit(); | 312 | ieee802154_nl_exit(); |
287 | unregister_netdevice_notifier(&cfg802154_netdev_notifier); | 313 | unregister_netdevice_notifier(&cfg802154_netdev_notifier); |
288 | wpan_phy_sysfs_exit(); | 314 | wpan_phy_sysfs_exit(); |
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h index e708d9d5878b..c8319bf1b61a 100644 --- a/net/ieee802154/core.h +++ b/net/ieee802154/core.h | |||
@@ -35,7 +35,11 @@ wpan_phy_to_rdev(struct wpan_phy *wpan_phy) | |||
35 | wpan_phy); | 35 | wpan_phy); |
36 | } | 36 | } |
37 | 37 | ||
38 | extern struct list_head cfg802154_rdev_list; | ||
39 | |||
38 | /* free object */ | 40 | /* free object */ |
39 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev); | 41 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev); |
42 | struct cfg802154_registered_device * | ||
43 | cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx); | ||
40 | 44 | ||
41 | #endif /* __IEEE802154_CORE_H */ | 45 | #endif /* __IEEE802154_CORE_H */ |
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index 42ae63a345ab..a5d7515b7f62 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h | |||
@@ -15,7 +15,7 @@ | |||
15 | #define IEEE_802154_LOCAL_H | 15 | #define IEEE_802154_LOCAL_H |
16 | 16 | ||
17 | int __init ieee802154_nl_init(void); | 17 | int __init ieee802154_nl_init(void); |
18 | void __exit ieee802154_nl_exit(void); | 18 | void ieee802154_nl_exit(void); |
19 | 19 | ||
20 | #define IEEE802154_OP(_cmd, _func) \ | 20 | #define IEEE802154_OP(_cmd, _func) \ |
21 | { \ | 21 | { \ |
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 6c3c2595a201..63ee7d66950e 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c | |||
@@ -155,7 +155,7 @@ int __init ieee802154_nl_init(void) | |||
155 | ieee802154_mcgrps); | 155 | ieee802154_mcgrps); |
156 | } | 156 | } |
157 | 157 | ||
158 | void __exit ieee802154_nl_exit(void) | 158 | void ieee802154_nl_exit(void) |
159 | { | 159 | { |
160 | genl_unregister_family(&nl802154_family); | 160 | genl_unregister_family(&nl802154_family); |
161 | } | 161 | } |
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c new file mode 100644 index 000000000000..5dec0bb5bb55 --- /dev/null +++ b/net/ieee802154/nl802154.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* This program is free software; you can redistribute it and/or modify | ||
2 | * it under the terms of the GNU General Public License version 2 | ||
3 | * as published by the Free Software Foundation. | ||
4 | * | ||
5 | * This program is distributed in the hope that it will be useful, | ||
6 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
7 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
8 | * GNU General Public License for more details. | ||
9 | * | ||
10 | * Authors: | ||
11 | * Alexander Aring <aar@pengutronix.de> | ||
12 | * | ||
13 | * Based on: net/wireless/nl80211.c | ||
14 | */ | ||
15 | |||
16 | #include <linux/rtnetlink.h> | ||
17 | |||
18 | #include <net/cfg802154.h> | ||
19 | #include <net/genetlink.h> | ||
20 | #include <net/mac802154.h> | ||
21 | #include <net/netlink.h> | ||
22 | #include <net/nl802154.h> | ||
23 | #include <net/sock.h> | ||
24 | |||
25 | #include "nl802154.h" | ||
26 | #include "core.h" | ||
27 | |||
28 | static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
29 | struct genl_info *info); | ||
30 | |||
31 | static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
32 | struct genl_info *info); | ||
33 | |||
34 | /* the netlink family */ | ||
35 | static struct genl_family nl802154_fam = { | ||
36 | .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ | ||
37 | .name = NL802154_GENL_NAME, /* have users key off the name instead */ | ||
38 | .hdrsize = 0, /* no private header */ | ||
39 | .version = 1, /* no particular meaning now */ | ||
40 | .maxattr = NL802154_ATTR_MAX, | ||
41 | .netnsok = true, | ||
42 | .pre_doit = nl802154_pre_doit, | ||
43 | .post_doit = nl802154_post_doit, | ||
44 | }; | ||
45 | |||
46 | /* multicast groups */ | ||
47 | enum nl802154_multicast_groups { | ||
48 | NL802154_MCGRP_CONFIG, | ||
49 | }; | ||
50 | |||
51 | static const struct genl_multicast_group nl802154_mcgrps[] = { | ||
52 | [NL802154_MCGRP_CONFIG] = { .name = "config", }, | ||
53 | }; | ||
54 | |||
55 | /* returns ERR_PTR values */ | ||
56 | static struct wpan_dev * | ||
57 | __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs) | ||
58 | { | ||
59 | struct cfg802154_registered_device *rdev; | ||
60 | struct wpan_dev *result = NULL; | ||
61 | bool have_ifidx = attrs[NL802154_ATTR_IFINDEX]; | ||
62 | bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV]; | ||
63 | u64 wpan_dev_id; | ||
64 | int wpan_phy_idx = -1; | ||
65 | int ifidx = -1; | ||
66 | |||
67 | ASSERT_RTNL(); | ||
68 | |||
69 | if (!have_ifidx && !have_wpan_dev_id) | ||
70 | return ERR_PTR(-EINVAL); | ||
71 | |||
72 | if (have_ifidx) | ||
73 | ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); | ||
74 | if (have_wpan_dev_id) { | ||
75 | wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); | ||
76 | wpan_phy_idx = wpan_dev_id >> 32; | ||
77 | } | ||
78 | |||
79 | list_for_each_entry(rdev, &cfg802154_rdev_list, list) { | ||
80 | struct wpan_dev *wpan_dev; | ||
81 | |||
82 | /* TODO netns compare */ | ||
83 | |||
84 | if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx) | ||
85 | continue; | ||
86 | |||
87 | list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { | ||
88 | if (have_ifidx && wpan_dev->netdev && | ||
89 | wpan_dev->netdev->ifindex == ifidx) { | ||
90 | result = wpan_dev; | ||
91 | break; | ||
92 | } | ||
93 | if (have_wpan_dev_id && | ||
94 | wpan_dev->identifier == (u32)wpan_dev_id) { | ||
95 | result = wpan_dev; | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (result) | ||
101 | break; | ||
102 | } | ||
103 | |||
104 | if (result) | ||
105 | return result; | ||
106 | |||
107 | return ERR_PTR(-ENODEV); | ||
108 | } | ||
109 | |||
110 | static struct cfg802154_registered_device * | ||
111 | __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs) | ||
112 | { | ||
113 | struct cfg802154_registered_device *rdev = NULL, *tmp; | ||
114 | struct net_device *netdev; | ||
115 | |||
116 | ASSERT_RTNL(); | ||
117 | |||
118 | if (!attrs[NL802154_ATTR_WPAN_PHY] && | ||
119 | !attrs[NL802154_ATTR_IFINDEX] && | ||
120 | !attrs[NL802154_ATTR_WPAN_DEV]) | ||
121 | return ERR_PTR(-EINVAL); | ||
122 | |||
123 | if (attrs[NL802154_ATTR_WPAN_PHY]) | ||
124 | rdev = cfg802154_rdev_by_wpan_phy_idx( | ||
125 | nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY])); | ||
126 | |||
127 | if (attrs[NL802154_ATTR_WPAN_DEV]) { | ||
128 | u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); | ||
129 | struct wpan_dev *wpan_dev; | ||
130 | bool found = false; | ||
131 | |||
132 | tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32); | ||
133 | if (tmp) { | ||
134 | /* make sure wpan_dev exists */ | ||
135 | list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) { | ||
136 | if (wpan_dev->identifier != (u32)wpan_dev_id) | ||
137 | continue; | ||
138 | found = true; | ||
139 | break; | ||
140 | } | ||
141 | |||
142 | if (!found) | ||
143 | tmp = NULL; | ||
144 | |||
145 | if (rdev && tmp != rdev) | ||
146 | return ERR_PTR(-EINVAL); | ||
147 | rdev = tmp; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | if (attrs[NL802154_ATTR_IFINDEX]) { | ||
152 | int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); | ||
153 | |||
154 | netdev = __dev_get_by_index(netns, ifindex); | ||
155 | if (netdev) { | ||
156 | if (netdev->ieee802154_ptr) | ||
157 | tmp = wpan_phy_to_rdev( | ||
158 | netdev->ieee802154_ptr->wpan_phy); | ||
159 | else | ||
160 | tmp = NULL; | ||
161 | |||
162 | /* not wireless device -- return error */ | ||
163 | if (!tmp) | ||
164 | return ERR_PTR(-EINVAL); | ||
165 | |||
166 | /* mismatch -- return error */ | ||
167 | if (rdev && tmp != rdev) | ||
168 | return ERR_PTR(-EINVAL); | ||
169 | |||
170 | rdev = tmp; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (!rdev) | ||
175 | return ERR_PTR(-ENODEV); | ||
176 | |||
177 | /* TODO netns compare */ | ||
178 | |||
179 | return rdev; | ||
180 | } | ||
181 | |||
182 | /* This function returns a pointer to the driver | ||
183 | * that the genl_info item that is passed refers to. | ||
184 | * | ||
185 | * The result of this can be a PTR_ERR and hence must | ||
186 | * be checked with IS_ERR() for errors. | ||
187 | */ | ||
188 | static struct cfg802154_registered_device * | ||
189 | cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info) | ||
190 | { | ||
191 | return __cfg802154_rdev_from_attrs(netns, info->attrs); | ||
192 | } | ||
193 | |||
194 | /* policy for the attributes */ | ||
195 | static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { | ||
196 | }; | ||
197 | |||
198 | /* message building helper */ | ||
199 | static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, | ||
200 | int flags, u8 cmd) | ||
201 | { | ||
202 | /* since there is no private header just add the generic one */ | ||
203 | return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); | ||
204 | } | ||
205 | |||
206 | #define NL802154_FLAG_NEED_WPAN_PHY 0x01 | ||
207 | #define NL802154_FLAG_NEED_NETDEV 0x02 | ||
208 | #define NL802154_FLAG_NEED_RTNL 0x04 | ||
209 | #define NL802154_FLAG_CHECK_NETDEV_UP 0x08 | ||
210 | #define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\ | ||
211 | NL802154_FLAG_CHECK_NETDEV_UP) | ||
212 | #define NL802154_FLAG_NEED_WPAN_DEV 0x10 | ||
213 | #define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\ | ||
214 | NL802154_FLAG_CHECK_NETDEV_UP) | ||
215 | |||
216 | static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
217 | struct genl_info *info) | ||
218 | { | ||
219 | struct cfg802154_registered_device *rdev; | ||
220 | struct wpan_dev *wpan_dev; | ||
221 | struct net_device *dev; | ||
222 | bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; | ||
223 | |||
224 | if (rtnl) | ||
225 | rtnl_lock(); | ||
226 | |||
227 | if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { | ||
228 | rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); | ||
229 | if (IS_ERR(rdev)) { | ||
230 | if (rtnl) | ||
231 | rtnl_unlock(); | ||
232 | return PTR_ERR(rdev); | ||
233 | } | ||
234 | info->user_ptr[0] = rdev; | ||
235 | } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || | ||
236 | ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { | ||
237 | ASSERT_RTNL(); | ||
238 | wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), | ||
239 | info->attrs); | ||
240 | if (IS_ERR(wpan_dev)) { | ||
241 | if (rtnl) | ||
242 | rtnl_unlock(); | ||
243 | return PTR_ERR(wpan_dev); | ||
244 | } | ||
245 | |||
246 | dev = wpan_dev->netdev; | ||
247 | rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); | ||
248 | |||
249 | if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { | ||
250 | if (!dev) { | ||
251 | if (rtnl) | ||
252 | rtnl_unlock(); | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | info->user_ptr[1] = dev; | ||
257 | } else { | ||
258 | info->user_ptr[1] = wpan_dev; | ||
259 | } | ||
260 | |||
261 | if (dev) { | ||
262 | if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && | ||
263 | !netif_running(dev)) { | ||
264 | if (rtnl) | ||
265 | rtnl_unlock(); | ||
266 | return -ENETDOWN; | ||
267 | } | ||
268 | |||
269 | dev_hold(dev); | ||
270 | } | ||
271 | |||
272 | info->user_ptr[0] = rdev; | ||
273 | } | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
279 | struct genl_info *info) | ||
280 | { | ||
281 | if (info->user_ptr[1]) { | ||
282 | if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { | ||
283 | struct wpan_dev *wpan_dev = info->user_ptr[1]; | ||
284 | |||
285 | if (wpan_dev->netdev) | ||
286 | dev_put(wpan_dev->netdev); | ||
287 | } else { | ||
288 | dev_put(info->user_ptr[1]); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) | ||
293 | rtnl_unlock(); | ||
294 | } | ||
295 | |||
296 | static const struct genl_ops nl802154_ops[] = { | ||
297 | }; | ||
298 | |||
299 | /* initialisation/exit functions */ | ||
300 | int nl802154_init(void) | ||
301 | { | ||
302 | return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops, | ||
303 | nl802154_mcgrps); | ||
304 | } | ||
305 | |||
306 | void nl802154_exit(void) | ||
307 | { | ||
308 | genl_unregister_family(&nl802154_fam); | ||
309 | } | ||
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h new file mode 100644 index 000000000000..3846a89d0958 --- /dev/null +++ b/net/ieee802154/nl802154.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __IEEE802154_NL802154_H | ||
2 | #define __IEEE802154_NL802154_H | ||
3 | |||
4 | int nl802154_init(void); | ||
5 | void nl802154_exit(void); | ||
6 | |||
7 | #endif /* __IEEE802154_NL802154_H */ | ||