Root/
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 <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 | goto out; |
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 | return genlmsg_end(msg, hdr); |
69 | |
70 | nla_put_failure: |
71 | mutex_unlock(&phy->pib_lock); |
72 | genlmsg_cancel(msg, hdr); |
73 | out: |
74 | kfree(buf); |
75 | return -EMSGSIZE; |
76 | } |
77 | |
78 | static int ieee802154_list_phy(struct sk_buff *skb, |
79 | struct genl_info *info) |
80 | { |
81 | /* Request for interface name, index, type, IEEE address, |
82 | PAN Id, short address */ |
83 | struct sk_buff *msg; |
84 | struct wpan_phy *phy; |
85 | const char *name; |
86 | int rc = -ENOBUFS; |
87 | |
88 | pr_debug("%s\n", __func__); |
89 | |
90 | if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) |
91 | return -EINVAL; |
92 | |
93 | name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); |
94 | if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') |
95 | return -EINVAL; /* phy name should be null-terminated */ |
96 | |
97 | |
98 | phy = wpan_phy_find(name); |
99 | if (!phy) |
100 | return -ENODEV; |
101 | |
102 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); |
103 | if (!msg) |
104 | goto out_dev; |
105 | |
106 | rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq, |
107 | 0, phy); |
108 | if (rc < 0) |
109 | goto out_free; |
110 | |
111 | wpan_phy_put(phy); |
112 | |
113 | return genlmsg_reply(msg, info); |
114 | out_free: |
115 | nlmsg_free(msg); |
116 | out_dev: |
117 | wpan_phy_put(phy); |
118 | return rc; |
119 | |
120 | } |
121 | |
122 | struct dump_phy_data { |
123 | struct sk_buff *skb; |
124 | struct netlink_callback *cb; |
125 | int idx, s_idx; |
126 | }; |
127 | |
128 | static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data) |
129 | { |
130 | int rc; |
131 | struct dump_phy_data *data = _data; |
132 | |
133 | pr_debug("%s\n", __func__); |
134 | |
135 | if (data->idx++ < data->s_idx) |
136 | return 0; |
137 | |
138 | rc = ieee802154_nl_fill_phy(data->skb, |
139 | NETLINK_CB(data->cb->skb).pid, |
140 | data->cb->nlh->nlmsg_seq, |
141 | NLM_F_MULTI, |
142 | phy); |
143 | |
144 | if (rc < 0) { |
145 | data->idx--; |
146 | return rc; |
147 | } |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static int ieee802154_dump_phy(struct sk_buff *skb, |
153 | struct netlink_callback *cb) |
154 | { |
155 | struct dump_phy_data data = { |
156 | .cb = cb, |
157 | .skb = skb, |
158 | .s_idx = cb->args[0], |
159 | .idx = 0, |
160 | }; |
161 | |
162 | pr_debug("%s\n", __func__); |
163 | |
164 | wpan_phy_for_each(ieee802154_dump_phy_iter, &data); |
165 | |
166 | cb->args[0] = data.idx; |
167 | |
168 | return skb->len; |
169 | } |
170 | |
171 | static int ieee802154_add_iface(struct sk_buff *skb, |
172 | 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 | |
181 | pr_debug("%s\n", __func__); |
182 | |
183 | if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) |
184 | return -EINVAL; |
185 | |
186 | name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); |
187 | if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') |
188 | return -EINVAL; /* phy name should be null-terminated */ |
189 | |
190 | if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { |
191 | devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]); |
192 | if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] |
193 | != '\0') |
194 | return -EINVAL; /* phy name should be null-terminated */ |
195 | } else { |
196 | devname = "wpan%d"; |
197 | } |
198 | |
199 | if (strlen(devname) >= IFNAMSIZ) |
200 | return -ENAMETOOLONG; |
201 | |
202 | phy = wpan_phy_find(name); |
203 | if (!phy) |
204 | return -ENODEV; |
205 | |
206 | msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE); |
207 | if (!msg) |
208 | goto out_dev; |
209 | |
210 | if (!phy->add_iface) { |
211 | rc = -EINVAL; |
212 | goto nla_put_failure; |
213 | } |
214 | |
215 | dev = phy->add_iface(phy, devname); |
216 | if (IS_ERR(dev)) { |
217 | rc = PTR_ERR(dev); |
218 | goto nla_put_failure; |
219 | } |
220 | |
221 | NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); |
222 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); |
223 | |
224 | dev_put(dev); |
225 | |
226 | wpan_phy_put(phy); |
227 | |
228 | return ieee802154_nl_reply(msg, info); |
229 | |
230 | nla_put_failure: |
231 | nlmsg_free(msg); |
232 | out_dev: |
233 | wpan_phy_put(phy); |
234 | return rc; |
235 | } |
236 | |
237 | static int ieee802154_del_iface(struct sk_buff *skb, |
238 | struct genl_info *info) |
239 | { |
240 | struct sk_buff *msg; |
241 | struct wpan_phy *phy; |
242 | const char *name; |
243 | int rc; |
244 | struct net_device *dev; |
245 | |
246 | pr_debug("%s\n", __func__); |
247 | |
248 | if (!info->attrs[IEEE802154_ATTR_DEV_NAME]) |
249 | return -EINVAL; |
250 | |
251 | name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]); |
252 | if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0') |
253 | return -EINVAL; /* name should be null-terminated */ |
254 | |
255 | dev = dev_get_by_name(genl_info_net(info), name); |
256 | if (!dev) |
257 | return -ENODEV; |
258 | |
259 | phy = ieee802154_mlme_ops(dev)->get_phy(dev); |
260 | BUG_ON(!phy); |
261 | |
262 | rc = -EINVAL; |
263 | /* phy name is optional, but should be checked if it's given */ |
264 | if (info->attrs[IEEE802154_ATTR_PHY_NAME]) { |
265 | struct wpan_phy *phy2; |
266 | |
267 | const char *pname = |
268 | nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); |
269 | if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] |
270 | != '\0') |
271 | /* name should be null-terminated */ |
272 | goto out_dev; |
273 | |
274 | phy2 = wpan_phy_find(pname); |
275 | if (!phy2) |
276 | goto out_dev; |
277 | |
278 | if (phy != phy2) { |
279 | wpan_phy_put(phy2); |
280 | goto out_dev; |
281 | } |
282 | } |
283 | |
284 | rc = -ENOBUFS; |
285 | |
286 | msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE); |
287 | if (!msg) |
288 | goto out_dev; |
289 | |
290 | if (!phy->del_iface) { |
291 | rc = -EINVAL; |
292 | goto nla_put_failure; |
293 | } |
294 | |
295 | rtnl_lock(); |
296 | phy->del_iface(phy, dev); |
297 | |
298 | /* We don't have device anymore */ |
299 | dev_put(dev); |
300 | dev = NULL; |
301 | |
302 | rtnl_unlock(); |
303 | |
304 | |
305 | NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); |
306 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name); |
307 | |
308 | wpan_phy_put(phy); |
309 | |
310 | return ieee802154_nl_reply(msg, info); |
311 | |
312 | nla_put_failure: |
313 | nlmsg_free(msg); |
314 | out_dev: |
315 | wpan_phy_put(phy); |
316 | if (dev) |
317 | dev_put(dev); |
318 | |
319 | return rc; |
320 | } |
321 | |
322 | static struct genl_ops ieee802154_phy_ops[] = { |
323 | IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy, |
324 | ieee802154_dump_phy), |
325 | IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface), |
326 | IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface), |
327 | }; |
328 | |
329 | /* |
330 | * No need to unregister as family unregistration will do it. |
331 | */ |
332 | int nl802154_phy_register(void) |
333 | { |
334 | int i; |
335 | int rc; |
336 | |
337 | for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) { |
338 | rc = genl_register_ops(&nl802154_family, |
339 | &ieee802154_phy_ops[i]); |
340 | if (rc) |
341 | return rc; |
342 | } |
343 | |
344 | return 0; |
345 | } |
346 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9