blob: 4f5c3bb0db138678f9b58d0dc74a9048ba4a5347 [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
Archana Ramachandran3abc3912016-04-29 17:01:32 -070030/* denote that this file does not allow legacy hddLog */
31#define HDD_DISALLOW_LEGACY_HDDLOG 1
32
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080033#include <wlan_hdd_includes.h>
34#include <wlan_hdd_wowl.h>
35#include "wlan_hdd_trace.h"
36#include "wlan_hdd_ioctl.h"
37#include "wlan_hdd_power.h"
38#include "wlan_hdd_driver_ops.h"
39#include "cds_concurrency.h"
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +053040#include "wlan_hdd_hostapd.h"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080041
42#include "wlan_hdd_p2p.h"
43#include <linux/ctype.h>
44#include "wma.h"
45#include "wlan_hdd_napi.h"
46
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -080047#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080048#include <sme_api.h>
49#include <sir_api.h>
50#endif
51#include "hif.h"
52
53#if defined(LINUX_QCMBR)
54#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
55#endif
56
57/*
58 * Size of Driver command strings from upper layer
59 */
60#define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */
61#define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */
62
Rajeev Kumar8e3e2832015-11-06 16:02:54 -080063/*
64 * Ibss prop IE from command will be of size:
65 * size = sizeof(oui) + sizeof(oui_data) + 1(Element ID) + 1(EID Length)
66 * OUI_DATA should be at least 3 bytes long
67 */
68#define WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH (3)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080069
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -080070#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080071#define TID_MIN_VALUE 0
72#define TID_MAX_VALUE 15
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -080073#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080074
75/*
76 * Maximum buffer size used for returning the data back to user space
77 */
78#define WLAN_MAX_BUF_SIZE 1024
79#define WLAN_PRIV_DATA_MAX_LEN 8192
80
81/*
82 * Driver miracast parameters 0-Disabled
83 * 1-Source, 2-Sink
84 */
85#define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0
86#define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2
87
88/*
89 * When ever we need to print IBSSPEERINFOALL for more than 16 STA
90 * we will split the printing.
91 */
92#define NUM_OF_STA_DATA_TO_PRINT 16
93
94/*
95 * Android DRIVER command structures
96 */
97struct android_wifi_reassoc_params {
98 unsigned char bssid[18];
99 int channel;
100};
101
102#define ANDROID_WIFI_ACTION_FRAME_SIZE 1040
103struct android_wifi_af_params {
104 unsigned char bssid[18];
105 int channel;
106 int dwell_time;
107 int len;
108 unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE];
109};
110
111/*
112 * Define HDD driver command handling entry, each contains a command
113 * string and the handler.
114 */
115typedef int (*hdd_drv_cmd_handler_t)(hdd_adapter_t *adapter,
116 hdd_context_t *hdd_ctx,
117 uint8_t *cmd,
118 uint8_t cmd_name_len,
119 hdd_priv_data_t *priv_data);
120
121typedef struct {
122 const char *cmd;
123 hdd_drv_cmd_handler_t handler;
124} hdd_drv_cmd_t;
125
126#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
127#define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000
128#define WLAN_HDD_MAX_TCP_PORT 65535
129#endif
130
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800131static uint16_t cesium_pid;
132extern struct sock *cesium_nl_srv_sock;
133
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -0800134#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800135static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics,
136 const uint32_t staId, void *context)
137{
138 struct statsContext *stats_context = NULL;
139 hdd_adapter_t *adapter = NULL;
140
141 if (NULL == context) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700142 hdd_err("Bad param, context [%p]", context);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800143 return;
144 }
145
146 /*
147 * there is a race condition that exists between this callback
148 * function and the caller since the caller could time out either
149 * before or while this code is executing. we use a spinlock to
150 * serialize these actions
151 */
152 spin_lock(&hdd_context_lock);
153
154 stats_context = context;
155 adapter = stats_context->pAdapter;
156 if ((NULL == adapter) ||
157 (STATS_CONTEXT_MAGIC != stats_context->magic)) {
158 /*
159 * the caller presumably timed out so there is
160 * nothing we can do
161 */
162 spin_unlock(&hdd_context_lock);
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700163 hdd_warn("Invalid context, adapter [%p] magic [%08x]",
164 adapter, stats_context->magic);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800165 return;
166 }
167
168 /* context is valid so caller is still waiting */
169
170 /* paranoia: invalidate the magic */
171 stats_context->magic = 0;
172
173 /* copy over the tsm stats */
174 adapter->tsmStats.UplinkPktQueueDly = tsm_metrics.UplinkPktQueueDly;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530175 qdf_mem_copy(adapter->tsmStats.UplinkPktQueueDlyHist,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800176 tsm_metrics.UplinkPktQueueDlyHist,
177 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
178 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist[0]));
179 adapter->tsmStats.UplinkPktTxDly = tsm_metrics.UplinkPktTxDly;
180 adapter->tsmStats.UplinkPktLoss = tsm_metrics.UplinkPktLoss;
181 adapter->tsmStats.UplinkPktCount = tsm_metrics.UplinkPktCount;
182 adapter->tsmStats.RoamingCount = tsm_metrics.RoamingCount;
183 adapter->tsmStats.RoamingDly = tsm_metrics.RoamingDly;
184
185 /* notify the caller */
186 complete(&stats_context->completion);
187
188 /* serialization is complete */
189 spin_unlock(&hdd_context_lock);
190}
191
192static
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530193QDF_STATUS hdd_get_tsm_stats(hdd_adapter_t *adapter,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800194 const uint8_t tid,
195 tAniTrafStrmMetrics *tsm_metrics)
196{
197 hdd_station_ctx_t *hdd_sta_ctx = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530198 QDF_STATUS hstatus;
199 QDF_STATUS vstatus = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800200 unsigned long rc;
201 struct statsContext context;
202 hdd_context_t *hdd_ctx = NULL;
203
204 if (NULL == adapter) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700205 hdd_err("adapter is NULL");
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) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700223 hdd_err("Unable to retrieve statistics");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530224 vstatus = QDF_STATUS_E_FAULT;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800225 } else {
226 /* request was sent -- wait for the response */
227 rc = wait_for_completion_timeout(&context.completion,
228 msecs_to_jiffies(WLAN_WAIT_TIME_STATS));
229 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700230 hdd_err("SME timed out while retrieving statistics");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530231 vstatus = QDF_STATUS_E_TIMEOUT;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800232 }
233 }
234
235 /*
236 * either we never sent a request, we sent a request and received a
237 * response or we sent a request and timed out. if we never sent a
238 * request or if we sent a request and got a response, we want to
239 * clear the magic out of paranoia. if we timed out there is a
240 * race condition such that the callback function could be
241 * executing at the same time we are. of primary concern is if the
242 * callback function had already verified the "magic" but had not
243 * yet set the completion variable when a timeout occurred. we
244 * serialize these activities by invalidating the magic while
245 * holding a shared spinlock which will cause us to block if the
246 * callback is currently executing
247 */
248 spin_lock(&hdd_context_lock);
249 context.magic = 0;
250 spin_unlock(&hdd_context_lock);
251
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530252 if (QDF_STATUS_SUCCESS == vstatus) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800253 tsm_metrics->UplinkPktQueueDly =
254 adapter->tsmStats.UplinkPktQueueDly;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530255 qdf_mem_copy(tsm_metrics->UplinkPktQueueDlyHist,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800256 adapter->tsmStats.UplinkPktQueueDlyHist,
257 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
258 sizeof(adapter->tsmStats.
259 UplinkPktQueueDlyHist[0]));
260 tsm_metrics->UplinkPktTxDly = adapter->tsmStats.UplinkPktTxDly;
261 tsm_metrics->UplinkPktLoss = adapter->tsmStats.UplinkPktLoss;
262 tsm_metrics->UplinkPktCount = adapter->tsmStats.UplinkPktCount;
263 tsm_metrics->RoamingCount = adapter->tsmStats.RoamingCount;
264 tsm_metrics->RoamingDly = adapter->tsmStats.RoamingDly;
265 }
266 return vstatus;
267}
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -0800268#endif /*FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800269
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800270/* Function header is left blank intentionally */
271static int hdd_parse_setrmcenable_command(uint8_t *pValue,
272 uint8_t *pRmcEnable)
273{
274 uint8_t *inPtr = pValue;
275 int tempInt;
276 int v = 0;
277 char buf[32];
278 *pRmcEnable = 0;
279
280 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
281
282 if (NULL == inPtr) {
283 return 0;
284 }
285
286 else if (SPACE_ASCII_VALUE != *inPtr) {
287 return 0;
288 }
289
290 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
291 inPtr++;
292
293 if ('\0' == *inPtr) {
294 return 0;
295 }
296
297 sscanf(inPtr, "%32s ", buf);
298 v = kstrtos32(buf, 10, &tempInt);
299 if (v < 0) {
300 return -EINVAL;
301 }
302
303 *pRmcEnable = tempInt;
304
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700305 hdd_info("ucRmcEnable: %d", *pRmcEnable);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800306
307 return 0;
308}
309
310/* Function header is left blank intentionally */
311static int hdd_parse_setrmcactionperiod_command(uint8_t *pValue,
312 uint32_t *pActionPeriod)
313{
314 uint8_t *inPtr = pValue;
315 int tempInt;
316 int v = 0;
317 char buf[32];
318 *pActionPeriod = 0;
319
320 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
321
322 if (NULL == inPtr) {
323 return -EINVAL;
324 }
325
326 else if (SPACE_ASCII_VALUE != *inPtr) {
327 return -EINVAL;
328 }
329
330 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
331 inPtr++;
332
333 if ('\0' == *inPtr) {
334 return 0;
335 }
336
337 sscanf(inPtr, "%32s ", buf);
338 v = kstrtos32(buf, 10, &tempInt);
339 if (v < 0) {
340 return -EINVAL;
341 }
342
343 if ((tempInt < WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMIN) ||
344 (tempInt > WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMAX)) {
345 return -EINVAL;
346 }
347
348 *pActionPeriod = tempInt;
349
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700350 hdd_info("uActionPeriod: %d", *pActionPeriod);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800351
352 return 0;
353}
354
355/* Function header is left blank intentionally */
356static int hdd_parse_setrmcrate_command(uint8_t *pValue,
357 uint32_t *pRate,
358 tTxrateinfoflags *pTxFlags)
359{
360 uint8_t *inPtr = pValue;
361 int tempInt;
362 int v = 0;
363 char buf[32];
364 *pRate = 0;
365 *pTxFlags = 0;
366
367 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
368
369 if (NULL == inPtr) {
370 return -EINVAL;
371 }
372
373 else if (SPACE_ASCII_VALUE != *inPtr) {
374 return -EINVAL;
375 }
376
377 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
378 inPtr++;
379
380 if ('\0' == *inPtr) {
381 return 0;
382 }
383
384 sscanf(inPtr, "%32s ", buf);
385 v = kstrtos32(buf, 10, &tempInt);
386 if (v < 0) {
387 return -EINVAL;
388 }
389
390 switch (tempInt) {
391 default:
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700392 hdd_warn("Unsupported rate: %d", tempInt);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800393 return -EINVAL;
394 case 0:
395 case 6:
396 case 9:
397 case 12:
398 case 18:
399 case 24:
400 case 36:
401 case 48:
402 case 54:
403 *pTxFlags = eHAL_TX_RATE_LEGACY;
404 *pRate = tempInt * 10;
405 break;
406 case 65:
407 *pTxFlags = eHAL_TX_RATE_HT20;
408 *pRate = tempInt * 10;
409 break;
410 case 72:
411 *pTxFlags = eHAL_TX_RATE_HT20 | eHAL_TX_RATE_SGI;
412 *pRate = 722;
413 break;
414 }
415
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700416 hdd_info("Rate: %d", *pRate);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800417
418 return 0;
419}
420
421/**
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700422 * hdd_get_ibss_peer_info_cb() - IBSS peer Info request callback
423 * @UserData: Adapter private data
424 * @pPeerInfoRsp: Peer info response
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800425 *
426 * This is an asynchronous callback function from SME when the peer info
427 * is received
428 *
429 * Return: 0 for success non-zero for failure
430 */
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700431void
432hdd_get_ibss_peer_info_cb(void *pUserData,
433 tSirPeerInfoRspParams *pPeerInfo)
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800434{
435 hdd_adapter_t *adapter = (hdd_adapter_t *) pUserData;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800436 hdd_station_ctx_t *pStaCtx;
437 uint8_t i;
438
439 /* Sanity check */
440 if ((NULL == adapter) ||
441 (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700442 hdd_alert("invalid adapter or adapter has invalid magic");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800443 return;
444 }
445
446 pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
447 if (NULL != pPeerInfo && QDF_STATUS_SUCCESS == pPeerInfo->status) {
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700448 /* validate number of peers */
Sriram Madhvapathi58f0e272016-10-03 11:47:51 +0530449 if (pPeerInfo->numPeers > SIR_MAX_NUM_STA_IN_IBSS) {
450 hdd_warn("Limiting num_peers %u to %u",
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700451 pPeerInfo->numPeers, SIR_MAX_NUM_STA_IN_IBSS);
Sriram Madhvapathi58f0e272016-10-03 11:47:51 +0530452 pPeerInfo->numPeers = SIR_MAX_NUM_STA_IN_IBSS;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800453 }
Sriram Madhvapathi58f0e272016-10-03 11:47:51 +0530454 pStaCtx->ibss_peer_info.status = pPeerInfo->status;
455 pStaCtx->ibss_peer_info.numPeers = pPeerInfo->numPeers;
456
457 for (i = 0; i < pPeerInfo->numPeers; i++)
458 pStaCtx->ibss_peer_info.peerInfoParams[i] =
459 pPeerInfo->peerInfoParams[i];
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800460 } else {
Sriram Madhvapathi58f0e272016-10-03 11:47:51 +0530461 hdd_err("peerInfo %s: status %u, numPeers %u",
462 pPeerInfo ? "valid" : "null",
463 pPeerInfo ? pPeerInfo->status : QDF_STATUS_E_FAILURE,
464 pPeerInfo ? pPeerInfo->numPeers : 0);
465 pStaCtx->ibss_peer_info.numPeers = 0;
466 pStaCtx->ibss_peer_info.status = QDF_STATUS_E_FAILURE;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800467 }
468
469 complete(&adapter->ibss_peer_info_comp);
470}
471
472/**
473 * hdd_cfg80211_get_ibss_peer_info_all() - get ibss peers' info
474 * @adapter: Adapter context
475 *
476 * Request function to get IBSS peer info from lower layers
477 *
478 * Return: 0 for success non-zero for failure
479 */
480static
481QDF_STATUS hdd_cfg80211_get_ibss_peer_info_all(hdd_adapter_t *adapter)
482{
483 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
484 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
485 unsigned long rc;
486
487 INIT_COMPLETION(adapter->ibss_peer_info_comp);
488
489 retStatus = sme_request_ibss_peer_info(hHal, adapter,
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700490 hdd_get_ibss_peer_info_cb,
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800491 true, 0xFF);
492
493 if (QDF_STATUS_SUCCESS == retStatus) {
494 rc = wait_for_completion_timeout
495 (&adapter->ibss_peer_info_comp,
496 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
497
498 /* status will be 0 if timed out */
499 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700500 hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800501 retStatus = QDF_STATUS_E_FAILURE;
502 return retStatus;
503 }
504 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700505 hdd_warn("Warning: sme_request_ibss_peer_info Request failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800506 }
507
508 return retStatus;
509}
510
511/**
512 * hdd_cfg80211_get_ibss_peer_info() - get ibss peer info
513 * @adapter: Adapter context
514 * @staIdx: Sta index for which the peer info is requested
515 *
516 * Request function to get IBSS peer info from lower layers
517 *
518 * Return: 0 for success non-zero for failure
519 */
520static QDF_STATUS
521hdd_cfg80211_get_ibss_peer_info(hdd_adapter_t *adapter, uint8_t staIdx)
522{
523 unsigned long rc;
524 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
525 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
526
527 INIT_COMPLETION(adapter->ibss_peer_info_comp);
528
529 retStatus = sme_request_ibss_peer_info(hHal, adapter,
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700530 hdd_get_ibss_peer_info_cb,
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800531 false, staIdx);
532
533 if (QDF_STATUS_SUCCESS == retStatus) {
534 rc = wait_for_completion_timeout(
535 &adapter->ibss_peer_info_comp,
536 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
537
538 /* status = 0 on timeout */
539 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700540 hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800541 retStatus = QDF_STATUS_E_FAILURE;
542 return retStatus;
543 }
544 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700545 hdd_warn("Warning: sme_request_ibss_peer_info Request failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800546 }
547
548 return retStatus;
549}
550
551/* Function header is left blank intentionally */
552QDF_STATUS
553hdd_parse_get_ibss_peer_info(uint8_t *pValue, struct qdf_mac_addr *pPeerMacAddr)
554{
555 uint8_t *inPtr = pValue;
556 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
557
558 if (NULL == inPtr) {
559 return QDF_STATUS_E_FAILURE;;
560 }
561
562 else if (SPACE_ASCII_VALUE != *inPtr) {
563 return QDF_STATUS_E_FAILURE;;
564 }
565
566 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
567 inPtr++;
568
569 if ('\0' == *inPtr) {
570 return QDF_STATUS_E_FAILURE;;
571 }
572
573 if (inPtr[2] != ':' || inPtr[5] != ':' || inPtr[8] != ':' ||
574 inPtr[11] != ':' || inPtr[14] != ':') {
575 return QDF_STATUS_E_FAILURE;;
576 }
577 sscanf(inPtr, "%2x:%2x:%2x:%2x:%2x:%2x",
578 (unsigned int *)&pPeerMacAddr->bytes[0],
579 (unsigned int *)&pPeerMacAddr->bytes[1],
580 (unsigned int *)&pPeerMacAddr->bytes[2],
581 (unsigned int *)&pPeerMacAddr->bytes[3],
582 (unsigned int *)&pPeerMacAddr->bytes[4],
583 (unsigned int *)&pPeerMacAddr->bytes[5]);
584
585 return QDF_STATUS_SUCCESS;
586}
587
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800588static void hdd_get_band_helper(hdd_context_t *hdd_ctx, int *pBand)
589{
590 eCsrBand band = -1;
591 sme_get_freq_band((tHalHandle) (hdd_ctx->hHal), &band);
592 switch (band) {
593 case eCSR_BAND_ALL:
594 *pBand = WLAN_HDD_UI_BAND_AUTO;
595 break;
596
597 case eCSR_BAND_24:
598 *pBand = WLAN_HDD_UI_BAND_2_4_GHZ;
599 break;
600
601 case eCSR_BAND_5G:
602 *pBand = WLAN_HDD_UI_BAND_5_GHZ;
603 break;
604
605 default:
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700606 hdd_warn("Invalid Band %d", band);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800607 *pBand = -1;
608 break;
609 }
610}
611
612/**
613 * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel
614 * @data: input data
615 * @target_ap_bssid: pointer to bssid (output parameter)
616 * @channel: pointer to channel (output parameter)
617 *
618 * Return: 0 if parsing is successful; -EINVAL otherwise
619 */
620static int _hdd_parse_bssid_and_chan(const uint8_t **data,
621 uint8_t *bssid,
622 uint8_t *channel)
623{
624 const uint8_t *in_ptr;
625 int v = 0;
626 int temp_int;
627 uint8_t temp_buf[32];
628
629 /* 12 hexa decimal digits, 5 ':' and '\0' */
630 uint8_t mac_addr[18];
631
632 if (!data || !*data)
633 return -EINVAL;
634
635 in_ptr = *data;
636
637 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
638 /* no argument after the command */
639 if (NULL == in_ptr)
640 goto error;
641 /* no space after the command */
642 else if (SPACE_ASCII_VALUE != *in_ptr)
643 goto error;
644
645 /* remove empty spaces */
646 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
647 in_ptr++;
648
649 /* no argument followed by spaces */
650 if ('\0' == *in_ptr)
651 goto error;
652
653 v = sscanf(in_ptr, "%17s", mac_addr);
654 if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700655 hdd_err("Invalid MAC address or All hex inputs are not read (%d)",
656 v);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800657 goto error;
658 }
659
660 bssid[0] = hex_to_bin(mac_addr[0]) << 4 |
661 hex_to_bin(mac_addr[1]);
662 bssid[1] = hex_to_bin(mac_addr[3]) << 4 |
663 hex_to_bin(mac_addr[4]);
664 bssid[2] = hex_to_bin(mac_addr[6]) << 4 |
665 hex_to_bin(mac_addr[7]);
666 bssid[3] = hex_to_bin(mac_addr[9]) << 4 |
667 hex_to_bin(mac_addr[10]);
668 bssid[4] = hex_to_bin(mac_addr[12]) << 4 |
669 hex_to_bin(mac_addr[13]);
670 bssid[5] = hex_to_bin(mac_addr[15]) << 4 |
671 hex_to_bin(mac_addr[16]);
672
673 /* point to the next argument */
674 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
675 /* no argument after the command */
676 if (NULL == in_ptr)
677 goto error;
678
679 /* remove empty spaces */
680 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
681 in_ptr++;
682
683 /* no argument followed by spaces */
684 if ('\0' == *in_ptr)
685 goto error;
686
687 /* get the next argument ie the channel number */
688 v = sscanf(in_ptr, "%31s ", temp_buf);
689 if (1 != v)
690 goto error;
691
692 v = kstrtos32(temp_buf, 10, &temp_int);
693 if ((v < 0) || (temp_int < 0) ||
694 (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX))
695 return -EINVAL;
696
697 *channel = temp_int;
698 *data = in_ptr;
699 return 0;
700error:
701 *data = in_ptr;
702 return -EINVAL;
703}
704
705/**
706 * hdd_parse_send_action_frame_data() - HDD Parse send action frame data
707 * @pValue: Pointer to input data
708 * @pTargetApBssid: Pointer to target Ap bssid
709 * @pChannel: Pointer to the Target AP channel
710 * @pDwellTime: Pointer to the time to stay off-channel
711 * after transmitting action frame
712 * @pBuf: Pointer to data
713 * @pBufLen: Pointer to data length
714 *
715 * This function parses the send action frame data passed in the format
716 * SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
717 *
718 * Return: 0 for success non-zero for failure
719 */
720static int
721hdd_parse_send_action_frame_v1_data(const uint8_t *pValue,
722 uint8_t *pTargetApBssid,
723 uint8_t *pChannel, uint8_t *pDwellTime,
724 uint8_t **pBuf, uint8_t *pBufLen)
725{
726 const uint8_t *inPtr = pValue;
727 const uint8_t *dataEnd;
728 int tempInt;
729 int j = 0;
730 int i = 0;
731 int v = 0;
732 uint8_t tempBuf[32];
733 uint8_t tempByte = 0;
734
735 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
736 return -EINVAL;
737
738 /* point to the next argument */
739 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
740 /* no argument after the command */
741 if (NULL == inPtr)
742 return -EINVAL;
743 /* removing empty spaces */
744 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
745 inPtr++;
746
747 /* no argument followed by spaces */
748 if ('\0' == *inPtr) {
749 return -EINVAL;
750 }
751
752 /* getting the next argument ie the dwell time */
753 v = sscanf(inPtr, "%31s ", tempBuf);
754 if (1 != v)
755 return -EINVAL;
756
757 v = kstrtos32(tempBuf, 10, &tempInt);
758 if (v < 0 || tempInt < 0)
759 return -EINVAL;
760
761 *pDwellTime = tempInt;
762
763 /* point to the next argument */
764 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
765 /* no argument after the command */
766 if (NULL == inPtr)
767 return -EINVAL;
768 /* removing empty spaces */
769 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
770 inPtr++;
771
772 /* no argument followed by spaces */
773 if ('\0' == *inPtr) {
774 return -EINVAL;
775 }
776
777 /* find the length of data */
778 dataEnd = inPtr;
779 while (('\0' != *dataEnd)) {
780 dataEnd++;
781 }
782 *pBufLen = dataEnd - inPtr;
783 if (*pBufLen <= 0)
784 return -EINVAL;
785
786 /*
787 * Allocate the number of bytes based on the number of input characters
788 * whether it is even or odd.
789 * if the number of input characters are even, then we need N/2 byte.
790 * if the number of input characters are odd, then we need do (N+1)/2
791 * to compensate rounding off.
792 * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
793 * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes
794 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530795 *pBuf = qdf_mem_malloc((*pBufLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800796 if (NULL == *pBuf) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700797 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800798 return -ENOMEM;
799 }
800
801 /* the buffer received from the upper layer is character buffer,
802 * we need to prepare the buffer taking 2 characters in to a U8 hex
803 * decimal number for example 7f0000f0...form a buffer to contain 7f
804 * in 0th location, 00 in 1st and f0 in 3rd location
805 */
806 for (i = 0, j = 0; j < *pBufLen; j += 2) {
807 if (j + 1 == *pBufLen) {
808 tempByte = hex_to_bin(inPtr[j]);
809 } else {
810 tempByte =
811 (hex_to_bin(inPtr[j]) << 4) |
812 (hex_to_bin(inPtr[j + 1]));
813 }
814 (*pBuf)[i++] = tempByte;
815 }
816 *pBufLen = i;
817 return 0;
818}
819
820/**
821 * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data
822 * @pValue: Pointer to input data (its a NULL terminated string)
823 * @pTargetApBssid: Pointer to target Ap bssid
824 * @pChannel: Pointer to the Target AP channel
825 *
826 * This function parses the reasoc command data passed in the format
827 * REASSOC<space><bssid><space><channel>
828 *
829 * Return: 0 for success non-zero for failure
830 */
831static int hdd_parse_reassoc_command_v1_data(const uint8_t *pValue,
832 uint8_t *pTargetApBssid,
833 uint8_t *pChannel)
834{
835 const uint8_t *inPtr = pValue;
836
837 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
838 return -EINVAL;
839
840 return 0;
841}
842
Naveen Rawat05376ee2016-07-18 16:43:32 -0700843#ifdef WLAN_FEATURE_ROAM_OFFLOAD
844void hdd_wma_send_fastreassoc_cmd(int sessionId, const tSirMacAddr bssid,
845 int channel)
846{
847 struct wma_roam_invoke_cmd *fastreassoc;
848 cds_msg_t msg = {0};
849
850 fastreassoc = qdf_mem_malloc(sizeof(*fastreassoc));
851 if (NULL == fastreassoc) {
852 hdd_err("qdf_mem_malloc failed for fastreassoc");
853 return;
854 }
855 fastreassoc->vdev_id = sessionId;
856 fastreassoc->channel = channel;
857 fastreassoc->bssid[0] = bssid[0];
858 fastreassoc->bssid[1] = bssid[1];
859 fastreassoc->bssid[2] = bssid[2];
860 fastreassoc->bssid[3] = bssid[3];
861 fastreassoc->bssid[4] = bssid[4];
862 fastreassoc->bssid[5] = bssid[5];
863
864 msg.type = SIR_HAL_ROAM_INVOKE;
865 msg.reserved = 0;
866 msg.bodyptr = fastreassoc;
867 if (QDF_STATUS_SUCCESS != cds_mq_post_message(QDF_MODULE_ID_WMA,
868 &msg)) {
869 qdf_mem_free(fastreassoc);
870 hdd_err("Not able to post ROAM_INVOKE_CMD message to WMA");
871 }
872}
873#else
874void hdd_wma_send_fastreassoc_cmd(int sessionId, const tSirMacAddr bssid,
875 int channel)
876{
877}
878
879#endif
880
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800881/**
882 * hdd_reassoc() - perform a userspace-directed reassoc
883 * @adapter: Adapter upon which the command was received
884 * @bssid: BSSID with which to reassociate
885 * @channel: channel upon which to reassociate
Varun Reddy Yeturu3e91dad2016-07-18 15:58:47 -0700886 * @src: The source for the trigger of this action
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800887 *
888 * This function performs a userspace-directed reassoc operation
889 *
890 * Return: 0 for success non-zero for failure
891 */
Varun Reddy Yeturu3e91dad2016-07-18 15:58:47 -0700892int hdd_reassoc(hdd_adapter_t *adapter, const uint8_t *bssid,
893 const uint8_t channel, const handoff_src src)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800894{
895 hdd_station_ctx_t *pHddStaCtx;
Naveen Rawat05376ee2016-07-18 16:43:32 -0700896 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800897 int ret = 0;
898
Naveen Rawat05376ee2016-07-18 16:43:32 -0700899 if (hdd_ctx == NULL) {
900 hdd_err("Invalid hdd ctx");
901 return -EINVAL;
902 }
903
Krunal Sonibe766b02016-03-10 13:00:44 -0800904 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800905 hdd_warn("Unsupported in mode %s(%d)",
906 hdd_device_mode_to_string(adapter->device_mode),
907 adapter->device_mode);
908 return -EINVAL;
909 }
910
911 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
912
913 /* if not associated, no need to proceed with reassoc */
914 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700915 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800916 ret = -EINVAL;
917 goto exit;
918 }
919
920 /*
921 * if the target bssid is same as currently associated AP,
922 * then no need to proceed with reassoc
923 */
924 if (!memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530925 QDF_MAC_ADDR_SIZE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700926 hdd_info("Reassoc BSSID is same as currently associated AP bssid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800927 ret = -EINVAL;
928 goto exit;
929 }
930
931 /* Check channel number is a valid channel number */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530932 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800933 wlan_hdd_validate_operation_channel(adapter, channel)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700934 hdd_err("Invalid Channel %d", channel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800935 ret = -EINVAL;
936 goto exit;
937 }
938
939 /* Proceed with reassoc */
Naveen Rawat05376ee2016-07-18 16:43:32 -0700940 if (roaming_offload_enabled(hdd_ctx)) {
941 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
942 bssid, (int)channel);
943 } else {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800944 tCsrHandoffRequest handoffInfo;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800945
946 handoffInfo.channel = channel;
Varun Reddy Yeturu3e91dad2016-07-18 15:58:47 -0700947 handoffInfo.src = src;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530948 qdf_mem_copy(handoffInfo.bssid.bytes, bssid, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800949 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
950 &handoffInfo);
951 }
952exit:
953 return ret;
954}
955
956/**
957 * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command
958 * @adapter: Adapter upon which the command was received
959 * @command: ASCII text command that was received
960 *
961 * This function parses the v1 REASSOC command with the format
962 *
963 * REASSOC xx:xx:xx:xx:xx:xx CH
964 *
965 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
966 * BSSID and CH is the ASCII representation of the channel. For
967 * example
968 *
969 * REASSOC 00:0a:0b:11:22:33 48
970 *
971 * Return: 0 for success non-zero for failure
972 */
973static int hdd_parse_reassoc_v1(hdd_adapter_t *adapter, const char *command)
974{
975 uint8_t channel = 0;
976 tSirMacAddr bssid;
977 int ret;
978
979 ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel);
980 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700981 hdd_err("Failed to parse reassoc command data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800982 } else {
Varun Reddy Yeturu3e91dad2016-07-18 15:58:47 -0700983 ret = hdd_reassoc(adapter, bssid, channel, REASSOC);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800984 }
985 return ret;
986}
987
988/**
989 * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command
990 * @adapter: Adapter upon which the command was received
991 * @command: Command that was received, ASCII command
992 * followed by binary data
993 *
994 * This function parses the v2 REASSOC command with the format
995 *
996 * REASSOC <android_wifi_reassoc_params>
997 *
998 * Return: 0 for success non-zero for failure
999 */
1000static int hdd_parse_reassoc_v2(hdd_adapter_t *adapter, const char *command)
1001{
1002 struct android_wifi_reassoc_params params;
1003 tSirMacAddr bssid;
1004 int ret;
1005
1006 /* The params are located after "REASSOC " */
1007 memcpy(&params, command + 8, sizeof(params));
1008
1009 if (!mac_pton(params.bssid, (u8 *) &bssid)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001010 hdd_err("MAC address parsing failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001011 ret = -EINVAL;
1012 } else {
Varun Reddy Yeturu3e91dad2016-07-18 15:58:47 -07001013 ret = hdd_reassoc(adapter, bssid, params.channel, REASSOC);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001014 }
1015 return ret;
1016}
1017
1018/**
1019 * hdd_parse_reassoc() - parse the REASSOC command
1020 * @adapter: Adapter upon which the command was received
1021 * @command: Command that was received
1022 *
1023 * There are two different versions of the REASSOC command. Version 1
1024 * of the command contains a parameter list that is ASCII characters
1025 * whereas version 2 contains a combination of ASCII and binary
1026 * payload. Determine if a version 1 or a version 2 command is being
1027 * parsed by examining the parameters, and then dispatch the parser
1028 * that is appropriate for the command.
1029 *
1030 * Return: 0 for success non-zero for failure
1031 */
1032static int hdd_parse_reassoc(hdd_adapter_t *adapter, const char *command)
1033{
1034 int ret;
1035
1036 /* both versions start with "REASSOC "
1037 * v1 has a bssid and channel # as an ASCII string
1038 * REASSOC xx:xx:xx:xx:xx:xx CH
1039 * v2 has a C struct
1040 * REASSOC <binary c struct>
1041 *
1042 * The first field in the v2 struct is also the bssid in ASCII.
1043 * But in the case of a v2 message the BSSID is NUL-terminated.
1044 * Hence we can peek at that offset to see if this is V1 or V2
1045 * REASSOC xx:xx:xx:xx:xx:xx*
1046 * 1111111111222222
1047 * 01234567890123456789012345
1048 */
1049 if (command[25]) {
1050 ret = hdd_parse_reassoc_v1(adapter, command);
1051 } else {
1052 ret = hdd_parse_reassoc_v2(adapter, command);
1053 }
1054
1055 return ret;
1056}
1057
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001058/**
1059 * hdd_sendactionframe() - send a userspace-supplied action frame
1060 * @adapter: Adapter upon which the command was received
1061 * @bssid: BSSID target of the action frame
1062 * @channel: Channel upon which to send the frame
1063 * @dwell_time: Amount of time to dwell when the frame is sent
1064 * @payload_len:Length of the payload
1065 * @payload: Payload of the frame
1066 *
1067 * This function sends a userspace-supplied action frame
1068 *
1069 * Return: 0 for success non-zero for failure
1070 */
1071static int
1072hdd_sendactionframe(hdd_adapter_t *adapter, const uint8_t *bssid,
1073 const uint8_t channel, const uint8_t dwell_time,
1074 const uint8_t payload_len, const uint8_t *payload)
1075{
1076 struct ieee80211_channel chan;
1077 uint8_t frame_len;
1078 uint8_t *frame;
1079 struct ieee80211_hdr_3addr *hdr;
1080 u64 cookie;
1081 hdd_station_ctx_t *pHddStaCtx;
1082 hdd_context_t *hdd_ctx;
1083 int ret = 0;
1084 tpSirMacVendorSpecificFrameHdr pVendorSpecific =
1085 (tpSirMacVendorSpecificFrameHdr) payload;
1086#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1087 struct cfg80211_mgmt_tx_params params;
1088#endif
1089
Krunal Sonibe766b02016-03-10 13:00:44 -08001090 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001091 hdd_warn("Unsupported in mode %s(%d)",
1092 hdd_device_mode_to_string(adapter->device_mode),
1093 adapter->device_mode);
1094 return -EINVAL;
1095 }
1096
1097 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
1098 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1099
1100 /* if not associated, no need to send action frame */
1101 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001102 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001103 ret = -EINVAL;
1104 goto exit;
1105 }
1106
1107 /*
1108 * if the target bssid is different from currently associated AP,
1109 * then no need to send action frame
1110 */
1111 if (memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301112 QDF_MAC_ADDR_SIZE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001113 hdd_info("STA is not associated to this AP");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001114 ret = -EINVAL;
1115 goto exit;
1116 }
1117
1118 chan.center_freq = sme_chn_to_freq(channel);
1119 /* Check if it is specific action frame */
1120 if (pVendorSpecific->category ==
1121 SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) {
1122 static const uint8_t Oui[] = { 0x00, 0x00, 0xf0 };
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301123 if (!qdf_mem_cmp(pVendorSpecific->Oui, (void *)Oui, 3)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001124 /*
1125 * if the channel number is different from operating
1126 * channel then no need to send action frame
1127 */
1128 if (channel != 0) {
1129 if (channel !=
1130 pHddStaCtx->conn_info.operationChannel) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001131 hdd_info("channel(%d) is different from operating channel(%d)",
1132 channel,
1133 pHddStaCtx->conn_info.
1134 operationChannel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001135 ret = -EINVAL;
1136 goto exit;
1137 }
1138 /*
1139 * If channel number is specified and same
1140 * as home channel, ensure that action frame
1141 * is sent immediately by cancelling
1142 * roaming scans. Otherwise large dwell times
1143 * may cause long delays in sending action
1144 * frames.
1145 */
1146 sme_abort_roam_scan(hdd_ctx->hHal,
1147 adapter->sessionId);
1148 } else {
1149 /*
1150 * 0 is accepted as current home channel,
1151 * delayed transmission of action frame is ok.
1152 */
1153 chan.center_freq =
1154 sme_chn_to_freq(pHddStaCtx->conn_info.
1155 operationChannel);
1156 }
1157 }
1158 }
1159 if (chan.center_freq == 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001160 hdd_err("Invalid channel number %d", channel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001161 ret = -EINVAL;
1162 goto exit;
1163 }
1164
1165 frame_len = payload_len + 24;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301166 frame = qdf_mem_malloc(frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001167 if (!frame) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001168 hdd_err("memory allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001169 ret = -ENOMEM;
1170 goto exit;
1171 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301172 qdf_mem_zero(frame, frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001173
1174 hdr = (struct ieee80211_hdr_3addr *)frame;
1175 hdr->frame_control =
1176 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301177 qdf_mem_copy(hdr->addr1, bssid, QDF_MAC_ADDR_SIZE);
1178 qdf_mem_copy(hdr->addr2, adapter->macAddressCurrent.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301179 QDF_MAC_ADDR_SIZE);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301180 qdf_mem_copy(hdr->addr3, bssid, QDF_MAC_ADDR_SIZE);
1181 qdf_mem_copy(hdr + 1, payload, payload_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001182
1183#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1184 params.chan = &chan;
1185 params.offchan = 0;
1186 params.wait = dwell_time;
1187 params.buf = frame;
1188 params.len = frame_len;
1189 params.no_cck = 1;
1190 params.dont_wait_for_ack = 1;
1191 ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, &params, &cookie);
1192#else
1193 ret = wlan_hdd_mgmt_tx(NULL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001194 &(adapter->wdev),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001195 &chan, 0,
Amar Singhal01098f72015-10-08 11:55:32 -07001196
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001197 dwell_time, frame, frame_len, 1, 1, &cookie);
1198#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
1199
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301200 qdf_mem_free(frame);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001201exit:
1202 return ret;
1203}
1204
1205/**
1206 * hdd_parse_sendactionframe_v1() - parse version 1 of the
1207 * SENDACTIONFRAME command
1208 * @adapter: Adapter upon which the command was received
1209 * @command: ASCII text command that was received
1210 *
1211 * This function parses the v1 SENDACTIONFRAME command with the format
1212 *
1213 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx
1214 *
1215 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
1216 * BSSID, CH is the ASCII representation of the channel, DW is the
1217 * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII
1218 * payload. For example
1219 *
1220 * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee
1221 *
1222 * Return: 0 for success non-zero for failure
1223 */
1224static int
1225hdd_parse_sendactionframe_v1(hdd_adapter_t *adapter, const char *command)
1226{
1227 uint8_t channel = 0;
1228 uint8_t dwell_time = 0;
1229 uint8_t payload_len = 0;
1230 uint8_t *payload = NULL;
1231 tSirMacAddr bssid;
1232 int ret;
1233
1234 ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel,
1235 &dwell_time, &payload,
1236 &payload_len);
1237 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001238 hdd_err("Failed to parse send action frame data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001239 } else {
1240 ret = hdd_sendactionframe(adapter, bssid, channel,
1241 dwell_time, payload_len, payload);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301242 qdf_mem_free(payload);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001243 }
1244
1245 return ret;
1246}
1247
1248/**
1249 * hdd_parse_sendactionframe_v2() - parse version 2 of the
1250 * SENDACTIONFRAME command
1251 * @adapter: Adapter upon which the command was received
1252 * @command: Command that was received, ASCII command
1253 * followed by binary data
1254 *
1255 * This function parses the v2 SENDACTIONFRAME command with the format
1256 *
1257 * SENDACTIONFRAME <android_wifi_af_params>
1258 *
1259 * Return: 0 for success non-zero for failure
1260 */
1261static int
1262hdd_parse_sendactionframe_v2(hdd_adapter_t *adapter, const char *command)
1263{
1264 struct android_wifi_af_params *params;
1265 tSirMacAddr bssid;
1266 int ret;
1267
1268 /* params are large so keep off the stack */
1269 params = kmalloc(sizeof(*params), GFP_KERNEL);
1270 if (!params)
1271 return -ENOMEM;
1272
1273 /* The params are located after "SENDACTIONFRAME " */
1274 memcpy(params, command + 16, sizeof(*params));
1275
1276 if (!mac_pton(params->bssid, (u8 *) &bssid)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001277 hdd_err("MAC address parsing failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001278 ret = -EINVAL;
1279 } else {
1280 ret = hdd_sendactionframe(adapter, bssid, params->channel,
1281 params->dwell_time, params->len,
1282 params->data);
1283 }
1284 kfree(params);
1285 return ret;
1286}
1287
1288/**
1289 * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command
1290 * @adapter: Adapter upon which the command was received
1291 * @command: Command that was received
1292 *
1293 * There are two different versions of the SENDACTIONFRAME command.
1294 * Version 1 of the command contains a parameter list that is ASCII
1295 * characters whereas version 2 contains a combination of ASCII and
1296 * binary payload. Determine if a version 1 or a version 2 command is
1297 * being parsed by examining the parameters, and then dispatch the
1298 * parser that is appropriate for the version of the command.
1299 *
1300 * Return: 0 for success non-zero for failure
1301 */
1302static int
1303hdd_parse_sendactionframe(hdd_adapter_t *adapter, const char *command)
1304{
1305 int ret;
1306
1307 /*
1308 * both versions start with "SENDACTIONFRAME "
1309 * v1 has a bssid and other parameters as an ASCII string
1310 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME
1311 * v2 has a C struct
1312 * SENDACTIONFRAME <binary c struct>
1313 *
1314 * The first field in the v2 struct is also the bssid in ASCII.
1315 * But in the case of a v2 message the BSSID is NUL-terminated.
1316 * Hence we can peek at that offset to see if this is V1 or V2
1317 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx*
1318 * 111111111122222222223333
1319 * 0123456789012345678901234567890123
1320 */
1321 if (command[33]) {
1322 ret = hdd_parse_sendactionframe_v1(adapter, command);
1323 } else {
1324 ret = hdd_parse_sendactionframe_v2(adapter, command);
1325 }
1326
1327 return ret;
1328}
1329
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001330/**
1331 * hdd_parse_channellist() - HDD Parse channel list
1332 * @pValue: Pointer to input channel list
1333 * @ChannelList: Pointer to local output array to record
1334 * channel list
1335 * @pNumChannels: Pointer to number of roam scan channels
1336 *
1337 * This function parses the channel list passed in the format
1338 * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space>Channel 2<space>Channel N
1339 * if the Number of channels (N) does not match with the actual number
1340 * of channels passed then take the minimum of N and count of
1341 * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48,
1342 * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48,
1343 * ignore 5 and take 36, 40, 44 and 48. This function does not take care of
1344 * removing duplicate channels from the list
1345 *
1346 * Return: 0 for success non-zero for failure
1347 */
1348static int
1349hdd_parse_channellist(const uint8_t *pValue, uint8_t *pChannelList,
1350 uint8_t *pNumChannels)
1351{
1352 const uint8_t *inPtr = pValue;
1353 int tempInt;
1354 int j = 0;
1355 int v = 0;
1356 char buf[32];
1357
1358 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1359 /* no argument after the command */
1360 if (NULL == inPtr) {
1361 return -EINVAL;
1362 }
1363
1364 /* no space after the command */
1365 else if (SPACE_ASCII_VALUE != *inPtr) {
1366 return -EINVAL;
1367 }
1368
1369 /* remove empty spaces */
1370 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1371 inPtr++;
1372
1373 /* no argument followed by spaces */
1374 if ('\0' == *inPtr) {
1375 return -EINVAL;
1376 }
1377
1378 /* get the first argument ie the number of channels */
1379 v = sscanf(inPtr, "%31s ", buf);
1380 if (1 != v)
1381 return -EINVAL;
1382
1383 v = kstrtos32(buf, 10, &tempInt);
1384 if ((v < 0) ||
1385 (tempInt <= 0) || (tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN)) {
1386 return -EINVAL;
1387 }
1388
1389 *pNumChannels = tempInt;
1390
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001391 hdd_info("Number of channels are: %d", *pNumChannels);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001392
1393 for (j = 0; j < (*pNumChannels); j++) {
1394 /*
1395 * inPtr pointing to the beginning of first space after number
1396 * of channels
1397 */
1398 inPtr = strpbrk(inPtr, " ");
1399 /* no channel list after the number of channels argument */
1400 if (NULL == inPtr) {
1401 if (0 != j) {
1402 *pNumChannels = j;
1403 return 0;
1404 } else {
1405 return -EINVAL;
1406 }
1407 }
1408
1409 /* remove empty space */
1410 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1411 inPtr++;
1412
1413 /*
1414 * no channel list after the number of channels
1415 * argument and spaces
1416 */
1417 if ('\0' == *inPtr) {
1418 if (0 != j) {
1419 *pNumChannels = j;
1420 return 0;
1421 } else {
1422 return -EINVAL;
1423 }
1424 }
1425
1426 v = sscanf(inPtr, "%31s ", buf);
1427 if (1 != v)
1428 return -EINVAL;
1429
1430 v = kstrtos32(buf, 10, &tempInt);
1431 if ((v < 0) ||
1432 (tempInt <= 0) ||
1433 (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
1434 return -EINVAL;
1435 }
1436 pChannelList[j] = tempInt;
1437
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001438 hdd_info("Channel %d added to preferred channel list",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001439 pChannelList[j]);
1440 }
1441
1442 return 0;
1443}
1444
1445/**
1446 * hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the
1447 * SETROAMSCANCHANNELS command
1448 * @adapter: Adapter upon which the command was received
1449 * @command: ASCII text command that was received
1450 *
1451 * This function parses the v1 SETROAMSCANCHANNELS command with the format
1452 *
1453 * SETROAMSCANCHANNELS N C1 C2 ... Cn
1454 *
1455 * Where "N" is the ASCII representation of the number of channels and
1456 * C1 thru Cn is the ASCII representation of the channels. For example
1457 *
1458 * SETROAMSCANCHANNELS 4 36 40 44 48
1459 *
1460 * Return: 0 for success non-zero for failure
1461 */
1462static int
1463hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *adapter,
1464 const char *command)
1465{
1466 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1467 uint8_t num_chan = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301468 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001469 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1470 int ret;
1471
1472 ret = hdd_parse_channellist(command, channel_list, &num_chan);
1473 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001474 hdd_err("Failed to parse channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001475 goto exit;
1476 }
1477
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301478 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001479 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1480 adapter->sessionId, num_chan));
1481
1482 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001483 hdd_err("number of channels (%d) supported exceeded max (%d)",
1484 num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001485 ret = -EINVAL;
1486 goto exit;
1487 }
1488
1489 status =
1490 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1491 adapter->sessionId,
1492 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301493 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001494 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001495 ret = -EINVAL;
1496 goto exit;
1497 }
1498exit:
1499 return ret;
1500}
1501
1502/**
1503 * hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the
1504 * SETROAMSCANCHANNELS command
1505 * @adapter: Adapter upon which the command was received
1506 * @command: Command that was received, ASCII command
1507 * followed by binary data
1508 *
1509 * This function parses the v2 SETROAMSCANCHANNELS command with the format
1510 *
1511 * SETROAMSCANCHANNELS [N][C1][C2][Cn]
1512 *
1513 * The command begins with SETROAMSCANCHANNELS followed by a space, but
1514 * what follows the space is an array of u08 parameters. For example
1515 *
1516 * SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30]
1517 *
1518 * Return: 0 for success non-zero for failure
1519 */
1520static int
1521hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *adapter,
1522 const char *command)
1523{
1524 const uint8_t *value;
1525 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1526 uint8_t channel;
1527 uint8_t num_chan;
1528 int i;
1529 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301530 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001531 int ret = 0;
1532
1533 /* array of values begins after "SETROAMSCANCHANNELS " */
1534 value = command + 20;
1535
1536 num_chan = *value++;
1537 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001538 hdd_err("number of channels (%d) supported exceeded max (%d)",
1539 num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001540 ret = -EINVAL;
1541 goto exit;
1542 }
1543
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301544 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001545 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1546 adapter->sessionId, num_chan));
1547
1548 for (i = 0; i < num_chan; i++) {
1549 channel = *value++;
1550 if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001551 hdd_err("index %d invalid channel %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001552 i, channel);
1553 ret = -EINVAL;
1554 goto exit;
1555 }
1556 channel_list[i] = channel;
1557 }
1558 status =
1559 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1560 adapter->sessionId,
1561 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301562 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001563 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001564 ret = -EINVAL;
1565 goto exit;
1566 }
1567exit:
1568 return ret;
1569}
1570
1571/**
1572 * hdd_parse_set_roam_scan_channels() - parse the
1573 * SETROAMSCANCHANNELS command
1574 * @adapter: Adapter upon which the command was received
1575 * @command: Command that was received
1576 *
1577 * There are two different versions of the SETROAMSCANCHANNELS command.
1578 * Version 1 of the command contains a parameter list that is ASCII
1579 * characters whereas version 2 contains a binary payload. Determine
1580 * if a version 1 or a version 2 command is being parsed by examining
1581 * the parameters, and then dispatch the parser that is appropriate for
1582 * the command.
1583 *
1584 * Return: 0 for success non-zero for failure
1585 */
1586static int
1587hdd_parse_set_roam_scan_channels(hdd_adapter_t *adapter, const char *command)
1588{
1589 const char *cursor;
1590 char ch;
1591 bool v1;
1592 int ret;
1593
1594 /* start after "SETROAMSCANCHANNELS " */
1595 cursor = command + 20;
1596
1597 /* assume we have a version 1 command until proven otherwise */
1598 v1 = true;
1599
1600 /* v1 params will only contain ASCII digits and space */
1601 while ((ch = *cursor++) && v1) {
1602 if (!(isdigit(ch) || isspace(ch))) {
1603 v1 = false;
1604 }
1605 }
1606 if (v1) {
1607 ret = hdd_parse_set_roam_scan_channels_v1(adapter, command);
1608 } else {
1609 ret = hdd_parse_set_roam_scan_channels_v2(adapter, command);
1610 }
1611
1612 return ret;
1613}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001614
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08001615#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001616/**
1617 * hdd_parse_plm_cmd() - HDD Parse Plm command
1618 * @pValue: Pointer to input data
1619 * @pPlmRequest:Pointer to output struct tpSirPlmReq
1620 *
1621 * This function parses the plm command passed in the format
1622 * CCXPLMREQ<space><enable><space><dialog_token><space>
1623 * <meas_token><space><num_of_bursts><space><burst_int><space>
1624 * <measu duration><space><burst_len><space><desired_tx_pwr>
1625 * <space><multcast_addr><space><number_of_channels>
1626 * <space><channel_numbers>
1627 *
1628 * Return: 0 for success non-zero for failure
1629 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301630QDF_STATUS hdd_parse_plm_cmd(uint8_t *pValue, tSirPlmReq *pPlmRequest)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001631{
1632 uint8_t *cmdPtr = NULL;
1633 int count, content = 0, ret = 0;
1634 char buf[32];
1635
1636 /* move to argument list */
1637 cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1638 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301639 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001640
1641 /* no space after the command */
1642 if (SPACE_ASCII_VALUE != *cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301643 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001644
1645 /* remove empty spaces */
1646 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1647 cmdPtr++;
1648
1649 /* START/STOP PLM req */
1650 ret = sscanf(cmdPtr, "%31s ", buf);
1651 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301652 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001653
1654 ret = kstrtos32(buf, 10, &content);
1655 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301656 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001657
1658 pPlmRequest->enable = content;
1659 cmdPtr = strpbrk(cmdPtr, " ");
1660
1661 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301662 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001663
1664 /* remove empty spaces */
1665 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1666 cmdPtr++;
1667
1668 /* Dialog token of radio meas req containing meas reqIE */
1669 ret = sscanf(cmdPtr, "%31s ", buf);
1670 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301671 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001672
1673 ret = kstrtos32(buf, 10, &content);
1674 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301675 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001676
1677 pPlmRequest->diag_token = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001678 hdd_debug("diag token %d", pPlmRequest->diag_token);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001679 cmdPtr = strpbrk(cmdPtr, " ");
1680
1681 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301682 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001683
1684 /* remove empty spaces */
1685 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1686 cmdPtr++;
1687
1688 /* measurement token of meas req IE */
1689 ret = sscanf(cmdPtr, "%31s ", buf);
1690 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301691 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001692
1693 ret = kstrtos32(buf, 10, &content);
1694 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301695 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001696
1697 pPlmRequest->meas_token = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001698 hdd_debug("meas token %d", pPlmRequest->meas_token);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001699
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001700 hdd_err("PLM req %s", pPlmRequest->enable ? "START" : "STOP");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001701 if (pPlmRequest->enable) {
1702
1703 cmdPtr = strpbrk(cmdPtr, " ");
1704
1705 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301706 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001707
1708 /* remove empty spaces */
1709 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1710 cmdPtr++;
1711
1712 /* total number of bursts after which STA stops sending */
1713 ret = sscanf(cmdPtr, "%31s ", buf);
1714 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301715 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001716
1717 ret = kstrtos32(buf, 10, &content);
1718 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301719 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001720
1721 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301722 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001723
1724 pPlmRequest->numBursts = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001725 hdd_debug("num burst %d", pPlmRequest->numBursts);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001726 cmdPtr = strpbrk(cmdPtr, " ");
1727
1728 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301729 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001730
1731 /* remove empty spaces */
1732 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1733 cmdPtr++;
1734
1735 /* burst interval in seconds */
1736 ret = sscanf(cmdPtr, "%31s ", buf);
1737 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301738 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001739
1740 ret = kstrtos32(buf, 10, &content);
1741 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301742 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001743
1744 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301745 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001746
1747 pPlmRequest->burstInt = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001748 hdd_debug("burst Int %d", pPlmRequest->burstInt);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001749 cmdPtr = strpbrk(cmdPtr, " ");
1750
1751 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301752 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001753
1754 /* remove empty spaces */
1755 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1756 cmdPtr++;
1757
1758 /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */
1759 ret = sscanf(cmdPtr, "%31s ", buf);
1760 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301761 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001762
1763 ret = kstrtos32(buf, 10, &content);
1764 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301765 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001766
1767 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301768 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001769
1770 pPlmRequest->measDuration = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001771 hdd_debug("measDur %d", pPlmRequest->measDuration);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001772 cmdPtr = strpbrk(cmdPtr, " ");
1773
1774 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301775 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001776
1777 /* remove empty spaces */
1778 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1779 cmdPtr++;
1780
1781 /* burst length of PLM bursts */
1782 ret = sscanf(cmdPtr, "%31s ", buf);
1783 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301784 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001785
1786 ret = kstrtos32(buf, 10, &content);
1787 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301788 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001789
1790 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301791 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001792
1793 pPlmRequest->burstLen = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001794 hdd_debug("burstLen %d", pPlmRequest->burstLen);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001795 cmdPtr = strpbrk(cmdPtr, " ");
1796
1797 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301798 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001799
1800 /* remove empty spaces */
1801 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1802 cmdPtr++;
1803
1804 /* desired tx power for transmission of PLM bursts */
1805 ret = sscanf(cmdPtr, "%31s ", buf);
1806 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301807 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001808
1809 ret = kstrtos32(buf, 10, &content);
1810 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301811 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001812
1813 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301814 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001815
1816 pPlmRequest->desiredTxPwr = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001817 hdd_debug("desiredTxPwr %d",
1818 pPlmRequest->desiredTxPwr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001819
Anurag Chouhan6d760662016-02-20 16:05:43 +05301820 for (count = 0; count < QDF_MAC_ADDR_SIZE; count++) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001821 cmdPtr = strpbrk(cmdPtr, " ");
1822
1823 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301824 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001825
1826 /* remove empty spaces */
1827 while ((SPACE_ASCII_VALUE == *cmdPtr)
1828 && ('\0' != *cmdPtr))
1829 cmdPtr++;
1830
1831 ret = sscanf(cmdPtr, "%31s ", buf);
1832 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301833 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001834
1835 ret = kstrtos32(buf, 16, &content);
1836 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301837 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001838
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001839 pPlmRequest->mac_addr.bytes[count] = content;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001840 }
1841
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001842 hdd_debug("MC addr " MAC_ADDRESS_STR,
1843 MAC_ADDR_ARRAY(pPlmRequest->mac_addr.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001844
1845 cmdPtr = strpbrk(cmdPtr, " ");
1846
1847 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301848 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001849
1850 /* remove empty spaces */
1851 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1852 cmdPtr++;
1853
1854 /* number of channels */
1855 ret = sscanf(cmdPtr, "%31s ", buf);
1856 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301857 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001858
1859 ret = kstrtos32(buf, 10, &content);
1860 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301861 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001862
1863 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301864 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001865
1866 pPlmRequest->plmNumCh = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001867 hdd_debug("numch %d", pPlmRequest->plmNumCh);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001868
1869 /* Channel numbers */
1870 for (count = 0; count < pPlmRequest->plmNumCh; count++) {
1871 cmdPtr = strpbrk(cmdPtr, " ");
1872
1873 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301874 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001875
1876 /* remove empty spaces */
1877 while ((SPACE_ASCII_VALUE == *cmdPtr)
1878 && ('\0' != *cmdPtr))
1879 cmdPtr++;
1880
1881 ret = sscanf(cmdPtr, "%31s ", buf);
1882 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301883 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001884
1885 ret = kstrtos32(buf, 10, &content);
1886 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301887 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001888
1889 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301890 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001891
1892 pPlmRequest->plmChList[count] = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001893 hdd_debug(" ch- %d", pPlmRequest->plmChList[count]);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001894 }
1895 }
1896 /* If PLM START */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301897 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001898}
1899#endif
1900
1901#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
1902static void wlan_hdd_ready_to_extwow(void *callbackContext, bool is_success)
1903{
1904 hdd_context_t *hdd_ctx = (hdd_context_t *) callbackContext;
1905 int rc;
1906
1907 rc = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301908 if (rc)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001909 return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001910 hdd_ctx->ext_wow_should_suspend = is_success;
1911 complete(&hdd_ctx->ready_to_extwow);
1912}
1913
1914static int hdd_enable_ext_wow(hdd_adapter_t *adapter,
1915 tpSirExtWoWParams arg_params)
1916{
1917 tSirExtWoWParams params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301918 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001919 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1920 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1921 int rc;
1922
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301923 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001924
1925 INIT_COMPLETION(hdd_ctx->ready_to_extwow);
1926
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301927 qdf_ret_status = sme_configure_ext_wow(hHal, &params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001928 &wlan_hdd_ready_to_extwow,
1929 hdd_ctx);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301930 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001931 hdd_err("sme_configure_ext_wow returned failure %d",
1932 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) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001939 hdd_err("Failed to get ready to extwow");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001940 return -EPERM;
1941 }
1942
1943 if (hdd_ctx->ext_wow_should_suspend) {
1944 if (hdd_ctx->config->extWowGotoSuspend) {
1945 pm_message_t state;
1946
1947 state.event = PM_EVENT_SUSPEND;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001948 hdd_info("Received ready to ExtWoW. Going to suspend");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001949
1950 rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL);
1951 if (rc < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001952 hdd_err("wlan_hdd_cfg80211_suspend_wlan failed, error = %d",
1953 rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001954 return rc;
1955 }
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301956 qdf_ret_status = wlan_hdd_bus_suspend(state);
1957 if (qdf_ret_status != QDF_STATUS_SUCCESS) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001958 hdd_err("wlan_hdd_suspend failed, status = %d",
1959 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001960 wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy);
1961 return -EPERM;
1962 }
1963 }
1964 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001965 hdd_err("Received ready to ExtWoW failure");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001966 return -EPERM;
1967 }
1968
1969 return 0;
1970}
1971
1972static int hdd_enable_ext_wow_parser(hdd_adapter_t *adapter, int vdev_id,
1973 int value)
1974{
1975 tSirExtWoWParams params;
1976 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1977 int rc;
1978
1979 rc = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301980 if (rc)
1981 return rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001982
1983 if (value < EXT_WOW_TYPE_APP_TYPE1 ||
1984 value > EXT_WOW_TYPE_APP_TYPE1_2) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001985 hdd_err("Invalid type");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001986 return -EINVAL;
1987 }
1988
1989 if (value == EXT_WOW_TYPE_APP_TYPE1 &&
1990 hdd_ctx->is_extwow_app_type1_param_set)
1991 params.type = value;
1992 else if (value == EXT_WOW_TYPE_APP_TYPE2 &&
1993 hdd_ctx->is_extwow_app_type2_param_set)
1994 params.type = value;
1995 else if (value == EXT_WOW_TYPE_APP_TYPE1_2 &&
1996 hdd_ctx->is_extwow_app_type1_param_set &&
1997 hdd_ctx->is_extwow_app_type2_param_set)
1998 params.type = value;
1999 else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002000 hdd_err("Set app params before enable it value %d",
2001 value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002002 return -EINVAL;
2003 }
2004
2005 params.vdev_id = vdev_id;
2006 params.wakeup_pin_num = hdd_ctx->config->extWowApp1WakeupPinNumber |
2007 (hdd_ctx->config->extWowApp2WakeupPinNumber
2008 << 8);
2009
2010 return hdd_enable_ext_wow(adapter, &params);
2011}
2012
2013static int hdd_set_app_type1_params(tHalHandle hHal,
2014 tpSirAppType1Params arg_params)
2015{
2016 tSirAppType1Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302017 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002018
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302019 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002020
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302021 qdf_ret_status = sme_configure_app_type1_params(hHal, &params);
2022 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002023 hdd_err("sme_configure_app_type1_params returned failure %d",
2024 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002025 return -EPERM;
2026 }
2027
2028 return 0;
2029}
2030
2031static int hdd_set_app_type1_parser(hdd_adapter_t *adapter,
2032 char *arg, int len)
2033{
2034 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2035 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2036 char id[20], password[20];
2037 tSirAppType1Params params;
Srinivas Girigowda04209912015-11-24 12:11:13 -08002038 int rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002039
2040 rc = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05302041 if (rc)
2042 return rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002043
2044 if (2 != sscanf(arg, "%8s %16s", id, password)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002045 hdd_err("Invalid Number of arguments");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002046 return -EINVAL;
2047 }
2048
2049 memset(&params, 0, sizeof(tSirAppType1Params));
2050 params.vdev_id = adapter->sessionId;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302051 qdf_copy_macaddr(&params.wakee_mac_addr, &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002052
2053 params.id_length = strlen(id);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302054 qdf_mem_copy(params.identification_id, id, params.id_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002055 params.pass_length = strlen(password);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302056 qdf_mem_copy(params.password, password, params.pass_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002057
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002058 hdd_info("%d %pM %.8s %u %.16s %u",
2059 params.vdev_id, params.wakee_mac_addr.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002060 params.identification_id, params.id_length,
2061 params.password, params.pass_length);
2062
2063 return hdd_set_app_type1_params(hHal, &params);
2064}
2065
2066static int hdd_set_app_type2_params(tHalHandle hHal,
2067 tpSirAppType2Params arg_params)
2068{
2069 tSirAppType2Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302070 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002071
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302072 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002073
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302074 qdf_ret_status = sme_configure_app_type2_params(hHal, &params);
2075 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002076 hdd_err("sme_configure_app_type2_params returned failure %d",
2077 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002078 return -EPERM;
2079 }
2080
2081 return 0;
2082}
2083
2084static int hdd_set_app_type2_parser(hdd_adapter_t *adapter,
2085 char *arg, int len)
2086{
2087 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2088 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2089 char mac_addr[20], rc4_key[20];
Anurag Chouhan6d760662016-02-20 16:05:43 +05302090 unsigned int gateway_mac[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002091 tSirAppType2Params params;
2092 int ret;
2093
2094 ret = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05302095 if (ret)
2096 return ret;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002097
2098 memset(&params, 0, sizeof(tSirAppType2Params));
2099
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302100 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 -08002101 mac_addr, rc4_key, (unsigned int *)&params.ip_id,
2102 (unsigned int *)&params.ip_device_ip,
2103 (unsigned int *)&params.ip_server_ip,
2104 (unsigned int *)&params.tcp_seq,
2105 (unsigned int *)&params.tcp_ack_seq,
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302106 (uint16_t *)&params.tcp_src_port,
2107 (uint16_t *)&params.tcp_dst_port,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002108 (unsigned int *)&params.keepalive_init,
2109 (unsigned int *)&params.keepalive_min,
2110 (unsigned int *)&params.keepalive_max,
2111 (unsigned int *)&params.keepalive_inc,
2112 (unsigned int *)&params.tcp_tx_timeout_val,
2113 (unsigned int *)&params.tcp_rx_timeout_val);
2114
2115 if (ret != 15 && ret != 7) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002116 hdd_err("Invalid Number of arguments");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002117 return -EINVAL;
2118 }
2119
2120 if (6 !=
2121 sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0],
2122 &gateway_mac[1], &gateway_mac[2], &gateway_mac[3],
2123 &gateway_mac[4], &gateway_mac[5])) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002124 hdd_err("Invalid MacAddress Input %s", mac_addr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002125 return -EINVAL;
2126 }
2127
2128 if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT ||
2129 params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002130 hdd_err("Invalid TCP Port Number");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002131 return -EINVAL;
2132 }
2133
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302134 qdf_mem_copy(&params.gateway_mac.bytes, (uint8_t *) &gateway_mac,
Anurag Chouhan6d760662016-02-20 16:05:43 +05302135 QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002136
2137 params.rc4_key_len = strlen(rc4_key);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302138 qdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002139
2140 params.vdev_id = adapter->sessionId;
2141 params.tcp_src_port = (params.tcp_src_port != 0) ?
2142 params.tcp_src_port : hdd_ctx->config->extWowApp2TcpSrcPort;
2143 params.tcp_dst_port = (params.tcp_dst_port != 0) ?
2144 params.tcp_dst_port : hdd_ctx->config->extWowApp2TcpDstPort;
2145 params.keepalive_init = (params.keepalive_init != 0) ?
2146 params.keepalive_init : hdd_ctx->config->
2147 extWowApp2KAInitPingInterval;
2148 params.keepalive_min =
2149 (params.keepalive_min != 0) ?
2150 params.keepalive_min :
2151 hdd_ctx->config->extWowApp2KAMinPingInterval;
2152 params.keepalive_max =
2153 (params.keepalive_max != 0) ?
2154 params.keepalive_max :
2155 hdd_ctx->config->extWowApp2KAMaxPingInterval;
2156 params.keepalive_inc =
2157 (params.keepalive_inc != 0) ?
2158 params.keepalive_inc :
2159 hdd_ctx->config->extWowApp2KAIncPingInterval;
2160 params.tcp_tx_timeout_val =
2161 (params.tcp_tx_timeout_val != 0) ?
2162 params.tcp_tx_timeout_val :
2163 hdd_ctx->config->extWowApp2TcpTxTimeout;
2164 params.tcp_rx_timeout_val =
2165 (params.tcp_rx_timeout_val != 0) ?
2166 params.tcp_rx_timeout_val :
2167 hdd_ctx->config->extWowApp2TcpRxTimeout;
2168
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002169 hdd_info("%pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u",
2170 gateway_mac, rc4_key, params.ip_id,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002171 params.ip_device_ip, params.ip_server_ip, params.tcp_seq,
2172 params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port,
2173 params.keepalive_init, params.keepalive_min,
2174 params.keepalive_max, params.keepalive_inc,
2175 params.tcp_tx_timeout_val, params.tcp_rx_timeout_val);
2176
2177 return hdd_set_app_type2_params(hHal, &params);
2178}
2179#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
2180
2181/**
2182 * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
2183 * @pValue: Pointer to MAXTXPOWER command
2184 * @pDbm: Pointer to tx power
2185 *
2186 * This function parses the MAXTXPOWER command passed in the format
2187 * MAXTXPOWER<space>X(Tx power in dbm)
2188 *
2189 * For example input commands:
2190 * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm
2191 * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm
2192 *
2193 * Return: 0 for success non-zero for failure
2194 */
2195static int hdd_parse_setmaxtxpower_command(uint8_t *pValue, int *pTxPower)
2196{
2197 uint8_t *inPtr = pValue;
2198 int tempInt;
2199 int v = 0;
2200 *pTxPower = 0;
2201
2202 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2203 /* no argument after the command */
2204 if (NULL == inPtr) {
2205 return -EINVAL;
2206 }
2207
2208 /* no space after the command */
2209 else if (SPACE_ASCII_VALUE != *inPtr) {
2210 return -EINVAL;
2211 }
2212
2213 /* remove empty spaces */
2214 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2215 inPtr++;
2216
2217 /* no argument followed by spaces */
2218 if ('\0' == *inPtr) {
2219 return 0;
2220 }
2221
2222 v = kstrtos32(inPtr, 10, &tempInt);
2223
2224 /* Range checking for passed parameter */
2225 if ((tempInt < HDD_MIN_TX_POWER) || (tempInt > HDD_MAX_TX_POWER)) {
2226 return -EINVAL;
2227 }
2228
2229 *pTxPower = tempInt;
2230
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002231 hdd_info("SETMAXTXPOWER: %d", *pTxPower);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002232
2233 return 0;
2234} /* End of hdd_parse_setmaxtxpower_command */
2235
2236static int hdd_get_dwell_time(struct hdd_config *pCfg, uint8_t *command,
2237 char *extra, uint8_t n, uint8_t *len)
2238{
2239 int ret = 0;
2240
2241 if (!pCfg || !command || !extra || !len) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002242 hdd_err("argument passed for GETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002243 ret = -EINVAL;
2244 return ret;
2245 }
2246
2247 if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) {
2248 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n",
2249 (int)pCfg->nActiveMaxChnTime);
2250 return ret;
2251 } else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0) {
2252 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n",
2253 (int)pCfg->nActiveMinChnTime);
2254 return ret;
2255 } else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) {
2256 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n",
2257 (int)pCfg->nPassiveMaxChnTime);
2258 return ret;
2259 } else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0) {
2260 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n",
2261 (int)pCfg->nPassiveMinChnTime);
2262 return ret;
2263 } else if (strncmp(command, "GETDWELLTIME", 12) == 0) {
2264 *len = scnprintf(extra, n, "GETDWELLTIME %u \n",
2265 (int)pCfg->nActiveMaxChnTime);
2266 return ret;
2267 } else {
2268 ret = -EINVAL;
2269 }
2270
2271 return ret;
2272}
2273
2274static int hdd_set_dwell_time(hdd_adapter_t *adapter, uint8_t *command)
2275{
2276 tHalHandle hHal;
2277 struct hdd_config *pCfg;
2278 uint8_t *value = command;
2279 tSmeConfigParams smeConfig;
2280 int val = 0, temp = 0;
2281
2282 pCfg = (WLAN_HDD_GET_CTX(adapter))->config;
2283 hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2284 if (!pCfg || !hHal) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002285 hdd_err("argument passed for SETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002286 return -EINVAL;
2287 }
2288
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302289 qdf_mem_zero(&smeConfig, sizeof(smeConfig));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002290 sme_get_config_param(hHal, &smeConfig);
2291
2292 if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) {
2293 value = value + 24;
2294 temp = kstrtou32(value, 10, &val);
2295 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2296 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002297 hdd_err("argument passed for SETDWELLTIME ACTIVE MAX is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002298 return -EFAULT;
2299 }
2300 pCfg->nActiveMaxChnTime = val;
2301 smeConfig.csrConfig.nActiveMaxChnTime = val;
2302 sme_update_config(hHal, &smeConfig);
2303 } else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0) {
2304 value = value + 24;
2305 temp = kstrtou32(value, 10, &val);
2306 if (temp != 0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN ||
2307 val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002308 hdd_err("argument passed for SETDWELLTIME ACTIVE MIN is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002309 return -EFAULT;
2310 }
2311 pCfg->nActiveMinChnTime = val;
2312 smeConfig.csrConfig.nActiveMinChnTime = val;
2313 sme_update_config(hHal, &smeConfig);
2314 } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) {
2315 value = value + 25;
2316 temp = kstrtou32(value, 10, &val);
2317 if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN ||
2318 val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002319 hdd_err("argument passed for SETDWELLTIME PASSIVE MAX is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002320 return -EFAULT;
2321 }
2322 pCfg->nPassiveMaxChnTime = val;
2323 smeConfig.csrConfig.nPassiveMaxChnTime = val;
2324 sme_update_config(hHal, &smeConfig);
2325 } else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0) {
2326 value = value + 25;
2327 temp = kstrtou32(value, 10, &val);
2328 if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN ||
2329 val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002330 hdd_err("argument passed for SETDWELLTIME PASSIVE MIN is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002331 return -EFAULT;
2332 }
2333 pCfg->nPassiveMinChnTime = val;
2334 smeConfig.csrConfig.nPassiveMinChnTime = val;
2335 sme_update_config(hHal, &smeConfig);
2336 } else if (strncmp(command, "SETDWELLTIME", 12) == 0) {
2337 value = value + 13;
2338 temp = kstrtou32(value, 10, &val);
2339 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2340 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002341 hdd_err("argument passed for SETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002342 return -EFAULT;
2343 }
2344 pCfg->nActiveMaxChnTime = val;
2345 smeConfig.csrConfig.nActiveMaxChnTime = val;
2346 sme_update_config(hHal, &smeConfig);
2347 } else {
2348 return -EINVAL;
2349 }
2350
2351 return 0;
2352}
2353
2354static void hdd_get_link_status_cb(uint8_t status, void *context)
2355{
2356 struct statsContext *pLinkContext;
2357 hdd_adapter_t *adapter;
2358
2359 if (NULL == context) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002360 hdd_err("Bad context [%p]", context);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002361 return;
2362 }
2363
2364 pLinkContext = context;
2365 adapter = pLinkContext->pAdapter;
2366
2367 spin_lock(&hdd_context_lock);
2368
2369 if ((NULL == adapter) ||
2370 (LINK_STATUS_MAGIC != pLinkContext->magic)) {
2371 /*
2372 * the caller presumably timed out so there is
2373 * nothing we can do
2374 */
2375 spin_unlock(&hdd_context_lock);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002376 hdd_warn("Invalid context, adapter [%p] magic [%08x]",
2377 adapter, pLinkContext->magic);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002378 return;
2379 }
2380
2381 /* context is valid so caller is still waiting */
2382
2383 /* paranoia: invalidate the magic */
2384 pLinkContext->magic = 0;
2385
2386 /* copy over the status */
2387 adapter->linkStatus = status;
2388
2389 /* notify the caller */
2390 complete(&pLinkContext->completion);
2391
2392 /* serialization is complete */
2393 spin_unlock(&hdd_context_lock);
2394}
2395
2396/**
2397 * wlan_hdd_get_link_status() - get link status
2398 * @pAdapter: pointer to the adapter
2399 *
2400 * This function sends a request to query the link status and waits
2401 * on a timer to invoke the callback. if the callback is invoked then
2402 * latest link status shall be returned or otherwise cached value
2403 * will be returned.
2404 *
2405 * Return: On success, link status shall be returned.
2406 * On error or not associated, link status 0 will be returned.
2407 */
2408static int wlan_hdd_get_link_status(hdd_adapter_t *adapter)
2409{
2410
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002411 hdd_station_ctx_t *pHddStaCtx =
2412 WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2413 struct statsContext context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302414 QDF_STATUS hstatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002415 unsigned long rc;
2416
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002417 if (cds_is_driver_recovering()) {
2418 hdd_warn("Recovery in Progress. State: 0x%x Ignore!!!",
2419 cds_get_driver_state());
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002420 return 0;
2421 }
2422
Krunal Sonibe766b02016-03-10 13:00:44 -08002423 if ((QDF_STA_MODE != adapter->device_mode) &&
2424 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002425 hdd_warn("Unsupported in mode %s(%d)",
2426 hdd_device_mode_to_string(adapter->device_mode),
2427 adapter->device_mode);
2428 return 0;
2429 }
2430
2431 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2432 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
2433 /* If not associated, then expected link status return
2434 * value is 0
2435 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002436 hdd_info("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002437 return 0;
2438 }
2439
2440 init_completion(&context.completion);
2441 context.pAdapter = adapter;
2442 context.magic = LINK_STATUS_MAGIC;
2443 hstatus = sme_get_link_status(WLAN_HDD_GET_HAL_CTX(adapter),
2444 hdd_get_link_status_cb,
2445 &context, adapter->sessionId);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302446 if (QDF_STATUS_SUCCESS != hstatus) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002447 hdd_err("Unable to retrieve link status");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002448 /* return a cached value */
2449 } else {
2450 /* request is sent -- wait for the response */
2451 rc = wait_for_completion_timeout(&context.completion,
2452 msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
2453 if (!rc)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002454 hdd_err("SME timed out while retrieving link status");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002455 }
2456
2457 spin_lock(&hdd_context_lock);
2458 context.magic = 0;
2459 spin_unlock(&hdd_context_lock);
2460
2461 /* either callback updated adapter stats or it has cached data */
2462 return adapter->linkStatus;
2463}
2464
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002465static void hdd_tx_fail_ind_callback(uint8_t *MacAddr, uint8_t seqNo)
2466{
2467 int payload_len;
2468 struct sk_buff *skb;
2469 struct nlmsghdr *nlh;
2470 uint8_t *data;
2471
2472 payload_len = ETH_ALEN;
2473
Ryan Hsu5e2e2052016-04-28 10:19:38 -07002474 if (0 == cesium_pid || cesium_nl_srv_sock == NULL) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002475 hdd_err("cesium process not registered");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002476 return;
2477 }
2478
2479 skb = nlmsg_new(payload_len, GFP_ATOMIC);
2480 if (skb == NULL) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002481 hdd_err("nlmsg_new() failed for msg size[%d]",
2482 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002483 return;
2484 }
2485
2486 nlh = nlmsg_put(skb, cesium_pid, seqNo, 0, payload_len, NLM_F_REQUEST);
2487
2488 if (NULL == nlh) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002489 hdd_err("nlmsg_put() failed for msg size[%d]",
2490 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002491
2492 kfree_skb(skb);
2493 return;
2494 }
2495
2496 data = nlmsg_data(nlh);
2497 memcpy(data, MacAddr, ETH_ALEN);
2498
2499 if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002500 hdd_err("nlmsg_unicast() failed for msg size[%d]",
2501 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002502 }
2503
2504 return;
2505}
2506
2507
2508/**
2509 * hdd_ParseuserParams - return a pointer to the next argument
2510 * @pValue: Input argument string
2511 * @ppArg: Output pointer to the next argument
2512 *
2513 * This function parses argument stream and finds the pointer
2514 * to the next argument
2515 *
2516 * Return: 0 if the next argument found; -EINVAL otherwise
2517 */
2518static int hdd_parse_user_params(uint8_t *pValue, uint8_t **ppArg)
2519{
2520 uint8_t *pVal;
2521
2522 pVal = strnchr(pValue, strlen(pValue), ' ');
2523
2524 if (NULL == pVal) {
2525 /* no argument remains */
2526 return -EINVAL;
2527 } else if (SPACE_ASCII_VALUE != *pVal) {
2528 /* no space after the current argument */
2529 return -EINVAL;
2530 }
2531
2532 pVal++;
2533
2534 /* remove empty spaces */
2535 while ((SPACE_ASCII_VALUE == *pVal) && ('\0' != *pVal)) {
2536 pVal++;
2537 }
2538
2539 /* no argument followed by spaces */
2540 if ('\0' == *pVal) {
2541 return -EINVAL;
2542 }
2543
2544 *ppArg = pVal;
2545
2546 return 0;
2547}
2548
2549/**
2550 * hdd_parse_ibsstx_fail_event_params - Parse params
2551 * for SETIBSSTXFAILEVENT
2552 * @pValue: Input ibss tx fail event argument
2553 * @tx_fail_count: (Output parameter) Tx fail counter
2554 * @pid: (Output parameter) PID
2555 *
2556 * Return: 0 if the parsing succeeds; -EINVAL otherwise
2557 */
2558static int hdd_parse_ibsstx_fail_event_params(uint8_t *pValue,
2559 uint8_t *tx_fail_count,
2560 uint16_t *pid)
2561{
2562 uint8_t *param = NULL;
2563 int ret;
2564
2565 ret = hdd_parse_user_params(pValue, &param);
2566
2567 if (0 == ret && NULL != param) {
2568 if (1 != sscanf(param, "%hhu", tx_fail_count)) {
2569 ret = -EINVAL;
2570 goto done;
2571 }
2572 } else {
2573 goto done;
2574 }
2575
2576 if (0 == *tx_fail_count) {
2577 *pid = 0;
2578 goto done;
2579 }
2580
2581 pValue = param;
2582 pValue++;
2583
2584 ret = hdd_parse_user_params(pValue, &param);
2585
2586 if (0 == ret) {
2587 if (1 != sscanf(param, "%hu", pid)) {
2588 ret = -EINVAL;
2589 goto done;
2590 }
2591 } else {
2592 goto done;
2593 }
2594
2595done:
2596 return ret;
2597}
2598
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08002599#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002600/**
2601 * hdd_parse_ese_beacon_req() - Parse ese beacon request
2602 * @pValue: Pointer to data
2603 * @pEseBcnReq: Output pointer to store parsed ie information
2604 *
2605 * This function parses the ese beacon request passed in the format
2606 * CCXBEACONREQ<space><Number of fields><space><Measurement token>
2607 * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N
2608 * <space>Scan Mode N<space>Meas Duration N
2609 *
2610 * If the Number of bcn req fields (N) does not match with the
2611 * actual number of fields passed then take N.
2612 * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated
2613 * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40.
2614 * This function does not take care of removing duplicate channels from the
2615 * list
2616 *
2617 * Return: 0 for success non-zero for failure
2618 */
2619static int hdd_parse_ese_beacon_req(uint8_t *pValue,
2620 tCsrEseBeaconReq *pEseBcnReq)
2621{
2622 uint8_t *inPtr = pValue;
2623 int tempInt = 0;
2624 int j = 0, i = 0, v = 0;
2625 char buf[32];
2626
2627 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2628 /* no argument after the command */
2629 if (NULL == inPtr) {
2630 return -EINVAL;
2631 }
2632 /* no space after the command */
2633 else if (SPACE_ASCII_VALUE != *inPtr) {
2634 return -EINVAL;
2635 }
2636
2637 /* remove empty spaces */
2638 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2639 inPtr++;
2640
2641 /* no argument followed by spaces */
2642 if ('\0' == *inPtr)
2643 return -EINVAL;
2644
Srinivas Girigowda476deeb2016-07-19 16:21:29 -07002645 /* Getting the first argument ie Number of IE fields */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002646 v = sscanf(inPtr, "%31s ", buf);
2647 if (1 != v)
2648 return -EINVAL;
2649
2650 v = kstrtos32(buf, 10, &tempInt);
2651 if (v < 0)
2652 return -EINVAL;
2653
Srinivas Girigowda476deeb2016-07-19 16:21:29 -07002654 tempInt = QDF_MIN(tempInt, SIR_ESE_MAX_MEAS_IE_REQS);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002655 pEseBcnReq->numBcnReqIe = tempInt;
2656
Srinivas Girigowda476deeb2016-07-19 16:21:29 -07002657 hdd_info("Number of Bcn Req Ie fields: %d", pEseBcnReq->numBcnReqIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002658
2659 for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++) {
2660 for (i = 0; i < 4; i++) {
2661 /*
2662 * inPtr pointing to the beginning of 1st space
2663 * after number of ie fields
2664 */
2665 inPtr = strpbrk(inPtr, " ");
2666 /* no ie data after the number of ie fields argument */
2667 if (NULL == inPtr)
2668 return -EINVAL;
2669
2670 /* remove empty space */
2671 while ((SPACE_ASCII_VALUE == *inPtr)
2672 && ('\0' != *inPtr))
2673 inPtr++;
2674
2675 /*
2676 * no ie data after the number of ie fields
2677 * argument and spaces
2678 */
2679 if ('\0' == *inPtr)
2680 return -EINVAL;
2681
2682 v = sscanf(inPtr, "%31s ", buf);
2683 if (1 != v)
2684 return -EINVAL;
2685
2686 v = kstrtos32(buf, 10, &tempInt);
2687 if (v < 0)
2688 return -EINVAL;
2689
2690 switch (i) {
2691 case 0: /* Measurement token */
2692 if (tempInt <= 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002693 hdd_err("Invalid Measurement Token(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002694 tempInt);
2695 return -EINVAL;
2696 }
2697 pEseBcnReq->bcnReq[j].measurementToken =
2698 tempInt;
2699 break;
2700
2701 case 1: /* Channel number */
2702 if ((tempInt <= 0) ||
2703 (tempInt >
2704 WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002705 hdd_err("Invalid Channel Number(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002706 tempInt);
2707 return -EINVAL;
2708 }
2709 pEseBcnReq->bcnReq[j].channel = tempInt;
2710 break;
2711
2712 case 2: /* Scan mode */
2713 if ((tempInt < eSIR_PASSIVE_SCAN)
2714 || (tempInt > eSIR_BEACON_TABLE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002715 hdd_err("Invalid Scan Mode(%d) Expected{0|1|2}",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002716 tempInt);
2717 return -EINVAL;
2718 }
2719 pEseBcnReq->bcnReq[j].scanMode = tempInt;
2720 break;
2721
2722 case 3: /* Measurement duration */
2723 if (((tempInt <= 0)
2724 && (pEseBcnReq->bcnReq[j].scanMode !=
2725 eSIR_BEACON_TABLE)) ||
2726 ((tempInt < 0) &&
2727 (pEseBcnReq->bcnReq[j].scanMode ==
2728 eSIR_BEACON_TABLE))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002729 hdd_err("Invalid Measurement Duration(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002730 tempInt);
2731 return -EINVAL;
2732 }
2733 pEseBcnReq->bcnReq[j].measurementDuration =
2734 tempInt;
2735 break;
2736 }
2737 }
2738 }
2739
2740 for (j = 0; j < pEseBcnReq->numBcnReqIe; j++) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002741 hdd_info("Index(%d) Measurement Token(%u) Channel(%u) Scan Mode(%u) Measurement Duration(%u)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002742 j,
2743 pEseBcnReq->bcnReq[j].measurementToken,
2744 pEseBcnReq->bcnReq[j].channel,
2745 pEseBcnReq->bcnReq[j].scanMode,
2746 pEseBcnReq->bcnReq[j].measurementDuration);
2747 }
2748
2749 return 0;
2750}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002751
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002752/**
2753 * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
2754 * @pValue: Pointer to input data
2755 * @pCckmIe: Pointer to output cckm Ie
2756 * @pCckmIeLen: Pointer to output cckm ie length
2757 *
2758 * This function parses the SETCCKM IE command
2759 * SETCCKMIE<space><ie data>
2760 *
2761 * Return: 0 for success non-zero for failure
2762 */
2763static int hdd_parse_get_cckm_ie(uint8_t *pValue, uint8_t **pCckmIe,
2764 uint8_t *pCckmIeLen)
2765{
2766 uint8_t *inPtr = pValue;
2767 uint8_t *dataEnd;
2768 int j = 0;
2769 int i = 0;
2770 uint8_t tempByte = 0;
2771 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2772 /* no argument after the command */
2773 if (NULL == inPtr) {
2774 return -EINVAL;
2775 }
2776 /* no space after the command */
2777 else if (SPACE_ASCII_VALUE != *inPtr) {
2778 return -EINVAL;
2779 }
2780 /* remove empty spaces */
2781 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2782 inPtr++;
2783 /* no argument followed by spaces */
2784 if ('\0' == *inPtr) {
2785 return -EINVAL;
2786 }
2787 /* find the length of data */
2788 dataEnd = inPtr;
2789 while (('\0' != *dataEnd)) {
2790 dataEnd++;
2791 ++(*pCckmIeLen);
2792 }
2793 if (*pCckmIeLen <= 0)
2794 return -EINVAL;
2795 /*
2796 * Allocate the number of bytes based on the number of input characters
2797 * whether it is even or odd.
2798 * if the number of input characters are even, then we need N / 2 byte.
2799 * if the number of input characters are odd, then we need do
2800 * (N + 1) / 2 to compensate rounding off.
2801 * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough.
2802 * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes
2803 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302804 *pCckmIe = qdf_mem_malloc((*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002805 if (NULL == *pCckmIe) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002806 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002807 return -ENOMEM;
2808 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302809 qdf_mem_zero(*pCckmIe, (*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002810 /*
2811 * the buffer received from the upper layer is character buffer,
2812 * we need to prepare the buffer taking 2 characters in to a U8 hex
2813 * decimal number for example 7f0000f0...form a buffer to contain
2814 * 7f in 0th location, 00 in 1st and f0 in 3rd location
2815 */
2816 for (i = 0, j = 0; j < *pCckmIeLen; j += 2) {
2817 tempByte = (hex_to_bin(inPtr[j]) << 4) |
2818 (hex_to_bin(inPtr[j + 1]));
2819 (*pCckmIe)[i++] = tempByte;
2820 }
2821 *pCckmIeLen = i;
2822 return 0;
2823}
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08002824#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002825
2826int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate)
2827{
2828 tSirRateUpdateInd rateUpdate = {0};
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302829 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002830 hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
2831 struct hdd_config *pConfig = NULL;
2832
2833 if (pHddCtx == NULL) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002834 hdd_err("HDD context is null");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002835 return -EINVAL;
2836 }
Krunal Sonibe766b02016-03-10 13:00:44 -08002837 if ((QDF_IBSS_MODE != pAdapter->device_mode) &&
2838 (QDF_SAP_MODE != pAdapter->device_mode) &&
2839 (QDF_STA_MODE != pAdapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002840 hdd_err("Received SETMCRATE cmd in invalid mode %s(%d)",
2841 hdd_device_mode_to_string(pAdapter->device_mode),
2842 pAdapter->device_mode);
2843 hdd_err("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002844 return -EINVAL;
2845 }
2846 pConfig = pHddCtx->config;
2847 rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
2848 rateUpdate.dev_mode = pAdapter->device_mode;
2849 rateUpdate.mcastDataRate24GHz = targetRate;
2850 rateUpdate.mcastDataRate24GHzTxFlag = 1;
2851 rateUpdate.mcastDataRate5GHz = targetRate;
2852 rateUpdate.bcastDataRate = -1;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302853 qdf_copy_macaddr(&rateUpdate.bssid, &pAdapter->macAddressCurrent);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002854 hdd_info("MC Target rate %d, mac = %pM, dev_mode %s(%d)",
2855 rateUpdate.mcastDataRate24GHz, rateUpdate.bssid.bytes,
2856 hdd_device_mode_to_string(pAdapter->device_mode),
2857 pAdapter->device_mode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002858 status = sme_send_rate_update_ind(pHddCtx->hHal, &rateUpdate);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302859 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002860 hdd_err("SETMCRATE failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002861 return -EFAULT;
2862 }
2863 return 0;
2864}
2865
2866static int drv_cmd_p2p_dev_addr(hdd_adapter_t *adapter,
2867 hdd_context_t *hdd_ctx,
2868 uint8_t *command,
2869 uint8_t command_len,
2870 hdd_priv_data_t *priv_data)
2871{
2872 int ret = 0;
2873
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302874 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002875 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
2876 adapter->sessionId,
2877 (unsigned)(*(hdd_ctx->p2pDeviceAddress.bytes + 2)
2878 << 24 | *(hdd_ctx->p2pDeviceAddress.bytes
2879 + 3) << 16 | *(hdd_ctx->
2880 p2pDeviceAddress.bytes + 4) << 8 |
2881 *(hdd_ctx->p2pDeviceAddress.bytes +
2882 5))));
2883
2884 if (copy_to_user(priv_data->buf, hdd_ctx->p2pDeviceAddress.bytes,
2885 sizeof(tSirMacAddr))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002886 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002887 ret = -EFAULT;
2888 }
2889
2890 return ret;
2891}
2892
2893/**
2894 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command
2895 * @adapter: Adapter on which the command was received
2896 * @hdd_ctx: HDD global context
2897 * @command: Entire driver command received from userspace
2898 * @command_len: Length of @command
2899 * @priv_data: Pointer to ioctl private data structure
2900 *
2901 * This is a trivial command hander function which simply forwards the
2902 * command to the actual command processor within the P2P module.
2903 *
2904 * Return: 0 on success, non-zero on failure
2905 */
2906static int drv_cmd_p2p_set_noa(hdd_adapter_t *adapter,
2907 hdd_context_t *hdd_ctx,
2908 uint8_t *command,
2909 uint8_t command_len,
2910 hdd_priv_data_t *priv_data)
2911{
2912 return hdd_set_p2p_noa(adapter->dev, command);
2913}
2914
2915/**
2916 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command
2917 * @adapter: Adapter on which the command was received
2918 * @hdd_ctx: HDD global context
2919 * @command: Entire driver command received from userspace
2920 * @command_len: Length of @command
2921 * @priv_data: Pointer to ioctl private data structure
2922 *
2923 * This is a trivial command hander function which simply forwards the
2924 * command to the actual command processor within the P2P module.
2925 *
2926 * Return: 0 on success, non-zero on failure
2927 */
2928static int drv_cmd_p2p_set_ps(hdd_adapter_t *adapter,
2929 hdd_context_t *hdd_ctx,
2930 uint8_t *command,
2931 uint8_t command_len,
2932 hdd_priv_data_t *priv_data)
2933{
2934 return hdd_set_p2p_opps(adapter->dev, command);
2935}
2936
2937static int drv_cmd_set_band(hdd_adapter_t *adapter,
2938 hdd_context_t *hdd_ctx,
2939 uint8_t *command,
2940 uint8_t command_len,
2941 hdd_priv_data_t *priv_data)
2942{
2943 int ret = 0;
2944
2945 uint8_t *ptr = command;
2946
2947 /* Change band request received */
2948
2949 /*
2950 * First 8 bytes will have "SETBAND " and
2951 * 9 byte will have band setting value
2952 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002953 hdd_info("SetBandCommand Info comm %s UL %d, TL %d",
2954 command, priv_data->used_len,
2955 priv_data->total_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002956
2957 /* Change band request received */
2958 ret = hdd_set_band_helper(adapter->dev, ptr);
2959
2960 return ret;
2961}
2962
2963static int drv_cmd_set_wmmps(hdd_adapter_t *adapter,
2964 hdd_context_t *hdd_ctx,
2965 uint8_t *command,
2966 uint8_t command_len,
2967 hdd_priv_data_t *priv_data)
2968{
2969 return hdd_wmmps_helper(adapter, command);
2970}
2971
2972static int drv_cmd_country(hdd_adapter_t *adapter,
2973 hdd_context_t *hdd_ctx,
2974 uint8_t *command,
2975 uint8_t command_len,
2976 hdd_priv_data_t *priv_data)
2977{
2978 int ret = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302979 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002980 unsigned long rc;
2981 char *country_code;
2982
2983 country_code = command + 8;
2984
2985 INIT_COMPLETION(adapter->change_country_code);
2986
2987 status = sme_change_country_code(hdd_ctx->hHal,
2988 wlan_hdd_change_country_code_callback,
2989 country_code,
2990 adapter,
2991 hdd_ctx->pcds_context,
2992 eSIR_TRUE,
2993 eSIR_TRUE);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302994 if (status == QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002995 rc = wait_for_completion_timeout(
2996 &adapter->change_country_code,
2997 msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
2998 if (!rc)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002999 hdd_err("SME while setting country code timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003000 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003001 hdd_err("SME Change Country code fail, status %d",
3002 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003003 ret = -EINVAL;
3004 }
3005
3006 return ret;
3007}
3008
3009static int drv_cmd_set_roam_trigger(hdd_adapter_t *adapter,
3010 hdd_context_t *hdd_ctx,
3011 uint8_t *command,
3012 uint8_t command_len,
3013 hdd_priv_data_t *priv_data)
3014{
3015 int ret = 0;
3016 uint8_t *value = command;
3017 int8_t rssi = 0;
3018 uint8_t lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303019 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003020
3021 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
3022 value = value + command_len + 1;
3023
3024 /* Convert the value from ascii to integer */
3025 ret = kstrtos8(value, 10, &rssi);
3026 if (ret < 0) {
3027 /*
3028 * If the input value is greater than max value of datatype,
3029 * then also kstrtou8 fails
3030 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003031 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
3032 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
3033 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003034 ret = -EINVAL;
3035 goto exit;
3036 }
3037
3038 lookUpThreshold = abs(rssi);
3039
3040 if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN)
3041 || (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003042 hdd_err("Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003043 lookUpThreshold,
3044 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
3045 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
3046 ret = -EINVAL;
3047 goto exit;
3048 }
3049
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303050 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003051 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
3052 adapter->sessionId, lookUpThreshold));
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003053 hdd_info("Received Command to Set Roam trigger (Neighbor lookup threshold) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003054 lookUpThreshold);
3055
3056 hdd_ctx->config->nNeighborLookupRssiThreshold = lookUpThreshold;
3057 status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->hHal,
3058 adapter->sessionId,
3059 lookUpThreshold);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303060 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003061 hdd_err("Failed to set roam trigger, try again");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003062 ret = -EPERM;
3063 goto exit;
3064 }
3065
3066exit:
3067 return ret;
3068}
3069
3070static int drv_cmd_get_roam_trigger(hdd_adapter_t *adapter,
3071 hdd_context_t *hdd_ctx,
3072 uint8_t *command,
3073 uint8_t command_len,
3074 hdd_priv_data_t *priv_data)
3075{
3076 int ret = 0;
3077 uint8_t lookUpThreshold =
3078 sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->hHal);
3079 int rssi = (-1) * lookUpThreshold;
3080 char extra[32];
3081 uint8_t len = 0;
3082
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303083 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003084 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
3085 adapter->sessionId, lookUpThreshold));
3086
3087 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303088 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003089 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003090 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003091 ret = -EFAULT;
3092 }
3093
3094 return ret;
3095}
3096
3097static int drv_cmd_set_roam_scan_period(hdd_adapter_t *adapter,
3098 hdd_context_t *hdd_ctx,
3099 uint8_t *command,
3100 uint8_t command_len,
3101 hdd_priv_data_t *priv_data)
3102{
3103 int ret = 0;
3104 uint8_t *value = command;
3105 uint8_t roamScanPeriod = 0;
3106 uint16_t neighborEmptyScanRefreshPeriod =
3107 CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
3108
3109 /* input refresh period is in terms of seconds */
3110
3111 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
3112 value = value + command_len + 1;
3113
3114 /* Convert the value from ascii to integer */
3115 ret = kstrtou8(value, 10, &roamScanPeriod);
3116 if (ret < 0) {
3117 /*
3118 * If the input value is greater than max value of datatype,
3119 * then also kstrtou8 fails
3120 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003121 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
3122 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3123 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003124 ret = -EINVAL;
3125 goto exit;
3126 }
3127
3128 if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000))
3129 || (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003130 hdd_err("Roam scan period value %d is out of range (Min: %d Max: %d)",
3131 roamScanPeriod,
3132 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3133 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003134 ret = -EINVAL;
3135 goto exit;
3136 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303137 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003138 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
3139 adapter->sessionId, roamScanPeriod));
3140 neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
3141
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003142 hdd_info("Received Command to Set roam scan period (Empty Scan refresh period) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003143 roamScanPeriod);
3144
3145 hdd_ctx->config->nEmptyScanRefreshPeriod =
3146 neighborEmptyScanRefreshPeriod;
3147 sme_update_empty_scan_refresh_period(hdd_ctx->hHal,
3148 adapter->sessionId,
3149 neighborEmptyScanRefreshPeriod);
3150
3151exit:
3152 return ret;
3153}
3154
3155static int drv_cmd_get_roam_scan_period(hdd_adapter_t *adapter,
3156 hdd_context_t *hdd_ctx,
3157 uint8_t *command,
3158 uint8_t command_len,
3159 hdd_priv_data_t *priv_data)
3160{
3161 int ret = 0;
3162 uint16_t nEmptyScanRefreshPeriod =
3163 sme_get_empty_scan_refresh_period(hdd_ctx->hHal);
3164 char extra[32];
3165 uint8_t len = 0;
3166
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303167 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003168 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
3169 adapter->sessionId,
3170 nEmptyScanRefreshPeriod));
3171 len = scnprintf(extra, sizeof(extra), "%s %d",
3172 "GETROAMSCANPERIOD",
3173 (nEmptyScanRefreshPeriod / 1000));
3174 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303175 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003176 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003177 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003178 ret = -EFAULT;
3179 }
3180
3181 return ret;
3182}
3183
3184static int drv_cmd_set_roam_scan_refresh_period(hdd_adapter_t *adapter,
3185 hdd_context_t *hdd_ctx,
3186 uint8_t *command,
3187 uint8_t command_len,
3188 hdd_priv_data_t *priv_data)
3189{
3190 int ret = 0;
3191 uint8_t *value = command;
3192 uint8_t roamScanRefreshPeriod = 0;
3193 uint16_t neighborScanRefreshPeriod =
3194 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
3195
3196 /* input refresh period is in terms of seconds */
3197 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
3198 value = value + command_len + 1;
3199
3200 /* Convert the value from ascii to integer */
3201 ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
3202 if (ret < 0) {
3203 /*
3204 * If the input value is greater than max value of datatype,
3205 * then also kstrtou8 fails
3206 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003207 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
3208 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000,
3209 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003210 ret = -EINVAL;
3211 goto exit;
3212 }
3213
3214 if ((roamScanRefreshPeriod <
3215 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000))
3216 || (roamScanRefreshPeriod >
3217 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003218 hdd_err("Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)",
3219 roamScanRefreshPeriod,
3220 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN
3221 / 1000),
3222 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX
3223 / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003224 ret = -EINVAL;
3225 goto exit;
3226 }
3227 neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
3228
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003229 hdd_info("Received Command to Set roam scan refresh period (Scan refresh period) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003230 roamScanRefreshPeriod);
3231
3232 hdd_ctx->config->nNeighborResultsRefreshPeriod =
3233 neighborScanRefreshPeriod;
3234 sme_set_neighbor_scan_refresh_period(hdd_ctx->hHal,
3235 adapter->sessionId,
3236 neighborScanRefreshPeriod);
3237
3238exit:
3239 return ret;
3240}
3241
3242static int drv_cmd_get_roam_scan_refresh_period(hdd_adapter_t *adapter,
3243 hdd_context_t *hdd_ctx,
3244 uint8_t *command,
3245 uint8_t command_len,
3246 hdd_priv_data_t *priv_data)
3247{
3248 int ret = 0;
3249 uint16_t value =
3250 sme_get_neighbor_scan_refresh_period(hdd_ctx->hHal);
3251 char extra[32];
3252 uint8_t len = 0;
3253
3254 len = scnprintf(extra, sizeof(extra), "%s %d",
3255 "GETROAMSCANREFRESHPERIOD",
3256 (value / 1000));
3257 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303258 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003259 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003260 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003261 ret = -EFAULT;
3262 }
3263
3264 return ret;
3265}
3266
3267static int drv_cmd_set_roam_mode(hdd_adapter_t *adapter,
3268 hdd_context_t *hdd_ctx,
3269 uint8_t *command,
3270 uint8_t command_len,
3271 hdd_priv_data_t *priv_data)
3272{
3273 int ret = 0;
3274 uint8_t *value = command;
3275 uint8_t roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
3276
3277 /* Move pointer to ahead of SETROAMMODE<delimiter> */
3278 value = value + SIZE_OF_SETROAMMODE + 1;
3279
3280 /* Convert the value from ascii to integer */
3281 ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
3282 if (ret < 0) {
3283 /*
3284 * If the input value is greater than max value of datatype,
3285 * then also kstrtou8 fails
3286 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003287 hdd_err("kstrtou8 failed range [%d - %d]",
3288 CFG_LFR_FEATURE_ENABLED_MIN,
3289 CFG_LFR_FEATURE_ENABLED_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003290 ret = -EINVAL;
3291 goto exit;
3292 }
3293 if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
3294 (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003295 hdd_err("Roam Mode value %d is out of range (Min: %d Max: %d)",
3296 roamMode,
3297 CFG_LFR_FEATURE_ENABLED_MIN,
3298 CFG_LFR_FEATURE_ENABLED_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003299 ret = -EINVAL;
3300 goto exit;
3301 }
3302
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003303 hdd_debug("Received Command to Set Roam Mode = %d",
3304 roamMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003305 /*
3306 * Note that
3307 * SETROAMMODE 0 is to enable LFR while
3308 * SETROAMMODE 1 is to disable LFR, but
3309 * notify_is_fast_roam_ini_feature_enabled 0/1 is to
3310 * enable/disable. So, we have to invert the value
3311 * to call sme_update_is_fast_roam_ini_feature_enabled.
3312 */
3313 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3314 roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
3315 else
3316 roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
3317
3318 hdd_ctx->config->isFastRoamIniFeatureEnabled = roamMode;
3319 if (roamMode) {
3320 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3321 sme_update_roam_scan_offload_enabled(
3322 (tHalHandle)(hdd_ctx->hHal),
3323 hdd_ctx->config->isRoamOffloadScanEnabled);
3324 sme_update_is_fast_roam_ini_feature_enabled(
3325 hdd_ctx->hHal,
3326 adapter->sessionId,
3327 roamMode);
3328 } else {
3329 sme_update_is_fast_roam_ini_feature_enabled(
3330 hdd_ctx->hHal,
3331 adapter->sessionId,
3332 roamMode);
3333 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3334 sme_update_roam_scan_offload_enabled(
3335 (tHalHandle)(hdd_ctx->hHal),
3336 hdd_ctx->config->isRoamOffloadScanEnabled);
3337 }
3338
3339
3340exit:
3341 return ret;
3342}
3343
3344static int drv_cmd_get_roam_mode(hdd_adapter_t *adapter,
3345 hdd_context_t *hdd_ctx,
3346 uint8_t *command,
3347 uint8_t command_len,
3348 hdd_priv_data_t *priv_data)
3349{
3350 int ret = 0;
3351 bool roamMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3352 char extra[32];
3353 uint8_t len = 0;
3354
3355 /*
3356 * roamMode value shall be inverted because the sementics is different.
3357 */
3358 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3359 roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
3360 else
3361 roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
3362
3363 len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303364 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003365 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003366 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003367 ret = -EFAULT;
3368 }
3369
3370 return ret;
3371}
3372
3373static int drv_cmd_set_roam_delta(hdd_adapter_t *adapter,
3374 hdd_context_t *hdd_ctx,
3375 uint8_t *command,
3376 uint8_t command_len,
3377 hdd_priv_data_t *priv_data)
3378{
3379 int ret = 0;
3380 uint8_t *value = command;
3381 uint8_t roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
3382
3383 /* Move pointer to ahead of SETROAMDELTA<delimiter> */
3384 value = value + command_len + 1;
3385
3386 /* Convert the value from ascii to integer */
3387 ret = kstrtou8(value, 10, &roamRssiDiff);
3388 if (ret < 0) {
3389 /*
3390 * If the input value is greater than max value of datatype,
3391 * then also kstrtou8 fails
3392 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003393 hdd_err("kstrtou8 failed range [%d - %d]",
3394 CFG_ROAM_RSSI_DIFF_MIN,
3395 CFG_ROAM_RSSI_DIFF_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003396 ret = -EINVAL;
3397 goto exit;
3398 }
3399
3400 if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
3401 (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003402 hdd_err("Roam rssi diff value %d is out of range (Min: %d Max: %d)",
3403 roamRssiDiff,
3404 CFG_ROAM_RSSI_DIFF_MIN,
3405 CFG_ROAM_RSSI_DIFF_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003406 ret = -EINVAL;
3407 goto exit;
3408 }
3409
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003410 hdd_info("Received Command to Set roam rssi diff = %d",
3411 roamRssiDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003412
3413 hdd_ctx->config->RoamRssiDiff = roamRssiDiff;
3414 sme_update_roam_rssi_diff(hdd_ctx->hHal,
3415 adapter->sessionId,
3416 roamRssiDiff);
3417
3418exit:
3419 return ret;
3420}
3421
3422static int drv_cmd_get_roam_delta(hdd_adapter_t *adapter,
3423 hdd_context_t *hdd_ctx,
3424 uint8_t *command,
3425 uint8_t command_len,
3426 hdd_priv_data_t *priv_data)
3427{
3428 int ret = 0;
3429 uint8_t roamRssiDiff =
3430 sme_get_roam_rssi_diff(hdd_ctx->hHal);
3431 char extra[32];
3432 uint8_t len = 0;
3433
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303434 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003435 TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
3436 adapter->sessionId, roamRssiDiff));
3437
3438 len = scnprintf(extra, sizeof(extra), "%s %d",
3439 command, roamRssiDiff);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303440 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003441
3442 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003443 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003444 ret = -EFAULT;
3445 }
3446
3447 return ret;
3448}
3449
3450static int drv_cmd_get_band(hdd_adapter_t *adapter,
3451 hdd_context_t *hdd_ctx,
3452 uint8_t *command,
3453 uint8_t command_len,
3454 hdd_priv_data_t *priv_data)
3455{
3456 int ret = 0;
3457 int band = -1;
3458 char extra[32];
3459 uint8_t len = 0;
3460
3461 hdd_get_band_helper(hdd_ctx, &band);
3462
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303463 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003464 TRACE_CODE_HDD_GETBAND_IOCTL,
3465 adapter->sessionId, band));
3466
3467 len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303468 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003469
3470 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003471 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003472 ret = -EFAULT;
3473 }
3474
3475 return ret;
3476}
3477
3478static int drv_cmd_set_roam_scan_channels(hdd_adapter_t *adapter,
3479 hdd_context_t *hdd_ctx,
3480 uint8_t *command,
3481 uint8_t command_len,
3482 hdd_priv_data_t *priv_data)
3483{
3484 return hdd_parse_set_roam_scan_channels(adapter, command);
3485}
3486
3487static int drv_cmd_get_roam_scan_channels(hdd_adapter_t *adapter,
3488 hdd_context_t *hdd_ctx,
3489 uint8_t *command,
3490 uint8_t command_len,
3491 hdd_priv_data_t *priv_data)
3492{
3493 int ret = 0;
3494 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
3495 uint8_t numChannels = 0;
3496 uint8_t j = 0;
3497 char extra[128] = { 0 };
3498 int len;
3499
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303500 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003501 sme_get_roam_scan_channel_list(hdd_ctx->hHal,
3502 ChannelList,
3503 &numChannels,
3504 adapter->sessionId)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003505 hdd_alert("failed to get roam scan channel list");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003506 ret = -EFAULT;
3507 goto exit;
3508 }
3509
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303510 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003511 TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
3512 adapter->sessionId, numChannels));
3513 /*
3514 * output channel list is of the format
3515 * [Number of roam scan channels][Channel1][Channel2]...
3516 * copy the number of channels in the 0th index
3517 */
3518 len = scnprintf(extra, sizeof(extra), "%s %d", command,
3519 numChannels);
3520 for (j = 0; (j < numChannels); j++)
3521 len += scnprintf(extra + len, sizeof(extra) - len,
3522 " %d", ChannelList[j]);
3523
Anurag Chouhan6d760662016-02-20 16:05:43 +05303524 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003525 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003526 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003527 ret = -EFAULT;
3528 goto exit;
3529 }
3530
3531exit:
3532 return ret;
3533}
3534
3535static int drv_cmd_get_ccx_mode(hdd_adapter_t *adapter,
3536 hdd_context_t *hdd_ctx,
3537 uint8_t *command,
3538 uint8_t command_len,
3539 hdd_priv_data_t *priv_data)
3540{
3541 int ret = 0;
3542 bool eseMode = sme_get_is_ese_feature_enabled(hdd_ctx->hHal);
3543 char extra[32];
3544 uint8_t len = 0;
3545
3546 /*
3547 * Check if the features OKC/ESE/11R are supported simultaneously,
3548 * then this operation is not permitted (return FAILURE)
3549 */
3550 if (eseMode &&
3551 hdd_is_okc_mode_enabled(hdd_ctx) &&
3552 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003553 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003554 ret = -EPERM;
3555 goto exit;
3556 }
3557
3558 len = scnprintf(extra, sizeof(extra), "%s %d",
3559 "GETCCXMODE", eseMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303560 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003561 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003562 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003563 ret = -EFAULT;
3564 goto exit;
3565 }
3566
3567exit:
3568 return ret;
3569}
3570
3571static int drv_cmd_get_okc_mode(hdd_adapter_t *adapter,
3572 hdd_context_t *hdd_ctx,
3573 uint8_t *command,
3574 uint8_t command_len,
3575 hdd_priv_data_t *priv_data)
3576{
3577 int ret = 0;
3578 bool okcMode = hdd_is_okc_mode_enabled(hdd_ctx);
3579 char extra[32];
3580 uint8_t len = 0;
3581
3582 /*
3583 * Check if the features OKC/ESE/11R are supported simultaneously,
3584 * then this operation is not permitted (return FAILURE)
3585 */
3586 if (okcMode &&
3587 sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
3588 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003589 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003590 ret = -EPERM;
3591 goto exit;
3592 }
3593
3594 len = scnprintf(extra, sizeof(extra), "%s %d",
3595 "GETOKCMODE", okcMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303596 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003597
3598 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003599 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003600 ret = -EFAULT;
3601 goto exit;
3602 }
3603
3604exit:
3605 return ret;
3606}
3607
3608static int drv_cmd_get_fast_roam(hdd_adapter_t *adapter,
3609 hdd_context_t *hdd_ctx,
3610 uint8_t *command,
3611 uint8_t command_len,
3612 hdd_priv_data_t *priv_data)
3613{
3614 int ret = 0;
3615 bool lfrMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3616 char extra[32];
3617 uint8_t len = 0;
3618
3619 len = scnprintf(extra, sizeof(extra), "%s %d",
3620 "GETFASTROAM", lfrMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303621 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003622
3623 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003624 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003625 ret = -EFAULT;
3626 }
3627
3628 return ret;
3629}
3630
3631static int drv_cmd_get_fast_transition(hdd_adapter_t *adapter,
3632 hdd_context_t *hdd_ctx,
3633 uint8_t *command,
3634 uint8_t command_len,
3635 hdd_priv_data_t *priv_data)
3636{
3637 int ret = 0;
3638 bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->hHal);
3639 char extra[32];
3640 uint8_t len = 0;
3641
3642 len = scnprintf(extra, sizeof(extra), "%s %d",
3643 "GETFASTTRANSITION", ft);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303644 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003645
3646 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003647 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003648 ret = -EFAULT;
3649 }
3650
3651 return ret;
3652}
3653
3654static int drv_cmd_set_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3655 hdd_context_t *hdd_ctx,
3656 uint8_t *command,
3657 uint8_t command_len,
3658 hdd_priv_data_t *priv_data)
3659{
3660 int ret = 0;
3661 uint8_t *value = command;
3662 uint8_t minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
3663
3664 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
3665 value = value + command_len + 1;
3666
3667 /* Convert the value from ascii to integer */
3668 ret = kstrtou8(value, 10, &minTime);
3669 if (ret < 0) {
3670 /*
3671 * If the input value is greater than max value of datatype,
3672 * then also kstrtou8 fails
3673 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003674 hdd_err("kstrtou8 failed range [%d - %d]",
3675 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3676 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003677 ret = -EINVAL;
3678 goto exit;
3679 }
3680
3681 if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
3682 (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003683 hdd_err("scan min channel time value %d is out of range (Min: %d Max: %d)",
3684 minTime,
3685 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3686 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003687 ret = -EINVAL;
3688 goto exit;
3689 }
3690
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303691 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003692 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
3693 adapter->sessionId, minTime));
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003694 hdd_info("Received Command to change channel min time = %d",
3695 minTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003696
3697 hdd_ctx->config->nNeighborScanMinChanTime = minTime;
3698 sme_set_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3699 minTime,
3700 adapter->sessionId);
3701
3702exit:
3703 return ret;
3704}
3705
3706static int drv_cmd_send_action_frame(hdd_adapter_t *adapter,
3707 hdd_context_t *hdd_ctx,
3708 uint8_t *command,
3709 uint8_t command_len,
3710 hdd_priv_data_t *priv_data)
3711{
3712 return hdd_parse_sendactionframe(adapter, command);
3713}
3714
3715static int drv_cmd_get_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3716 hdd_context_t *hdd_ctx,
3717 uint8_t *command,
3718 uint8_t command_len,
3719 hdd_priv_data_t *priv_data)
3720{
3721 int ret = 0;
3722 uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3723 adapter->sessionId);
3724 char extra[32];
3725 uint8_t len = 0;
3726
3727 /* value is interms of msec */
3728 len = scnprintf(extra, sizeof(extra), "%s %d",
3729 "GETROAMSCANCHANNELMINTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303730 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003731
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303732 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003733 TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL,
3734 adapter->sessionId, val));
3735
3736 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003737 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003738 ret = -EFAULT;
3739 }
3740
3741 return ret;
3742}
3743
3744static int drv_cmd_set_scan_channel_time(hdd_adapter_t *adapter,
3745 hdd_context_t *hdd_ctx,
3746 uint8_t *command,
3747 uint8_t command_len,
3748 hdd_priv_data_t *priv_data)
3749{
3750 int ret = 0;
3751 uint8_t *value = command;
3752 uint16_t maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
3753
3754 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
3755 value = value + command_len + 1;
3756
3757 /* Convert the value from ascii to integer */
3758 ret = kstrtou16(value, 10, &maxTime);
3759 if (ret < 0) {
3760 /*
3761 * If the input value is greater than max value of datatype,
3762 * then also kstrtou8 fails
3763 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003764 hdd_err("kstrtou16 failed range [%d - %d]",
3765 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3766 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003767 ret = -EINVAL;
3768 goto exit;
3769 }
3770
3771 if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
3772 (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003773 hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)",
3774 maxTime,
3775 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3776 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003777 ret = -EINVAL;
3778 goto exit;
3779 }
3780
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003781 hdd_info("Received Command to change channel max time = %d",
3782 maxTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003783
3784 hdd_ctx->config->nNeighborScanMaxChanTime = maxTime;
3785 sme_set_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3786 adapter->sessionId,
3787 maxTime);
3788
3789exit:
3790 return ret;
3791}
3792
3793static int drv_cmd_get_scan_channel_time(hdd_adapter_t *adapter,
3794 hdd_context_t *hdd_ctx,
3795 uint8_t *command,
3796 uint8_t command_len,
3797 hdd_priv_data_t *priv_data)
3798{
3799 int ret = 0;
3800 uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3801 adapter->sessionId);
3802 char extra[32];
3803 uint8_t len = 0;
3804
3805 /* value is interms of msec */
3806 len = scnprintf(extra, sizeof(extra), "%s %d",
3807 "GETSCANCHANNELTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303808 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003809
3810 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003811 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003812 ret = -EFAULT;
3813 }
3814
3815 return ret;
3816}
3817
3818static int drv_cmd_set_scan_home_time(hdd_adapter_t *adapter,
3819 hdd_context_t *hdd_ctx,
3820 uint8_t *command,
3821 uint8_t command_len,
3822 hdd_priv_data_t *priv_data)
3823{
3824 int ret = 0;
3825 uint8_t *value = command;
3826 uint16_t val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
3827
3828 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
3829 value = value + command_len + 1;
3830
3831 /* Convert the value from ascii to integer */
3832 ret = kstrtou16(value, 10, &val);
3833 if (ret < 0) {
3834 /*
3835 * If the input value is greater than max value of datatype,
3836 * then also kstrtou8 fails
3837 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003838 hdd_err("kstrtou16 failed range [%d - %d]",
3839 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3840 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003841 ret = -EINVAL;
3842 goto exit;
3843 }
3844
3845 if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
3846 (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003847 hdd_err("scan home time value %d is out of range (Min: %d Max: %d)",
3848 val,
3849 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3850 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003851 ret = -EINVAL;
3852 goto exit;
3853 }
3854
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003855 hdd_info("Received Command to change scan home time = %d",
3856 val);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003857
3858 hdd_ctx->config->nNeighborScanPeriod = val;
3859 sme_set_neighbor_scan_period(hdd_ctx->hHal,
3860 adapter->sessionId, val);
3861
3862exit:
3863 return ret;
3864}
3865
3866static int drv_cmd_get_scan_home_time(hdd_adapter_t *adapter,
3867 hdd_context_t *hdd_ctx,
3868 uint8_t *command,
3869 uint8_t command_len,
3870 hdd_priv_data_t *priv_data)
3871{
3872 int ret = 0;
3873 uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->hHal,
3874 adapter->
3875 sessionId);
3876 char extra[32];
3877 uint8_t len = 0;
3878
3879 /* value is interms of msec */
3880 len = scnprintf(extra, sizeof(extra), "%s %d",
3881 "GETSCANHOMETIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303882 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003883
3884 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003885 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003886 ret = -EFAULT;
3887 }
3888
3889 return ret;
3890}
3891
3892static int drv_cmd_set_roam_intra_band(hdd_adapter_t *adapter,
3893 hdd_context_t *hdd_ctx,
3894 uint8_t *command,
3895 uint8_t command_len,
3896 hdd_priv_data_t *priv_data)
3897{
3898 int ret = 0;
3899 uint8_t *value = command;
3900 uint8_t val = CFG_ROAM_INTRA_BAND_DEFAULT;
3901
3902 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
3903 value = value + command_len + 1;
3904
3905 /* Convert the value from ascii to integer */
3906 ret = kstrtou8(value, 10, &val);
3907 if (ret < 0) {
3908 /*
3909 * If the input value is greater than max value of datatype,
3910 * then also kstrtou8 fails
3911 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003912 hdd_err("kstrtou8 failed range [%d - %d]",
3913 CFG_ROAM_INTRA_BAND_MIN,
3914 CFG_ROAM_INTRA_BAND_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003915 ret = -EINVAL;
3916 goto exit;
3917 }
3918
3919 if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
3920 (val > CFG_ROAM_INTRA_BAND_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003921 hdd_err("intra band mode value %d is out of range (Min: %d Max: %d)",
3922 val,
3923 CFG_ROAM_INTRA_BAND_MIN,
3924 CFG_ROAM_INTRA_BAND_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003925 ret = -EINVAL;
3926 goto exit;
3927 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003928 hdd_info("Received Command to change intra band = %d",
3929 val);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003930
3931 hdd_ctx->config->nRoamIntraBand = val;
3932 sme_set_roam_intra_band(hdd_ctx->hHal, val);
3933
3934exit:
3935 return ret;
3936}
3937
3938static int drv_cmd_get_roam_intra_band(hdd_adapter_t *adapter,
3939 hdd_context_t *hdd_ctx,
3940 uint8_t *command,
3941 uint8_t command_len,
3942 hdd_priv_data_t *priv_data)
3943{
3944 int ret = 0;
3945 uint16_t val = sme_get_roam_intra_band(hdd_ctx->hHal);
3946 char extra[32];
3947 uint8_t len = 0;
3948
3949 /* value is interms of msec */
3950 len = scnprintf(extra, sizeof(extra), "%s %d",
3951 "GETROAMINTRABAND", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303952 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003953 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003954 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003955 ret = -EFAULT;
3956 }
3957
3958 return ret;
3959}
3960
3961static int drv_cmd_set_scan_n_probes(hdd_adapter_t *adapter,
3962 hdd_context_t *hdd_ctx,
3963 uint8_t *command,
3964 uint8_t command_len,
3965 hdd_priv_data_t *priv_data)
3966{
3967 int ret = 0;
3968 uint8_t *value = command;
3969 uint8_t nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
3970
3971 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */
3972 value = value + command_len + 1;
3973
3974 /* Convert the value from ascii to integer */
3975 ret = kstrtou8(value, 10, &nProbes);
3976 if (ret < 0) {
3977 /*
3978 * If the input value is greater than max value of datatype,
3979 * then also kstrtou8 fails
3980 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003981 hdd_err("kstrtou8 failed range [%d - %d]",
3982 CFG_ROAM_SCAN_N_PROBES_MIN,
3983 CFG_ROAM_SCAN_N_PROBES_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003984 ret = -EINVAL;
3985 goto exit;
3986 }
3987
3988 if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
3989 (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003990 hdd_err("NProbes value %d is out of range (Min: %d Max: %d)",
3991 nProbes,
3992 CFG_ROAM_SCAN_N_PROBES_MIN,
3993 CFG_ROAM_SCAN_N_PROBES_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003994 ret = -EINVAL;
3995 goto exit;
3996 }
3997
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003998 hdd_info("Received Command to Set nProbes = %d",
3999 nProbes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004000
4001 hdd_ctx->config->nProbes = nProbes;
4002 sme_update_roam_scan_n_probes(hdd_ctx->hHal,
4003 adapter->sessionId, nProbes);
4004
4005exit:
4006 return ret;
4007}
4008
4009static int drv_cmd_get_scan_n_probes(hdd_adapter_t *adapter,
4010 hdd_context_t *hdd_ctx,
4011 uint8_t *command,
4012 uint8_t command_len,
4013 hdd_priv_data_t *priv_data)
4014{
4015 int ret = 0;
4016 uint8_t val = sme_get_roam_scan_n_probes(hdd_ctx->hHal);
4017 char extra[32];
4018 uint8_t len = 0;
4019
4020 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304021 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004022 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004023 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004024 ret = -EFAULT;
4025 }
4026
4027 return ret;
4028}
4029
4030static int drv_cmd_set_scan_home_away_time(hdd_adapter_t *adapter,
4031 hdd_context_t *hdd_ctx,
4032 uint8_t *command,
4033 uint8_t command_len,
4034 hdd_priv_data_t *priv_data)
4035{
4036 int ret = 0;
4037 uint8_t *value = command;
4038 uint16_t homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
4039
4040 /* input value is in units of msec */
4041
4042 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
4043 value = value + command_len + 1;
4044
4045 /* Convert the value from ascii to integer */
4046 ret = kstrtou16(value, 10, &homeAwayTime);
4047 if (ret < 0) {
4048 /*
4049 * If the input value is greater than max value of datatype,
4050 * then also kstrtou8 fails
4051 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004052 hdd_err("kstrtou8 failed range [%d - %d]",
4053 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4054 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004055 ret = -EINVAL;
4056 goto exit;
4057 }
4058
4059 if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
4060 (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004061 hdd_err("homeAwayTime value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004062 homeAwayTime,
4063 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4064 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
4065 ret = -EINVAL;
4066 goto exit;
4067 }
4068
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004069 hdd_info("Received Command to Set scan away time = %d",
4070 homeAwayTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004071
4072 if (hdd_ctx->config->nRoamScanHomeAwayTime !=
4073 homeAwayTime) {
4074 hdd_ctx->config->nRoamScanHomeAwayTime = homeAwayTime;
4075 sme_update_roam_scan_home_away_time(hdd_ctx->hHal,
4076 adapter->sessionId,
4077 homeAwayTime,
4078 true);
4079 }
4080
4081exit:
4082 return ret;
4083}
4084
4085static int drv_cmd_get_scan_home_away_time(hdd_adapter_t *adapter,
4086 hdd_context_t *hdd_ctx,
4087 uint8_t *command,
4088 uint8_t command_len,
4089 hdd_priv_data_t *priv_data)
4090{
4091 int ret = 0;
4092 uint16_t val = sme_get_roam_scan_home_away_time(hdd_ctx->hHal);
4093 char extra[32];
4094 uint8_t len = 0;
4095
4096 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304097 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004098
4099 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004100 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004101 ret = -EFAULT;
4102 }
4103
4104 return ret;
4105}
4106
4107static int drv_cmd_reassoc(hdd_adapter_t *adapter,
4108 hdd_context_t *hdd_ctx,
4109 uint8_t *command,
4110 uint8_t command_len,
4111 hdd_priv_data_t *priv_data)
4112{
4113 return hdd_parse_reassoc(adapter, command);
4114}
4115
4116static int drv_cmd_set_wes_mode(hdd_adapter_t *adapter,
4117 hdd_context_t *hdd_ctx,
4118 uint8_t *command,
4119 uint8_t command_len,
4120 hdd_priv_data_t *priv_data)
4121{
4122 int ret = 0;
4123 uint8_t *value = command;
4124 uint8_t wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
4125
4126 /* Move pointer to ahead of SETWESMODE<delimiter> */
4127 value = value + command_len + 1;
4128
4129 /* Convert the value from ascii to integer */
4130 ret = kstrtou8(value, 10, &wesMode);
4131 if (ret < 0) {
4132 /*
4133 * If the input value is greater than max value of datatype,
4134 * then also kstrtou8 fails
4135 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004136 hdd_err("kstrtou8 failed range [%d - %d]",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004137 CFG_ENABLE_WES_MODE_NAME_MIN,
4138 CFG_ENABLE_WES_MODE_NAME_MAX);
4139 ret = -EINVAL;
4140 goto exit;
4141 }
4142
4143 if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
4144 (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004145 hdd_err("WES Mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004146 wesMode,
4147 CFG_ENABLE_WES_MODE_NAME_MIN,
4148 CFG_ENABLE_WES_MODE_NAME_MAX);
4149 ret = -EINVAL;
4150 goto exit;
4151 }
4152
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004153 hdd_info("Received Command to Set WES Mode rssi diff = %d",
4154 wesMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004155
4156 hdd_ctx->config->isWESModeEnabled = wesMode;
4157 sme_update_wes_mode(hdd_ctx->hHal, wesMode, adapter->sessionId);
4158
4159exit:
4160 return ret;
4161}
4162
4163static int drv_cmd_get_wes_mode(hdd_adapter_t *adapter,
4164 hdd_context_t *hdd_ctx,
4165 uint8_t *command,
4166 uint8_t command_len,
4167 hdd_priv_data_t *priv_data)
4168{
4169 int ret = 0;
4170 bool wesMode = sme_get_wes_mode(hdd_ctx->hHal);
4171 char extra[32];
4172 uint8_t len = 0;
4173
4174 len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304175 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004176 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004177 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004178 ret = -EFAULT;
4179 }
4180
4181 return ret;
4182}
4183
4184static int drv_cmd_set_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4185 hdd_context_t *hdd_ctx,
4186 uint8_t *command,
4187 uint8_t command_len,
4188 hdd_priv_data_t *priv_data)
4189{
4190 int ret = 0;
4191 uint8_t *value = command;
4192 uint8_t nOpportunisticThresholdDiff =
4193 CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
4194
4195 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
4196 value = value + command_len + 1;
4197
4198 /* Convert the value from ascii to integer */
4199 ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
4200 if (ret < 0) {
4201 /*
4202 * If the input value is greater than max value of datatype,
4203 * then also kstrtou8 fails
4204 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004205 hdd_err("kstrtou8 failed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004206 ret = -EINVAL;
4207 goto exit;
4208 }
4209
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004210 hdd_info("Received Command to Set Opportunistic Threshold diff = %d",
4211 nOpportunisticThresholdDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004212
4213 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->hHal,
4214 adapter->sessionId,
4215 nOpportunisticThresholdDiff);
4216
4217exit:
4218 return ret;
4219}
4220
4221static int drv_cmd_get_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4222 hdd_context_t *hdd_ctx,
4223 uint8_t *command,
4224 uint8_t command_len,
4225 hdd_priv_data_t *priv_data)
4226{
4227 int ret = 0;
4228 int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(
4229 hdd_ctx->hHal);
4230 char extra[32];
4231 uint8_t len = 0;
4232
4233 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304234 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004235 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004236 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004237 ret = -EFAULT;
4238 }
4239
4240 return ret;
4241}
4242
4243static int drv_cmd_set_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4244 hdd_context_t *hdd_ctx,
4245 uint8_t *command,
4246 uint8_t command_len,
4247 hdd_priv_data_t *priv_data)
4248{
4249 int ret = 0;
4250 uint8_t *value = command;
4251 uint8_t nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
4252
4253 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
4254 value = value + command_len + 1;
4255
4256 /* Convert the value from ascii to integer */
4257 ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
4258 if (ret < 0) {
4259 /*
4260 * If the input value is greater than max value of datatype,
4261 * then also kstrtou8 fails
4262 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004263 hdd_err("kstrtou8 failed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004264 ret = -EINVAL;
4265 goto exit;
4266 }
4267
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004268 hdd_info("Received Command to Set Roam Rescan RSSI Diff = %d",
4269 nRoamRescanRssiDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004270
4271 sme_set_roam_rescan_rssi_diff(hdd_ctx->hHal,
4272 adapter->sessionId,
4273 nRoamRescanRssiDiff);
4274
4275exit:
4276 return ret;
4277}
4278
4279static int drv_cmd_get_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4280 hdd_context_t *hdd_ctx,
4281 uint8_t *command,
4282 uint8_t command_len,
4283 hdd_priv_data_t *priv_data)
4284{
4285 int ret = 0;
4286 uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->hHal);
4287 char extra[32];
4288 uint8_t len = 0;
4289
4290 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304291 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004292 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004293 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004294 ret = -EFAULT;
4295 }
4296
4297 return ret;
4298}
4299
4300static int drv_cmd_set_fast_roam(hdd_adapter_t *adapter,
4301 hdd_context_t *hdd_ctx,
4302 uint8_t *command,
4303 uint8_t command_len,
4304 hdd_priv_data_t *priv_data)
4305{
4306 int ret = 0;
4307 uint8_t *value = command;
4308 uint8_t lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
4309
4310 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4311 value = value + command_len + 1;
4312
4313 /* Convert the value from ascii to integer */
4314 ret = kstrtou8(value, 10, &lfrMode);
4315 if (ret < 0) {
4316 /*
4317 * If the input value is greater than max value of datatype,
4318 * then also kstrtou8 fails
4319 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004320 hdd_err("kstrtou8 failed range [%d - %d]",
4321 CFG_LFR_FEATURE_ENABLED_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004322 CFG_LFR_FEATURE_ENABLED_MAX);
4323 ret = -EINVAL;
4324 goto exit;
4325 }
4326
4327 if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
4328 (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004329 hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004330 lfrMode,
4331 CFG_LFR_FEATURE_ENABLED_MIN,
4332 CFG_LFR_FEATURE_ENABLED_MAX);
4333 ret = -EINVAL;
4334 goto exit;
4335 }
4336
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004337 hdd_info("Received Command to change lfr mode = %d",
4338 lfrMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004339
4340 hdd_ctx->config->isFastRoamIniFeatureEnabled = lfrMode;
4341 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->hHal,
4342 adapter->
4343 sessionId,
4344 lfrMode);
4345
4346exit:
4347 return ret;
4348}
4349
4350static int drv_cmd_set_fast_transition(hdd_adapter_t *adapter,
4351 hdd_context_t *hdd_ctx,
4352 uint8_t *command,
4353 uint8_t command_len,
4354 hdd_priv_data_t *priv_data)
4355{
4356 int ret = 0;
4357 uint8_t *value = command;
4358 uint8_t ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
4359
4360 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4361 value = value + command_len + 1;
4362
4363 /* Convert the value from ascii to integer */
4364 ret = kstrtou8(value, 10, &ft);
4365 if (ret < 0) {
4366 /*
4367 * If the input value is greater than max value of datatype,
4368 * then also kstrtou8 fails
4369 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004370 hdd_err("kstrtou8 failed range [%d - %d]",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004371 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4372 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4373 ret = -EINVAL;
4374 goto exit;
4375 }
4376
4377 if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
4378 (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004379 hdd_err("ft mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004380 ft,
4381 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4382 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4383 ret = -EINVAL;
4384 goto exit;
4385 }
4386
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004387 hdd_info("Received Command to change ft mode = %d", ft);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004388
4389 hdd_ctx->config->isFastTransitionEnabled = ft;
4390 sme_update_fast_transition_enabled(hdd_ctx->hHal, ft);
4391
4392exit:
4393 return ret;
4394}
4395
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004396static int drv_cmd_fast_reassoc(hdd_adapter_t *adapter,
4397 hdd_context_t *hdd_ctx,
4398 uint8_t *command,
4399 uint8_t command_len,
4400 hdd_priv_data_t *priv_data)
4401{
4402 int ret = 0;
4403 uint8_t *value = command;
4404 uint8_t channel = 0;
4405 tSirMacAddr targetApBssid;
4406 uint32_t roamId = 0;
4407 tCsrRoamModifyProfileFields modProfileFields;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004408 tCsrHandoffRequest handoffInfo;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004409 hdd_station_ctx_t *pHddStaCtx;
4410
Krunal Sonibe766b02016-03-10 13:00:44 -08004411 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004412 hdd_warn("Unsupported in mode %s(%d)",
4413 hdd_device_mode_to_string(adapter->device_mode),
4414 adapter->device_mode);
4415 return -EINVAL;
4416 }
4417
4418 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4419
4420 /* if not associated, no need to proceed with reassoc */
4421 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004422 hdd_info("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004423 ret = -EINVAL;
4424 goto exit;
4425 }
4426
4427 ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
4428 &channel);
4429 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004430 hdd_err("Failed to parse reassoc command data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004431 goto exit;
4432 }
4433
Deepak Dhamdhere5fda0e42016-06-24 18:30:02 +05304434 /* Check channel number is a valid channel number */
4435 if (QDF_STATUS_SUCCESS !=
4436 wlan_hdd_validate_operation_channel(adapter, channel)) {
4437 hdd_err("Invalid Channel [%d]", channel);
4438 return -EINVAL;
4439 }
4440
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004441 /*
4442 * if the target bssid is same as currently associated AP,
4443 * issue reassoc to same AP
4444 */
Ankit Guptaa5076012016-09-14 11:32:19 -07004445 if (!qdf_mem_cmp(targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004446 pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05304447 QDF_MAC_ADDR_SIZE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004448 hdd_info("Reassoc BSSID is same as currently associated AP bssid");
Deepak Dhamdhere5fda0e42016-06-24 18:30:02 +05304449 if (roaming_offload_enabled(hdd_ctx)) {
4450 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4451 targetApBssid, (int)channel);
4452 } else {
4453 sme_get_modify_profile_fields(hdd_ctx->hHal,
4454 adapter->sessionId,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004455 &modProfileFields);
Deepak Dhamdhere5fda0e42016-06-24 18:30:02 +05304456 sme_roam_reassoc(hdd_ctx->hHal, adapter->sessionId,
4457 NULL, modProfileFields, &roamId, 1);
4458 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004459 return 0;
4460 }
4461
Varun Reddy Yeturud351a6c2016-03-16 14:01:00 -07004462 if (roaming_offload_enabled(hdd_ctx)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004463 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4464 targetApBssid, (int)channel);
4465 goto exit;
4466 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004467 /* Proceed with reassoc */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004468 handoffInfo.channel = channel;
4469 handoffInfo.src = FASTREASSOC;
Varun Reddy Yeturua66f9c52016-02-17 09:58:40 -08004470 qdf_mem_copy(handoffInfo.bssid.bytes, targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004471 sizeof(tSirMacAddr));
4472 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
4473 &handoffInfo);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004474exit:
4475 return ret;
4476}
4477
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004478static int drv_cmd_set_roam_scan_control(hdd_adapter_t *adapter,
4479 hdd_context_t *hdd_ctx,
4480 uint8_t *command,
4481 uint8_t command_len,
4482 hdd_priv_data_t *priv_data)
4483{
4484 int ret = 0;
4485 uint8_t *value = command;
4486 uint8_t roamScanControl = 0;
4487
4488 /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
4489 value = value + command_len + 1;
4490
4491 /* Convert the value from ascii to integer */
4492 ret = kstrtou8(value, 10, &roamScanControl);
4493 if (ret < 0) {
4494 /*
4495 * If the input value is greater than max value of datatype,
4496 * then also kstrtou8 fails
4497 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004498 hdd_err("kstrtou8 failed ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004499 ret = -EINVAL;
4500 goto exit;
4501 }
4502
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004503 hdd_info("Received Command to Set roam scan control = %d",
4504 roamScanControl);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004505
4506 if (0 != roamScanControl) {
4507 ret = 0; /* return success but ignore param value "true" */
4508 goto exit;
4509 }
4510
4511 sme_set_roam_scan_control(hdd_ctx->hHal,
4512 adapter->sessionId,
4513 roamScanControl);
4514
4515exit:
4516 return ret;
4517}
4518
4519static int drv_cmd_set_okc_mode(hdd_adapter_t *adapter,
4520 hdd_context_t *hdd_ctx,
4521 uint8_t *command,
4522 uint8_t command_len,
4523 hdd_priv_data_t *priv_data)
4524{
4525 int ret = 0;
4526 uint8_t *value = command;
4527 uint8_t okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
4528
4529 /*
4530 * Check if the features OKC/ESE/11R are supported simultaneously,
4531 * then this operation is not permitted (return FAILURE)
4532 */
4533 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4534 hdd_is_okc_mode_enabled(hdd_ctx) &&
4535 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004536 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004537 ret = -EPERM;
4538 goto exit;
4539 }
4540
4541 /* Move pointer to ahead of SETOKCMODE<delimiter> */
4542 value = value + command_len + 1;
4543
4544 /* Convert the value from ascii to integer */
4545 ret = kstrtou8(value, 10, &okcMode);
4546 if (ret < 0) {
4547 /*
4548 * If the input value is greater than max value of datatype,
4549 * then also kstrtou8 fails
4550 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004551 hdd_err("kstrtou8 failed range [%d - %d]",
4552 CFG_OKC_FEATURE_ENABLED_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004553 CFG_OKC_FEATURE_ENABLED_MAX);
4554 ret = -EINVAL;
4555 goto exit;
4556 }
4557
4558 if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
4559 (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004560 hdd_err("Okc mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004561 okcMode,
4562 CFG_OKC_FEATURE_ENABLED_MIN,
4563 CFG_OKC_FEATURE_ENABLED_MAX);
4564 ret = -EINVAL;
4565 goto exit;
4566 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004567 hdd_info("Received Command to change okc mode = %d",
4568 okcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004569
4570 hdd_ctx->config->isOkcIniFeatureEnabled = okcMode;
4571
4572exit:
4573 return ret;
4574}
4575
4576static int drv_cmd_get_roam_scan_control(hdd_adapter_t *adapter,
4577 hdd_context_t *hdd_ctx,
4578 uint8_t *command,
4579 uint8_t command_len,
4580 hdd_priv_data_t *priv_data)
4581{
4582 int ret = 0;
4583 bool roamScanControl = sme_get_roam_scan_control(hdd_ctx->hHal);
4584 char extra[32];
4585 uint8_t len = 0;
4586
4587 len = scnprintf(extra, sizeof(extra), "%s %d",
4588 command, roamScanControl);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304589 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004590 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004591 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004592 ret = -EFAULT;
4593 }
4594
4595 return ret;
4596}
4597
4598static int drv_cmd_bt_coex_mode(hdd_adapter_t *adapter,
4599 hdd_context_t *hdd_ctx,
4600 uint8_t *command,
4601 uint8_t command_len,
4602 hdd_priv_data_t *priv_data)
4603{
4604 int ret = 0;
4605 char *bcMode;
4606
4607 bcMode = command + 11;
4608 if ('1' == *bcMode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004609 hdd_debug("BTCOEXMODE %d", *bcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004610 hdd_ctx->btCoexModeSet = true;
4611 ret = wlan_hdd_scan_abort(adapter);
4612 if (ret < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004613 hdd_err("Failed to abort existing scan status: %d",
4614 ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004615 }
4616 } else if ('2' == *bcMode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004617 hdd_debug("BTCOEXMODE %d", *bcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004618 hdd_ctx->btCoexModeSet = false;
4619 }
4620
4621 return ret;
4622}
4623
4624static int drv_cmd_scan_active(hdd_adapter_t *adapter,
4625 hdd_context_t *hdd_ctx,
4626 uint8_t *command,
4627 uint8_t command_len,
4628 hdd_priv_data_t *priv_data)
4629{
4630 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
4631 return 0;
4632}
4633
4634static int drv_cmd_scan_passive(hdd_adapter_t *adapter,
4635 hdd_context_t *hdd_ctx,
4636 uint8_t *command,
4637 uint8_t command_len,
4638 hdd_priv_data_t *priv_data)
4639{
4640 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN;
4641 return 0;
4642}
4643
4644static int drv_cmd_get_dwell_time(hdd_adapter_t *adapter,
4645 hdd_context_t *hdd_ctx,
4646 uint8_t *command,
4647 uint8_t command_len,
4648 hdd_priv_data_t *priv_data)
4649{
4650 int ret = 0;
4651 struct hdd_config *pCfg =
4652 (WLAN_HDD_GET_CTX(adapter))->config;
4653 char extra[32];
4654 uint8_t len = 0;
4655
4656 memset(extra, 0, sizeof(extra));
4657 ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304658 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004659 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004660 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004661 ret = -EFAULT;
4662 goto exit;
4663 }
4664 ret = len;
4665exit:
4666 return ret;
4667}
4668
4669static int drv_cmd_set_dwell_time(hdd_adapter_t *adapter,
4670 hdd_context_t *hdd_ctx,
4671 uint8_t *command,
4672 uint8_t command_len,
4673 hdd_priv_data_t *priv_data)
4674{
4675 return hdd_set_dwell_time(adapter, command);
4676}
4677
4678static int drv_cmd_miracast(hdd_adapter_t *adapter,
4679 hdd_context_t *hdd_ctx,
4680 uint8_t *command,
4681 uint8_t command_len,
4682 hdd_priv_data_t *priv_data)
4683{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304684 QDF_STATUS ret_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004685 int ret = 0;
4686 tHalHandle hHal;
4687 uint8_t filterType = 0;
4688 hdd_context_t *pHddCtx = NULL;
4689 uint8_t *value;
4690
4691 pHddCtx = WLAN_HDD_GET_CTX(adapter);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05304692 if (wlan_hdd_validate_context(pHddCtx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004693 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004694
4695 hHal = pHddCtx->hHal;
4696 value = command + 9;
4697
4698 /* Convert the value from ascii to integer */
4699 ret = kstrtou8(value, 10, &filterType);
4700 if (ret < 0) {
4701 /*
4702 * If the input value is greater than max value of datatype,
4703 * then also kstrtou8 fails
4704 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004705 hdd_err("kstrtou8 failed range ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004706 ret = -EINVAL;
4707 goto exit;
4708 }
4709 if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL)
4710 || (filterType >
4711 WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004712 hdd_err("Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004713 ret = -EINVAL;
4714 goto exit;
4715 }
4716 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
4717 pHddCtx->miracast_value = filterType;
4718
4719 ret_status = sme_set_miracast(hHal, filterType);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304720 if (QDF_STATUS_SUCCESS != ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004721 hdd_err("Failed to set miracast");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004722 return -EBUSY;
4723 }
4724
Tushnim Bhattacharyyaca50b322015-12-28 17:14:36 -08004725 if (cds_is_mcc_in_24G())
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004726 return cds_set_mas(adapter, filterType);
4727
4728exit:
4729 return ret;
4730}
4731
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004732/* Function header is left blank intentionally */
4733static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie,
4734 int32_t *oui_length, int32_t limit)
4735{
4736 uint8_t len;
4737 uint8_t data;
4738
4739 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
4740 command++;
4741 limit--;
4742 }
4743
4744 len = 2;
4745
4746 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
4747 (limit > 1)) {
4748 sscanf(command, "%02x", (unsigned int *)&data);
4749 ie[len++] = data;
4750 command += 2;
4751 limit -= 2;
4752 }
4753
4754 *oui_length = len - 2;
4755
4756 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
4757 command++;
4758 limit--;
4759 }
4760
4761 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
4762 (limit > 1)) {
4763 sscanf(command, "%02x", (unsigned int *)&data);
4764 ie[len++] = data;
4765 command += 2;
4766 limit -= 2;
4767 }
4768
4769 ie[0] = IE_EID_VENDOR;
4770 ie[1] = len - 2;
4771
4772 return len;
4773}
4774
4775/**
4776 * drv_cmd_set_ibss_beacon_oui_data() - set ibss oui data command
4777 * @adapter: Pointer to adapter
4778 * @hdd_ctx: Pointer to HDD context
4779 * @command: Pointer to command string
4780 * @command_len : Command length
4781 * @priv_data : Pointer to priv data
4782 *
4783 * Return:
4784 * int status code
4785 */
4786static int drv_cmd_set_ibss_beacon_oui_data(hdd_adapter_t *adapter,
4787 hdd_context_t *hdd_ctx,
4788 uint8_t *command,
4789 uint8_t command_len,
4790 hdd_priv_data_t *priv_data)
4791{
4792 int i = 0;
4793 int status;
4794 int ret = 0;
4795 uint8_t *ibss_ie;
4796 int32_t oui_length = 0;
4797 uint32_t ibss_ie_length;
4798 uint8_t *value = command;
4799 tSirModifyIE ibssModifyIE;
4800 tCsrRoamProfile *pRoamProfile;
4801 hdd_wext_state_t *pWextState;
4802
4803
Krunal Sonibe766b02016-03-10 13:00:44 -08004804 if (QDF_IBSS_MODE != adapter->device_mode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004805 hdd_info("Device_mode %s(%d) not IBSS",
4806 hdd_device_mode_to_string(adapter->device_mode),
4807 adapter->device_mode);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004808 return ret;
4809 }
4810
4811 pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(adapter);
4812
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004813 hdd_info("received command %s", ((char *)value));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004814
4815
4816 /* validate argument of command */
4817 if (strlen(value) <= command_len) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004818 hdd_err("No arguments in command length %zu",
4819 strlen(value));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004820 ret = -EFAULT;
4821 goto exit;
4822 }
4823
4824 /* moving to arguments of commands */
4825 value = value + command_len;
4826 command_len = strlen(value);
4827
4828 /* oui_data can't be less than 3 bytes */
4829 if (command_len < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004830 hdd_err("Invalid SETIBSSBEACONOUIDATA command length %d",
4831 command_len);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004832 ret = -EFAULT;
4833 goto exit;
4834 }
4835
4836 ibss_ie = qdf_mem_malloc(command_len);
4837 if (!ibss_ie) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004838 hdd_err("Could not allocate memory for command length %d",
4839 command_len);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004840 ret = -ENOMEM;
4841 goto exit;
4842 }
4843 qdf_mem_zero(ibss_ie, command_len);
4844
4845 ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie,
4846 &oui_length,
4847 command_len);
4848 if (ibss_ie_length <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004849 hdd_err("Could not parse command %s return length %d",
4850 value, ibss_ie_length);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004851 ret = -EFAULT;
4852 qdf_mem_free(ibss_ie);
4853 goto exit;
4854 }
4855
4856 pRoamProfile = &pWextState->roamProfile;
4857
4858 qdf_copy_macaddr(&ibssModifyIE.bssid,
4859 pRoamProfile->BSSIDs.bssid);
4860
4861 ibssModifyIE.smeSessionId = adapter->sessionId;
4862 ibssModifyIE.notify = true;
4863 ibssModifyIE.ieID = IE_EID_VENDOR;
4864 ibssModifyIE.ieIDLen = ibss_ie_length;
4865 ibssModifyIE.ieBufferlength = ibss_ie_length;
4866 ibssModifyIE.pIEBuffer = ibss_ie;
4867 ibssModifyIE.oui_length = oui_length;
4868
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004869 hdd_warn("ibss_ie length %d oui_length %d ibss_ie:",
4870 ibss_ie_length, oui_length);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004871 while (i < ibssModifyIE.ieBufferlength)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004872 hdd_warn("0x%x", ibss_ie[i++]);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004873
4874 /* Probe Bcn modification */
4875 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
4876 &ibssModifyIE, eUPDATE_IE_PROBE_BCN);
4877
4878 /* Populating probe resp frame */
4879 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
4880 &ibssModifyIE, eUPDATE_IE_PROBE_RESP);
4881
4882 qdf_mem_free(ibss_ie);
4883
4884 status = sme_send_cesium_enable_ind((tHalHandle)(hdd_ctx->hHal),
4885 adapter->sessionId);
4886 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004887 hdd_err("Could not send cesium enable indication %d",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004888 status);
4889 ret = -EINVAL;
4890 goto exit;
4891 }
4892
4893exit:
4894 return ret;
4895}
4896
4897static int drv_cmd_set_rmc_enable(hdd_adapter_t *adapter,
4898 hdd_context_t *hdd_ctx,
4899 uint8_t *command,
4900 uint8_t command_len,
4901 hdd_priv_data_t *priv_data)
4902{
4903 int ret = 0;
4904 uint8_t *value = command;
4905 uint8_t ucRmcEnable = 0;
4906 int status;
4907
Krunal Sonibe766b02016-03-10 13:00:44 -08004908 if ((QDF_IBSS_MODE != adapter->device_mode) &&
4909 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004910 hdd_err("Received SETRMCENABLE cmd in invalid mode %s(%d)",
4911 hdd_device_mode_to_string(adapter->device_mode),
4912 adapter->device_mode);
4913 hdd_err("SETRMCENABLE cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004914 ret = -EINVAL;
4915 goto exit;
4916 }
4917
4918 status = hdd_parse_setrmcenable_command(value, &ucRmcEnable);
4919 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004920 hdd_err("Invalid SETRMCENABLE command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004921 ret = -EINVAL;
4922 goto exit;
4923 }
4924
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004925 hdd_info("ucRmcEnable %d ", ucRmcEnable);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004926
4927 if (true == ucRmcEnable) {
4928 status = sme_enable_rmc((tHalHandle)
4929 (hdd_ctx->hHal),
4930 adapter->sessionId);
4931 } else if (false == ucRmcEnable) {
4932 status = sme_disable_rmc((tHalHandle)
4933 (hdd_ctx->hHal),
4934 adapter->sessionId);
4935 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004936 hdd_err("Invalid SETRMCENABLE command %d",
4937 ucRmcEnable);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004938 ret = -EINVAL;
4939 goto exit;
4940 }
4941
4942 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004943 hdd_err("SETRMC %d failed status %d",
4944 ucRmcEnable, status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004945 ret = -EINVAL;
4946 goto exit;
4947 }
4948
4949exit:
4950 return ret;
4951}
4952
4953static int drv_cmd_set_rmc_action_period(hdd_adapter_t *adapter,
4954 hdd_context_t *hdd_ctx,
4955 uint8_t *command,
4956 uint8_t command_len,
4957 hdd_priv_data_t *priv_data)
4958{
4959 int ret = 0;
4960 uint8_t *value = command;
4961 uint32_t uActionPeriod = 0;
4962 int status;
4963
Krunal Sonibe766b02016-03-10 13:00:44 -08004964 if ((QDF_IBSS_MODE != adapter->device_mode) &&
4965 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004966 hdd_err("Received SETRMC cmd in invalid mode %s(%d)",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004967 hdd_device_mode_to_string(adapter->device_mode),
4968 adapter->device_mode);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004969 hdd_err("SETRMC cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004970 ret = -EINVAL;
4971 goto exit;
4972 }
4973
4974 status = hdd_parse_setrmcactionperiod_command(value, &uActionPeriod);
4975 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004976 hdd_err("Invalid SETRMCACTIONPERIOD command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004977 ret = -EINVAL;
4978 goto exit;
4979 }
4980
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004981 hdd_info("uActionPeriod %d ",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004982 uActionPeriod);
4983
4984 if (sme_cfg_set_int(hdd_ctx->hHal,
4985 WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY,
4986 uActionPeriod)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004987 hdd_err("Could not set SETRMCACTIONPERIOD %d",
4988 uActionPeriod);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004989 ret = -EINVAL;
4990 goto exit;
4991 }
4992
4993 status = sme_send_rmc_action_period((tHalHandle)(hdd_ctx->hHal),
4994 adapter->sessionId);
4995 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004996 hdd_err("Could not send cesium enable indication %d",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004997 status);
4998 ret = -EINVAL;
4999 goto exit;
5000 }
5001
5002exit:
5003 return ret;
5004}
5005
5006static int drv_cmd_get_ibss_peer_info_all(hdd_adapter_t *adapter,
5007 hdd_context_t *hdd_ctx,
5008 uint8_t *command,
5009 uint8_t command_len,
5010 hdd_priv_data_t *priv_data)
5011{
5012 int ret = 0;
5013 int status = QDF_STATUS_SUCCESS;
5014 hdd_station_ctx_t *pHddStaCtx = NULL;
5015 char *extra = NULL;
5016 int idx = 0;
5017 int length = 0;
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005018 uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005019 uint32_t numOfBytestoPrint = 0;
5020
Krunal Sonibe766b02016-03-10 13:00:44 -08005021 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005022 hdd_warn("Unsupported in mode %s(%d)",
5023 hdd_device_mode_to_string(adapter->device_mode),
5024 adapter->device_mode);
5025 return -EINVAL;
5026 }
5027
5028 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005029 hdd_info("Received GETIBSSPEERINFOALL Command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005030
5031 /* Handle the command */
5032 status = hdd_cfg80211_get_ibss_peer_info_all(adapter);
5033 if (QDF_STATUS_SUCCESS == status) {
5034 /*
5035 * The variable extra needed to be allocated on the heap since
5036 * amount of memory required to copy the data for 32 devices
5037 * exceeds the size of 1024 bytes of default stack size. On
5038 * 64 bit devices, the default max stack size of 2048 bytes
5039 */
5040 extra = kmalloc(WLAN_MAX_BUF_SIZE, GFP_KERNEL);
5041
5042 if (NULL == extra) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005043 hdd_err("kmalloc failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005044 ret = -EINVAL;
5045 goto exit;
5046 }
5047
5048 /* Copy number of stations */
5049 length = scnprintf(extra, WLAN_MAX_BUF_SIZE, "%d ",
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005050 pHddStaCtx->ibss_peer_info.numPeers);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005051 numOfBytestoPrint = length;
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005052 for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numPeers;
5053 idx++) {
5054 int8_t rssi;
5055 uint32_t tx_rate;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005056
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005057 qdf_mem_copy(mac_addr,
5058 pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5059 mac_addr, sizeof(mac_addr));
5060
5061 tx_rate =
5062 pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5063 txRate;
Sriram Madhvapathi85df7c72016-09-30 11:35:28 +05305064 /*
5065 * Only lower 3 bytes are rate info. Mask of the MSByte
5066 */
5067 tx_rate &= 0x00FFFFFF;
5068
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005069 rssi = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5070 rssi;
5071
5072 length += scnprintf((extra + length),
5073 WLAN_MAX_BUF_SIZE - length,
5074 "%02x:%02x:%02x:%02x:%02x:%02x %d %d ",
5075 mac_addr[0], mac_addr[1], mac_addr[2],
5076 mac_addr[3], mac_addr[4], mac_addr[5],
5077 tx_rate, rssi);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005078 /*
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005079 * cdf_trace_msg has limitation of 512 bytes for the
5080 * print buffer. Hence printing the data in two chunks.
5081 * The first chunk will have the data for 16 devices
5082 * and the second chunk will have the rest.
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005083 */
5084 if (idx < NUM_OF_STA_DATA_TO_PRINT)
5085 numOfBytestoPrint = length;
5086 }
5087
5088 /*
5089 * Copy the data back into buffer, if the data to copy is
5090 * more than 512 bytes than we will split the data and do
5091 * it in two shots
5092 */
5093 if (copy_to_user(priv_data->buf, extra, numOfBytestoPrint)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005094 hdd_err("Copy into user data buffer failed ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005095 ret = -EFAULT;
5096 goto exit;
5097 }
5098
5099 priv_data->buf[numOfBytestoPrint] = '\0';
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005100 hdd_debug("%s", priv_data->buf);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005101
5102 if (length > numOfBytestoPrint) {
5103 if (copy_to_user
5104 (priv_data->buf + numOfBytestoPrint,
5105 extra + numOfBytestoPrint,
5106 length - numOfBytestoPrint + 1)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005107 hdd_err("Copy into user data buffer failed ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005108 ret = -EFAULT;
5109 goto exit;
5110 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005111 hdd_debug("%s", &priv_data->buf[numOfBytestoPrint]);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005112 }
5113
5114 /* Free temporary buffer */
5115 kfree(extra);
5116 } else {
5117 /* Command failed, log error */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005118 hdd_err("GETIBSSPEERINFOALL command failed with status code %d",
5119 status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005120 ret = -EINVAL;
5121 goto exit;
5122 }
5123 ret = 0;
5124
5125exit:
5126 return ret;
5127}
5128
5129/* Peer Info <Peer Addr> command */
5130static int drv_cmd_get_ibss_peer_info(hdd_adapter_t *adapter,
5131 hdd_context_t *hdd_ctx,
5132 uint8_t *command,
5133 uint8_t command_len,
5134 hdd_priv_data_t *priv_data)
5135{
5136 int ret = 0;
5137 uint8_t *value = command;
5138 QDF_STATUS status;
5139 hdd_station_ctx_t *pHddStaCtx = NULL;
5140 char extra[128] = { 0 };
5141 uint32_t length = 0;
5142 uint8_t staIdx = 0;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005143 struct qdf_mac_addr peerMacAddr;
5144
Krunal Sonibe766b02016-03-10 13:00:44 -08005145 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005146 hdd_warn("Unsupported in mode %s(%d)",
5147 hdd_device_mode_to_string(adapter->device_mode),
5148 adapter->device_mode);
5149 return -EINVAL;
5150 }
5151
5152 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5153
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005154 hdd_info("Received GETIBSSPEERINFO Command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005155
5156 /* if there are no peers, no need to continue with the command */
5157 if (eConnectionState_IbssConnected !=
5158 pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005159 hdd_info("No IBSS Peers coalesced");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005160 ret = -EINVAL;
5161 goto exit;
5162 }
5163
5164 /* Parse the incoming command buffer */
5165 status = hdd_parse_get_ibss_peer_info(value, &peerMacAddr);
5166 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005167 hdd_err("Invalid GETIBSSPEERINFO command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005168 ret = -EINVAL;
5169 goto exit;
5170 }
5171
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005172 /* Get station index for the peer mac address and sanitize it */
Deepak Dhamdhere5872c8c2016-06-02 15:51:47 -07005173 hdd_get_peer_sta_id(pHddStaCtx, &peerMacAddr, &staIdx);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005174
Naveen Rawatc45d1622016-07-05 12:20:09 -07005175 if (staIdx > MAX_PEERS) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005176 hdd_err("Invalid StaIdx %d returned", staIdx);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005177 ret = -EINVAL;
5178 goto exit;
5179 }
5180
5181 /* Handle the command */
5182 status = hdd_cfg80211_get_ibss_peer_info(adapter, staIdx);
5183 if (QDF_STATUS_SUCCESS == status) {
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005184 uint32_t txRate =
5185 pHddStaCtx->ibss_peer_info.peerInfoParams[0].txRate;
Sriram Madhvapathi85df7c72016-09-30 11:35:28 +05305186 /* Only lower 3 bytes are rate info. Mask of the MSByte */
5187 txRate &= 0x00FFFFFF;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005188
5189 length = scnprintf(extra, sizeof(extra), "%d %d",
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005190 (int)txRate,
5191 (int)pHddStaCtx->ibss_peer_info.
5192 peerInfoParams[0].rssi);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005193
5194 /* Copy the data back into buffer */
5195 if (copy_to_user(priv_data->buf, &extra, length + 1)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005196 hdd_err("copy data to user buffer failed GETIBSSPEERINFO command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005197 ret = -EFAULT;
5198 goto exit;
5199 }
5200 } else {
5201 /* Command failed, log error */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005202 hdd_err("GETIBSSPEERINFO command failed with status code %d",
5203 status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005204 ret = -EINVAL;
5205 goto exit;
5206 }
5207
5208 /* Success ! */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005209 hdd_debug("%s", priv_data->buf);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005210 ret = 0;
5211
5212exit:
5213 return ret;
5214}
5215
5216static int drv_cmd_set_rmc_tx_rate(hdd_adapter_t *adapter,
5217 hdd_context_t *hdd_ctx,
5218 uint8_t *command,
5219 uint8_t command_len,
5220 hdd_priv_data_t *priv_data)
5221{
5222 int ret = 0;
5223 uint8_t *value = command;
5224 uint32_t uRate = 0;
5225 tTxrateinfoflags txFlags = 0;
5226 tSirRateUpdateInd rateUpdateParams = {0};
5227 int status;
5228 struct hdd_config *pConfig = hdd_ctx->config;
5229
Krunal Sonibe766b02016-03-10 13:00:44 -08005230 if ((QDF_IBSS_MODE != adapter->device_mode) &&
5231 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005232 hdd_err("Received SETRMCTXRATE cmd in invalid mode %s(%d)",
5233 hdd_device_mode_to_string(adapter->device_mode),
5234 adapter->device_mode);
5235 hdd_err("SETRMCTXRATE cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005236 ret = -EINVAL;
5237 goto exit;
5238 }
5239
5240 status = hdd_parse_setrmcrate_command(value, &uRate, &txFlags);
5241 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005242 hdd_err("Invalid SETRMCTXRATE command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005243 ret = -EINVAL;
5244 goto exit;
5245 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005246 hdd_info("uRate %d ", uRate);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005247 /* -1 implies ignore this param */
5248 rateUpdateParams.ucastDataRate = -1;
5249
5250 /*
5251 * Fill the user specifieed RMC rate param
5252 * and the derived tx flags.
5253 */
5254 rateUpdateParams.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
5255 rateUpdateParams.reliableMcastDataRate = uRate;
5256 rateUpdateParams.reliableMcastDataRateTxFlag = txFlags;
5257 rateUpdateParams.dev_mode = adapter->device_mode;
5258 rateUpdateParams.bcastDataRate = -1;
5259 memcpy(rateUpdateParams.bssid.bytes,
5260 adapter->macAddressCurrent.bytes,
5261 sizeof(rateUpdateParams.bssid));
5262 status = sme_send_rate_update_ind((tHalHandle) (hdd_ctx->hHal),
5263 &rateUpdateParams);
5264
5265exit:
5266 return ret;
5267}
5268
5269static int drv_cmd_set_ibss_tx_fail_event(hdd_adapter_t *adapter,
5270 hdd_context_t *hdd_ctx,
5271 uint8_t *command,
5272 uint8_t command_len,
5273 hdd_priv_data_t *priv_data)
5274{
5275 int ret = 0;
5276 char *value;
5277 uint8_t tx_fail_count = 0;
5278 uint16_t pid = 0;
5279
5280 value = command;
5281
5282 ret = hdd_parse_ibsstx_fail_event_params(value, &tx_fail_count, &pid);
5283
5284 if (0 != ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005285 hdd_info("Failed to parse SETIBSSTXFAILEVENT arguments");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005286 goto exit;
5287 }
5288
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005289 hdd_info("tx_fail_cnt=%hhu, pid=%hu", tx_fail_count, pid);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005290
5291 if (0 == tx_fail_count) {
5292 /* Disable TX Fail Indication */
5293 if (QDF_STATUS_SUCCESS ==
5294 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5295 tx_fail_count,
5296 NULL)) {
5297 cesium_pid = 0;
5298 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005299 hdd_err("failed to disable TX Fail Event ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005300 ret = -EINVAL;
5301 }
5302 } else {
5303 if (QDF_STATUS_SUCCESS ==
5304 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5305 tx_fail_count,
5306 (void *)hdd_tx_fail_ind_callback)) {
5307 cesium_pid = pid;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005308 hdd_info("Registered Cesium pid %u",
5309 cesium_pid);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005310 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005311 hdd_err("Failed to enable TX Fail Monitoring");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005312 ret = -EINVAL;
5313 }
5314 }
5315
5316exit:
5317 return ret;
5318}
5319
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08005320#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005321static int drv_cmd_set_ccx_roam_scan_channels(hdd_adapter_t *adapter,
5322 hdd_context_t *hdd_ctx,
5323 uint8_t *command,
5324 uint8_t command_len,
5325 hdd_priv_data_t *priv_data)
5326{
5327 int ret = 0;
5328 uint8_t *value = command;
5329 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
5330 uint8_t numChannels = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305331 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005332
5333 ret = hdd_parse_channellist(value, ChannelList, &numChannels);
5334 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005335 hdd_err("Failed to parse channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005336 goto exit;
5337 }
5338 if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005339 hdd_err("number of channels (%d) supported exceeded max (%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005340 numChannels,
5341 WNI_CFG_VALID_CHANNEL_LIST_LEN);
5342 ret = -EINVAL;
5343 goto exit;
5344 }
5345 status = sme_set_ese_roam_scan_channel_list(hdd_ctx->hHal,
5346 adapter->sessionId,
5347 ChannelList,
5348 numChannels);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305349 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005350 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005351 ret = -EINVAL;
5352 goto exit;
5353 }
5354
5355exit:
5356 return ret;
5357}
5358
5359static int drv_cmd_get_tsm_stats(hdd_adapter_t *adapter,
5360 hdd_context_t *hdd_ctx,
5361 uint8_t *command,
5362 uint8_t command_len,
5363 hdd_priv_data_t *priv_data)
5364{
5365 int ret = 0;
5366 uint8_t *value = command;
5367 char extra[128] = { 0 };
5368 int len = 0;
5369 uint8_t tid = 0;
5370 hdd_station_ctx_t *pHddStaCtx;
5371 tAniTrafStrmMetrics tsm_metrics;
5372
Krunal Sonibe766b02016-03-10 13:00:44 -08005373 if ((QDF_STA_MODE != adapter->device_mode) &&
5374 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005375 hdd_warn("Unsupported in mode %s(%d)",
5376 hdd_device_mode_to_string(adapter->device_mode),
5377 adapter->device_mode);
5378 return -EINVAL;
5379 }
5380
5381 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5382
5383 /* if not associated, return error */
5384 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005385 hdd_err("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005386 ret = -EINVAL;
5387 goto exit;
5388 }
5389
5390 /* Move pointer to ahead of GETTSMSTATS<delimiter> */
5391 value = value + command_len + 1;
5392
5393 /* Convert the value from ascii to integer */
5394 ret = kstrtou8(value, 10, &tid);
5395 if (ret < 0) {
5396 /*
5397 * If the input value is greater than max value of datatype,
5398 * then also kstrtou8 fails
5399 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005400 hdd_err("kstrtou8 failed range [%d - %d]",
5401 TID_MIN_VALUE,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005402 TID_MAX_VALUE);
5403 ret = -EINVAL;
5404 goto exit;
5405 }
5406 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005407 hdd_err("tid value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005408 tid, TID_MIN_VALUE, TID_MAX_VALUE);
5409 ret = -EINVAL;
5410 goto exit;
5411 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005412 hdd_info("Received Command to get tsm stats tid = %d",
5413 tid);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305414 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005415 hdd_get_tsm_stats(adapter, tid, &tsm_metrics)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005416 hdd_err("failed to get tsm stats");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005417 ret = -EFAULT;
5418 goto exit;
5419 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005420 hdd_info(
5421 "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005422 tsm_metrics.UplinkPktQueueDly,
5423 tsm_metrics.UplinkPktQueueDlyHist[0],
5424 tsm_metrics.UplinkPktQueueDlyHist[1],
5425 tsm_metrics.UplinkPktQueueDlyHist[2],
5426 tsm_metrics.UplinkPktQueueDlyHist[3],
5427 tsm_metrics.UplinkPktTxDly,
5428 tsm_metrics.UplinkPktLoss,
5429 tsm_metrics.UplinkPktCount,
5430 tsm_metrics.RoamingCount,
5431 tsm_metrics.RoamingDly);
5432 /*
5433 * Output TSM stats is of the format
5434 * GETTSMSTATS [PktQueueDly]
5435 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
5436 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800
5437 */
5438 len = scnprintf(extra,
5439 sizeof(extra),
5440 "%s %d %d:%d:%d:%d %u %d %d %d %d",
5441 command,
5442 tsm_metrics.UplinkPktQueueDly,
5443 tsm_metrics.UplinkPktQueueDlyHist[0],
5444 tsm_metrics.UplinkPktQueueDlyHist[1],
5445 tsm_metrics.UplinkPktQueueDlyHist[2],
5446 tsm_metrics.UplinkPktQueueDlyHist[3],
5447 tsm_metrics.UplinkPktTxDly,
5448 tsm_metrics.UplinkPktLoss,
5449 tsm_metrics.UplinkPktCount,
5450 tsm_metrics.RoamingCount,
5451 tsm_metrics.RoamingDly);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305452 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005453 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005454 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005455 ret = -EFAULT;
5456 goto exit;
5457 }
5458
5459exit:
5460 return ret;
5461}
5462
5463static int drv_cmd_set_cckm_ie(hdd_adapter_t *adapter,
5464 hdd_context_t *hdd_ctx,
5465 uint8_t *command,
5466 uint8_t command_len,
5467 hdd_priv_data_t *priv_data)
5468{
5469 int ret;
5470 uint8_t *value = command;
5471 uint8_t *cckmIe = NULL;
5472 uint8_t cckmIeLen = 0;
5473
5474 ret = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
5475 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005476 hdd_err("Failed to parse cckm ie data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005477 goto exit;
5478 }
5479
5480 if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005481 hdd_err("CCKM Ie input length is more than max[%d]",
5482 DOT11F_IE_RSN_MAX_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005483 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05305484 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005485 cckmIe = NULL;
5486 }
5487 ret = -EINVAL;
5488 goto exit;
5489 }
5490
5491 sme_set_cckm_ie(hdd_ctx->hHal, adapter->sessionId,
5492 cckmIe, cckmIeLen);
5493 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05305494 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005495 cckmIe = NULL;
5496 }
5497
5498exit:
5499 return ret;
5500}
5501
5502static int drv_cmd_ccx_beacon_req(hdd_adapter_t *adapter,
5503 hdd_context_t *hdd_ctx,
5504 uint8_t *command,
5505 uint8_t command_len,
5506 hdd_priv_data_t *priv_data)
5507{
5508 int ret;
5509 uint8_t *value = command;
5510 tCsrEseBeaconReq eseBcnReq;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305511 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005512
Krunal Sonibe766b02016-03-10 13:00:44 -08005513 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005514 hdd_warn("Unsupported in mode %s(%d)",
5515 hdd_device_mode_to_string(adapter->device_mode),
5516 adapter->device_mode);
5517 return -EINVAL;
5518 }
5519
5520 ret = hdd_parse_ese_beacon_req(value, &eseBcnReq);
5521 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005522 hdd_err("Failed to parse ese beacon req");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005523 goto exit;
5524 }
5525
5526 if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005527 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005528 hdd_indicate_ese_bcn_report_no_results(adapter,
5529 eseBcnReq.bcnReq[0].measurementToken,
5530 0x02, /* BIT(1) set for measurement done */
5531 0); /* no BSS */
5532 goto exit;
5533 }
5534
5535 status = sme_set_ese_beacon_request(hdd_ctx->hHal,
5536 adapter->sessionId,
5537 &eseBcnReq);
5538
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305539 if (QDF_STATUS_E_RESOURCES == status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005540 hdd_info("sme_set_ese_beacon_request failed (%d), a request already in progress",
5541 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005542 ret = -EBUSY;
5543 goto exit;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305544 } else if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005545 hdd_err("sme_set_ese_beacon_request failed (%d)",
5546 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005547 ret = -EINVAL;
5548 goto exit;
5549 }
5550
5551exit:
5552 return ret;
5553}
Srinivas Girigowdad79b3452016-07-19 18:39:46 -07005554
5555/**
5556 * drv_cmd_ccx_plm_req() - Set ESE PLM request
5557 * @adapter: Pointer to the HDD adapter
5558 * @hdd_ctx: Pointer to the HDD context
5559 * @command: Driver command string
5560 * @command_len: Driver command string length
5561 * @priv_data: Private data coming with the driver command. Unused here
5562 *
5563 * This function handles driver command that sets the ESE PLM request
5564 *
5565 * Return: 0 on success; negative errno otherwise
5566 */
5567static int drv_cmd_ccx_plm_req(hdd_adapter_t *adapter,
5568 hdd_context_t *hdd_ctx,
5569 uint8_t *command,
5570 uint8_t command_len,
5571 hdd_priv_data_t *priv_data)
5572{
5573 int ret = 0;
5574 uint8_t *value = command;
5575 QDF_STATUS status = QDF_STATUS_SUCCESS;
5576 tpSirPlmReq pPlmRequest = NULL;
5577
5578 pPlmRequest = qdf_mem_malloc(sizeof(tSirPlmReq));
5579 if (NULL == pPlmRequest) {
5580 ret = -ENOMEM;
5581 goto exit;
5582 }
5583
5584 status = hdd_parse_plm_cmd(value, pPlmRequest);
5585 if (QDF_STATUS_SUCCESS != status) {
5586 qdf_mem_free(pPlmRequest);
5587 pPlmRequest = NULL;
5588 ret = -EINVAL;
5589 goto exit;
5590 }
5591 pPlmRequest->sessionId = adapter->sessionId;
5592
5593 status = sme_set_plm_request(hdd_ctx->hHal, pPlmRequest);
5594 if (QDF_STATUS_SUCCESS != status) {
5595 qdf_mem_free(pPlmRequest);
5596 pPlmRequest = NULL;
5597 ret = -EINVAL;
5598 goto exit;
5599 }
5600
5601exit:
5602 return ret;
5603}
5604
5605/**
5606 * drv_cmd_set_ccx_mode() - Set ESE mode
5607 * @adapter: Pointer to the HDD adapter
5608 * @hdd_ctx: Pointer to the HDD context
5609 * @command: Driver command string
5610 * @command_len: Driver command string length
5611 * @priv_data: Private data coming with the driver command. Unused here
5612 *
5613 * This function handles driver command that sets ESE mode
5614 *
5615 * Return: 0 on success; negative errno otherwise
5616 */
5617static int drv_cmd_set_ccx_mode(hdd_adapter_t *adapter,
5618 hdd_context_t *hdd_ctx,
5619 uint8_t *command,
5620 uint8_t command_len,
5621 hdd_priv_data_t *priv_data)
5622{
5623 int ret = 0;
5624 uint8_t *value = command;
5625 uint8_t eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT;
5626
5627 /*
5628 * Check if the features OKC/ESE/11R are supported simultaneously,
5629 * then this operation is not permitted (return FAILURE)
5630 */
5631 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
5632 hdd_is_okc_mode_enabled(hdd_ctx) &&
5633 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
5634 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
5635 ret = -EPERM;
5636 goto exit;
5637 }
5638
5639 /* Move pointer to ahead of SETCCXMODE<delimiter> */
5640 value = value + command_len + 1;
5641
5642 /* Convert the value from ascii to integer */
5643 ret = kstrtou8(value, 10, &eseMode);
5644 if (ret < 0) {
5645 /*
5646 * If the input value is greater than max value of datatype,
5647 * then also kstrtou8 fails
5648 */
5649 hdd_err("kstrtou8 failed range [%d - %d]",
5650 CFG_ESE_FEATURE_ENABLED_MIN,
5651 CFG_ESE_FEATURE_ENABLED_MAX);
5652 ret = -EINVAL;
5653 goto exit;
5654 }
5655
5656 if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) ||
5657 (eseMode > CFG_ESE_FEATURE_ENABLED_MAX)) {
5658 hdd_err("Ese mode value %d is out of range (Min: %d Max: %d)",
5659 eseMode,
5660 CFG_ESE_FEATURE_ENABLED_MIN,
5661 CFG_ESE_FEATURE_ENABLED_MAX);
5662 ret = -EINVAL;
5663 goto exit;
5664 }
5665 hdd_info("Received Command to change ese mode = %d", eseMode);
5666
5667 hdd_ctx->config->isEseIniFeatureEnabled = eseMode;
5668 sme_update_is_ese_feature_enabled(hdd_ctx->hHal,
5669 adapter->sessionId,
5670 eseMode);
5671
5672exit:
5673 return ret;
5674}
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08005675#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005676
5677static int drv_cmd_set_mc_rate(hdd_adapter_t *adapter,
5678 hdd_context_t *hdd_ctx,
5679 uint8_t *command,
5680 uint8_t command_len,
5681 hdd_priv_data_t *priv_data)
5682{
5683 int ret = 0;
5684 uint8_t *value = command;
5685 int targetRate;
5686
5687 /* input value is in units of hundred kbps */
5688
5689 /* Move pointer to ahead of SETMCRATE<delimiter> */
5690 value = value + command_len + 1;
5691
5692 /* Convert the value from ascii to integer, decimal base */
5693 ret = kstrtouint(value, 10, &targetRate);
5694
5695 ret = wlan_hdd_set_mc_rate(adapter, targetRate);
5696 return ret;
5697}
5698
5699static int drv_cmd_max_tx_power(hdd_adapter_t *adapter,
5700 hdd_context_t *hdd_ctx,
5701 uint8_t *command,
5702 uint8_t command_len,
5703 hdd_priv_data_t *priv_data)
5704{
5705 int ret = 0;
5706 int status;
5707 int txPower;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305708 QDF_STATUS qdf_status;
5709 QDF_STATUS smeStatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005710 uint8_t *value = command;
Anurag Chouhan6d760662016-02-20 16:05:43 +05305711 struct qdf_mac_addr bssid = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
5712 struct qdf_mac_addr selfMac = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005713 hdd_adapter_list_node_t *pAdapterNode = NULL;
5714 hdd_adapter_list_node_t *pNext = NULL;
5715
5716 status = hdd_parse_setmaxtxpower_command(value, &txPower);
5717 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005718 hdd_err("Invalid MAXTXPOWER command ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005719 ret = -EINVAL;
5720 goto exit;
5721 }
5722
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305723 qdf_status = hdd_get_front_adapter(hdd_ctx, &pAdapterNode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005724 while (NULL != pAdapterNode
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305725 && QDF_STATUS_SUCCESS == qdf_status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005726 adapter = pAdapterNode->pAdapter;
5727 /* Assign correct self MAC address */
Anurag Chouhanc5548422016-02-24 18:33:27 +05305728 qdf_copy_macaddr(&bssid,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005729 &adapter->macAddressCurrent);
Anurag Chouhanc5548422016-02-24 18:33:27 +05305730 qdf_copy_macaddr(&selfMac,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005731 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005732
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005733 hdd_info("Device mode %d max tx power %d selfMac: "
5734 MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005735 adapter->device_mode, txPower,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005736 MAC_ADDR_ARRAY(selfMac.bytes),
5737 MAC_ADDR_ARRAY(bssid.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005738
Srinivas Girigowda97215232015-09-24 12:26:28 -07005739 smeStatus = sme_set_max_tx_power(hdd_ctx->hHal,
5740 bssid, selfMac, txPower);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305741 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005742 hdd_err("Set max tx power failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005743 ret = -EINVAL;
5744 goto exit;
5745 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005746 hdd_info("Set max tx power success");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305747 qdf_status = hdd_get_next_adapter(hdd_ctx, pAdapterNode,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005748 &pNext);
5749 pAdapterNode = pNext;
5750 }
5751
5752exit:
5753 return ret;
5754}
5755
5756static int drv_cmd_set_dfs_scan_mode(hdd_adapter_t *adapter,
5757 hdd_context_t *hdd_ctx,
5758 uint8_t *command,
5759 uint8_t command_len,
5760 hdd_priv_data_t *priv_data)
5761{
5762 int ret = 0;
5763 uint8_t *value = command;
5764 uint8_t dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
5765
5766 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
5767 value = value + command_len + 1;
5768
5769 /* Convert the value from ascii to integer */
5770 ret = kstrtou8(value, 10, &dfsScanMode);
5771 if (ret < 0) {
5772 /*
5773 * If the input value is greater than max value of datatype,
5774 * then also kstrtou8 fails
5775 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005776 hdd_err("kstrtou8 failed range [%d - %d]",
5777 CFG_ROAMING_DFS_CHANNEL_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005778 CFG_ROAMING_DFS_CHANNEL_MAX);
5779 ret = -EINVAL;
5780 goto exit;
5781 }
5782
5783 if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
5784 (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005785 hdd_err("dfsScanMode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005786 dfsScanMode,
5787 CFG_ROAMING_DFS_CHANNEL_MIN,
5788 CFG_ROAMING_DFS_CHANNEL_MAX);
5789 ret = -EINVAL;
5790 goto exit;
5791 }
5792
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005793 hdd_info("Received Command to Set DFS Scan Mode = %d",
5794 dfsScanMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005795
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08005796 /* When DFS scanning is disabled, the DFS channels need to be
5797 * removed from the operation of device.
5798 */
5799 ret = wlan_hdd_disable_dfs_chan_scan(hdd_ctx, adapter,
5800 (dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED));
5801 if (ret < 0) {
5802 /* Some conditions prevented it from disabling DFS channels */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005803 hdd_err("disable/enable DFS channel request was denied");
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08005804 goto exit;
5805 }
5806
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005807 hdd_ctx->config->allowDFSChannelRoam = dfsScanMode;
5808 sme_update_dfs_scan_mode(hdd_ctx->hHal, adapter->sessionId,
5809 dfsScanMode);
5810
5811exit:
5812 return ret;
5813}
5814
5815static int drv_cmd_get_dfs_scan_mode(hdd_adapter_t *adapter,
5816 hdd_context_t *hdd_ctx,
5817 uint8_t *command,
5818 uint8_t command_len,
5819 hdd_priv_data_t *priv_data)
5820{
5821 int ret = 0;
5822 uint8_t dfsScanMode = sme_get_dfs_scan_mode(hdd_ctx->hHal);
5823 char extra[32];
5824 uint8_t len = 0;
5825
5826 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305827 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005828 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005829 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005830 ret = -EFAULT;
5831 }
5832
5833 return ret;
5834}
5835
5836static int drv_cmd_get_link_status(hdd_adapter_t *adapter,
5837 hdd_context_t *hdd_ctx,
5838 uint8_t *command,
5839 uint8_t command_len,
5840 hdd_priv_data_t *priv_data)
5841{
5842 int ret = 0;
5843 int value = wlan_hdd_get_link_status(adapter);
5844 char extra[32];
5845 uint8_t len;
5846
5847 len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305848 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005849 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005850 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005851 ret = -EFAULT;
5852 }
5853
5854 return ret;
5855}
5856
5857#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
5858static int drv_cmd_enable_ext_wow(hdd_adapter_t *adapter,
5859 hdd_context_t *hdd_ctx,
5860 uint8_t *command,
5861 uint8_t command_len,
5862 hdd_priv_data_t *priv_data)
5863{
5864 uint8_t *value = command;
5865 int set_value;
5866
5867 /* Move pointer to ahead of ENABLEEXTWOW */
5868 value = value + command_len;
5869
Anurag Chouhan43e0c752016-09-03 16:17:02 +05305870 if (!(sscanf(value, "%d", &set_value))) {
5871 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
5872 ("No input identified"));
5873 return -EINVAL;
5874 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005875
5876 return hdd_enable_ext_wow_parser(adapter,
5877 adapter->sessionId,
5878 set_value);
5879}
5880
5881static int drv_cmd_set_app1_params(hdd_adapter_t *adapter,
5882 hdd_context_t *hdd_ctx,
5883 uint8_t *command,
5884 uint8_t command_len,
5885 hdd_priv_data_t *priv_data)
5886{
5887 int ret;
5888 uint8_t *value = command;
5889
5890 /* Move pointer to ahead of SETAPP1PARAMS */
5891 value = value + command_len;
5892
5893 ret = hdd_set_app_type1_parser(adapter,
5894 value, strlen(value));
5895 if (ret >= 0)
5896 hdd_ctx->is_extwow_app_type1_param_set = true;
5897
5898 return ret;
5899}
5900
5901static int drv_cmd_set_app2_params(hdd_adapter_t *adapter,
5902 hdd_context_t *hdd_ctx,
5903 uint8_t *command,
5904 uint8_t command_len,
5905 hdd_priv_data_t *priv_data)
5906{
5907 int ret;
5908 uint8_t *value = command;
5909
5910 /* Move pointer to ahead of SETAPP2PARAMS */
5911 value = value + command_len;
5912
5913 ret = hdd_set_app_type2_parser(adapter, value, strlen(value));
5914 if (ret >= 0)
5915 hdd_ctx->is_extwow_app_type2_param_set = true;
5916
5917 return ret;
5918}
5919#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
5920
5921#ifdef FEATURE_WLAN_TDLS
5922/**
5923 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset
5924 * @adapter: Pointer to the HDD adapter
5925 * @hdd_ctx: Pointer to the HDD context
5926 * @command: Driver command string
5927 * @command_len: Driver command string length
5928 * @priv_data: Private data coming with the driver command. Unused here
5929 *
5930 * This function handles driver command that sets the secondary tdls off channel
5931 * offset
5932 *
5933 * Return: 0 on success; negative errno otherwise
5934 */
5935static int drv_cmd_tdls_secondary_channel_offset(hdd_adapter_t *adapter,
5936 hdd_context_t *hdd_ctx,
5937 uint8_t *command,
5938 uint8_t command_len,
5939 hdd_priv_data_t *priv_data)
5940{
5941 int ret;
5942 uint8_t *value = command;
5943 int set_value;
5944
5945 /* Move pointer to point the string */
5946 value += command_len;
5947
5948 ret = sscanf(value, "%d", &set_value);
5949 if (ret != 1)
5950 return -EINVAL;
5951
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005952 hdd_info("Tdls offchannel offset:%d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005953
5954 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, set_value);
5955
5956 return ret;
5957}
5958
5959/**
5960 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode
5961 * @adapter: Pointer to the HDD adapter
5962 * @hdd_ctx: Pointer to the HDD context
5963 * @command: Driver command string
5964 * @command_len: Driver command string length
5965 * @priv_data: Private data coming with the driver command. Unused here
5966 *
5967 * This function handles driver command that sets tdls off channel mode
5968 *
5969 * Return: 0 on success; negative errno otherwise
5970 */
5971static int drv_cmd_tdls_off_channel_mode(hdd_adapter_t *adapter,
5972 hdd_context_t *hdd_ctx,
5973 uint8_t *command,
5974 uint8_t command_len,
5975 hdd_priv_data_t *priv_data)
5976{
5977 int ret;
5978 uint8_t *value = command;
5979 int set_value;
5980
5981 /* Move pointer to point the string */
5982 value += command_len;
5983
5984 ret = sscanf(value, "%d", &set_value);
5985 if (ret != 1)
5986 return -EINVAL;
5987
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005988 hdd_info("Tdls offchannel mode:%d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005989
5990 ret = hdd_set_tdls_offchannelmode(adapter, set_value);
5991
5992 return ret;
5993}
5994
5995/**
5996 * drv_cmd_tdls_off_channel() - set tdls off channel number
5997 * @adapter: Pointer to the HDD adapter
5998 * @hdd_ctx: Pointer to the HDD context
5999 * @command: Driver command string
6000 * @command_len: Driver command string length
6001 * @priv_data: Private data coming with the driver command. Unused here
6002 *
6003 * This function handles driver command that sets tdls off channel number
6004 *
6005 * Return: 0 on success; negative errno otherwise
6006 */
6007static int drv_cmd_tdls_off_channel(hdd_adapter_t *adapter,
6008 hdd_context_t *hdd_ctx,
6009 uint8_t *command,
6010 uint8_t command_len,
6011 hdd_priv_data_t *priv_data)
6012{
6013 int ret;
6014 uint8_t *value = command;
6015 int set_value;
6016
6017 /* Move pointer to point the string */
6018 value += command_len;
6019
6020 ret = sscanf(value, "%d", &set_value);
6021 if (ret != 1)
6022 return -EINVAL;
6023
Krishna Kumaar Natarajan4d090352015-10-26 18:30:53 -07006024 if (CDS_IS_DFS_CH(set_value)) {
6025 hdd_err("DFS channel %d is passed for hdd_set_tdls_offchannel",
6026 set_value);
6027 return -EINVAL;
6028 }
6029
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006030 hdd_info("Tdls offchannel num: %d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006031
6032 ret = hdd_set_tdls_offchannel(hdd_ctx, set_value);
6033
6034 return ret;
6035}
6036
6037/**
6038 * drv_cmd_tdls_scan() - set tdls scan type
6039 * @adapter: Pointer to the HDD adapter
6040 * @hdd_ctx: Pointer to the HDD context
6041 * @command: Driver command string
6042 * @command_len: Driver command string length
6043 * @priv_data: Private data coming with the driver command. Unused here
6044 *
6045 * This function handles driver command that sets tdls scan type
6046 *
6047 * Return: 0 on success; negative errno otherwise
6048 */
6049static int drv_cmd_tdls_scan(hdd_adapter_t *adapter,
6050 hdd_context_t *hdd_ctx,
6051 uint8_t *command,
6052 uint8_t command_len,
6053 hdd_priv_data_t *priv_data)
6054{
6055 int ret;
6056 uint8_t *value = command;
6057 int set_value;
6058
6059 /* Move pointer to point the string */
6060 value += command_len;
6061
6062 ret = sscanf(value, "%d", &set_value);
6063 if (ret != 1)
6064 return -EINVAL;
6065
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006066 hdd_info("Tdls scan type val: %d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006067
6068 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value);
6069
6070 return ret;
6071}
6072#endif
6073
6074static int drv_cmd_get_rssi(hdd_adapter_t *adapter,
6075 hdd_context_t *hdd_ctx,
6076 uint8_t *command,
6077 uint8_t command_len,
6078 hdd_priv_data_t *priv_data)
6079{
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006080 int ret = 0;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006081 int8_t rssi = 0;
6082 char extra[32];
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006083
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006084 uint8_t len = 0;
6085
6086 wlan_hdd_get_rssi(adapter, &rssi);
6087
6088 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306089 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006090
6091 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006092 hdd_err("Failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006093 ret = -EFAULT;
6094 }
6095
6096 return ret;
6097}
6098
6099static int drv_cmd_get_linkspeed(hdd_adapter_t *adapter,
6100 hdd_context_t *hdd_ctx,
6101 uint8_t *command,
6102 uint8_t command_len,
6103 hdd_priv_data_t *priv_data)
6104{
6105 int ret;
6106 uint32_t link_speed = 0;
6107 char extra[32];
6108 uint8_t len = 0;
6109
6110 ret = wlan_hdd_get_link_speed(adapter, &link_speed);
6111 if (0 != ret)
6112 return ret;
6113
6114 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
Anurag Chouhan6d760662016-02-20 16:05:43 +05306115 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006116 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006117 hdd_err("Failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006118 ret = -EFAULT;
6119 }
6120
6121 return ret;
6122}
6123
6124#ifdef FEATURE_NAPI
6125/**
6126 * hdd_parse_napi() - helper functions to drv_cmd_napi
6127 * @str : source string to parse
6128 * @cmd : pointer to cmd part after parsing
6129 * @sub : pointer to subcmd part after parsing
6130 * @aux : pointer to optional aux part after parsing
6131 *
6132 * Example:
6133 * NAPI SCALE <n> +-- IN str
6134 * | | +------ OUT aux
6135 * | +------------ OUT subcmd
6136 * +----------------- OUT cmd
6137 *
6138 * Return: ==0: success; !=0: failure
6139 */
6140static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
6141{
6142 int rc;
6143 char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
6144
6145 NAPI_DEBUG("-->\n");
6146
6147 token = strsep(str, " \t");
6148 if (NULL == token) {
6149 hdd_err("cannot parse cmd");
6150 goto parse_end;
6151 }
6152 lcmd = token;
6153
6154 token = strsep(str, " \t");
6155 if (NULL == token) {
6156 hdd_err("cannot parse subcmd");
6157 goto parse_end;
6158 }
6159 lsub = token;
6160
6161 token = strsep(str, " \t");
6162 if (NULL == token)
6163 hdd_warn("cannot parse aux\n");
6164 else
6165 laux = token;
6166
6167parse_end:
6168 if ((NULL == lcmd) || (NULL == lsub))
6169 rc = -EINVAL;
6170 else {
6171 rc = 0;
6172 *cmd = lcmd;
6173 *sub = lsub;
6174 if (NULL != aux)
6175 *aux = laux;
6176 }
6177 NAPI_DEBUG("<--[rc=%d]\n", rc);
6178 return rc;
6179}
6180
6181
6182/**
6183 * hdd_parse_stats() - print NAPI stats into a buffer
6184 * @buf : buffer to write stats into
6185 * @max : "size of buffer"
6186 * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
6187 * @napid: binary structure to retrieve the stats from
6188 *
6189 * Return: number of bytes written into the buffer
6190 */
6191int hdd_napi_stats(char *buf,
6192 int max,
6193 char *indp,
6194 struct qca_napi_data *napid)
6195{
6196 int n = 0;
6197 int i, j, k; /* NAPI, CPU, bucket indices */
6198 int from, to;
6199 struct qca_napi_info *napii;
6200 struct qca_napi_stat *napis;
6201
6202 NAPI_DEBUG("-->\n");
6203
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006204 if (NULL == napid)
6205 return n;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006206 if (NULL == indp) {
6207 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006208 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006209 } else {
6210 if (0 > kstrtoint(indp, 10, &to)) {
6211 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006212 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006213 } else
6214 from = to;
6215 }
6216
6217 for (i = from; i < to; i++)
6218 if (napid->ce_map & (0x01 << i)) {
6219 napii = &(napid->napis[i]);
6220 for (j = 0; j < NR_CPUS; j++) {
6221 napis = &(napii->stats[j]);
6222 n += scnprintf(buf + n, max - n,
6223 "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
6224 i, j,
6225 napis->napi_schedules,
6226 napis->napi_polls,
6227 napis->napi_completes,
6228 napis->napi_workdone);
6229
6230 for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
6231 n += scnprintf(
6232 buf + n, max - n,
6233 " %d",
6234 napis->napi_budget_uses[k]);
6235 }
6236 n += scnprintf(buf+n, max - n, "\n");
6237 }
6238 }
6239
6240 NAPI_DEBUG("<--[n=%d]\n", n);
6241 return n;
6242}
6243
6244/**
6245 * napi_set_scale() - sets the scale attribute in all NAPI entries
6246 * @sc : scale to set
6247 *
6248 * Return: void
6249 */
6250static void napi_set_scale(uint8_t sc)
6251{
6252 uint32_t i;
6253 struct qca_napi_data *napi_data;
6254
6255 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006256 if (likely(NULL != napi_data))
6257 for (i = 0; i < CE_COUNT_MAX; i++)
6258 if (napi_data->ce_map & (0x01 << i))
6259 napi_data->napis[i].scale = sc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006260
6261 return;
6262}
6263/**
6264 * drv_cmd_napi() - processes NAPI commands
6265 * @adapter : net_device
6266 * @hdd_ctx : HDD context
6267 * @command : command string from user command (including "NAPI")
6268 * @command_len: length of command
6269 * @priv_data : ifr_data
6270 *
6271 * Commands supported:
6272 * NAPI ENABLE : enables NAPI administratively. Note that this may not
6273 * enable NAPI functionally, as some other conditions
6274 * may not have been satisfied yet
6275 * NAPI DISABLE : reverse operation of "enable"
6276 * NAPI STATUS : get global status of NAPI instances
6277 * NAPI STATS [<n>] : get the stats for a given NAPI instance
6278 * NAPI SCALE <n> : set the scale factor
6279 *
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006280 * Return: 0: success; !0: failure
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006281 */
6282static int drv_cmd_napi(hdd_adapter_t *adapter,
6283 hdd_context_t *hdd_ctx,
6284 uint8_t *command,
6285 uint8_t command_len,
6286 hdd_priv_data_t *priv_data)
6287{
6288 int rc = 0;
6289 int n, l;
6290 char *cmd = NULL, *subcmd = NULL, *aux = NULL;
6291 char *synopsis = "NAPI ENABLE\n"
6292 "NAPI DISABLE\n"
6293 "NAPI STATUS\n"
6294 "NAPI STATS [<n>] -- if no <n> then all\n"
6295 "NAPI SCALE <n> -- set the scale\n";
6296 char *reply = NULL;
6297
6298 /* make a local copy, as strsep modifies the str in place */
6299 char *str = NULL;
6300
6301 NAPI_DEBUG("-->\n");
6302
6303 /**
6304 * NOTE TO MAINTAINER: from this point to the end of the function,
6305 * please do not return anywhere in the code except the very end
6306 * to avoid memory leakage (goto end_drv_napi instead)
6307 * or make sure that reply+str is freed
6308 */
6309 reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
6310 if (NULL == reply) {
6311 hdd_err("could not allocate reply buffer");
6312 rc = -ENOMEM;
6313 goto end_drv_napi;
6314 }
6315
6316 str = kmalloc(strlen(command) + 1, GFP_KERNEL);
6317 if (NULL == str) {
6318 hdd_err("could not allocate copy of input buffer");
6319 rc = -ENOMEM;
6320 goto end_drv_napi;
6321 }
6322
6323 strlcpy(str, command, strlen(command) + 1);
6324 hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
6325 cmd, subcmd, aux);
6326
6327
6328 rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
6329
6330 if (0 != rc) {
6331 const char *msg = "unknown or badly formatted cmd\n%s";
Anurag Chouhan6d760662016-02-20 16:05:43 +05306332 l = QDF_MIN(MAX_USER_COMMAND_SIZE,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006333 strlen(msg)+strlen(synopsis));
6334 n = scnprintf(reply, l, msg, synopsis);
6335
6336 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306337 QDF_MIN(priv_data->total_len, l)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006338 hdd_err("failed to copy data to user buffer");
6339 hdd_debug("reply: %s", reply);
6340
6341 rc = -EINVAL;
6342 } else {
6343 hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
6344 cmd, subcmd, aux);
6345 if (!strcmp(subcmd, "ENABLE"))
6346 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
6347 else if (!strcmp(subcmd, "DISABLE"))
6348 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
6349 else if (!strcmp(subcmd, "STATUS")) {
6350 int n = 0;
6351 uint32_t i;
6352 struct qca_napi_data *napi_data;
6353
6354 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006355 if (unlikely(NULL == napi_data))
6356 goto status_end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006357 n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
6358 "NAPI state: 0x%08x map: 0x%08x\n",
6359 napi_data->state,
6360 napi_data->ce_map);
6361
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006362 for (i = 0; i < CE_COUNT_MAX; i++)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006363 if (napi_data->ce_map & (0x01 << i)) {
6364 n += scnprintf(
6365 reply + n,
6366 MAX_USER_COMMAND_SIZE - n,
6367 "#%d: id: %d, scale=%d\n",
6368 i,
6369 napi_data->napis[i].id,
6370 napi_data->napis[i].scale);
6371 }
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006372 status_end:
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006373 hdd_info("wlan: STATUS DATA:\n%s", reply);
6374 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306375 QDF_MIN(n, priv_data->total_len)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006376 rc = -EINVAL;
6377 } else if (!strcmp(subcmd, "STATS")) {
6378 int n = 0;
6379 struct qca_napi_data *napi_data;
6380
6381 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006382 if (NULL != napi_data) {
6383 n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
6384 aux, napi_data);
6385 NAPI_DEBUG("STATS: returns %d\n", n);
6386 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006387 if (n > 0) {
6388 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306389 QDF_MIN(priv_data->total_len,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006390 n)))
6391 rc = -EINVAL;
6392 hdd_info("wlan: STATS_DATA\n%s\n", reply);
6393 } else
6394 rc = -EINVAL;
6395 } else if (!strcmp(subcmd, "SCALE")) {
6396 if (NULL == aux) {
6397 rc = -EINVAL;
6398 hdd_err("wlan: SCALE cmd requires <n>");
6399 } else {
6400 uint8_t sc;
6401 rc = kstrtou8(aux, 10, &sc);
6402 if (rc) {
6403 hdd_err("wlan: bad scale (%s)", aux);
6404 rc = -EINVAL;
6405 } else
6406 napi_set_scale(sc);
6407 }
6408 } /* SCALE */
6409 }
6410end_drv_napi:
6411 if (NULL != str)
6412 kfree(str);
6413 if (NULL != reply)
6414 kfree(reply);
6415
6416 NAPI_DEBUG("<--[rc=%d]\n", rc);
6417 return rc;
6418}
6419#endif /* FEATURE_NAPI */
6420
6421/**
6422 * hdd_set_rx_filter() - set RX filter
6423 * @adapter: Pointer to adapter
6424 * @action: Filter action
6425 * @pattern: Address pattern
6426 *
6427 * Address pattern is most significant byte of address for example
6428 * 0x01 for IPV4 multicast address
6429 * 0x33 for IPV6 multicast address
6430 * 0xFF for broadcast address
6431 *
6432 * Return: 0 for success, non-zero for failure
6433 */
6434static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
6435 uint8_t pattern)
6436{
6437 int ret;
6438 uint8_t i;
6439 tHalHandle handle;
6440 tSirRcvFltMcAddrList *filter;
6441 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
6442
6443 ret = wlan_hdd_validate_context(hdd_ctx);
6444 if (0 != ret)
6445 return ret;
6446
6447 handle = hdd_ctx->hHal;
6448
6449 if (NULL == handle) {
6450 hdd_err("HAL Handle is NULL");
6451 return -EINVAL;
6452 }
6453
6454 /*
6455 * If action is false it means start dropping packets
6456 * Set addr_filter_pattern which will be used when sending
6457 * MC/BC address list to target
6458 */
6459 if (!action)
6460 adapter->addr_filter_pattern = pattern;
6461 else
6462 adapter->addr_filter_pattern = 0;
6463
Krunal Sonibe766b02016-03-10 13:00:44 -08006464 if (((adapter->device_mode == QDF_STA_MODE) ||
6465 (adapter->device_mode == QDF_P2P_CLIENT_MODE)) &&
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006466 adapter->mc_addr_list.mc_cnt &&
6467 hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
6468
6469
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306470 filter = qdf_mem_malloc(sizeof(*filter));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006471 if (NULL == filter) {
6472 hdd_err("Could not allocate Memory");
6473 return -ENOMEM;
6474 }
6475 filter->action = action;
6476 for (i = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
6477 if (!memcmp(adapter->mc_addr_list.addr[i],
6478 &pattern, 1)) {
Srinivas Girigowda98530492015-11-20 17:39:24 -08006479 memcpy(filter->multicastAddr[i].bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006480 adapter->mc_addr_list.addr[i],
6481 sizeof(adapter->mc_addr_list.addr[i]));
6482 filter->ulMulticastAddrCnt++;
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006483 hdd_info("%s RX filter : addr ="
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006484 MAC_ADDRESS_STR,
6485 action ? "setting" : "clearing",
Srinivas Girigowda98530492015-11-20 17:39:24 -08006486 MAC_ADDR_ARRAY(filter->multicastAddr[i].bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006487 }
6488 }
6489 /* Set rx filter */
6490 sme_8023_multicast_list(handle, adapter->sessionId, filter);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306491 qdf_mem_free(filter);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006492 } else {
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006493 hdd_info("mode %d mc_cnt %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006494 adapter->device_mode, adapter->mc_addr_list.mc_cnt);
6495 }
6496
6497 return 0;
6498}
6499
6500/**
6501 * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
6502 * @command: Pointer to input string driver command
6503 * @adapter: Pointer to adapter
6504 * @action: Action to enable/disable filtering
6505 *
6506 * If action == false
6507 * Start filtering out data packets based on type
6508 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
6509 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
6510 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
6511 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
6512 *
6513 * if action == true
6514 * Stop filtering data packets based on type
6515 * RXFILTER-ADD 0 -> Stop filtering unicast data packets
6516 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets
6517 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
6518 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
6519 *
6520 * Current implementation only supports IPV4 address filtering by
6521 * selectively allowing IPV4 multicast data packest based on
6522 * address list received in .ndo_set_rx_mode
6523 *
6524 * Return: 0 for success, non-zero for failure
6525 */
6526static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
6527 hdd_adapter_t *adapter,
6528 bool action)
6529{
6530 int ret = 0;
6531 uint8_t *value;
6532 uint8_t type;
6533
6534 value = command;
6535 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
6536 if (!action)
6537 value = command + 16;
6538 else
6539 value = command + 13;
6540 ret = kstrtou8(value, 10, &type);
6541 if (ret < 0) {
6542 hdd_err("kstrtou8 failed invalid input value %d", type);
6543 return -EINVAL;
6544 }
6545
6546 switch (type) {
6547 case 2:
6548 /* Set rx filter for IPV4 multicast data packets */
6549 ret = hdd_set_rx_filter(adapter, action, 0x01);
6550 break;
6551 default:
6552 hdd_info("Unsupported RXFILTER type %d", type);
6553 break;
6554 }
6555
6556 return ret;
6557}
6558
6559/**
6560 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler
6561 * @adapter: Pointer to network adapter
6562 * @hdd_ctx: Pointer to hdd context
6563 * @command: Pointer to input command
6564 * @command_len: Command length
6565 * @priv_data: Pointer to private data in command
6566 */
6567static int drv_cmd_rx_filter_remove(hdd_adapter_t *adapter,
6568 hdd_context_t *hdd_ctx,
6569 uint8_t *command,
6570 uint8_t command_len,
6571 hdd_priv_data_t *priv_data)
6572{
6573 return hdd_driver_rxfilter_comand_handler(command, adapter, false);
6574}
6575
6576/**
6577 * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler
6578 * @adapter: Pointer to network adapter
6579 * @hdd_ctx: Pointer to hdd context
6580 * @command: Pointer to input command
6581 * @command_len: Command length
6582 * @priv_data: Pointer to private data in command
6583 */
6584static int drv_cmd_rx_filter_add(hdd_adapter_t *adapter,
6585 hdd_context_t *hdd_ctx,
6586 uint8_t *command,
6587 uint8_t command_len,
6588 hdd_priv_data_t *priv_data)
6589{
6590 return hdd_driver_rxfilter_comand_handler(command, adapter, true);
6591}
6592
Archana Ramachandran393f3792015-11-13 17:13:21 -08006593/**
6594 * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE
6595 * command
6596 * @value: Pointer to SETANTENNAMODE command
6597 * @mode: Pointer to antenna mode
6598 * @reason: Pointer to reason for set antenna mode
6599 *
6600 * This function parses the SETANTENNAMODE command passed in the format
6601 * SETANTENNAMODE<space>mode
6602 *
6603 * Return: 0 for success non-zero for failure
6604 */
6605static int hdd_parse_setantennamode_command(const uint8_t *value)
6606{
6607 const uint8_t *in_ptr = value;
6608 int tmp, v;
6609 char arg1[32];
6610
6611 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
6612
6613 /* no argument after the command */
6614 if (NULL == in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006615 hdd_err("No argument after the command");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006616 return -EINVAL;
6617 }
6618
6619 /* no space after the command */
6620 if (SPACE_ASCII_VALUE != *in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006621 hdd_err("No space after the command");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006622 return -EINVAL;
6623 }
6624
6625 /* remove empty spaces */
6626 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
6627 in_ptr++;
6628
6629 /* no argument followed by spaces */
6630 if ('\0' == *in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006631 hdd_err("No argument followed by spaces");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006632 return -EINVAL;
6633 }
6634
6635 /* get the argument i.e. antenna mode */
6636 v = sscanf(in_ptr, "%31s ", arg1);
6637 if (1 != v) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006638 hdd_err("argument retrieval from cmd string failed");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006639 return -EINVAL;
6640 }
6641
6642 v = kstrtos32(arg1, 10, &tmp);
6643 if (v < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006644 hdd_err("argument string to int conversion failed");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006645 return -EINVAL;
6646 }
6647
6648 return tmp;
6649}
6650
6651/**
6652 * hdd_is_supported_chain_mask_2x2() - Verify if supported chain
6653 * mask is 2x2 mode
6654 * @hdd_ctx: Pointer to hdd contex
6655 *
6656 * Return: true if supported chain mask 2x2 else false
6657 */
6658static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx)
6659{
6660 /*
6661 * Revisit and the update logic to determine the number
6662 * of TX/RX chains supported in the system when
6663 * antenna sharing per band chain mask support is
6664 * brought in
6665 */
6666 return (hdd_ctx->config->enable2x2 == 0x01) ? true : false;
6667}
6668
6669/**
6670 * hdd_is_supported_chain_mask_1x1() - Verify if the supported
6671 * chain mask is 1x1
6672 * @hdd_ctx: Pointer to hdd contex
6673 *
6674 * Return: true if supported chain mask 1x1 else false
6675 */
6676static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx)
6677{
6678 /*
6679 * Revisit and update the logic to determine the number
6680 * of TX/RX chains supported in the system when
6681 * antenna sharing per band chain mask support is
6682 * brought in
6683 */
6684 return (!hdd_ctx->config->enable2x2) ? true : false;
6685}
6686
6687/**
6688 * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command
6689 * handler
6690 * @adapter: Pointer to network adapter
6691 * @hdd_ctx: Pointer to hdd context
6692 * @command: Pointer to input command
6693 * @command_len: Command length
6694 * @priv_data: Pointer to private data in command
6695 */
6696static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter,
6697 hdd_context_t *hdd_ctx,
6698 uint8_t *command,
6699 uint8_t command_len,
6700 hdd_priv_data_t *priv_data)
6701{
6702 struct sir_antenna_mode_param params;
6703 QDF_STATUS status;
6704 int ret = 0;
6705 int mode;
6706 uint8_t *value = command;
6707 uint8_t smps_mode;
6708 uint8_t smps_enable;
6709
6710 if (((1 << QDF_STA_MODE) != hdd_ctx->concurrency_mode) ||
6711 (hdd_ctx->no_of_active_sessions[QDF_STA_MODE] > 1)) {
6712 hdd_err("Operation invalid in non sta or concurrent mode");
6713 ret = -EPERM;
6714 goto exit;
6715 }
6716
6717 mode = hdd_parse_setantennamode_command(value);
6718 if (mode < 0) {
6719 hdd_err("Invalid SETANTENNA command");
6720 ret = mode;
6721 goto exit;
6722 }
6723
6724 hdd_info("Processing antenna mode switch to: %d", mode);
6725
6726 if (hdd_ctx->current_antenna_mode == mode) {
6727 hdd_err("System already in the requested mode");
6728 ret = 0;
6729 goto exit;
6730 }
6731
6732 if ((HDD_ANTENNA_MODE_2X2 == mode) &&
6733 (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) {
6734 hdd_err("System does not support 2x2 mode");
6735 ret = -EPERM;
6736 goto exit;
6737 }
6738
6739 if ((HDD_ANTENNA_MODE_1X1 == mode) &&
6740 hdd_is_supported_chain_mask_1x1(hdd_ctx)) {
6741 hdd_err("System only supports 1x1 mode");
6742 ret = 0;
6743 goto exit;
6744 }
6745
6746 switch (mode) {
6747 case HDD_ANTENNA_MODE_1X1:
6748 params.num_rx_chains = 1;
6749 params.num_tx_chains = 1;
6750 break;
6751 case HDD_ANTENNA_MODE_2X2:
6752 params.num_rx_chains = 2;
6753 params.num_tx_chains = 2;
6754 break;
6755 default:
6756 hdd_err("unsupported antenna mode");
6757 ret = -EINVAL;
6758 goto exit;
6759 }
6760
Archana Ramachandrand5d2e922016-04-20 16:57:35 -07006761 /* Check TDLS status and update antenna mode */
6762 if ((QDF_STA_MODE == adapter->device_mode) &&
Kabilan Kannan32eb5022016-10-04 12:24:50 -07006763 cds_is_sta_active_connection_exists()) {
Archana Ramachandran2ad7de22016-04-22 16:53:25 -07006764 ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter,
6765 mode);
Archana Ramachandrand5d2e922016-04-20 16:57:35 -07006766 if (0 != ret)
6767 goto exit;
6768 }
6769
Archana Ramachandran393f3792015-11-13 17:13:21 -08006770 params.set_antenna_mode_resp =
6771 (void *)wlan_hdd_soc_set_antenna_mode_cb;
6772 hdd_info("Set antenna mode rx chains: %d tx chains: %d",
6773 params.num_rx_chains,
6774 params.num_tx_chains);
6775
6776
6777 INIT_COMPLETION(hdd_ctx->set_antenna_mode_cmpl);
6778 status = sme_soc_set_antenna_mode(hdd_ctx->hHal, &params);
6779 if (QDF_STATUS_SUCCESS != status) {
6780 hdd_err("set antenna mode failed status : %d", status);
6781 ret = -EFAULT;
6782 goto exit;
6783 }
6784
6785 ret = wait_for_completion_timeout(
6786 &hdd_ctx->set_antenna_mode_cmpl,
6787 msecs_to_jiffies(WLAN_WAIT_TIME_ANTENNA_MODE_REQ));
6788 if (!ret) {
6789 ret = -EFAULT;
6790 hdd_err("send set antenna mode timed out");
6791 goto exit;
6792 }
6793
6794 /* Update SME SMPS config */
6795 if (HDD_ANTENNA_MODE_1X1 == mode) {
6796 smps_enable = true;
6797 smps_mode = HDD_SMPS_MODE_STATIC;
6798 } else {
6799 smps_enable = false;
6800 smps_mode = HDD_SMPS_MODE_DISABLED;
6801 }
6802
6803 hdd_info("Update SME SMPS enable: %d mode: %d",
6804 smps_enable, smps_mode);
6805 status = sme_update_mimo_power_save(
6806 hdd_ctx->hHal, smps_enable, smps_mode, false);
6807 if (QDF_STATUS_SUCCESS != status) {
6808 hdd_err("Update SMPS config failed enable: %d mode: %d status: %d",
6809 smps_enable, smps_mode, status);
6810 ret = -EFAULT;
6811 goto exit;
6812 }
6813
Archana Ramachandran393f3792015-11-13 17:13:21 -08006814 hdd_ctx->current_antenna_mode = mode;
Archana Ramachandran5041b252016-04-25 14:29:25 -07006815 /* Update the user requested nss in the mac context.
6816 * This will be used in tdls protocol engine to form tdls
6817 * Management frames.
6818 */
6819 sme_update_user_configured_nss(
6820 hdd_ctx->hHal,
6821 hdd_ctx->current_antenna_mode);
Archana Ramachandran393f3792015-11-13 17:13:21 -08006822
Archana Ramachandran5041b252016-04-25 14:29:25 -07006823 hdd_info("Successfully switched to mode: %d x %d",
6824 hdd_ctx->current_antenna_mode,
6825 hdd_ctx->current_antenna_mode);
6826 ret = 0;
Archana Ramachandran393f3792015-11-13 17:13:21 -08006827exit:
Kabilan Kannanff89f742016-08-15 18:14:10 -07006828#ifdef FEATURE_WLAN_TDLS
6829 /* Reset tdls NSS flags */
6830 if (hdd_ctx->tdls_nss_switch_in_progress &&
6831 hdd_ctx->tdls_nss_teardown_complete) {
6832 hdd_ctx->tdls_nss_switch_in_progress = false;
6833 hdd_ctx->tdls_nss_teardown_complete = false;
6834 }
6835 hdd_info("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d",
6836 hdd_ctx->tdls_nss_switch_in_progress,
6837 hdd_ctx->tdls_nss_teardown_complete);
6838#endif
Archana Ramachandran393f3792015-11-13 17:13:21 -08006839 hdd_info("Set antenna status: %d current mode: %d",
6840 ret, hdd_ctx->current_antenna_mode);
6841 return ret;
6842
6843}
6844
6845/**
6846 * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command
6847 * handler
6848 * @adapter: Pointer to hdd adapter
6849 * @hdd_ctx: Pointer to hdd context
6850 * @command: Pointer to input command
6851 * @command_len: length of the command
6852 * @priv_data: private data coming with the driver command
6853 *
6854 * Return: 0 for success non-zero for failure
6855 */
6856static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter,
6857 hdd_context_t *hdd_ctx,
6858 uint8_t *command,
6859 uint8_t command_len,
6860 hdd_priv_data_t *priv_data)
6861{
6862 uint32_t antenna_mode = 0;
6863 char extra[32];
6864 uint8_t len = 0;
6865
6866 antenna_mode = hdd_ctx->current_antenna_mode;
6867 len = scnprintf(extra, sizeof(extra), "%s %d", command,
6868 antenna_mode);
6869 len = QDF_MIN(priv_data->total_len, len + 1);
6870 if (copy_to_user(priv_data->buf, &extra, len)) {
6871 hdd_err("Failed to copy data to user buffer");
6872 return -EFAULT;
6873 }
6874
6875 hdd_info("Get antenna mode: %d", antenna_mode);
6876
6877 return 0;
6878}
6879
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006880/*
6881 * dummy (no-op) hdd driver command handler
6882 */
6883static int drv_cmd_dummy(hdd_adapter_t *adapter,
6884 hdd_context_t *hdd_ctx,
6885 uint8_t *command,
6886 uint8_t command_len,
6887 hdd_priv_data_t *priv_data)
6888{
6889 hdd_info("%s: Ignoring driver command \"%s\"",
6890 adapter->dev->name, command);
6891 return 0;
6892}
6893
6894/*
6895 * handler for any unsupported wlan hdd driver command
6896 */
6897static int drv_cmd_invalid(hdd_adapter_t *adapter,
6898 hdd_context_t *hdd_ctx,
6899 uint8_t *command,
6900 uint8_t command_len,
6901 hdd_priv_data_t *priv_data)
6902{
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306903 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006904 TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
6905 adapter->sessionId, 0));
6906
6907 hdd_warn("%s: Unsupported driver command \"%s\"",
6908 adapter->dev->name, command);
6909
6910 return -ENOTSUPP;
6911}
6912
6913/**
6914 * drv_cmd_set_fcc_channel() - handle fcc constraint request
6915 * @adapter: HDD adapter
6916 * @hdd_ctx: HDD context
6917 * @command: command ptr, SET_FCC_CHANNEL 0/1 is the command
6918 * @command_len: command len
6919 * @priv_data: private data
6920 *
6921 * Return: status
6922 */
6923static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter,
6924 hdd_context_t *hdd_ctx,
6925 uint8_t *command,
6926 uint8_t command_len,
6927 hdd_priv_data_t *priv_data)
6928{
6929 uint8_t *value;
6930 uint8_t fcc_constraint;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306931 QDF_STATUS status;
Amar Singhal83a047a2016-05-19 15:56:11 -07006932 bool scan_pending;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006933 int ret = 0;
6934
6935 /*
6936 * this command would be called by user-space when it detects WLAN
6937 * ON after airplane mode is set. When APM is set, WLAN turns off.
6938 * But it can be turned back on. Otherwise; when APM is turned back
6939 * off, WLAN would turn back on. So at that point the command is
6940 * expected to come down. 0 means disable, 1 means enable. The
6941 * constraint is removed when parameter 1 is set or different
6942 * country code is set
6943 */
6944
6945 value = command + command_len + 1;
6946
6947 ret = kstrtou8(value, 10, &fcc_constraint);
6948 if ((ret < 0) || (fcc_constraint > 1)) {
6949 /*
6950 * If the input value is greater than max value of datatype,
6951 * then also it is a failure
6952 */
6953 hdd_err("value out of range");
6954 return -EINVAL;
6955 }
6956
Amar Singhal83a047a2016-05-19 15:56:11 -07006957 scan_pending = !qdf_list_empty(&hdd_ctx->hdd_scan_req_q);
6958 status = sme_handle_set_fcc_channel(hdd_ctx->hHal, !fcc_constraint,
6959 scan_pending);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306960 if (status != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006961 hdd_err("sme disable fn. returned err");
6962 ret = -EPERM;
6963 }
6964
6965 return ret;
6966}
6967
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05306968/**
6969 * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH
6970 * command
6971 * @value: Pointer to the command
6972 * @chan_number: Pointer to the channel number
6973 * @chan_bw: Pointer to the channel bandwidth
6974 *
6975 * Parses and provides the channel number and channel width from the input
6976 * command which is expected to be of the format: CHANNEL_SWITCH <CH> <BW>
6977 * <CH> is channel number to move (where 1 = channel 1, 149 = channel 149, ...)
6978 * <BW> is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80)
6979 *
6980 * Return: 0 for success, non-zero for failure
6981 */
6982static int hdd_parse_set_channel_switch_command(uint8_t *value,
6983 uint32_t *chan_number,
6984 uint32_t *chan_bw)
6985{
6986 const uint8_t *in_ptr = value;
6987 int ret;
6988
6989 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
6990
6991 /* no argument after the command */
6992 if (NULL == in_ptr) {
6993 hdd_err("No argument after the command");
6994 return -EINVAL;
6995 }
6996
6997 /* no space after the command */
6998 if (SPACE_ASCII_VALUE != *in_ptr) {
6999 hdd_err("No space after the command ");
7000 return -EINVAL;
7001 }
7002
7003 /* remove empty spaces and move the next argument */
7004 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
7005 in_ptr++;
7006
7007 /* no argument followed by spaces */
7008 if ('\0' == *in_ptr) {
7009 hdd_err("No argument followed by spaces");
7010 return -EINVAL;
7011 }
7012
7013 /* get the two arguments: channel number and bandwidth */
7014 ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw);
7015 if (ret != 2) {
7016 hdd_err("Arguments retrieval from cmd string failed");
7017 return -EINVAL;
7018 }
7019
7020 return 0;
7021}
7022
7023/**
7024 * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel
7025 * @adapter: HDD adapter
7026 * @hdd_ctx: HDD context
7027 * @command: Pointer to the input command CHANNEL_SWITCH
7028 * @command_len: Command len
7029 * @priv_data: Private data
7030 *
7031 * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel
7032 * of SAP/P2P-GO
7033 *
7034 * Return: 0 for success, non-zero for failure
7035 */
7036static int drv_cmd_set_channel_switch(hdd_adapter_t *adapter,
7037 hdd_context_t *hdd_ctx,
7038 uint8_t *command,
7039 uint8_t command_len,
7040 hdd_priv_data_t *priv_data)
7041{
7042 struct net_device *dev = adapter->dev;
7043 int status;
7044 uint32_t chan_number = 0, chan_bw = 0;
7045 uint8_t *value = command;
Kiran Kumar Lokere13644672016-02-29 15:40:10 -08007046 enum phy_ch_width width;
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307047
Krunal Sonibe766b02016-03-10 13:00:44 -08007048 if ((adapter->device_mode != QDF_P2P_GO_MODE) &&
7049 (adapter->device_mode != QDF_SAP_MODE)) {
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307050 hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d",
7051 adapter->device_mode);
7052 return -EINVAL;
7053 }
7054
7055 status = hdd_parse_set_channel_switch_command(value,
7056 &chan_number, &chan_bw);
7057 if (status) {
7058 hdd_err("Invalid CHANNEL_SWITCH command");
7059 return status;
7060 }
7061
7062 if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80)) {
7063 hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw);
7064 return -EINVAL;
7065 }
7066
7067 if (chan_bw == 80)
7068 width = CH_WIDTH_80MHZ;
7069 else if (chan_bw == 40)
7070 width = CH_WIDTH_40MHZ;
7071 else
7072 width = CH_WIDTH_20MHZ;
7073
7074 hdd_info("CH:%d BW:%d", chan_number, chan_bw);
7075
7076 status = hdd_softap_set_channel_change(dev, chan_number, width);
7077 if (status) {
7078 hdd_err("Set channel change fail");
7079 return status;
7080 }
7081
7082 return 0;
7083}
7084
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007085/*
7086 * The following table contains all supported WLAN HDD
7087 * IOCTL driver commands and the handler for each of them.
7088 */
7089static const hdd_drv_cmd_t hdd_drv_cmds[] = {
7090 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr},
7091 {"P2P_SET_NOA", drv_cmd_p2p_set_noa},
7092 {"P2P_SET_PS", drv_cmd_p2p_set_ps},
7093 {"SETBAND", drv_cmd_set_band},
7094 {"SETWMMPS", drv_cmd_set_wmmps},
7095 {"COUNTRY", drv_cmd_country},
7096 {"SETSUSPENDMODE", drv_cmd_dummy},
7097 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy},
7098 {"BTCOEXSCAN", drv_cmd_dummy},
7099 {"RXFILTER", drv_cmd_dummy},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007100 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger},
7101 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger},
7102 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period},
7103 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period},
7104 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period},
7105 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007106 {"SETROAMMODE", drv_cmd_set_roam_mode},
7107 {"GETROAMMODE", drv_cmd_get_roam_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007108 {"SETROAMDELTA", drv_cmd_set_roam_delta},
7109 {"GETROAMDELTA", drv_cmd_get_roam_delta},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007110 {"GETBAND", drv_cmd_get_band},
7111 {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels},
7112 {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels},
7113 {"GETCCXMODE", drv_cmd_get_ccx_mode},
7114 {"GETOKCMODE", drv_cmd_get_okc_mode},
7115 {"GETFASTROAM", drv_cmd_get_fast_roam},
7116 {"GETFASTTRANSITION", drv_cmd_get_fast_transition},
7117 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time},
7118 {"SENDACTIONFRAME", drv_cmd_send_action_frame},
7119 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time},
7120 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time},
7121 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time},
7122 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time},
7123 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time},
7124 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band},
7125 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band},
7126 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes},
7127 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes},
7128 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time},
7129 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time},
7130 {"REASSOC", drv_cmd_reassoc},
7131 {"SETWESMODE", drv_cmd_set_wes_mode},
7132 {"GETWESMODE", drv_cmd_get_wes_mode},
7133 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff},
7134 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff},
7135 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff},
7136 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007137 {"SETFASTROAM", drv_cmd_set_fast_roam},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007138 {"SETFASTTRANSITION", drv_cmd_set_fast_transition},
7139 {"FASTREASSOC", drv_cmd_fast_reassoc},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007140 {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007141 {"SETOKCMODE", drv_cmd_set_okc_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007142 {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control},
7143 {"BTCOEXMODE", drv_cmd_bt_coex_mode},
7144 {"SCAN-ACTIVE", drv_cmd_scan_active},
7145 {"SCAN-PASSIVE", drv_cmd_scan_passive},
7146 {"GETDWELLTIME", drv_cmd_get_dwell_time},
7147 {"SETDWELLTIME", drv_cmd_set_dwell_time},
7148 {"MIRACAST", drv_cmd_miracast},
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08007149 {"SETIBSSBEACONOUIDATA", drv_cmd_set_ibss_beacon_oui_data},
7150 {"SETRMCENABLE", drv_cmd_set_rmc_enable},
7151 {"SETRMCACTIONPERIOD", drv_cmd_set_rmc_action_period},
7152 {"GETIBSSPEERINFOALL", drv_cmd_get_ibss_peer_info_all},
7153 {"GETIBSSPEERINFO", drv_cmd_get_ibss_peer_info},
7154 {"SETRMCTXRATE", drv_cmd_set_rmc_tx_rate},
7155 {"SETIBSSTXFAILEVENT", drv_cmd_set_ibss_tx_fail_event},
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08007156#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007157 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels},
7158 {"GETTSMSTATS", drv_cmd_get_tsm_stats},
7159 {"SETCCKMIE", drv_cmd_set_cckm_ie},
Srinivas Girigowdad79b3452016-07-19 18:39:46 -07007160 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req},
7161 {"CCXPLMREQ", drv_cmd_ccx_plm_req},
7162 {"SETCCXMODE", drv_cmd_set_ccx_mode},
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08007163#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007164 {"SETMCRATE", drv_cmd_set_mc_rate},
7165 {"MAXTXPOWER", drv_cmd_max_tx_power},
7166 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode},
7167 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode},
7168 {"GETLINKSTATUS", drv_cmd_get_link_status},
7169#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
7170 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow},
7171 {"SETAPP1PARAMS", drv_cmd_set_app1_params},
7172 {"SETAPP2PARAMS", drv_cmd_set_app2_params},
7173#endif
7174#ifdef FEATURE_WLAN_TDLS
7175 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset},
7176 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode},
7177 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel},
7178 {"TDLSSCAN", drv_cmd_tdls_scan},
7179#endif
7180 {"RSSI", drv_cmd_get_rssi},
7181 {"LINKSPEED", drv_cmd_get_linkspeed},
7182#ifdef FEATURE_NAPI
7183 {"NAPI", drv_cmd_napi},
7184#endif /* FEATURE_NAPI */
7185 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove},
7186 {"RXFILTER-ADD", drv_cmd_rx_filter_add},
7187 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel},
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307188 {"CHANNEL_SWITCH", drv_cmd_set_channel_switch},
Archana Ramachandran393f3792015-11-13 17:13:21 -08007189 {"SETANTENNAMODE", drv_cmd_set_antenna_mode},
7190 {"GETANTENNAMODE", drv_cmd_get_antenna_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007191};
7192
7193/**
7194 * hdd_drv_cmd_process() - chooses and runs the proper
7195 * handler based on the input command
7196 * @adapter: Pointer to the hdd adapter
7197 * @cmd: Pointer to the driver command
7198 * @priv_data: Pointer to the data associated with the command
7199 *
7200 * This function parses the input hdd driver command and runs
7201 * the proper handler
7202 *
7203 * Return: 0 for success non-zero for failure
7204 */
7205static int hdd_drv_cmd_process(hdd_adapter_t *adapter,
7206 uint8_t *cmd,
7207 hdd_priv_data_t *priv_data)
7208{
7209 hdd_context_t *hdd_ctx;
7210 int i;
7211 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds);
7212 uint8_t *cmd_i = NULL;
7213 hdd_drv_cmd_handler_t handler = NULL;
7214 int len = 0;
7215
7216 if (!adapter || !cmd || !priv_data) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007217 hdd_err("at least 1 param is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007218 return -EINVAL;
7219 }
7220
7221 hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
7222
7223 for (i = 0; i < cmd_num_total; i++) {
7224
7225 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd;
7226 handler = hdd_drv_cmds[i].handler;
7227 len = strlen(cmd_i);
7228
7229 if (!handler) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007230 hdd_err("no. %d handler is NULL", i);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007231 return -EINVAL;
7232 }
7233
7234 if (strncasecmp(cmd, cmd_i, len) == 0)
7235 return handler(adapter, hdd_ctx,
7236 cmd, len, priv_data);
7237 }
7238
7239 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data);
7240}
7241
7242/**
7243 * hdd_driver_command() - top level wlan hdd driver command handler
7244 * @adapter: Pointer to the hdd adapter
7245 * @priv_data: Pointer to the raw command data
7246 *
7247 * This function is the top level wlan hdd driver command handler. It
7248 * handles the command with the help of hdd_drv_cmd_process()
7249 *
7250 * Return: 0 for success non-zero for failure
7251 */
7252static int hdd_driver_command(hdd_adapter_t *adapter,
7253 hdd_priv_data_t *priv_data)
7254{
7255 uint8_t *command = NULL;
7256 int ret = 0;
7257
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307258 ENTER();
7259
Anurag Chouhan6d760662016-02-20 16:05:43 +05307260 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007261 hdd_err("Command not allowed in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007262 return -EINVAL;
7263 }
7264
7265 /*
7266 * Note that valid pointers are provided by caller
7267 */
7268
7269 /* copy to local struct to avoid numerous changes to legacy code */
7270 if (priv_data->total_len <= 0 ||
7271 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007272 hdd_warn("Invalid priv_data.total_len(%d)!!!",
7273 priv_data->total_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007274 ret = -EINVAL;
7275 goto exit;
7276 }
7277
7278 /* Allocate +1 for '\0' */
7279 command = kmalloc(priv_data->total_len + 1, GFP_KERNEL);
7280 if (!command) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007281 hdd_err("failed to allocate memory");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007282 ret = -ENOMEM;
7283 goto exit;
7284 }
7285
7286 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) {
7287 ret = -EFAULT;
7288 goto exit;
7289 }
7290
7291 /* Make sure the command is NUL-terminated */
7292 command[priv_data->total_len] = '\0';
7293
7294 hdd_info("%s: %s", adapter->dev->name, command);
7295 ret = hdd_drv_cmd_process(adapter, command, priv_data);
7296
7297exit:
7298 if (command)
7299 kfree(command);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307300 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007301 return ret;
7302}
7303
7304#ifdef CONFIG_COMPAT
7305static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7306{
7307 struct {
7308 compat_uptr_t buf;
7309 int used_len;
7310 int total_len;
7311 } compat_priv_data;
7312 hdd_priv_data_t priv_data;
7313 int ret = 0;
7314
7315 /*
7316 * Note that adapter and ifr have already been verified by caller,
7317 * and HDD context has also been validated
7318 */
7319 if (copy_from_user(&compat_priv_data, ifr->ifr_data,
7320 sizeof(compat_priv_data))) {
7321 ret = -EFAULT;
7322 goto exit;
7323 }
7324 priv_data.buf = compat_ptr(compat_priv_data.buf);
7325 priv_data.used_len = compat_priv_data.used_len;
7326 priv_data.total_len = compat_priv_data.total_len;
7327 ret = hdd_driver_command(adapter, &priv_data);
7328exit:
7329 return ret;
7330}
7331#else /* CONFIG_COMPAT */
7332static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7333{
7334 /* will never be invoked */
7335 return 0;
7336}
7337#endif /* CONFIG_COMPAT */
7338
7339static int hdd_driver_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7340{
7341 hdd_priv_data_t priv_data;
7342 int ret = 0;
7343
7344 /*
7345 * Note that adapter and ifr have already been verified by caller,
7346 * and HDD context has also been validated
7347 */
7348 if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data)))
7349 ret = -EFAULT;
7350 else
7351 ret = hdd_driver_command(adapter, &priv_data);
7352
7353 return ret;
7354}
7355
7356/**
7357 * __hdd_ioctl() - ioctl handler for wlan network interfaces
7358 * @dev: device upon which the ioctl was received
7359 * @ifr: ioctl request information
7360 * @cmd: ioctl command
7361 *
7362 * This function does initial processing of wlan device ioctls.
7363 * Currently two flavors of ioctls are supported. The primary ioctl
7364 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used
7365 * for Android "DRIVER" commands. The other ioctl that is
7366 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used
7367 * for FTM on some platforms. This function simply verifies that the
7368 * driver is in a sane state, and that the ioctl is one of the
7369 * supported flavors, in which case flavor-specific handlers are
7370 * dispatched.
7371 *
7372 * Return: 0 on success, non-zero on error
7373 */
7374static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7375{
7376 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
7377 hdd_context_t *hdd_ctx;
7378 int ret;
7379
Jeff Johnson3c3994a2016-02-11 08:12:30 -08007380 ENTER_DEV(dev);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307381
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007382 if (dev != adapter->dev) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007383 hdd_alert("HDD adapter/dev inconsistency");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007384 ret = -ENODEV;
7385 goto exit;
7386 }
7387
7388 if ((!ifr) || (!ifr->ifr_data)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007389 hdd_err("invalid data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007390 ret = -EINVAL;
7391 goto exit;
7392 }
7393#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
Anurag Chouhan6d760662016-02-20 16:05:43 +05307394 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007395 if (SIOCIOCTLTX99 == cmd) {
7396 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr);
7397 goto exit;
7398 }
7399 }
7400#endif
7401
7402 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
7403 ret = wlan_hdd_validate_context(hdd_ctx);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307404 if (ret)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007405 goto exit;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007406
7407 switch (cmd) {
7408 case (SIOCDEVPRIVATE + 1):
7409 if (is_compat_task())
7410 ret = hdd_driver_compat_ioctl(adapter, ifr);
7411 else
7412 ret = hdd_driver_ioctl(adapter, ifr);
7413 break;
7414 default:
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007415 hdd_err("unknown ioctl %d", cmd);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007416 ret = -EINVAL;
7417 break;
7418 }
7419exit:
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307420 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007421 return ret;
7422}
7423
7424/**
7425 * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces
7426 * @dev: device upon which the ioctl was received
7427 * @ifr: ioctl request information
7428 * @cmd: ioctl command
7429 *
7430 * This function acts as an SSR-protecting wrapper to __hdd_ioctl()
7431 * which is where the ioctls are really handled.
7432 *
7433 * Return: 0 on success, non-zero on error
7434 */
7435int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7436{
7437 int ret;
7438
7439 cds_ssr_protect(__func__);
7440 ret = __hdd_ioctl(dev, ifr, cmd);
7441 cds_ssr_unprotect(__func__);
7442 return ret;
7443}