xref: /openbmc/linux/net/ieee802154/nl-phy.c (revision 6ca00197)
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/kernel.h>
26 #include <linux/slab.h>
27 #include <linux/if_arp.h>
28 #include <net/netlink.h>
29 #include <net/genetlink.h>
30 #include <net/wpan-phy.h>
31 #include <net/af_ieee802154.h>
32 #include <net/ieee802154_netdev.h>
33 #include <net/rtnetlink.h> /* for rtnl_{un,}lock */
34 #include <linux/nl802154.h>
35 
36 #include "ieee802154.h"
37 
38 static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
39 	u32 seq, int flags, struct wpan_phy *phy)
40 {
41 	void *hdr;
42 	int i, pages = 0;
43 	uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL);
44 
45 	pr_debug("%s\n", __func__);
46 
47 	if (!buf)
48 		return -EMSGSIZE;
49 
50 	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
51 		IEEE802154_LIST_PHY);
52 	if (!hdr)
53 		goto out;
54 
55 	mutex_lock(&phy->pib_lock);
56 	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
57 	    nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
58 	    nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) ||
59 	    nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) ||
60 	    nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) ||
61 	    nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode) ||
62 	    nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level))
63 		goto nla_put_failure;
64 	for (i = 0; i < 32; i++) {
65 		if (phy->channels_supported[i])
66 			buf[pages++] = phy->channels_supported[i] | (i << 27);
67 	}
68 	if (pages &&
69 	    nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
70 		    pages * sizeof(uint32_t), buf))
71 		goto nla_put_failure;
72 	mutex_unlock(&phy->pib_lock);
73 	kfree(buf);
74 	return genlmsg_end(msg, hdr);
75 
76 nla_put_failure:
77 	mutex_unlock(&phy->pib_lock);
78 	genlmsg_cancel(msg, hdr);
79 out:
80 	kfree(buf);
81 	return -EMSGSIZE;
82 }
83 
84 int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
85 {
86 	/* Request for interface name, index, type, IEEE address,
87 	   PAN Id, short address */
88 	struct sk_buff *msg;
89 	struct wpan_phy *phy;
90 	const char *name;
91 	int rc = -ENOBUFS;
92 
93 	pr_debug("%s\n", __func__);
94 
95 	if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
96 		return -EINVAL;
97 
98 	name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
99 	if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
100 		return -EINVAL; /* phy name should be null-terminated */
101 
102 
103 	phy = wpan_phy_find(name);
104 	if (!phy)
105 		return -ENODEV;
106 
107 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
108 	if (!msg)
109 		goto out_dev;
110 
111 	rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
112 			0, phy);
113 	if (rc < 0)
114 		goto out_free;
115 
116 	wpan_phy_put(phy);
117 
118 	return genlmsg_reply(msg, info);
119 out_free:
120 	nlmsg_free(msg);
121 out_dev:
122 	wpan_phy_put(phy);
123 	return rc;
124 
125 }
126 
127 struct dump_phy_data {
128 	struct sk_buff *skb;
129 	struct netlink_callback *cb;
130 	int idx, s_idx;
131 };
132 
133 static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
134 {
135 	int rc;
136 	struct dump_phy_data *data = _data;
137 
138 	pr_debug("%s\n", __func__);
139 
140 	if (data->idx++ < data->s_idx)
141 		return 0;
142 
143 	rc = ieee802154_nl_fill_phy(data->skb,
144 			NETLINK_CB(data->cb->skb).portid,
145 			data->cb->nlh->nlmsg_seq,
146 			NLM_F_MULTI,
147 			phy);
148 
149 	if (rc < 0) {
150 		data->idx--;
151 		return rc;
152 	}
153 
154 	return 0;
155 }
156 
157 int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
158 {
159 	struct dump_phy_data data = {
160 		.cb = cb,
161 		.skb = skb,
162 		.s_idx = cb->args[0],
163 		.idx = 0,
164 	};
165 
166 	pr_debug("%s\n", __func__);
167 
168 	wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
169 
170 	cb->args[0] = data.idx;
171 
172 	return skb->len;
173 }
174 
175 int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
176 {
177 	struct sk_buff *msg;
178 	struct wpan_phy *phy;
179 	const char *name;
180 	const char *devname;
181 	int rc = -ENOBUFS;
182 	struct net_device *dev;
183 	int type = __IEEE802154_DEV_INVALID;
184 
185 	pr_debug("%s\n", __func__);
186 
187 	if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
188 		return -EINVAL;
189 
190 	name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
191 	if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
192 		return -EINVAL; /* phy name should be null-terminated */
193 
194 	if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
195 		devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
196 		if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
197 				!= '\0')
198 			return -EINVAL; /* phy name should be null-terminated */
199 	} else  {
200 		devname = "wpan%d";
201 	}
202 
203 	if (strlen(devname) >= IFNAMSIZ)
204 		return -ENAMETOOLONG;
205 
206 	phy = wpan_phy_find(name);
207 	if (!phy)
208 		return -ENODEV;
209 
210 	msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
211 	if (!msg)
212 		goto out_dev;
213 
214 	if (!phy->add_iface) {
215 		rc = -EINVAL;
216 		goto nla_put_failure;
217 	}
218 
219 	if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
220 	    nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
221 			IEEE802154_ADDR_LEN) {
222 		rc = -EINVAL;
223 		goto nla_put_failure;
224 	}
225 
226 	if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
227 		type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
228 		if (type >= __IEEE802154_DEV_MAX) {
229 			rc = -EINVAL;
230 			goto nla_put_failure;
231 		}
232 	}
233 
234 	dev = phy->add_iface(phy, devname, type);
235 	if (IS_ERR(dev)) {
236 		rc = PTR_ERR(dev);
237 		goto nla_put_failure;
238 	}
239 
240 	if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
241 		struct sockaddr addr;
242 
243 		addr.sa_family = ARPHRD_IEEE802154;
244 		nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
245 				IEEE802154_ADDR_LEN);
246 
247 		/*
248 		 * strangely enough, some callbacks (inetdev_event) from
249 		 * dev_set_mac_address require RTNL_LOCK
250 		 */
251 		rtnl_lock();
252 		rc = dev_set_mac_address(dev, &addr);
253 		rtnl_unlock();
254 		if (rc)
255 			goto dev_unregister;
256 	}
257 
258 	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
259 	    nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name))
260 		goto nla_put_failure;
261 	dev_put(dev);
262 
263 	wpan_phy_put(phy);
264 
265 	return ieee802154_nl_reply(msg, info);
266 
267 dev_unregister:
268 	rtnl_lock(); /* del_iface must be called with RTNL lock */
269 	phy->del_iface(phy, dev);
270 	dev_put(dev);
271 	rtnl_unlock();
272 nla_put_failure:
273 	nlmsg_free(msg);
274 out_dev:
275 	wpan_phy_put(phy);
276 	return rc;
277 }
278 
279 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
280 {
281 	struct sk_buff *msg;
282 	struct wpan_phy *phy;
283 	const char *name;
284 	int rc;
285 	struct net_device *dev;
286 
287 	pr_debug("%s\n", __func__);
288 
289 	if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
290 		return -EINVAL;
291 
292 	name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
293 	if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
294 		return -EINVAL; /* name should be null-terminated */
295 
296 	dev = dev_get_by_name(genl_info_net(info), name);
297 	if (!dev)
298 		return -ENODEV;
299 
300 	phy = ieee802154_mlme_ops(dev)->get_phy(dev);
301 	BUG_ON(!phy);
302 
303 	rc = -EINVAL;
304 	/* phy name is optional, but should be checked if it's given */
305 	if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
306 		struct wpan_phy *phy2;
307 
308 		const char *pname =
309 			nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
310 		if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
311 				!= '\0')
312 			/* name should be null-terminated */
313 			goto out_dev;
314 
315 		phy2 = wpan_phy_find(pname);
316 		if (!phy2)
317 			goto out_dev;
318 
319 		if (phy != phy2) {
320 			wpan_phy_put(phy2);
321 			goto out_dev;
322 		}
323 	}
324 
325 	rc = -ENOBUFS;
326 
327 	msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
328 	if (!msg)
329 		goto out_dev;
330 
331 	if (!phy->del_iface) {
332 		rc = -EINVAL;
333 		goto nla_put_failure;
334 	}
335 
336 	rtnl_lock();
337 	phy->del_iface(phy, dev);
338 
339 	/* We don't have device anymore */
340 	dev_put(dev);
341 	dev = NULL;
342 
343 	rtnl_unlock();
344 
345 	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
346 	    nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
347 		goto nla_put_failure;
348 	wpan_phy_put(phy);
349 
350 	return ieee802154_nl_reply(msg, info);
351 
352 nla_put_failure:
353 	nlmsg_free(msg);
354 out_dev:
355 	wpan_phy_put(phy);
356 	if (dev)
357 		dev_put(dev);
358 
359 	return rc;
360 }
361 
362 static int phy_set_txpower(struct wpan_phy *phy, struct genl_info *info)
363 {
364 	int txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
365 	int rc;
366 
367 	rc = phy->set_txpower(phy, txpower);
368 	if (rc < 0)
369 		return rc;
370 
371 	phy->transmit_power = txpower;
372 
373 	return 0;
374 }
375 
376 static int phy_set_lbt(struct wpan_phy *phy, struct genl_info *info)
377 {
378 	u8 on = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
379 	int rc;
380 
381 	rc = phy->set_lbt(phy, on);
382 	if (rc < 0)
383 		return rc;
384 
385 	phy->lbt = on;
386 
387 	return 0;
388 }
389 
390 static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info)
391 {
392 	u8 mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
393 	int rc;
394 
395 	if (mode > 3)
396 		return -EINVAL;
397 
398 	rc = phy->set_cca_mode(phy, mode);
399 	if (rc < 0)
400 		return rc;
401 
402 	phy->cca_mode = mode;
403 
404 	return 0;
405 }
406 
407 static int phy_set_cca_ed_level(struct wpan_phy *phy, struct genl_info *info)
408 {
409 	s32 level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
410 	int rc;
411 
412 	rc = phy->set_cca_ed_level(phy, level);
413 	if (rc < 0)
414 		return rc;
415 
416 	phy->cca_ed_level = level;
417 
418 	return 0;
419 }
420 
421 int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
422 {
423 	struct wpan_phy *phy;
424 	const char *name;
425 	int rc = -ENOTSUPP;
426 
427 	pr_debug("%s\n", __func__);
428 
429 	if (!info->attrs[IEEE802154_ATTR_PHY_NAME] &&
430 	    !info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
431 	    !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
432 	    !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
433 		return -EINVAL;
434 
435 	name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
436 	if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
437 		return -EINVAL; /* phy name should be null-terminated */
438 
439 	phy = wpan_phy_find(name);
440 	if (!phy)
441 		return -ENODEV;
442 
443 	if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) ||
444 	    (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
445 	    (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
446 	    (!phy->set_cca_ed_level &&
447 	     info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]))
448 		goto out;
449 
450 	mutex_lock(&phy->pib_lock);
451 
452 	if (info->attrs[IEEE802154_ATTR_TXPOWER]) {
453 		rc = phy_set_txpower(phy, info);
454 		if (rc < 0)
455 			goto error;
456 	}
457 
458 	if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) {
459 		rc = phy_set_lbt(phy, info);
460 		if (rc < 0)
461 			goto error;
462 	}
463 
464 	if (info->attrs[IEEE802154_ATTR_CCA_MODE]) {
465 		rc = phy_set_cca_mode(phy, info);
466 		if (rc < 0)
467 			goto error;
468 	}
469 
470 	if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) {
471 		rc = phy_set_cca_ed_level(phy, info);
472 		if (rc < 0)
473 			goto error;
474 	}
475 
476 	mutex_unlock(&phy->pib_lock);
477 
478 	wpan_phy_put(phy);
479 
480 	return 0;
481 
482 error:
483 	mutex_unlock(&phy->pib_lock);
484 out:
485 	wpan_phy_put(phy);
486 	return rc;
487 }
488