blob: 63ae62ef9a276ee09c36e7d1214c5d61c9dc6f5c [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#ifdef FEATURE_OEM_DATA_SUPPORT
29
30/**
31 * DOC: wlan_hdd_oemdata.c
32 *
33 * Support for generic OEM Data Request handling
34 *
35 */
36
37#include <linux/version.h>
38#include <linux/module.h>
39#include <linux/kernel.h>
40#include <linux/init.h>
41#include <linux/wireless.h>
42#include <wlan_hdd_includes.h>
43#include <net/arp.h>
44#include "qwlan_version.h"
45#include "cds_utils.h"
46#include "wma.h"
47
48static struct hdd_context_s *p_hdd_ctx;
49
50/**
51 * hdd_oem_data_req_callback() - OEM Data request callback handler
52 * @hHal: MAC handle
53 * @pContext: User context. For this callback the net device was registered
54 * @oemDataReqID: The ID of the request
55 * @oemDataReqStatus: Status of the request
56 *
57 * This function reports the results of the request to user space
58 *
59 * Return: CDF_STATUS enumeration
60 */
61static CDF_STATUS hdd_oem_data_req_callback(tHalHandle hHal,
62 void *pContext,
63 uint32_t oemDataReqID,
64 eOemDataReqStatus oemDataReqStatus)
65{
66 CDF_STATUS status = CDF_STATUS_SUCCESS;
67 struct net_device *dev = (struct net_device *)pContext;
68 union iwreq_data wrqu;
69 char buffer[IW_CUSTOM_MAX + 1];
70
71 memset(&wrqu, '\0', sizeof(wrqu));
72 memset(buffer, '\0', sizeof(buffer));
73
74 if (oemDataReqStatus == eOEM_DATA_REQ_FAILURE) {
75 snprintf(buffer, IW_CUSTOM_MAX, "QCOM: OEM-DATA-REQ-FAILED");
76 hddLog(LOGW, "%s: oem data req %d failed", __func__,
77 oemDataReqID);
78 } else if (oemDataReqStatus == eOEM_DATA_REQ_INVALID_MODE) {
79 snprintf(buffer, IW_CUSTOM_MAX,
80 "QCOM: OEM-DATA-REQ-INVALID-MODE");
81 hddLog(LOGW,
82 "%s: oem data req %d failed because the driver is in invalid mode (IBSS|AP)",
83 __func__, oemDataReqID);
84 } else {
85 snprintf(buffer, IW_CUSTOM_MAX, "QCOM: OEM-DATA-REQ-SUCCESS");
86 }
87
88 wrqu.data.pointer = buffer;
89 wrqu.data.length = strlen(buffer);
90
91 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buffer);
92
93 return status;
94}
95
96/**
97 * iw_get_oem_data_cap() - Get OEM Data Capabilities
98 * @dev: net device upon which the request was received
99 * @info: ioctl request information
100 * @wrqu: ioctl request data
101 * @extra: ioctl data payload
102 *
103 * This function gets the capability information for OEM Data Request
104 * and Response.
105 *
106 * Return: 0 for success, negative errno value on failure
107 */
108int iw_get_oem_data_cap(struct net_device *dev,
109 struct iw_request_info *info,
110 union iwreq_data *wrqu, char *extra)
111{
112 CDF_STATUS status;
113 t_iw_oem_data_cap oemDataCap;
114 t_iw_oem_data_cap *pHddOemDataCap;
115 hdd_adapter_t *pAdapter = (netdev_priv(dev));
116 hdd_context_t *pHddContext;
117 struct hdd_config *pConfig;
118 uint32_t numChannels;
119 uint8_t chanList[OEM_CAP_MAX_NUM_CHANNELS];
120 uint32_t i;
121 int ret;
122
123 ENTER();
124
125 pHddContext = WLAN_HDD_GET_CTX(pAdapter);
126 ret = wlan_hdd_validate_context(pHddContext);
127 if (0 != ret)
128 return ret;
129
130 pConfig = pHddContext->config;
131 if (!pConfig) {
132 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
133 "%s:HDD configuration is null", __func__);
134 return -ENOENT;
135 }
136
137 do {
138 cdf_mem_zero(&oemDataCap, sizeof(oemDataCap));
139 strlcpy(oemDataCap.oem_target_signature, OEM_TARGET_SIGNATURE,
140 OEM_TARGET_SIGNATURE_LEN);
141 oemDataCap.oem_target_type = pHddContext->target_type;
142 oemDataCap.oem_fw_version = pHddContext->target_fw_version;
143 oemDataCap.driver_version.major = QWLAN_VERSION_MAJOR;
144 oemDataCap.driver_version.minor = QWLAN_VERSION_MINOR;
145 oemDataCap.driver_version.patch = QWLAN_VERSION_PATCH;
146 oemDataCap.driver_version.build = QWLAN_VERSION_BUILD;
147 oemDataCap.allowed_dwell_time_min =
148 pConfig->nNeighborScanMinChanTime;
149 oemDataCap.allowed_dwell_time_max =
150 pConfig->nNeighborScanMaxChanTime;
151 oemDataCap.curr_dwell_time_min =
152 sme_get_neighbor_scan_min_chan_time(pHddContext->hHal,
153 pAdapter->sessionId);
154 oemDataCap.curr_dwell_time_max =
155 sme_get_neighbor_scan_max_chan_time(pHddContext->hHal,
156 pAdapter->sessionId);
157 oemDataCap.supported_bands = pConfig->nBandCapability;
158
159 /* request for max num of channels */
160 numChannels = WNI_CFG_VALID_CHANNEL_LIST_LEN;
161 status = sme_get_cfg_valid_channels(pHddContext->hHal,
162 &chanList[0], &numChannels);
163 if (CDF_STATUS_SUCCESS != status) {
164 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
165 "%s:failed to get valid channel list",
166 __func__);
167 return -ENOENT;
168 } else {
169 /* make sure num channels is not more than chan list array */
170 if (numChannels > OEM_CAP_MAX_NUM_CHANNELS) {
171 CDF_TRACE(CDF_MODULE_ID_HDD,
172 CDF_TRACE_LEVEL_ERROR,
173 "%s:Num of channels(%d) more than length(%d) of chanlist",
174 __func__, numChannels,
175 OEM_CAP_MAX_NUM_CHANNELS);
176 return -ENOMEM;
177 }
178
179 oemDataCap.num_channels = numChannels;
180 for (i = 0; i < numChannels; i++) {
181 oemDataCap.channel_list[i] = chanList[i];
182 }
183 }
184
185 pHddOemDataCap = (t_iw_oem_data_cap *) (extra);
186 cdf_mem_copy(pHddOemDataCap, &oemDataCap,
187 sizeof(*pHddOemDataCap));
188 } while (0);
189
190 EXIT();
191 return 0;
192}
193
194/**
195 * send_oem_reg_rsp_nlink_msg() - send oem registration response
196 *
197 * This function sends oem message to registered application process
198 *
199 * Return: none
200 */
201static void send_oem_reg_rsp_nlink_msg(void)
202{
203 struct sk_buff *skb;
204 struct nlmsghdr *nlh;
205 tAniMsgHdr *aniHdr;
206 uint8_t *buf;
207 uint8_t *numInterfaces;
208 uint8_t *deviceMode;
209 uint8_t *vdevId;
210 hdd_adapter_list_node_t *pAdapterNode = NULL;
211 hdd_adapter_list_node_t *pNext = NULL;
212 hdd_adapter_t *pAdapter = NULL;
213 CDF_STATUS status = 0;
214
215 /* OEM message is always to a specific process and cannot be a broadcast */
216 if (p_hdd_ctx->oem_pid == 0) {
217 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
218 "%s: invalid dest pid", __func__);
219 return;
220 }
221
222 skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL);
223 if (skb == NULL) {
224 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
225 "%s: alloc_skb failed", __func__);
226 return;
227 }
228
229 nlh = (struct nlmsghdr *)skb->data;
230 nlh->nlmsg_pid = 0; /* from kernel */
231 nlh->nlmsg_flags = 0;
232 nlh->nlmsg_seq = 0;
233 nlh->nlmsg_type = WLAN_NL_MSG_OEM;
234 aniHdr = NLMSG_DATA(nlh);
235 aniHdr->type = ANI_MSG_APP_REG_RSP;
236
237 /* Fill message body:
238 * First byte will be number of interfaces, followed by
239 * two bytes for each interfaces
240 * - one byte for device mode
241 * - one byte for vdev id
242 */
243 buf = (char *)((char *)aniHdr + sizeof(tAniMsgHdr));
244 numInterfaces = buf++;
245 *numInterfaces = 0;
246
247 /* Iterate through each of the adapters and fill device mode and vdev id */
248 status = hdd_get_front_adapter(p_hdd_ctx, &pAdapterNode);
249 while ((CDF_STATUS_SUCCESS == status) && pAdapterNode) {
250 pAdapter = pAdapterNode->pAdapter;
251 if (pAdapter) {
252 deviceMode = buf++;
253 vdevId = buf++;
254 *deviceMode = pAdapter->device_mode;
255 *vdevId = pAdapter->sessionId;
256 (*numInterfaces)++;
257 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
258 "%s: numInterfaces: %d, deviceMode: %d, vdevId: %d",
259 __func__, *numInterfaces, *deviceMode,
260 *vdevId);
261 }
262 status = hdd_get_next_adapter(p_hdd_ctx, pAdapterNode, &pNext);
263 pAdapterNode = pNext;
264 }
265
266 aniHdr->length =
267 sizeof(uint8_t) + (*numInterfaces) * 2 * sizeof(uint8_t);
268 nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + aniHdr->length));
269
270 skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + aniHdr->length)));
271
272 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
273 "%s: sending App Reg Response length (%d) to process pid (%d)",
274 __func__, aniHdr->length, p_hdd_ctx->oem_pid);
275
276 (void)nl_srv_ucast(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT);
277
278 return;
279}
280
281/**
282 * send_oem_err_rsp_nlink_msg() - send oem error response
283 * @app_pid: PID of oem application process
284 * @error_code: response error code
285 *
286 * This function sends error response to oem app
287 *
288 * Return: none
289 */
290static void send_oem_err_rsp_nlink_msg(int32_t app_pid, uint8_t error_code)
291{
292 struct sk_buff *skb;
293 struct nlmsghdr *nlh;
294 tAniMsgHdr *aniHdr;
295 uint8_t *buf;
296
297 skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL);
298 if (skb == NULL) {
299 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
300 "%s: alloc_skb failed", __func__);
301 return;
302 }
303
304 nlh = (struct nlmsghdr *)skb->data;
305 nlh->nlmsg_pid = 0; /* from kernel */
306 nlh->nlmsg_flags = 0;
307 nlh->nlmsg_seq = 0;
308 nlh->nlmsg_type = WLAN_NL_MSG_OEM;
309 aniHdr = NLMSG_DATA(nlh);
310 aniHdr->type = ANI_MSG_OEM_ERROR;
311 aniHdr->length = sizeof(uint8_t);
312 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + aniHdr->length);
313
314 /* message body will contain one byte of error code */
315 buf = (char *)((char *)aniHdr + sizeof(tAniMsgHdr));
316 *buf = error_code;
317
318 skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + aniHdr->length));
319
320 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
321 "%s: sending oem error response to process pid (%d)",
322 __func__, app_pid);
323
324 (void)nl_srv_ucast(skb, app_pid, MSG_DONTWAIT);
325
326 return;
327}
328
329/**
330 * hdd_send_oem_data_rsp_msg() - send oem data response
331 * @length: length of the OEM Data Response message
332 * @oemDataRsp: the actual OEM Data Response message
333 *
334 * This function sends an OEM Data Response message to a registered
335 * application process over the netlink socket.
336 *
337 * Return: 0 for success, non zero for failure
338 */
339void hdd_send_oem_data_rsp_msg(int length, uint8_t *oemDataRsp)
340{
341 struct sk_buff *skb;
342 struct nlmsghdr *nlh;
343 tAniMsgHdr *aniHdr;
344 uint8_t *oemData;
345
346 /* OEM message is always to a specific process and cannot be a broadcast */
347 if (p_hdd_ctx->oem_pid == 0) {
348 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
349 "%s: invalid dest pid", __func__);
350 return;
351 }
352
353 if (length > OEM_DATA_RSP_SIZE) {
354 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
355 "%s: invalid length of Oem Data response", __func__);
356 return;
357 }
358
359 skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + OEM_DATA_RSP_SIZE),
360 GFP_KERNEL);
361 if (skb == NULL) {
362 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
363 "%s: alloc_skb failed", __func__);
364 return;
365 }
366
367 nlh = (struct nlmsghdr *)skb->data;
368 nlh->nlmsg_pid = 0; /* from kernel */
369 nlh->nlmsg_flags = 0;
370 nlh->nlmsg_seq = 0;
371 nlh->nlmsg_type = WLAN_NL_MSG_OEM;
372 aniHdr = NLMSG_DATA(nlh);
373 aniHdr->type = ANI_MSG_OEM_DATA_RSP;
374
375 aniHdr->length = length;
376 nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + aniHdr->length));
377 oemData = (uint8_t *) ((char *)aniHdr + sizeof(tAniMsgHdr));
378 cdf_mem_copy(oemData, oemDataRsp, length);
379
380 skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + aniHdr->length)));
381
382 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
383 "%s: sending Oem Data Response of len (%d) to process pid (%d)",
384 __func__, length, p_hdd_ctx->oem_pid);
385
386 (void)nl_srv_ucast(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT);
387
388 return;
389}
390
391/**
392 * oem_process_data_req_msg() - process oem data request
393 * @oemDataLen: Length to OEM Data buffer
394 * @oemData: Pointer to OEM Data buffer
395 *
396 * This function sends oem message to SME
397 *
398 * Return: CDF_STATUS enumeration
399 */
400static CDF_STATUS oem_process_data_req_msg(int oemDataLen, char *oemData)
401{
402 hdd_adapter_t *pAdapter = NULL;
403 tOemDataReqConfig oemDataReqConfig;
404 uint32_t oemDataReqID = 0;
405 CDF_STATUS status = CDF_STATUS_SUCCESS;
406
407 /* for now, STA interface only */
408 pAdapter = hdd_get_adapter(p_hdd_ctx, WLAN_HDD_INFRA_STATION);
409 if (!pAdapter) {
410 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
411 "%s: No adapter for STA mode", __func__);
412 return CDF_STATUS_E_FAILURE;
413 }
414
415 if (!oemData) {
416 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
417 "%s: oemData is null", __func__);
418 return CDF_STATUS_E_FAILURE;
419 }
420
421 cdf_mem_zero(&oemDataReqConfig, sizeof(tOemDataReqConfig));
422
423 cdf_mem_copy((&oemDataReqConfig)->oemDataReq, oemData, oemDataLen);
424
425 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
426 "%s: calling sme_oem_data_req", __func__);
427
428 status = sme_oem_data_req(p_hdd_ctx->hHal,
429 pAdapter->sessionId,
430 &oemDataReqConfig,
431 &oemDataReqID,
432 &hdd_oem_data_req_callback, pAdapter->dev);
433 return status;
434}
435
436/**
437 * oem_process_channel_info_req_msg() - process oem channel_info request
438 * @numOfChannels: number of channels
439 * @chanList: list of channel information
440 *
441 * This function responds with channel info to oem process
442 *
443 * Return: 0 for success, non zero for failure
444 */
445static int oem_process_channel_info_req_msg(int numOfChannels, char *chanList)
446{
447 struct sk_buff *skb;
448 struct nlmsghdr *nlh;
449 tAniMsgHdr *aniHdr;
450 tHddChannelInfo *pHddChanInfo;
451 tHddChannelInfo hddChanInfo;
452 uint8_t chanId;
453 uint32_t reg_info_1;
454 uint32_t reg_info_2;
455 CDF_STATUS status = CDF_STATUS_E_FAILURE;
456 int i;
457 uint8_t *buf;
458
459 /* OEM message is always to a specific process and cannot be a broadcast */
460 if (p_hdd_ctx->oem_pid == 0) {
461 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
462 "%s: invalid dest pid", __func__);
463 return -EPERM;
464 }
465
466 skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + sizeof(uint8_t) +
467 numOfChannels * sizeof(tHddChannelInfo)),
468 GFP_KERNEL);
469 if (skb == NULL) {
470 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
471 "%s: alloc_skb failed", __func__);
472 return -ENOMEM;
473 }
474
475 nlh = (struct nlmsghdr *)skb->data;
476 nlh->nlmsg_pid = 0; /* from kernel */
477 nlh->nlmsg_flags = 0;
478 nlh->nlmsg_seq = 0;
479 nlh->nlmsg_type = WLAN_NL_MSG_OEM;
480 aniHdr = NLMSG_DATA(nlh);
481 aniHdr->type = ANI_MSG_CHANNEL_INFO_RSP;
482
483 aniHdr->length =
484 sizeof(uint8_t) + numOfChannels * sizeof(tHddChannelInfo);
485 nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + aniHdr->length));
486
487 /* First byte of message body will have num of channels */
488 buf = (char *)((char *)aniHdr + sizeof(tAniMsgHdr));
489 *buf++ = numOfChannels;
490
491 /* Next follows channel info struct for each channel id.
492 * If chan id is wrong or SME returns failure for a channel
493 * then fill in 0 in channel info for that particular channel
494 */
495 for (i = 0; i < numOfChannels; i++) {
496 pHddChanInfo = (tHddChannelInfo *) ((char *)buf +
497 i *
498 sizeof(tHddChannelInfo));
499
500 chanId = chanList[i];
501 status = sme_get_reg_info(p_hdd_ctx->hHal, chanId,
502 &reg_info_1, &reg_info_2);
503 if (CDF_STATUS_SUCCESS == status) {
504 /* copy into hdd chan info struct */
505 hddChanInfo.chan_id = chanId;
506 hddChanInfo.reserved0 = 0;
507 hddChanInfo.mhz = cds_chan_to_freq(chanId);
508 hddChanInfo.band_center_freq1 = hddChanInfo.mhz;
509 hddChanInfo.band_center_freq2 = 0;
510
511 hddChanInfo.info = 0;
512 if (CHANNEL_STATE_DFS ==
513 cds_get_channel_state(chanId))
514 WMI_SET_CHANNEL_FLAG(&hddChanInfo,
515 WMI_CHAN_FLAG_DFS);
516 hddChanInfo.reg_info_1 = reg_info_1;
517 hddChanInfo.reg_info_2 = reg_info_2;
518 } else {
519 /* channel info is not returned, fill in zeros in channel
520 * info struct
521 */
522 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
523 "%s: sme_get_reg_info failed for chan (%d), return info 0",
524 __func__, chanId);
525 hddChanInfo.chan_id = chanId;
526 hddChanInfo.reserved0 = 0;
527 hddChanInfo.mhz = 0;
528 hddChanInfo.band_center_freq1 = 0;
529 hddChanInfo.band_center_freq2 = 0;
530 hddChanInfo.info = 0;
531 hddChanInfo.reg_info_1 = 0;
532 hddChanInfo.reg_info_2 = 0;
533 }
534 cdf_mem_copy(pHddChanInfo, &hddChanInfo,
535 sizeof(tHddChannelInfo));
536 }
537
538 skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + aniHdr->length)));
539
540 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
541 "%s: sending channel info resp for num channels (%d) to pid (%d)",
542 __func__, numOfChannels, p_hdd_ctx->oem_pid);
543
544 (void)nl_srv_ucast(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT);
545
546 return 0;
547}
548
549/**
550 * hdd_send_peer_status_ind_to_oem_app() -
551 * Function to send peer status to a registered application
552 * @peerMac: MAC address of peer
553 * @peerStatus: ePeerConnected or ePeerDisconnected
554 * @peerTimingMeasCap: 0: RTT/RTT2, 1: RTT3. Default is 0
555 * @sessionId: SME session id, i.e. vdev_id
556 * @chan_info: operating channel information
557 *
558 * Return: none
559 */
560void hdd_send_peer_status_ind_to_oem_app(struct cdf_mac_addr *peerMac,
561 uint8_t peerStatus,
562 uint8_t peerTimingMeasCap,
563 uint8_t sessionId,
564 tSirSmeChanInfo *chan_info)
565{
566 struct sk_buff *skb;
567 struct nlmsghdr *nlh;
568 tAniMsgHdr *aniHdr;
569 tPeerStatusInfo *pPeerInfo;
570
571 if (!p_hdd_ctx || !p_hdd_ctx->hHal) {
572 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
573 "%s: Either HDD Ctx is null or Hal Ctx is null",
574 __func__);
575 return;
576 }
577
578 /* check if oem app has registered and pid is valid */
579 if ((!p_hdd_ctx->oem_app_registered) || (p_hdd_ctx->oem_pid == 0)) {
580 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
581 "%s: OEM app is not registered(%d) or pid is invalid(%d)",
582 __func__, p_hdd_ctx->oem_app_registered,
583 p_hdd_ctx->oem_pid);
584 return;
585 }
586
587 skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) +
588 sizeof(tPeerStatusInfo)),
589 GFP_KERNEL);
590 if (skb == NULL) {
591 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
592 "%s: alloc_skb failed", __func__);
593 return;
594 }
595
596 nlh = (struct nlmsghdr *)skb->data;
597 nlh->nlmsg_pid = 0; /* from kernel */
598 nlh->nlmsg_flags = 0;
599 nlh->nlmsg_seq = 0;
600 nlh->nlmsg_type = WLAN_NL_MSG_OEM;
601 aniHdr = NLMSG_DATA(nlh);
602 aniHdr->type = ANI_MSG_PEER_STATUS_IND;
603
604 aniHdr->length = sizeof(tPeerStatusInfo);
605 nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + aniHdr->length));
606
607 pPeerInfo = (tPeerStatusInfo *) ((char *)aniHdr + sizeof(tAniMsgHdr));
608
609 cdf_mem_copy(pPeerInfo->peer_mac_addr, peerMac->bytes,
610 sizeof(peerMac->bytes));
611 pPeerInfo->peer_status = peerStatus;
612 pPeerInfo->vdev_id = sessionId;
613 pPeerInfo->peer_capability = peerTimingMeasCap;
614 pPeerInfo->reserved0 = 0;
615
616 if (chan_info) {
617 pPeerInfo->peer_chan_info.chan_id = chan_info->chan_id;
618 pPeerInfo->peer_chan_info.reserved0 = 0;
619 pPeerInfo->peer_chan_info.mhz = chan_info->mhz;
620 pPeerInfo->peer_chan_info.band_center_freq1 =
621 chan_info->band_center_freq1;
622 pPeerInfo->peer_chan_info.band_center_freq2 =
623 chan_info->band_center_freq2;
624 pPeerInfo->peer_chan_info.info = chan_info->info;
625 pPeerInfo->peer_chan_info.reg_info_1 = chan_info->reg_info_1;
626 pPeerInfo->peer_chan_info.reg_info_2 = chan_info->reg_info_2;
627 } else {
628 pPeerInfo->peer_chan_info.chan_id = 0;
629 pPeerInfo->peer_chan_info.reserved0 = 0;
630 pPeerInfo->peer_chan_info.mhz = 0;
631 pPeerInfo->peer_chan_info.band_center_freq1 = 0;
632 pPeerInfo->peer_chan_info.band_center_freq2 = 0;
633 pPeerInfo->peer_chan_info.info = 0;
634 pPeerInfo->peer_chan_info.reg_info_1 = 0;
635 pPeerInfo->peer_chan_info.reg_info_2 = 0;
636 }
637 skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + aniHdr->length)));
638
639 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
640 "%s: sending peer " MAC_ADDRESS_STR
641 " status(%d), peerTimingMeasCap(%d), vdevId(%d), chanId(%d)"
642 " to oem app pid(%d), center freq 1 (%d), center freq 2 (%d),"
643 " info (0x%x), frequency (%d),reg info 1 (0x%x),"
644 " reg info 2 (0x%x)", __func__,
645 MAC_ADDR_ARRAY(peerMac->bytes),
646 peerStatus, peerTimingMeasCap,
647 sessionId, pPeerInfo->peer_chan_info.chan_id,
648 p_hdd_ctx->oem_pid,
649 pPeerInfo->peer_chan_info.band_center_freq1,
650 pPeerInfo->peer_chan_info.band_center_freq2,
651 pPeerInfo->peer_chan_info.info,
652 pPeerInfo->peer_chan_info.mhz,
653 pPeerInfo->peer_chan_info.reg_info_1,
654 pPeerInfo->peer_chan_info.reg_info_2);
655
656 (void)nl_srv_ucast(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT);
657
658 return;
659}
660
661/*
662 * Callback function invoked by Netlink service for all netlink
663 * messages (from user space) addressed to WLAN_NL_MSG_OEM
664 */
665
666/**
667 * oem_msg_callback() - callback invoked by netlink service
668 * @skb: skb with netlink message
669 *
670 * This function gets invoked by netlink service when a message
671 * is received from user space addressed to WLAN_NL_MSG_OEM
672 *
673 * Return: zero on success
674 * On error, error number will be returned.
675 */
676static int oem_msg_callback(struct sk_buff *skb)
677{
678 struct nlmsghdr *nlh;
679 tAniMsgHdr *msg_hdr;
680 int ret;
681 char *sign_str = NULL;
682 nlh = (struct nlmsghdr *)skb->data;
683
684 if (!nlh) {
685 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
686 "%s: Netlink header null", __func__);
687 return -EPERM;
688 }
689
690 ret = wlan_hdd_validate_context(p_hdd_ctx);
691 if (0 != ret) {
692 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
693 FL("HDD context is not valid"));
694 return ret;
695 }
696
697 msg_hdr = NLMSG_DATA(nlh);
698
699 if (!msg_hdr) {
700 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
701 "%s: Message header null", __func__);
702 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
703 OEM_ERR_NULL_MESSAGE_HEADER);
704 return -EPERM;
705 }
706
707 if (nlh->nlmsg_len <
708 NLMSG_LENGTH(sizeof(tAniMsgHdr) + msg_hdr->length)) {
709 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
710 "%s: Invalid nl msg len, nlh->nlmsg_len (%d), msg_hdr->len (%d)",
711 __func__, nlh->nlmsg_len, msg_hdr->length);
712 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
713 OEM_ERR_INVALID_MESSAGE_LENGTH);
714 return -EPERM;
715 }
716
717 switch (msg_hdr->type) {
718 case ANI_MSG_APP_REG_REQ:
719 /* Registration request is only allowed for Qualcomm Application */
720 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
721 "%s: Received App Req Req from App process pid(%d), len(%d)",
722 __func__, nlh->nlmsg_pid, msg_hdr->length);
723
724 sign_str = (char *)((char *)msg_hdr + sizeof(tAniMsgHdr));
725 if ((OEM_APP_SIGNATURE_LEN == msg_hdr->length) &&
726 (0 == strncmp(sign_str, OEM_APP_SIGNATURE_STR,
727 OEM_APP_SIGNATURE_LEN))) {
728 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
729 "%s: Valid App Req Req from oem app process pid(%d)",
730 __func__, nlh->nlmsg_pid);
731
732 p_hdd_ctx->oem_app_registered = true;
733 p_hdd_ctx->oem_pid = nlh->nlmsg_pid;
734 send_oem_reg_rsp_nlink_msg();
735 } else {
736 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
737 "%s: Invalid signature in App Reg Request from pid(%d)",
738 __func__, nlh->nlmsg_pid);
739 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
740 OEM_ERR_INVALID_SIGNATURE);
741 return -EPERM;
742 }
743 break;
744
745 case ANI_MSG_OEM_DATA_REQ:
746 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
747 "%s: Received Oem Data Request length(%d) from pid: %d",
748 __func__, msg_hdr->length, nlh->nlmsg_pid);
749
750 if ((!p_hdd_ctx->oem_app_registered) ||
751 (nlh->nlmsg_pid != p_hdd_ctx->oem_pid)) {
752 /* either oem app is not registered yet or pid is different */
753 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
754 "%s: OEM DataReq: app not registered(%d) or incorrect pid(%d)",
755 __func__, p_hdd_ctx->oem_app_registered,
756 nlh->nlmsg_pid);
757 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
758 OEM_ERR_APP_NOT_REGISTERED);
759 return -EPERM;
760 }
761
762 if ((!msg_hdr->length) || (OEM_DATA_REQ_SIZE < msg_hdr->length)) {
763 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
764 "%s: Invalid length (%d) in Oem Data Request",
765 __func__, msg_hdr->length);
766 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
767 OEM_ERR_INVALID_MESSAGE_LENGTH);
768 return -EPERM;
769 }
770 oem_process_data_req_msg(msg_hdr->length,
771 (char *)((char *)msg_hdr +
772 sizeof(tAniMsgHdr)));
773 break;
774
775 case ANI_MSG_CHANNEL_INFO_REQ:
776 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
777 "%s: Received channel info request, num channel(%d) from pid: %d",
778 __func__, msg_hdr->length, nlh->nlmsg_pid);
779
780 if ((!p_hdd_ctx->oem_app_registered) ||
781 (nlh->nlmsg_pid != p_hdd_ctx->oem_pid)) {
782 /* either oem app is not registered yet or pid is different */
783 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
784 "%s: Chan InfoReq: app not registered(%d) or incorrect pid(%d)",
785 __func__, p_hdd_ctx->oem_app_registered,
786 nlh->nlmsg_pid);
787 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
788 OEM_ERR_APP_NOT_REGISTERED);
789 return -EPERM;
790 }
791
792 /* message length contains list of channel ids */
793 if ((!msg_hdr->length) ||
794 (WNI_CFG_VALID_CHANNEL_LIST_LEN < msg_hdr->length)) {
795 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
796 "%s: Invalid length (%d) in channel info request",
797 __func__, msg_hdr->length);
798 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
799 OEM_ERR_INVALID_MESSAGE_LENGTH);
800 return -EPERM;
801 }
802 oem_process_channel_info_req_msg(msg_hdr->length,
803 (char *)((char *)msg_hdr +
804 sizeof(tAniMsgHdr)));
805 break;
806
807 default:
808 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
809 "%s: Received Invalid message type (%d), length (%d)",
810 __func__, msg_hdr->type, msg_hdr->length);
811 send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
812 OEM_ERR_INVALID_MESSAGE_TYPE);
813 return -EPERM;
814 }
815 return 0;
816}
817
818static int __oem_msg_callback(struct sk_buff *skb)
819{
820 int ret;
821
822 cds_ssr_protect(__func__);
823 ret = oem_msg_callback(skb);
824 cds_ssr_unprotect(__func__);
825
826 return ret;
827}
828
829/**
830 * oem_activate_service() - Activate oem message handler
831 * @hdd_ctx: pointer to global HDD context
832 *
833 * This function registers a handler to receive netlink message from
834 * an OEM application process.
835 *
836 * Return: zero on success
837 * On error, error number will be returned.
838 */
839int oem_activate_service(struct hdd_context_s *hdd_ctx)
840{
841 p_hdd_ctx = hdd_ctx;
842
843 /* Register the msg handler for msgs addressed to WLAN_NL_MSG_OEM */
844 return nl_srv_register(WLAN_NL_MSG_OEM, __oem_msg_callback);
845}
846
847#endif