ieee802154: add LIST_PHY command support
[safe/jmp/linux-2.6] / 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 <net/netlink.h>
27 #include <net/genetlink.h>
28 #include <net/wpan-phy.h>
29 #include <linux/nl802154.h>
30
31 #include "ieee802154.h"
32
33 static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,
34         u32 seq, int flags, struct wpan_phy *phy)
35 {
36         void *hdr;
37         int i, pages = 0;
38         uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL);
39
40         pr_debug("%s\n", __func__);
41
42         if (!buf)
43                 goto out;
44
45         hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
46                 IEEE802154_LIST_PHY);
47         if (!hdr)
48                 goto out;
49
50         mutex_lock(&phy->pib_lock);
51         NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
52
53         NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page);
54         NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel);
55         for (i = 0; i < 32; i++) {
56                 if (phy->channels_supported[i])
57                         buf[pages++] = phy->channels_supported[i] | (i << 27);
58         }
59         if (pages)
60                 NLA_PUT(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
61                                 pages * sizeof(uint32_t), buf);
62
63         mutex_unlock(&phy->pib_lock);
64         return genlmsg_end(msg, hdr);
65
66 nla_put_failure:
67         mutex_unlock(&phy->pib_lock);
68         genlmsg_cancel(msg, hdr);
69 out:
70         kfree(buf);
71         return -EMSGSIZE;
72 }
73
74 static int ieee802154_list_phy(struct sk_buff *skb,
75         struct genl_info *info)
76 {
77         /* Request for interface name, index, type, IEEE address,
78            PAN Id, short address */
79         struct sk_buff *msg;
80         struct wpan_phy *phy;
81         const char *name;
82         int rc = -ENOBUFS;
83
84         pr_debug("%s\n", __func__);
85
86         if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
87                 return -EINVAL;
88
89         name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
90         if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
91                 return -EINVAL; /* phy name should be null-terminated */
92
93
94         phy = wpan_phy_find(name);
95         if (!phy)
96                 return -ENODEV;
97
98         msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
99         if (!msg)
100                 goto out_dev;
101
102         rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq,
103                         0, phy);
104         if (rc < 0)
105                 goto out_free;
106
107         wpan_phy_put(phy);
108
109         return genlmsg_reply(msg, info);
110 out_free:
111         nlmsg_free(msg);
112 out_dev:
113         wpan_phy_put(phy);
114         return rc;
115
116 }
117
118 struct dump_phy_data {
119         struct sk_buff *skb;
120         struct netlink_callback *cb;
121         int idx, s_idx;
122 };
123
124 static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
125 {
126         int rc;
127         struct dump_phy_data *data = _data;
128
129         pr_debug("%s\n", __func__);
130
131         if (data->idx++ < data->s_idx)
132                 return 0;
133
134         rc = ieee802154_nl_fill_phy(data->skb,
135                         NETLINK_CB(data->cb->skb).pid,
136                         data->cb->nlh->nlmsg_seq,
137                         NLM_F_MULTI,
138                         phy);
139
140         if (rc < 0) {
141                 data->idx--;
142                 return rc;
143         }
144
145         return 0;
146 }
147
148 static int ieee802154_dump_phy(struct sk_buff *skb,
149         struct netlink_callback *cb)
150 {
151         struct dump_phy_data data = {
152                 .cb = cb,
153                 .skb = skb,
154                 .s_idx = cb->args[0],
155                 .idx = 0,
156         };
157
158         pr_debug("%s\n", __func__);
159
160         wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
161
162         cb->args[0] = data.idx;
163
164         return skb->len;
165 }
166
167 static struct genl_ops ieee802154_phy_ops[] = {
168         IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
169                                                         ieee802154_dump_phy),
170 };
171
172 /*
173  * No need to unregister as family unregistration will do it.
174  */
175 int nl802154_phy_register(void)
176 {
177         int i;
178         int rc;
179
180         for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) {
181                 rc = genl_register_ops(&nl802154_family,
182                                 &ieee802154_phy_ops[i]);
183                 if (rc)
184                         return rc;
185         }
186
187         return 0;
188 }