Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/ieee802154/nl-phy.c
15111 views
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 <[email protected]>
21
* Dmitry Eremin-Solenikov <[email protected]>
22
* Maxim Osipov <[email protected]>
23
*/
24
25
#include <linux/kernel.h>
26
#include <linux/slab.h>
27
#include <net/netlink.h>
28
#include <net/genetlink.h>
29
#include <net/wpan-phy.h>
30
#include <net/af_ieee802154.h>
31
#include <net/ieee802154_netdev.h>
32
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
33
#include <linux/nl802154.h>
34
35
#include "ieee802154.h"
36
37
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,
38
u32 seq, int flags, struct wpan_phy *phy)
39
{
40
void *hdr;
41
int i, pages = 0;
42
uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL);
43
44
pr_debug("%s\n", __func__);
45
46
if (!buf)
47
return -EMSGSIZE;
48
49
hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
50
IEEE802154_LIST_PHY);
51
if (!hdr)
52
goto out;
53
54
mutex_lock(&phy->pib_lock);
55
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
56
57
NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page);
58
NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel);
59
for (i = 0; i < 32; i++) {
60
if (phy->channels_supported[i])
61
buf[pages++] = phy->channels_supported[i] | (i << 27);
62
}
63
if (pages)
64
NLA_PUT(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
65
pages * sizeof(uint32_t), buf);
66
67
mutex_unlock(&phy->pib_lock);
68
kfree(buf);
69
return genlmsg_end(msg, hdr);
70
71
nla_put_failure:
72
mutex_unlock(&phy->pib_lock);
73
genlmsg_cancel(msg, hdr);
74
out:
75
kfree(buf);
76
return -EMSGSIZE;
77
}
78
79
static int ieee802154_list_phy(struct sk_buff *skb,
80
struct genl_info *info)
81
{
82
/* Request for interface name, index, type, IEEE address,
83
PAN Id, short address */
84
struct sk_buff *msg;
85
struct wpan_phy *phy;
86
const char *name;
87
int rc = -ENOBUFS;
88
89
pr_debug("%s\n", __func__);
90
91
if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
92
return -EINVAL;
93
94
name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
95
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
96
return -EINVAL; /* phy name should be null-terminated */
97
98
99
phy = wpan_phy_find(name);
100
if (!phy)
101
return -ENODEV;
102
103
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
104
if (!msg)
105
goto out_dev;
106
107
rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq,
108
0, phy);
109
if (rc < 0)
110
goto out_free;
111
112
wpan_phy_put(phy);
113
114
return genlmsg_reply(msg, info);
115
out_free:
116
nlmsg_free(msg);
117
out_dev:
118
wpan_phy_put(phy);
119
return rc;
120
121
}
122
123
struct dump_phy_data {
124
struct sk_buff *skb;
125
struct netlink_callback *cb;
126
int idx, s_idx;
127
};
128
129
static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
130
{
131
int rc;
132
struct dump_phy_data *data = _data;
133
134
pr_debug("%s\n", __func__);
135
136
if (data->idx++ < data->s_idx)
137
return 0;
138
139
rc = ieee802154_nl_fill_phy(data->skb,
140
NETLINK_CB(data->cb->skb).pid,
141
data->cb->nlh->nlmsg_seq,
142
NLM_F_MULTI,
143
phy);
144
145
if (rc < 0) {
146
data->idx--;
147
return rc;
148
}
149
150
return 0;
151
}
152
153
static int ieee802154_dump_phy(struct sk_buff *skb,
154
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
static int ieee802154_add_iface(struct sk_buff *skb,
173
struct genl_info *info)
174
{
175
struct sk_buff *msg;
176
struct wpan_phy *phy;
177
const char *name;
178
const char *devname;
179
int rc = -ENOBUFS;
180
struct net_device *dev;
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
dev = phy->add_iface(phy, devname);
217
if (IS_ERR(dev)) {
218
rc = PTR_ERR(dev);
219
goto nla_put_failure;
220
}
221
222
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
223
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
224
225
dev_put(dev);
226
227
wpan_phy_put(phy);
228
229
return ieee802154_nl_reply(msg, info);
230
231
nla_put_failure:
232
nlmsg_free(msg);
233
out_dev:
234
wpan_phy_put(phy);
235
return rc;
236
}
237
238
static int ieee802154_del_iface(struct sk_buff *skb,
239
struct genl_info *info)
240
{
241
struct sk_buff *msg;
242
struct wpan_phy *phy;
243
const char *name;
244
int rc;
245
struct net_device *dev;
246
247
pr_debug("%s\n", __func__);
248
249
if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
250
return -EINVAL;
251
252
name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
253
if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
254
return -EINVAL; /* name should be null-terminated */
255
256
dev = dev_get_by_name(genl_info_net(info), name);
257
if (!dev)
258
return -ENODEV;
259
260
phy = ieee802154_mlme_ops(dev)->get_phy(dev);
261
BUG_ON(!phy);
262
263
rc = -EINVAL;
264
/* phy name is optional, but should be checked if it's given */
265
if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
266
struct wpan_phy *phy2;
267
268
const char *pname =
269
nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
270
if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
271
!= '\0')
272
/* name should be null-terminated */
273
goto out_dev;
274
275
phy2 = wpan_phy_find(pname);
276
if (!phy2)
277
goto out_dev;
278
279
if (phy != phy2) {
280
wpan_phy_put(phy2);
281
goto out_dev;
282
}
283
}
284
285
rc = -ENOBUFS;
286
287
msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
288
if (!msg)
289
goto out_dev;
290
291
if (!phy->del_iface) {
292
rc = -EINVAL;
293
goto nla_put_failure;
294
}
295
296
rtnl_lock();
297
phy->del_iface(phy, dev);
298
299
/* We don't have device anymore */
300
dev_put(dev);
301
dev = NULL;
302
303
rtnl_unlock();
304
305
306
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
307
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name);
308
309
wpan_phy_put(phy);
310
311
return ieee802154_nl_reply(msg, info);
312
313
nla_put_failure:
314
nlmsg_free(msg);
315
out_dev:
316
wpan_phy_put(phy);
317
if (dev)
318
dev_put(dev);
319
320
return rc;
321
}
322
323
static struct genl_ops ieee802154_phy_ops[] = {
324
IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
325
ieee802154_dump_phy),
326
IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
327
IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
328
};
329
330
/*
331
* No need to unregister as family unregistration will do it.
332
*/
333
int nl802154_phy_register(void)
334
{
335
int i;
336
int rc;
337
338
for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) {
339
rc = genl_register_ops(&nl802154_family,
340
&ieee802154_phy_ops[i]);
341
if (rc)
342
return rc;
343
}
344
345
return 0;
346
}
347
348