diff mbox series

[net-next,08/18] net: ieee802154: Add support for internal PAN management

Message ID 20211222155743.256280-9-miquel.raynal@bootlin.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series IEEE 802.15.4 passive scan support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/apply fail Patch does not apply to net-next

Commit Message

Miquel Raynal Dec. 22, 2021, 3:57 p.m. UTC
Let's introduce the basics of PAN management:
- structures defining PANs
- helpers for PANs registration
- helpers discarding old PANs

Co-developed-by: David Girault <david.girault@qorvo.com>
Signed-off-by: David Girault <david.girault@qorvo.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 include/net/cfg802154.h |  31 ++++++
 net/ieee802154/Makefile |   2 +-
 net/ieee802154/core.c   |   2 +
 net/ieee802154/core.h   |  20 ++++
 net/ieee802154/pan.c    | 208 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 262 insertions(+), 1 deletion(-)
 create mode 100644 net/ieee802154/pan.c

Comments

Jakub Kicinski Dec. 22, 2021, 8:55 p.m. UTC | #1
On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote:
> +/* Maximum number of PAN entries to store */
> +static int max_pan_entries = 100;
> +module_param(max_pan_entries, uint, 0644);
> +MODULE_PARM_DESC(max_pan_entries,
> +		 "Maximum number of PANs to discover per scan (default is 100)");
> +
> +static int pan_expiration = 60;
> +module_param(pan_expiration, uint, 0644);
> +MODULE_PARM_DESC(pan_expiration,
> +		 "Expiration of the scan validity in seconds (default is 60s)");

Can these be per-device control knobs? Module params are rarely the
best answer.
Alexander Aring Dec. 28, 2021, 10:22 p.m. UTC | #2
Hi,

On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Let's introduce the basics of PAN management:
> - structures defining PANs
> - helpers for PANs registration
> - helpers discarding old PANs
>

I think there exists a little misunderstanding about how the
architecture is between the structures wpan_phy, wpan_dev and
cfg802154.

 - wpan_phy: represents the PHY layer of IEEE 802154 and is a
registered device class.
 - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface.

You can have multiple wpan_dev operate on one wpan_phy. To my best
knowledge it's like having multiple access points running on one phy
(wireless) or macvlan on ethernet. You can actually do that with the
mac802154_hwsim driver. However as there exists currently no (as my
knowledge) hardware which supports e.g. multiple address filters we
wanted to be prepared for to support such handling. Although, there
exists some transceivers which support something like a "pan bridge"
which goes into such a direction.

What is a cfg802154 registered device? Well, at first it offers an
interface between SoftMAC and HardMAC from nl802154, that's the
cfg802154_ops structure. In theory a HardMAC transceiver would bypass
the SoftMAC stack by implementing "cfg802154_ops" on the driver layer
and try to do everything there as much as possible to support it. It
is not a registered device class but the instance is tight to a
wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a
phy/cfg802154 registered device). We currently don't support a HardMAC
transceiver and I think because this misunderstanding came up.

That means as far I see you should move the most of those attributes
to per wpan_dev instead of per cfg802154.

- Alex
Alexander Aring Dec. 29, 2021, 1:45 a.m. UTC | #3
Hi,

On Tue, 28 Dec 2021 at 17:22, Alexander Aring <alex.aring@gmail.com> wrote:
...
> That means as far I see you should move the most of those attributes
> to per wpan_dev instead of per cfg802154.

Sorry that's wrong.

I see now, that the result for a scan on every possible wpan_dev for a
specific wpan_phy should return the same result, that's why it belongs
to cfg802154 and this is correct (as a cfg802154 has a 1:1 mapping to
wpan_phy).
Same as in wireless...

- Alex
Miquel Raynal Jan. 4, 2022, 2:41 p.m. UTC | #4
Hi Jakub,

kuba@kernel.org wrote on Wed, 22 Dec 2021 12:55:55 -0800:

> On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote:
> > +/* Maximum number of PAN entries to store */
> > +static int max_pan_entries = 100;
> > +module_param(max_pan_entries, uint, 0644);
> > +MODULE_PARM_DESC(max_pan_entries,
> > +		 "Maximum number of PANs to discover per scan (default is 100)");
> > +
> > +static int pan_expiration = 60;
> > +module_param(pan_expiration, uint, 0644);
> > +MODULE_PARM_DESC(pan_expiration,
> > +		 "Expiration of the scan validity in seconds (default is 60s)");  
> 
> Can these be per-device control knobs? Module params are rarely the
> best answer.

I believe we can do that on a per FFD device basis (for now it will be
on a per-device basis, but later when we will have the necessary
information we might do something more fine grained). Would a couple of
sysfs entries work?

Thanks,
Miquèl
Jakub Kicinski Jan. 4, 2022, 3:01 p.m. UTC | #5
On Tue, 4 Jan 2022 15:41:51 +0100 Miquel Raynal wrote:
> > On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote:  
> > > +/* Maximum number of PAN entries to store */
> > > +static int max_pan_entries = 100;
> > > +module_param(max_pan_entries, uint, 0644);
> > > +MODULE_PARM_DESC(max_pan_entries,
> > > +		 "Maximum number of PANs to discover per scan (default is 100)");
> > > +
> > > +static int pan_expiration = 60;
> > > +module_param(pan_expiration, uint, 0644);
> > > +MODULE_PARM_DESC(pan_expiration,
> > > +		 "Expiration of the scan validity in seconds (default is 60s)");    
> > 
> > Can these be per-device control knobs? Module params are rarely the
> > best answer.  
> 
> I believe we can do that on a per FFD device basis (for now it will be
> on a per-device basis, but later when we will have the necessary
> information we might do something more fine grained). Would a couple of
> sysfs entries work?

Is there no netlink object where this would fit? Sorry, I'm not at all
familiar with WPAN. If it's orthogonal to current cfg802154 objects
sysfs is fine, I guess.
Miquel Raynal Jan. 4, 2022, 3:05 p.m. UTC | #6
Hi Alexander,

alex.aring@gmail.com wrote on Tue, 28 Dec 2021 17:22:38 -0500:

> Hi,
> 
> On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> >
> > Let's introduce the basics of PAN management:
> > - structures defining PANs
> > - helpers for PANs registration
> > - helpers discarding old PANs
> >  
> 
> I think there exists a little misunderstanding about how the
> architecture is between the structures wpan_phy, wpan_dev and
> cfg802154.
> 
>  - wpan_phy: represents the PHY layer of IEEE 802154 and is a
> registered device class.
>  - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface.
> 
> You can have multiple wpan_dev operate on one wpan_phy. To my best
> knowledge it's like having multiple access points running on one phy
> (wireless) or macvlan on ethernet. You can actually do that with the
> mac802154_hwsim driver. However as there exists currently no (as my
> knowledge) hardware which supports e.g. multiple address filters we
> wanted to be prepared for to support such handling. Although, there
> exists some transceivers which support something like a "pan bridge"
> which goes into such a direction.
> 
> What is a cfg802154 registered device? Well, at first it offers an
> interface between SoftMAC and HardMAC from nl802154, that's the
> cfg802154_ops structure. In theory a HardMAC transceiver would bypass
> the SoftMAC stack by implementing "cfg802154_ops" on the driver layer
> and try to do everything there as much as possible to support it. It
> is not a registered device class but the instance is tight to a
> wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a
> phy/cfg802154 registered device). We currently don't support a HardMAC
> transceiver and I think because this misunderstanding came up.

Thanks for the explanation, I think it helps because the relationship
between wpan_dev and wpan_phy was not yet fully clear to me.

In order to clarify further your explanation and be sure that I
understand it the correct way, I tried to picture the above explanation
into a figure. Would you mind looking at it and tell me if something
does not fit?

https://bootlin.com/~miquel/ieee802154.pdf

Thanks,
Miquèl
Miquel Raynal Jan. 4, 2022, 3:13 p.m. UTC | #7
Hi Jakub,

kuba@kernel.org wrote on Tue, 4 Jan 2022 07:01:27 -0800:

> On Tue, 4 Jan 2022 15:41:51 +0100 Miquel Raynal wrote:
> > > On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote:    
> > > > +/* Maximum number of PAN entries to store */
> > > > +static int max_pan_entries = 100;
> > > > +module_param(max_pan_entries, uint, 0644);
> > > > +MODULE_PARM_DESC(max_pan_entries,
> > > > +		 "Maximum number of PANs to discover per scan (default is 100)");
> > > > +
> > > > +static int pan_expiration = 60;
> > > > +module_param(pan_expiration, uint, 0644);
> > > > +MODULE_PARM_DESC(pan_expiration,
> > > > +		 "Expiration of the scan validity in seconds (default is 60s)");      
> > > 
> > > Can these be per-device control knobs? Module params are rarely the
> > > best answer.    
> > 
> > I believe we can do that on a per FFD device basis (for now it will be
> > on a per-device basis, but later when we will have the necessary
> > information we might do something more fine grained). Would a couple of
> > sysfs entries work?  
> 
> Is there no netlink object where this would fit? Sorry, I'm not at all
> familiar with WPAN. If it's orthogonal to current cfg802154 objects
> sysfs is fine, I guess.

Yes it's definitely possible to add a netlink arg for these two
parameters as well.

Thanks,
Miquèl
Alexander Aring Jan. 4, 2022, 10:07 p.m. UTC | #8
Hi,

On Tue, 4 Jan 2022 at 10:05, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Alexander,
>
> alex.aring@gmail.com wrote on Tue, 28 Dec 2021 17:22:38 -0500:
>
> > Hi,
> >
> > On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > >
> > > Let's introduce the basics of PAN management:
> > > - structures defining PANs
> > > - helpers for PANs registration
> > > - helpers discarding old PANs
> > >
> >
> > I think there exists a little misunderstanding about how the
> > architecture is between the structures wpan_phy, wpan_dev and
> > cfg802154.
> >
> >  - wpan_phy: represents the PHY layer of IEEE 802154 and is a
> > registered device class.
> >  - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface.
> >
> > You can have multiple wpan_dev operate on one wpan_phy. To my best
> > knowledge it's like having multiple access points running on one phy
> > (wireless) or macvlan on ethernet. You can actually do that with the
> > mac802154_hwsim driver. However as there exists currently no (as my
> > knowledge) hardware which supports e.g. multiple address filters we
> > wanted to be prepared for to support such handling. Although, there
> > exists some transceivers which support something like a "pan bridge"
> > which goes into such a direction.
> >
> > What is a cfg802154 registered device? Well, at first it offers an
> > interface between SoftMAC and HardMAC from nl802154, that's the
> > cfg802154_ops structure. In theory a HardMAC transceiver would bypass
> > the SoftMAC stack by implementing "cfg802154_ops" on the driver layer
> > and try to do everything there as much as possible to support it. It
> > is not a registered device class but the instance is tight to a
> > wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a
> > phy/cfg802154 registered device). We currently don't support a HardMAC
> > transceiver and I think because this misunderstanding came up.
>
> Thanks for the explanation, I think it helps because the relationship
> between wpan_dev and wpan_phy was not yet fully clear to me.
>
> In order to clarify further your explanation and be sure that I
> understand it the correct way, I tried to picture the above explanation
> into a figure. Would you mind looking at it and tell me if something
> does not fit?
>
> https://bootlin.com/~miquel/ieee802154.pdf

I think so, yes... if a transceiver has e.g. two antennas/phy's it can
also register two phy's and so on... then phy's can also move into net
namespaces (like what we do for hwsim for routing testing [0]). Should
keep that in mind.

- Alex

[0] https://github.com/linux-wpan/rpld/blob/nonstoring_mode/test/ns_setup
diff mbox series

Patch

diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h
index 4f36003bca98..4402f93cda32 100644
--- a/include/net/cfg802154.h
+++ b/include/net/cfg802154.h
@@ -48,6 +48,24 @@  struct ieee802154_addr {
 	};
 };
 
+/**
+ * struct ieee802154_pan_desc - PAN descriptor information
+ * @coord: PAN ID and coordinator address
+ * @page: page this PAN is on
+ * @channel: channel this PAN is on
+ * @superframe_spec: SuperFrame specification as received
+ * @link_quality: link quality indicator at which the beacon was received
+ * @gts_permit: the PAN coordinator accepts GTS requests
+ */
+struct ieee802154_pan_desc {
+	struct ieee802154_addr *coord;
+	u8 page;
+	u8 channel;
+	u16 superframe_spec;
+	u8 link_quality;
+	bool gts_permit;
+};
+
 struct cfg802154_ops {
 	struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
 							   const char *name,
@@ -415,4 +433,17 @@  static inline const char *wpan_phy_name(struct wpan_phy *phy)
 	return dev_name(&phy->dev);
 }
 
+/**
+ * cfg802154_record_pan - Advertize a new PAN following a beacon's reception
+ * @wpan_phy: PHY receiving the beacon
+ * @pan: PAN descriptor
+ *
+ * Tells the internal pan management layer to either register this PAN if it is
+ * new or at least update its entry if already discovered.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int cfg802154_record_pan(struct wpan_phy *wpan_phy,
+			 struct ieee802154_pan_desc *pan);
+
 #endif /* __NET_CFG802154_H */
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index f05b7bdae2aa..6b7c66de730d 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -4,7 +4,7 @@  obj-$(CONFIG_IEEE802154_SOCKET) += ieee802154_socket.o
 obj-y += 6lowpan/
 
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
-                header_ops.o sysfs.o nl802154.o trace.o
+                header_ops.o sysfs.o nl802154.o pan.o trace.o
 ieee802154_socket-y := socket.o
 
 CFLAGS_trace.o := -I$(src)
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index de259b5170ab..0f73e0571883 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -115,6 +115,8 @@  wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 		kfree(rdev);
 		return NULL;
 	}
+	spin_lock_init(&rdev->pan_lock);
+	INIT_LIST_HEAD(&rdev->pan_list);
 
 	/* atomic_inc_return makes it start at 1, make it start at 0 */
 	rdev->wpan_phy_idx--;
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
index 1c19f575d574..dea1d6f70489 100644
--- a/net/ieee802154/core.h
+++ b/net/ieee802154/core.h
@@ -22,6 +22,12 @@  struct cfg802154_registered_device {
 	struct list_head wpan_dev_list;
 	int devlist_generation, wpan_dev_id;
 
+	/* pan management */
+	spinlock_t pan_lock;
+	struct list_head pan_list;
+	int pan_entries;
+	int pan_generation;
+
 	/* must be last because of the way we do wpan_phy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN
 	 */
@@ -39,6 +45,17 @@  wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
 extern struct list_head cfg802154_rdev_list;
 extern int cfg802154_rdev_list_generation;
 
+struct cfg802154_internal_pan {
+	struct list_head list;
+	unsigned long discovery_ts;
+	struct ieee802154_pan_desc desc;
+};
+
+/* Always update the list by dropping the expired PANs before iterating */
+#define ieee802154_for_each_pan(pan, rdev)				\
+	cfg802154_expire_pans(rdev);					\
+	list_for_each_entry((pan), &(rdev)->pan_list, list)
+
 int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
 			   struct net *net);
 /* free object */
@@ -47,4 +64,7 @@  struct cfg802154_registered_device *
 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
 struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx);
 
+void cfg802154_expire_pans(struct cfg802154_registered_device *rdev);
+void cfg802154_flush_pans(struct cfg802154_registered_device *rdev);
+
 #endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c
new file mode 100644
index 000000000000..c71a3664d5c3
--- /dev/null
+++ b/net/ieee802154/pan.c
@@ -0,0 +1,208 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IEEE 802.15.4 PAN management
+ *
+ * Copyright (C) Qorvo, 2021
+ * Authors:
+ *   - David Girault <david.girault@qorvo.com>
+ *   - Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+#include <net/af_ieee802154.h>
+
+#include "ieee802154.h"
+#include "core.h"
+
+/* Maximum number of PAN entries to store */
+static int max_pan_entries = 100;
+module_param(max_pan_entries, uint, 0644);
+MODULE_PARM_DESC(max_pan_entries,
+		 "Maximum number of PANs to discover per scan (default is 100)");
+
+static int pan_expiration = 60;
+module_param(pan_expiration, uint, 0644);
+MODULE_PARM_DESC(pan_expiration,
+		 "Expiration of the scan validity in seconds (default is 60s)");
+
+static struct cfg802154_internal_pan *
+cfg802154_alloc_pan(struct ieee802154_pan_desc *desc)
+{
+	struct cfg802154_internal_pan *new;
+	struct ieee802154_addr *coord;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return ERR_PTR(-ENOMEM);
+
+	coord = kzalloc(sizeof(*coord), GFP_KERNEL);
+	if (!coord) {
+		kfree(new);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	new->discovery_ts = jiffies;
+	new->desc = *desc;
+
+	*coord = *desc->coord;
+	new->desc.coord = coord;
+
+	return new;
+}
+
+static void cfg802154_free_pan(struct cfg802154_internal_pan *pan)
+{
+	kfree(pan->desc.coord);
+	kfree(pan);
+}
+
+static void cfg802154_unlink_pan(struct cfg802154_registered_device *rdev,
+				 struct cfg802154_internal_pan *pan)
+{
+	lockdep_assert_held(&rdev->pan_lock);
+
+	list_del(&pan->list);
+	cfg802154_free_pan(pan);
+	rdev->pan_entries--;
+	rdev->pan_generation++;
+}
+
+static void cfg802154_link_pan(struct cfg802154_registered_device *rdev,
+			       struct cfg802154_internal_pan *pan)
+{
+	lockdep_assert_held(&rdev->pan_lock);
+
+	list_add_tail(&pan->list, &rdev->pan_list);
+	rdev->pan_entries++;
+	rdev->pan_generation++;
+}
+
+void cfg802154_expire_pans(struct cfg802154_registered_device *rdev)
+{
+	unsigned long expiration_time = jiffies - (pan_expiration * HZ);
+	struct cfg802154_internal_pan *pan, *tmp;
+
+	lockdep_assert_held(&rdev->pan_lock);
+
+	list_for_each_entry_safe(pan, tmp, &rdev->pan_list, list) {
+		if (!time_after(expiration_time, pan->discovery_ts))
+			continue;
+
+		cfg802154_unlink_pan(rdev, pan);
+	}
+}
+EXPORT_SYMBOL(cfg802154_expire_pans);
+
+static void cfg802154_expire_oldest_pan(struct cfg802154_registered_device *rdev)
+{
+	struct cfg802154_internal_pan *pan, *oldest;
+
+	lockdep_assert_held(&rdev->pan_lock);
+
+	if (WARN_ON(!max_pan_entries || list_empty(&rdev->pan_list)))
+		return;
+
+	oldest = list_first_entry(&rdev->pan_list,
+				  struct cfg802154_internal_pan, list);
+
+	list_for_each_entry(pan, &rdev->pan_list, list) {
+		if (!time_before(oldest->discovery_ts, pan->discovery_ts))
+			oldest = pan;
+	}
+
+	cfg802154_unlink_pan(rdev, oldest);
+}
+
+void cfg802154_flush_pans(struct cfg802154_registered_device *rdev)
+{
+	struct cfg802154_internal_pan *pan, *tmp;
+
+	lockdep_assert_held(&rdev->pan_lock);
+
+	list_for_each_entry_safe(pan, tmp, &rdev->pan_list, list)
+		cfg802154_unlink_pan(rdev, pan);
+}
+EXPORT_SYMBOL(cfg802154_flush_pans);
+
+static bool cfg802154_same_pan(struct ieee802154_pan_desc *a,
+			       struct ieee802154_pan_desc *b)
+{
+	int ret;
+
+	if (a->page != b->page)
+		return false;
+
+	if (a->channel != b->channel)
+		return false;
+
+	ret = memcmp(&a->coord->pan_id, &b->coord->pan_id,
+		     sizeof(a->coord->pan_id));
+	if (ret)
+		return false;
+
+	if (a->coord->mode != b->coord->mode)
+		return false;
+
+	if (a->coord->mode == IEEE802154_ADDR_SHORT)
+		ret = memcmp(&a->coord->short_addr, &b->coord->short_addr,
+			     IEEE802154_SHORT_ADDR_LEN);
+	else
+		ret = memcmp(&a->coord->extended_addr, &b->coord->extended_addr,
+			     IEEE802154_EXTENDED_ADDR_LEN);
+
+	return true;
+}
+
+static struct cfg802154_internal_pan *
+cfg802154_find_matching_pan(struct cfg802154_registered_device *rdev,
+			    struct cfg802154_internal_pan *tmp)
+{
+	struct cfg802154_internal_pan *pan;
+
+	list_for_each_entry(pan, &rdev->pan_list, list) {
+		if (cfg802154_same_pan(&pan->desc, &tmp->desc))
+			return pan;
+	}
+
+	return NULL;
+}
+
+static void cfg802154_pan_update(struct cfg802154_registered_device *rdev,
+				 struct cfg802154_internal_pan *new)
+{
+	struct cfg802154_internal_pan *found;
+
+	spin_lock_bh(&rdev->pan_lock);
+
+	found = cfg802154_find_matching_pan(rdev, new);
+	if (found)
+		cfg802154_unlink_pan(rdev, found);
+
+	if (unlikely(rdev->pan_entries >= max_pan_entries))
+		cfg802154_expire_oldest_pan(rdev);
+
+	cfg802154_link_pan(rdev, new);
+
+	spin_unlock_bh(&rdev->pan_lock);
+}
+
+int cfg802154_record_pan(struct wpan_phy *wpan_phy,
+			 struct ieee802154_pan_desc *desc)
+{
+	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
+	struct cfg802154_internal_pan *new;
+
+	new = cfg802154_alloc_pan(desc);
+	if (IS_ERR(new))
+		return (PTR_ERR(new));
+
+	cfg802154_pan_update(rdev, new);
+
+	return 0;
+}
+EXPORT_SYMBOL(cfg802154_record_pan);