diff options
Diffstat (limited to 'net/ieee802154/netlink.c')
-rw-r--r-- | net/ieee802154/netlink.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c new file mode 100644 index 000000000000..105ad10876af --- /dev/null +++ b/net/ieee802154/netlink.c | |||
@@ -0,0 +1,523 @@ | |||
1 | /* | ||
2 | * Netlink inteface for IEEE 802.15.4 stack | ||
3 | * | ||
4 | * Copyright 2007, 2008 Siemens AG | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Written by: | ||
20 | * Sergey Lapin <slapin@ossfans.org> | ||
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/if_arp.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <net/netlink.h> | ||
28 | #include <net/genetlink.h> | ||
29 | #include <linux/nl802154.h> | ||
30 | #include <net/ieee802154/af_ieee802154.h> | ||
31 | #include <net/ieee802154/nl802154.h> | ||
32 | #include <net/ieee802154/netdevice.h> | ||
33 | |||
34 | static unsigned int ieee802154_seq_num; | ||
35 | |||
36 | static struct genl_family ieee802154_coordinator_family = { | ||
37 | .id = GENL_ID_GENERATE, | ||
38 | .hdrsize = 0, | ||
39 | .name = IEEE802154_NL_NAME, | ||
40 | .version = 1, | ||
41 | .maxattr = IEEE802154_ATTR_MAX, | ||
42 | }; | ||
43 | |||
44 | static struct genl_multicast_group ieee802154_coord_mcgrp = { | ||
45 | .name = IEEE802154_MCAST_COORD_NAME, | ||
46 | }; | ||
47 | |||
48 | static struct genl_multicast_group ieee802154_beacon_mcgrp = { | ||
49 | .name = IEEE802154_MCAST_BEACON_NAME, | ||
50 | }; | ||
51 | |||
52 | /* Requests to userspace */ | ||
53 | static struct sk_buff *ieee802154_nl_create(int flags, u8 req) | ||
54 | { | ||
55 | void *hdr; | ||
56 | struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); | ||
57 | |||
58 | if (!msg) | ||
59 | return NULL; | ||
60 | |||
61 | hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, | ||
62 | &ieee802154_coordinator_family, flags, req); | ||
63 | if (!hdr) { | ||
64 | nlmsg_free(msg); | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | return msg; | ||
69 | } | ||
70 | |||
71 | static int ieee802154_nl_finish(struct sk_buff *msg) | ||
72 | { | ||
73 | /* XXX: nlh is right at the start of msg */ | ||
74 | void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); | ||
75 | |||
76 | if (!genlmsg_end(msg, hdr)) | ||
77 | goto out; | ||
78 | |||
79 | return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, | ||
80 | GFP_ATOMIC); | ||
81 | out: | ||
82 | nlmsg_free(msg); | ||
83 | return -ENOBUFS; | ||
84 | } | ||
85 | |||
86 | int ieee802154_nl_assoc_indic(struct net_device *dev, | ||
87 | struct ieee802154_addr *addr, u8 cap) | ||
88 | { | ||
89 | struct sk_buff *msg; | ||
90 | |||
91 | pr_debug("%s\n", __func__); | ||
92 | |||
93 | if (addr->addr_type != IEEE802154_ADDR_LONG) { | ||
94 | pr_err("%s: received non-long source address!\n", __func__); | ||
95 | return -EINVAL; | ||
96 | } | ||
97 | |||
98 | msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC); | ||
99 | if (!msg) | ||
100 | return -ENOBUFS; | ||
101 | |||
102 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
103 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
104 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
105 | dev->dev_addr); | ||
106 | |||
107 | NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, | ||
108 | addr->hwaddr); | ||
109 | |||
110 | NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap); | ||
111 | |||
112 | return ieee802154_nl_finish(msg); | ||
113 | |||
114 | nla_put_failure: | ||
115 | nlmsg_free(msg); | ||
116 | return -ENOBUFS; | ||
117 | } | ||
118 | EXPORT_SYMBOL(ieee802154_nl_assoc_indic); | ||
119 | |||
120 | int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, | ||
121 | u8 status) | ||
122 | { | ||
123 | struct sk_buff *msg; | ||
124 | |||
125 | pr_debug("%s\n", __func__); | ||
126 | |||
127 | msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF); | ||
128 | if (!msg) | ||
129 | return -ENOBUFS; | ||
130 | |||
131 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
132 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
133 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
134 | dev->dev_addr); | ||
135 | |||
136 | NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr); | ||
137 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); | ||
138 | |||
139 | return ieee802154_nl_finish(msg); | ||
140 | |||
141 | nla_put_failure: | ||
142 | nlmsg_free(msg); | ||
143 | return -ENOBUFS; | ||
144 | } | ||
145 | EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); | ||
146 | |||
147 | int ieee802154_nl_disassoc_indic(struct net_device *dev, | ||
148 | struct ieee802154_addr *addr, u8 reason) | ||
149 | { | ||
150 | struct sk_buff *msg; | ||
151 | |||
152 | pr_debug("%s\n", __func__); | ||
153 | |||
154 | msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC); | ||
155 | if (!msg) | ||
156 | return -ENOBUFS; | ||
157 | |||
158 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
159 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
160 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
161 | dev->dev_addr); | ||
162 | |||
163 | if (addr->addr_type == IEEE802154_ADDR_LONG) | ||
164 | NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, | ||
165 | addr->hwaddr); | ||
166 | else | ||
167 | NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, | ||
168 | addr->short_addr); | ||
169 | |||
170 | NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason); | ||
171 | |||
172 | return ieee802154_nl_finish(msg); | ||
173 | |||
174 | nla_put_failure: | ||
175 | nlmsg_free(msg); | ||
176 | return -ENOBUFS; | ||
177 | } | ||
178 | EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); | ||
179 | |||
180 | int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status) | ||
181 | { | ||
182 | struct sk_buff *msg; | ||
183 | |||
184 | pr_debug("%s\n", __func__); | ||
185 | |||
186 | msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF); | ||
187 | if (!msg) | ||
188 | return -ENOBUFS; | ||
189 | |||
190 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
191 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
192 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
193 | dev->dev_addr); | ||
194 | |||
195 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); | ||
196 | |||
197 | return ieee802154_nl_finish(msg); | ||
198 | |||
199 | nla_put_failure: | ||
200 | nlmsg_free(msg); | ||
201 | return -ENOBUFS; | ||
202 | } | ||
203 | EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); | ||
204 | |||
205 | int ieee802154_nl_beacon_indic(struct net_device *dev, | ||
206 | u16 panid, u16 coord_addr) | ||
207 | { | ||
208 | struct sk_buff *msg; | ||
209 | |||
210 | pr_debug("%s\n", __func__); | ||
211 | |||
212 | msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC); | ||
213 | if (!msg) | ||
214 | return -ENOBUFS; | ||
215 | |||
216 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
217 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
218 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
219 | dev->dev_addr); | ||
220 | NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr); | ||
221 | NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid); | ||
222 | |||
223 | return ieee802154_nl_finish(msg); | ||
224 | |||
225 | nla_put_failure: | ||
226 | nlmsg_free(msg); | ||
227 | return -ENOBUFS; | ||
228 | } | ||
229 | EXPORT_SYMBOL(ieee802154_nl_beacon_indic); | ||
230 | |||
231 | int ieee802154_nl_scan_confirm(struct net_device *dev, | ||
232 | u8 status, u8 scan_type, u32 unscanned, | ||
233 | u8 *edl/* , struct list_head *pan_desc_list */) | ||
234 | { | ||
235 | struct sk_buff *msg; | ||
236 | |||
237 | pr_debug("%s\n", __func__); | ||
238 | |||
239 | msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF); | ||
240 | if (!msg) | ||
241 | return -ENOBUFS; | ||
242 | |||
243 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
244 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
245 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
246 | dev->dev_addr); | ||
247 | |||
248 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); | ||
249 | NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); | ||
250 | NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); | ||
251 | |||
252 | if (edl) | ||
253 | NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); | ||
254 | |||
255 | return ieee802154_nl_finish(msg); | ||
256 | |||
257 | nla_put_failure: | ||
258 | nlmsg_free(msg); | ||
259 | return -ENOBUFS; | ||
260 | } | ||
261 | EXPORT_SYMBOL(ieee802154_nl_scan_confirm); | ||
262 | |||
263 | /* Requests from userspace */ | ||
264 | static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) | ||
265 | { | ||
266 | struct net_device *dev; | ||
267 | |||
268 | if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { | ||
269 | char name[IFNAMSIZ + 1]; | ||
270 | nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], | ||
271 | sizeof(name)); | ||
272 | dev = dev_get_by_name(&init_net, name); | ||
273 | } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) | ||
274 | dev = dev_get_by_index(&init_net, | ||
275 | nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); | ||
276 | else | ||
277 | return NULL; | ||
278 | |||
279 | if (dev->type != ARPHRD_IEEE802154) { | ||
280 | dev_put(dev); | ||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | return dev; | ||
285 | } | ||
286 | |||
287 | static int ieee802154_associate_req(struct sk_buff *skb, | ||
288 | struct genl_info *info) | ||
289 | { | ||
290 | struct net_device *dev; | ||
291 | struct ieee802154_addr addr; | ||
292 | int ret = -EINVAL; | ||
293 | |||
294 | if (!info->attrs[IEEE802154_ATTR_CHANNEL] || | ||
295 | !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || | ||
296 | (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && | ||
297 | !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) || | ||
298 | !info->attrs[IEEE802154_ATTR_CAPABILITY]) | ||
299 | return -EINVAL; | ||
300 | |||
301 | dev = ieee802154_nl_get_dev(info); | ||
302 | if (!dev) | ||
303 | return -ENODEV; | ||
304 | |||
305 | if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { | ||
306 | addr.addr_type = IEEE802154_ADDR_LONG; | ||
307 | nla_memcpy(addr.hwaddr, | ||
308 | info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], | ||
309 | IEEE802154_ADDR_LEN); | ||
310 | } else { | ||
311 | addr.addr_type = IEEE802154_ADDR_SHORT; | ||
312 | addr.short_addr = nla_get_u16( | ||
313 | info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); | ||
314 | } | ||
315 | addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); | ||
316 | |||
317 | ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, | ||
318 | nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), | ||
319 | nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); | ||
320 | |||
321 | dev_put(dev); | ||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | static int ieee802154_associate_resp(struct sk_buff *skb, | ||
326 | struct genl_info *info) | ||
327 | { | ||
328 | struct net_device *dev; | ||
329 | struct ieee802154_addr addr; | ||
330 | int ret = -EINVAL; | ||
331 | |||
332 | if (!info->attrs[IEEE802154_ATTR_STATUS] || | ||
333 | !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] || | ||
334 | !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) | ||
335 | return -EINVAL; | ||
336 | |||
337 | dev = ieee802154_nl_get_dev(info); | ||
338 | if (!dev) | ||
339 | return -ENODEV; | ||
340 | |||
341 | addr.addr_type = IEEE802154_ADDR_LONG; | ||
342 | nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], | ||
343 | IEEE802154_ADDR_LEN); | ||
344 | addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
345 | |||
346 | |||
347 | ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, | ||
348 | nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), | ||
349 | nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); | ||
350 | |||
351 | dev_put(dev); | ||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | static int ieee802154_disassociate_req(struct sk_buff *skb, | ||
356 | struct genl_info *info) | ||
357 | { | ||
358 | struct net_device *dev; | ||
359 | struct ieee802154_addr addr; | ||
360 | int ret = -EINVAL; | ||
361 | |||
362 | if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && | ||
363 | !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) || | ||
364 | !info->attrs[IEEE802154_ATTR_REASON]) | ||
365 | return -EINVAL; | ||
366 | |||
367 | dev = ieee802154_nl_get_dev(info); | ||
368 | if (!dev) | ||
369 | return -ENODEV; | ||
370 | |||
371 | if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { | ||
372 | addr.addr_type = IEEE802154_ADDR_LONG; | ||
373 | nla_memcpy(addr.hwaddr, | ||
374 | info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], | ||
375 | IEEE802154_ADDR_LEN); | ||
376 | } else { | ||
377 | addr.addr_type = IEEE802154_ADDR_SHORT; | ||
378 | addr.short_addr = nla_get_u16( | ||
379 | info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); | ||
380 | } | ||
381 | addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); | ||
382 | |||
383 | ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, | ||
384 | nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); | ||
385 | |||
386 | dev_put(dev); | ||
387 | return ret; | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * PANid, channel, beacon_order = 15, superframe_order = 15, | ||
392 | * PAN_coordinator, battery_life_extension = 0, | ||
393 | * coord_realignment = 0, security_enable = 0 | ||
394 | */ | ||
395 | static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) | ||
396 | { | ||
397 | struct net_device *dev; | ||
398 | struct ieee802154_addr addr; | ||
399 | |||
400 | u8 channel, bcn_ord, sf_ord; | ||
401 | int pan_coord, blx, coord_realign; | ||
402 | int ret; | ||
403 | |||
404 | if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || | ||
405 | !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || | ||
406 | !info->attrs[IEEE802154_ATTR_CHANNEL] || | ||
407 | !info->attrs[IEEE802154_ATTR_BCN_ORD] || | ||
408 | !info->attrs[IEEE802154_ATTR_SF_ORD] || | ||
409 | !info->attrs[IEEE802154_ATTR_PAN_COORD] || | ||
410 | !info->attrs[IEEE802154_ATTR_BAT_EXT] || | ||
411 | !info->attrs[IEEE802154_ATTR_COORD_REALIGN] | ||
412 | ) | ||
413 | return -EINVAL; | ||
414 | |||
415 | dev = ieee802154_nl_get_dev(info); | ||
416 | if (!dev) | ||
417 | return -ENODEV; | ||
418 | |||
419 | addr.addr_type = IEEE802154_ADDR_SHORT; | ||
420 | addr.short_addr = nla_get_u16( | ||
421 | info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); | ||
422 | addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); | ||
423 | |||
424 | channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); | ||
425 | bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); | ||
426 | sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]); | ||
427 | pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]); | ||
428 | blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); | ||
429 | coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); | ||
430 | |||
431 | ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, | ||
432 | bcn_ord, sf_ord, pan_coord, blx, coord_realign); | ||
433 | |||
434 | dev_put(dev); | ||
435 | return ret; | ||
436 | } | ||
437 | |||
438 | static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) | ||
439 | { | ||
440 | struct net_device *dev; | ||
441 | int ret; | ||
442 | u8 type; | ||
443 | u32 channels; | ||
444 | u8 duration; | ||
445 | |||
446 | if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || | ||
447 | !info->attrs[IEEE802154_ATTR_CHANNELS] || | ||
448 | !info->attrs[IEEE802154_ATTR_DURATION]) | ||
449 | return -EINVAL; | ||
450 | |||
451 | dev = ieee802154_nl_get_dev(info); | ||
452 | if (!dev) | ||
453 | return -ENODEV; | ||
454 | |||
455 | type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]); | ||
456 | channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); | ||
457 | duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); | ||
458 | |||
459 | ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, | ||
460 | duration); | ||
461 | |||
462 | dev_put(dev); | ||
463 | return ret; | ||
464 | } | ||
465 | |||
466 | #define IEEE802154_OP(_cmd, _func) \ | ||
467 | { \ | ||
468 | .cmd = _cmd, \ | ||
469 | .policy = ieee802154_policy, \ | ||
470 | .doit = _func, \ | ||
471 | .dumpit = NULL, \ | ||
472 | .flags = GENL_ADMIN_PERM, \ | ||
473 | } | ||
474 | |||
475 | static struct genl_ops ieee802154_coordinator_ops[] = { | ||
476 | IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), | ||
477 | IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), | ||
478 | IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), | ||
479 | IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), | ||
480 | IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), | ||
481 | }; | ||
482 | |||
483 | static int __init ieee802154_nl_init(void) | ||
484 | { | ||
485 | int rc; | ||
486 | int i; | ||
487 | |||
488 | rc = genl_register_family(&ieee802154_coordinator_family); | ||
489 | if (rc) | ||
490 | goto fail; | ||
491 | |||
492 | rc = genl_register_mc_group(&ieee802154_coordinator_family, | ||
493 | &ieee802154_coord_mcgrp); | ||
494 | if (rc) | ||
495 | goto fail; | ||
496 | |||
497 | rc = genl_register_mc_group(&ieee802154_coordinator_family, | ||
498 | &ieee802154_beacon_mcgrp); | ||
499 | if (rc) | ||
500 | goto fail; | ||
501 | |||
502 | |||
503 | for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { | ||
504 | rc = genl_register_ops(&ieee802154_coordinator_family, | ||
505 | &ieee802154_coordinator_ops[i]); | ||
506 | if (rc) | ||
507 | goto fail; | ||
508 | } | ||
509 | |||
510 | return 0; | ||
511 | |||
512 | fail: | ||
513 | genl_unregister_family(&ieee802154_coordinator_family); | ||
514 | return rc; | ||
515 | } | ||
516 | module_init(ieee802154_nl_init); | ||
517 | |||
518 | static void __exit ieee802154_nl_exit(void) | ||
519 | { | ||
520 | genl_unregister_family(&ieee802154_coordinator_family); | ||
521 | } | ||
522 | module_exit(ieee802154_nl_exit); | ||
523 | |||