blob: 90104812d73e08941106102ba532dde677c81887 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Tushnim Bhattacharyyaca50b322015-12-28 17:14:36 -08002 * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
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/* Include Files */
29
30#include <wlan_hdd_includes.h>
31#include <wlan_hdd_wowl.h>
32#include "wlan_hdd_trace.h"
33#include "wlan_hdd_ioctl.h"
34#include "wlan_hdd_power.h"
35#include "wlan_hdd_driver_ops.h"
36#include "cds_concurrency.h"
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +053037#include "wlan_hdd_hostapd.h"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080038
39#include "wlan_hdd_p2p.h"
40#include <linux/ctype.h>
41#include "wma.h"
42#include "wlan_hdd_napi.h"
43
44#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
45#include <sme_api.h>
46#include <sir_api.h>
47#endif
48#include "hif.h"
49
50#if defined(LINUX_QCMBR)
51#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
52#endif
53
54/*
55 * Size of Driver command strings from upper layer
56 */
57#define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */
58#define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */
59
Rajeev Kumar8e3e2832015-11-06 16:02:54 -080060/*
61 * Ibss prop IE from command will be of size:
62 * size = sizeof(oui) + sizeof(oui_data) + 1(Element ID) + 1(EID Length)
63 * OUI_DATA should be at least 3 bytes long
64 */
65#define WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH (3)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080066
67#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
68#define TID_MIN_VALUE 0
69#define TID_MAX_VALUE 15
70#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
71
72/*
73 * Maximum buffer size used for returning the data back to user space
74 */
75#define WLAN_MAX_BUF_SIZE 1024
76#define WLAN_PRIV_DATA_MAX_LEN 8192
77
78/*
79 * Driver miracast parameters 0-Disabled
80 * 1-Source, 2-Sink
81 */
82#define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0
83#define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2
84
85/*
86 * When ever we need to print IBSSPEERINFOALL for more than 16 STA
87 * we will split the printing.
88 */
89#define NUM_OF_STA_DATA_TO_PRINT 16
90
91/*
92 * Android DRIVER command structures
93 */
94struct android_wifi_reassoc_params {
95 unsigned char bssid[18];
96 int channel;
97};
98
99#define ANDROID_WIFI_ACTION_FRAME_SIZE 1040
100struct android_wifi_af_params {
101 unsigned char bssid[18];
102 int channel;
103 int dwell_time;
104 int len;
105 unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE];
106};
107
108/*
109 * Define HDD driver command handling entry, each contains a command
110 * string and the handler.
111 */
112typedef int (*hdd_drv_cmd_handler_t)(hdd_adapter_t *adapter,
113 hdd_context_t *hdd_ctx,
114 uint8_t *cmd,
115 uint8_t cmd_name_len,
116 hdd_priv_data_t *priv_data);
117
118typedef struct {
119 const char *cmd;
120 hdd_drv_cmd_handler_t handler;
121} hdd_drv_cmd_t;
122
123#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
124#define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000
125#define WLAN_HDD_MAX_TCP_PORT 65535
126#endif
127
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800128static uint16_t cesium_pid;
129extern struct sock *cesium_nl_srv_sock;
130
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800131#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
132static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics,
133 const uint32_t staId, void *context)
134{
135 struct statsContext *stats_context = NULL;
136 hdd_adapter_t *adapter = NULL;
137
138 if (NULL == context) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530139 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800140 "%s: Bad param, context [%p]", __func__, context);
141 return;
142 }
143
144 /*
145 * there is a race condition that exists between this callback
146 * function and the caller since the caller could time out either
147 * before or while this code is executing. we use a spinlock to
148 * serialize these actions
149 */
150 spin_lock(&hdd_context_lock);
151
152 stats_context = context;
153 adapter = stats_context->pAdapter;
154 if ((NULL == adapter) ||
155 (STATS_CONTEXT_MAGIC != stats_context->magic)) {
156 /*
157 * the caller presumably timed out so there is
158 * nothing we can do
159 */
160 spin_unlock(&hdd_context_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530161 hddLog(QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800162 "%s: Invalid context, adapter [%p] magic [%08x]",
163 __func__, adapter, stats_context->magic);
164 return;
165 }
166
167 /* context is valid so caller is still waiting */
168
169 /* paranoia: invalidate the magic */
170 stats_context->magic = 0;
171
172 /* copy over the tsm stats */
173 adapter->tsmStats.UplinkPktQueueDly = tsm_metrics.UplinkPktQueueDly;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530174 qdf_mem_copy(adapter->tsmStats.UplinkPktQueueDlyHist,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800175 tsm_metrics.UplinkPktQueueDlyHist,
176 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
177 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist[0]));
178 adapter->tsmStats.UplinkPktTxDly = tsm_metrics.UplinkPktTxDly;
179 adapter->tsmStats.UplinkPktLoss = tsm_metrics.UplinkPktLoss;
180 adapter->tsmStats.UplinkPktCount = tsm_metrics.UplinkPktCount;
181 adapter->tsmStats.RoamingCount = tsm_metrics.RoamingCount;
182 adapter->tsmStats.RoamingDly = tsm_metrics.RoamingDly;
183
184 /* notify the caller */
185 complete(&stats_context->completion);
186
187 /* serialization is complete */
188 spin_unlock(&hdd_context_lock);
189}
190
191static
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530192QDF_STATUS hdd_get_tsm_stats(hdd_adapter_t *adapter,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800193 const uint8_t tid,
194 tAniTrafStrmMetrics *tsm_metrics)
195{
196 hdd_station_ctx_t *hdd_sta_ctx = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530197 QDF_STATUS hstatus;
198 QDF_STATUS vstatus = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800199 unsigned long rc;
200 struct statsContext context;
201 hdd_context_t *hdd_ctx = NULL;
202
203 if (NULL == adapter) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530204 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800205 "%s: adapter is NULL", __func__);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530206 return QDF_STATUS_E_FAULT;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800207 }
208
209 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
210 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
211
212 /* we are connected prepare our callback context */
213 init_completion(&context.completion);
214 context.pAdapter = adapter;
215 context.magic = STATS_CONTEXT_MAGIC;
216
217 /* query tsm stats */
218 hstatus = sme_get_tsm_stats(hdd_ctx->hHal, hdd_get_tsm_stats_cb,
219 hdd_sta_ctx->conn_info.staId[0],
220 hdd_sta_ctx->conn_info.bssId,
221 &context, hdd_ctx->pcds_context, tid);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530222 if (QDF_STATUS_SUCCESS != hstatus) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530223 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800224 "%s: Unable to retrieve statistics", __func__);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530225 vstatus = QDF_STATUS_E_FAULT;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800226 } else {
227 /* request was sent -- wait for the response */
228 rc = wait_for_completion_timeout(&context.completion,
229 msecs_to_jiffies(WLAN_WAIT_TIME_STATS));
230 if (!rc) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530231 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800232 "%s: SME timed out while retrieving statistics",
233 __func__);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530234 vstatus = QDF_STATUS_E_TIMEOUT;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800235 }
236 }
237
238 /*
239 * either we never sent a request, we sent a request and received a
240 * response or we sent a request and timed out. if we never sent a
241 * request or if we sent a request and got a response, we want to
242 * clear the magic out of paranoia. if we timed out there is a
243 * race condition such that the callback function could be
244 * executing at the same time we are. of primary concern is if the
245 * callback function had already verified the "magic" but had not
246 * yet set the completion variable when a timeout occurred. we
247 * serialize these activities by invalidating the magic while
248 * holding a shared spinlock which will cause us to block if the
249 * callback is currently executing
250 */
251 spin_lock(&hdd_context_lock);
252 context.magic = 0;
253 spin_unlock(&hdd_context_lock);
254
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530255 if (QDF_STATUS_SUCCESS == vstatus) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800256 tsm_metrics->UplinkPktQueueDly =
257 adapter->tsmStats.UplinkPktQueueDly;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530258 qdf_mem_copy(tsm_metrics->UplinkPktQueueDlyHist,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800259 adapter->tsmStats.UplinkPktQueueDlyHist,
260 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
261 sizeof(adapter->tsmStats.
262 UplinkPktQueueDlyHist[0]));
263 tsm_metrics->UplinkPktTxDly = adapter->tsmStats.UplinkPktTxDly;
264 tsm_metrics->UplinkPktLoss = adapter->tsmStats.UplinkPktLoss;
265 tsm_metrics->UplinkPktCount = adapter->tsmStats.UplinkPktCount;
266 tsm_metrics->RoamingCount = adapter->tsmStats.RoamingCount;
267 tsm_metrics->RoamingDly = adapter->tsmStats.RoamingDly;
268 }
269 return vstatus;
270}
271#endif /*FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
272
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800273/* Function header is left blank intentionally */
274static int hdd_parse_setrmcenable_command(uint8_t *pValue,
275 uint8_t *pRmcEnable)
276{
277 uint8_t *inPtr = pValue;
278 int tempInt;
279 int v = 0;
280 char buf[32];
281 *pRmcEnable = 0;
282
283 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
284
285 if (NULL == inPtr) {
286 return 0;
287 }
288
289 else if (SPACE_ASCII_VALUE != *inPtr) {
290 return 0;
291 }
292
293 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
294 inPtr++;
295
296 if ('\0' == *inPtr) {
297 return 0;
298 }
299
300 sscanf(inPtr, "%32s ", buf);
301 v = kstrtos32(buf, 10, &tempInt);
302 if (v < 0) {
303 return -EINVAL;
304 }
305
306 *pRmcEnable = tempInt;
307
308 hddLog(LOG1, FL("ucRmcEnable: %d"), *pRmcEnable);
309
310 return 0;
311}
312
313/* Function header is left blank intentionally */
314static int hdd_parse_setrmcactionperiod_command(uint8_t *pValue,
315 uint32_t *pActionPeriod)
316{
317 uint8_t *inPtr = pValue;
318 int tempInt;
319 int v = 0;
320 char buf[32];
321 *pActionPeriod = 0;
322
323 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
324
325 if (NULL == inPtr) {
326 return -EINVAL;
327 }
328
329 else if (SPACE_ASCII_VALUE != *inPtr) {
330 return -EINVAL;
331 }
332
333 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
334 inPtr++;
335
336 if ('\0' == *inPtr) {
337 return 0;
338 }
339
340 sscanf(inPtr, "%32s ", buf);
341 v = kstrtos32(buf, 10, &tempInt);
342 if (v < 0) {
343 return -EINVAL;
344 }
345
346 if ((tempInt < WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMIN) ||
347 (tempInt > WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMAX)) {
348 return -EINVAL;
349 }
350
351 *pActionPeriod = tempInt;
352
353 hddLog(LOG1, FL("uActionPeriod: %d"), *pActionPeriod);
354
355 return 0;
356}
357
358/* Function header is left blank intentionally */
359static int hdd_parse_setrmcrate_command(uint8_t *pValue,
360 uint32_t *pRate,
361 tTxrateinfoflags *pTxFlags)
362{
363 uint8_t *inPtr = pValue;
364 int tempInt;
365 int v = 0;
366 char buf[32];
367 *pRate = 0;
368 *pTxFlags = 0;
369
370 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
371
372 if (NULL == inPtr) {
373 return -EINVAL;
374 }
375
376 else if (SPACE_ASCII_VALUE != *inPtr) {
377 return -EINVAL;
378 }
379
380 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
381 inPtr++;
382
383 if ('\0' == *inPtr) {
384 return 0;
385 }
386
387 sscanf(inPtr, "%32s ", buf);
388 v = kstrtos32(buf, 10, &tempInt);
389 if (v < 0) {
390 return -EINVAL;
391 }
392
393 switch (tempInt) {
394 default:
395 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_WARN,
396 "Unsupported rate: %d", tempInt);
397 return -EINVAL;
398 case 0:
399 case 6:
400 case 9:
401 case 12:
402 case 18:
403 case 24:
404 case 36:
405 case 48:
406 case 54:
407 *pTxFlags = eHAL_TX_RATE_LEGACY;
408 *pRate = tempInt * 10;
409 break;
410 case 65:
411 *pTxFlags = eHAL_TX_RATE_HT20;
412 *pRate = tempInt * 10;
413 break;
414 case 72:
415 *pTxFlags = eHAL_TX_RATE_HT20 | eHAL_TX_RATE_SGI;
416 *pRate = 722;
417 break;
418 }
419
420 hddLog(LOG1, FL("Rate: %d"), *pRate);
421
422 return 0;
423}
424
425/**
426 * hdd_cfg80211_get_ibss_peer_info_cb() - Callback function for IBSS
427 * Peer Info request
428 * @pUserData: Adapter private data
429 * @pPeerInfoRsp: Peer info response
430 *
431 * This is an asynchronous callback function from SME when the peer info
432 * is received
433 *
434 * Return: 0 for success non-zero for failure
435 */
436static void
437hdd_cfg80211_get_ibss_peer_info_cb(void *pUserData, void *pPeerInfoRsp)
438{
439 hdd_adapter_t *adapter = (hdd_adapter_t *) pUserData;
440 hdd_ibss_peer_info_t *pPeerInfo = (hdd_ibss_peer_info_t *)pPeerInfoRsp;
441 hdd_station_ctx_t *pStaCtx;
442 uint8_t i;
443
444 /* Sanity check */
445 if ((NULL == adapter) ||
446 (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
447 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_FATAL,
448 "invalid adapter or adapter has invalid magic");
449 return;
450 }
451
452 pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
453 if (NULL != pPeerInfo && QDF_STATUS_SUCCESS == pPeerInfo->status) {
454 pStaCtx->ibss_peer_info.status = pPeerInfo->status;
455 pStaCtx->ibss_peer_info.numIBSSPeers = pPeerInfo->numIBSSPeers;
456
457 /* Paranoia check */
458 if (pPeerInfo->numIBSSPeers < MAX_IBSS_PEERS) {
459 for (i = 0; i < pPeerInfo->numIBSSPeers; i++) {
460 memcpy(&pStaCtx->ibss_peer_info.ibssPeerList[i],
461 &pPeerInfo->ibssPeerList[i],
462 sizeof(hdd_ibss_peer_info_params_t));
463 }
464 hddLog(LOG1, FL("Peer Info copied in HDD"));
465 } else {
466 hddLog(LOG1,
467 FL("Number of peers %d returned is more than limit %d"),
468 pPeerInfo->numIBSSPeers, MAX_IBSS_PEERS);
469 }
470 } else {
471 hddLog(LOG1, FL("peerInfo returned is NULL"));
472 }
473
474 complete(&adapter->ibss_peer_info_comp);
475}
476
477/**
478 * hdd_cfg80211_get_ibss_peer_info_all() - get ibss peers' info
479 * @adapter: Adapter context
480 *
481 * Request function to get IBSS peer info from lower layers
482 *
483 * Return: 0 for success non-zero for failure
484 */
485static
486QDF_STATUS hdd_cfg80211_get_ibss_peer_info_all(hdd_adapter_t *adapter)
487{
488 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
489 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
490 unsigned long rc;
491
492 INIT_COMPLETION(adapter->ibss_peer_info_comp);
493
494 retStatus = sme_request_ibss_peer_info(hHal, adapter,
495 hdd_cfg80211_get_ibss_peer_info_cb,
496 true, 0xFF);
497
498 if (QDF_STATUS_SUCCESS == retStatus) {
499 rc = wait_for_completion_timeout
500 (&adapter->ibss_peer_info_comp,
501 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
502
503 /* status will be 0 if timed out */
504 if (!rc) {
505 hddLog(QDF_TRACE_LEVEL_WARN,
506 "%s: Warning: IBSS_PEER_INFO_TIMEOUT",
507 __func__);
508 retStatus = QDF_STATUS_E_FAILURE;
509 return retStatus;
510 }
511 } else {
512 hddLog(QDF_TRACE_LEVEL_WARN,
513 "%s: Warning: sme_request_ibss_peer_info Request failed",
514 __func__);
515 }
516
517 return retStatus;
518}
519
520/**
521 * hdd_cfg80211_get_ibss_peer_info() - get ibss peer info
522 * @adapter: Adapter context
523 * @staIdx: Sta index for which the peer info is requested
524 *
525 * Request function to get IBSS peer info from lower layers
526 *
527 * Return: 0 for success non-zero for failure
528 */
529static QDF_STATUS
530hdd_cfg80211_get_ibss_peer_info(hdd_adapter_t *adapter, uint8_t staIdx)
531{
532 unsigned long rc;
533 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
534 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
535
536 INIT_COMPLETION(adapter->ibss_peer_info_comp);
537
538 retStatus = sme_request_ibss_peer_info(hHal, adapter,
539 hdd_cfg80211_get_ibss_peer_info_cb,
540 false, staIdx);
541
542 if (QDF_STATUS_SUCCESS == retStatus) {
543 rc = wait_for_completion_timeout(
544 &adapter->ibss_peer_info_comp,
545 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
546
547 /* status = 0 on timeout */
548 if (!rc) {
549 hddLog(QDF_TRACE_LEVEL_WARN,
550 "%s: Warning: IBSS_PEER_INFO_TIMEOUT",
551 __func__);
552 retStatus = QDF_STATUS_E_FAILURE;
553 return retStatus;
554 }
555 } else {
556 hddLog(QDF_TRACE_LEVEL_WARN,
557 "%s: Warning: sme_request_ibss_peer_info Request failed",
558 __func__);
559 }
560
561 return retStatus;
562}
563
564/* Function header is left blank intentionally */
565QDF_STATUS
566hdd_parse_get_ibss_peer_info(uint8_t *pValue, struct qdf_mac_addr *pPeerMacAddr)
567{
568 uint8_t *inPtr = pValue;
569 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
570
571 if (NULL == inPtr) {
572 return QDF_STATUS_E_FAILURE;;
573 }
574
575 else if (SPACE_ASCII_VALUE != *inPtr) {
576 return QDF_STATUS_E_FAILURE;;
577 }
578
579 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
580 inPtr++;
581
582 if ('\0' == *inPtr) {
583 return QDF_STATUS_E_FAILURE;;
584 }
585
586 if (inPtr[2] != ':' || inPtr[5] != ':' || inPtr[8] != ':' ||
587 inPtr[11] != ':' || inPtr[14] != ':') {
588 return QDF_STATUS_E_FAILURE;;
589 }
590 sscanf(inPtr, "%2x:%2x:%2x:%2x:%2x:%2x",
591 (unsigned int *)&pPeerMacAddr->bytes[0],
592 (unsigned int *)&pPeerMacAddr->bytes[1],
593 (unsigned int *)&pPeerMacAddr->bytes[2],
594 (unsigned int *)&pPeerMacAddr->bytes[3],
595 (unsigned int *)&pPeerMacAddr->bytes[4],
596 (unsigned int *)&pPeerMacAddr->bytes[5]);
597
598 return QDF_STATUS_SUCCESS;
599}
600
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800601static void hdd_get_band_helper(hdd_context_t *hdd_ctx, int *pBand)
602{
603 eCsrBand band = -1;
604 sme_get_freq_band((tHalHandle) (hdd_ctx->hHal), &band);
605 switch (band) {
606 case eCSR_BAND_ALL:
607 *pBand = WLAN_HDD_UI_BAND_AUTO;
608 break;
609
610 case eCSR_BAND_24:
611 *pBand = WLAN_HDD_UI_BAND_2_4_GHZ;
612 break;
613
614 case eCSR_BAND_5G:
615 *pBand = WLAN_HDD_UI_BAND_5_GHZ;
616 break;
617
618 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530619 hddLog(QDF_TRACE_LEVEL_WARN, "%s: Invalid Band %d", __func__,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800620 band);
621 *pBand = -1;
622 break;
623 }
624}
625
626/**
627 * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel
628 * @data: input data
629 * @target_ap_bssid: pointer to bssid (output parameter)
630 * @channel: pointer to channel (output parameter)
631 *
632 * Return: 0 if parsing is successful; -EINVAL otherwise
633 */
634static int _hdd_parse_bssid_and_chan(const uint8_t **data,
635 uint8_t *bssid,
636 uint8_t *channel)
637{
638 const uint8_t *in_ptr;
639 int v = 0;
640 int temp_int;
641 uint8_t temp_buf[32];
642
643 /* 12 hexa decimal digits, 5 ':' and '\0' */
644 uint8_t mac_addr[18];
645
646 if (!data || !*data)
647 return -EINVAL;
648
649 in_ptr = *data;
650
651 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
652 /* no argument after the command */
653 if (NULL == in_ptr)
654 goto error;
655 /* no space after the command */
656 else if (SPACE_ASCII_VALUE != *in_ptr)
657 goto error;
658
659 /* remove empty spaces */
660 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
661 in_ptr++;
662
663 /* no argument followed by spaces */
664 if ('\0' == *in_ptr)
665 goto error;
666
667 v = sscanf(in_ptr, "%17s", mac_addr);
668 if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) {
669 hddLog(LOGE,
670 FL(
671 "Invalid MAC address or All hex inputs are not read (%d)"
672 ),
673 v);
674 goto error;
675 }
676
677 bssid[0] = hex_to_bin(mac_addr[0]) << 4 |
678 hex_to_bin(mac_addr[1]);
679 bssid[1] = hex_to_bin(mac_addr[3]) << 4 |
680 hex_to_bin(mac_addr[4]);
681 bssid[2] = hex_to_bin(mac_addr[6]) << 4 |
682 hex_to_bin(mac_addr[7]);
683 bssid[3] = hex_to_bin(mac_addr[9]) << 4 |
684 hex_to_bin(mac_addr[10]);
685 bssid[4] = hex_to_bin(mac_addr[12]) << 4 |
686 hex_to_bin(mac_addr[13]);
687 bssid[5] = hex_to_bin(mac_addr[15]) << 4 |
688 hex_to_bin(mac_addr[16]);
689
690 /* point to the next argument */
691 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
692 /* no argument after the command */
693 if (NULL == in_ptr)
694 goto error;
695
696 /* remove empty spaces */
697 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
698 in_ptr++;
699
700 /* no argument followed by spaces */
701 if ('\0' == *in_ptr)
702 goto error;
703
704 /* get the next argument ie the channel number */
705 v = sscanf(in_ptr, "%31s ", temp_buf);
706 if (1 != v)
707 goto error;
708
709 v = kstrtos32(temp_buf, 10, &temp_int);
710 if ((v < 0) || (temp_int < 0) ||
711 (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX))
712 return -EINVAL;
713
714 *channel = temp_int;
715 *data = in_ptr;
716 return 0;
717error:
718 *data = in_ptr;
719 return -EINVAL;
720}
721
722/**
723 * hdd_parse_send_action_frame_data() - HDD Parse send action frame data
724 * @pValue: Pointer to input data
725 * @pTargetApBssid: Pointer to target Ap bssid
726 * @pChannel: Pointer to the Target AP channel
727 * @pDwellTime: Pointer to the time to stay off-channel
728 * after transmitting action frame
729 * @pBuf: Pointer to data
730 * @pBufLen: Pointer to data length
731 *
732 * This function parses the send action frame data passed in the format
733 * SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
734 *
735 * Return: 0 for success non-zero for failure
736 */
737static int
738hdd_parse_send_action_frame_v1_data(const uint8_t *pValue,
739 uint8_t *pTargetApBssid,
740 uint8_t *pChannel, uint8_t *pDwellTime,
741 uint8_t **pBuf, uint8_t *pBufLen)
742{
743 const uint8_t *inPtr = pValue;
744 const uint8_t *dataEnd;
745 int tempInt;
746 int j = 0;
747 int i = 0;
748 int v = 0;
749 uint8_t tempBuf[32];
750 uint8_t tempByte = 0;
751
752 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
753 return -EINVAL;
754
755 /* point to the next argument */
756 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
757 /* no argument after the command */
758 if (NULL == inPtr)
759 return -EINVAL;
760 /* removing empty spaces */
761 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
762 inPtr++;
763
764 /* no argument followed by spaces */
765 if ('\0' == *inPtr) {
766 return -EINVAL;
767 }
768
769 /* getting the next argument ie the dwell time */
770 v = sscanf(inPtr, "%31s ", tempBuf);
771 if (1 != v)
772 return -EINVAL;
773
774 v = kstrtos32(tempBuf, 10, &tempInt);
775 if (v < 0 || tempInt < 0)
776 return -EINVAL;
777
778 *pDwellTime = tempInt;
779
780 /* point to the next argument */
781 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
782 /* no argument after the command */
783 if (NULL == inPtr)
784 return -EINVAL;
785 /* removing empty spaces */
786 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
787 inPtr++;
788
789 /* no argument followed by spaces */
790 if ('\0' == *inPtr) {
791 return -EINVAL;
792 }
793
794 /* find the length of data */
795 dataEnd = inPtr;
796 while (('\0' != *dataEnd)) {
797 dataEnd++;
798 }
799 *pBufLen = dataEnd - inPtr;
800 if (*pBufLen <= 0)
801 return -EINVAL;
802
803 /*
804 * Allocate the number of bytes based on the number of input characters
805 * whether it is even or odd.
806 * if the number of input characters are even, then we need N/2 byte.
807 * if the number of input characters are odd, then we need do (N+1)/2
808 * to compensate rounding off.
809 * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
810 * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes
811 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530812 *pBuf = qdf_mem_malloc((*pBufLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800813 if (NULL == *pBuf) {
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530814 hddLog(LOGE, FL("qdf_mem_malloc failed"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800815 return -ENOMEM;
816 }
817
818 /* the buffer received from the upper layer is character buffer,
819 * we need to prepare the buffer taking 2 characters in to a U8 hex
820 * decimal number for example 7f0000f0...form a buffer to contain 7f
821 * in 0th location, 00 in 1st and f0 in 3rd location
822 */
823 for (i = 0, j = 0; j < *pBufLen; j += 2) {
824 if (j + 1 == *pBufLen) {
825 tempByte = hex_to_bin(inPtr[j]);
826 } else {
827 tempByte =
828 (hex_to_bin(inPtr[j]) << 4) |
829 (hex_to_bin(inPtr[j + 1]));
830 }
831 (*pBuf)[i++] = tempByte;
832 }
833 *pBufLen = i;
834 return 0;
835}
836
837/**
838 * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data
839 * @pValue: Pointer to input data (its a NULL terminated string)
840 * @pTargetApBssid: Pointer to target Ap bssid
841 * @pChannel: Pointer to the Target AP channel
842 *
843 * This function parses the reasoc command data passed in the format
844 * REASSOC<space><bssid><space><channel>
845 *
846 * Return: 0 for success non-zero for failure
847 */
848static int hdd_parse_reassoc_command_v1_data(const uint8_t *pValue,
849 uint8_t *pTargetApBssid,
850 uint8_t *pChannel)
851{
852 const uint8_t *inPtr = pValue;
853
854 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
855 return -EINVAL;
856
857 return 0;
858}
859
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800860/**
861 * hdd_reassoc() - perform a userspace-directed reassoc
862 * @adapter: Adapter upon which the command was received
863 * @bssid: BSSID with which to reassociate
864 * @channel: channel upon which to reassociate
865 *
866 * This function performs a userspace-directed reassoc operation
867 *
868 * Return: 0 for success non-zero for failure
869 */
870static int
871hdd_reassoc(hdd_adapter_t *adapter, const uint8_t *bssid,
872 const uint8_t channel)
873{
874 hdd_station_ctx_t *pHddStaCtx;
875 int ret = 0;
876
Krunal Sonibe766b02016-03-10 13:00:44 -0800877 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800878 hdd_warn("Unsupported in mode %s(%d)",
879 hdd_device_mode_to_string(adapter->device_mode),
880 adapter->device_mode);
881 return -EINVAL;
882 }
883
884 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
885
886 /* if not associated, no need to proceed with reassoc */
887 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530888 hddLog(QDF_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800889 ret = -EINVAL;
890 goto exit;
891 }
892
893 /*
894 * if the target bssid is same as currently associated AP,
895 * then no need to proceed with reassoc
896 */
897 if (!memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530898 QDF_MAC_ADDR_SIZE)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800899 hddLog(LOG1,
900 FL("Reassoc BSSID is same as currently associated AP bssid"));
901 ret = -EINVAL;
902 goto exit;
903 }
904
905 /* Check channel number is a valid channel number */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530906 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800907 wlan_hdd_validate_operation_channel(adapter, channel)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530908 hddLog(QDF_TRACE_LEVEL_ERROR, "%s: Invalid Channel %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800909 __func__, channel);
910 ret = -EINVAL;
911 goto exit;
912 }
913
914 /* Proceed with reassoc */
915 {
916 tCsrHandoffRequest handoffInfo;
917 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
918
919 handoffInfo.channel = channel;
920 handoffInfo.src = REASSOC;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530921 qdf_mem_copy(handoffInfo.bssid.bytes, bssid, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800922 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
923 &handoffInfo);
924 }
925exit:
926 return ret;
927}
928
929/**
930 * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command
931 * @adapter: Adapter upon which the command was received
932 * @command: ASCII text command that was received
933 *
934 * This function parses the v1 REASSOC command with the format
935 *
936 * REASSOC xx:xx:xx:xx:xx:xx CH
937 *
938 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
939 * BSSID and CH is the ASCII representation of the channel. For
940 * example
941 *
942 * REASSOC 00:0a:0b:11:22:33 48
943 *
944 * Return: 0 for success non-zero for failure
945 */
946static int hdd_parse_reassoc_v1(hdd_adapter_t *adapter, const char *command)
947{
948 uint8_t channel = 0;
949 tSirMacAddr bssid;
950 int ret;
951
952 ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel);
953 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530954 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800955 "%s: Failed to parse reassoc command data", __func__);
956 } else {
957 ret = hdd_reassoc(adapter, bssid, channel);
958 }
959 return ret;
960}
961
962/**
963 * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command
964 * @adapter: Adapter upon which the command was received
965 * @command: Command that was received, ASCII command
966 * followed by binary data
967 *
968 * This function parses the v2 REASSOC command with the format
969 *
970 * REASSOC <android_wifi_reassoc_params>
971 *
972 * Return: 0 for success non-zero for failure
973 */
974static int hdd_parse_reassoc_v2(hdd_adapter_t *adapter, const char *command)
975{
976 struct android_wifi_reassoc_params params;
977 tSirMacAddr bssid;
978 int ret;
979
980 /* The params are located after "REASSOC " */
981 memcpy(&params, command + 8, sizeof(params));
982
983 if (!mac_pton(params.bssid, (u8 *) &bssid)) {
984 hddLog(LOGE, "%s: MAC address parsing failed", __func__);
985 ret = -EINVAL;
986 } else {
987 ret = hdd_reassoc(adapter, bssid, params.channel);
988 }
989 return ret;
990}
991
992/**
993 * hdd_parse_reassoc() - parse the REASSOC command
994 * @adapter: Adapter upon which the command was received
995 * @command: Command that was received
996 *
997 * There are two different versions of the REASSOC command. Version 1
998 * of the command contains a parameter list that is ASCII characters
999 * whereas version 2 contains a combination of ASCII and binary
1000 * payload. Determine if a version 1 or a version 2 command is being
1001 * parsed by examining the parameters, and then dispatch the parser
1002 * that is appropriate for the command.
1003 *
1004 * Return: 0 for success non-zero for failure
1005 */
1006static int hdd_parse_reassoc(hdd_adapter_t *adapter, const char *command)
1007{
1008 int ret;
1009
1010 /* both versions start with "REASSOC "
1011 * v1 has a bssid and channel # as an ASCII string
1012 * REASSOC xx:xx:xx:xx:xx:xx CH
1013 * v2 has a C struct
1014 * REASSOC <binary c struct>
1015 *
1016 * The first field in the v2 struct is also the bssid in ASCII.
1017 * But in the case of a v2 message the BSSID is NUL-terminated.
1018 * Hence we can peek at that offset to see if this is V1 or V2
1019 * REASSOC xx:xx:xx:xx:xx:xx*
1020 * 1111111111222222
1021 * 01234567890123456789012345
1022 */
1023 if (command[25]) {
1024 ret = hdd_parse_reassoc_v1(adapter, command);
1025 } else {
1026 ret = hdd_parse_reassoc_v2(adapter, command);
1027 }
1028
1029 return ret;
1030}
1031
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001032/**
1033 * hdd_sendactionframe() - send a userspace-supplied action frame
1034 * @adapter: Adapter upon which the command was received
1035 * @bssid: BSSID target of the action frame
1036 * @channel: Channel upon which to send the frame
1037 * @dwell_time: Amount of time to dwell when the frame is sent
1038 * @payload_len:Length of the payload
1039 * @payload: Payload of the frame
1040 *
1041 * This function sends a userspace-supplied action frame
1042 *
1043 * Return: 0 for success non-zero for failure
1044 */
1045static int
1046hdd_sendactionframe(hdd_adapter_t *adapter, const uint8_t *bssid,
1047 const uint8_t channel, const uint8_t dwell_time,
1048 const uint8_t payload_len, const uint8_t *payload)
1049{
1050 struct ieee80211_channel chan;
1051 uint8_t frame_len;
1052 uint8_t *frame;
1053 struct ieee80211_hdr_3addr *hdr;
1054 u64 cookie;
1055 hdd_station_ctx_t *pHddStaCtx;
1056 hdd_context_t *hdd_ctx;
1057 int ret = 0;
1058 tpSirMacVendorSpecificFrameHdr pVendorSpecific =
1059 (tpSirMacVendorSpecificFrameHdr) payload;
1060#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1061 struct cfg80211_mgmt_tx_params params;
1062#endif
1063
Krunal Sonibe766b02016-03-10 13:00:44 -08001064 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001065 hdd_warn("Unsupported in mode %s(%d)",
1066 hdd_device_mode_to_string(adapter->device_mode),
1067 adapter->device_mode);
1068 return -EINVAL;
1069 }
1070
1071 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
1072 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1073
1074 /* if not associated, no need to send action frame */
1075 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301076 hddLog(QDF_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001077 ret = -EINVAL;
1078 goto exit;
1079 }
1080
1081 /*
1082 * if the target bssid is different from currently associated AP,
1083 * then no need to send action frame
1084 */
1085 if (memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301086 QDF_MAC_ADDR_SIZE)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001087 hddLog(LOG1, FL("STA is not associated to this AP"));
1088 ret = -EINVAL;
1089 goto exit;
1090 }
1091
1092 chan.center_freq = sme_chn_to_freq(channel);
1093 /* Check if it is specific action frame */
1094 if (pVendorSpecific->category ==
1095 SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) {
1096 static const uint8_t Oui[] = { 0x00, 0x00, 0xf0 };
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301097 if (!qdf_mem_cmp(pVendorSpecific->Oui, (void *)Oui, 3)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001098 /*
1099 * if the channel number is different from operating
1100 * channel then no need to send action frame
1101 */
1102 if (channel != 0) {
1103 if (channel !=
1104 pHddStaCtx->conn_info.operationChannel) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301105 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001106 "%s: channel(%d) is different from operating channel(%d)",
1107 __func__, channel,
1108 pHddStaCtx->conn_info.
1109 operationChannel);
1110 ret = -EINVAL;
1111 goto exit;
1112 }
1113 /*
1114 * If channel number is specified and same
1115 * as home channel, ensure that action frame
1116 * is sent immediately by cancelling
1117 * roaming scans. Otherwise large dwell times
1118 * may cause long delays in sending action
1119 * frames.
1120 */
1121 sme_abort_roam_scan(hdd_ctx->hHal,
1122 adapter->sessionId);
1123 } else {
1124 /*
1125 * 0 is accepted as current home channel,
1126 * delayed transmission of action frame is ok.
1127 */
1128 chan.center_freq =
1129 sme_chn_to_freq(pHddStaCtx->conn_info.
1130 operationChannel);
1131 }
1132 }
1133 }
1134 if (chan.center_freq == 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301135 hddLog(QDF_TRACE_LEVEL_ERROR, "%s:invalid channel number %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001136 __func__, channel);
1137 ret = -EINVAL;
1138 goto exit;
1139 }
1140
1141 frame_len = payload_len + 24;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301142 frame = qdf_mem_malloc(frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001143 if (!frame) {
1144 hddLog(LOGE, FL("memory allocation failed"));
1145 ret = -ENOMEM;
1146 goto exit;
1147 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301148 qdf_mem_zero(frame, frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001149
1150 hdr = (struct ieee80211_hdr_3addr *)frame;
1151 hdr->frame_control =
1152 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301153 qdf_mem_copy(hdr->addr1, bssid, QDF_MAC_ADDR_SIZE);
1154 qdf_mem_copy(hdr->addr2, adapter->macAddressCurrent.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301155 QDF_MAC_ADDR_SIZE);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301156 qdf_mem_copy(hdr->addr3, bssid, QDF_MAC_ADDR_SIZE);
1157 qdf_mem_copy(hdr + 1, payload, payload_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001158
1159#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1160 params.chan = &chan;
1161 params.offchan = 0;
1162 params.wait = dwell_time;
1163 params.buf = frame;
1164 params.len = frame_len;
1165 params.no_cck = 1;
1166 params.dont_wait_for_ack = 1;
1167 ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, &params, &cookie);
1168#else
1169 ret = wlan_hdd_mgmt_tx(NULL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001170 &(adapter->wdev),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001171 &chan, 0,
Amar Singhal01098f72015-10-08 11:55:32 -07001172
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001173 dwell_time, frame, frame_len, 1, 1, &cookie);
1174#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
1175
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301176 qdf_mem_free(frame);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001177exit:
1178 return ret;
1179}
1180
1181/**
1182 * hdd_parse_sendactionframe_v1() - parse version 1 of the
1183 * SENDACTIONFRAME command
1184 * @adapter: Adapter upon which the command was received
1185 * @command: ASCII text command that was received
1186 *
1187 * This function parses the v1 SENDACTIONFRAME command with the format
1188 *
1189 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx
1190 *
1191 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
1192 * BSSID, CH is the ASCII representation of the channel, DW is the
1193 * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII
1194 * payload. For example
1195 *
1196 * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee
1197 *
1198 * Return: 0 for success non-zero for failure
1199 */
1200static int
1201hdd_parse_sendactionframe_v1(hdd_adapter_t *adapter, const char *command)
1202{
1203 uint8_t channel = 0;
1204 uint8_t dwell_time = 0;
1205 uint8_t payload_len = 0;
1206 uint8_t *payload = NULL;
1207 tSirMacAddr bssid;
1208 int ret;
1209
1210 ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel,
1211 &dwell_time, &payload,
1212 &payload_len);
1213 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301214 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001215 "%s: Failed to parse send action frame data", __func__);
1216 } else {
1217 ret = hdd_sendactionframe(adapter, bssid, channel,
1218 dwell_time, payload_len, payload);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301219 qdf_mem_free(payload);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001220 }
1221
1222 return ret;
1223}
1224
1225/**
1226 * hdd_parse_sendactionframe_v2() - parse version 2 of the
1227 * SENDACTIONFRAME command
1228 * @adapter: Adapter upon which the command was received
1229 * @command: Command that was received, ASCII command
1230 * followed by binary data
1231 *
1232 * This function parses the v2 SENDACTIONFRAME command with the format
1233 *
1234 * SENDACTIONFRAME <android_wifi_af_params>
1235 *
1236 * Return: 0 for success non-zero for failure
1237 */
1238static int
1239hdd_parse_sendactionframe_v2(hdd_adapter_t *adapter, const char *command)
1240{
1241 struct android_wifi_af_params *params;
1242 tSirMacAddr bssid;
1243 int ret;
1244
1245 /* params are large so keep off the stack */
1246 params = kmalloc(sizeof(*params), GFP_KERNEL);
1247 if (!params)
1248 return -ENOMEM;
1249
1250 /* The params are located after "SENDACTIONFRAME " */
1251 memcpy(params, command + 16, sizeof(*params));
1252
1253 if (!mac_pton(params->bssid, (u8 *) &bssid)) {
1254 hddLog(LOGE, "%s: MAC address parsing failed", __func__);
1255 ret = -EINVAL;
1256 } else {
1257 ret = hdd_sendactionframe(adapter, bssid, params->channel,
1258 params->dwell_time, params->len,
1259 params->data);
1260 }
1261 kfree(params);
1262 return ret;
1263}
1264
1265/**
1266 * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command
1267 * @adapter: Adapter upon which the command was received
1268 * @command: Command that was received
1269 *
1270 * There are two different versions of the SENDACTIONFRAME command.
1271 * Version 1 of the command contains a parameter list that is ASCII
1272 * characters whereas version 2 contains a combination of ASCII and
1273 * binary payload. Determine if a version 1 or a version 2 command is
1274 * being parsed by examining the parameters, and then dispatch the
1275 * parser that is appropriate for the version of the command.
1276 *
1277 * Return: 0 for success non-zero for failure
1278 */
1279static int
1280hdd_parse_sendactionframe(hdd_adapter_t *adapter, const char *command)
1281{
1282 int ret;
1283
1284 /*
1285 * both versions start with "SENDACTIONFRAME "
1286 * v1 has a bssid and other parameters as an ASCII string
1287 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME
1288 * v2 has a C struct
1289 * SENDACTIONFRAME <binary c struct>
1290 *
1291 * The first field in the v2 struct is also the bssid in ASCII.
1292 * But in the case of a v2 message the BSSID is NUL-terminated.
1293 * Hence we can peek at that offset to see if this is V1 or V2
1294 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx*
1295 * 111111111122222222223333
1296 * 0123456789012345678901234567890123
1297 */
1298 if (command[33]) {
1299 ret = hdd_parse_sendactionframe_v1(adapter, command);
1300 } else {
1301 ret = hdd_parse_sendactionframe_v2(adapter, command);
1302 }
1303
1304 return ret;
1305}
1306
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001307/**
1308 * hdd_parse_channellist() - HDD Parse channel list
1309 * @pValue: Pointer to input channel list
1310 * @ChannelList: Pointer to local output array to record
1311 * channel list
1312 * @pNumChannels: Pointer to number of roam scan channels
1313 *
1314 * This function parses the channel list passed in the format
1315 * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space>Channel 2<space>Channel N
1316 * if the Number of channels (N) does not match with the actual number
1317 * of channels passed then take the minimum of N and count of
1318 * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48,
1319 * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48,
1320 * ignore 5 and take 36, 40, 44 and 48. This function does not take care of
1321 * removing duplicate channels from the list
1322 *
1323 * Return: 0 for success non-zero for failure
1324 */
1325static int
1326hdd_parse_channellist(const uint8_t *pValue, uint8_t *pChannelList,
1327 uint8_t *pNumChannels)
1328{
1329 const uint8_t *inPtr = pValue;
1330 int tempInt;
1331 int j = 0;
1332 int v = 0;
1333 char buf[32];
1334
1335 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1336 /* no argument after the command */
1337 if (NULL == inPtr) {
1338 return -EINVAL;
1339 }
1340
1341 /* no space after the command */
1342 else if (SPACE_ASCII_VALUE != *inPtr) {
1343 return -EINVAL;
1344 }
1345
1346 /* remove empty spaces */
1347 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1348 inPtr++;
1349
1350 /* no argument followed by spaces */
1351 if ('\0' == *inPtr) {
1352 return -EINVAL;
1353 }
1354
1355 /* get the first argument ie the number of channels */
1356 v = sscanf(inPtr, "%31s ", buf);
1357 if (1 != v)
1358 return -EINVAL;
1359
1360 v = kstrtos32(buf, 10, &tempInt);
1361 if ((v < 0) ||
1362 (tempInt <= 0) || (tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN)) {
1363 return -EINVAL;
1364 }
1365
1366 *pNumChannels = tempInt;
1367
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301368 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001369 "Number of channels are: %d", *pNumChannels);
1370
1371 for (j = 0; j < (*pNumChannels); j++) {
1372 /*
1373 * inPtr pointing to the beginning of first space after number
1374 * of channels
1375 */
1376 inPtr = strpbrk(inPtr, " ");
1377 /* no channel list after the number of channels argument */
1378 if (NULL == inPtr) {
1379 if (0 != j) {
1380 *pNumChannels = j;
1381 return 0;
1382 } else {
1383 return -EINVAL;
1384 }
1385 }
1386
1387 /* remove empty space */
1388 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1389 inPtr++;
1390
1391 /*
1392 * no channel list after the number of channels
1393 * argument and spaces
1394 */
1395 if ('\0' == *inPtr) {
1396 if (0 != j) {
1397 *pNumChannels = j;
1398 return 0;
1399 } else {
1400 return -EINVAL;
1401 }
1402 }
1403
1404 v = sscanf(inPtr, "%31s ", buf);
1405 if (1 != v)
1406 return -EINVAL;
1407
1408 v = kstrtos32(buf, 10, &tempInt);
1409 if ((v < 0) ||
1410 (tempInt <= 0) ||
1411 (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
1412 return -EINVAL;
1413 }
1414 pChannelList[j] = tempInt;
1415
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301416 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001417 "Channel %d added to preferred channel list",
1418 pChannelList[j]);
1419 }
1420
1421 return 0;
1422}
1423
1424/**
1425 * hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the
1426 * SETROAMSCANCHANNELS command
1427 * @adapter: Adapter upon which the command was received
1428 * @command: ASCII text command that was received
1429 *
1430 * This function parses the v1 SETROAMSCANCHANNELS command with the format
1431 *
1432 * SETROAMSCANCHANNELS N C1 C2 ... Cn
1433 *
1434 * Where "N" is the ASCII representation of the number of channels and
1435 * C1 thru Cn is the ASCII representation of the channels. For example
1436 *
1437 * SETROAMSCANCHANNELS 4 36 40 44 48
1438 *
1439 * Return: 0 for success non-zero for failure
1440 */
1441static int
1442hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *adapter,
1443 const char *command)
1444{
1445 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1446 uint8_t num_chan = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301447 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001448 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1449 int ret;
1450
1451 ret = hdd_parse_channellist(command, channel_list, &num_chan);
1452 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301453 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001454 "%s: Failed to parse channel list information",
1455 __func__);
1456 goto exit;
1457 }
1458
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301459 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001460 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1461 adapter->sessionId, num_chan));
1462
1463 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301464 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001465 "%s: number of channels (%d) supported exceeded max (%d)",
1466 __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
1467 ret = -EINVAL;
1468 goto exit;
1469 }
1470
1471 status =
1472 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1473 adapter->sessionId,
1474 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301475 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301476 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001477 "%s: Failed to update channel list information",
1478 __func__);
1479 ret = -EINVAL;
1480 goto exit;
1481 }
1482exit:
1483 return ret;
1484}
1485
1486/**
1487 * hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the
1488 * SETROAMSCANCHANNELS command
1489 * @adapter: Adapter upon which the command was received
1490 * @command: Command that was received, ASCII command
1491 * followed by binary data
1492 *
1493 * This function parses the v2 SETROAMSCANCHANNELS command with the format
1494 *
1495 * SETROAMSCANCHANNELS [N][C1][C2][Cn]
1496 *
1497 * The command begins with SETROAMSCANCHANNELS followed by a space, but
1498 * what follows the space is an array of u08 parameters. For example
1499 *
1500 * SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30]
1501 *
1502 * Return: 0 for success non-zero for failure
1503 */
1504static int
1505hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *adapter,
1506 const char *command)
1507{
1508 const uint8_t *value;
1509 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1510 uint8_t channel;
1511 uint8_t num_chan;
1512 int i;
1513 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301514 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001515 int ret = 0;
1516
1517 /* array of values begins after "SETROAMSCANCHANNELS " */
1518 value = command + 20;
1519
1520 num_chan = *value++;
1521 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301522 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001523 "%s: number of channels (%d) supported exceeded max (%d)",
1524 __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
1525 ret = -EINVAL;
1526 goto exit;
1527 }
1528
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301529 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001530 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1531 adapter->sessionId, num_chan));
1532
1533 for (i = 0; i < num_chan; i++) {
1534 channel = *value++;
1535 if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301536 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001537 "%s: index %d invalid channel %d", __func__,
1538 i, channel);
1539 ret = -EINVAL;
1540 goto exit;
1541 }
1542 channel_list[i] = channel;
1543 }
1544 status =
1545 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1546 adapter->sessionId,
1547 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301548 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301549 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001550 "%s: Failed to update channel list information",
1551 __func__);
1552 ret = -EINVAL;
1553 goto exit;
1554 }
1555exit:
1556 return ret;
1557}
1558
1559/**
1560 * hdd_parse_set_roam_scan_channels() - parse the
1561 * SETROAMSCANCHANNELS command
1562 * @adapter: Adapter upon which the command was received
1563 * @command: Command that was received
1564 *
1565 * There are two different versions of the SETROAMSCANCHANNELS command.
1566 * Version 1 of the command contains a parameter list that is ASCII
1567 * characters whereas version 2 contains a binary payload. Determine
1568 * if a version 1 or a version 2 command is being parsed by examining
1569 * the parameters, and then dispatch the parser that is appropriate for
1570 * the command.
1571 *
1572 * Return: 0 for success non-zero for failure
1573 */
1574static int
1575hdd_parse_set_roam_scan_channels(hdd_adapter_t *adapter, const char *command)
1576{
1577 const char *cursor;
1578 char ch;
1579 bool v1;
1580 int ret;
1581
1582 /* start after "SETROAMSCANCHANNELS " */
1583 cursor = command + 20;
1584
1585 /* assume we have a version 1 command until proven otherwise */
1586 v1 = true;
1587
1588 /* v1 params will only contain ASCII digits and space */
1589 while ((ch = *cursor++) && v1) {
1590 if (!(isdigit(ch) || isspace(ch))) {
1591 v1 = false;
1592 }
1593 }
1594 if (v1) {
1595 ret = hdd_parse_set_roam_scan_channels_v1(adapter, command);
1596 } else {
1597 ret = hdd_parse_set_roam_scan_channels_v2(adapter, command);
1598 }
1599
1600 return ret;
1601}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001602
1603#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
1604/**
1605 * hdd_parse_plm_cmd() - HDD Parse Plm command
1606 * @pValue: Pointer to input data
1607 * @pPlmRequest:Pointer to output struct tpSirPlmReq
1608 *
1609 * This function parses the plm command passed in the format
1610 * CCXPLMREQ<space><enable><space><dialog_token><space>
1611 * <meas_token><space><num_of_bursts><space><burst_int><space>
1612 * <measu duration><space><burst_len><space><desired_tx_pwr>
1613 * <space><multcast_addr><space><number_of_channels>
1614 * <space><channel_numbers>
1615 *
1616 * Return: 0 for success non-zero for failure
1617 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301618QDF_STATUS hdd_parse_plm_cmd(uint8_t *pValue, tSirPlmReq *pPlmRequest)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001619{
1620 uint8_t *cmdPtr = NULL;
1621 int count, content = 0, ret = 0;
1622 char buf[32];
1623
1624 /* move to argument list */
1625 cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1626 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301627 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001628
1629 /* no space after the command */
1630 if (SPACE_ASCII_VALUE != *cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301631 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001632
1633 /* remove empty spaces */
1634 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1635 cmdPtr++;
1636
1637 /* START/STOP PLM req */
1638 ret = sscanf(cmdPtr, "%31s ", buf);
1639 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301640 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001641
1642 ret = kstrtos32(buf, 10, &content);
1643 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301644 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001645
1646 pPlmRequest->enable = content;
1647 cmdPtr = strpbrk(cmdPtr, " ");
1648
1649 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301650 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001651
1652 /* remove empty spaces */
1653 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1654 cmdPtr++;
1655
1656 /* Dialog token of radio meas req containing meas reqIE */
1657 ret = sscanf(cmdPtr, "%31s ", buf);
1658 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301659 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001660
1661 ret = kstrtos32(buf, 10, &content);
1662 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301663 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001664
1665 pPlmRequest->diag_token = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301666 hddLog(QDF_TRACE_LEVEL_DEBUG, "diag token %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001667 pPlmRequest->diag_token);
1668 cmdPtr = strpbrk(cmdPtr, " ");
1669
1670 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301671 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001672
1673 /* remove empty spaces */
1674 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1675 cmdPtr++;
1676
1677 /* measurement token of meas req IE */
1678 ret = sscanf(cmdPtr, "%31s ", buf);
1679 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301680 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001681
1682 ret = kstrtos32(buf, 10, &content);
1683 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301684 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001685
1686 pPlmRequest->meas_token = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301687 hddLog(QDF_TRACE_LEVEL_DEBUG, "meas token %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001688 pPlmRequest->meas_token);
1689
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301690 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001691 "PLM req %s", pPlmRequest->enable ? "START" : "STOP");
1692 if (pPlmRequest->enable) {
1693
1694 cmdPtr = strpbrk(cmdPtr, " ");
1695
1696 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301697 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001698
1699 /* remove empty spaces */
1700 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1701 cmdPtr++;
1702
1703 /* total number of bursts after which STA stops sending */
1704 ret = sscanf(cmdPtr, "%31s ", buf);
1705 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301706 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001707
1708 ret = kstrtos32(buf, 10, &content);
1709 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301710 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001711
1712 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301713 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001714
1715 pPlmRequest->numBursts = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301716 hddLog(QDF_TRACE_LEVEL_DEBUG, "num burst %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001717 pPlmRequest->numBursts);
1718 cmdPtr = strpbrk(cmdPtr, " ");
1719
1720 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301721 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001722
1723 /* remove empty spaces */
1724 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1725 cmdPtr++;
1726
1727 /* burst interval in seconds */
1728 ret = sscanf(cmdPtr, "%31s ", buf);
1729 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301730 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001731
1732 ret = kstrtos32(buf, 10, &content);
1733 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301734 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001735
1736 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301737 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001738
1739 pPlmRequest->burstInt = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301740 hddLog(QDF_TRACE_LEVEL_DEBUG, "burst Int %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001741 pPlmRequest->burstInt);
1742 cmdPtr = strpbrk(cmdPtr, " ");
1743
1744 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301745 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001746
1747 /* remove empty spaces */
1748 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1749 cmdPtr++;
1750
1751 /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */
1752 ret = sscanf(cmdPtr, "%31s ", buf);
1753 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301754 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001755
1756 ret = kstrtos32(buf, 10, &content);
1757 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301758 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001759
1760 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301761 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001762
1763 pPlmRequest->measDuration = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301764 hddLog(QDF_TRACE_LEVEL_DEBUG, "measDur %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001765 pPlmRequest->measDuration);
1766 cmdPtr = strpbrk(cmdPtr, " ");
1767
1768 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301769 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001770
1771 /* remove empty spaces */
1772 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1773 cmdPtr++;
1774
1775 /* burst length of PLM bursts */
1776 ret = sscanf(cmdPtr, "%31s ", buf);
1777 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301778 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001779
1780 ret = kstrtos32(buf, 10, &content);
1781 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301782 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001783
1784 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301785 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001786
1787 pPlmRequest->burstLen = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301788 hddLog(QDF_TRACE_LEVEL_DEBUG, "burstLen %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001789 pPlmRequest->burstLen);
1790 cmdPtr = strpbrk(cmdPtr, " ");
1791
1792 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301793 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001794
1795 /* remove empty spaces */
1796 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1797 cmdPtr++;
1798
1799 /* desired tx power for transmission of PLM bursts */
1800 ret = sscanf(cmdPtr, "%31s ", buf);
1801 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301802 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001803
1804 ret = kstrtos32(buf, 10, &content);
1805 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301806 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001807
1808 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301809 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001810
1811 pPlmRequest->desiredTxPwr = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301812 hddLog(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001813 "desiredTxPwr %d", pPlmRequest->desiredTxPwr);
1814
Anurag Chouhan6d760662016-02-20 16:05:43 +05301815 for (count = 0; count < QDF_MAC_ADDR_SIZE; count++) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001816 cmdPtr = strpbrk(cmdPtr, " ");
1817
1818 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301819 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001820
1821 /* remove empty spaces */
1822 while ((SPACE_ASCII_VALUE == *cmdPtr)
1823 && ('\0' != *cmdPtr))
1824 cmdPtr++;
1825
1826 ret = sscanf(cmdPtr, "%31s ", buf);
1827 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301828 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001829
1830 ret = kstrtos32(buf, 16, &content);
1831 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301832 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001833
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001834 pPlmRequest->mac_addr.bytes[count] = content;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001835 }
1836
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001837 hdd_debug("MC addr " MAC_ADDRESS_STR,
1838 MAC_ADDR_ARRAY(pPlmRequest->mac_addr.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001839
1840 cmdPtr = strpbrk(cmdPtr, " ");
1841
1842 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301843 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001844
1845 /* remove empty spaces */
1846 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1847 cmdPtr++;
1848
1849 /* number of channels */
1850 ret = sscanf(cmdPtr, "%31s ", buf);
1851 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301852 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001853
1854 ret = kstrtos32(buf, 10, &content);
1855 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301856 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001857
1858 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301859 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001860
1861 pPlmRequest->plmNumCh = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301862 hddLog(QDF_TRACE_LEVEL_DEBUG, "numch %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001863 pPlmRequest->plmNumCh);
1864
1865 /* Channel numbers */
1866 for (count = 0; count < pPlmRequest->plmNumCh; count++) {
1867 cmdPtr = strpbrk(cmdPtr, " ");
1868
1869 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301870 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001871
1872 /* remove empty spaces */
1873 while ((SPACE_ASCII_VALUE == *cmdPtr)
1874 && ('\0' != *cmdPtr))
1875 cmdPtr++;
1876
1877 ret = sscanf(cmdPtr, "%31s ", buf);
1878 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301879 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001880
1881 ret = kstrtos32(buf, 10, &content);
1882 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301883 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001884
1885 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301886 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001887
1888 pPlmRequest->plmChList[count] = content;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301889 hddLog(QDF_TRACE_LEVEL_DEBUG, " ch- %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001890 pPlmRequest->plmChList[count]);
1891 }
1892 }
1893 /* If PLM START */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301894 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001895}
1896#endif
1897
1898#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
1899static void wlan_hdd_ready_to_extwow(void *callbackContext, bool is_success)
1900{
1901 hdd_context_t *hdd_ctx = (hdd_context_t *) callbackContext;
1902 int rc;
1903
1904 rc = wlan_hdd_validate_context(hdd_ctx);
1905 if (0 != rc) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301906 hddLog(QDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001907 return;
1908 }
1909 hdd_ctx->ext_wow_should_suspend = is_success;
1910 complete(&hdd_ctx->ready_to_extwow);
1911}
1912
1913static int hdd_enable_ext_wow(hdd_adapter_t *adapter,
1914 tpSirExtWoWParams arg_params)
1915{
1916 tSirExtWoWParams params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301917 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001918 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1919 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1920 int rc;
1921
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301922 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001923
1924 INIT_COMPLETION(hdd_ctx->ready_to_extwow);
1925
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301926 qdf_ret_status = sme_configure_ext_wow(hHal, &params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001927 &wlan_hdd_ready_to_extwow,
1928 hdd_ctx);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301929 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301930 hddLog(QDF_TRACE_LEVEL_ERROR,
Krishna Kumaar Natarajand9131902015-10-19 11:52:47 -07001931 FL("sme_configure_ext_wow returned failure %d"),
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301932 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001933 return -EPERM;
1934 }
1935
1936 rc = wait_for_completion_timeout(&hdd_ctx->ready_to_extwow,
1937 msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_EXTWOW));
1938 if (!rc) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301939 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001940 "%s: Failed to get ready to extwow", __func__);
1941 return -EPERM;
1942 }
1943
1944 if (hdd_ctx->ext_wow_should_suspend) {
1945 if (hdd_ctx->config->extWowGotoSuspend) {
1946 pm_message_t state;
1947
1948 state.event = PM_EVENT_SUSPEND;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301949 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001950 "%s: Received ready to ExtWoW. Going to suspend",
1951 __func__);
1952
1953 rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL);
1954 if (rc < 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301955 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001956 "%s: wlan_hdd_cfg80211_suspend_wlan failed, error = %d",
1957 __func__, rc);
1958 return rc;
1959 }
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301960 qdf_ret_status = wlan_hdd_bus_suspend(state);
1961 if (qdf_ret_status != QDF_STATUS_SUCCESS) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301962 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001963 "%s: wlan_hdd_suspend failed, status = %d",
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301964 __func__, qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001965 wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy);
1966 return -EPERM;
1967 }
1968 }
1969 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301970 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001971 "%s: Received ready to ExtWoW failure", __func__);
1972 return -EPERM;
1973 }
1974
1975 return 0;
1976}
1977
1978static int hdd_enable_ext_wow_parser(hdd_adapter_t *adapter, int vdev_id,
1979 int value)
1980{
1981 tSirExtWoWParams params;
1982 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1983 int rc;
1984
1985 rc = wlan_hdd_validate_context(hdd_ctx);
1986 if (0 != rc) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301987 hddLog(QDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001988 return -EINVAL;
1989 }
1990
1991 if (value < EXT_WOW_TYPE_APP_TYPE1 ||
1992 value > EXT_WOW_TYPE_APP_TYPE1_2) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301993 hddLog(QDF_TRACE_LEVEL_ERROR, FL("Invalid type"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001994 return -EINVAL;
1995 }
1996
1997 if (value == EXT_WOW_TYPE_APP_TYPE1 &&
1998 hdd_ctx->is_extwow_app_type1_param_set)
1999 params.type = value;
2000 else if (value == EXT_WOW_TYPE_APP_TYPE2 &&
2001 hdd_ctx->is_extwow_app_type2_param_set)
2002 params.type = value;
2003 else if (value == EXT_WOW_TYPE_APP_TYPE1_2 &&
2004 hdd_ctx->is_extwow_app_type1_param_set &&
2005 hdd_ctx->is_extwow_app_type2_param_set)
2006 params.type = value;
2007 else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302008 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002009 FL("Set app params before enable it value %d"), value);
2010 return -EINVAL;
2011 }
2012
2013 params.vdev_id = vdev_id;
2014 params.wakeup_pin_num = hdd_ctx->config->extWowApp1WakeupPinNumber |
2015 (hdd_ctx->config->extWowApp2WakeupPinNumber
2016 << 8);
2017
2018 return hdd_enable_ext_wow(adapter, &params);
2019}
2020
2021static int hdd_set_app_type1_params(tHalHandle hHal,
2022 tpSirAppType1Params arg_params)
2023{
2024 tSirAppType1Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302025 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002026
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302027 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002028
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302029 qdf_ret_status = sme_configure_app_type1_params(hHal, &params);
2030 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302031 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002032 FL("sme_configure_app_type1_params returned failure %d"),
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302033 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002034 return -EPERM;
2035 }
2036
2037 return 0;
2038}
2039
2040static int hdd_set_app_type1_parser(hdd_adapter_t *adapter,
2041 char *arg, int len)
2042{
2043 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2044 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2045 char id[20], password[20];
2046 tSirAppType1Params params;
Srinivas Girigowda04209912015-11-24 12:11:13 -08002047 int rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002048
2049 rc = wlan_hdd_validate_context(hdd_ctx);
2050 if (0 != rc) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302051 hddLog(QDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002052 return -EINVAL;
2053 }
2054
2055 if (2 != sscanf(arg, "%8s %16s", id, password)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302056 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002057 FL("Invalid Number of arguments"));
2058 return -EINVAL;
2059 }
2060
2061 memset(&params, 0, sizeof(tSirAppType1Params));
2062 params.vdev_id = adapter->sessionId;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302063 qdf_copy_macaddr(&params.wakee_mac_addr, &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002064
2065 params.id_length = strlen(id);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302066 qdf_mem_copy(params.identification_id, id, params.id_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002067 params.pass_length = strlen(password);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302068 qdf_mem_copy(params.password, password, params.pass_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002069
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302070 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002071 "%s: %d %pM %.8s %u %.16s %u",
Srinivas Girigowda04209912015-11-24 12:11:13 -08002072 __func__, params.vdev_id, params.wakee_mac_addr.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002073 params.identification_id, params.id_length,
2074 params.password, params.pass_length);
2075
2076 return hdd_set_app_type1_params(hHal, &params);
2077}
2078
2079static int hdd_set_app_type2_params(tHalHandle hHal,
2080 tpSirAppType2Params arg_params)
2081{
2082 tSirAppType2Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302083 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002084
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302085 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002086
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302087 qdf_ret_status = sme_configure_app_type2_params(hHal, &params);
2088 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302089 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002090 FL("sme_configure_app_type2_params returned failure %d"),
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302091 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002092 return -EPERM;
2093 }
2094
2095 return 0;
2096}
2097
2098static int hdd_set_app_type2_parser(hdd_adapter_t *adapter,
2099 char *arg, int len)
2100{
2101 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2102 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2103 char mac_addr[20], rc4_key[20];
Anurag Chouhan6d760662016-02-20 16:05:43 +05302104 unsigned int gateway_mac[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002105 tSirAppType2Params params;
2106 int ret;
2107
2108 ret = wlan_hdd_validate_context(hdd_ctx);
2109 if (0 != ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302110 hddLog(QDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002111 return -EINVAL;
2112 }
2113
2114 memset(&params, 0, sizeof(tSirAppType2Params));
2115
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302116 ret = sscanf(arg, "%17s %16s %x %x %x %u %u %hu %hu %u %u %u %u %u %u",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002117 mac_addr, rc4_key, (unsigned int *)&params.ip_id,
2118 (unsigned int *)&params.ip_device_ip,
2119 (unsigned int *)&params.ip_server_ip,
2120 (unsigned int *)&params.tcp_seq,
2121 (unsigned int *)&params.tcp_ack_seq,
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302122 (uint16_t *)&params.tcp_src_port,
2123 (uint16_t *)&params.tcp_dst_port,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002124 (unsigned int *)&params.keepalive_init,
2125 (unsigned int *)&params.keepalive_min,
2126 (unsigned int *)&params.keepalive_max,
2127 (unsigned int *)&params.keepalive_inc,
2128 (unsigned int *)&params.tcp_tx_timeout_val,
2129 (unsigned int *)&params.tcp_rx_timeout_val);
2130
2131 if (ret != 15 && ret != 7) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302132 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002133 "Invalid Number of arguments");
2134 return -EINVAL;
2135 }
2136
2137 if (6 !=
2138 sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0],
2139 &gateway_mac[1], &gateway_mac[2], &gateway_mac[3],
2140 &gateway_mac[4], &gateway_mac[5])) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302141 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002142 "Invalid MacAddress Input %s", mac_addr);
2143 return -EINVAL;
2144 }
2145
2146 if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT ||
2147 params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302148 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002149 "Invalid TCP Port Number");
2150 return -EINVAL;
2151 }
2152
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302153 qdf_mem_copy(&params.gateway_mac.bytes, (uint8_t *) &gateway_mac,
Anurag Chouhan6d760662016-02-20 16:05:43 +05302154 QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002155
2156 params.rc4_key_len = strlen(rc4_key);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302157 qdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002158
2159 params.vdev_id = adapter->sessionId;
2160 params.tcp_src_port = (params.tcp_src_port != 0) ?
2161 params.tcp_src_port : hdd_ctx->config->extWowApp2TcpSrcPort;
2162 params.tcp_dst_port = (params.tcp_dst_port != 0) ?
2163 params.tcp_dst_port : hdd_ctx->config->extWowApp2TcpDstPort;
2164 params.keepalive_init = (params.keepalive_init != 0) ?
2165 params.keepalive_init : hdd_ctx->config->
2166 extWowApp2KAInitPingInterval;
2167 params.keepalive_min =
2168 (params.keepalive_min != 0) ?
2169 params.keepalive_min :
2170 hdd_ctx->config->extWowApp2KAMinPingInterval;
2171 params.keepalive_max =
2172 (params.keepalive_max != 0) ?
2173 params.keepalive_max :
2174 hdd_ctx->config->extWowApp2KAMaxPingInterval;
2175 params.keepalive_inc =
2176 (params.keepalive_inc != 0) ?
2177 params.keepalive_inc :
2178 hdd_ctx->config->extWowApp2KAIncPingInterval;
2179 params.tcp_tx_timeout_val =
2180 (params.tcp_tx_timeout_val != 0) ?
2181 params.tcp_tx_timeout_val :
2182 hdd_ctx->config->extWowApp2TcpTxTimeout;
2183 params.tcp_rx_timeout_val =
2184 (params.tcp_rx_timeout_val != 0) ?
2185 params.tcp_rx_timeout_val :
2186 hdd_ctx->config->extWowApp2TcpRxTimeout;
2187
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302188 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002189 "%s: %pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u",
2190 __func__, gateway_mac, rc4_key, params.ip_id,
2191 params.ip_device_ip, params.ip_server_ip, params.tcp_seq,
2192 params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port,
2193 params.keepalive_init, params.keepalive_min,
2194 params.keepalive_max, params.keepalive_inc,
2195 params.tcp_tx_timeout_val, params.tcp_rx_timeout_val);
2196
2197 return hdd_set_app_type2_params(hHal, &params);
2198}
2199#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
2200
2201/**
2202 * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
2203 * @pValue: Pointer to MAXTXPOWER command
2204 * @pDbm: Pointer to tx power
2205 *
2206 * This function parses the MAXTXPOWER command passed in the format
2207 * MAXTXPOWER<space>X(Tx power in dbm)
2208 *
2209 * For example input commands:
2210 * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm
2211 * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm
2212 *
2213 * Return: 0 for success non-zero for failure
2214 */
2215static int hdd_parse_setmaxtxpower_command(uint8_t *pValue, int *pTxPower)
2216{
2217 uint8_t *inPtr = pValue;
2218 int tempInt;
2219 int v = 0;
2220 *pTxPower = 0;
2221
2222 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2223 /* no argument after the command */
2224 if (NULL == inPtr) {
2225 return -EINVAL;
2226 }
2227
2228 /* no space after the command */
2229 else if (SPACE_ASCII_VALUE != *inPtr) {
2230 return -EINVAL;
2231 }
2232
2233 /* remove empty spaces */
2234 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2235 inPtr++;
2236
2237 /* no argument followed by spaces */
2238 if ('\0' == *inPtr) {
2239 return 0;
2240 }
2241
2242 v = kstrtos32(inPtr, 10, &tempInt);
2243
2244 /* Range checking for passed parameter */
2245 if ((tempInt < HDD_MIN_TX_POWER) || (tempInt > HDD_MAX_TX_POWER)) {
2246 return -EINVAL;
2247 }
2248
2249 *pTxPower = tempInt;
2250
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302251 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002252 "SETMAXTXPOWER: %d", *pTxPower);
2253
2254 return 0;
2255} /* End of hdd_parse_setmaxtxpower_command */
2256
2257static int hdd_get_dwell_time(struct hdd_config *pCfg, uint8_t *command,
2258 char *extra, uint8_t n, uint8_t *len)
2259{
2260 int ret = 0;
2261
2262 if (!pCfg || !command || !extra || !len) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302263 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002264 "%s: argument passed for GETDWELLTIME is incorrect",
2265 __func__);
2266 ret = -EINVAL;
2267 return ret;
2268 }
2269
2270 if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) {
2271 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n",
2272 (int)pCfg->nActiveMaxChnTime);
2273 return ret;
2274 } else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0) {
2275 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n",
2276 (int)pCfg->nActiveMinChnTime);
2277 return ret;
2278 } else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) {
2279 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n",
2280 (int)pCfg->nPassiveMaxChnTime);
2281 return ret;
2282 } else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0) {
2283 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n",
2284 (int)pCfg->nPassiveMinChnTime);
2285 return ret;
2286 } else if (strncmp(command, "GETDWELLTIME", 12) == 0) {
2287 *len = scnprintf(extra, n, "GETDWELLTIME %u \n",
2288 (int)pCfg->nActiveMaxChnTime);
2289 return ret;
2290 } else {
2291 ret = -EINVAL;
2292 }
2293
2294 return ret;
2295}
2296
2297static int hdd_set_dwell_time(hdd_adapter_t *adapter, uint8_t *command)
2298{
2299 tHalHandle hHal;
2300 struct hdd_config *pCfg;
2301 uint8_t *value = command;
2302 tSmeConfigParams smeConfig;
2303 int val = 0, temp = 0;
2304
2305 pCfg = (WLAN_HDD_GET_CTX(adapter))->config;
2306 hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2307 if (!pCfg || !hHal) {
2308 hddLog(LOGE,
2309 FL("argument passed for SETDWELLTIME is incorrect"));
2310 return -EINVAL;
2311 }
2312
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302313 qdf_mem_zero(&smeConfig, sizeof(smeConfig));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002314 sme_get_config_param(hHal, &smeConfig);
2315
2316 if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) {
2317 value = value + 24;
2318 temp = kstrtou32(value, 10, &val);
2319 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2320 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
2321 hddLog(LOGE,
2322 FL("argument passed for SETDWELLTIME ACTIVE MAX is incorrect"));
2323 return -EFAULT;
2324 }
2325 pCfg->nActiveMaxChnTime = val;
2326 smeConfig.csrConfig.nActiveMaxChnTime = val;
2327 sme_update_config(hHal, &smeConfig);
2328 } else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0) {
2329 value = value + 24;
2330 temp = kstrtou32(value, 10, &val);
2331 if (temp != 0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN ||
2332 val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX) {
2333 hddLog(LOGE,
2334 FL("argument passed for SETDWELLTIME ACTIVE MIN is incorrect"));
2335 return -EFAULT;
2336 }
2337 pCfg->nActiveMinChnTime = val;
2338 smeConfig.csrConfig.nActiveMinChnTime = val;
2339 sme_update_config(hHal, &smeConfig);
2340 } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) {
2341 value = value + 25;
2342 temp = kstrtou32(value, 10, &val);
2343 if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN ||
2344 val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX) {
2345 hddLog(LOGE,
2346 FL("argument passed for SETDWELLTIME PASSIVE MAX is incorrect"));
2347 return -EFAULT;
2348 }
2349 pCfg->nPassiveMaxChnTime = val;
2350 smeConfig.csrConfig.nPassiveMaxChnTime = val;
2351 sme_update_config(hHal, &smeConfig);
2352 } else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0) {
2353 value = value + 25;
2354 temp = kstrtou32(value, 10, &val);
2355 if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN ||
2356 val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX) {
2357 hddLog(LOGE,
2358 FL("argument passed for SETDWELLTIME PASSIVE MIN is incorrect"));
2359 return -EFAULT;
2360 }
2361 pCfg->nPassiveMinChnTime = val;
2362 smeConfig.csrConfig.nPassiveMinChnTime = val;
2363 sme_update_config(hHal, &smeConfig);
2364 } else if (strncmp(command, "SETDWELLTIME", 12) == 0) {
2365 value = value + 13;
2366 temp = kstrtou32(value, 10, &val);
2367 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2368 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
2369 hddLog(LOGE,
2370 FL("argument passed for SETDWELLTIME is incorrect"));
2371 return -EFAULT;
2372 }
2373 pCfg->nActiveMaxChnTime = val;
2374 smeConfig.csrConfig.nActiveMaxChnTime = val;
2375 sme_update_config(hHal, &smeConfig);
2376 } else {
2377 return -EINVAL;
2378 }
2379
2380 return 0;
2381}
2382
2383static void hdd_get_link_status_cb(uint8_t status, void *context)
2384{
2385 struct statsContext *pLinkContext;
2386 hdd_adapter_t *adapter;
2387
2388 if (NULL == context) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302389 hddLog(QDF_TRACE_LEVEL_ERROR, "%s: Bad context [%p]",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002390 __func__, context);
2391 return;
2392 }
2393
2394 pLinkContext = context;
2395 adapter = pLinkContext->pAdapter;
2396
2397 spin_lock(&hdd_context_lock);
2398
2399 if ((NULL == adapter) ||
2400 (LINK_STATUS_MAGIC != pLinkContext->magic)) {
2401 /*
2402 * the caller presumably timed out so there is
2403 * nothing we can do
2404 */
2405 spin_unlock(&hdd_context_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302406 hddLog(QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002407 "%s: Invalid context, adapter [%p] magic [%08x]",
2408 __func__, adapter, pLinkContext->magic);
2409 return;
2410 }
2411
2412 /* context is valid so caller is still waiting */
2413
2414 /* paranoia: invalidate the magic */
2415 pLinkContext->magic = 0;
2416
2417 /* copy over the status */
2418 adapter->linkStatus = status;
2419
2420 /* notify the caller */
2421 complete(&pLinkContext->completion);
2422
2423 /* serialization is complete */
2424 spin_unlock(&hdd_context_lock);
2425}
2426
2427/**
2428 * wlan_hdd_get_link_status() - get link status
2429 * @pAdapter: pointer to the adapter
2430 *
2431 * This function sends a request to query the link status and waits
2432 * on a timer to invoke the callback. if the callback is invoked then
2433 * latest link status shall be returned or otherwise cached value
2434 * will be returned.
2435 *
2436 * Return: On success, link status shall be returned.
2437 * On error or not associated, link status 0 will be returned.
2438 */
2439static int wlan_hdd_get_link_status(hdd_adapter_t *adapter)
2440{
2441
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002442 hdd_station_ctx_t *pHddStaCtx =
2443 WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2444 struct statsContext context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302445 QDF_STATUS hstatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002446 unsigned long rc;
2447
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002448 if (cds_is_driver_recovering()) {
2449 hdd_warn("Recovery in Progress. State: 0x%x Ignore!!!",
2450 cds_get_driver_state());
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002451 return 0;
2452 }
2453
Krunal Sonibe766b02016-03-10 13:00:44 -08002454 if ((QDF_STA_MODE != adapter->device_mode) &&
2455 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002456 hdd_warn("Unsupported in mode %s(%d)",
2457 hdd_device_mode_to_string(adapter->device_mode),
2458 adapter->device_mode);
2459 return 0;
2460 }
2461
2462 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2463 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
2464 /* If not associated, then expected link status return
2465 * value is 0
2466 */
2467 hddLog(LOG1, FL("Not associated!"));
2468 return 0;
2469 }
2470
2471 init_completion(&context.completion);
2472 context.pAdapter = adapter;
2473 context.magic = LINK_STATUS_MAGIC;
2474 hstatus = sme_get_link_status(WLAN_HDD_GET_HAL_CTX(adapter),
2475 hdd_get_link_status_cb,
2476 &context, adapter->sessionId);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302477 if (QDF_STATUS_SUCCESS != hstatus) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302478 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002479 "%s: Unable to retrieve link status", __func__);
2480 /* return a cached value */
2481 } else {
2482 /* request is sent -- wait for the response */
2483 rc = wait_for_completion_timeout(&context.completion,
2484 msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
2485 if (!rc)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302486 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002487 FL("SME timed out while retrieving link status"));
2488 }
2489
2490 spin_lock(&hdd_context_lock);
2491 context.magic = 0;
2492 spin_unlock(&hdd_context_lock);
2493
2494 /* either callback updated adapter stats or it has cached data */
2495 return adapter->linkStatus;
2496}
2497
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002498static void hdd_tx_fail_ind_callback(uint8_t *MacAddr, uint8_t seqNo)
2499{
2500 int payload_len;
2501 struct sk_buff *skb;
2502 struct nlmsghdr *nlh;
2503 uint8_t *data;
2504
2505 payload_len = ETH_ALEN;
2506
2507 if (0 == cesium_pid) {
2508 hddLog(QDF_TRACE_LEVEL_ERROR,
2509 "%s: cesium process not registered", __func__);
2510 return;
2511 }
2512
2513 skb = nlmsg_new(payload_len, GFP_ATOMIC);
2514 if (skb == NULL) {
2515 hddLog(LOGE,
2516 FL("nlmsg_new() failed for msg size[%d]"),
2517 NLMSG_SPACE(payload_len));
2518 return;
2519 }
2520
2521 nlh = nlmsg_put(skb, cesium_pid, seqNo, 0, payload_len, NLM_F_REQUEST);
2522
2523 if (NULL == nlh) {
2524 hddLog(QDF_TRACE_LEVEL_ERROR,
2525 "%s: nlmsg_put() failed for msg size[%d]",
2526 __func__, NLMSG_SPACE(payload_len));
2527
2528 kfree_skb(skb);
2529 return;
2530 }
2531
2532 data = nlmsg_data(nlh);
2533 memcpy(data, MacAddr, ETH_ALEN);
2534
2535 if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0) {
2536 hddLog(QDF_TRACE_LEVEL_ERROR,
2537 "%s: nlmsg_unicast() failed for msg size[%d]",
2538 __func__, NLMSG_SPACE(payload_len));
2539 }
2540
2541 return;
2542}
2543
2544
2545/**
2546 * hdd_ParseuserParams - return a pointer to the next argument
2547 * @pValue: Input argument string
2548 * @ppArg: Output pointer to the next argument
2549 *
2550 * This function parses argument stream and finds the pointer
2551 * to the next argument
2552 *
2553 * Return: 0 if the next argument found; -EINVAL otherwise
2554 */
2555static int hdd_parse_user_params(uint8_t *pValue, uint8_t **ppArg)
2556{
2557 uint8_t *pVal;
2558
2559 pVal = strnchr(pValue, strlen(pValue), ' ');
2560
2561 if (NULL == pVal) {
2562 /* no argument remains */
2563 return -EINVAL;
2564 } else if (SPACE_ASCII_VALUE != *pVal) {
2565 /* no space after the current argument */
2566 return -EINVAL;
2567 }
2568
2569 pVal++;
2570
2571 /* remove empty spaces */
2572 while ((SPACE_ASCII_VALUE == *pVal) && ('\0' != *pVal)) {
2573 pVal++;
2574 }
2575
2576 /* no argument followed by spaces */
2577 if ('\0' == *pVal) {
2578 return -EINVAL;
2579 }
2580
2581 *ppArg = pVal;
2582
2583 return 0;
2584}
2585
2586/**
2587 * hdd_parse_ibsstx_fail_event_params - Parse params
2588 * for SETIBSSTXFAILEVENT
2589 * @pValue: Input ibss tx fail event argument
2590 * @tx_fail_count: (Output parameter) Tx fail counter
2591 * @pid: (Output parameter) PID
2592 *
2593 * Return: 0 if the parsing succeeds; -EINVAL otherwise
2594 */
2595static int hdd_parse_ibsstx_fail_event_params(uint8_t *pValue,
2596 uint8_t *tx_fail_count,
2597 uint16_t *pid)
2598{
2599 uint8_t *param = NULL;
2600 int ret;
2601
2602 ret = hdd_parse_user_params(pValue, &param);
2603
2604 if (0 == ret && NULL != param) {
2605 if (1 != sscanf(param, "%hhu", tx_fail_count)) {
2606 ret = -EINVAL;
2607 goto done;
2608 }
2609 } else {
2610 goto done;
2611 }
2612
2613 if (0 == *tx_fail_count) {
2614 *pid = 0;
2615 goto done;
2616 }
2617
2618 pValue = param;
2619 pValue++;
2620
2621 ret = hdd_parse_user_params(pValue, &param);
2622
2623 if (0 == ret) {
2624 if (1 != sscanf(param, "%hu", pid)) {
2625 ret = -EINVAL;
2626 goto done;
2627 }
2628 } else {
2629 goto done;
2630 }
2631
2632done:
2633 return ret;
2634}
2635
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002636#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
2637/**
2638 * hdd_parse_ese_beacon_req() - Parse ese beacon request
2639 * @pValue: Pointer to data
2640 * @pEseBcnReq: Output pointer to store parsed ie information
2641 *
2642 * This function parses the ese beacon request passed in the format
2643 * CCXBEACONREQ<space><Number of fields><space><Measurement token>
2644 * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N
2645 * <space>Scan Mode N<space>Meas Duration N
2646 *
2647 * If the Number of bcn req fields (N) does not match with the
2648 * actual number of fields passed then take N.
2649 * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated
2650 * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40.
2651 * This function does not take care of removing duplicate channels from the
2652 * list
2653 *
2654 * Return: 0 for success non-zero for failure
2655 */
2656static int hdd_parse_ese_beacon_req(uint8_t *pValue,
2657 tCsrEseBeaconReq *pEseBcnReq)
2658{
2659 uint8_t *inPtr = pValue;
2660 int tempInt = 0;
2661 int j = 0, i = 0, v = 0;
2662 char buf[32];
2663
2664 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2665 /* no argument after the command */
2666 if (NULL == inPtr) {
2667 return -EINVAL;
2668 }
2669 /* no space after the command */
2670 else if (SPACE_ASCII_VALUE != *inPtr) {
2671 return -EINVAL;
2672 }
2673
2674 /* remove empty spaces */
2675 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2676 inPtr++;
2677
2678 /* no argument followed by spaces */
2679 if ('\0' == *inPtr)
2680 return -EINVAL;
2681
2682 /* get the first argument ie measurement token */
2683 v = sscanf(inPtr, "%31s ", buf);
2684 if (1 != v)
2685 return -EINVAL;
2686
2687 v = kstrtos32(buf, 10, &tempInt);
2688 if (v < 0)
2689 return -EINVAL;
2690
2691 pEseBcnReq->numBcnReqIe = tempInt;
2692
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302693 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002694 "Number of Bcn Req Ie fields(%d)", pEseBcnReq->numBcnReqIe);
2695
2696 for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++) {
2697 for (i = 0; i < 4; i++) {
2698 /*
2699 * inPtr pointing to the beginning of 1st space
2700 * after number of ie fields
2701 */
2702 inPtr = strpbrk(inPtr, " ");
2703 /* no ie data after the number of ie fields argument */
2704 if (NULL == inPtr)
2705 return -EINVAL;
2706
2707 /* remove empty space */
2708 while ((SPACE_ASCII_VALUE == *inPtr)
2709 && ('\0' != *inPtr))
2710 inPtr++;
2711
2712 /*
2713 * no ie data after the number of ie fields
2714 * argument and spaces
2715 */
2716 if ('\0' == *inPtr)
2717 return -EINVAL;
2718
2719 v = sscanf(inPtr, "%31s ", buf);
2720 if (1 != v)
2721 return -EINVAL;
2722
2723 v = kstrtos32(buf, 10, &tempInt);
2724 if (v < 0)
2725 return -EINVAL;
2726
2727 switch (i) {
2728 case 0: /* Measurement token */
2729 if (tempInt <= 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302730 QDF_TRACE(QDF_MODULE_ID_HDD,
2731 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002732 "Invalid Measurement Token(%d)",
2733 tempInt);
2734 return -EINVAL;
2735 }
2736 pEseBcnReq->bcnReq[j].measurementToken =
2737 tempInt;
2738 break;
2739
2740 case 1: /* Channel number */
2741 if ((tempInt <= 0) ||
2742 (tempInt >
2743 WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302744 QDF_TRACE(QDF_MODULE_ID_HDD,
2745 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002746 "Invalid Channel Number(%d)",
2747 tempInt);
2748 return -EINVAL;
2749 }
2750 pEseBcnReq->bcnReq[j].channel = tempInt;
2751 break;
2752
2753 case 2: /* Scan mode */
2754 if ((tempInt < eSIR_PASSIVE_SCAN)
2755 || (tempInt > eSIR_BEACON_TABLE)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302756 QDF_TRACE(QDF_MODULE_ID_HDD,
2757 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002758 "Invalid Scan Mode(%d) Expected{0|1|2}",
2759 tempInt);
2760 return -EINVAL;
2761 }
2762 pEseBcnReq->bcnReq[j].scanMode = tempInt;
2763 break;
2764
2765 case 3: /* Measurement duration */
2766 if (((tempInt <= 0)
2767 && (pEseBcnReq->bcnReq[j].scanMode !=
2768 eSIR_BEACON_TABLE)) ||
2769 ((tempInt < 0) &&
2770 (pEseBcnReq->bcnReq[j].scanMode ==
2771 eSIR_BEACON_TABLE))) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302772 QDF_TRACE(QDF_MODULE_ID_HDD,
2773 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002774 "Invalid Measurement Duration(%d)",
2775 tempInt);
2776 return -EINVAL;
2777 }
2778 pEseBcnReq->bcnReq[j].measurementDuration =
2779 tempInt;
2780 break;
2781 }
2782 }
2783 }
2784
2785 for (j = 0; j < pEseBcnReq->numBcnReqIe; j++) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302786 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002787 "Index(%d) Measurement Token(%u) Channel(%u) Scan Mode(%u) Measurement Duration(%u)",
2788 j,
2789 pEseBcnReq->bcnReq[j].measurementToken,
2790 pEseBcnReq->bcnReq[j].channel,
2791 pEseBcnReq->bcnReq[j].scanMode,
2792 pEseBcnReq->bcnReq[j].measurementDuration);
2793 }
2794
2795 return 0;
2796}
2797#endif /* defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) */
2798
2799#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
2800/**
2801 * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
2802 * @pValue: Pointer to input data
2803 * @pCckmIe: Pointer to output cckm Ie
2804 * @pCckmIeLen: Pointer to output cckm ie length
2805 *
2806 * This function parses the SETCCKM IE command
2807 * SETCCKMIE<space><ie data>
2808 *
2809 * Return: 0 for success non-zero for failure
2810 */
2811static int hdd_parse_get_cckm_ie(uint8_t *pValue, uint8_t **pCckmIe,
2812 uint8_t *pCckmIeLen)
2813{
2814 uint8_t *inPtr = pValue;
2815 uint8_t *dataEnd;
2816 int j = 0;
2817 int i = 0;
2818 uint8_t tempByte = 0;
2819 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2820 /* no argument after the command */
2821 if (NULL == inPtr) {
2822 return -EINVAL;
2823 }
2824 /* no space after the command */
2825 else if (SPACE_ASCII_VALUE != *inPtr) {
2826 return -EINVAL;
2827 }
2828 /* remove empty spaces */
2829 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2830 inPtr++;
2831 /* no argument followed by spaces */
2832 if ('\0' == *inPtr) {
2833 return -EINVAL;
2834 }
2835 /* find the length of data */
2836 dataEnd = inPtr;
2837 while (('\0' != *dataEnd)) {
2838 dataEnd++;
2839 ++(*pCckmIeLen);
2840 }
2841 if (*pCckmIeLen <= 0)
2842 return -EINVAL;
2843 /*
2844 * Allocate the number of bytes based on the number of input characters
2845 * whether it is even or odd.
2846 * if the number of input characters are even, then we need N / 2 byte.
2847 * if the number of input characters are odd, then we need do
2848 * (N + 1) / 2 to compensate rounding off.
2849 * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough.
2850 * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes
2851 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302852 *pCckmIe = qdf_mem_malloc((*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002853 if (NULL == *pCckmIe) {
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302854 hddLog(LOGE, FL("qdf_mem_malloc failed"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002855 return -ENOMEM;
2856 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302857 qdf_mem_zero(*pCckmIe, (*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002858 /*
2859 * the buffer received from the upper layer is character buffer,
2860 * we need to prepare the buffer taking 2 characters in to a U8 hex
2861 * decimal number for example 7f0000f0...form a buffer to contain
2862 * 7f in 0th location, 00 in 1st and f0 in 3rd location
2863 */
2864 for (i = 0, j = 0; j < *pCckmIeLen; j += 2) {
2865 tempByte = (hex_to_bin(inPtr[j]) << 4) |
2866 (hex_to_bin(inPtr[j + 1]));
2867 (*pCckmIe)[i++] = tempByte;
2868 }
2869 *pCckmIeLen = i;
2870 return 0;
2871}
2872#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
2873
2874int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate)
2875{
2876 tSirRateUpdateInd rateUpdate = {0};
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302877 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002878 hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
2879 struct hdd_config *pConfig = NULL;
2880
2881 if (pHddCtx == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302882 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002883 "%s: HDD context is null", __func__);
2884 return -EINVAL;
2885 }
Krunal Sonibe766b02016-03-10 13:00:44 -08002886 if ((QDF_IBSS_MODE != pAdapter->device_mode) &&
2887 (QDF_SAP_MODE != pAdapter->device_mode) &&
2888 (QDF_STA_MODE != pAdapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002889 hddLog(LOGE,
2890 FL("Received SETMCRATE cmd in invalid mode %s(%d)"),
2891 hdd_device_mode_to_string(pAdapter->device_mode),
2892 pAdapter->device_mode);
2893 hddLog(LOGE,
2894 FL("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode"));
2895 return -EINVAL;
2896 }
2897 pConfig = pHddCtx->config;
2898 rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
2899 rateUpdate.dev_mode = pAdapter->device_mode;
2900 rateUpdate.mcastDataRate24GHz = targetRate;
2901 rateUpdate.mcastDataRate24GHzTxFlag = 1;
2902 rateUpdate.mcastDataRate5GHz = targetRate;
2903 rateUpdate.bcastDataRate = -1;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302904 qdf_copy_macaddr(&rateUpdate.bssid, &pAdapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002905 hddLog(LOG1,
2906 FL("MC Target rate %d, mac = %pM, dev_mode %s(%d)"),
Srinivas Girigowdaafede182015-11-18 22:36:12 -08002907 rateUpdate.mcastDataRate24GHz, rateUpdate.bssid.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002908 hdd_device_mode_to_string(pAdapter->device_mode),
2909 pAdapter->device_mode);
2910 status = sme_send_rate_update_ind(pHddCtx->hHal, &rateUpdate);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302911 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302912 hddLog(QDF_TRACE_LEVEL_ERROR, "%s: SETMCRATE failed",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002913 __func__);
2914 return -EFAULT;
2915 }
2916 return 0;
2917}
2918
2919static int drv_cmd_p2p_dev_addr(hdd_adapter_t *adapter,
2920 hdd_context_t *hdd_ctx,
2921 uint8_t *command,
2922 uint8_t command_len,
2923 hdd_priv_data_t *priv_data)
2924{
2925 int ret = 0;
2926
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302927 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002928 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
2929 adapter->sessionId,
2930 (unsigned)(*(hdd_ctx->p2pDeviceAddress.bytes + 2)
2931 << 24 | *(hdd_ctx->p2pDeviceAddress.bytes
2932 + 3) << 16 | *(hdd_ctx->
2933 p2pDeviceAddress.bytes + 4) << 8 |
2934 *(hdd_ctx->p2pDeviceAddress.bytes +
2935 5))));
2936
2937 if (copy_to_user(priv_data->buf, hdd_ctx->p2pDeviceAddress.bytes,
2938 sizeof(tSirMacAddr))) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302939 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002940 "%s: failed to copy data to user buffer",
2941 __func__);
2942 ret = -EFAULT;
2943 }
2944
2945 return ret;
2946}
2947
2948/**
2949 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command
2950 * @adapter: Adapter on which the command was received
2951 * @hdd_ctx: HDD global context
2952 * @command: Entire driver command received from userspace
2953 * @command_len: Length of @command
2954 * @priv_data: Pointer to ioctl private data structure
2955 *
2956 * This is a trivial command hander function which simply forwards the
2957 * command to the actual command processor within the P2P module.
2958 *
2959 * Return: 0 on success, non-zero on failure
2960 */
2961static int drv_cmd_p2p_set_noa(hdd_adapter_t *adapter,
2962 hdd_context_t *hdd_ctx,
2963 uint8_t *command,
2964 uint8_t command_len,
2965 hdd_priv_data_t *priv_data)
2966{
2967 return hdd_set_p2p_noa(adapter->dev, command);
2968}
2969
2970/**
2971 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command
2972 * @adapter: Adapter on which the command was received
2973 * @hdd_ctx: HDD global context
2974 * @command: Entire driver command received from userspace
2975 * @command_len: Length of @command
2976 * @priv_data: Pointer to ioctl private data structure
2977 *
2978 * This is a trivial command hander function which simply forwards the
2979 * command to the actual command processor within the P2P module.
2980 *
2981 * Return: 0 on success, non-zero on failure
2982 */
2983static int drv_cmd_p2p_set_ps(hdd_adapter_t *adapter,
2984 hdd_context_t *hdd_ctx,
2985 uint8_t *command,
2986 uint8_t command_len,
2987 hdd_priv_data_t *priv_data)
2988{
2989 return hdd_set_p2p_opps(adapter->dev, command);
2990}
2991
2992static int drv_cmd_set_band(hdd_adapter_t *adapter,
2993 hdd_context_t *hdd_ctx,
2994 uint8_t *command,
2995 uint8_t command_len,
2996 hdd_priv_data_t *priv_data)
2997{
2998 int ret = 0;
2999
3000 uint8_t *ptr = command;
3001
3002 /* Change band request received */
3003
3004 /*
3005 * First 8 bytes will have "SETBAND " and
3006 * 9 byte will have band setting value
3007 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303008 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003009 "%s: SetBandCommand Info comm %s UL %d, TL %d",
3010 __func__, command, priv_data->used_len,
3011 priv_data->total_len);
3012
3013 /* Change band request received */
3014 ret = hdd_set_band_helper(adapter->dev, ptr);
3015
3016 return ret;
3017}
3018
3019static int drv_cmd_set_wmmps(hdd_adapter_t *adapter,
3020 hdd_context_t *hdd_ctx,
3021 uint8_t *command,
3022 uint8_t command_len,
3023 hdd_priv_data_t *priv_data)
3024{
3025 return hdd_wmmps_helper(adapter, command);
3026}
3027
3028static int drv_cmd_country(hdd_adapter_t *adapter,
3029 hdd_context_t *hdd_ctx,
3030 uint8_t *command,
3031 uint8_t command_len,
3032 hdd_priv_data_t *priv_data)
3033{
3034 int ret = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303035 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003036 unsigned long rc;
3037 char *country_code;
3038
3039 country_code = command + 8;
3040
3041 INIT_COMPLETION(adapter->change_country_code);
3042
3043 status = sme_change_country_code(hdd_ctx->hHal,
3044 wlan_hdd_change_country_code_callback,
3045 country_code,
3046 adapter,
3047 hdd_ctx->pcds_context,
3048 eSIR_TRUE,
3049 eSIR_TRUE);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303050 if (status == QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003051 rc = wait_for_completion_timeout(
3052 &adapter->change_country_code,
3053 msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
3054 if (!rc)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303055 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003056 "%s: SME while setting country code timed out",
3057 __func__);
3058 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303059 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003060 "%s: SME Change Country code fail, status=%d",
3061 __func__, status);
3062 ret = -EINVAL;
3063 }
3064
3065 return ret;
3066}
3067
3068static int drv_cmd_set_roam_trigger(hdd_adapter_t *adapter,
3069 hdd_context_t *hdd_ctx,
3070 uint8_t *command,
3071 uint8_t command_len,
3072 hdd_priv_data_t *priv_data)
3073{
3074 int ret = 0;
3075 uint8_t *value = command;
3076 int8_t rssi = 0;
3077 uint8_t lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303078 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003079
3080 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
3081 value = value + command_len + 1;
3082
3083 /* Convert the value from ascii to integer */
3084 ret = kstrtos8(value, 10, &rssi);
3085 if (ret < 0) {
3086 /*
3087 * If the input value is greater than max value of datatype,
3088 * then also kstrtou8 fails
3089 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303090 QDF_TRACE(QDF_MODULE_ID_HDD,
3091 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003092 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
3093 __func__,
3094 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
3095 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
3096 ret = -EINVAL;
3097 goto exit;
3098 }
3099
3100 lookUpThreshold = abs(rssi);
3101
3102 if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN)
3103 || (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303104 QDF_TRACE(QDF_MODULE_ID_HDD,
3105 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003106 "Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)",
3107 lookUpThreshold,
3108 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
3109 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
3110 ret = -EINVAL;
3111 goto exit;
3112 }
3113
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303114 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003115 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
3116 adapter->sessionId, lookUpThreshold));
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303117 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003118 "%s: Received Command to Set Roam trigger (Neighbor lookup threshold) = %d",
3119 __func__,
3120 lookUpThreshold);
3121
3122 hdd_ctx->config->nNeighborLookupRssiThreshold = lookUpThreshold;
3123 status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->hHal,
3124 adapter->sessionId,
3125 lookUpThreshold);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303126 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303127 QDF_TRACE(QDF_MODULE_ID_HDD,
3128 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003129 "%s: Failed to set roam trigger, try again",
3130 __func__);
3131 ret = -EPERM;
3132 goto exit;
3133 }
3134
3135exit:
3136 return ret;
3137}
3138
3139static int drv_cmd_get_roam_trigger(hdd_adapter_t *adapter,
3140 hdd_context_t *hdd_ctx,
3141 uint8_t *command,
3142 uint8_t command_len,
3143 hdd_priv_data_t *priv_data)
3144{
3145 int ret = 0;
3146 uint8_t lookUpThreshold =
3147 sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->hHal);
3148 int rssi = (-1) * lookUpThreshold;
3149 char extra[32];
3150 uint8_t len = 0;
3151
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303152 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003153 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
3154 adapter->sessionId, lookUpThreshold));
3155
3156 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303157 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003158 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303159 QDF_TRACE(QDF_MODULE_ID_HDD,
3160 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003161 "%s: failed to copy data to user buffer",
3162 __func__);
3163 ret = -EFAULT;
3164 }
3165
3166 return ret;
3167}
3168
3169static int drv_cmd_set_roam_scan_period(hdd_adapter_t *adapter,
3170 hdd_context_t *hdd_ctx,
3171 uint8_t *command,
3172 uint8_t command_len,
3173 hdd_priv_data_t *priv_data)
3174{
3175 int ret = 0;
3176 uint8_t *value = command;
3177 uint8_t roamScanPeriod = 0;
3178 uint16_t neighborEmptyScanRefreshPeriod =
3179 CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
3180
3181 /* input refresh period is in terms of seconds */
3182
3183 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
3184 value = value + command_len + 1;
3185
3186 /* Convert the value from ascii to integer */
3187 ret = kstrtou8(value, 10, &roamScanPeriod);
3188 if (ret < 0) {
3189 /*
3190 * If the input value is greater than max value of datatype,
3191 * then also kstrtou8 fails
3192 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303193 QDF_TRACE(QDF_MODULE_ID_HDD,
3194 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003195 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
3196 __func__,
3197 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3198 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
3199 ret = -EINVAL;
3200 goto exit;
3201 }
3202
3203 if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000))
3204 || (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000))) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303205 QDF_TRACE(QDF_MODULE_ID_HDD,
3206 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003207 "Roam scan period value %d is out of range (Min: %d Max: %d)",
3208 roamScanPeriod,
3209 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3210 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
3211 ret = -EINVAL;
3212 goto exit;
3213 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303214 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003215 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
3216 adapter->sessionId, roamScanPeriod));
3217 neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
3218
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303219 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003220 "%s: Received Command to Set roam scan period (Empty Scan refresh period) = %d",
3221 __func__,
3222 roamScanPeriod);
3223
3224 hdd_ctx->config->nEmptyScanRefreshPeriod =
3225 neighborEmptyScanRefreshPeriod;
3226 sme_update_empty_scan_refresh_period(hdd_ctx->hHal,
3227 adapter->sessionId,
3228 neighborEmptyScanRefreshPeriod);
3229
3230exit:
3231 return ret;
3232}
3233
3234static int drv_cmd_get_roam_scan_period(hdd_adapter_t *adapter,
3235 hdd_context_t *hdd_ctx,
3236 uint8_t *command,
3237 uint8_t command_len,
3238 hdd_priv_data_t *priv_data)
3239{
3240 int ret = 0;
3241 uint16_t nEmptyScanRefreshPeriod =
3242 sme_get_empty_scan_refresh_period(hdd_ctx->hHal);
3243 char extra[32];
3244 uint8_t len = 0;
3245
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303246 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003247 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
3248 adapter->sessionId,
3249 nEmptyScanRefreshPeriod));
3250 len = scnprintf(extra, sizeof(extra), "%s %d",
3251 "GETROAMSCANPERIOD",
3252 (nEmptyScanRefreshPeriod / 1000));
3253 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303254 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003255 if (copy_to_user(priv_data->buf, &extra, len)) {
3256 hddLog(LOGE,
3257 FL("failed to copy data to user buffer"));
3258 ret = -EFAULT;
3259 }
3260
3261 return ret;
3262}
3263
3264static int drv_cmd_set_roam_scan_refresh_period(hdd_adapter_t *adapter,
3265 hdd_context_t *hdd_ctx,
3266 uint8_t *command,
3267 uint8_t command_len,
3268 hdd_priv_data_t *priv_data)
3269{
3270 int ret = 0;
3271 uint8_t *value = command;
3272 uint8_t roamScanRefreshPeriod = 0;
3273 uint16_t neighborScanRefreshPeriod =
3274 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
3275
3276 /* input refresh period is in terms of seconds */
3277 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
3278 value = value + command_len + 1;
3279
3280 /* Convert the value from ascii to integer */
3281 ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
3282 if (ret < 0) {
3283 /*
3284 * If the input value is greater than max value of datatype,
3285 * then also kstrtou8 fails
3286 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303287 QDF_TRACE(QDF_MODULE_ID_HDD,
3288 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003289 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
3290 __func__,
3291 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000,
3292 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000);
3293 ret = -EINVAL;
3294 goto exit;
3295 }
3296
3297 if ((roamScanRefreshPeriod <
3298 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000))
3299 || (roamScanRefreshPeriod >
3300 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000))) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303301 QDF_TRACE(QDF_MODULE_ID_HDD,
3302 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003303 "Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)",
3304 roamScanRefreshPeriod,
3305 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN
3306 / 1000),
3307 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX
3308 / 1000));
3309 ret = -EINVAL;
3310 goto exit;
3311 }
3312 neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
3313
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303314 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003315 "%s: Received Command to Set roam scan refresh period (Scan refresh period) = %d",
3316 __func__,
3317 roamScanRefreshPeriod);
3318
3319 hdd_ctx->config->nNeighborResultsRefreshPeriod =
3320 neighborScanRefreshPeriod;
3321 sme_set_neighbor_scan_refresh_period(hdd_ctx->hHal,
3322 adapter->sessionId,
3323 neighborScanRefreshPeriod);
3324
3325exit:
3326 return ret;
3327}
3328
3329static int drv_cmd_get_roam_scan_refresh_period(hdd_adapter_t *adapter,
3330 hdd_context_t *hdd_ctx,
3331 uint8_t *command,
3332 uint8_t command_len,
3333 hdd_priv_data_t *priv_data)
3334{
3335 int ret = 0;
3336 uint16_t value =
3337 sme_get_neighbor_scan_refresh_period(hdd_ctx->hHal);
3338 char extra[32];
3339 uint8_t len = 0;
3340
3341 len = scnprintf(extra, sizeof(extra), "%s %d",
3342 "GETROAMSCANREFRESHPERIOD",
3343 (value / 1000));
3344 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303345 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003346 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303347 QDF_TRACE(QDF_MODULE_ID_HDD,
3348 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003349 "%s: failed to copy data to user buffer",
3350 __func__);
3351 ret = -EFAULT;
3352 }
3353
3354 return ret;
3355}
3356
3357static int drv_cmd_set_roam_mode(hdd_adapter_t *adapter,
3358 hdd_context_t *hdd_ctx,
3359 uint8_t *command,
3360 uint8_t command_len,
3361 hdd_priv_data_t *priv_data)
3362{
3363 int ret = 0;
3364 uint8_t *value = command;
3365 uint8_t roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
3366
3367 /* Move pointer to ahead of SETROAMMODE<delimiter> */
3368 value = value + SIZE_OF_SETROAMMODE + 1;
3369
3370 /* Convert the value from ascii to integer */
3371 ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
3372 if (ret < 0) {
3373 /*
3374 * If the input value is greater than max value of datatype,
3375 * then also kstrtou8 fails
3376 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303377 QDF_TRACE(QDF_MODULE_ID_HDD,
3378 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003379 "%s: kstrtou8 failed range [%d - %d]",
3380 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
3381 CFG_LFR_FEATURE_ENABLED_MAX);
3382 ret = -EINVAL;
3383 goto exit;
3384 }
3385 if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
3386 (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303387 QDF_TRACE(QDF_MODULE_ID_HDD,
3388 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003389 "Roam Mode value %d is out of range (Min: %d Max: %d)",
3390 roamMode,
3391 CFG_LFR_FEATURE_ENABLED_MIN,
3392 CFG_LFR_FEATURE_ENABLED_MAX);
3393 ret = -EINVAL;
3394 goto exit;
3395 }
3396
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303397 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003398 "%s: Received Command to Set Roam Mode = %d",
3399 __func__, roamMode);
3400 /*
3401 * Note that
3402 * SETROAMMODE 0 is to enable LFR while
3403 * SETROAMMODE 1 is to disable LFR, but
3404 * notify_is_fast_roam_ini_feature_enabled 0/1 is to
3405 * enable/disable. So, we have to invert the value
3406 * to call sme_update_is_fast_roam_ini_feature_enabled.
3407 */
3408 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3409 roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
3410 else
3411 roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
3412
3413 hdd_ctx->config->isFastRoamIniFeatureEnabled = roamMode;
3414 if (roamMode) {
3415 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3416 sme_update_roam_scan_offload_enabled(
3417 (tHalHandle)(hdd_ctx->hHal),
3418 hdd_ctx->config->isRoamOffloadScanEnabled);
3419 sme_update_is_fast_roam_ini_feature_enabled(
3420 hdd_ctx->hHal,
3421 adapter->sessionId,
3422 roamMode);
3423 } else {
3424 sme_update_is_fast_roam_ini_feature_enabled(
3425 hdd_ctx->hHal,
3426 adapter->sessionId,
3427 roamMode);
3428 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3429 sme_update_roam_scan_offload_enabled(
3430 (tHalHandle)(hdd_ctx->hHal),
3431 hdd_ctx->config->isRoamOffloadScanEnabled);
3432 }
3433
3434
3435exit:
3436 return ret;
3437}
3438
3439static int drv_cmd_get_roam_mode(hdd_adapter_t *adapter,
3440 hdd_context_t *hdd_ctx,
3441 uint8_t *command,
3442 uint8_t command_len,
3443 hdd_priv_data_t *priv_data)
3444{
3445 int ret = 0;
3446 bool roamMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3447 char extra[32];
3448 uint8_t len = 0;
3449
3450 /*
3451 * roamMode value shall be inverted because the sementics is different.
3452 */
3453 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3454 roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
3455 else
3456 roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
3457
3458 len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303459 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003460 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303461 QDF_TRACE(QDF_MODULE_ID_HDD,
3462 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003463 "%s: failed to copy data to user buffer",
3464 __func__);
3465 ret = -EFAULT;
3466 }
3467
3468 return ret;
3469}
3470
3471static int drv_cmd_set_roam_delta(hdd_adapter_t *adapter,
3472 hdd_context_t *hdd_ctx,
3473 uint8_t *command,
3474 uint8_t command_len,
3475 hdd_priv_data_t *priv_data)
3476{
3477 int ret = 0;
3478 uint8_t *value = command;
3479 uint8_t roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
3480
3481 /* Move pointer to ahead of SETROAMDELTA<delimiter> */
3482 value = value + command_len + 1;
3483
3484 /* Convert the value from ascii to integer */
3485 ret = kstrtou8(value, 10, &roamRssiDiff);
3486 if (ret < 0) {
3487 /*
3488 * If the input value is greater than max value of datatype,
3489 * then also kstrtou8 fails
3490 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303491 QDF_TRACE(QDF_MODULE_ID_HDD,
3492 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003493 "%s: kstrtou8 failed range [%d - %d]",
3494 __func__, CFG_ROAM_RSSI_DIFF_MIN,
3495 CFG_ROAM_RSSI_DIFF_MAX);
3496 ret = -EINVAL;
3497 goto exit;
3498 }
3499
3500 if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
3501 (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303502 QDF_TRACE(QDF_MODULE_ID_HDD,
3503 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003504 "Roam rssi diff value %d is out of range (Min: %d Max: %d)",
3505 roamRssiDiff,
3506 CFG_ROAM_RSSI_DIFF_MIN,
3507 CFG_ROAM_RSSI_DIFF_MAX);
3508 ret = -EINVAL;
3509 goto exit;
3510 }
3511
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303512 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003513 "%s: Received Command to Set roam rssi diff = %d",
3514 __func__, roamRssiDiff);
3515
3516 hdd_ctx->config->RoamRssiDiff = roamRssiDiff;
3517 sme_update_roam_rssi_diff(hdd_ctx->hHal,
3518 adapter->sessionId,
3519 roamRssiDiff);
3520
3521exit:
3522 return ret;
3523}
3524
3525static int drv_cmd_get_roam_delta(hdd_adapter_t *adapter,
3526 hdd_context_t *hdd_ctx,
3527 uint8_t *command,
3528 uint8_t command_len,
3529 hdd_priv_data_t *priv_data)
3530{
3531 int ret = 0;
3532 uint8_t roamRssiDiff =
3533 sme_get_roam_rssi_diff(hdd_ctx->hHal);
3534 char extra[32];
3535 uint8_t len = 0;
3536
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303537 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003538 TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
3539 adapter->sessionId, roamRssiDiff));
3540
3541 len = scnprintf(extra, sizeof(extra), "%s %d",
3542 command, roamRssiDiff);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303543 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003544
3545 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303546 QDF_TRACE(QDF_MODULE_ID_HDD,
3547 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003548 "%s: failed to copy data to user buffer",
3549 __func__);
3550 ret = -EFAULT;
3551 }
3552
3553 return ret;
3554}
3555
3556static int drv_cmd_get_band(hdd_adapter_t *adapter,
3557 hdd_context_t *hdd_ctx,
3558 uint8_t *command,
3559 uint8_t command_len,
3560 hdd_priv_data_t *priv_data)
3561{
3562 int ret = 0;
3563 int band = -1;
3564 char extra[32];
3565 uint8_t len = 0;
3566
3567 hdd_get_band_helper(hdd_ctx, &band);
3568
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303569 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003570 TRACE_CODE_HDD_GETBAND_IOCTL,
3571 adapter->sessionId, band));
3572
3573 len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303574 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003575
3576 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303577 QDF_TRACE(QDF_MODULE_ID_HDD,
3578 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003579 "%s: failed to copy data to user buffer",
3580 __func__);
3581 ret = -EFAULT;
3582 }
3583
3584 return ret;
3585}
3586
3587static int drv_cmd_set_roam_scan_channels(hdd_adapter_t *adapter,
3588 hdd_context_t *hdd_ctx,
3589 uint8_t *command,
3590 uint8_t command_len,
3591 hdd_priv_data_t *priv_data)
3592{
3593 return hdd_parse_set_roam_scan_channels(adapter, command);
3594}
3595
3596static int drv_cmd_get_roam_scan_channels(hdd_adapter_t *adapter,
3597 hdd_context_t *hdd_ctx,
3598 uint8_t *command,
3599 uint8_t command_len,
3600 hdd_priv_data_t *priv_data)
3601{
3602 int ret = 0;
3603 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
3604 uint8_t numChannels = 0;
3605 uint8_t j = 0;
3606 char extra[128] = { 0 };
3607 int len;
3608
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303609 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003610 sme_get_roam_scan_channel_list(hdd_ctx->hHal,
3611 ChannelList,
3612 &numChannels,
3613 adapter->sessionId)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303614 QDF_TRACE(QDF_MODULE_ID_HDD,
3615 QDF_TRACE_LEVEL_FATAL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003616 "%s: failed to get roam scan channel list",
3617 __func__);
3618 ret = -EFAULT;
3619 goto exit;
3620 }
3621
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303622 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003623 TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
3624 adapter->sessionId, numChannels));
3625 /*
3626 * output channel list is of the format
3627 * [Number of roam scan channels][Channel1][Channel2]...
3628 * copy the number of channels in the 0th index
3629 */
3630 len = scnprintf(extra, sizeof(extra), "%s %d", command,
3631 numChannels);
3632 for (j = 0; (j < numChannels); j++)
3633 len += scnprintf(extra + len, sizeof(extra) - len,
3634 " %d", ChannelList[j]);
3635
Anurag Chouhan6d760662016-02-20 16:05:43 +05303636 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003637 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303638 QDF_TRACE(QDF_MODULE_ID_HDD,
3639 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003640 "%s: failed to copy data to user buffer",
3641 __func__);
3642 ret = -EFAULT;
3643 goto exit;
3644 }
3645
3646exit:
3647 return ret;
3648}
3649
3650static int drv_cmd_get_ccx_mode(hdd_adapter_t *adapter,
3651 hdd_context_t *hdd_ctx,
3652 uint8_t *command,
3653 uint8_t command_len,
3654 hdd_priv_data_t *priv_data)
3655{
3656 int ret = 0;
3657 bool eseMode = sme_get_is_ese_feature_enabled(hdd_ctx->hHal);
3658 char extra[32];
3659 uint8_t len = 0;
3660
3661 /*
3662 * Check if the features OKC/ESE/11R are supported simultaneously,
3663 * then this operation is not permitted (return FAILURE)
3664 */
3665 if (eseMode &&
3666 hdd_is_okc_mode_enabled(hdd_ctx) &&
3667 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303668 QDF_TRACE(QDF_MODULE_ID_HDD,
3669 QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003670 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3671 __func__);
3672 ret = -EPERM;
3673 goto exit;
3674 }
3675
3676 len = scnprintf(extra, sizeof(extra), "%s %d",
3677 "GETCCXMODE", eseMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303678 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003679 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303680 QDF_TRACE(QDF_MODULE_ID_HDD,
3681 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003682 "%s: failed to copy data to user buffer",
3683 __func__);
3684 ret = -EFAULT;
3685 goto exit;
3686 }
3687
3688exit:
3689 return ret;
3690}
3691
3692static int drv_cmd_get_okc_mode(hdd_adapter_t *adapter,
3693 hdd_context_t *hdd_ctx,
3694 uint8_t *command,
3695 uint8_t command_len,
3696 hdd_priv_data_t *priv_data)
3697{
3698 int ret = 0;
3699 bool okcMode = hdd_is_okc_mode_enabled(hdd_ctx);
3700 char extra[32];
3701 uint8_t len = 0;
3702
3703 /*
3704 * Check if the features OKC/ESE/11R are supported simultaneously,
3705 * then this operation is not permitted (return FAILURE)
3706 */
3707 if (okcMode &&
3708 sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
3709 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303710 QDF_TRACE(QDF_MODULE_ID_HDD,
3711 QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003712 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3713 __func__);
3714 ret = -EPERM;
3715 goto exit;
3716 }
3717
3718 len = scnprintf(extra, sizeof(extra), "%s %d",
3719 "GETOKCMODE", okcMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303720 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003721
3722 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303723 QDF_TRACE(QDF_MODULE_ID_HDD,
3724 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003725 "%s: failed to copy data to user buffer",
3726 __func__);
3727 ret = -EFAULT;
3728 goto exit;
3729 }
3730
3731exit:
3732 return ret;
3733}
3734
3735static int drv_cmd_get_fast_roam(hdd_adapter_t *adapter,
3736 hdd_context_t *hdd_ctx,
3737 uint8_t *command,
3738 uint8_t command_len,
3739 hdd_priv_data_t *priv_data)
3740{
3741 int ret = 0;
3742 bool lfrMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3743 char extra[32];
3744 uint8_t len = 0;
3745
3746 len = scnprintf(extra, sizeof(extra), "%s %d",
3747 "GETFASTROAM", lfrMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303748 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003749
3750 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303751 QDF_TRACE(QDF_MODULE_ID_HDD,
3752 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003753 "%s: failed to copy data to user buffer",
3754 __func__);
3755 ret = -EFAULT;
3756 }
3757
3758 return ret;
3759}
3760
3761static int drv_cmd_get_fast_transition(hdd_adapter_t *adapter,
3762 hdd_context_t *hdd_ctx,
3763 uint8_t *command,
3764 uint8_t command_len,
3765 hdd_priv_data_t *priv_data)
3766{
3767 int ret = 0;
3768 bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->hHal);
3769 char extra[32];
3770 uint8_t len = 0;
3771
3772 len = scnprintf(extra, sizeof(extra), "%s %d",
3773 "GETFASTTRANSITION", ft);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303774 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003775
3776 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303777 QDF_TRACE(QDF_MODULE_ID_HDD,
3778 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003779 "%s: failed to copy data to user buffer",
3780 __func__);
3781 ret = -EFAULT;
3782 }
3783
3784 return ret;
3785}
3786
3787static int drv_cmd_set_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3788 hdd_context_t *hdd_ctx,
3789 uint8_t *command,
3790 uint8_t command_len,
3791 hdd_priv_data_t *priv_data)
3792{
3793 int ret = 0;
3794 uint8_t *value = command;
3795 uint8_t minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
3796
3797 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
3798 value = value + command_len + 1;
3799
3800 /* Convert the value from ascii to integer */
3801 ret = kstrtou8(value, 10, &minTime);
3802 if (ret < 0) {
3803 /*
3804 * If the input value is greater than max value of datatype,
3805 * then also kstrtou8 fails
3806 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303807 QDF_TRACE(QDF_MODULE_ID_HDD,
3808 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003809 "%s: kstrtou8 failed range [%d - %d]",
3810 __func__,
3811 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3812 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3813 ret = -EINVAL;
3814 goto exit;
3815 }
3816
3817 if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
3818 (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303819 QDF_TRACE(QDF_MODULE_ID_HDD,
3820 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003821 "scan min channel time value %d is out of range (Min: %d Max: %d)",
3822 minTime,
3823 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3824 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3825 ret = -EINVAL;
3826 goto exit;
3827 }
3828
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303829 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003830 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
3831 adapter->sessionId, minTime));
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303832 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003833 "%s: Received Command to change channel min time = %d",
3834 __func__, minTime);
3835
3836 hdd_ctx->config->nNeighborScanMinChanTime = minTime;
3837 sme_set_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3838 minTime,
3839 adapter->sessionId);
3840
3841exit:
3842 return ret;
3843}
3844
3845static int drv_cmd_send_action_frame(hdd_adapter_t *adapter,
3846 hdd_context_t *hdd_ctx,
3847 uint8_t *command,
3848 uint8_t command_len,
3849 hdd_priv_data_t *priv_data)
3850{
3851 return hdd_parse_sendactionframe(adapter, command);
3852}
3853
3854static int drv_cmd_get_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3855 hdd_context_t *hdd_ctx,
3856 uint8_t *command,
3857 uint8_t command_len,
3858 hdd_priv_data_t *priv_data)
3859{
3860 int ret = 0;
3861 uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3862 adapter->sessionId);
3863 char extra[32];
3864 uint8_t len = 0;
3865
3866 /* value is interms of msec */
3867 len = scnprintf(extra, sizeof(extra), "%s %d",
3868 "GETROAMSCANCHANNELMINTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303869 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003870
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303871 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003872 TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL,
3873 adapter->sessionId, val));
3874
3875 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303876 QDF_TRACE(QDF_MODULE_ID_HDD,
3877 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003878 "%s: failed to copy data to user buffer",
3879 __func__);
3880 ret = -EFAULT;
3881 }
3882
3883 return ret;
3884}
3885
3886static int drv_cmd_set_scan_channel_time(hdd_adapter_t *adapter,
3887 hdd_context_t *hdd_ctx,
3888 uint8_t *command,
3889 uint8_t command_len,
3890 hdd_priv_data_t *priv_data)
3891{
3892 int ret = 0;
3893 uint8_t *value = command;
3894 uint16_t maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
3895
3896 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
3897 value = value + command_len + 1;
3898
3899 /* Convert the value from ascii to integer */
3900 ret = kstrtou16(value, 10, &maxTime);
3901 if (ret < 0) {
3902 /*
3903 * If the input value is greater than max value of datatype,
3904 * then also kstrtou8 fails
3905 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303906 QDF_TRACE(QDF_MODULE_ID_HDD,
3907 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003908 "%s: kstrtou16 failed range [%d - %d]",
3909 __func__,
3910 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3911 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3912 ret = -EINVAL;
3913 goto exit;
3914 }
3915
3916 if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
3917 (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303918 QDF_TRACE(QDF_MODULE_ID_HDD,
3919 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003920 "lfr mode value %d is out of range (Min: %d Max: %d)",
3921 maxTime,
3922 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3923 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3924 ret = -EINVAL;
3925 goto exit;
3926 }
3927
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303928 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003929 "%s: Received Command to change channel max time = %d",
3930 __func__, maxTime);
3931
3932 hdd_ctx->config->nNeighborScanMaxChanTime = maxTime;
3933 sme_set_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3934 adapter->sessionId,
3935 maxTime);
3936
3937exit:
3938 return ret;
3939}
3940
3941static int drv_cmd_get_scan_channel_time(hdd_adapter_t *adapter,
3942 hdd_context_t *hdd_ctx,
3943 uint8_t *command,
3944 uint8_t command_len,
3945 hdd_priv_data_t *priv_data)
3946{
3947 int ret = 0;
3948 uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3949 adapter->sessionId);
3950 char extra[32];
3951 uint8_t len = 0;
3952
3953 /* value is interms of msec */
3954 len = scnprintf(extra, sizeof(extra), "%s %d",
3955 "GETSCANCHANNELTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303956 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003957
3958 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303959 QDF_TRACE(QDF_MODULE_ID_HDD,
3960 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003961 "%s: failed to copy data to user buffer",
3962 __func__);
3963 ret = -EFAULT;
3964 }
3965
3966 return ret;
3967}
3968
3969static int drv_cmd_set_scan_home_time(hdd_adapter_t *adapter,
3970 hdd_context_t *hdd_ctx,
3971 uint8_t *command,
3972 uint8_t command_len,
3973 hdd_priv_data_t *priv_data)
3974{
3975 int ret = 0;
3976 uint8_t *value = command;
3977 uint16_t val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
3978
3979 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
3980 value = value + command_len + 1;
3981
3982 /* Convert the value from ascii to integer */
3983 ret = kstrtou16(value, 10, &val);
3984 if (ret < 0) {
3985 /*
3986 * If the input value is greater than max value of datatype,
3987 * then also kstrtou8 fails
3988 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303989 QDF_TRACE(QDF_MODULE_ID_HDD,
3990 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003991 "%s: kstrtou16 failed range [%d - %d]",
3992 __func__,
3993 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3994 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
3995 ret = -EINVAL;
3996 goto exit;
3997 }
3998
3999 if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
4000 (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304001 QDF_TRACE(QDF_MODULE_ID_HDD,
4002 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004003 "scan home time value %d is out of range (Min: %d Max: %d)",
4004 val,
4005 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
4006 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
4007 ret = -EINVAL;
4008 goto exit;
4009 }
4010
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304011 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004012 "%s: Received Command to change scan home time = %d",
4013 __func__, val);
4014
4015 hdd_ctx->config->nNeighborScanPeriod = val;
4016 sme_set_neighbor_scan_period(hdd_ctx->hHal,
4017 adapter->sessionId, val);
4018
4019exit:
4020 return ret;
4021}
4022
4023static int drv_cmd_get_scan_home_time(hdd_adapter_t *adapter,
4024 hdd_context_t *hdd_ctx,
4025 uint8_t *command,
4026 uint8_t command_len,
4027 hdd_priv_data_t *priv_data)
4028{
4029 int ret = 0;
4030 uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->hHal,
4031 adapter->
4032 sessionId);
4033 char extra[32];
4034 uint8_t len = 0;
4035
4036 /* value is interms of msec */
4037 len = scnprintf(extra, sizeof(extra), "%s %d",
4038 "GETSCANHOMETIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304039 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004040
4041 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304042 QDF_TRACE(QDF_MODULE_ID_HDD,
4043 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004044 "%s: failed to copy data to user buffer",
4045 __func__);
4046 ret = -EFAULT;
4047 }
4048
4049 return ret;
4050}
4051
4052static int drv_cmd_set_roam_intra_band(hdd_adapter_t *adapter,
4053 hdd_context_t *hdd_ctx,
4054 uint8_t *command,
4055 uint8_t command_len,
4056 hdd_priv_data_t *priv_data)
4057{
4058 int ret = 0;
4059 uint8_t *value = command;
4060 uint8_t val = CFG_ROAM_INTRA_BAND_DEFAULT;
4061
4062 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
4063 value = value + command_len + 1;
4064
4065 /* Convert the value from ascii to integer */
4066 ret = kstrtou8(value, 10, &val);
4067 if (ret < 0) {
4068 /*
4069 * If the input value is greater than max value of datatype,
4070 * then also kstrtou8 fails
4071 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304072 QDF_TRACE(QDF_MODULE_ID_HDD,
4073 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004074 "%s: kstrtou8 failed range [%d - %d]",
4075 __func__, CFG_ROAM_INTRA_BAND_MIN,
4076 CFG_ROAM_INTRA_BAND_MAX);
4077 ret = -EINVAL;
4078 goto exit;
4079 }
4080
4081 if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
4082 (val > CFG_ROAM_INTRA_BAND_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304083 QDF_TRACE(QDF_MODULE_ID_HDD,
4084 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004085 "intra band mode value %d is out of range (Min: %d Max: %d)",
4086 val,
4087 CFG_ROAM_INTRA_BAND_MIN,
4088 CFG_ROAM_INTRA_BAND_MAX);
4089 ret = -EINVAL;
4090 goto exit;
4091 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304092 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004093 "%s: Received Command to change intra band = %d",
4094 __func__, val);
4095
4096 hdd_ctx->config->nRoamIntraBand = val;
4097 sme_set_roam_intra_band(hdd_ctx->hHal, val);
4098
4099exit:
4100 return ret;
4101}
4102
4103static int drv_cmd_get_roam_intra_band(hdd_adapter_t *adapter,
4104 hdd_context_t *hdd_ctx,
4105 uint8_t *command,
4106 uint8_t command_len,
4107 hdd_priv_data_t *priv_data)
4108{
4109 int ret = 0;
4110 uint16_t val = sme_get_roam_intra_band(hdd_ctx->hHal);
4111 char extra[32];
4112 uint8_t len = 0;
4113
4114 /* value is interms of msec */
4115 len = scnprintf(extra, sizeof(extra), "%s %d",
4116 "GETROAMINTRABAND", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304117 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004118 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304119 QDF_TRACE(QDF_MODULE_ID_HDD,
4120 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004121 "%s: failed to copy data to user buffer",
4122 __func__);
4123 ret = -EFAULT;
4124 }
4125
4126 return ret;
4127}
4128
4129static int drv_cmd_set_scan_n_probes(hdd_adapter_t *adapter,
4130 hdd_context_t *hdd_ctx,
4131 uint8_t *command,
4132 uint8_t command_len,
4133 hdd_priv_data_t *priv_data)
4134{
4135 int ret = 0;
4136 uint8_t *value = command;
4137 uint8_t nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
4138
4139 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */
4140 value = value + command_len + 1;
4141
4142 /* Convert the value from ascii to integer */
4143 ret = kstrtou8(value, 10, &nProbes);
4144 if (ret < 0) {
4145 /*
4146 * If the input value is greater than max value of datatype,
4147 * then also kstrtou8 fails
4148 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304149 QDF_TRACE(QDF_MODULE_ID_HDD,
4150 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004151 "%s: kstrtou8 failed range [%d - %d]",
4152 __func__, CFG_ROAM_SCAN_N_PROBES_MIN,
4153 CFG_ROAM_SCAN_N_PROBES_MAX);
4154 ret = -EINVAL;
4155 goto exit;
4156 }
4157
4158 if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
4159 (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304160 QDF_TRACE(QDF_MODULE_ID_HDD,
4161 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004162 "NProbes value %d is out of range (Min: %d Max: %d)",
4163 nProbes,
4164 CFG_ROAM_SCAN_N_PROBES_MIN,
4165 CFG_ROAM_SCAN_N_PROBES_MAX);
4166 ret = -EINVAL;
4167 goto exit;
4168 }
4169
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304170 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004171 "%s: Received Command to Set nProbes = %d",
4172 __func__, nProbes);
4173
4174 hdd_ctx->config->nProbes = nProbes;
4175 sme_update_roam_scan_n_probes(hdd_ctx->hHal,
4176 adapter->sessionId, nProbes);
4177
4178exit:
4179 return ret;
4180}
4181
4182static int drv_cmd_get_scan_n_probes(hdd_adapter_t *adapter,
4183 hdd_context_t *hdd_ctx,
4184 uint8_t *command,
4185 uint8_t command_len,
4186 hdd_priv_data_t *priv_data)
4187{
4188 int ret = 0;
4189 uint8_t val = sme_get_roam_scan_n_probes(hdd_ctx->hHal);
4190 char extra[32];
4191 uint8_t len = 0;
4192
4193 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304194 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004195 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304196 QDF_TRACE(QDF_MODULE_ID_HDD,
4197 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004198 "%s: failed to copy data to user buffer",
4199 __func__);
4200 ret = -EFAULT;
4201 }
4202
4203 return ret;
4204}
4205
4206static int drv_cmd_set_scan_home_away_time(hdd_adapter_t *adapter,
4207 hdd_context_t *hdd_ctx,
4208 uint8_t *command,
4209 uint8_t command_len,
4210 hdd_priv_data_t *priv_data)
4211{
4212 int ret = 0;
4213 uint8_t *value = command;
4214 uint16_t homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
4215
4216 /* input value is in units of msec */
4217
4218 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
4219 value = value + command_len + 1;
4220
4221 /* Convert the value from ascii to integer */
4222 ret = kstrtou16(value, 10, &homeAwayTime);
4223 if (ret < 0) {
4224 /*
4225 * If the input value is greater than max value of datatype,
4226 * then also kstrtou8 fails
4227 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304228 QDF_TRACE(QDF_MODULE_ID_HDD,
4229 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004230 "%s: kstrtou8 failed range [%d - %d]",
4231 __func__,
4232 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4233 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
4234 ret = -EINVAL;
4235 goto exit;
4236 }
4237
4238 if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
4239 (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304240 QDF_TRACE(QDF_MODULE_ID_HDD,
4241 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004242 "homeAwayTime value %d is out of range (Min: %d Max: %d)",
4243 homeAwayTime,
4244 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4245 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
4246 ret = -EINVAL;
4247 goto exit;
4248 }
4249
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304250 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004251 "%s: Received Command to Set scan away time = %d",
4252 __func__, homeAwayTime);
4253
4254 if (hdd_ctx->config->nRoamScanHomeAwayTime !=
4255 homeAwayTime) {
4256 hdd_ctx->config->nRoamScanHomeAwayTime = homeAwayTime;
4257 sme_update_roam_scan_home_away_time(hdd_ctx->hHal,
4258 adapter->sessionId,
4259 homeAwayTime,
4260 true);
4261 }
4262
4263exit:
4264 return ret;
4265}
4266
4267static int drv_cmd_get_scan_home_away_time(hdd_adapter_t *adapter,
4268 hdd_context_t *hdd_ctx,
4269 uint8_t *command,
4270 uint8_t command_len,
4271 hdd_priv_data_t *priv_data)
4272{
4273 int ret = 0;
4274 uint16_t val = sme_get_roam_scan_home_away_time(hdd_ctx->hHal);
4275 char extra[32];
4276 uint8_t len = 0;
4277
4278 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304279 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004280
4281 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304282 QDF_TRACE(QDF_MODULE_ID_HDD,
4283 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004284 "%s: failed to copy data to user buffer",
4285 __func__);
4286 ret = -EFAULT;
4287 }
4288
4289 return ret;
4290}
4291
4292static int drv_cmd_reassoc(hdd_adapter_t *adapter,
4293 hdd_context_t *hdd_ctx,
4294 uint8_t *command,
4295 uint8_t command_len,
4296 hdd_priv_data_t *priv_data)
4297{
4298 return hdd_parse_reassoc(adapter, command);
4299}
4300
4301static int drv_cmd_set_wes_mode(hdd_adapter_t *adapter,
4302 hdd_context_t *hdd_ctx,
4303 uint8_t *command,
4304 uint8_t command_len,
4305 hdd_priv_data_t *priv_data)
4306{
4307 int ret = 0;
4308 uint8_t *value = command;
4309 uint8_t wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
4310
4311 /* Move pointer to ahead of SETWESMODE<delimiter> */
4312 value = value + command_len + 1;
4313
4314 /* Convert the value from ascii to integer */
4315 ret = kstrtou8(value, 10, &wesMode);
4316 if (ret < 0) {
4317 /*
4318 * If the input value is greater than max value of datatype,
4319 * then also kstrtou8 fails
4320 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304321 QDF_TRACE(QDF_MODULE_ID_HDD,
4322 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004323 "%s: kstrtou8 failed range [%d - %d]",
4324 __func__,
4325 CFG_ENABLE_WES_MODE_NAME_MIN,
4326 CFG_ENABLE_WES_MODE_NAME_MAX);
4327 ret = -EINVAL;
4328 goto exit;
4329 }
4330
4331 if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
4332 (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304333 QDF_TRACE(QDF_MODULE_ID_HDD,
4334 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004335 "WES Mode value %d is out of range (Min: %d Max: %d)",
4336 wesMode,
4337 CFG_ENABLE_WES_MODE_NAME_MIN,
4338 CFG_ENABLE_WES_MODE_NAME_MAX);
4339 ret = -EINVAL;
4340 goto exit;
4341 }
4342
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304343 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004344 "%s: Received Command to Set WES Mode rssi diff = %d",
4345 __func__, wesMode);
4346
4347 hdd_ctx->config->isWESModeEnabled = wesMode;
4348 sme_update_wes_mode(hdd_ctx->hHal, wesMode, adapter->sessionId);
4349
4350exit:
4351 return ret;
4352}
4353
4354static int drv_cmd_get_wes_mode(hdd_adapter_t *adapter,
4355 hdd_context_t *hdd_ctx,
4356 uint8_t *command,
4357 uint8_t command_len,
4358 hdd_priv_data_t *priv_data)
4359{
4360 int ret = 0;
4361 bool wesMode = sme_get_wes_mode(hdd_ctx->hHal);
4362 char extra[32];
4363 uint8_t len = 0;
4364
4365 len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304366 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004367 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304368 QDF_TRACE(QDF_MODULE_ID_HDD,
4369 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004370 "%s: failed to copy data to user buffer",
4371 __func__);
4372 ret = -EFAULT;
4373 }
4374
4375 return ret;
4376}
4377
4378static int drv_cmd_set_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4379 hdd_context_t *hdd_ctx,
4380 uint8_t *command,
4381 uint8_t command_len,
4382 hdd_priv_data_t *priv_data)
4383{
4384 int ret = 0;
4385 uint8_t *value = command;
4386 uint8_t nOpportunisticThresholdDiff =
4387 CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
4388
4389 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
4390 value = value + command_len + 1;
4391
4392 /* Convert the value from ascii to integer */
4393 ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
4394 if (ret < 0) {
4395 /*
4396 * If the input value is greater than max value of datatype,
4397 * then also kstrtou8 fails
4398 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304399 QDF_TRACE(QDF_MODULE_ID_HDD,
4400 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004401 "%s: kstrtou8 failed.", __func__);
4402 ret = -EINVAL;
4403 goto exit;
4404 }
4405
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304406 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004407 "%s: Received Command to Set Opportunistic Threshold diff = %d",
4408 __func__, nOpportunisticThresholdDiff);
4409
4410 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->hHal,
4411 adapter->sessionId,
4412 nOpportunisticThresholdDiff);
4413
4414exit:
4415 return ret;
4416}
4417
4418static int drv_cmd_get_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4419 hdd_context_t *hdd_ctx,
4420 uint8_t *command,
4421 uint8_t command_len,
4422 hdd_priv_data_t *priv_data)
4423{
4424 int ret = 0;
4425 int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(
4426 hdd_ctx->hHal);
4427 char extra[32];
4428 uint8_t len = 0;
4429
4430 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304431 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004432 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304433 QDF_TRACE(QDF_MODULE_ID_HDD,
4434 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004435 "%s: failed to copy data to user buffer",
4436 __func__);
4437 ret = -EFAULT;
4438 }
4439
4440 return ret;
4441}
4442
4443static int drv_cmd_set_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4444 hdd_context_t *hdd_ctx,
4445 uint8_t *command,
4446 uint8_t command_len,
4447 hdd_priv_data_t *priv_data)
4448{
4449 int ret = 0;
4450 uint8_t *value = command;
4451 uint8_t nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
4452
4453 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
4454 value = value + command_len + 1;
4455
4456 /* Convert the value from ascii to integer */
4457 ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
4458 if (ret < 0) {
4459 /*
4460 * If the input value is greater than max value of datatype,
4461 * then also kstrtou8 fails
4462 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304463 QDF_TRACE(QDF_MODULE_ID_HDD,
4464 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004465 "%s: kstrtou8 failed.", __func__);
4466 ret = -EINVAL;
4467 goto exit;
4468 }
4469
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304470 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004471 "%s: Received Command to Set Roam Rescan RSSI Diff = %d",
4472 __func__, nRoamRescanRssiDiff);
4473
4474 sme_set_roam_rescan_rssi_diff(hdd_ctx->hHal,
4475 adapter->sessionId,
4476 nRoamRescanRssiDiff);
4477
4478exit:
4479 return ret;
4480}
4481
4482static int drv_cmd_get_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4483 hdd_context_t *hdd_ctx,
4484 uint8_t *command,
4485 uint8_t command_len,
4486 hdd_priv_data_t *priv_data)
4487{
4488 int ret = 0;
4489 uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->hHal);
4490 char extra[32];
4491 uint8_t len = 0;
4492
4493 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304494 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004495 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304496 QDF_TRACE(QDF_MODULE_ID_HDD,
4497 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004498 "%s: failed to copy data to user buffer",
4499 __func__);
4500 ret = -EFAULT;
4501 }
4502
4503 return ret;
4504}
4505
4506static int drv_cmd_set_fast_roam(hdd_adapter_t *adapter,
4507 hdd_context_t *hdd_ctx,
4508 uint8_t *command,
4509 uint8_t command_len,
4510 hdd_priv_data_t *priv_data)
4511{
4512 int ret = 0;
4513 uint8_t *value = command;
4514 uint8_t lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
4515
4516 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4517 value = value + command_len + 1;
4518
4519 /* Convert the value from ascii to integer */
4520 ret = kstrtou8(value, 10, &lfrMode);
4521 if (ret < 0) {
4522 /*
4523 * If the input value is greater than max value of datatype,
4524 * then also kstrtou8 fails
4525 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304526 QDF_TRACE(QDF_MODULE_ID_HDD,
4527 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004528 "%s: kstrtou8 failed range [%d - %d]",
4529 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
4530 CFG_LFR_FEATURE_ENABLED_MAX);
4531 ret = -EINVAL;
4532 goto exit;
4533 }
4534
4535 if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
4536 (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304537 QDF_TRACE(QDF_MODULE_ID_HDD,
4538 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004539 "lfr mode value %d is out of range (Min: %d Max: %d)",
4540 lfrMode,
4541 CFG_LFR_FEATURE_ENABLED_MIN,
4542 CFG_LFR_FEATURE_ENABLED_MAX);
4543 ret = -EINVAL;
4544 goto exit;
4545 }
4546
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304547 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004548 "%s: Received Command to change lfr mode = %d",
4549 __func__, lfrMode);
4550
4551 hdd_ctx->config->isFastRoamIniFeatureEnabled = lfrMode;
4552 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->hHal,
4553 adapter->
4554 sessionId,
4555 lfrMode);
4556
4557exit:
4558 return ret;
4559}
4560
4561static int drv_cmd_set_fast_transition(hdd_adapter_t *adapter,
4562 hdd_context_t *hdd_ctx,
4563 uint8_t *command,
4564 uint8_t command_len,
4565 hdd_priv_data_t *priv_data)
4566{
4567 int ret = 0;
4568 uint8_t *value = command;
4569 uint8_t ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
4570
4571 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4572 value = value + command_len + 1;
4573
4574 /* Convert the value from ascii to integer */
4575 ret = kstrtou8(value, 10, &ft);
4576 if (ret < 0) {
4577 /*
4578 * If the input value is greater than max value of datatype,
4579 * then also kstrtou8 fails
4580 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304581 QDF_TRACE(QDF_MODULE_ID_HDD,
4582 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004583 "%s: kstrtou8 failed range [%d - %d]",
4584 __func__,
4585 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4586 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4587 ret = -EINVAL;
4588 goto exit;
4589 }
4590
4591 if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
4592 (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304593 QDF_TRACE(QDF_MODULE_ID_HDD,
4594 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004595 "ft mode value %d is out of range (Min: %d Max: %d)",
4596 ft,
4597 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4598 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4599 ret = -EINVAL;
4600 goto exit;
4601 }
4602
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304603 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004604 "%s: Received Command to change ft mode = %d",
4605 __func__, ft);
4606
4607 hdd_ctx->config->isFastTransitionEnabled = ft;
4608 sme_update_fast_transition_enabled(hdd_ctx->hHal, ft);
4609
4610exit:
4611 return ret;
4612}
4613
4614#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4615static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid,
4616 int channel)
4617{
4618 struct wma_roam_invoke_cmd *fastreassoc;
4619 cds_msg_t msg = {0};
4620
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304621 fastreassoc = qdf_mem_malloc(sizeof(*fastreassoc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004622 if (NULL == fastreassoc) {
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05304623 hddLog(LOGE, FL("qdf_mem_malloc failed for fastreassoc"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004624 return;
4625 }
4626 fastreassoc->vdev_id = sessionId;
4627 fastreassoc->channel = channel;
4628 fastreassoc->bssid[0] = bssid[0];
4629 fastreassoc->bssid[1] = bssid[1];
4630 fastreassoc->bssid[2] = bssid[2];
4631 fastreassoc->bssid[3] = bssid[3];
4632 fastreassoc->bssid[4] = bssid[4];
4633 fastreassoc->bssid[5] = bssid[5];
4634
4635 msg.type = SIR_HAL_ROAM_INVOKE;
4636 msg.reserved = 0;
4637 msg.bodyptr = fastreassoc;
Anurag Chouhan6d760662016-02-20 16:05:43 +05304638 if (QDF_STATUS_SUCCESS != cds_mq_post_message(QDF_MODULE_ID_WMA,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004639 &msg)) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304640 qdf_mem_free(fastreassoc);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304641 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004642 FL("Not able to post ROAM_INVOKE_CMD message to WMA"));
4643 }
4644}
4645#endif
4646static int drv_cmd_fast_reassoc(hdd_adapter_t *adapter,
4647 hdd_context_t *hdd_ctx,
4648 uint8_t *command,
4649 uint8_t command_len,
4650 hdd_priv_data_t *priv_data)
4651{
4652 int ret = 0;
4653 uint8_t *value = command;
4654 uint8_t channel = 0;
4655 tSirMacAddr targetApBssid;
4656 uint32_t roamId = 0;
4657 tCsrRoamModifyProfileFields modProfileFields;
4658#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4659 tCsrHandoffRequest handoffInfo;
4660#endif
4661 hdd_station_ctx_t *pHddStaCtx;
4662
Krunal Sonibe766b02016-03-10 13:00:44 -08004663 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004664 hdd_warn("Unsupported in mode %s(%d)",
4665 hdd_device_mode_to_string(adapter->device_mode),
4666 adapter->device_mode);
4667 return -EINVAL;
4668 }
4669
4670 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4671
4672 /* if not associated, no need to proceed with reassoc */
4673 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304674 QDF_TRACE(QDF_MODULE_ID_HDD,
4675 QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004676 "%s:Not associated!", __func__);
4677 ret = -EINVAL;
4678 goto exit;
4679 }
4680
4681 ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
4682 &channel);
4683 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304684 QDF_TRACE(QDF_MODULE_ID_HDD,
4685 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004686 "%s: Failed to parse reassoc command data",
4687 __func__);
4688 goto exit;
4689 }
4690
4691 /*
4692 * if the target bssid is same as currently associated AP,
4693 * issue reassoc to same AP
4694 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304695 if (true != qdf_mem_cmp(targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004696 pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05304697 QDF_MAC_ADDR_SIZE)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004698 /* Reassoc to same AP, only supported for Open Security*/
4699 if ((pHddStaCtx->conn_info.ucEncryptionType ||
4700 pHddStaCtx->conn_info.mcEncryptionType)) {
4701 hddLog(LOGE,
4702 FL("Reassoc to same AP, only supported for Open Security"));
4703 return -ENOTSUPP;
4704 }
4705 hddLog(LOG1,
4706 FL("Reassoc BSSID is same as currently associated AP bssid"));
4707 sme_get_modify_profile_fields(hdd_ctx->hHal, adapter->sessionId,
4708 &modProfileFields);
4709 sme_roam_reassoc(hdd_ctx->hHal, adapter->sessionId,
4710 NULL, modProfileFields, &roamId, 1);
4711 return 0;
4712 }
4713
4714 /* Check channel number is a valid channel number */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304715 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004716 wlan_hdd_validate_operation_channel(adapter, channel)) {
4717 hddLog(LOGE, FL("Invalid Channel [%d]"), channel);
4718 return -EINVAL;
4719 }
4720#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4721 if (hdd_ctx->config->isRoamOffloadEnabled) {
4722 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4723 targetApBssid, (int)channel);
4724 goto exit;
4725 }
4726#endif
4727 /* Proceed with reassoc */
4728#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4729 handoffInfo.channel = channel;
4730 handoffInfo.src = FASTREASSOC;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304731 qdf_mem_copy(handoffInfo.bssid, targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004732 sizeof(tSirMacAddr));
4733 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
4734 &handoffInfo);
4735#endif
4736
4737exit:
4738 return ret;
4739}
4740
4741#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
4742static int drv_cmd_ccx_plm_req(hdd_adapter_t *adapter,
4743 hdd_context_t *hdd_ctx,
4744 uint8_t *command,
4745 uint8_t command_len,
4746 hdd_priv_data_t *priv_data)
4747{
4748 int ret = 0;
4749 uint8_t *value = command;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304750 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004751 tpSirPlmReq pPlmRequest = NULL;
4752
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304753 pPlmRequest = qdf_mem_malloc(sizeof(tSirPlmReq));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004754 if (NULL == pPlmRequest) {
4755 ret = -ENOMEM;
4756 goto exit;
4757 }
4758
4759 status = hdd_parse_plm_cmd(value, pPlmRequest);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304760 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304761 qdf_mem_free(pPlmRequest);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004762 pPlmRequest = NULL;
4763 ret = -EINVAL;
4764 goto exit;
4765 }
4766 pPlmRequest->sessionId = adapter->sessionId;
4767
4768 status = sme_set_plm_request(hdd_ctx->hHal, pPlmRequest);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304769 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304770 qdf_mem_free(pPlmRequest);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004771 pPlmRequest = NULL;
4772 ret = -EINVAL;
4773 goto exit;
4774 }
4775
4776exit:
4777 return ret;
4778}
4779#endif
4780
4781#ifdef FEATURE_WLAN_ESE
4782static int drv_cmd_set_ccx_mode(hdd_adapter_t *adapter,
4783 hdd_context_t *hdd_ctx,
4784 uint8_t *command,
4785 uint8_t command_len,
4786 hdd_priv_data_t *priv_data)
4787{
4788 int ret = 0;
4789 uint8_t *value = command;
4790 uint8_t eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT;
4791
4792 /*
4793 * Check if the features OKC/ESE/11R are supported simultaneously,
4794 * then this operation is not permitted (return FAILURE)
4795 */
4796 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4797 hdd_is_okc_mode_enabled(hdd_ctx) &&
4798 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304799 QDF_TRACE(QDF_MODULE_ID_HDD,
4800 QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004801 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4802 __func__);
4803 ret = -EPERM;
4804 goto exit;
4805 }
4806
4807 /* Move pointer to ahead of SETCCXMODE<delimiter> */
4808 value = value + command_len + 1;
4809
4810 /* Convert the value from ascii to integer */
4811 ret = kstrtou8(value, 10, &eseMode);
4812 if (ret < 0) {
4813 /*
4814 * If the input value is greater than max value of datatype,
4815 * then also kstrtou8 fails
4816 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304817 QDF_TRACE(QDF_MODULE_ID_HDD,
4818 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004819 "%s: kstrtou8 failed range [%d - %d]",
4820 __func__, CFG_ESE_FEATURE_ENABLED_MIN,
4821 CFG_ESE_FEATURE_ENABLED_MAX);
4822 ret = -EINVAL;
4823 goto exit;
4824 }
4825
4826 if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) ||
4827 (eseMode > CFG_ESE_FEATURE_ENABLED_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304828 QDF_TRACE(QDF_MODULE_ID_HDD,
4829 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004830 "Ese mode value %d is out of range (Min: %d Max: %d)",
4831 eseMode,
4832 CFG_ESE_FEATURE_ENABLED_MIN,
4833 CFG_ESE_FEATURE_ENABLED_MAX);
4834 ret = -EINVAL;
4835 goto exit;
4836 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304837 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004838 "%s: Received Command to change ese mode = %d",
4839 __func__, eseMode);
4840
4841 hdd_ctx->config->isEseIniFeatureEnabled = eseMode;
4842 sme_update_is_ese_feature_enabled(hdd_ctx->hHal,
4843 adapter->sessionId,
4844 eseMode);
4845
4846exit:
4847 return ret;
4848}
4849#endif
4850
4851static int drv_cmd_set_roam_scan_control(hdd_adapter_t *adapter,
4852 hdd_context_t *hdd_ctx,
4853 uint8_t *command,
4854 uint8_t command_len,
4855 hdd_priv_data_t *priv_data)
4856{
4857 int ret = 0;
4858 uint8_t *value = command;
4859 uint8_t roamScanControl = 0;
4860
4861 /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
4862 value = value + command_len + 1;
4863
4864 /* Convert the value from ascii to integer */
4865 ret = kstrtou8(value, 10, &roamScanControl);
4866 if (ret < 0) {
4867 /*
4868 * If the input value is greater than max value of datatype,
4869 * then also kstrtou8 fails
4870 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304871 QDF_TRACE(QDF_MODULE_ID_HDD,
4872 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004873 "%s: kstrtou8 failed ", __func__);
4874 ret = -EINVAL;
4875 goto exit;
4876 }
4877
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304878 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004879 "%s: Received Command to Set roam scan control = %d",
4880 __func__, roamScanControl);
4881
4882 if (0 != roamScanControl) {
4883 ret = 0; /* return success but ignore param value "true" */
4884 goto exit;
4885 }
4886
4887 sme_set_roam_scan_control(hdd_ctx->hHal,
4888 adapter->sessionId,
4889 roamScanControl);
4890
4891exit:
4892 return ret;
4893}
4894
4895static int drv_cmd_set_okc_mode(hdd_adapter_t *adapter,
4896 hdd_context_t *hdd_ctx,
4897 uint8_t *command,
4898 uint8_t command_len,
4899 hdd_priv_data_t *priv_data)
4900{
4901 int ret = 0;
4902 uint8_t *value = command;
4903 uint8_t okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
4904
4905 /*
4906 * Check if the features OKC/ESE/11R are supported simultaneously,
4907 * then this operation is not permitted (return FAILURE)
4908 */
4909 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4910 hdd_is_okc_mode_enabled(hdd_ctx) &&
4911 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304912 QDF_TRACE(QDF_MODULE_ID_HDD,
4913 QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004914 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4915 __func__);
4916 ret = -EPERM;
4917 goto exit;
4918 }
4919
4920 /* Move pointer to ahead of SETOKCMODE<delimiter> */
4921 value = value + command_len + 1;
4922
4923 /* Convert the value from ascii to integer */
4924 ret = kstrtou8(value, 10, &okcMode);
4925 if (ret < 0) {
4926 /*
4927 * If the input value is greater than max value of datatype,
4928 * then also kstrtou8 fails
4929 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304930 QDF_TRACE(QDF_MODULE_ID_HDD,
4931 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004932 "%s: kstrtou8 failed range [%d - %d]",
4933 __func__, CFG_OKC_FEATURE_ENABLED_MIN,
4934 CFG_OKC_FEATURE_ENABLED_MAX);
4935 ret = -EINVAL;
4936 goto exit;
4937 }
4938
4939 if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
4940 (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304941 QDF_TRACE(QDF_MODULE_ID_HDD,
4942 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004943 "Okc mode value %d is out of range (Min: %d Max: %d)",
4944 okcMode,
4945 CFG_OKC_FEATURE_ENABLED_MIN,
4946 CFG_OKC_FEATURE_ENABLED_MAX);
4947 ret = -EINVAL;
4948 goto exit;
4949 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304950 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004951 "%s: Received Command to change okc mode = %d",
4952 __func__, okcMode);
4953
4954 hdd_ctx->config->isOkcIniFeatureEnabled = okcMode;
4955
4956exit:
4957 return ret;
4958}
4959
4960static int drv_cmd_get_roam_scan_control(hdd_adapter_t *adapter,
4961 hdd_context_t *hdd_ctx,
4962 uint8_t *command,
4963 uint8_t command_len,
4964 hdd_priv_data_t *priv_data)
4965{
4966 int ret = 0;
4967 bool roamScanControl = sme_get_roam_scan_control(hdd_ctx->hHal);
4968 char extra[32];
4969 uint8_t len = 0;
4970
4971 len = scnprintf(extra, sizeof(extra), "%s %d",
4972 command, roamScanControl);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304973 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004974 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304975 QDF_TRACE(QDF_MODULE_ID_HDD,
4976 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004977 "%s: failed to copy data to user buffer",
4978 __func__);
4979 ret = -EFAULT;
4980 }
4981
4982 return ret;
4983}
4984
4985static int drv_cmd_bt_coex_mode(hdd_adapter_t *adapter,
4986 hdd_context_t *hdd_ctx,
4987 uint8_t *command,
4988 uint8_t command_len,
4989 hdd_priv_data_t *priv_data)
4990{
4991 int ret = 0;
4992 char *bcMode;
4993
4994 bcMode = command + 11;
4995 if ('1' == *bcMode) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304996 QDF_TRACE(QDF_MODULE_ID_HDD,
4997 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004998 FL("BTCOEXMODE %d"), *bcMode);
4999 hdd_ctx->btCoexModeSet = true;
5000 ret = wlan_hdd_scan_abort(adapter);
5001 if (ret < 0) {
5002 hddLog(LOGE,
5003 FL("Failed to abort existing scan status:%d"), ret);
5004 }
5005 } else if ('2' == *bcMode) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305006 QDF_TRACE(QDF_MODULE_ID_HDD,
5007 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005008 FL("BTCOEXMODE %d"), *bcMode);
5009 hdd_ctx->btCoexModeSet = false;
5010 }
5011
5012 return ret;
5013}
5014
5015static int drv_cmd_scan_active(hdd_adapter_t *adapter,
5016 hdd_context_t *hdd_ctx,
5017 uint8_t *command,
5018 uint8_t command_len,
5019 hdd_priv_data_t *priv_data)
5020{
5021 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
5022 return 0;
5023}
5024
5025static int drv_cmd_scan_passive(hdd_adapter_t *adapter,
5026 hdd_context_t *hdd_ctx,
5027 uint8_t *command,
5028 uint8_t command_len,
5029 hdd_priv_data_t *priv_data)
5030{
5031 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN;
5032 return 0;
5033}
5034
5035static int drv_cmd_get_dwell_time(hdd_adapter_t *adapter,
5036 hdd_context_t *hdd_ctx,
5037 uint8_t *command,
5038 uint8_t command_len,
5039 hdd_priv_data_t *priv_data)
5040{
5041 int ret = 0;
5042 struct hdd_config *pCfg =
5043 (WLAN_HDD_GET_CTX(adapter))->config;
5044 char extra[32];
5045 uint8_t len = 0;
5046
5047 memset(extra, 0, sizeof(extra));
5048 ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305049 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005050 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305051 QDF_TRACE(QDF_MODULE_ID_HDD,
5052 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005053 "%s: failed to copy data to user buffer",
5054 __func__);
5055 ret = -EFAULT;
5056 goto exit;
5057 }
5058 ret = len;
5059exit:
5060 return ret;
5061}
5062
5063static int drv_cmd_set_dwell_time(hdd_adapter_t *adapter,
5064 hdd_context_t *hdd_ctx,
5065 uint8_t *command,
5066 uint8_t command_len,
5067 hdd_priv_data_t *priv_data)
5068{
5069 return hdd_set_dwell_time(adapter, command);
5070}
5071
5072static int drv_cmd_miracast(hdd_adapter_t *adapter,
5073 hdd_context_t *hdd_ctx,
5074 uint8_t *command,
5075 uint8_t command_len,
5076 hdd_priv_data_t *priv_data)
5077{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305078 QDF_STATUS ret_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005079 int ret = 0;
5080 tHalHandle hHal;
5081 uint8_t filterType = 0;
5082 hdd_context_t *pHddCtx = NULL;
5083 uint8_t *value;
5084
5085 pHddCtx = WLAN_HDD_GET_CTX(adapter);
5086 if (0 != wlan_hdd_validate_context(pHddCtx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305087 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005088 "%s pHddCtx is not valid, Unable to set miracast mode",
5089 __func__);
5090 return -EINVAL;
5091 }
5092
5093 hHal = pHddCtx->hHal;
5094 value = command + 9;
5095
5096 /* Convert the value from ascii to integer */
5097 ret = kstrtou8(value, 10, &filterType);
5098 if (ret < 0) {
5099 /*
5100 * If the input value is greater than max value of datatype,
5101 * then also kstrtou8 fails
5102 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305103 QDF_TRACE(QDF_MODULE_ID_HDD,
5104 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005105 "%s: kstrtou8 failed range ",
5106 __func__);
5107 ret = -EINVAL;
5108 goto exit;
5109 }
5110 if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL)
5111 || (filterType >
5112 WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305113 QDF_TRACE(QDF_MODULE_ID_HDD,
5114 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005115 "%s: Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink ",
5116 __func__);
5117 ret = -EINVAL;
5118 goto exit;
5119 }
5120 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
5121 pHddCtx->miracast_value = filterType;
5122
5123 ret_status = sme_set_miracast(hHal, filterType);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305124 if (QDF_STATUS_SUCCESS != ret_status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005125 hddLog(LOGE, "Failed to set miracast");
5126 return -EBUSY;
5127 }
5128
Tushnim Bhattacharyyaca50b322015-12-28 17:14:36 -08005129 if (cds_is_mcc_in_24G())
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005130 return cds_set_mas(adapter, filterType);
5131
5132exit:
5133 return ret;
5134}
5135
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005136/* Function header is left blank intentionally */
5137static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie,
5138 int32_t *oui_length, int32_t limit)
5139{
5140 uint8_t len;
5141 uint8_t data;
5142
5143 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
5144 command++;
5145 limit--;
5146 }
5147
5148 len = 2;
5149
5150 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
5151 (limit > 1)) {
5152 sscanf(command, "%02x", (unsigned int *)&data);
5153 ie[len++] = data;
5154 command += 2;
5155 limit -= 2;
5156 }
5157
5158 *oui_length = len - 2;
5159
5160 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
5161 command++;
5162 limit--;
5163 }
5164
5165 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
5166 (limit > 1)) {
5167 sscanf(command, "%02x", (unsigned int *)&data);
5168 ie[len++] = data;
5169 command += 2;
5170 limit -= 2;
5171 }
5172
5173 ie[0] = IE_EID_VENDOR;
5174 ie[1] = len - 2;
5175
5176 return len;
5177}
5178
5179/**
5180 * drv_cmd_set_ibss_beacon_oui_data() - set ibss oui data command
5181 * @adapter: Pointer to adapter
5182 * @hdd_ctx: Pointer to HDD context
5183 * @command: Pointer to command string
5184 * @command_len : Command length
5185 * @priv_data : Pointer to priv data
5186 *
5187 * Return:
5188 * int status code
5189 */
5190static int drv_cmd_set_ibss_beacon_oui_data(hdd_adapter_t *adapter,
5191 hdd_context_t *hdd_ctx,
5192 uint8_t *command,
5193 uint8_t command_len,
5194 hdd_priv_data_t *priv_data)
5195{
5196 int i = 0;
5197 int status;
5198 int ret = 0;
5199 uint8_t *ibss_ie;
5200 int32_t oui_length = 0;
5201 uint32_t ibss_ie_length;
5202 uint8_t *value = command;
5203 tSirModifyIE ibssModifyIE;
5204 tCsrRoamProfile *pRoamProfile;
5205 hdd_wext_state_t *pWextState;
5206
5207
Krunal Sonibe766b02016-03-10 13:00:44 -08005208 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005209 hddLog(LOG1, FL("Device_mode %s(%d) not IBSS"),
5210 hdd_device_mode_to_string(adapter->device_mode),
5211 adapter->device_mode);
5212 return ret;
5213 }
5214
5215 pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(adapter);
5216
5217 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5218 "%s: received command %s", __func__,
5219 ((char *)value));
5220
5221
5222 /* validate argument of command */
5223 if (strlen(value) <= command_len) {
5224 hddLog(LOGE,
5225 FL("No arguements in command length %zu"),
5226 strlen(value));
5227 ret = -EFAULT;
5228 goto exit;
5229 }
5230
5231 /* moving to arguments of commands */
5232 value = value + command_len;
5233 command_len = strlen(value);
5234
5235 /* oui_data can't be less than 3 bytes */
5236 if (command_len < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
5237 hddLog(LOGE,
5238 FL("Invalid SETIBSSBEACONOUIDATA command length %d"),
5239 command_len);
5240 ret = -EFAULT;
5241 goto exit;
5242 }
5243
5244 ibss_ie = qdf_mem_malloc(command_len);
5245 if (!ibss_ie) {
5246 hddLog(LOGE,
5247 FL("Could not allocate memory for command length %d"),
5248 command_len);
5249 ret = -ENOMEM;
5250 goto exit;
5251 }
5252 qdf_mem_zero(ibss_ie, command_len);
5253
5254 ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie,
5255 &oui_length,
5256 command_len);
5257 if (ibss_ie_length <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
5258 hddLog(LOGE, FL("Could not parse command %s return length %d"),
5259 value, ibss_ie_length);
5260 ret = -EFAULT;
5261 qdf_mem_free(ibss_ie);
5262 goto exit;
5263 }
5264
5265 pRoamProfile = &pWextState->roamProfile;
5266
5267 qdf_copy_macaddr(&ibssModifyIE.bssid,
5268 pRoamProfile->BSSIDs.bssid);
5269
5270 ibssModifyIE.smeSessionId = adapter->sessionId;
5271 ibssModifyIE.notify = true;
5272 ibssModifyIE.ieID = IE_EID_VENDOR;
5273 ibssModifyIE.ieIDLen = ibss_ie_length;
5274 ibssModifyIE.ieBufferlength = ibss_ie_length;
5275 ibssModifyIE.pIEBuffer = ibss_ie;
5276 ibssModifyIE.oui_length = oui_length;
5277
5278 hddLog(LOGW, FL("ibss_ie length %d oui_length %d ibss_ie:"),
5279 ibss_ie_length, oui_length);
5280 while (i < ibssModifyIE.ieBufferlength)
5281 hddLog(LOGW, FL("0x%x"), ibss_ie[i++]);
5282
5283 /* Probe Bcn modification */
5284 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
5285 &ibssModifyIE, eUPDATE_IE_PROBE_BCN);
5286
5287 /* Populating probe resp frame */
5288 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
5289 &ibssModifyIE, eUPDATE_IE_PROBE_RESP);
5290
5291 qdf_mem_free(ibss_ie);
5292
5293 status = sme_send_cesium_enable_ind((tHalHandle)(hdd_ctx->hHal),
5294 adapter->sessionId);
5295 if (QDF_STATUS_SUCCESS != status) {
5296 QDF_TRACE(QDF_MODULE_ID_HDD,
5297 QDF_TRACE_LEVEL_ERROR,
5298 "Could not send cesium enable indication %d",
5299 status);
5300 ret = -EINVAL;
5301 goto exit;
5302 }
5303
5304exit:
5305 return ret;
5306}
5307
5308static int drv_cmd_set_rmc_enable(hdd_adapter_t *adapter,
5309 hdd_context_t *hdd_ctx,
5310 uint8_t *command,
5311 uint8_t command_len,
5312 hdd_priv_data_t *priv_data)
5313{
5314 int ret = 0;
5315 uint8_t *value = command;
5316 uint8_t ucRmcEnable = 0;
5317 int status;
5318
Krunal Sonibe766b02016-03-10 13:00:44 -08005319 if ((QDF_IBSS_MODE != adapter->device_mode) &&
5320 (QDF_SAP_MODE != adapter->device_mode)) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005321 hddLog(LOGE,
5322 "Received SETRMCENABLE cmd in invalid mode %s(%d)",
5323 hdd_device_mode_to_string(adapter->device_mode),
5324 adapter->device_mode);
5325 hddLog(LOGE,
5326 "SETRMCENABLE cmd is allowed only in IBSS/SOFTAP mode");
5327 ret = -EINVAL;
5328 goto exit;
5329 }
5330
5331 status = hdd_parse_setrmcenable_command(value, &ucRmcEnable);
5332 if (status) {
5333 QDF_TRACE(QDF_MODULE_ID_HDD,
5334 QDF_TRACE_LEVEL_ERROR,
5335 "Invalid SETRMCENABLE command ");
5336 ret = -EINVAL;
5337 goto exit;
5338 }
5339
5340 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5341 "%s: ucRmcEnable %d ", __func__, ucRmcEnable);
5342
5343 if (true == ucRmcEnable) {
5344 status = sme_enable_rmc((tHalHandle)
5345 (hdd_ctx->hHal),
5346 adapter->sessionId);
5347 } else if (false == ucRmcEnable) {
5348 status = sme_disable_rmc((tHalHandle)
5349 (hdd_ctx->hHal),
5350 adapter->sessionId);
5351 } else {
5352 QDF_TRACE(QDF_MODULE_ID_HDD,
5353 QDF_TRACE_LEVEL_ERROR,
5354 "Invalid SETRMCENABLE command %d",
5355 ucRmcEnable);
5356 ret = -EINVAL;
5357 goto exit;
5358 }
5359
5360 if (QDF_STATUS_SUCCESS != status) {
5361 QDF_TRACE(QDF_MODULE_ID_HDD,
5362 QDF_TRACE_LEVEL_ERROR,
5363 "%s: SETRMC %d failed status %d",
5364 __func__, ucRmcEnable, status);
5365 ret = -EINVAL;
5366 goto exit;
5367 }
5368
5369exit:
5370 return ret;
5371}
5372
5373static int drv_cmd_set_rmc_action_period(hdd_adapter_t *adapter,
5374 hdd_context_t *hdd_ctx,
5375 uint8_t *command,
5376 uint8_t command_len,
5377 hdd_priv_data_t *priv_data)
5378{
5379 int ret = 0;
5380 uint8_t *value = command;
5381 uint32_t uActionPeriod = 0;
5382 int status;
5383
Krunal Sonibe766b02016-03-10 13:00:44 -08005384 if ((QDF_IBSS_MODE != adapter->device_mode) &&
5385 (QDF_SAP_MODE != adapter->device_mode)) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005386 hddLog(LOGE, "Received SETRMC cmd in invalid mode %s(%d)",
5387 hdd_device_mode_to_string(adapter->device_mode),
5388 adapter->device_mode);
5389 hddLog(LOGE,
5390 "SETRMC cmd is allowed only in IBSS/SOFTAP mode");
5391 ret = -EINVAL;
5392 goto exit;
5393 }
5394
5395 status = hdd_parse_setrmcactionperiod_command(value, &uActionPeriod);
5396 if (status) {
5397 QDF_TRACE(QDF_MODULE_ID_HDD,
5398 QDF_TRACE_LEVEL_ERROR,
5399 "Invalid SETRMCACTIONPERIOD command ");
5400 ret = -EINVAL;
5401 goto exit;
5402 }
5403
5404 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5405 "%s: uActionPeriod %d ", __func__,
5406 uActionPeriod);
5407
5408 if (sme_cfg_set_int(hdd_ctx->hHal,
5409 WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY,
5410 uActionPeriod)) {
5411 QDF_TRACE(QDF_MODULE_ID_HDD,
5412 QDF_TRACE_LEVEL_ERROR,
5413 "%s: Could not set SETRMCACTIONPERIOD %d",
5414 __func__, uActionPeriod);
5415 ret = -EINVAL;
5416 goto exit;
5417 }
5418
5419 status = sme_send_rmc_action_period((tHalHandle)(hdd_ctx->hHal),
5420 adapter->sessionId);
5421 if (QDF_STATUS_SUCCESS != status) {
5422 QDF_TRACE(QDF_MODULE_ID_HDD,
5423 QDF_TRACE_LEVEL_ERROR,
5424 "Could not send cesium enable indication %d",
5425 status);
5426 ret = -EINVAL;
5427 goto exit;
5428 }
5429
5430exit:
5431 return ret;
5432}
5433
5434static int drv_cmd_get_ibss_peer_info_all(hdd_adapter_t *adapter,
5435 hdd_context_t *hdd_ctx,
5436 uint8_t *command,
5437 uint8_t command_len,
5438 hdd_priv_data_t *priv_data)
5439{
5440 int ret = 0;
5441 int status = QDF_STATUS_SUCCESS;
5442 hdd_station_ctx_t *pHddStaCtx = NULL;
5443 char *extra = NULL;
5444 int idx = 0;
5445 int length = 0;
5446 struct qdf_mac_addr *macAddr;
5447 uint32_t txRateMbps = 0;
5448 uint32_t numOfBytestoPrint = 0;
5449
Krunal Sonibe766b02016-03-10 13:00:44 -08005450 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005451 hdd_warn("Unsupported in mode %s(%d)",
5452 hdd_device_mode_to_string(adapter->device_mode),
5453 adapter->device_mode);
5454 return -EINVAL;
5455 }
5456
5457 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5458 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5459 "%s: Received GETIBSSPEERINFOALL Command",
5460 __func__);
5461
5462 /* Handle the command */
5463 status = hdd_cfg80211_get_ibss_peer_info_all(adapter);
5464 if (QDF_STATUS_SUCCESS == status) {
5465 /*
5466 * The variable extra needed to be allocated on the heap since
5467 * amount of memory required to copy the data for 32 devices
5468 * exceeds the size of 1024 bytes of default stack size. On
5469 * 64 bit devices, the default max stack size of 2048 bytes
5470 */
5471 extra = kmalloc(WLAN_MAX_BUF_SIZE, GFP_KERNEL);
5472
5473 if (NULL == extra) {
5474 QDF_TRACE(QDF_MODULE_ID_HDD,
5475 QDF_TRACE_LEVEL_ERROR,
5476 "%s:kmalloc failed",
5477 __func__);
5478 ret = -EINVAL;
5479 goto exit;
5480 }
5481
5482 /* Copy number of stations */
5483 length = scnprintf(extra, WLAN_MAX_BUF_SIZE, "%d ",
5484 pHddStaCtx->ibss_peer_info.
5485 numIBSSPeers);
5486 numOfBytestoPrint = length;
5487 for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numIBSSPeers;
5488 idx++) {
5489 macAddr = hdd_wlan_get_ibss_mac_addr_from_staid
5490 (adapter,
5491 pHddStaCtx->ibss_peer_info.
5492 ibssPeerList[idx].staIdx);
5493 if (NULL != macAddr) {
5494 txRateMbps = ((pHddStaCtx->
5495 ibss_peer_info.
5496 ibssPeerList[idx].txRate)
5497 * 500 * 1000) / 1000000;
5498 length += scnprintf((extra + length),
5499 WLAN_MAX_BUF_SIZE - length,
5500 "%02x:%02x:%02x:%02x:%02x:%02x %d %d ",
5501 macAddr->bytes[0],
5502 macAddr->bytes[1],
5503 macAddr->bytes[2],
5504 macAddr->bytes[3],
5505 macAddr->bytes[4],
5506 macAddr->bytes[5],
5507 (int)txRateMbps,
5508 (int)pHddStaCtx->
5509 ibss_peer_info.
5510 ibssPeerList[idx].
5511 rssi);
5512 } else {
5513 QDF_TRACE(QDF_MODULE_ID_HDD,
5514 QDF_TRACE_LEVEL_ERROR,
5515 "%s: MAC ADDR is NULL for staIdx: %d",
5516 __func__,
5517 pHddStaCtx->
5518 ibss_peer_info.
5519 ibssPeerList[idx].
5520 staIdx);
5521 }
5522
5523 /*
5524 * QDF_TRACE() macro has limitation of 512 bytes
5525 * for the print buffer. Hence printing the data
5526 * in two chunks. The first chunk will have the data
5527 * for 16 devices and the second chunk will
5528 * have the rest.
5529 */
5530 if (idx < NUM_OF_STA_DATA_TO_PRINT)
5531 numOfBytestoPrint = length;
5532 }
5533
5534 /*
5535 * Copy the data back into buffer, if the data to copy is
5536 * more than 512 bytes than we will split the data and do
5537 * it in two shots
5538 */
5539 if (copy_to_user(priv_data->buf, extra, numOfBytestoPrint)) {
5540 QDF_TRACE(QDF_MODULE_ID_HDD,
5541 QDF_TRACE_LEVEL_ERROR,
5542 "%s: Copy into user data buffer failed ",
5543 __func__);
5544 ret = -EFAULT;
5545 goto exit;
5546 }
5547
5548 priv_data->buf[numOfBytestoPrint] = '\0';
5549 QDF_TRACE(QDF_MODULE_ID_HDD,
5550 QDF_TRACE_LEVEL_INFO_MED, "%s",
5551 priv_data->buf);
5552
5553 if (length > numOfBytestoPrint) {
5554 if (copy_to_user
5555 (priv_data->buf + numOfBytestoPrint,
5556 extra + numOfBytestoPrint,
5557 length - numOfBytestoPrint + 1)) {
5558 QDF_TRACE(QDF_MODULE_ID_HDD,
5559 QDF_TRACE_LEVEL_ERROR,
5560 "%s: Copy into user data buffer failed ",
5561 __func__);
5562 ret = -EFAULT;
5563 goto exit;
5564 }
5565 QDF_TRACE(QDF_MODULE_ID_HDD,
5566 QDF_TRACE_LEVEL_INFO_MED,
5567 "%s",
5568 &priv_data->buf[numOfBytestoPrint]);
5569 }
5570
5571 /* Free temporary buffer */
5572 kfree(extra);
5573 } else {
5574 /* Command failed, log error */
5575 QDF_TRACE(QDF_MODULE_ID_HDD,
5576 QDF_TRACE_LEVEL_ERROR,
5577 "%s: GETIBSSPEERINFOALL command failed with status code %d",
5578 __func__, status);
5579 ret = -EINVAL;
5580 goto exit;
5581 }
5582 ret = 0;
5583
5584exit:
5585 return ret;
5586}
5587
5588/* Peer Info <Peer Addr> command */
5589static int drv_cmd_get_ibss_peer_info(hdd_adapter_t *adapter,
5590 hdd_context_t *hdd_ctx,
5591 uint8_t *command,
5592 uint8_t command_len,
5593 hdd_priv_data_t *priv_data)
5594{
5595 int ret = 0;
5596 uint8_t *value = command;
5597 QDF_STATUS status;
5598 hdd_station_ctx_t *pHddStaCtx = NULL;
5599 char extra[128] = { 0 };
5600 uint32_t length = 0;
5601 uint8_t staIdx = 0;
5602 uint32_t txRateMbps = 0;
5603 uint32_t txRate;
5604 struct qdf_mac_addr peerMacAddr;
5605
Krunal Sonibe766b02016-03-10 13:00:44 -08005606 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005607 hdd_warn("Unsupported in mode %s(%d)",
5608 hdd_device_mode_to_string(adapter->device_mode),
5609 adapter->device_mode);
5610 return -EINVAL;
5611 }
5612
5613 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5614
5615 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5616 "%s: Received GETIBSSPEERINFO Command",
5617 __func__);
5618
5619 /* if there are no peers, no need to continue with the command */
5620 if (eConnectionState_IbssConnected !=
5621 pHddStaCtx->conn_info.connState) {
5622 QDF_TRACE(QDF_MODULE_ID_HDD,
5623 QDF_TRACE_LEVEL_INFO,
5624 "%s:No IBSS Peers coalesced",
5625 __func__);
5626 ret = -EINVAL;
5627 goto exit;
5628 }
5629
5630 /* Parse the incoming command buffer */
5631 status = hdd_parse_get_ibss_peer_info(value, &peerMacAddr);
5632 if (QDF_STATUS_SUCCESS != status) {
5633 QDF_TRACE(QDF_MODULE_ID_HDD,
5634 QDF_TRACE_LEVEL_ERROR,
5635 "%s: Invalid GETIBSSPEERINFO command",
5636 __func__);
5637 ret = -EINVAL;
5638 goto exit;
5639 }
5640
5641 /* Get station index for the peer mac address */
5642 hdd_ibss_get_sta_id(pHddStaCtx, &peerMacAddr, &staIdx);
5643
5644 if (staIdx > MAX_IBSS_PEERS) {
5645 QDF_TRACE(QDF_MODULE_ID_HDD,
5646 QDF_TRACE_LEVEL_ERROR,
5647 "%s: Invalid StaIdx %d returned",
5648 __func__, staIdx);
5649 ret = -EINVAL;
5650 goto exit;
5651 }
5652
5653 /* Handle the command */
5654 status = hdd_cfg80211_get_ibss_peer_info(adapter, staIdx);
5655 if (QDF_STATUS_SUCCESS == status) {
5656 txRate = pHddStaCtx->ibss_peer_info.ibssPeerList[0].txRate;
5657 txRateMbps = (txRate * 500 * 1000) / 1000000;
5658
5659 length = scnprintf(extra, sizeof(extra), "%d %d",
5660 (int)txRateMbps,
5661 (int)pHddStaCtx->ibss_peer_info.
5662 ibssPeerList[0].rssi);
5663
5664 /* Copy the data back into buffer */
5665 if (copy_to_user(priv_data->buf, &extra, length + 1)) {
5666 QDF_TRACE(QDF_MODULE_ID_HDD,
5667 QDF_TRACE_LEVEL_ERROR,
5668 "%s: copy data to user buffer failed GETIBSSPEERINFO command",
5669 __func__);
5670 ret = -EFAULT;
5671 goto exit;
5672 }
5673 } else {
5674 /* Command failed, log error */
5675 QDF_TRACE(QDF_MODULE_ID_HDD,
5676 QDF_TRACE_LEVEL_ERROR,
5677 "%s: GETIBSSPEERINFO command failed with status code %d",
5678 __func__, status);
5679 ret = -EINVAL;
5680 goto exit;
5681 }
5682
5683 /* Success ! */
5684 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_MED,
5685 "%s", priv_data->buf);
5686 ret = 0;
5687
5688exit:
5689 return ret;
5690}
5691
5692static int drv_cmd_set_rmc_tx_rate(hdd_adapter_t *adapter,
5693 hdd_context_t *hdd_ctx,
5694 uint8_t *command,
5695 uint8_t command_len,
5696 hdd_priv_data_t *priv_data)
5697{
5698 int ret = 0;
5699 uint8_t *value = command;
5700 uint32_t uRate = 0;
5701 tTxrateinfoflags txFlags = 0;
5702 tSirRateUpdateInd rateUpdateParams = {0};
5703 int status;
5704 struct hdd_config *pConfig = hdd_ctx->config;
5705
Krunal Sonibe766b02016-03-10 13:00:44 -08005706 if ((QDF_IBSS_MODE != adapter->device_mode) &&
5707 (QDF_SAP_MODE != adapter->device_mode)) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005708 hddLog(LOGE,
5709 "Received SETRMCTXRATE cmd in invalid mode %s(%d)",
5710 hdd_device_mode_to_string(adapter->device_mode),
5711 adapter->device_mode);
5712 hddLog(LOGE,
5713 "SETRMCTXRATE cmd is allowed only in IBSS/SOFTAP mode");
5714 ret = -EINVAL;
5715 goto exit;
5716 }
5717
5718 status = hdd_parse_setrmcrate_command(value, &uRate, &txFlags);
5719 if (status) {
5720 QDF_TRACE(QDF_MODULE_ID_HDD,
5721 QDF_TRACE_LEVEL_ERROR,
5722 "Invalid SETRMCTXRATE command ");
5723 ret = -EINVAL;
5724 goto exit;
5725 }
5726 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5727 "%s: uRate %d ", __func__, uRate);
5728 /* -1 implies ignore this param */
5729 rateUpdateParams.ucastDataRate = -1;
5730
5731 /*
5732 * Fill the user specifieed RMC rate param
5733 * and the derived tx flags.
5734 */
5735 rateUpdateParams.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
5736 rateUpdateParams.reliableMcastDataRate = uRate;
5737 rateUpdateParams.reliableMcastDataRateTxFlag = txFlags;
5738 rateUpdateParams.dev_mode = adapter->device_mode;
5739 rateUpdateParams.bcastDataRate = -1;
5740 memcpy(rateUpdateParams.bssid.bytes,
5741 adapter->macAddressCurrent.bytes,
5742 sizeof(rateUpdateParams.bssid));
5743 status = sme_send_rate_update_ind((tHalHandle) (hdd_ctx->hHal),
5744 &rateUpdateParams);
5745
5746exit:
5747 return ret;
5748}
5749
5750static int drv_cmd_set_ibss_tx_fail_event(hdd_adapter_t *adapter,
5751 hdd_context_t *hdd_ctx,
5752 uint8_t *command,
5753 uint8_t command_len,
5754 hdd_priv_data_t *priv_data)
5755{
5756 int ret = 0;
5757 char *value;
5758 uint8_t tx_fail_count = 0;
5759 uint16_t pid = 0;
5760
5761 value = command;
5762
5763 ret = hdd_parse_ibsstx_fail_event_params(value, &tx_fail_count, &pid);
5764
5765 if (0 != ret) {
5766 hddLog(QDF_TRACE_LEVEL_INFO,
5767 "%s: Failed to parse SETIBSSTXFAILEVENT arguments",
5768 __func__);
5769 goto exit;
5770 }
5771
5772 hddLog(QDF_TRACE_LEVEL_INFO,
5773 "%s: tx_fail_cnt=%hhu, pid=%hu", __func__,
5774 tx_fail_count, pid);
5775
5776 if (0 == tx_fail_count) {
5777 /* Disable TX Fail Indication */
5778 if (QDF_STATUS_SUCCESS ==
5779 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5780 tx_fail_count,
5781 NULL)) {
5782 cesium_pid = 0;
5783 } else {
5784 QDF_TRACE(QDF_MODULE_ID_HDD,
5785 QDF_TRACE_LEVEL_ERROR,
5786 "%s: failed to disable TX Fail Event ",
5787 __func__);
5788 ret = -EINVAL;
5789 }
5790 } else {
5791 if (QDF_STATUS_SUCCESS ==
5792 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5793 tx_fail_count,
5794 (void *)hdd_tx_fail_ind_callback)) {
5795 cesium_pid = pid;
5796 QDF_TRACE(QDF_MODULE_ID_HDD,
5797 QDF_TRACE_LEVEL_INFO,
5798 "%s: Registered Cesium pid %u",
5799 __func__, cesium_pid);
5800 } else {
5801 QDF_TRACE(QDF_MODULE_ID_HDD,
5802 QDF_TRACE_LEVEL_ERROR,
5803 "%s: Failed to enable TX Fail Monitoring",
5804 __func__);
5805 ret = -EINVAL;
5806 }
5807 }
5808
5809exit:
5810 return ret;
5811}
5812
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005813#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
5814static int drv_cmd_set_ccx_roam_scan_channels(hdd_adapter_t *adapter,
5815 hdd_context_t *hdd_ctx,
5816 uint8_t *command,
5817 uint8_t command_len,
5818 hdd_priv_data_t *priv_data)
5819{
5820 int ret = 0;
5821 uint8_t *value = command;
5822 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
5823 uint8_t numChannels = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305824 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005825
5826 ret = hdd_parse_channellist(value, ChannelList, &numChannels);
5827 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305828 QDF_TRACE(QDF_MODULE_ID_HDD,
5829 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005830 "%s: Failed to parse channel list information",
5831 __func__);
5832 goto exit;
5833 }
5834 if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305835 QDF_TRACE(QDF_MODULE_ID_HDD,
5836 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005837 "%s: number of channels (%d) supported exceeded max (%d)",
5838 __func__,
5839 numChannels,
5840 WNI_CFG_VALID_CHANNEL_LIST_LEN);
5841 ret = -EINVAL;
5842 goto exit;
5843 }
5844 status = sme_set_ese_roam_scan_channel_list(hdd_ctx->hHal,
5845 adapter->sessionId,
5846 ChannelList,
5847 numChannels);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305848 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305849 QDF_TRACE(QDF_MODULE_ID_HDD,
5850 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005851 "%s: Failed to update channel list information",
5852 __func__);
5853 ret = -EINVAL;
5854 goto exit;
5855 }
5856
5857exit:
5858 return ret;
5859}
5860
5861static int drv_cmd_get_tsm_stats(hdd_adapter_t *adapter,
5862 hdd_context_t *hdd_ctx,
5863 uint8_t *command,
5864 uint8_t command_len,
5865 hdd_priv_data_t *priv_data)
5866{
5867 int ret = 0;
5868 uint8_t *value = command;
5869 char extra[128] = { 0 };
5870 int len = 0;
5871 uint8_t tid = 0;
5872 hdd_station_ctx_t *pHddStaCtx;
5873 tAniTrafStrmMetrics tsm_metrics;
5874
Krunal Sonibe766b02016-03-10 13:00:44 -08005875 if ((QDF_STA_MODE != adapter->device_mode) &&
5876 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005877 hdd_warn("Unsupported in mode %s(%d)",
5878 hdd_device_mode_to_string(adapter->device_mode),
5879 adapter->device_mode);
5880 return -EINVAL;
5881 }
5882
5883 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5884
5885 /* if not associated, return error */
5886 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305887 QDF_TRACE(QDF_MODULE_ID_HDD,
5888 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005889 "%s:Not associated!", __func__);
5890 ret = -EINVAL;
5891 goto exit;
5892 }
5893
5894 /* Move pointer to ahead of GETTSMSTATS<delimiter> */
5895 value = value + command_len + 1;
5896
5897 /* Convert the value from ascii to integer */
5898 ret = kstrtou8(value, 10, &tid);
5899 if (ret < 0) {
5900 /*
5901 * If the input value is greater than max value of datatype,
5902 * then also kstrtou8 fails
5903 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305904 QDF_TRACE(QDF_MODULE_ID_HDD,
5905 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005906 "%s: kstrtou8 failed range [%d - %d]",
5907 __func__, TID_MIN_VALUE,
5908 TID_MAX_VALUE);
5909 ret = -EINVAL;
5910 goto exit;
5911 }
5912 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305913 QDF_TRACE(QDF_MODULE_ID_HDD,
5914 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005915 "tid value %d is out of range (Min: %d Max: %d)",
5916 tid, TID_MIN_VALUE, TID_MAX_VALUE);
5917 ret = -EINVAL;
5918 goto exit;
5919 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305920 QDF_TRACE(QDF_MODULE_ID_HDD,
5921 QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005922 "%s: Received Command to get tsm stats tid = %d",
5923 __func__, tid);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305924 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005925 hdd_get_tsm_stats(adapter, tid, &tsm_metrics)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305926 QDF_TRACE(QDF_MODULE_ID_HDD,
5927 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005928 "%s: failed to get tsm stats",
5929 __func__);
5930 ret = -EFAULT;
5931 goto exit;
5932 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305933 QDF_TRACE(QDF_MODULE_ID_HDD,
5934 QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005935 "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)",
5936 tsm_metrics.UplinkPktQueueDly,
5937 tsm_metrics.UplinkPktQueueDlyHist[0],
5938 tsm_metrics.UplinkPktQueueDlyHist[1],
5939 tsm_metrics.UplinkPktQueueDlyHist[2],
5940 tsm_metrics.UplinkPktQueueDlyHist[3],
5941 tsm_metrics.UplinkPktTxDly,
5942 tsm_metrics.UplinkPktLoss,
5943 tsm_metrics.UplinkPktCount,
5944 tsm_metrics.RoamingCount,
5945 tsm_metrics.RoamingDly);
5946 /*
5947 * Output TSM stats is of the format
5948 * GETTSMSTATS [PktQueueDly]
5949 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
5950 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800
5951 */
5952 len = scnprintf(extra,
5953 sizeof(extra),
5954 "%s %d %d:%d:%d:%d %u %d %d %d %d",
5955 command,
5956 tsm_metrics.UplinkPktQueueDly,
5957 tsm_metrics.UplinkPktQueueDlyHist[0],
5958 tsm_metrics.UplinkPktQueueDlyHist[1],
5959 tsm_metrics.UplinkPktQueueDlyHist[2],
5960 tsm_metrics.UplinkPktQueueDlyHist[3],
5961 tsm_metrics.UplinkPktTxDly,
5962 tsm_metrics.UplinkPktLoss,
5963 tsm_metrics.UplinkPktCount,
5964 tsm_metrics.RoamingCount,
5965 tsm_metrics.RoamingDly);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305966 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005967 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305968 QDF_TRACE(QDF_MODULE_ID_HDD,
5969 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005970 "%s: failed to copy data to user buffer",
5971 __func__);
5972 ret = -EFAULT;
5973 goto exit;
5974 }
5975
5976exit:
5977 return ret;
5978}
5979
5980static int drv_cmd_set_cckm_ie(hdd_adapter_t *adapter,
5981 hdd_context_t *hdd_ctx,
5982 uint8_t *command,
5983 uint8_t command_len,
5984 hdd_priv_data_t *priv_data)
5985{
5986 int ret;
5987 uint8_t *value = command;
5988 uint8_t *cckmIe = NULL;
5989 uint8_t cckmIeLen = 0;
5990
5991 ret = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
5992 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05305993 QDF_TRACE(QDF_MODULE_ID_HDD,
5994 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005995 "%s: Failed to parse cckm ie data",
5996 __func__);
5997 goto exit;
5998 }
5999
6000 if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306001 QDF_TRACE(QDF_MODULE_ID_HDD,
6002 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006003 "%s: CCKM Ie input length is more than max[%d]",
6004 __func__, DOT11F_IE_RSN_MAX_LEN);
6005 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306006 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006007 cckmIe = NULL;
6008 }
6009 ret = -EINVAL;
6010 goto exit;
6011 }
6012
6013 sme_set_cckm_ie(hdd_ctx->hHal, adapter->sessionId,
6014 cckmIe, cckmIeLen);
6015 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306016 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006017 cckmIe = NULL;
6018 }
6019
6020exit:
6021 return ret;
6022}
6023
6024static int drv_cmd_ccx_beacon_req(hdd_adapter_t *adapter,
6025 hdd_context_t *hdd_ctx,
6026 uint8_t *command,
6027 uint8_t command_len,
6028 hdd_priv_data_t *priv_data)
6029{
6030 int ret;
6031 uint8_t *value = command;
6032 tCsrEseBeaconReq eseBcnReq;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306033 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006034
Krunal Sonibe766b02016-03-10 13:00:44 -08006035 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006036 hdd_warn("Unsupported in mode %s(%d)",
6037 hdd_device_mode_to_string(adapter->device_mode),
6038 adapter->device_mode);
6039 return -EINVAL;
6040 }
6041
6042 ret = hdd_parse_ese_beacon_req(value, &eseBcnReq);
6043 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306044 QDF_TRACE(QDF_MODULE_ID_HDD,
6045 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006046 "%s: Failed to parse ese beacon req",
6047 __func__);
6048 goto exit;
6049 }
6050
6051 if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306052 hddLog(QDF_TRACE_LEVEL_INFO, FL("Not associated"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006053 hdd_indicate_ese_bcn_report_no_results(adapter,
6054 eseBcnReq.bcnReq[0].measurementToken,
6055 0x02, /* BIT(1) set for measurement done */
6056 0); /* no BSS */
6057 goto exit;
6058 }
6059
6060 status = sme_set_ese_beacon_request(hdd_ctx->hHal,
6061 adapter->sessionId,
6062 &eseBcnReq);
6063
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306064 if (QDF_STATUS_E_RESOURCES == status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306065 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006066 FL("sme_set_ese_beacon_request failed (%d), a request already in progress"),
6067 status);
6068 ret = -EBUSY;
6069 goto exit;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306070 } else if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306071 QDF_TRACE(QDF_MODULE_ID_HDD,
6072 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006073 "%s: sme_set_ese_beacon_request failed (%d)",
6074 __func__, status);
6075 ret = -EINVAL;
6076 goto exit;
6077 }
6078
6079exit:
6080 return ret;
6081}
6082#endif /* #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) */
6083
6084static int drv_cmd_set_mc_rate(hdd_adapter_t *adapter,
6085 hdd_context_t *hdd_ctx,
6086 uint8_t *command,
6087 uint8_t command_len,
6088 hdd_priv_data_t *priv_data)
6089{
6090 int ret = 0;
6091 uint8_t *value = command;
6092 int targetRate;
6093
6094 /* input value is in units of hundred kbps */
6095
6096 /* Move pointer to ahead of SETMCRATE<delimiter> */
6097 value = value + command_len + 1;
6098
6099 /* Convert the value from ascii to integer, decimal base */
6100 ret = kstrtouint(value, 10, &targetRate);
6101
6102 ret = wlan_hdd_set_mc_rate(adapter, targetRate);
6103 return ret;
6104}
6105
6106static int drv_cmd_max_tx_power(hdd_adapter_t *adapter,
6107 hdd_context_t *hdd_ctx,
6108 uint8_t *command,
6109 uint8_t command_len,
6110 hdd_priv_data_t *priv_data)
6111{
6112 int ret = 0;
6113 int status;
6114 int txPower;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306115 QDF_STATUS qdf_status;
6116 QDF_STATUS smeStatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006117 uint8_t *value = command;
Anurag Chouhan6d760662016-02-20 16:05:43 +05306118 struct qdf_mac_addr bssid = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
6119 struct qdf_mac_addr selfMac = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006120 hdd_adapter_list_node_t *pAdapterNode = NULL;
6121 hdd_adapter_list_node_t *pNext = NULL;
6122
6123 status = hdd_parse_setmaxtxpower_command(value, &txPower);
6124 if (status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306125 QDF_TRACE(QDF_MODULE_ID_HDD,
6126 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006127 "Invalid MAXTXPOWER command ");
6128 ret = -EINVAL;
6129 goto exit;
6130 }
6131
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306132 qdf_status = hdd_get_front_adapter(hdd_ctx, &pAdapterNode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006133 while (NULL != pAdapterNode
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306134 && QDF_STATUS_SUCCESS == qdf_status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006135 adapter = pAdapterNode->pAdapter;
6136 /* Assign correct self MAC address */
Anurag Chouhanc5548422016-02-24 18:33:27 +05306137 qdf_copy_macaddr(&bssid,
Srinivas Girigowda97215232015-09-24 12:26:28 -07006138 &adapter->macAddressCurrent);
Anurag Chouhanc5548422016-02-24 18:33:27 +05306139 qdf_copy_macaddr(&selfMac,
Srinivas Girigowda97215232015-09-24 12:26:28 -07006140 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006141
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306142 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006143 "Device mode %d max tx power %d selfMac: " MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
6144 adapter->device_mode, txPower,
Srinivas Girigowda97215232015-09-24 12:26:28 -07006145 MAC_ADDR_ARRAY(selfMac.bytes),
6146 MAC_ADDR_ARRAY(bssid.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006147
Srinivas Girigowda97215232015-09-24 12:26:28 -07006148 smeStatus = sme_set_max_tx_power(hdd_ctx->hHal,
6149 bssid, selfMac, txPower);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306150 if (QDF_STATUS_SUCCESS != status) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306151 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006152 "%s:Set max tx power failed",
6153 __func__);
6154 ret = -EINVAL;
6155 goto exit;
6156 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306157 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006158 "%s: Set max tx power success",
6159 __func__);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306160 qdf_status = hdd_get_next_adapter(hdd_ctx, pAdapterNode,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006161 &pNext);
6162 pAdapterNode = pNext;
6163 }
6164
6165exit:
6166 return ret;
6167}
6168
6169static int drv_cmd_set_dfs_scan_mode(hdd_adapter_t *adapter,
6170 hdd_context_t *hdd_ctx,
6171 uint8_t *command,
6172 uint8_t command_len,
6173 hdd_priv_data_t *priv_data)
6174{
6175 int ret = 0;
6176 uint8_t *value = command;
6177 uint8_t dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
6178
6179 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
6180 value = value + command_len + 1;
6181
6182 /* Convert the value from ascii to integer */
6183 ret = kstrtou8(value, 10, &dfsScanMode);
6184 if (ret < 0) {
6185 /*
6186 * If the input value is greater than max value of datatype,
6187 * then also kstrtou8 fails
6188 */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306189 QDF_TRACE(QDF_MODULE_ID_HDD,
6190 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006191 "%s: kstrtou8 failed range [%d - %d]",
6192 __func__, CFG_ROAMING_DFS_CHANNEL_MIN,
6193 CFG_ROAMING_DFS_CHANNEL_MAX);
6194 ret = -EINVAL;
6195 goto exit;
6196 }
6197
6198 if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
6199 (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306200 QDF_TRACE(QDF_MODULE_ID_HDD,
6201 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006202 "dfsScanMode value %d is out of range (Min: %d Max: %d)",
6203 dfsScanMode,
6204 CFG_ROAMING_DFS_CHANNEL_MIN,
6205 CFG_ROAMING_DFS_CHANNEL_MAX);
6206 ret = -EINVAL;
6207 goto exit;
6208 }
6209
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306210 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006211 "%s: Received Command to Set DFS Scan Mode = %d",
6212 __func__, dfsScanMode);
6213
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08006214 /* When DFS scanning is disabled, the DFS channels need to be
6215 * removed from the operation of device.
6216 */
6217 ret = wlan_hdd_disable_dfs_chan_scan(hdd_ctx, adapter,
6218 (dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED));
6219 if (ret < 0) {
6220 /* Some conditions prevented it from disabling DFS channels */
6221 hddLog(LOGE,
6222 FL("disable/enable DFS channel request was denied"));
6223 goto exit;
6224 }
6225
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006226 hdd_ctx->config->allowDFSChannelRoam = dfsScanMode;
6227 sme_update_dfs_scan_mode(hdd_ctx->hHal, adapter->sessionId,
6228 dfsScanMode);
6229
6230exit:
6231 return ret;
6232}
6233
6234static int drv_cmd_get_dfs_scan_mode(hdd_adapter_t *adapter,
6235 hdd_context_t *hdd_ctx,
6236 uint8_t *command,
6237 uint8_t command_len,
6238 hdd_priv_data_t *priv_data)
6239{
6240 int ret = 0;
6241 uint8_t dfsScanMode = sme_get_dfs_scan_mode(hdd_ctx->hHal);
6242 char extra[32];
6243 uint8_t len = 0;
6244
6245 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306246 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006247 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306248 QDF_TRACE(QDF_MODULE_ID_HDD,
6249 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006250 "%s: failed to copy data to user buffer",
6251 __func__);
6252 ret = -EFAULT;
6253 }
6254
6255 return ret;
6256}
6257
6258static int drv_cmd_get_link_status(hdd_adapter_t *adapter,
6259 hdd_context_t *hdd_ctx,
6260 uint8_t *command,
6261 uint8_t command_len,
6262 hdd_priv_data_t *priv_data)
6263{
6264 int ret = 0;
6265 int value = wlan_hdd_get_link_status(adapter);
6266 char extra[32];
6267 uint8_t len;
6268
6269 len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306270 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006271 if (copy_to_user(priv_data->buf, &extra, len)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306272 QDF_TRACE(QDF_MODULE_ID_HDD,
6273 QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006274 "%s: failed to copy data to user buffer",
6275 __func__);
6276 ret = -EFAULT;
6277 }
6278
6279 return ret;
6280}
6281
6282#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
6283static int drv_cmd_enable_ext_wow(hdd_adapter_t *adapter,
6284 hdd_context_t *hdd_ctx,
6285 uint8_t *command,
6286 uint8_t command_len,
6287 hdd_priv_data_t *priv_data)
6288{
6289 uint8_t *value = command;
6290 int set_value;
6291
6292 /* Move pointer to ahead of ENABLEEXTWOW */
6293 value = value + command_len;
6294
6295 sscanf(value, "%d", &set_value);
6296
6297 return hdd_enable_ext_wow_parser(adapter,
6298 adapter->sessionId,
6299 set_value);
6300}
6301
6302static int drv_cmd_set_app1_params(hdd_adapter_t *adapter,
6303 hdd_context_t *hdd_ctx,
6304 uint8_t *command,
6305 uint8_t command_len,
6306 hdd_priv_data_t *priv_data)
6307{
6308 int ret;
6309 uint8_t *value = command;
6310
6311 /* Move pointer to ahead of SETAPP1PARAMS */
6312 value = value + command_len;
6313
6314 ret = hdd_set_app_type1_parser(adapter,
6315 value, strlen(value));
6316 if (ret >= 0)
6317 hdd_ctx->is_extwow_app_type1_param_set = true;
6318
6319 return ret;
6320}
6321
6322static int drv_cmd_set_app2_params(hdd_adapter_t *adapter,
6323 hdd_context_t *hdd_ctx,
6324 uint8_t *command,
6325 uint8_t command_len,
6326 hdd_priv_data_t *priv_data)
6327{
6328 int ret;
6329 uint8_t *value = command;
6330
6331 /* Move pointer to ahead of SETAPP2PARAMS */
6332 value = value + command_len;
6333
6334 ret = hdd_set_app_type2_parser(adapter, value, strlen(value));
6335 if (ret >= 0)
6336 hdd_ctx->is_extwow_app_type2_param_set = true;
6337
6338 return ret;
6339}
6340#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
6341
6342#ifdef FEATURE_WLAN_TDLS
6343/**
6344 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset
6345 * @adapter: Pointer to the HDD adapter
6346 * @hdd_ctx: Pointer to the HDD context
6347 * @command: Driver command string
6348 * @command_len: Driver command string length
6349 * @priv_data: Private data coming with the driver command. Unused here
6350 *
6351 * This function handles driver command that sets the secondary tdls off channel
6352 * offset
6353 *
6354 * Return: 0 on success; negative errno otherwise
6355 */
6356static int drv_cmd_tdls_secondary_channel_offset(hdd_adapter_t *adapter,
6357 hdd_context_t *hdd_ctx,
6358 uint8_t *command,
6359 uint8_t command_len,
6360 hdd_priv_data_t *priv_data)
6361{
6362 int ret;
6363 uint8_t *value = command;
6364 int set_value;
6365
6366 /* Move pointer to point the string */
6367 value += command_len;
6368
6369 ret = sscanf(value, "%d", &set_value);
6370 if (ret != 1)
6371 return -EINVAL;
6372
6373 hddLog(LOG1, FL("Tdls offchannel offset:%d"), set_value);
6374
6375 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, set_value);
6376
6377 return ret;
6378}
6379
6380/**
6381 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode
6382 * @adapter: Pointer to the HDD adapter
6383 * @hdd_ctx: Pointer to the HDD context
6384 * @command: Driver command string
6385 * @command_len: Driver command string length
6386 * @priv_data: Private data coming with the driver command. Unused here
6387 *
6388 * This function handles driver command that sets tdls off channel mode
6389 *
6390 * Return: 0 on success; negative errno otherwise
6391 */
6392static int drv_cmd_tdls_off_channel_mode(hdd_adapter_t *adapter,
6393 hdd_context_t *hdd_ctx,
6394 uint8_t *command,
6395 uint8_t command_len,
6396 hdd_priv_data_t *priv_data)
6397{
6398 int ret;
6399 uint8_t *value = command;
6400 int set_value;
6401
6402 /* Move pointer to point the string */
6403 value += command_len;
6404
6405 ret = sscanf(value, "%d", &set_value);
6406 if (ret != 1)
6407 return -EINVAL;
6408
6409 hddLog(LOG1, FL("Tdls offchannel mode:%d"), set_value);
6410
6411 ret = hdd_set_tdls_offchannelmode(adapter, set_value);
6412
6413 return ret;
6414}
6415
6416/**
6417 * drv_cmd_tdls_off_channel() - set tdls off channel number
6418 * @adapter: Pointer to the HDD adapter
6419 * @hdd_ctx: Pointer to the HDD context
6420 * @command: Driver command string
6421 * @command_len: Driver command string length
6422 * @priv_data: Private data coming with the driver command. Unused here
6423 *
6424 * This function handles driver command that sets tdls off channel number
6425 *
6426 * Return: 0 on success; negative errno otherwise
6427 */
6428static int drv_cmd_tdls_off_channel(hdd_adapter_t *adapter,
6429 hdd_context_t *hdd_ctx,
6430 uint8_t *command,
6431 uint8_t command_len,
6432 hdd_priv_data_t *priv_data)
6433{
6434 int ret;
6435 uint8_t *value = command;
6436 int set_value;
6437
6438 /* Move pointer to point the string */
6439 value += command_len;
6440
6441 ret = sscanf(value, "%d", &set_value);
6442 if (ret != 1)
6443 return -EINVAL;
6444
Krishna Kumaar Natarajan4d090352015-10-26 18:30:53 -07006445 if (CDS_IS_DFS_CH(set_value)) {
6446 hdd_err("DFS channel %d is passed for hdd_set_tdls_offchannel",
6447 set_value);
6448 return -EINVAL;
6449 }
6450
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006451 hddLog(LOG1, FL("Tdls offchannel num: %d"), set_value);
6452
6453 ret = hdd_set_tdls_offchannel(hdd_ctx, set_value);
6454
6455 return ret;
6456}
6457
6458/**
6459 * drv_cmd_tdls_scan() - set tdls scan type
6460 * @adapter: Pointer to the HDD adapter
6461 * @hdd_ctx: Pointer to the HDD context
6462 * @command: Driver command string
6463 * @command_len: Driver command string length
6464 * @priv_data: Private data coming with the driver command. Unused here
6465 *
6466 * This function handles driver command that sets tdls scan type
6467 *
6468 * Return: 0 on success; negative errno otherwise
6469 */
6470static int drv_cmd_tdls_scan(hdd_adapter_t *adapter,
6471 hdd_context_t *hdd_ctx,
6472 uint8_t *command,
6473 uint8_t command_len,
6474 hdd_priv_data_t *priv_data)
6475{
6476 int ret;
6477 uint8_t *value = command;
6478 int set_value;
6479
6480 /* Move pointer to point the string */
6481 value += command_len;
6482
6483 ret = sscanf(value, "%d", &set_value);
6484 if (ret != 1)
6485 return -EINVAL;
6486
6487 hddLog(LOG1, FL("Tdls scan type val: %d"), set_value);
6488
6489 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value);
6490
6491 return ret;
6492}
6493#endif
6494
6495static int drv_cmd_get_rssi(hdd_adapter_t *adapter,
6496 hdd_context_t *hdd_ctx,
6497 uint8_t *command,
6498 uint8_t command_len,
6499 hdd_priv_data_t *priv_data)
6500{
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006501 int ret = 0;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006502 int8_t rssi = 0;
6503 char extra[32];
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006504
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006505 uint8_t len = 0;
6506
6507 wlan_hdd_get_rssi(adapter, &rssi);
6508
6509 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306510 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006511
6512 if (copy_to_user(priv_data->buf, &extra, len)) {
6513 hddLog(LOGE, FL("Failed to copy data to user buffer"));
6514 ret = -EFAULT;
6515 }
6516
6517 return ret;
6518}
6519
6520static int drv_cmd_get_linkspeed(hdd_adapter_t *adapter,
6521 hdd_context_t *hdd_ctx,
6522 uint8_t *command,
6523 uint8_t command_len,
6524 hdd_priv_data_t *priv_data)
6525{
6526 int ret;
6527 uint32_t link_speed = 0;
6528 char extra[32];
6529 uint8_t len = 0;
6530
6531 ret = wlan_hdd_get_link_speed(adapter, &link_speed);
6532 if (0 != ret)
6533 return ret;
6534
6535 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306536 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006537 if (copy_to_user(priv_data->buf, &extra, len)) {
6538 hddLog(LOGE, FL("Failed to copy data to user buffer"));
6539 ret = -EFAULT;
6540 }
6541
6542 return ret;
6543}
6544
6545#ifdef FEATURE_NAPI
6546/**
6547 * hdd_parse_napi() - helper functions to drv_cmd_napi
6548 * @str : source string to parse
6549 * @cmd : pointer to cmd part after parsing
6550 * @sub : pointer to subcmd part after parsing
6551 * @aux : pointer to optional aux part after parsing
6552 *
6553 * Example:
6554 * NAPI SCALE <n> +-- IN str
6555 * | | +------ OUT aux
6556 * | +------------ OUT subcmd
6557 * +----------------- OUT cmd
6558 *
6559 * Return: ==0: success; !=0: failure
6560 */
6561static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
6562{
6563 int rc;
6564 char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
6565
6566 NAPI_DEBUG("-->\n");
6567
6568 token = strsep(str, " \t");
6569 if (NULL == token) {
6570 hdd_err("cannot parse cmd");
6571 goto parse_end;
6572 }
6573 lcmd = token;
6574
6575 token = strsep(str, " \t");
6576 if (NULL == token) {
6577 hdd_err("cannot parse subcmd");
6578 goto parse_end;
6579 }
6580 lsub = token;
6581
6582 token = strsep(str, " \t");
6583 if (NULL == token)
6584 hdd_warn("cannot parse aux\n");
6585 else
6586 laux = token;
6587
6588parse_end:
6589 if ((NULL == lcmd) || (NULL == lsub))
6590 rc = -EINVAL;
6591 else {
6592 rc = 0;
6593 *cmd = lcmd;
6594 *sub = lsub;
6595 if (NULL != aux)
6596 *aux = laux;
6597 }
6598 NAPI_DEBUG("<--[rc=%d]\n", rc);
6599 return rc;
6600}
6601
6602
6603/**
6604 * hdd_parse_stats() - print NAPI stats into a buffer
6605 * @buf : buffer to write stats into
6606 * @max : "size of buffer"
6607 * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
6608 * @napid: binary structure to retrieve the stats from
6609 *
6610 * Return: number of bytes written into the buffer
6611 */
6612int hdd_napi_stats(char *buf,
6613 int max,
6614 char *indp,
6615 struct qca_napi_data *napid)
6616{
6617 int n = 0;
6618 int i, j, k; /* NAPI, CPU, bucket indices */
6619 int from, to;
6620 struct qca_napi_info *napii;
6621 struct qca_napi_stat *napis;
6622
6623 NAPI_DEBUG("-->\n");
6624
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006625 if (NULL == napid)
6626 return n;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006627 if (NULL == indp) {
6628 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006629 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006630 } else {
6631 if (0 > kstrtoint(indp, 10, &to)) {
6632 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006633 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006634 } else
6635 from = to;
6636 }
6637
6638 for (i = from; i < to; i++)
6639 if (napid->ce_map & (0x01 << i)) {
6640 napii = &(napid->napis[i]);
6641 for (j = 0; j < NR_CPUS; j++) {
6642 napis = &(napii->stats[j]);
6643 n += scnprintf(buf + n, max - n,
6644 "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
6645 i, j,
6646 napis->napi_schedules,
6647 napis->napi_polls,
6648 napis->napi_completes,
6649 napis->napi_workdone);
6650
6651 for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
6652 n += scnprintf(
6653 buf + n, max - n,
6654 " %d",
6655 napis->napi_budget_uses[k]);
6656 }
6657 n += scnprintf(buf+n, max - n, "\n");
6658 }
6659 }
6660
6661 NAPI_DEBUG("<--[n=%d]\n", n);
6662 return n;
6663}
6664
6665/**
6666 * napi_set_scale() - sets the scale attribute in all NAPI entries
6667 * @sc : scale to set
6668 *
6669 * Return: void
6670 */
6671static void napi_set_scale(uint8_t sc)
6672{
6673 uint32_t i;
6674 struct qca_napi_data *napi_data;
6675
6676 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006677 if (likely(NULL != napi_data))
6678 for (i = 0; i < CE_COUNT_MAX; i++)
6679 if (napi_data->ce_map & (0x01 << i))
6680 napi_data->napis[i].scale = sc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006681
6682 return;
6683}
6684/**
6685 * drv_cmd_napi() - processes NAPI commands
6686 * @adapter : net_device
6687 * @hdd_ctx : HDD context
6688 * @command : command string from user command (including "NAPI")
6689 * @command_len: length of command
6690 * @priv_data : ifr_data
6691 *
6692 * Commands supported:
6693 * NAPI ENABLE : enables NAPI administratively. Note that this may not
6694 * enable NAPI functionally, as some other conditions
6695 * may not have been satisfied yet
6696 * NAPI DISABLE : reverse operation of "enable"
6697 * NAPI STATUS : get global status of NAPI instances
6698 * NAPI STATS [<n>] : get the stats for a given NAPI instance
6699 * NAPI SCALE <n> : set the scale factor
6700 *
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006701 * Return: 0: success; !0: failure
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006702 */
6703static int drv_cmd_napi(hdd_adapter_t *adapter,
6704 hdd_context_t *hdd_ctx,
6705 uint8_t *command,
6706 uint8_t command_len,
6707 hdd_priv_data_t *priv_data)
6708{
6709 int rc = 0;
6710 int n, l;
6711 char *cmd = NULL, *subcmd = NULL, *aux = NULL;
6712 char *synopsis = "NAPI ENABLE\n"
6713 "NAPI DISABLE\n"
6714 "NAPI STATUS\n"
6715 "NAPI STATS [<n>] -- if no <n> then all\n"
6716 "NAPI SCALE <n> -- set the scale\n";
6717 char *reply = NULL;
6718
6719 /* make a local copy, as strsep modifies the str in place */
6720 char *str = NULL;
6721
6722 NAPI_DEBUG("-->\n");
6723
6724 /**
6725 * NOTE TO MAINTAINER: from this point to the end of the function,
6726 * please do not return anywhere in the code except the very end
6727 * to avoid memory leakage (goto end_drv_napi instead)
6728 * or make sure that reply+str is freed
6729 */
6730 reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
6731 if (NULL == reply) {
6732 hdd_err("could not allocate reply buffer");
6733 rc = -ENOMEM;
6734 goto end_drv_napi;
6735 }
6736
6737 str = kmalloc(strlen(command) + 1, GFP_KERNEL);
6738 if (NULL == str) {
6739 hdd_err("could not allocate copy of input buffer");
6740 rc = -ENOMEM;
6741 goto end_drv_napi;
6742 }
6743
6744 strlcpy(str, command, strlen(command) + 1);
6745 hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
6746 cmd, subcmd, aux);
6747
6748
6749 rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
6750
6751 if (0 != rc) {
6752 const char *msg = "unknown or badly formatted cmd\n%s";
Anurag Chouhan6d760662016-02-20 16:05:43 +05306753 l = QDF_MIN(MAX_USER_COMMAND_SIZE,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006754 strlen(msg)+strlen(synopsis));
6755 n = scnprintf(reply, l, msg, synopsis);
6756
6757 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306758 QDF_MIN(priv_data->total_len, l)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006759 hdd_err("failed to copy data to user buffer");
6760 hdd_debug("reply: %s", reply);
6761
6762 rc = -EINVAL;
6763 } else {
6764 hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
6765 cmd, subcmd, aux);
6766 if (!strcmp(subcmd, "ENABLE"))
6767 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
6768 else if (!strcmp(subcmd, "DISABLE"))
6769 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
6770 else if (!strcmp(subcmd, "STATUS")) {
6771 int n = 0;
6772 uint32_t i;
6773 struct qca_napi_data *napi_data;
6774
6775 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006776 if (unlikely(NULL == napi_data))
6777 goto status_end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006778 n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
6779 "NAPI state: 0x%08x map: 0x%08x\n",
6780 napi_data->state,
6781 napi_data->ce_map);
6782
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006783 for (i = 0; i < CE_COUNT_MAX; i++)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006784 if (napi_data->ce_map & (0x01 << i)) {
6785 n += scnprintf(
6786 reply + n,
6787 MAX_USER_COMMAND_SIZE - n,
6788 "#%d: id: %d, scale=%d\n",
6789 i,
6790 napi_data->napis[i].id,
6791 napi_data->napis[i].scale);
6792 }
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006793 status_end:
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006794 hdd_info("wlan: STATUS DATA:\n%s", reply);
6795 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306796 QDF_MIN(n, priv_data->total_len)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006797 rc = -EINVAL;
6798 } else if (!strcmp(subcmd, "STATS")) {
6799 int n = 0;
6800 struct qca_napi_data *napi_data;
6801
6802 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006803 if (NULL != napi_data) {
6804 n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
6805 aux, napi_data);
6806 NAPI_DEBUG("STATS: returns %d\n", n);
6807 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006808 if (n > 0) {
6809 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306810 QDF_MIN(priv_data->total_len,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006811 n)))
6812 rc = -EINVAL;
6813 hdd_info("wlan: STATS_DATA\n%s\n", reply);
6814 } else
6815 rc = -EINVAL;
6816 } else if (!strcmp(subcmd, "SCALE")) {
6817 if (NULL == aux) {
6818 rc = -EINVAL;
6819 hdd_err("wlan: SCALE cmd requires <n>");
6820 } else {
6821 uint8_t sc;
6822 rc = kstrtou8(aux, 10, &sc);
6823 if (rc) {
6824 hdd_err("wlan: bad scale (%s)", aux);
6825 rc = -EINVAL;
6826 } else
6827 napi_set_scale(sc);
6828 }
6829 } /* SCALE */
6830 }
6831end_drv_napi:
6832 if (NULL != str)
6833 kfree(str);
6834 if (NULL != reply)
6835 kfree(reply);
6836
6837 NAPI_DEBUG("<--[rc=%d]\n", rc);
6838 return rc;
6839}
6840#endif /* FEATURE_NAPI */
6841
6842/**
6843 * hdd_set_rx_filter() - set RX filter
6844 * @adapter: Pointer to adapter
6845 * @action: Filter action
6846 * @pattern: Address pattern
6847 *
6848 * Address pattern is most significant byte of address for example
6849 * 0x01 for IPV4 multicast address
6850 * 0x33 for IPV6 multicast address
6851 * 0xFF for broadcast address
6852 *
6853 * Return: 0 for success, non-zero for failure
6854 */
6855static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
6856 uint8_t pattern)
6857{
6858 int ret;
6859 uint8_t i;
6860 tHalHandle handle;
6861 tSirRcvFltMcAddrList *filter;
6862 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
6863
6864 ret = wlan_hdd_validate_context(hdd_ctx);
6865 if (0 != ret)
6866 return ret;
6867
6868 handle = hdd_ctx->hHal;
6869
6870 if (NULL == handle) {
6871 hdd_err("HAL Handle is NULL");
6872 return -EINVAL;
6873 }
6874
6875 /*
6876 * If action is false it means start dropping packets
6877 * Set addr_filter_pattern which will be used when sending
6878 * MC/BC address list to target
6879 */
6880 if (!action)
6881 adapter->addr_filter_pattern = pattern;
6882 else
6883 adapter->addr_filter_pattern = 0;
6884
Krunal Sonibe766b02016-03-10 13:00:44 -08006885 if (((adapter->device_mode == QDF_STA_MODE) ||
6886 (adapter->device_mode == QDF_P2P_CLIENT_MODE)) &&
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006887 adapter->mc_addr_list.mc_cnt &&
6888 hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
6889
6890
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306891 filter = qdf_mem_malloc(sizeof(*filter));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006892 if (NULL == filter) {
6893 hdd_err("Could not allocate Memory");
6894 return -ENOMEM;
6895 }
6896 filter->action = action;
6897 for (i = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
6898 if (!memcmp(adapter->mc_addr_list.addr[i],
6899 &pattern, 1)) {
Srinivas Girigowda98530492015-11-20 17:39:24 -08006900 memcpy(filter->multicastAddr[i].bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006901 adapter->mc_addr_list.addr[i],
6902 sizeof(adapter->mc_addr_list.addr[i]));
6903 filter->ulMulticastAddrCnt++;
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006904 hdd_info("%s RX filter : addr ="
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006905 MAC_ADDRESS_STR,
6906 action ? "setting" : "clearing",
Srinivas Girigowda98530492015-11-20 17:39:24 -08006907 MAC_ADDR_ARRAY(filter->multicastAddr[i].bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006908 }
6909 }
6910 /* Set rx filter */
6911 sme_8023_multicast_list(handle, adapter->sessionId, filter);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306912 qdf_mem_free(filter);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006913 } else {
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006914 hdd_info("mode %d mc_cnt %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006915 adapter->device_mode, adapter->mc_addr_list.mc_cnt);
6916 }
6917
6918 return 0;
6919}
6920
6921/**
6922 * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
6923 * @command: Pointer to input string driver command
6924 * @adapter: Pointer to adapter
6925 * @action: Action to enable/disable filtering
6926 *
6927 * If action == false
6928 * Start filtering out data packets based on type
6929 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
6930 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
6931 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
6932 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
6933 *
6934 * if action == true
6935 * Stop filtering data packets based on type
6936 * RXFILTER-ADD 0 -> Stop filtering unicast data packets
6937 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets
6938 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
6939 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
6940 *
6941 * Current implementation only supports IPV4 address filtering by
6942 * selectively allowing IPV4 multicast data packest based on
6943 * address list received in .ndo_set_rx_mode
6944 *
6945 * Return: 0 for success, non-zero for failure
6946 */
6947static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
6948 hdd_adapter_t *adapter,
6949 bool action)
6950{
6951 int ret = 0;
6952 uint8_t *value;
6953 uint8_t type;
6954
6955 value = command;
6956 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
6957 if (!action)
6958 value = command + 16;
6959 else
6960 value = command + 13;
6961 ret = kstrtou8(value, 10, &type);
6962 if (ret < 0) {
6963 hdd_err("kstrtou8 failed invalid input value %d", type);
6964 return -EINVAL;
6965 }
6966
6967 switch (type) {
6968 case 2:
6969 /* Set rx filter for IPV4 multicast data packets */
6970 ret = hdd_set_rx_filter(adapter, action, 0x01);
6971 break;
6972 default:
6973 hdd_info("Unsupported RXFILTER type %d", type);
6974 break;
6975 }
6976
6977 return ret;
6978}
6979
6980/**
6981 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler
6982 * @adapter: Pointer to network adapter
6983 * @hdd_ctx: Pointer to hdd context
6984 * @command: Pointer to input command
6985 * @command_len: Command length
6986 * @priv_data: Pointer to private data in command
6987 */
6988static int drv_cmd_rx_filter_remove(hdd_adapter_t *adapter,
6989 hdd_context_t *hdd_ctx,
6990 uint8_t *command,
6991 uint8_t command_len,
6992 hdd_priv_data_t *priv_data)
6993{
6994 return hdd_driver_rxfilter_comand_handler(command, adapter, false);
6995}
6996
6997/**
6998 * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler
6999 * @adapter: Pointer to network adapter
7000 * @hdd_ctx: Pointer to hdd context
7001 * @command: Pointer to input command
7002 * @command_len: Command length
7003 * @priv_data: Pointer to private data in command
7004 */
7005static int drv_cmd_rx_filter_add(hdd_adapter_t *adapter,
7006 hdd_context_t *hdd_ctx,
7007 uint8_t *command,
7008 uint8_t command_len,
7009 hdd_priv_data_t *priv_data)
7010{
7011 return hdd_driver_rxfilter_comand_handler(command, adapter, true);
7012}
7013
Archana Ramachandran393f3792015-11-13 17:13:21 -08007014/**
7015 * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE
7016 * command
7017 * @value: Pointer to SETANTENNAMODE command
7018 * @mode: Pointer to antenna mode
7019 * @reason: Pointer to reason for set antenna mode
7020 *
7021 * This function parses the SETANTENNAMODE command passed in the format
7022 * SETANTENNAMODE<space>mode
7023 *
7024 * Return: 0 for success non-zero for failure
7025 */
7026static int hdd_parse_setantennamode_command(const uint8_t *value)
7027{
7028 const uint8_t *in_ptr = value;
7029 int tmp, v;
7030 char arg1[32];
7031
7032 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
7033
7034 /* no argument after the command */
7035 if (NULL == in_ptr) {
7036 hddLog(LOGE, FL("No argument after the command"));
7037 return -EINVAL;
7038 }
7039
7040 /* no space after the command */
7041 if (SPACE_ASCII_VALUE != *in_ptr) {
7042 hddLog(LOGE, FL("No space after the command"));
7043 return -EINVAL;
7044 }
7045
7046 /* remove empty spaces */
7047 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
7048 in_ptr++;
7049
7050 /* no argument followed by spaces */
7051 if ('\0' == *in_ptr) {
7052 hddLog(LOGE, FL("No argument followed by spaces"));
7053 return -EINVAL;
7054 }
7055
7056 /* get the argument i.e. antenna mode */
7057 v = sscanf(in_ptr, "%31s ", arg1);
7058 if (1 != v) {
7059 hddLog(LOGE, FL("argument retrieval from cmd string failed"));
7060 return -EINVAL;
7061 }
7062
7063 v = kstrtos32(arg1, 10, &tmp);
7064 if (v < 0) {
7065 hddLog(LOGE, FL("argument string to int conversion failed"));
7066 return -EINVAL;
7067 }
7068
7069 return tmp;
7070}
7071
7072/**
7073 * hdd_is_supported_chain_mask_2x2() - Verify if supported chain
7074 * mask is 2x2 mode
7075 * @hdd_ctx: Pointer to hdd contex
7076 *
7077 * Return: true if supported chain mask 2x2 else false
7078 */
7079static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx)
7080{
7081 /*
7082 * Revisit and the update logic to determine the number
7083 * of TX/RX chains supported in the system when
7084 * antenna sharing per band chain mask support is
7085 * brought in
7086 */
7087 return (hdd_ctx->config->enable2x2 == 0x01) ? true : false;
7088}
7089
7090/**
7091 * hdd_is_supported_chain_mask_1x1() - Verify if the supported
7092 * chain mask is 1x1
7093 * @hdd_ctx: Pointer to hdd contex
7094 *
7095 * Return: true if supported chain mask 1x1 else false
7096 */
7097static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx)
7098{
7099 /*
7100 * Revisit and update the logic to determine the number
7101 * of TX/RX chains supported in the system when
7102 * antenna sharing per band chain mask support is
7103 * brought in
7104 */
7105 return (!hdd_ctx->config->enable2x2) ? true : false;
7106}
7107
7108/**
7109 * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command
7110 * handler
7111 * @adapter: Pointer to network adapter
7112 * @hdd_ctx: Pointer to hdd context
7113 * @command: Pointer to input command
7114 * @command_len: Command length
7115 * @priv_data: Pointer to private data in command
7116 */
7117static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter,
7118 hdd_context_t *hdd_ctx,
7119 uint8_t *command,
7120 uint8_t command_len,
7121 hdd_priv_data_t *priv_data)
7122{
7123 struct sir_antenna_mode_param params;
7124 QDF_STATUS status;
7125 int ret = 0;
7126 int mode;
7127 uint8_t *value = command;
7128 uint8_t smps_mode;
7129 uint8_t smps_enable;
7130
7131 if (((1 << QDF_STA_MODE) != hdd_ctx->concurrency_mode) ||
7132 (hdd_ctx->no_of_active_sessions[QDF_STA_MODE] > 1)) {
7133 hdd_err("Operation invalid in non sta or concurrent mode");
7134 ret = -EPERM;
7135 goto exit;
7136 }
7137
7138 mode = hdd_parse_setantennamode_command(value);
7139 if (mode < 0) {
7140 hdd_err("Invalid SETANTENNA command");
7141 ret = mode;
7142 goto exit;
7143 }
7144
7145 hdd_info("Processing antenna mode switch to: %d", mode);
7146
7147 if (hdd_ctx->current_antenna_mode == mode) {
7148 hdd_err("System already in the requested mode");
7149 ret = 0;
7150 goto exit;
7151 }
7152
7153 if ((HDD_ANTENNA_MODE_2X2 == mode) &&
7154 (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) {
7155 hdd_err("System does not support 2x2 mode");
7156 ret = -EPERM;
7157 goto exit;
7158 }
7159
7160 if ((HDD_ANTENNA_MODE_1X1 == mode) &&
7161 hdd_is_supported_chain_mask_1x1(hdd_ctx)) {
7162 hdd_err("System only supports 1x1 mode");
7163 ret = 0;
7164 goto exit;
7165 }
7166
7167 switch (mode) {
7168 case HDD_ANTENNA_MODE_1X1:
7169 params.num_rx_chains = 1;
7170 params.num_tx_chains = 1;
7171 break;
7172 case HDD_ANTENNA_MODE_2X2:
7173 params.num_rx_chains = 2;
7174 params.num_tx_chains = 2;
7175 break;
7176 default:
7177 hdd_err("unsupported antenna mode");
7178 ret = -EINVAL;
7179 goto exit;
7180 }
7181
7182 params.set_antenna_mode_resp =
7183 (void *)wlan_hdd_soc_set_antenna_mode_cb;
7184 hdd_info("Set antenna mode rx chains: %d tx chains: %d",
7185 params.num_rx_chains,
7186 params.num_tx_chains);
7187
7188
7189 INIT_COMPLETION(hdd_ctx->set_antenna_mode_cmpl);
7190 status = sme_soc_set_antenna_mode(hdd_ctx->hHal, &params);
7191 if (QDF_STATUS_SUCCESS != status) {
7192 hdd_err("set antenna mode failed status : %d", status);
7193 ret = -EFAULT;
7194 goto exit;
7195 }
7196
7197 ret = wait_for_completion_timeout(
7198 &hdd_ctx->set_antenna_mode_cmpl,
7199 msecs_to_jiffies(WLAN_WAIT_TIME_ANTENNA_MODE_REQ));
7200 if (!ret) {
7201 ret = -EFAULT;
7202 hdd_err("send set antenna mode timed out");
7203 goto exit;
7204 }
7205
7206 /* Update SME SMPS config */
7207 if (HDD_ANTENNA_MODE_1X1 == mode) {
7208 smps_enable = true;
7209 smps_mode = HDD_SMPS_MODE_STATIC;
7210 } else {
7211 smps_enable = false;
7212 smps_mode = HDD_SMPS_MODE_DISABLED;
7213 }
7214
7215 hdd_info("Update SME SMPS enable: %d mode: %d",
7216 smps_enable, smps_mode);
7217 status = sme_update_mimo_power_save(
7218 hdd_ctx->hHal, smps_enable, smps_mode, false);
7219 if (QDF_STATUS_SUCCESS != status) {
7220 hdd_err("Update SMPS config failed enable: %d mode: %d status: %d",
7221 smps_enable, smps_mode, status);
7222 ret = -EFAULT;
7223 goto exit;
7224 }
7225
7226 hdd_info("Successfully switched to mode: %d x %d", mode, mode);
7227 ret = 0;
7228 hdd_ctx->current_antenna_mode = mode;
7229
7230exit:
7231 hdd_info("Set antenna status: %d current mode: %d",
7232 ret, hdd_ctx->current_antenna_mode);
7233 return ret;
7234
7235}
7236
7237/**
7238 * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command
7239 * handler
7240 * @adapter: Pointer to hdd adapter
7241 * @hdd_ctx: Pointer to hdd context
7242 * @command: Pointer to input command
7243 * @command_len: length of the command
7244 * @priv_data: private data coming with the driver command
7245 *
7246 * Return: 0 for success non-zero for failure
7247 */
7248static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter,
7249 hdd_context_t *hdd_ctx,
7250 uint8_t *command,
7251 uint8_t command_len,
7252 hdd_priv_data_t *priv_data)
7253{
7254 uint32_t antenna_mode = 0;
7255 char extra[32];
7256 uint8_t len = 0;
7257
7258 antenna_mode = hdd_ctx->current_antenna_mode;
7259 len = scnprintf(extra, sizeof(extra), "%s %d", command,
7260 antenna_mode);
7261 len = QDF_MIN(priv_data->total_len, len + 1);
7262 if (copy_to_user(priv_data->buf, &extra, len)) {
7263 hdd_err("Failed to copy data to user buffer");
7264 return -EFAULT;
7265 }
7266
7267 hdd_info("Get antenna mode: %d", antenna_mode);
7268
7269 return 0;
7270}
7271
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007272/*
7273 * dummy (no-op) hdd driver command handler
7274 */
7275static int drv_cmd_dummy(hdd_adapter_t *adapter,
7276 hdd_context_t *hdd_ctx,
7277 uint8_t *command,
7278 uint8_t command_len,
7279 hdd_priv_data_t *priv_data)
7280{
7281 hdd_info("%s: Ignoring driver command \"%s\"",
7282 adapter->dev->name, command);
7283 return 0;
7284}
7285
7286/*
7287 * handler for any unsupported wlan hdd driver command
7288 */
7289static int drv_cmd_invalid(hdd_adapter_t *adapter,
7290 hdd_context_t *hdd_ctx,
7291 uint8_t *command,
7292 uint8_t command_len,
7293 hdd_priv_data_t *priv_data)
7294{
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307295 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007296 TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
7297 adapter->sessionId, 0));
7298
7299 hdd_warn("%s: Unsupported driver command \"%s\"",
7300 adapter->dev->name, command);
7301
7302 return -ENOTSUPP;
7303}
7304
7305/**
7306 * drv_cmd_set_fcc_channel() - handle fcc constraint request
7307 * @adapter: HDD adapter
7308 * @hdd_ctx: HDD context
7309 * @command: command ptr, SET_FCC_CHANNEL 0/1 is the command
7310 * @command_len: command len
7311 * @priv_data: private data
7312 *
7313 * Return: status
7314 */
7315static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter,
7316 hdd_context_t *hdd_ctx,
7317 uint8_t *command,
7318 uint8_t command_len,
7319 hdd_priv_data_t *priv_data)
7320{
7321 uint8_t *value;
7322 uint8_t fcc_constraint;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05307323 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007324 int ret = 0;
7325
7326 /*
7327 * this command would be called by user-space when it detects WLAN
7328 * ON after airplane mode is set. When APM is set, WLAN turns off.
7329 * But it can be turned back on. Otherwise; when APM is turned back
7330 * off, WLAN would turn back on. So at that point the command is
7331 * expected to come down. 0 means disable, 1 means enable. The
7332 * constraint is removed when parameter 1 is set or different
7333 * country code is set
7334 */
7335
7336 value = command + command_len + 1;
7337
7338 ret = kstrtou8(value, 10, &fcc_constraint);
7339 if ((ret < 0) || (fcc_constraint > 1)) {
7340 /*
7341 * If the input value is greater than max value of datatype,
7342 * then also it is a failure
7343 */
7344 hdd_err("value out of range");
7345 return -EINVAL;
7346 }
7347
7348 status = sme_disable_non_fcc_channel(hdd_ctx->hHal, !fcc_constraint);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05307349 if (status != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007350 hdd_err("sme disable fn. returned err");
7351 ret = -EPERM;
7352 }
7353
7354 return ret;
7355}
7356
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307357/**
7358 * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH
7359 * command
7360 * @value: Pointer to the command
7361 * @chan_number: Pointer to the channel number
7362 * @chan_bw: Pointer to the channel bandwidth
7363 *
7364 * Parses and provides the channel number and channel width from the input
7365 * command which is expected to be of the format: CHANNEL_SWITCH <CH> <BW>
7366 * <CH> is channel number to move (where 1 = channel 1, 149 = channel 149, ...)
7367 * <BW> is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80)
7368 *
7369 * Return: 0 for success, non-zero for failure
7370 */
7371static int hdd_parse_set_channel_switch_command(uint8_t *value,
7372 uint32_t *chan_number,
7373 uint32_t *chan_bw)
7374{
7375 const uint8_t *in_ptr = value;
7376 int ret;
7377
7378 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
7379
7380 /* no argument after the command */
7381 if (NULL == in_ptr) {
7382 hdd_err("No argument after the command");
7383 return -EINVAL;
7384 }
7385
7386 /* no space after the command */
7387 if (SPACE_ASCII_VALUE != *in_ptr) {
7388 hdd_err("No space after the command ");
7389 return -EINVAL;
7390 }
7391
7392 /* remove empty spaces and move the next argument */
7393 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
7394 in_ptr++;
7395
7396 /* no argument followed by spaces */
7397 if ('\0' == *in_ptr) {
7398 hdd_err("No argument followed by spaces");
7399 return -EINVAL;
7400 }
7401
7402 /* get the two arguments: channel number and bandwidth */
7403 ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw);
7404 if (ret != 2) {
7405 hdd_err("Arguments retrieval from cmd string failed");
7406 return -EINVAL;
7407 }
7408
7409 return 0;
7410}
7411
7412/**
7413 * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel
7414 * @adapter: HDD adapter
7415 * @hdd_ctx: HDD context
7416 * @command: Pointer to the input command CHANNEL_SWITCH
7417 * @command_len: Command len
7418 * @priv_data: Private data
7419 *
7420 * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel
7421 * of SAP/P2P-GO
7422 *
7423 * Return: 0 for success, non-zero for failure
7424 */
7425static int drv_cmd_set_channel_switch(hdd_adapter_t *adapter,
7426 hdd_context_t *hdd_ctx,
7427 uint8_t *command,
7428 uint8_t command_len,
7429 hdd_priv_data_t *priv_data)
7430{
7431 struct net_device *dev = adapter->dev;
7432 int status;
7433 uint32_t chan_number = 0, chan_bw = 0;
7434 uint8_t *value = command;
Amar Singhale4f28ee2015-10-21 14:36:56 -07007435 enum ch_width width;
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307436
Krunal Sonibe766b02016-03-10 13:00:44 -08007437 if ((adapter->device_mode != QDF_P2P_GO_MODE) &&
7438 (adapter->device_mode != QDF_SAP_MODE)) {
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307439 hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d",
7440 adapter->device_mode);
7441 return -EINVAL;
7442 }
7443
7444 status = hdd_parse_set_channel_switch_command(value,
7445 &chan_number, &chan_bw);
7446 if (status) {
7447 hdd_err("Invalid CHANNEL_SWITCH command");
7448 return status;
7449 }
7450
7451 if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80)) {
7452 hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw);
7453 return -EINVAL;
7454 }
7455
7456 if (chan_bw == 80)
7457 width = CH_WIDTH_80MHZ;
7458 else if (chan_bw == 40)
7459 width = CH_WIDTH_40MHZ;
7460 else
7461 width = CH_WIDTH_20MHZ;
7462
7463 hdd_info("CH:%d BW:%d", chan_number, chan_bw);
7464
7465 status = hdd_softap_set_channel_change(dev, chan_number, width);
7466 if (status) {
7467 hdd_err("Set channel change fail");
7468 return status;
7469 }
7470
7471 return 0;
7472}
7473
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007474/*
7475 * The following table contains all supported WLAN HDD
7476 * IOCTL driver commands and the handler for each of them.
7477 */
7478static const hdd_drv_cmd_t hdd_drv_cmds[] = {
7479 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr},
7480 {"P2P_SET_NOA", drv_cmd_p2p_set_noa},
7481 {"P2P_SET_PS", drv_cmd_p2p_set_ps},
7482 {"SETBAND", drv_cmd_set_band},
7483 {"SETWMMPS", drv_cmd_set_wmmps},
7484 {"COUNTRY", drv_cmd_country},
7485 {"SETSUSPENDMODE", drv_cmd_dummy},
7486 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy},
7487 {"BTCOEXSCAN", drv_cmd_dummy},
7488 {"RXFILTER", drv_cmd_dummy},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007489 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger},
7490 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger},
7491 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period},
7492 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period},
7493 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period},
7494 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007495 {"SETROAMMODE", drv_cmd_set_roam_mode},
7496 {"GETROAMMODE", drv_cmd_get_roam_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007497 {"SETROAMDELTA", drv_cmd_set_roam_delta},
7498 {"GETROAMDELTA", drv_cmd_get_roam_delta},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007499 {"GETBAND", drv_cmd_get_band},
7500 {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels},
7501 {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels},
7502 {"GETCCXMODE", drv_cmd_get_ccx_mode},
7503 {"GETOKCMODE", drv_cmd_get_okc_mode},
7504 {"GETFASTROAM", drv_cmd_get_fast_roam},
7505 {"GETFASTTRANSITION", drv_cmd_get_fast_transition},
7506 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time},
7507 {"SENDACTIONFRAME", drv_cmd_send_action_frame},
7508 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time},
7509 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time},
7510 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time},
7511 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time},
7512 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time},
7513 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band},
7514 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band},
7515 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes},
7516 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes},
7517 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time},
7518 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time},
7519 {"REASSOC", drv_cmd_reassoc},
7520 {"SETWESMODE", drv_cmd_set_wes_mode},
7521 {"GETWESMODE", drv_cmd_get_wes_mode},
7522 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff},
7523 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff},
7524 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff},
7525 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007526 {"SETFASTROAM", drv_cmd_set_fast_roam},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007527 {"SETFASTTRANSITION", drv_cmd_set_fast_transition},
7528 {"FASTREASSOC", drv_cmd_fast_reassoc},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007529#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
7530 {"CCXPLMREQ", drv_cmd_ccx_plm_req},
7531#endif
7532#ifdef FEATURE_WLAN_ESE
7533 {"SETCCXMODE", drv_cmd_set_ccx_mode},
7534#endif
7535 {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control},
7536#ifdef FEATURE_WLAN_OKC
7537 {"SETOKCMODE", drv_cmd_set_okc_mode},
7538#endif /* FEATURE_WLAN_OKC */
7539 {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control},
7540 {"BTCOEXMODE", drv_cmd_bt_coex_mode},
7541 {"SCAN-ACTIVE", drv_cmd_scan_active},
7542 {"SCAN-PASSIVE", drv_cmd_scan_passive},
7543 {"GETDWELLTIME", drv_cmd_get_dwell_time},
7544 {"SETDWELLTIME", drv_cmd_set_dwell_time},
7545 {"MIRACAST", drv_cmd_miracast},
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08007546 {"SETIBSSBEACONOUIDATA", drv_cmd_set_ibss_beacon_oui_data},
7547 {"SETRMCENABLE", drv_cmd_set_rmc_enable},
7548 {"SETRMCACTIONPERIOD", drv_cmd_set_rmc_action_period},
7549 {"GETIBSSPEERINFOALL", drv_cmd_get_ibss_peer_info_all},
7550 {"GETIBSSPEERINFO", drv_cmd_get_ibss_peer_info},
7551 {"SETRMCTXRATE", drv_cmd_set_rmc_tx_rate},
7552 {"SETIBSSTXFAILEVENT", drv_cmd_set_ibss_tx_fail_event},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007553#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
7554 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels},
7555 {"GETTSMSTATS", drv_cmd_get_tsm_stats},
7556 {"SETCCKMIE", drv_cmd_set_cckm_ie},
7557 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req},
7558#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
7559 {"SETMCRATE", drv_cmd_set_mc_rate},
7560 {"MAXTXPOWER", drv_cmd_max_tx_power},
7561 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode},
7562 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode},
7563 {"GETLINKSTATUS", drv_cmd_get_link_status},
7564#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
7565 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow},
7566 {"SETAPP1PARAMS", drv_cmd_set_app1_params},
7567 {"SETAPP2PARAMS", drv_cmd_set_app2_params},
7568#endif
7569#ifdef FEATURE_WLAN_TDLS
7570 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset},
7571 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode},
7572 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel},
7573 {"TDLSSCAN", drv_cmd_tdls_scan},
7574#endif
7575 {"RSSI", drv_cmd_get_rssi},
7576 {"LINKSPEED", drv_cmd_get_linkspeed},
7577#ifdef FEATURE_NAPI
7578 {"NAPI", drv_cmd_napi},
7579#endif /* FEATURE_NAPI */
7580 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove},
7581 {"RXFILTER-ADD", drv_cmd_rx_filter_add},
7582 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel},
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307583 {"CHANNEL_SWITCH", drv_cmd_set_channel_switch},
Archana Ramachandran393f3792015-11-13 17:13:21 -08007584 {"SETANTENNAMODE", drv_cmd_set_antenna_mode},
7585 {"GETANTENNAMODE", drv_cmd_get_antenna_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007586};
7587
7588/**
7589 * hdd_drv_cmd_process() - chooses and runs the proper
7590 * handler based on the input command
7591 * @adapter: Pointer to the hdd adapter
7592 * @cmd: Pointer to the driver command
7593 * @priv_data: Pointer to the data associated with the command
7594 *
7595 * This function parses the input hdd driver command and runs
7596 * the proper handler
7597 *
7598 * Return: 0 for success non-zero for failure
7599 */
7600static int hdd_drv_cmd_process(hdd_adapter_t *adapter,
7601 uint8_t *cmd,
7602 hdd_priv_data_t *priv_data)
7603{
7604 hdd_context_t *hdd_ctx;
7605 int i;
7606 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds);
7607 uint8_t *cmd_i = NULL;
7608 hdd_drv_cmd_handler_t handler = NULL;
7609 int len = 0;
7610
7611 if (!adapter || !cmd || !priv_data) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307612 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007613 "%s: at least 1 param is NULL", __func__);
7614 return -EINVAL;
7615 }
7616
7617 hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
7618
7619 for (i = 0; i < cmd_num_total; i++) {
7620
7621 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd;
7622 handler = hdd_drv_cmds[i].handler;
7623 len = strlen(cmd_i);
7624
7625 if (!handler) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307626 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007627 "%s: no. %d handler is NULL", __func__, i);
7628 return -EINVAL;
7629 }
7630
7631 if (strncasecmp(cmd, cmd_i, len) == 0)
7632 return handler(adapter, hdd_ctx,
7633 cmd, len, priv_data);
7634 }
7635
7636 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data);
7637}
7638
7639/**
7640 * hdd_driver_command() - top level wlan hdd driver command handler
7641 * @adapter: Pointer to the hdd adapter
7642 * @priv_data: Pointer to the raw command data
7643 *
7644 * This function is the top level wlan hdd driver command handler. It
7645 * handles the command with the help of hdd_drv_cmd_process()
7646 *
7647 * Return: 0 for success non-zero for failure
7648 */
7649static int hdd_driver_command(hdd_adapter_t *adapter,
7650 hdd_priv_data_t *priv_data)
7651{
7652 uint8_t *command = NULL;
7653 int ret = 0;
7654
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307655 ENTER();
7656
Anurag Chouhan6d760662016-02-20 16:05:43 +05307657 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007658 hddLog(LOGE, FL("Command not allowed in FTM mode"));
7659 return -EINVAL;
7660 }
7661
7662 /*
7663 * Note that valid pointers are provided by caller
7664 */
7665
7666 /* copy to local struct to avoid numerous changes to legacy code */
7667 if (priv_data->total_len <= 0 ||
7668 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307669 hddLog(QDF_TRACE_LEVEL_WARN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007670 "%s:invalid priv_data.total_len(%d)!!!", __func__,
7671 priv_data->total_len);
7672 ret = -EINVAL;
7673 goto exit;
7674 }
7675
7676 /* Allocate +1 for '\0' */
7677 command = kmalloc(priv_data->total_len + 1, GFP_KERNEL);
7678 if (!command) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307679 hddLog(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007680 "%s: failed to allocate memory", __func__);
7681 ret = -ENOMEM;
7682 goto exit;
7683 }
7684
7685 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) {
7686 ret = -EFAULT;
7687 goto exit;
7688 }
7689
7690 /* Make sure the command is NUL-terminated */
7691 command[priv_data->total_len] = '\0';
7692
7693 hdd_info("%s: %s", adapter->dev->name, command);
7694 ret = hdd_drv_cmd_process(adapter, command, priv_data);
7695
7696exit:
7697 if (command)
7698 kfree(command);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307699 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007700 return ret;
7701}
7702
7703#ifdef CONFIG_COMPAT
7704static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7705{
7706 struct {
7707 compat_uptr_t buf;
7708 int used_len;
7709 int total_len;
7710 } compat_priv_data;
7711 hdd_priv_data_t priv_data;
7712 int ret = 0;
7713
7714 /*
7715 * Note that adapter and ifr have already been verified by caller,
7716 * and HDD context has also been validated
7717 */
7718 if (copy_from_user(&compat_priv_data, ifr->ifr_data,
7719 sizeof(compat_priv_data))) {
7720 ret = -EFAULT;
7721 goto exit;
7722 }
7723 priv_data.buf = compat_ptr(compat_priv_data.buf);
7724 priv_data.used_len = compat_priv_data.used_len;
7725 priv_data.total_len = compat_priv_data.total_len;
7726 ret = hdd_driver_command(adapter, &priv_data);
7727exit:
7728 return ret;
7729}
7730#else /* CONFIG_COMPAT */
7731static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7732{
7733 /* will never be invoked */
7734 return 0;
7735}
7736#endif /* CONFIG_COMPAT */
7737
7738static int hdd_driver_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7739{
7740 hdd_priv_data_t priv_data;
7741 int ret = 0;
7742
7743 /*
7744 * Note that adapter and ifr have already been verified by caller,
7745 * and HDD context has also been validated
7746 */
7747 if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data)))
7748 ret = -EFAULT;
7749 else
7750 ret = hdd_driver_command(adapter, &priv_data);
7751
7752 return ret;
7753}
7754
7755/**
7756 * __hdd_ioctl() - ioctl handler for wlan network interfaces
7757 * @dev: device upon which the ioctl was received
7758 * @ifr: ioctl request information
7759 * @cmd: ioctl command
7760 *
7761 * This function does initial processing of wlan device ioctls.
7762 * Currently two flavors of ioctls are supported. The primary ioctl
7763 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used
7764 * for Android "DRIVER" commands. The other ioctl that is
7765 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used
7766 * for FTM on some platforms. This function simply verifies that the
7767 * driver is in a sane state, and that the ioctl is one of the
7768 * supported flavors, in which case flavor-specific handlers are
7769 * dispatched.
7770 *
7771 * Return: 0 on success, non-zero on error
7772 */
7773static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7774{
7775 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
7776 hdd_context_t *hdd_ctx;
7777 int ret;
7778
Jeff Johnson3c3994a2016-02-11 08:12:30 -08007779 ENTER_DEV(dev);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307780
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007781 if (dev != adapter->dev) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307782 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_FATAL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007783 "%s: HDD adapter/dev inconsistency", __func__);
7784 ret = -ENODEV;
7785 goto exit;
7786 }
7787
7788 if ((!ifr) || (!ifr->ifr_data)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307789 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007790 "%s: invalid data", __func__);
7791 ret = -EINVAL;
7792 goto exit;
7793 }
7794#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
Anurag Chouhan6d760662016-02-20 16:05:43 +05307795 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007796 if (SIOCIOCTLTX99 == cmd) {
7797 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr);
7798 goto exit;
7799 }
7800 }
7801#endif
7802
7803 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
7804 ret = wlan_hdd_validate_context(hdd_ctx);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307805 if (ret)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007806 goto exit;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007807
7808 switch (cmd) {
7809 case (SIOCDEVPRIVATE + 1):
7810 if (is_compat_task())
7811 ret = hdd_driver_compat_ioctl(adapter, ifr);
7812 else
7813 ret = hdd_driver_ioctl(adapter, ifr);
7814 break;
7815 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05307816 hddLog(QDF_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007817 __func__, cmd);
7818 ret = -EINVAL;
7819 break;
7820 }
7821exit:
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307822 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007823 return ret;
7824}
7825
7826/**
7827 * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces
7828 * @dev: device upon which the ioctl was received
7829 * @ifr: ioctl request information
7830 * @cmd: ioctl command
7831 *
7832 * This function acts as an SSR-protecting wrapper to __hdd_ioctl()
7833 * which is where the ioctls are really handled.
7834 *
7835 * Return: 0 on success, non-zero on error
7836 */
7837int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7838{
7839 int ret;
7840
7841 cds_ssr_protect(__func__);
7842 ret = __hdd_ioctl(dev, ifr, cmd);
7843 cds_ssr_unprotect(__func__);
7844 return ret;
7845}