aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee802154
diff options
context:
space:
mode:
authorAlexander Aring <alex.aring@gmail.com>2014-11-09 02:36:54 -0500
committerMarcel Holtmann <marcel@holtmann.org>2014-11-09 13:50:29 -0500
commitca20ce201c8d4123de0fd2b0d59ea19b0160d88f (patch)
treef99451e5a1e87312deaebfcc8fb4624c9498c2c0 /net/ieee802154
parent79fe1a2aa7b504c68642e510154f17e2de60da60 (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.c2
-rw-r--r--net/ieee802154/core.h1
-rw-r--r--net/ieee802154/nl802154.c221
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) */
29LIST_HEAD(cfg802154_rdev_list); 29LIST_HEAD(cfg802154_rdev_list);
30static int cfg802154_rdev_list_generation; 30int cfg802154_rdev_list_generation;
31 31
32static int wpan_phy_match(struct device *dev, const void *data) 32static 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
38extern struct list_head cfg802154_rdev_list; 38extern struct list_head cfg802154_rdev_list;
39extern int cfg802154_rdev_list_generation;
39 40
40/* free object */ 41/* free object */
41void cfg802154_dev_free(struct cfg802154_registered_device *rdev); 42void 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 */
195static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { 195static 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
222static int
223nl802154_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
243static 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
286finish:
287 return genlmsg_end(msg, hdr);
288
289nla_put_failure:
290 genlmsg_cancel(msg, hdr);
291 return -EMSGSIZE;
292}
293
294struct nl802154_dump_wpan_phy_state {
295 s64 filter_wpan_phy;
296 long start;
297
298};
299
300static 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
337static int
338nl802154_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
393static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
394{
395 kfree((void *)cb->args[0]);
396 return 0;
397}
398
399static 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
296static const struct genl_ops nl802154_ops[] = { 507static 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 */