blob: b787bb51d0cada84763ff7ae537178d19d5f0015 [file] [log] [blame]
Jeff Johnson295189b2012-06-20 16:38:30 -07001/*
Kiet Lamaa8e15a2014-02-11 23:30:06 -08002 * Copyright (c) 2012-2013 Qualcomm Atheros, Inc.
3 * All Rights Reserved.
4 * Qualcomm Atheros Confidential and Proprietary.
Gopichand Nakkala92f07d82013-01-08 21:16:34 -08005 */
Jeff Johnson295189b2012-06-20 16:38:30 -07006/******************************************************************************
7* wlan_nlink_srv.c
8*
9* This file contains the definitions specific to the wlan_nlink_srv
10*
11******************************************************************************/
12
13#include <linux/version.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/netdevice.h>
18#include <linux/netlink.h>
19#include <linux/skbuff.h>
20#include <net/sock.h>
21#include <wlan_nlink_srv.h>
22#include <vos_trace.h>
23
24//Global variables
25static DEFINE_MUTEX(nl_srv_sem);
Madan Mohan Koyyalamudidfd6aa82012-10-18 20:18:43 -070026static struct sock *nl_srv_sock;
Jeff Johnson295189b2012-06-20 16:38:30 -070027static nl_srv_msg_callback nl_srv_msg_handler[NLINK_MAX_CALLBACKS];
28
Leo Chang59cdc7e2013-07-10 10:08:21 -070029#ifdef WLAN_KD_READY_NOTIFIER
30const char driverLoaded[] = "KNLREADY";
31const char driverUnLoaded[] = "KNLCLOSE";
32#endif /* WLAN_KD_READY_NOTIFIER */
33
Jeff Johnson295189b2012-06-20 16:38:30 -070034//Forward declaration
35static void nl_srv_rcv (struct sk_buff *sk);
36static void nl_srv_rcv_skb (struct sk_buff *skb);
37static void nl_srv_rcv_msg (struct sk_buff *skb, struct nlmsghdr *nlh);
38
39/*
40 * Initialize the netlink service.
41 * Netlink service is usable after this.
42 */
43int nl_srv_init(void)
44{
45 int retcode = 0;
Jeff Johnsonf6624a42013-01-17 18:25:55 -080046#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
47 struct netlink_kernel_cfg cfg = {
48 .groups = WLAN_NLINK_MCAST_GRP_ID,
49 .input = nl_srv_rcv
50 };
51#endif
52
53#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
54 nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY,
55#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
56 THIS_MODULE,
57#endif
58 &cfg);
59#else
Jeff Johnson295189b2012-06-20 16:38:30 -070060 nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY,
61 WLAN_NLINK_MCAST_GRP_ID, nl_srv_rcv, NULL, THIS_MODULE);
Jeff Johnsonf6624a42013-01-17 18:25:55 -080062#endif
Jeff Johnson295189b2012-06-20 16:38:30 -070063
64 if (nl_srv_sock != NULL) {
65 memset(nl_srv_msg_handler, 0, sizeof(nl_srv_msg_handler));
66 } else {
67 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
68 "NLINK: netlink_kernel_create failed");
69 retcode = -ECONNREFUSED;
70 }
71 return retcode;
72}
73
74/*
75 * Deinit the netlink service.
76 * Netlink service is unusable after this.
77 */
Leo Chang59cdc7e2013-07-10 10:08:21 -070078#ifdef WLAN_KD_READY_NOTIFIER
79void nl_srv_exit(int dst_pid)
80#else
Jeff Johnson295189b2012-06-20 16:38:30 -070081void nl_srv_exit(void)
Leo Chang59cdc7e2013-07-10 10:08:21 -070082#endif /* WLAN_KD_READY_NOTIFIER */
Jeff Johnson295189b2012-06-20 16:38:30 -070083{
Leo Chang59cdc7e2013-07-10 10:08:21 -070084#ifdef WLAN_KD_READY_NOTIFIER
85 if (0 != dst_pid)
86 {
87 nl_srv_nl_close_indication(dst_pid);
88 }
89#endif /* WLAN_KD_READY_NOTIFIER */
Jeff Johnson295189b2012-06-20 16:38:30 -070090 netlink_kernel_release(nl_srv_sock);
91}
92
93/*
94 * Register a message handler for a specified module.
95 * Each module (e.g. WLAN_NL_MSG_BTC )will register a
96 * handler to handle messages addressed to it.
97 */
98int nl_srv_register(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler)
99{
100 int retcode = 0;
101
102 if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) &&
103 msg_handler != NULL)
104 {
105 nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = msg_handler;
106 }
107 else {
108 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
109 "NLINK: nl_srv_register failed for msg_type %d", msg_type);
110 retcode = -EINVAL;
111 }
112
113 return retcode;
114}
115/*
116 * Unregister the message handler for a specified module.
117 */
118int nl_srv_unregister(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler)
119{
120 int retcode = 0;
121
122 if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) &&
123 (nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] == msg_handler))
124 {
125 nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = NULL;
126 }
127 else
128 {
129 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
130 "NLINK: nl_srv_unregister failed for msg_type %d", msg_type);
131 retcode = -EINVAL;
132 }
133
134 return retcode;
135}
136
137/*
138 * Unicast the message to the process in user space identfied
139 * by the dst-pid
140 */
141int nl_srv_ucast(struct sk_buff *skb, int dst_pid)
142{
143 int err;
144
Jeff Johnsonf6624a42013-01-17 18:25:55 -0800145#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
Jeff Johnson295189b2012-06-20 16:38:30 -0700146 NETLINK_CB(skb).pid = 0; //sender's pid
Jeff Johnsonf6624a42013-01-17 18:25:55 -0800147#else
148 NETLINK_CB(skb).portid = 0; //sender's pid
149#endif
Jeff Johnson295189b2012-06-20 16:38:30 -0700150 NETLINK_CB(skb).dst_group = 0; //not multicast
151
152 err = netlink_unicast(nl_srv_sock, skb, dst_pid, MSG_DONTWAIT);
153
154 if (err < 0)
155 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
156 "NLINK: netlink_unicast to pid[%d] failed, ret[0x%X]", dst_pid, err);
157
158 return err;
159}
160
161/*
162 * Broadcast the message. Broadcast will return an error if
163 * there are no listeners
164 */
165int nl_srv_bcast(struct sk_buff *skb)
166{
167 int err;
168
Jeff Johnsonf6624a42013-01-17 18:25:55 -0800169#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
Jeff Johnson295189b2012-06-20 16:38:30 -0700170 NETLINK_CB(skb).pid = 0; //sender's pid
Jeff Johnsonf6624a42013-01-17 18:25:55 -0800171#else
172 NETLINK_CB(skb).portid = 0; //sender's pid
173#endif
Jeff Johnson295189b2012-06-20 16:38:30 -0700174 NETLINK_CB(skb).dst_group = WLAN_NLINK_MCAST_GRP_ID; //destination group
175
176 err = netlink_broadcast(nl_srv_sock, skb, 0, WLAN_NLINK_MCAST_GRP_ID, GFP_KERNEL);
177
178 if (err < 0)
179 {
180 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
181 "NLINK: netlink_broadcast failed err = %d", err);
182 }
183 return err;
184}
185
186/*
187 * Processes the Netlink socket input queue.
188 * Dequeue skb's from the socket input queue and process
189 * all the netlink messages in that skb, before moving
190 * to the next skb.
191 */
192static void nl_srv_rcv (struct sk_buff *sk)
193{
194 mutex_lock(&nl_srv_sem);
195 nl_srv_rcv_skb(sk);
196 mutex_unlock(&nl_srv_sem);
197}
198
199/*
200 * Each skb could contain multiple Netlink messages. Process all the
201 * messages in one skb and discard malformed skb's silently.
202 */
203static void nl_srv_rcv_skb (struct sk_buff *skb)
204{
205 struct nlmsghdr * nlh;
206
207 while (skb->len >= NLMSG_SPACE(0)) {
208 u32 rlen;
209
210 nlh = (struct nlmsghdr *)skb->data;
211
212 if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) {
213 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, "NLINK: Invalid "
Jeff Johnsonc7c54b12013-11-17 11:49:03 -0800214 "Netlink message: skb[%p], len[%d], nlhdr[%p], nlmsg_len[%d]",
215 skb, skb->len, nlh, nlh->nlmsg_len);
Jeff Johnson295189b2012-06-20 16:38:30 -0700216 return;
217 }
218
219 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
220 if (rlen > skb->len)
221 rlen = skb->len;
222 nl_srv_rcv_msg(skb, nlh);
223 skb_pull(skb, rlen);
224 }
225}
226
227/*
228 * Process a netlink message.
229 * Each netlink message will have a message of type tAniMsgHdr inside.
230 */
231static void nl_srv_rcv_msg (struct sk_buff *skb, struct nlmsghdr *nlh)
232{
233 int type;
234
235 /* Only requests are handled by kernel now */
236 if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
237 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
238 "NLINK: Received Invalid NL Req type [%x]", nlh->nlmsg_flags);
239 return;
240 }
241
242 type = nlh->nlmsg_type;
243
244 /* Unknown message */
245 if (type < WLAN_NL_MSG_BASE || type >= WLAN_NL_MSG_MAX) {
246 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
247 "NLINK: Received Invalid NL Msg type [%x]", type);
248 return;
249 }
250
251 /*
252 * All the messages must at least carry the tAniMsgHdr
253 * Drop any message with invalid length
254 */
255 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(tAniMsgHdr))) {
256 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
257 "NLINK: Received NL Msg with invalid len[%x]", nlh->nlmsg_len);
258 return;
259 }
260
261 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
262 "NLINK: Received NL msg type [%d]", type);
263
264 // turn type into dispatch table offset
265 type -= WLAN_NL_MSG_BASE;
266
267 // dispatch to handler
268 if (nl_srv_msg_handler[type] != NULL) {
269 (nl_srv_msg_handler[type])(skb);
270 } else {
271 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
272 "NLINK: No handler for Netlink Msg [0x%X]", type);
273 }
274}
275
Leo Chang59cdc7e2013-07-10 10:08:21 -0700276#ifdef WLAN_KD_READY_NOTIFIER
277/*
278 * Send Net Link interface ready indication to application daemon
279 * Each netlink message will have a message of type tAniMsgHdr inside.
280 */
281void nl_srv_nl_ready_indication
282(
283 void
284)
285{
286 struct sk_buff *skb = NULL;
287 struct nlmsghdr *nlh;
288 int err;
289
290 skb = alloc_skb(NLMSG_SPACE(sizeof(driverLoaded)), GFP_KERNEL);
291 if (NULL == skb)
292 {
293 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
294 "NLINK: skb alloc fail %s", __func__);
295 return;
296 }
297
298 nlh = (struct nlmsghdr *)skb->data;
299 nlh->nlmsg_pid = 0; /* from kernel */
300 nlh->nlmsg_flags = 0;
301 nlh->nlmsg_seq = 0;
302 nlh->nlmsg_len = sizeof(driverLoaded);
303 memcpy(((char *)nlh) + sizeof(struct nlmsghdr),
304 driverLoaded,
305 sizeof(driverLoaded));
306 skb_put(skb, NLMSG_SPACE(sizeof(driverLoaded)));
307
308 /* sender is in group 1<<0 */
309 NETLINK_CB(skb).dst_group = WLAN_NLINK_MCAST_GRP_ID;
310
311 /*multicast the message to all listening processes*/
312 err = netlink_broadcast(nl_srv_sock, skb, 0, 1, GFP_KERNEL);
Leo Chang9e646082013-08-02 11:20:21 -0700313 if (err)
Leo Chang59cdc7e2013-07-10 10:08:21 -0700314 {
Leo Chang9e646082013-08-02 11:20:21 -0700315 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
316 "NLINK: Ready Indication Send Fail %s, err %d",
317 __func__, err);
Leo Chang59cdc7e2013-07-10 10:08:21 -0700318 }
319 return;
320}
321
322/*
323 * Send Net Link interface close indication to application daemon
324 * Each netlink message will have a message of type tAniMsgHdr inside.
325 */
326void nl_srv_nl_close_indication
327(
328 int pid
329)
330{
331 struct sk_buff *skb = NULL;
332 struct nlmsghdr *nlh;
333 int err;
334
335 skb = alloc_skb(sizeof(driverUnLoaded),GFP_KERNEL);
336 if (NULL == skb)
337 {
338 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
339 "NLINK: skb alloc fail %s", __func__);
340 return;
341 }
342
343 nlh = (struct nlmsghdr *)skb->data;
344 nlh->nlmsg_pid = 0; /* from kernel */
345 nlh->nlmsg_flags = 0;
346 nlh->nlmsg_seq = 0;
347 nlh->nlmsg_len = sizeof(driverUnLoaded);
348 memcpy(((char *)nlh) + sizeof(struct nlmsghdr),
349 driverUnLoaded,
350 sizeof(driverUnLoaded));
351 skb_put(skb, NLMSG_SPACE(sizeof(driverUnLoaded)));
352
353 /* sender is in group 1<<0 */
354 NETLINK_CB(skb).dst_group = 0;
355 err = netlink_unicast(nl_srv_sock, skb, pid, MSG_DONTWAIT);
Leo Chang9e646082013-08-02 11:20:21 -0700356 if (err)
Leo Chang59cdc7e2013-07-10 10:08:21 -0700357 {
Leo Chang9e646082013-08-02 11:20:21 -0700358 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
359 "NLINK: Close Indication Send Fail %s", __func__);
Leo Chang59cdc7e2013-07-10 10:08:21 -0700360 }
361
362 return;
363}
364#endif /* WLAN_KD_READY_NOTIFIER */
365