diff options
author | Alexander Aring <alex.aring@gmail.com> | 2014-11-09 02:36:54 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-11-09 13:50:29 -0500 |
commit | ca20ce201c8d4123de0fd2b0d59ea19b0160d88f (patch) | |
tree | f99451e5a1e87312deaebfcc8fb4624c9498c2c0 /net/ieee802154 | |
parent | 79fe1a2aa7b504c68642e510154f17e2de60da60 (diff) |
ieee802154: add wpan_phy dump support
This patch adds support for dumping wpan_phy attributes via nl802154.
Signed-off-by: Alexander Aring <alex.aring@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/ieee802154')
-rw-r--r-- | net/ieee802154/core.c | 2 | ||||
-rw-r--r-- | net/ieee802154/core.h | 1 | ||||
-rw-r--r-- | net/ieee802154/nl802154.c | 221 |
3 files changed, 223 insertions, 1 deletions
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index ae5ecbc2ca0a..18bc7e738507 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c | |||
@@ -27,7 +27,7 @@ | |||
27 | 27 | ||
28 | /* RCU-protected (and RTNL for writers) */ | 28 | /* RCU-protected (and RTNL for writers) */ |
29 | LIST_HEAD(cfg802154_rdev_list); | 29 | LIST_HEAD(cfg802154_rdev_list); |
30 | static int cfg802154_rdev_list_generation; | 30 | int cfg802154_rdev_list_generation; |
31 | 31 | ||
32 | static int wpan_phy_match(struct device *dev, const void *data) | 32 | static int wpan_phy_match(struct device *dev, const void *data) |
33 | { | 33 | { |
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h index c8319bf1b61a..f3e95580caee 100644 --- a/net/ieee802154/core.h +++ b/net/ieee802154/core.h | |||
@@ -36,6 +36,7 @@ wpan_phy_to_rdev(struct wpan_phy *wpan_phy) | |||
36 | } | 36 | } |
37 | 37 | ||
38 | extern struct list_head cfg802154_rdev_list; | 38 | extern struct list_head cfg802154_rdev_list; |
39 | extern int cfg802154_rdev_list_generation; | ||
39 | 40 | ||
40 | /* free object */ | 41 | /* free object */ |
41 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev); | 42 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev); |
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 5dec0bb5bb55..32e884732eb1 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c | |||
@@ -193,6 +193,22 @@ cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info) | |||
193 | 193 | ||
194 | /* policy for the attributes */ | 194 | /* policy for the attributes */ |
195 | static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { | 195 | static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { |
196 | [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 }, | ||
197 | [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING, | ||
198 | .len = 20-1 }, | ||
199 | |||
200 | [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 }, | ||
201 | |||
202 | [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 }, | ||
203 | |||
204 | [NL802154_ATTR_PAGE] = { .type = NLA_U8, }, | ||
205 | [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, }, | ||
206 | |||
207 | [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, }, | ||
208 | |||
209 | [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, | ||
210 | |||
211 | [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, }, | ||
196 | }; | 212 | }; |
197 | 213 | ||
198 | /* message building helper */ | 214 | /* message building helper */ |
@@ -203,6 +219,201 @@ static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, | |||
203 | return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); | 219 | return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); |
204 | } | 220 | } |
205 | 221 | ||
222 | static int | ||
223 | nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev, | ||
224 | struct sk_buff *msg) | ||
225 | { | ||
226 | struct nlattr *nl_page; | ||
227 | unsigned long page; | ||
228 | |||
229 | nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED); | ||
230 | if (!nl_page) | ||
231 | return -ENOBUFS; | ||
232 | |||
233 | for (page = 0; page < WPAN_NUM_PAGES; page++) { | ||
234 | if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL, | ||
235 | rdev->wpan_phy.channels_supported[page])) | ||
236 | return -ENOBUFS; | ||
237 | } | ||
238 | nla_nest_end(msg, nl_page); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, | ||
244 | enum nl802154_commands cmd, | ||
245 | struct sk_buff *msg, u32 portid, u32 seq, | ||
246 | int flags) | ||
247 | { | ||
248 | void *hdr; | ||
249 | |||
250 | hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); | ||
251 | if (!hdr) | ||
252 | return -ENOBUFS; | ||
253 | |||
254 | if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || | ||
255 | nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME, | ||
256 | wpan_phy_name(&rdev->wpan_phy)) || | ||
257 | nla_put_u32(msg, NL802154_ATTR_GENERATION, | ||
258 | cfg802154_rdev_list_generation)) | ||
259 | goto nla_put_failure; | ||
260 | |||
261 | if (cmd != NL802154_CMD_NEW_WPAN_PHY) | ||
262 | goto finish; | ||
263 | |||
264 | /* DUMP PHY PIB */ | ||
265 | |||
266 | /* current channel settings */ | ||
267 | if (nla_put_u8(msg, NL802154_ATTR_PAGE, | ||
268 | rdev->wpan_phy.current_page) || | ||
269 | nla_put_u8(msg, NL802154_ATTR_CHANNEL, | ||
270 | rdev->wpan_phy.current_channel)) | ||
271 | goto nla_put_failure; | ||
272 | |||
273 | /* supported channels array */ | ||
274 | if (nl802154_send_wpan_phy_channels(rdev, msg)) | ||
275 | goto nla_put_failure; | ||
276 | |||
277 | /* cca mode */ | ||
278 | if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE, | ||
279 | rdev->wpan_phy.cca_mode)) | ||
280 | goto nla_put_failure; | ||
281 | |||
282 | if (nla_put_s8(msg, NL802154_ATTR_TX_POWER, | ||
283 | rdev->wpan_phy.transmit_power)) | ||
284 | goto nla_put_failure; | ||
285 | |||
286 | finish: | ||
287 | return genlmsg_end(msg, hdr); | ||
288 | |||
289 | nla_put_failure: | ||
290 | genlmsg_cancel(msg, hdr); | ||
291 | return -EMSGSIZE; | ||
292 | } | ||
293 | |||
294 | struct nl802154_dump_wpan_phy_state { | ||
295 | s64 filter_wpan_phy; | ||
296 | long start; | ||
297 | |||
298 | }; | ||
299 | |||
300 | static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, | ||
301 | struct netlink_callback *cb, | ||
302 | struct nl802154_dump_wpan_phy_state *state) | ||
303 | { | ||
304 | struct nlattr **tb = nl802154_fam.attrbuf; | ||
305 | int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, | ||
306 | tb, nl802154_fam.maxattr, nl802154_policy); | ||
307 | |||
308 | /* TODO check if we can handle error here, | ||
309 | * we have no backward compatibility | ||
310 | */ | ||
311 | if (ret) | ||
312 | return 0; | ||
313 | |||
314 | if (tb[NL802154_ATTR_WPAN_PHY]) | ||
315 | state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]); | ||
316 | if (tb[NL802154_ATTR_WPAN_DEV]) | ||
317 | state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32; | ||
318 | if (tb[NL802154_ATTR_IFINDEX]) { | ||
319 | struct net_device *netdev; | ||
320 | struct cfg802154_registered_device *rdev; | ||
321 | int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]); | ||
322 | |||
323 | /* TODO netns */ | ||
324 | netdev = __dev_get_by_index(&init_net, ifidx); | ||
325 | if (!netdev) | ||
326 | return -ENODEV; | ||
327 | if (netdev->ieee802154_ptr) { | ||
328 | rdev = wpan_phy_to_rdev( | ||
329 | netdev->ieee802154_ptr->wpan_phy); | ||
330 | state->filter_wpan_phy = rdev->wpan_phy_idx; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int | ||
338 | nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb) | ||
339 | { | ||
340 | int idx = 0, ret; | ||
341 | struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0]; | ||
342 | struct cfg802154_registered_device *rdev; | ||
343 | |||
344 | rtnl_lock(); | ||
345 | if (!state) { | ||
346 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
347 | if (!state) { | ||
348 | rtnl_unlock(); | ||
349 | return -ENOMEM; | ||
350 | } | ||
351 | state->filter_wpan_phy = -1; | ||
352 | ret = nl802154_dump_wpan_phy_parse(skb, cb, state); | ||
353 | if (ret) { | ||
354 | kfree(state); | ||
355 | rtnl_unlock(); | ||
356 | return ret; | ||
357 | } | ||
358 | cb->args[0] = (long)state; | ||
359 | } | ||
360 | |||
361 | list_for_each_entry(rdev, &cfg802154_rdev_list, list) { | ||
362 | /* TODO net ns compare */ | ||
363 | if (++idx <= state->start) | ||
364 | continue; | ||
365 | if (state->filter_wpan_phy != -1 && | ||
366 | state->filter_wpan_phy != rdev->wpan_phy_idx) | ||
367 | continue; | ||
368 | /* attempt to fit multiple wpan_phy data chunks into the skb */ | ||
369 | ret = nl802154_send_wpan_phy(rdev, | ||
370 | NL802154_CMD_NEW_WPAN_PHY, | ||
371 | skb, | ||
372 | NETLINK_CB(cb->skb).portid, | ||
373 | cb->nlh->nlmsg_seq, NLM_F_MULTI); | ||
374 | if (ret < 0) { | ||
375 | if ((ret == -ENOBUFS || ret == -EMSGSIZE) && | ||
376 | !skb->len && cb->min_dump_alloc < 4096) { | ||
377 | cb->min_dump_alloc = 4096; | ||
378 | rtnl_unlock(); | ||
379 | return 1; | ||
380 | } | ||
381 | idx--; | ||
382 | break; | ||
383 | } | ||
384 | break; | ||
385 | } | ||
386 | rtnl_unlock(); | ||
387 | |||
388 | state->start = idx; | ||
389 | |||
390 | return skb->len; | ||
391 | } | ||
392 | |||
393 | static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb) | ||
394 | { | ||
395 | kfree((void *)cb->args[0]); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info) | ||
400 | { | ||
401 | struct sk_buff *msg; | ||
402 | struct cfg802154_registered_device *rdev = info->user_ptr[0]; | ||
403 | |||
404 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
405 | if (!msg) | ||
406 | return -ENOMEM; | ||
407 | |||
408 | if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg, | ||
409 | info->snd_portid, info->snd_seq, 0) < 0) { | ||
410 | nlmsg_free(msg); | ||
411 | return -ENOBUFS; | ||
412 | } | ||
413 | |||
414 | return genlmsg_reply(msg, info); | ||
415 | } | ||
416 | |||
206 | #define NL802154_FLAG_NEED_WPAN_PHY 0x01 | 417 | #define NL802154_FLAG_NEED_WPAN_PHY 0x01 |
207 | #define NL802154_FLAG_NEED_NETDEV 0x02 | 418 | #define NL802154_FLAG_NEED_NETDEV 0x02 |
208 | #define NL802154_FLAG_NEED_RTNL 0x04 | 419 | #define NL802154_FLAG_NEED_RTNL 0x04 |
@@ -294,6 +505,16 @@ static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | |||
294 | } | 505 | } |
295 | 506 | ||
296 | static const struct genl_ops nl802154_ops[] = { | 507 | static const struct genl_ops nl802154_ops[] = { |
508 | { | ||
509 | .cmd = NL802154_CMD_GET_WPAN_PHY, | ||
510 | .doit = nl802154_get_wpan_phy, | ||
511 | .dumpit = nl802154_dump_wpan_phy, | ||
512 | .done = nl802154_dump_wpan_phy_done, | ||
513 | .policy = nl802154_policy, | ||
514 | /* can be retrieved by unprivileged users */ | ||
515 | .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | | ||
516 | NL802154_FLAG_NEED_RTNL, | ||
517 | }, | ||
297 | }; | 518 | }; |
298 | 519 | ||
299 | /* initialisation/exit functions */ | 520 | /* initialisation/exit functions */ |