blob: 8ed084b8784b642ae390fd2308940f598036a136 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/******************************************************************************
29* wlan_nlink_srv.c
30*
31* This file contains the definitions specific to the wlan_nlink_srv
32*
33******************************************************************************/
34
35#include <linux/version.h>
36#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/init.h>
39#include <linux/netdevice.h>
40#include <linux/netlink.h>
41#include <linux/skbuff.h>
42#include <net/sock.h>
43#include <wlan_nlink_srv.h>
44#include <cdf_trace.h>
45
46/* Global variables */
47static DEFINE_MUTEX(nl_srv_sem);
48static struct sock *nl_srv_sock;
49static nl_srv_msg_callback nl_srv_msg_handler[NLINK_MAX_CALLBACKS];
50
51/* Forward declaration */
52static void nl_srv_rcv(struct sk_buff *sk);
53static void nl_srv_rcv_skb(struct sk_buff *skb);
54static void nl_srv_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh);
55
56/*
57 * Initialize the netlink service.
58 * Netlink service is usable after this.
59 */
60int nl_srv_init(void)
61{
62 int retcode = 0;
63#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
64 struct netlink_kernel_cfg cfg = {
65 .groups = WLAN_NLINK_MCAST_GRP_ID,
66 .input = nl_srv_rcv
67 };
68#endif
69
70#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
71 nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY,
72#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
73 THIS_MODULE,
74#endif
75 &cfg);
76#else
77 nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY,
78 WLAN_NLINK_MCAST_GRP_ID, nl_srv_rcv,
79 NULL, THIS_MODULE);
80#endif
81
82 if (nl_srv_sock != NULL) {
83 memset(nl_srv_msg_handler, 0, sizeof(nl_srv_msg_handler));
84 } else {
85 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
86 "NLINK: netlink_kernel_create failed");
87 retcode = -ECONNREFUSED;
88 }
89 return retcode;
90}
91
92/*
93 * Deinit the netlink service.
94 * Netlink service is unusable after this.
95 */
96void nl_srv_exit(void)
97{
98 if (nl_srv_is_initialized() == 0)
99 netlink_kernel_release(nl_srv_sock);
100
101 nl_srv_sock = NULL;
102}
103
104/*
105 * Register a message handler for a specified module.
106 * Each module (e.g. WLAN_NL_MSG_BTC )will register a
107 * handler to handle messages addressed to it.
108 */
109int nl_srv_register(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler)
110{
111 int retcode = 0;
112
113 if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) &&
114 msg_handler != NULL) {
115 nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = msg_handler;
116 } else {
117 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
118 "NLINK: nl_srv_register failed for msg_type %d",
119 msg_type);
120 retcode = -EINVAL;
121 }
122
123 return retcode;
124}
125
126/*
127 * Unregister the message handler for a specified module.
128 */
129int nl_srv_unregister(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler)
130{
131 int retcode = 0;
132
133 if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) &&
134 (nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] == msg_handler)) {
135 nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = NULL;
136 } else {
137 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
138 "NLINK: nl_srv_unregister failed for msg_type %d",
139 msg_type);
140 retcode = -EINVAL;
141 }
142
143 return retcode;
144}
145
146/*
147 * Unicast the message to the process in user space identfied
148 * by the dst-pid
149 */
150int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag)
151{
152 int err;
153
154#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
155 NETLINK_CB(skb).pid = 0; /* sender's pid */
156#else
157 NETLINK_CB(skb).portid = 0; /* sender's pid */
158#endif
159 NETLINK_CB(skb).dst_group = 0; /* not multicast */
160
161 err = netlink_unicast(nl_srv_sock, skb, dst_pid, flag);
162
163 if (err < 0)
164 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
165 "NLINK: netlink_unicast to pid[%d] failed, ret[0x%X]",
166 dst_pid, err);
167
168 return err;
169}
170
171/*
172 * Broadcast the message. Broadcast will return an error if
173 * there are no listeners
174 */
175int nl_srv_bcast(struct sk_buff *skb)
176{
177 int err;
178 int flags = GFP_KERNEL;
179
180 if (in_interrupt() || irqs_disabled() || in_atomic())
181 flags = GFP_ATOMIC;
182
183#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
184 NETLINK_CB(skb).pid = 0; /* sender's pid */
185#else
186 NETLINK_CB(skb).portid = 0; /* sender's pid */
187#endif
188 NETLINK_CB(skb).dst_group = WLAN_NLINK_MCAST_GRP_ID; /* destination group */
189
190 err =
191 netlink_broadcast(nl_srv_sock, skb, 0, WLAN_NLINK_MCAST_GRP_ID,
192 flags);
193
194 if (err < 0) {
195 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
196 "NLINK: netlink_broadcast failed err = %d", err);
197 }
198 return err;
199}
200
201/*
202 * Processes the Netlink socket input queue.
203 * Dequeue skb's from the socket input queue and process
204 * all the netlink messages in that skb, before moving
205 * to the next skb.
206 */
207static void nl_srv_rcv(struct sk_buff *sk)
208{
209 mutex_lock(&nl_srv_sem);
210 nl_srv_rcv_skb(sk);
211 mutex_unlock(&nl_srv_sem);
212}
213
214/*
215 * Each skb could contain multiple Netlink messages. Process all the
216 * messages in one skb and discard malformed skb's silently.
217 */
218static void nl_srv_rcv_skb(struct sk_buff *skb)
219{
220 struct nlmsghdr *nlh;
221
222 while (skb->len >= NLMSG_SPACE(0)) {
223 u32 rlen;
224
225 nlh = (struct nlmsghdr *)skb->data;
226
227 if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) {
228 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
229 "NLINK: Invalid "
230 "Netlink message: skb[%p], len[%d], nlhdr[%p], nlmsg_len[%d]",
231 skb, skb->len, nlh, nlh->nlmsg_len);
232 return;
233 }
234
235 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
236 if (rlen > skb->len)
237 rlen = skb->len;
238 nl_srv_rcv_msg(skb, nlh);
239 skb_pull(skb, rlen);
240 }
241}
242
243/*
244 * Process a netlink message.
245 * Each netlink message will have a message of type tAniMsgHdr inside.
246 */
247static void nl_srv_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
248{
249 int type;
250
251 /* Only requests are handled by kernel now */
252 if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
253 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
254 "NLINK: Received Invalid NL Req type [%x]",
255 nlh->nlmsg_flags);
256 return;
257 }
258
259 type = nlh->nlmsg_type;
260
261 /* Unknown message */
262 if (type < WLAN_NL_MSG_BASE || type >= WLAN_NL_MSG_MAX) {
263 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
264 "NLINK: Received Invalid NL Msg type [%x]", type);
265 return;
266 }
267
268 /*
269 * All the messages must at least carry the tAniMsgHdr
270 * Drop any message with invalid length
271 */
272 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(tAniMsgHdr))) {
273 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
274 "NLINK: Received NL Msg with invalid len[%x]",
275 nlh->nlmsg_len);
276 return;
277 }
278
279 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
280 "NLINK: Received NL msg type [%d]", type);
281
282 /* turn type into dispatch table offset */
283 type -= WLAN_NL_MSG_BASE;
284
285 /* dispatch to handler */
286 if (nl_srv_msg_handler[type] != NULL) {
287 (nl_srv_msg_handler[type])(skb);
288 } else {
289 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
290 "NLINK: No handler for Netlink Msg [0x%X]", type);
291 }
292}
293
294/**
295 * nl_srv_is_initialized() - This function is used check if the netlink
296 * service is initialized
297 *
298 * This function is used check if the netlink service is initialized
299 *
300 * Return: Return -EPERM if the service is not initialized
301 *
302 */
303int nl_srv_is_initialized()
304{
305 if (nl_srv_sock)
306 return 0;
307
308 return -EPERM;
309}