xref: /openbmc/linux/net/ieee802154/nl-phy.c (revision bb1cafb8fc414d6dbe933f888df6540c2ef02101)
11eaa9d03SDmitry Eremin-Solenikov /*
21eaa9d03SDmitry Eremin-Solenikov  * Netlink inteface for IEEE 802.15.4 stack
31eaa9d03SDmitry Eremin-Solenikov  *
41eaa9d03SDmitry Eremin-Solenikov  * Copyright 2007, 2008 Siemens AG
51eaa9d03SDmitry Eremin-Solenikov  *
61eaa9d03SDmitry Eremin-Solenikov  * This program is free software; you can redistribute it and/or modify
71eaa9d03SDmitry Eremin-Solenikov  * it under the terms of the GNU General Public License version 2
81eaa9d03SDmitry Eremin-Solenikov  * as published by the Free Software Foundation.
91eaa9d03SDmitry Eremin-Solenikov  *
101eaa9d03SDmitry Eremin-Solenikov  * This program is distributed in the hope that it will be useful,
111eaa9d03SDmitry Eremin-Solenikov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
121eaa9d03SDmitry Eremin-Solenikov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131eaa9d03SDmitry Eremin-Solenikov  * GNU General Public License for more details.
141eaa9d03SDmitry Eremin-Solenikov  *
151eaa9d03SDmitry Eremin-Solenikov  * You should have received a copy of the GNU General Public License along
161eaa9d03SDmitry Eremin-Solenikov  * with this program; if not, write to the Free Software Foundation, Inc.,
171eaa9d03SDmitry Eremin-Solenikov  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
181eaa9d03SDmitry Eremin-Solenikov  *
191eaa9d03SDmitry Eremin-Solenikov  * Written by:
201eaa9d03SDmitry Eremin-Solenikov  * Sergey Lapin <slapin@ossfans.org>
211eaa9d03SDmitry Eremin-Solenikov  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
221eaa9d03SDmitry Eremin-Solenikov  * Maxim Osipov <maxim.osipov@siemens.com>
231eaa9d03SDmitry Eremin-Solenikov  */
241eaa9d03SDmitry Eremin-Solenikov 
251eaa9d03SDmitry Eremin-Solenikov #include <linux/kernel.h>
261eaa9d03SDmitry Eremin-Solenikov #include <net/netlink.h>
271eaa9d03SDmitry Eremin-Solenikov #include <net/genetlink.h>
281eaa9d03SDmitry Eremin-Solenikov #include <net/wpan-phy.h>
29*bb1cafb8SDmitry Eremin-Solenikov #include <net/af_ieee802154.h>
30*bb1cafb8SDmitry Eremin-Solenikov #include <net/ieee802154_netdev.h>
31*bb1cafb8SDmitry Eremin-Solenikov #include <net/rtnetlink.h> /* for rtnl_{un,}lock */
321eaa9d03SDmitry Eremin-Solenikov #include <linux/nl802154.h>
331eaa9d03SDmitry Eremin-Solenikov 
341eaa9d03SDmitry Eremin-Solenikov #include "ieee802154.h"
351eaa9d03SDmitry Eremin-Solenikov 
361eaa9d03SDmitry Eremin-Solenikov static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,
371eaa9d03SDmitry Eremin-Solenikov 	u32 seq, int flags, struct wpan_phy *phy)
381eaa9d03SDmitry Eremin-Solenikov {
391eaa9d03SDmitry Eremin-Solenikov 	void *hdr;
401eaa9d03SDmitry Eremin-Solenikov 	int i, pages = 0;
411eaa9d03SDmitry Eremin-Solenikov 	uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL);
421eaa9d03SDmitry Eremin-Solenikov 
431eaa9d03SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
441eaa9d03SDmitry Eremin-Solenikov 
451eaa9d03SDmitry Eremin-Solenikov 	if (!buf)
461eaa9d03SDmitry Eremin-Solenikov 		goto out;
471eaa9d03SDmitry Eremin-Solenikov 
481eaa9d03SDmitry Eremin-Solenikov 	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
491eaa9d03SDmitry Eremin-Solenikov 		IEEE802154_LIST_PHY);
501eaa9d03SDmitry Eremin-Solenikov 	if (!hdr)
511eaa9d03SDmitry Eremin-Solenikov 		goto out;
521eaa9d03SDmitry Eremin-Solenikov 
531eaa9d03SDmitry Eremin-Solenikov 	mutex_lock(&phy->pib_lock);
541eaa9d03SDmitry Eremin-Solenikov 	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
551eaa9d03SDmitry Eremin-Solenikov 
561eaa9d03SDmitry Eremin-Solenikov 	NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page);
571eaa9d03SDmitry Eremin-Solenikov 	NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel);
581eaa9d03SDmitry Eremin-Solenikov 	for (i = 0; i < 32; i++) {
591eaa9d03SDmitry Eremin-Solenikov 		if (phy->channels_supported[i])
601eaa9d03SDmitry Eremin-Solenikov 			buf[pages++] = phy->channels_supported[i] | (i << 27);
611eaa9d03SDmitry Eremin-Solenikov 	}
621eaa9d03SDmitry Eremin-Solenikov 	if (pages)
631eaa9d03SDmitry Eremin-Solenikov 		NLA_PUT(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
641eaa9d03SDmitry Eremin-Solenikov 				pages * sizeof(uint32_t), buf);
651eaa9d03SDmitry Eremin-Solenikov 
661eaa9d03SDmitry Eremin-Solenikov 	mutex_unlock(&phy->pib_lock);
671eaa9d03SDmitry Eremin-Solenikov 	return genlmsg_end(msg, hdr);
681eaa9d03SDmitry Eremin-Solenikov 
691eaa9d03SDmitry Eremin-Solenikov nla_put_failure:
701eaa9d03SDmitry Eremin-Solenikov 	mutex_unlock(&phy->pib_lock);
711eaa9d03SDmitry Eremin-Solenikov 	genlmsg_cancel(msg, hdr);
721eaa9d03SDmitry Eremin-Solenikov out:
731eaa9d03SDmitry Eremin-Solenikov 	kfree(buf);
741eaa9d03SDmitry Eremin-Solenikov 	return -EMSGSIZE;
751eaa9d03SDmitry Eremin-Solenikov }
761eaa9d03SDmitry Eremin-Solenikov 
771eaa9d03SDmitry Eremin-Solenikov static int ieee802154_list_phy(struct sk_buff *skb,
781eaa9d03SDmitry Eremin-Solenikov 	struct genl_info *info)
791eaa9d03SDmitry Eremin-Solenikov {
801eaa9d03SDmitry Eremin-Solenikov 	/* Request for interface name, index, type, IEEE address,
811eaa9d03SDmitry Eremin-Solenikov 	   PAN Id, short address */
821eaa9d03SDmitry Eremin-Solenikov 	struct sk_buff *msg;
831eaa9d03SDmitry Eremin-Solenikov 	struct wpan_phy *phy;
841eaa9d03SDmitry Eremin-Solenikov 	const char *name;
851eaa9d03SDmitry Eremin-Solenikov 	int rc = -ENOBUFS;
861eaa9d03SDmitry Eremin-Solenikov 
871eaa9d03SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
881eaa9d03SDmitry Eremin-Solenikov 
891eaa9d03SDmitry Eremin-Solenikov 	if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
901eaa9d03SDmitry Eremin-Solenikov 		return -EINVAL;
911eaa9d03SDmitry Eremin-Solenikov 
921eaa9d03SDmitry Eremin-Solenikov 	name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
931eaa9d03SDmitry Eremin-Solenikov 	if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
941eaa9d03SDmitry Eremin-Solenikov 		return -EINVAL; /* phy name should be null-terminated */
951eaa9d03SDmitry Eremin-Solenikov 
961eaa9d03SDmitry Eremin-Solenikov 
971eaa9d03SDmitry Eremin-Solenikov 	phy = wpan_phy_find(name);
981eaa9d03SDmitry Eremin-Solenikov 	if (!phy)
991eaa9d03SDmitry Eremin-Solenikov 		return -ENODEV;
1001eaa9d03SDmitry Eremin-Solenikov 
1011eaa9d03SDmitry Eremin-Solenikov 	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1021eaa9d03SDmitry Eremin-Solenikov 	if (!msg)
1031eaa9d03SDmitry Eremin-Solenikov 		goto out_dev;
1041eaa9d03SDmitry Eremin-Solenikov 
1051eaa9d03SDmitry Eremin-Solenikov 	rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq,
1061eaa9d03SDmitry Eremin-Solenikov 			0, phy);
1071eaa9d03SDmitry Eremin-Solenikov 	if (rc < 0)
1081eaa9d03SDmitry Eremin-Solenikov 		goto out_free;
1091eaa9d03SDmitry Eremin-Solenikov 
1101eaa9d03SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
1111eaa9d03SDmitry Eremin-Solenikov 
1121eaa9d03SDmitry Eremin-Solenikov 	return genlmsg_reply(msg, info);
1131eaa9d03SDmitry Eremin-Solenikov out_free:
1141eaa9d03SDmitry Eremin-Solenikov 	nlmsg_free(msg);
1151eaa9d03SDmitry Eremin-Solenikov out_dev:
1161eaa9d03SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
1171eaa9d03SDmitry Eremin-Solenikov 	return rc;
1181eaa9d03SDmitry Eremin-Solenikov 
1191eaa9d03SDmitry Eremin-Solenikov }
1201eaa9d03SDmitry Eremin-Solenikov 
1211eaa9d03SDmitry Eremin-Solenikov struct dump_phy_data {
1221eaa9d03SDmitry Eremin-Solenikov 	struct sk_buff *skb;
1231eaa9d03SDmitry Eremin-Solenikov 	struct netlink_callback *cb;
1241eaa9d03SDmitry Eremin-Solenikov 	int idx, s_idx;
1251eaa9d03SDmitry Eremin-Solenikov };
1261eaa9d03SDmitry Eremin-Solenikov 
1271eaa9d03SDmitry Eremin-Solenikov static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
1281eaa9d03SDmitry Eremin-Solenikov {
1291eaa9d03SDmitry Eremin-Solenikov 	int rc;
1301eaa9d03SDmitry Eremin-Solenikov 	struct dump_phy_data *data = _data;
1311eaa9d03SDmitry Eremin-Solenikov 
1321eaa9d03SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
1331eaa9d03SDmitry Eremin-Solenikov 
1341eaa9d03SDmitry Eremin-Solenikov 	if (data->idx++ < data->s_idx)
1351eaa9d03SDmitry Eremin-Solenikov 		return 0;
1361eaa9d03SDmitry Eremin-Solenikov 
1371eaa9d03SDmitry Eremin-Solenikov 	rc = ieee802154_nl_fill_phy(data->skb,
1381eaa9d03SDmitry Eremin-Solenikov 			NETLINK_CB(data->cb->skb).pid,
1391eaa9d03SDmitry Eremin-Solenikov 			data->cb->nlh->nlmsg_seq,
1401eaa9d03SDmitry Eremin-Solenikov 			NLM_F_MULTI,
1411eaa9d03SDmitry Eremin-Solenikov 			phy);
1421eaa9d03SDmitry Eremin-Solenikov 
1431eaa9d03SDmitry Eremin-Solenikov 	if (rc < 0) {
1441eaa9d03SDmitry Eremin-Solenikov 		data->idx--;
1451eaa9d03SDmitry Eremin-Solenikov 		return rc;
1461eaa9d03SDmitry Eremin-Solenikov 	}
1471eaa9d03SDmitry Eremin-Solenikov 
1481eaa9d03SDmitry Eremin-Solenikov 	return 0;
1491eaa9d03SDmitry Eremin-Solenikov }
1501eaa9d03SDmitry Eremin-Solenikov 
1511eaa9d03SDmitry Eremin-Solenikov static int ieee802154_dump_phy(struct sk_buff *skb,
1521eaa9d03SDmitry Eremin-Solenikov 	struct netlink_callback *cb)
1531eaa9d03SDmitry Eremin-Solenikov {
1541eaa9d03SDmitry Eremin-Solenikov 	struct dump_phy_data data = {
1551eaa9d03SDmitry Eremin-Solenikov 		.cb = cb,
1561eaa9d03SDmitry Eremin-Solenikov 		.skb = skb,
1571eaa9d03SDmitry Eremin-Solenikov 		.s_idx = cb->args[0],
1581eaa9d03SDmitry Eremin-Solenikov 		.idx = 0,
1591eaa9d03SDmitry Eremin-Solenikov 	};
1601eaa9d03SDmitry Eremin-Solenikov 
1611eaa9d03SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
1621eaa9d03SDmitry Eremin-Solenikov 
1631eaa9d03SDmitry Eremin-Solenikov 	wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
1641eaa9d03SDmitry Eremin-Solenikov 
1651eaa9d03SDmitry Eremin-Solenikov 	cb->args[0] = data.idx;
1661eaa9d03SDmitry Eremin-Solenikov 
1671eaa9d03SDmitry Eremin-Solenikov 	return skb->len;
1681eaa9d03SDmitry Eremin-Solenikov }
1691eaa9d03SDmitry Eremin-Solenikov 
170*bb1cafb8SDmitry Eremin-Solenikov static int ieee802154_add_iface(struct sk_buff *skb,
171*bb1cafb8SDmitry Eremin-Solenikov 		struct genl_info *info)
172*bb1cafb8SDmitry Eremin-Solenikov {
173*bb1cafb8SDmitry Eremin-Solenikov 	struct sk_buff *msg;
174*bb1cafb8SDmitry Eremin-Solenikov 	struct wpan_phy *phy;
175*bb1cafb8SDmitry Eremin-Solenikov 	const char *name;
176*bb1cafb8SDmitry Eremin-Solenikov 	const char *devname;
177*bb1cafb8SDmitry Eremin-Solenikov 	int rc = -ENOBUFS;
178*bb1cafb8SDmitry Eremin-Solenikov 	struct net_device *dev;
179*bb1cafb8SDmitry Eremin-Solenikov 
180*bb1cafb8SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
181*bb1cafb8SDmitry Eremin-Solenikov 
182*bb1cafb8SDmitry Eremin-Solenikov 	if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
183*bb1cafb8SDmitry Eremin-Solenikov 		return -EINVAL;
184*bb1cafb8SDmitry Eremin-Solenikov 
185*bb1cafb8SDmitry Eremin-Solenikov 	name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
186*bb1cafb8SDmitry Eremin-Solenikov 	if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
187*bb1cafb8SDmitry Eremin-Solenikov 		return -EINVAL; /* phy name should be null-terminated */
188*bb1cafb8SDmitry Eremin-Solenikov 
189*bb1cafb8SDmitry Eremin-Solenikov 	if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
190*bb1cafb8SDmitry Eremin-Solenikov 		devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
191*bb1cafb8SDmitry Eremin-Solenikov 		if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
192*bb1cafb8SDmitry Eremin-Solenikov 				!= '\0')
193*bb1cafb8SDmitry Eremin-Solenikov 			return -EINVAL; /* phy name should be null-terminated */
194*bb1cafb8SDmitry Eremin-Solenikov 	} else  {
195*bb1cafb8SDmitry Eremin-Solenikov 		devname = "wpan%d";
196*bb1cafb8SDmitry Eremin-Solenikov 	}
197*bb1cafb8SDmitry Eremin-Solenikov 
198*bb1cafb8SDmitry Eremin-Solenikov 	if (strlen(devname) >= IFNAMSIZ)
199*bb1cafb8SDmitry Eremin-Solenikov 		return -ENAMETOOLONG;
200*bb1cafb8SDmitry Eremin-Solenikov 
201*bb1cafb8SDmitry Eremin-Solenikov 	phy = wpan_phy_find(name);
202*bb1cafb8SDmitry Eremin-Solenikov 	if (!phy)
203*bb1cafb8SDmitry Eremin-Solenikov 		return -ENODEV;
204*bb1cafb8SDmitry Eremin-Solenikov 
205*bb1cafb8SDmitry Eremin-Solenikov 	msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
206*bb1cafb8SDmitry Eremin-Solenikov 	if (!msg)
207*bb1cafb8SDmitry Eremin-Solenikov 		goto out_dev;
208*bb1cafb8SDmitry Eremin-Solenikov 
209*bb1cafb8SDmitry Eremin-Solenikov 	if (!phy->add_iface) {
210*bb1cafb8SDmitry Eremin-Solenikov 		rc = -EINVAL;
211*bb1cafb8SDmitry Eremin-Solenikov 		goto nla_put_failure;
212*bb1cafb8SDmitry Eremin-Solenikov 	}
213*bb1cafb8SDmitry Eremin-Solenikov 
214*bb1cafb8SDmitry Eremin-Solenikov 	dev = phy->add_iface(phy, devname);
215*bb1cafb8SDmitry Eremin-Solenikov 	if (IS_ERR(dev)) {
216*bb1cafb8SDmitry Eremin-Solenikov 		rc = PTR_ERR(dev);
217*bb1cafb8SDmitry Eremin-Solenikov 		goto nla_put_failure;
218*bb1cafb8SDmitry Eremin-Solenikov 	}
219*bb1cafb8SDmitry Eremin-Solenikov 
220*bb1cafb8SDmitry Eremin-Solenikov 	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
221*bb1cafb8SDmitry Eremin-Solenikov 	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
222*bb1cafb8SDmitry Eremin-Solenikov 
223*bb1cafb8SDmitry Eremin-Solenikov 	dev_put(dev);
224*bb1cafb8SDmitry Eremin-Solenikov 
225*bb1cafb8SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
226*bb1cafb8SDmitry Eremin-Solenikov 
227*bb1cafb8SDmitry Eremin-Solenikov 	return ieee802154_nl_reply(msg, info);
228*bb1cafb8SDmitry Eremin-Solenikov 
229*bb1cafb8SDmitry Eremin-Solenikov nla_put_failure:
230*bb1cafb8SDmitry Eremin-Solenikov 	nlmsg_free(msg);
231*bb1cafb8SDmitry Eremin-Solenikov out_dev:
232*bb1cafb8SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
233*bb1cafb8SDmitry Eremin-Solenikov 	return rc;
234*bb1cafb8SDmitry Eremin-Solenikov }
235*bb1cafb8SDmitry Eremin-Solenikov 
236*bb1cafb8SDmitry Eremin-Solenikov static int ieee802154_del_iface(struct sk_buff *skb,
237*bb1cafb8SDmitry Eremin-Solenikov 		struct genl_info *info)
238*bb1cafb8SDmitry Eremin-Solenikov {
239*bb1cafb8SDmitry Eremin-Solenikov 	struct sk_buff *msg;
240*bb1cafb8SDmitry Eremin-Solenikov 	struct wpan_phy *phy;
241*bb1cafb8SDmitry Eremin-Solenikov 	const char *name;
242*bb1cafb8SDmitry Eremin-Solenikov 	int rc;
243*bb1cafb8SDmitry Eremin-Solenikov 	struct net_device *dev;
244*bb1cafb8SDmitry Eremin-Solenikov 
245*bb1cafb8SDmitry Eremin-Solenikov 	pr_debug("%s\n", __func__);
246*bb1cafb8SDmitry Eremin-Solenikov 
247*bb1cafb8SDmitry Eremin-Solenikov 	if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
248*bb1cafb8SDmitry Eremin-Solenikov 		return -EINVAL;
249*bb1cafb8SDmitry Eremin-Solenikov 
250*bb1cafb8SDmitry Eremin-Solenikov 	name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
251*bb1cafb8SDmitry Eremin-Solenikov 	if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
252*bb1cafb8SDmitry Eremin-Solenikov 		return -EINVAL; /* name should be null-terminated */
253*bb1cafb8SDmitry Eremin-Solenikov 
254*bb1cafb8SDmitry Eremin-Solenikov 	dev = dev_get_by_name(genl_info_net(info), name);
255*bb1cafb8SDmitry Eremin-Solenikov 	if (!dev)
256*bb1cafb8SDmitry Eremin-Solenikov 		return -ENODEV;
257*bb1cafb8SDmitry Eremin-Solenikov 
258*bb1cafb8SDmitry Eremin-Solenikov 	phy = ieee802154_mlme_ops(dev)->get_phy(dev);
259*bb1cafb8SDmitry Eremin-Solenikov 	BUG_ON(!phy);
260*bb1cafb8SDmitry Eremin-Solenikov 
261*bb1cafb8SDmitry Eremin-Solenikov 	rc = -EINVAL;
262*bb1cafb8SDmitry Eremin-Solenikov 	/* phy name is optional, but should be checked if it's given */
263*bb1cafb8SDmitry Eremin-Solenikov 	if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
264*bb1cafb8SDmitry Eremin-Solenikov 		struct wpan_phy *phy2;
265*bb1cafb8SDmitry Eremin-Solenikov 
266*bb1cafb8SDmitry Eremin-Solenikov 		const char *pname =
267*bb1cafb8SDmitry Eremin-Solenikov 			nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
268*bb1cafb8SDmitry Eremin-Solenikov 		if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
269*bb1cafb8SDmitry Eremin-Solenikov 				!= '\0')
270*bb1cafb8SDmitry Eremin-Solenikov 			/* name should be null-terminated */
271*bb1cafb8SDmitry Eremin-Solenikov 			goto out_dev;
272*bb1cafb8SDmitry Eremin-Solenikov 
273*bb1cafb8SDmitry Eremin-Solenikov 		phy2 = wpan_phy_find(pname);
274*bb1cafb8SDmitry Eremin-Solenikov 		if (!phy2)
275*bb1cafb8SDmitry Eremin-Solenikov 			goto out_dev;
276*bb1cafb8SDmitry Eremin-Solenikov 
277*bb1cafb8SDmitry Eremin-Solenikov 		if (phy != phy2) {
278*bb1cafb8SDmitry Eremin-Solenikov 			wpan_phy_put(phy2);
279*bb1cafb8SDmitry Eremin-Solenikov 			goto out_dev;
280*bb1cafb8SDmitry Eremin-Solenikov 		}
281*bb1cafb8SDmitry Eremin-Solenikov 	}
282*bb1cafb8SDmitry Eremin-Solenikov 
283*bb1cafb8SDmitry Eremin-Solenikov 	rc = -ENOBUFS;
284*bb1cafb8SDmitry Eremin-Solenikov 
285*bb1cafb8SDmitry Eremin-Solenikov 	msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
286*bb1cafb8SDmitry Eremin-Solenikov 	if (!msg)
287*bb1cafb8SDmitry Eremin-Solenikov 		goto out_dev;
288*bb1cafb8SDmitry Eremin-Solenikov 
289*bb1cafb8SDmitry Eremin-Solenikov 	if (!phy->del_iface) {
290*bb1cafb8SDmitry Eremin-Solenikov 		rc = -EINVAL;
291*bb1cafb8SDmitry Eremin-Solenikov 		goto nla_put_failure;
292*bb1cafb8SDmitry Eremin-Solenikov 	}
293*bb1cafb8SDmitry Eremin-Solenikov 
294*bb1cafb8SDmitry Eremin-Solenikov 	rtnl_lock();
295*bb1cafb8SDmitry Eremin-Solenikov 	phy->del_iface(phy, dev);
296*bb1cafb8SDmitry Eremin-Solenikov 
297*bb1cafb8SDmitry Eremin-Solenikov 	/* We don't have device anymore */
298*bb1cafb8SDmitry Eremin-Solenikov 	dev_put(dev);
299*bb1cafb8SDmitry Eremin-Solenikov 	dev = NULL;
300*bb1cafb8SDmitry Eremin-Solenikov 
301*bb1cafb8SDmitry Eremin-Solenikov 	rtnl_unlock();
302*bb1cafb8SDmitry Eremin-Solenikov 
303*bb1cafb8SDmitry Eremin-Solenikov 
304*bb1cafb8SDmitry Eremin-Solenikov 	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
305*bb1cafb8SDmitry Eremin-Solenikov 	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name);
306*bb1cafb8SDmitry Eremin-Solenikov 
307*bb1cafb8SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
308*bb1cafb8SDmitry Eremin-Solenikov 
309*bb1cafb8SDmitry Eremin-Solenikov 	return ieee802154_nl_reply(msg, info);
310*bb1cafb8SDmitry Eremin-Solenikov 
311*bb1cafb8SDmitry Eremin-Solenikov nla_put_failure:
312*bb1cafb8SDmitry Eremin-Solenikov 	nlmsg_free(msg);
313*bb1cafb8SDmitry Eremin-Solenikov out_dev:
314*bb1cafb8SDmitry Eremin-Solenikov 	wpan_phy_put(phy);
315*bb1cafb8SDmitry Eremin-Solenikov 	if (dev)
316*bb1cafb8SDmitry Eremin-Solenikov 		dev_put(dev);
317*bb1cafb8SDmitry Eremin-Solenikov 
318*bb1cafb8SDmitry Eremin-Solenikov 	return rc;
319*bb1cafb8SDmitry Eremin-Solenikov }
320*bb1cafb8SDmitry Eremin-Solenikov 
3211eaa9d03SDmitry Eremin-Solenikov static struct genl_ops ieee802154_phy_ops[] = {
3221eaa9d03SDmitry Eremin-Solenikov 	IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
3231eaa9d03SDmitry Eremin-Solenikov 							ieee802154_dump_phy),
324*bb1cafb8SDmitry Eremin-Solenikov 	IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
325*bb1cafb8SDmitry Eremin-Solenikov 	IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
3261eaa9d03SDmitry Eremin-Solenikov };
3271eaa9d03SDmitry Eremin-Solenikov 
3281eaa9d03SDmitry Eremin-Solenikov /*
3291eaa9d03SDmitry Eremin-Solenikov  * No need to unregister as family unregistration will do it.
3301eaa9d03SDmitry Eremin-Solenikov  */
3311eaa9d03SDmitry Eremin-Solenikov int nl802154_phy_register(void)
3321eaa9d03SDmitry Eremin-Solenikov {
3331eaa9d03SDmitry Eremin-Solenikov 	int i;
3341eaa9d03SDmitry Eremin-Solenikov 	int rc;
3351eaa9d03SDmitry Eremin-Solenikov 
3361eaa9d03SDmitry Eremin-Solenikov 	for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) {
3371eaa9d03SDmitry Eremin-Solenikov 		rc = genl_register_ops(&nl802154_family,
3381eaa9d03SDmitry Eremin-Solenikov 				&ieee802154_phy_ops[i]);
3391eaa9d03SDmitry Eremin-Solenikov 		if (rc)
3401eaa9d03SDmitry Eremin-Solenikov 			return rc;
3411eaa9d03SDmitry Eremin-Solenikov 	}
3421eaa9d03SDmitry Eremin-Solenikov 
3431eaa9d03SDmitry Eremin-Solenikov 	return 0;
3441eaa9d03SDmitry Eremin-Solenikov }
345