aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/nl802154.h2
-rw-r--r--include/net/wpan-phy.h4
-rw-r--r--net/ieee802154/nl-phy.c156
3 files changed, 162 insertions, 0 deletions
diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h
index 275fd94f4727..33d9f5175109 100644
--- a/include/linux/nl802154.h
+++ b/include/linux/nl802154.h
@@ -118,6 +118,8 @@ enum {
118 118
119 IEEE802154_LIST_IFACE, 119 IEEE802154_LIST_IFACE,
120 IEEE802154_LIST_PHY, 120 IEEE802154_LIST_PHY,
121 IEEE802154_ADD_IFACE,
122 IEEE802154_DEL_IFACE,
121 123
122 __IEEE802154_CMD_MAX, 124 __IEEE802154_CMD_MAX,
123}; 125};
diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h
index a65e98509fbc..85926231c07a 100644
--- a/include/net/wpan-phy.h
+++ b/include/net/wpan-phy.h
@@ -41,6 +41,10 @@ struct wpan_phy {
41 struct device dev; 41 struct device dev;
42 int idx; 42 int idx;
43 43
44 struct net_device *(*add_iface)(struct wpan_phy *phy,
45 const char *name);
46 void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
47
44 char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); 48 char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
45}; 49};
46 50
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index b7af722eb784..199a2d9d12f9 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -26,6 +26,9 @@
26#include <net/netlink.h> 26#include <net/netlink.h>
27#include <net/genetlink.h> 27#include <net/genetlink.h>
28#include <net/wpan-phy.h> 28#include <net/wpan-phy.h>
29#include <net/af_ieee802154.h>
30#include <net/ieee802154_netdev.h>
31#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
29#include <linux/nl802154.h> 32#include <linux/nl802154.h>
30 33
31#include "ieee802154.h" 34#include "ieee802154.h"
@@ -164,9 +167,162 @@ static int ieee802154_dump_phy(struct sk_buff *skb,
164 return skb->len; 167 return skb->len;
165} 168}
166 169
170static int ieee802154_add_iface(struct sk_buff *skb,
171 struct genl_info *info)
172{
173 struct sk_buff *msg;
174 struct wpan_phy *phy;
175 const char *name;
176 const char *devname;
177 int rc = -ENOBUFS;
178 struct net_device *dev;
179
180 pr_debug("%s\n", __func__);
181
182 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
183 return -EINVAL;
184
185 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
186 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
187 return -EINVAL; /* phy name should be null-terminated */
188
189 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
190 devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
191 if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
192 != '\0')
193 return -EINVAL; /* phy name should be null-terminated */
194 } else {
195 devname = "wpan%d";
196 }
197
198 if (strlen(devname) >= IFNAMSIZ)
199 return -ENAMETOOLONG;
200
201 phy = wpan_phy_find(name);
202 if (!phy)
203 return -ENODEV;
204
205 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
206 if (!msg)
207 goto out_dev;
208
209 if (!phy->add_iface) {
210 rc = -EINVAL;
211 goto nla_put_failure;
212 }
213
214 dev = phy->add_iface(phy, devname);
215 if (IS_ERR(dev)) {
216 rc = PTR_ERR(dev);
217 goto nla_put_failure;
218 }
219
220 NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
221 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
222
223 dev_put(dev);
224
225 wpan_phy_put(phy);
226
227 return ieee802154_nl_reply(msg, info);
228
229nla_put_failure:
230 nlmsg_free(msg);
231out_dev:
232 wpan_phy_put(phy);
233 return rc;
234}
235
236static int ieee802154_del_iface(struct sk_buff *skb,
237 struct genl_info *info)
238{
239 struct sk_buff *msg;
240 struct wpan_phy *phy;
241 const char *name;
242 int rc;
243 struct net_device *dev;
244
245 pr_debug("%s\n", __func__);
246
247 if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
248 return -EINVAL;
249
250 name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
251 if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
252 return -EINVAL; /* name should be null-terminated */
253
254 dev = dev_get_by_name(genl_info_net(info), name);
255 if (!dev)
256 return -ENODEV;
257
258 phy = ieee802154_mlme_ops(dev)->get_phy(dev);
259 BUG_ON(!phy);
260
261 rc = -EINVAL;
262 /* phy name is optional, but should be checked if it's given */
263 if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
264 struct wpan_phy *phy2;
265
266 const char *pname =
267 nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
268 if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
269 != '\0')
270 /* name should be null-terminated */
271 goto out_dev;
272
273 phy2 = wpan_phy_find(pname);
274 if (!phy2)
275 goto out_dev;
276
277 if (phy != phy2) {
278 wpan_phy_put(phy2);
279 goto out_dev;
280 }
281 }
282
283 rc = -ENOBUFS;
284
285 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
286 if (!msg)
287 goto out_dev;
288
289 if (!phy->del_iface) {
290 rc = -EINVAL;
291 goto nla_put_failure;
292 }
293
294 rtnl_lock();
295 phy->del_iface(phy, dev);
296
297 /* We don't have device anymore */
298 dev_put(dev);
299 dev = NULL;
300
301 rtnl_unlock();
302
303
304 NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
305 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name);
306
307 wpan_phy_put(phy);
308
309 return ieee802154_nl_reply(msg, info);
310
311nla_put_failure:
312 nlmsg_free(msg);
313out_dev:
314 wpan_phy_put(phy);
315 if (dev)
316 dev_put(dev);
317
318 return rc;
319}
320
167static struct genl_ops ieee802154_phy_ops[] = { 321static struct genl_ops ieee802154_phy_ops[] = {
168 IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy, 322 IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
169 ieee802154_dump_phy), 323 ieee802154_dump_phy),
324 IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
325 IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
170}; 326};
171 327
172/* 328/*