ieee802154: add TX power control to wpan_phy
[deliverable/linux.git] / net / ieee802154 / nl-phy.c
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 goto nla_put_failure;
61 for (i = 0; i < 32; i++) {
62 if (phy->channels_supported[i])
63 buf[pages++] = phy->channels_supported[i] | (i << 27);
64 }
65 if (pages &&
66 nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
67 pages * sizeof(uint32_t), buf))
68 goto nla_put_failure;
69 mutex_unlock(&phy->pib_lock);
70 kfree(buf);
71 return genlmsg_end(msg, hdr);
72
73 nla_put_failure:
74 mutex_unlock(&phy->pib_lock);
75 genlmsg_cancel(msg, hdr);
76 out:
77 kfree(buf);
78 return -EMSGSIZE;
79 }
80
81 int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
82 {
83 /* Request for interface name, index, type, IEEE address,
84 PAN Id, short address */
85 struct sk_buff *msg;
86 struct wpan_phy *phy;
87 const char *name;
88 int rc = -ENOBUFS;
89
90 pr_debug("%s\n", __func__);
91
92 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
93 return -EINVAL;
94
95 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
96 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
97 return -EINVAL; /* phy name should be null-terminated */
98
99
100 phy = wpan_phy_find(name);
101 if (!phy)
102 return -ENODEV;
103
104 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
105 if (!msg)
106 goto out_dev;
107
108 rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
109 0, phy);
110 if (rc < 0)
111 goto out_free;
112
113 wpan_phy_put(phy);
114
115 return genlmsg_reply(msg, info);
116 out_free:
117 nlmsg_free(msg);
118 out_dev:
119 wpan_phy_put(phy);
120 return rc;
121
122 }
123
124 struct dump_phy_data {
125 struct sk_buff *skb;
126 struct netlink_callback *cb;
127 int idx, s_idx;
128 };
129
130 static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
131 {
132 int rc;
133 struct dump_phy_data *data = _data;
134
135 pr_debug("%s\n", __func__);
136
137 if (data->idx++ < data->s_idx)
138 return 0;
139
140 rc = ieee802154_nl_fill_phy(data->skb,
141 NETLINK_CB(data->cb->skb).portid,
142 data->cb->nlh->nlmsg_seq,
143 NLM_F_MULTI,
144 phy);
145
146 if (rc < 0) {
147 data->idx--;
148 return rc;
149 }
150
151 return 0;
152 }
153
154 int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
155 {
156 struct dump_phy_data data = {
157 .cb = cb,
158 .skb = skb,
159 .s_idx = cb->args[0],
160 .idx = 0,
161 };
162
163 pr_debug("%s\n", __func__);
164
165 wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
166
167 cb->args[0] = data.idx;
168
169 return skb->len;
170 }
171
172 int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
173 {
174 struct sk_buff *msg;
175 struct wpan_phy *phy;
176 const char *name;
177 const char *devname;
178 int rc = -ENOBUFS;
179 struct net_device *dev;
180 int type = __IEEE802154_DEV_INVALID;
181
182 pr_debug("%s\n", __func__);
183
184 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
185 return -EINVAL;
186
187 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
188 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
189 return -EINVAL; /* phy name should be null-terminated */
190
191 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
192 devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
193 if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
194 != '\0')
195 return -EINVAL; /* phy name should be null-terminated */
196 } else {
197 devname = "wpan%d";
198 }
199
200 if (strlen(devname) >= IFNAMSIZ)
201 return -ENAMETOOLONG;
202
203 phy = wpan_phy_find(name);
204 if (!phy)
205 return -ENODEV;
206
207 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
208 if (!msg)
209 goto out_dev;
210
211 if (!phy->add_iface) {
212 rc = -EINVAL;
213 goto nla_put_failure;
214 }
215
216 if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
217 nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
218 IEEE802154_ADDR_LEN) {
219 rc = -EINVAL;
220 goto nla_put_failure;
221 }
222
223 if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
224 type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
225 if (type >= __IEEE802154_DEV_MAX) {
226 rc = -EINVAL;
227 goto nla_put_failure;
228 }
229 }
230
231 dev = phy->add_iface(phy, devname, type);
232 if (IS_ERR(dev)) {
233 rc = PTR_ERR(dev);
234 goto nla_put_failure;
235 }
236
237 if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
238 struct sockaddr addr;
239
240 addr.sa_family = ARPHRD_IEEE802154;
241 nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
242 IEEE802154_ADDR_LEN);
243
244 /*
245 * strangely enough, some callbacks (inetdev_event) from
246 * dev_set_mac_address require RTNL_LOCK
247 */
248 rtnl_lock();
249 rc = dev_set_mac_address(dev, &addr);
250 rtnl_unlock();
251 if (rc)
252 goto dev_unregister;
253 }
254
255 if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
256 nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name))
257 goto nla_put_failure;
258 dev_put(dev);
259
260 wpan_phy_put(phy);
261
262 return ieee802154_nl_reply(msg, info);
263
264 dev_unregister:
265 rtnl_lock(); /* del_iface must be called with RTNL lock */
266 phy->del_iface(phy, dev);
267 dev_put(dev);
268 rtnl_unlock();
269 nla_put_failure:
270 nlmsg_free(msg);
271 out_dev:
272 wpan_phy_put(phy);
273 return rc;
274 }
275
276 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
277 {
278 struct sk_buff *msg;
279 struct wpan_phy *phy;
280 const char *name;
281 int rc;
282 struct net_device *dev;
283
284 pr_debug("%s\n", __func__);
285
286 if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
287 return -EINVAL;
288
289 name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
290 if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
291 return -EINVAL; /* name should be null-terminated */
292
293 dev = dev_get_by_name(genl_info_net(info), name);
294 if (!dev)
295 return -ENODEV;
296
297 phy = ieee802154_mlme_ops(dev)->get_phy(dev);
298 BUG_ON(!phy);
299
300 rc = -EINVAL;
301 /* phy name is optional, but should be checked if it's given */
302 if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
303 struct wpan_phy *phy2;
304
305 const char *pname =
306 nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
307 if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
308 != '\0')
309 /* name should be null-terminated */
310 goto out_dev;
311
312 phy2 = wpan_phy_find(pname);
313 if (!phy2)
314 goto out_dev;
315
316 if (phy != phy2) {
317 wpan_phy_put(phy2);
318 goto out_dev;
319 }
320 }
321
322 rc = -ENOBUFS;
323
324 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
325 if (!msg)
326 goto out_dev;
327
328 if (!phy->del_iface) {
329 rc = -EINVAL;
330 goto nla_put_failure;
331 }
332
333 rtnl_lock();
334 phy->del_iface(phy, dev);
335
336 /* We don't have device anymore */
337 dev_put(dev);
338 dev = NULL;
339
340 rtnl_unlock();
341
342 if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
343 nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
344 goto nla_put_failure;
345 wpan_phy_put(phy);
346
347 return ieee802154_nl_reply(msg, info);
348
349 nla_put_failure:
350 nlmsg_free(msg);
351 out_dev:
352 wpan_phy_put(phy);
353 if (dev)
354 dev_put(dev);
355
356 return rc;
357 }
358
359 int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
360 {
361 struct wpan_phy *phy;
362 const char *name;
363 int txpower;
364 int rc = -EINVAL;
365
366 pr_debug("%s\n", __func__);
367
368 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
369 return -EINVAL;
370
371 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
372 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
373 return -EINVAL; /* phy name should be null-terminated */
374
375 txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
376
377 phy = wpan_phy_find(name);
378 if (!phy)
379 return -ENODEV;
380
381 if (!phy->set_txpower)
382 goto out;
383
384 mutex_lock(&phy->pib_lock);
385
386 rc = phy->set_txpower(phy, txpower);
387 if (rc < 0) {
388 mutex_unlock(&phy->pib_lock);
389 goto out;
390 }
391
392 phy->transmit_power = txpower;
393
394 mutex_unlock(&phy->pib_lock);
395
396 wpan_phy_put(phy);
397
398 return 0;
399
400 out:
401 wpan_phy_put(phy);
402 return rc;
403 }
This page took 0.039862 seconds and 6 git commands to generate.