Linux Audio

Check our new training course

Loading...
  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 * Maxim Osipov <maxim.osipov@siemens.com>
 23 */
 24
 25#include <linux/gfp.h>
 26#include <linux/kernel.h>
 27#include <linux/if_arp.h>
 28#include <linux/netdevice.h>
 29#include <net/netlink.h>
 30#include <net/genetlink.h>
 31#include <net/sock.h>
 32#include <linux/nl802154.h>
 33#include <linux/export.h>
 34#include <net/af_ieee802154.h>
 35#include <net/nl802154.h>
 36#include <net/ieee802154.h>
 37#include <net/ieee802154_netdev.h>
 38#include <net/wpan-phy.h>
 39
 40#include "ieee802154.h"
 41
 42static struct genl_multicast_group ieee802154_coord_mcgrp = {
 43	.name		= IEEE802154_MCAST_COORD_NAME,
 44};
 45
 46static struct genl_multicast_group ieee802154_beacon_mcgrp = {
 47	.name		= IEEE802154_MCAST_BEACON_NAME,
 48};
 49
 50int ieee802154_nl_assoc_indic(struct net_device *dev,
 51		struct ieee802154_addr *addr, u8 cap)
 52{
 53	struct sk_buff *msg;
 54
 55	pr_debug("%s\n", __func__);
 56
 57	if (addr->addr_type != IEEE802154_ADDR_LONG) {
 58		pr_err("%s: received non-long source address!\n", __func__);
 59		return -EINVAL;
 60	}
 61
 62	msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
 63	if (!msg)
 64		return -ENOBUFS;
 65
 66	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
 67	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
 68	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
 69		    dev->dev_addr) ||
 70	    nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
 71		    addr->hwaddr) ||
 72	    nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
 73		goto nla_put_failure;
 74
 75	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
 76
 77nla_put_failure:
 78	nlmsg_free(msg);
 79	return -ENOBUFS;
 80}
 81EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
 82
 83int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
 84		u8 status)
 85{
 86	struct sk_buff *msg;
 87
 88	pr_debug("%s\n", __func__);
 89
 90	msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
 91	if (!msg)
 92		return -ENOBUFS;
 93
 94	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
 95	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
 96	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
 97		    dev->dev_addr) ||
 98	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
 99	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
100		goto nla_put_failure;
101	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
102
103nla_put_failure:
104	nlmsg_free(msg);
105	return -ENOBUFS;
106}
107EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
108
109int ieee802154_nl_disassoc_indic(struct net_device *dev,
110		struct ieee802154_addr *addr, u8 reason)
111{
112	struct sk_buff *msg;
113
114	pr_debug("%s\n", __func__);
115
116	msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
117	if (!msg)
118		return -ENOBUFS;
119
120	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
121	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
122	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
123		    dev->dev_addr))
124		goto nla_put_failure;
125	if (addr->addr_type == IEEE802154_ADDR_LONG) {
126		if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
127			    addr->hwaddr))
128			goto nla_put_failure;
129	} else {
130		if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
131				addr->short_addr))
132			goto nla_put_failure;
133	}
134	if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
135		goto nla_put_failure;
136	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
137
138nla_put_failure:
139	nlmsg_free(msg);
140	return -ENOBUFS;
141}
142EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
143
144int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
145{
146	struct sk_buff *msg;
147
148	pr_debug("%s\n", __func__);
149
150	msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
151	if (!msg)
152		return -ENOBUFS;
153
154	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
155	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
156	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
157		    dev->dev_addr) ||
158	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
159		goto nla_put_failure;
160	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
161
162nla_put_failure:
163	nlmsg_free(msg);
164	return -ENOBUFS;
165}
166EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
167
168int ieee802154_nl_beacon_indic(struct net_device *dev,
169		u16 panid, u16 coord_addr)
170{
171	struct sk_buff *msg;
172
173	pr_debug("%s\n", __func__);
174
175	msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
176	if (!msg)
177		return -ENOBUFS;
178
179	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
180	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
181	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
182		    dev->dev_addr) ||
183	    nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
184	    nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
185		goto nla_put_failure;
186	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
187
188nla_put_failure:
189	nlmsg_free(msg);
190	return -ENOBUFS;
191}
192EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
193
194int ieee802154_nl_scan_confirm(struct net_device *dev,
195		u8 status, u8 scan_type, u32 unscanned, u8 page,
196		u8 *edl/* , struct list_head *pan_desc_list */)
197{
198	struct sk_buff *msg;
199
200	pr_debug("%s\n", __func__);
201
202	msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
203	if (!msg)
204		return -ENOBUFS;
205
206	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
207	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
208	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
209		    dev->dev_addr) ||
210	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
211	    nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
212	    nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
213	    nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
214	    (edl &&
215	     nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
216		goto nla_put_failure;
217	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
218
219nla_put_failure:
220	nlmsg_free(msg);
221	return -ENOBUFS;
222}
223EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
224
225int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
226{
227	struct sk_buff *msg;
228
229	pr_debug("%s\n", __func__);
230
231	msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
232	if (!msg)
233		return -ENOBUFS;
234
235	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
236	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
237	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
238		    dev->dev_addr) ||
239	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
240		goto nla_put_failure;
241	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
242
243nla_put_failure:
244	nlmsg_free(msg);
245	return -ENOBUFS;
246}
247EXPORT_SYMBOL(ieee802154_nl_start_confirm);
248
249static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid,
250	u32 seq, int flags, struct net_device *dev)
251{
252	void *hdr;
253	struct wpan_phy *phy;
254
255	pr_debug("%s\n", __func__);
256
257	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
258		IEEE802154_LIST_IFACE);
259	if (!hdr)
260		goto out;
261
262	phy = ieee802154_mlme_ops(dev)->get_phy(dev);
263	BUG_ON(!phy);
264
265	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
266	    nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
267	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
268	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
269		    dev->dev_addr) ||
270	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR,
271			ieee802154_mlme_ops(dev)->get_short_addr(dev)) ||
272	    nla_put_u16(msg, IEEE802154_ATTR_PAN_ID,
273			ieee802154_mlme_ops(dev)->get_pan_id(dev)))
274		goto nla_put_failure;
275	wpan_phy_put(phy);
276	return genlmsg_end(msg, hdr);
277
278nla_put_failure:
279	wpan_phy_put(phy);
280	genlmsg_cancel(msg, hdr);
281out:
282	return -EMSGSIZE;
283}
284
285/* Requests from userspace */
286static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
287{
288	struct net_device *dev;
289
290	if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
291		char name[IFNAMSIZ + 1];
292		nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
293				sizeof(name));
294		dev = dev_get_by_name(&init_net, name);
295	} else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
296		dev = dev_get_by_index(&init_net,
297			nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
298	else
299		return NULL;
300
301	if (!dev)
302		return NULL;
303
304	if (dev->type != ARPHRD_IEEE802154) {
305		dev_put(dev);
306		return NULL;
307	}
308
309	return dev;
310}
311
312static int ieee802154_associate_req(struct sk_buff *skb,
313		struct genl_info *info)
314{
315	struct net_device *dev;
316	struct ieee802154_addr addr;
317	u8 page;
318	int ret = -EINVAL;
319
320	if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
321	    !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
322	    (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
323		!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
324	    !info->attrs[IEEE802154_ATTR_CAPABILITY])
325		return -EINVAL;
326
327	dev = ieee802154_nl_get_dev(info);
328	if (!dev)
329		return -ENODEV;
330
331	if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
332		addr.addr_type = IEEE802154_ADDR_LONG;
333		nla_memcpy(addr.hwaddr,
334				info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
335				IEEE802154_ADDR_LEN);
336	} else {
337		addr.addr_type = IEEE802154_ADDR_SHORT;
338		addr.short_addr = nla_get_u16(
339				info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
340	}
341	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
342
343	if (info->attrs[IEEE802154_ATTR_PAGE])
344		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
345	else
346		page = 0;
347
348	ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
349			nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
350			page,
351			nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
352
353	dev_put(dev);
354	return ret;
355}
356
357static int ieee802154_associate_resp(struct sk_buff *skb,
358		struct genl_info *info)
359{
360	struct net_device *dev;
361	struct ieee802154_addr addr;
362	int ret = -EINVAL;
363
364	if (!info->attrs[IEEE802154_ATTR_STATUS] ||
365	    !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
366	    !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
367		return -EINVAL;
368
369	dev = ieee802154_nl_get_dev(info);
370	if (!dev)
371		return -ENODEV;
372
373	addr.addr_type = IEEE802154_ADDR_LONG;
374	nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
375			IEEE802154_ADDR_LEN);
376	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
377
378
379	ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
380		nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
381		nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
382
383	dev_put(dev);
384	return ret;
385}
386
387static int ieee802154_disassociate_req(struct sk_buff *skb,
388		struct genl_info *info)
389{
390	struct net_device *dev;
391	struct ieee802154_addr addr;
392	int ret = -EINVAL;
393
394	if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
395		!info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
396	    !info->attrs[IEEE802154_ATTR_REASON])
397		return -EINVAL;
398
399	dev = ieee802154_nl_get_dev(info);
400	if (!dev)
401		return -ENODEV;
402
403	if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
404		addr.addr_type = IEEE802154_ADDR_LONG;
405		nla_memcpy(addr.hwaddr,
406				info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
407				IEEE802154_ADDR_LEN);
408	} else {
409		addr.addr_type = IEEE802154_ADDR_SHORT;
410		addr.short_addr = nla_get_u16(
411				info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
412	}
413	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
414
415	ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
416			nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
417
418	dev_put(dev);
419	return ret;
420}
421
422/*
423 * PANid, channel, beacon_order = 15, superframe_order = 15,
424 * PAN_coordinator, battery_life_extension = 0,
425 * coord_realignment = 0, security_enable = 0
426*/
427static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
428{
429	struct net_device *dev;
430	struct ieee802154_addr addr;
431
432	u8 channel, bcn_ord, sf_ord;
433	u8 page;
434	int pan_coord, blx, coord_realign;
435	int ret;
436
437	if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
438	    !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
439	    !info->attrs[IEEE802154_ATTR_CHANNEL] ||
440	    !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
441	    !info->attrs[IEEE802154_ATTR_SF_ORD] ||
442	    !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
443	    !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
444	    !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
445	 )
446		return -EINVAL;
447
448	dev = ieee802154_nl_get_dev(info);
449	if (!dev)
450		return -ENODEV;
451
452	addr.addr_type = IEEE802154_ADDR_SHORT;
453	addr.short_addr = nla_get_u16(
454			info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
455	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
456
457	channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
458	bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
459	sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
460	pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
461	blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
462	coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
463
464	if (info->attrs[IEEE802154_ATTR_PAGE])
465		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
466	else
467		page = 0;
468
469
470	if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
471		ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
472		dev_put(dev);
473		return -EINVAL;
474	}
475
476	ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
477		bcn_ord, sf_ord, pan_coord, blx, coord_realign);
478
479	dev_put(dev);
480	return ret;
481}
482
483static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
484{
485	struct net_device *dev;
486	int ret;
487	u8 type;
488	u32 channels;
489	u8 duration;
490	u8 page;
491
492	if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
493	    !info->attrs[IEEE802154_ATTR_CHANNELS] ||
494	    !info->attrs[IEEE802154_ATTR_DURATION])
495		return -EINVAL;
496
497	dev = ieee802154_nl_get_dev(info);
498	if (!dev)
499		return -ENODEV;
500
501	type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
502	channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
503	duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
504
505	if (info->attrs[IEEE802154_ATTR_PAGE])
506		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
507	else
508		page = 0;
509
510
511	ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
512			duration);
513
514	dev_put(dev);
515	return ret;
516}
517
518static int ieee802154_list_iface(struct sk_buff *skb,
519	struct genl_info *info)
520{
521	/* Request for interface name, index, type, IEEE address,
522	   PAN Id, short address */
523	struct sk_buff *msg;
524	struct net_device *dev = NULL;
525	int rc = -ENOBUFS;
526
527	pr_debug("%s\n", __func__);
528
529	dev = ieee802154_nl_get_dev(info);
530	if (!dev)
531		return -ENODEV;
532
533	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
534	if (!msg)
535		goto out_dev;
536
537	rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq,
538			0, dev);
539	if (rc < 0)
540		goto out_free;
541
542	dev_put(dev);
543
544	return genlmsg_reply(msg, info);
545out_free:
546	nlmsg_free(msg);
547out_dev:
548	dev_put(dev);
549	return rc;
550
551}
552
553static int ieee802154_dump_iface(struct sk_buff *skb,
554	struct netlink_callback *cb)
555{
556	struct net *net = sock_net(skb->sk);
557	struct net_device *dev;
558	int idx;
559	int s_idx = cb->args[0];
560
561	pr_debug("%s\n", __func__);
562
563	idx = 0;
564	for_each_netdev(net, dev) {
565		if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
566			goto cont;
567
568		if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid,
569			cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
570			break;
571cont:
572		idx++;
573	}
574	cb->args[0] = idx;
575
576	return skb->len;
577}
578
579static struct genl_ops ieee802154_coordinator_ops[] = {
580	IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
581	IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
582	IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
583	IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
584	IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
585	IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
586							ieee802154_dump_iface),
587};
588
589/*
590 * No need to unregister as family unregistration will do it.
591 */
592int nl802154_mac_register(void)
593{
594	int i;
595	int rc;
596
597	rc = genl_register_mc_group(&nl802154_family,
598			&ieee802154_coord_mcgrp);
599	if (rc)
600		return rc;
601
602	rc = genl_register_mc_group(&nl802154_family,
603			&ieee802154_beacon_mcgrp);
604	if (rc)
605		return rc;
606
607	for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
608		rc = genl_register_ops(&nl802154_family,
609				&ieee802154_coordinator_ops[i]);
610		if (rc)
611			return rc;
612	}
613
614	return 0;
615}