blob: ded6f9bc1fc23038030de09a0fa7ac9dabb640c1 [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 */
449 if (pPeerInfo->numPeers < SIR_MAX_NUM_STA_IN_IBSS) {
450 pStaCtx->ibss_peer_info.status = pPeerInfo->status;
451 pStaCtx->ibss_peer_info.numPeers = pPeerInfo->numPeers;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800452
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700453 for (i = 0; i < pPeerInfo->numPeers; i++) {
454 pStaCtx->ibss_peer_info.peerInfoParams[i] =
455 pPeerInfo->peerInfoParams[i];
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800456 }
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700457 hdd_info("Peer Info copied in HDD");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800458 } else {
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700459 hdd_info("Number of peers %d returned is more than limit %d",
460 pPeerInfo->numPeers, SIR_MAX_NUM_STA_IN_IBSS);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800461 }
462 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700463 hdd_info("peerInfo returned is NULL");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800464 }
465
466 complete(&adapter->ibss_peer_info_comp);
467}
468
469/**
470 * hdd_cfg80211_get_ibss_peer_info_all() - get ibss peers' info
471 * @adapter: Adapter context
472 *
473 * Request function to get IBSS peer info from lower layers
474 *
475 * Return: 0 for success non-zero for failure
476 */
477static
478QDF_STATUS hdd_cfg80211_get_ibss_peer_info_all(hdd_adapter_t *adapter)
479{
480 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
481 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
482 unsigned long rc;
483
484 INIT_COMPLETION(adapter->ibss_peer_info_comp);
485
486 retStatus = sme_request_ibss_peer_info(hHal, adapter,
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700487 hdd_get_ibss_peer_info_cb,
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800488 true, 0xFF);
489
490 if (QDF_STATUS_SUCCESS == retStatus) {
491 rc = wait_for_completion_timeout
492 (&adapter->ibss_peer_info_comp,
493 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
494
495 /* status will be 0 if timed out */
496 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700497 hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800498 retStatus = QDF_STATUS_E_FAILURE;
499 return retStatus;
500 }
501 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700502 hdd_warn("Warning: sme_request_ibss_peer_info Request failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800503 }
504
505 return retStatus;
506}
507
508/**
509 * hdd_cfg80211_get_ibss_peer_info() - get ibss peer info
510 * @adapter: Adapter context
511 * @staIdx: Sta index for which the peer info is requested
512 *
513 * Request function to get IBSS peer info from lower layers
514 *
515 * Return: 0 for success non-zero for failure
516 */
517static QDF_STATUS
518hdd_cfg80211_get_ibss_peer_info(hdd_adapter_t *adapter, uint8_t staIdx)
519{
520 unsigned long rc;
521 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
522 QDF_STATUS retStatus = QDF_STATUS_E_FAILURE;
523
524 INIT_COMPLETION(adapter->ibss_peer_info_comp);
525
526 retStatus = sme_request_ibss_peer_info(hHal, adapter,
Rajeev Kumar94c9b452016-03-24 12:58:47 -0700527 hdd_get_ibss_peer_info_cb,
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800528 false, staIdx);
529
530 if (QDF_STATUS_SUCCESS == retStatus) {
531 rc = wait_for_completion_timeout(
532 &adapter->ibss_peer_info_comp,
533 msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
534
535 /* status = 0 on timeout */
536 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700537 hdd_warn("Warning: IBSS_PEER_INFO_TIMEOUT");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800538 retStatus = QDF_STATUS_E_FAILURE;
539 return retStatus;
540 }
541 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700542 hdd_warn("Warning: sme_request_ibss_peer_info Request failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -0800543 }
544
545 return retStatus;
546}
547
548/* Function header is left blank intentionally */
549QDF_STATUS
550hdd_parse_get_ibss_peer_info(uint8_t *pValue, struct qdf_mac_addr *pPeerMacAddr)
551{
552 uint8_t *inPtr = pValue;
553 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
554
555 if (NULL == inPtr) {
556 return QDF_STATUS_E_FAILURE;;
557 }
558
559 else if (SPACE_ASCII_VALUE != *inPtr) {
560 return QDF_STATUS_E_FAILURE;;
561 }
562
563 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
564 inPtr++;
565
566 if ('\0' == *inPtr) {
567 return QDF_STATUS_E_FAILURE;;
568 }
569
570 if (inPtr[2] != ':' || inPtr[5] != ':' || inPtr[8] != ':' ||
571 inPtr[11] != ':' || inPtr[14] != ':') {
572 return QDF_STATUS_E_FAILURE;;
573 }
574 sscanf(inPtr, "%2x:%2x:%2x:%2x:%2x:%2x",
575 (unsigned int *)&pPeerMacAddr->bytes[0],
576 (unsigned int *)&pPeerMacAddr->bytes[1],
577 (unsigned int *)&pPeerMacAddr->bytes[2],
578 (unsigned int *)&pPeerMacAddr->bytes[3],
579 (unsigned int *)&pPeerMacAddr->bytes[4],
580 (unsigned int *)&pPeerMacAddr->bytes[5]);
581
582 return QDF_STATUS_SUCCESS;
583}
584
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800585static void hdd_get_band_helper(hdd_context_t *hdd_ctx, int *pBand)
586{
587 eCsrBand band = -1;
588 sme_get_freq_band((tHalHandle) (hdd_ctx->hHal), &band);
589 switch (band) {
590 case eCSR_BAND_ALL:
591 *pBand = WLAN_HDD_UI_BAND_AUTO;
592 break;
593
594 case eCSR_BAND_24:
595 *pBand = WLAN_HDD_UI_BAND_2_4_GHZ;
596 break;
597
598 case eCSR_BAND_5G:
599 *pBand = WLAN_HDD_UI_BAND_5_GHZ;
600 break;
601
602 default:
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700603 hdd_warn("Invalid Band %d", band);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800604 *pBand = -1;
605 break;
606 }
607}
608
609/**
610 * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel
611 * @data: input data
612 * @target_ap_bssid: pointer to bssid (output parameter)
613 * @channel: pointer to channel (output parameter)
614 *
615 * Return: 0 if parsing is successful; -EINVAL otherwise
616 */
617static int _hdd_parse_bssid_and_chan(const uint8_t **data,
618 uint8_t *bssid,
619 uint8_t *channel)
620{
621 const uint8_t *in_ptr;
622 int v = 0;
623 int temp_int;
624 uint8_t temp_buf[32];
625
626 /* 12 hexa decimal digits, 5 ':' and '\0' */
627 uint8_t mac_addr[18];
628
629 if (!data || !*data)
630 return -EINVAL;
631
632 in_ptr = *data;
633
634 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
635 /* no argument after the command */
636 if (NULL == in_ptr)
637 goto error;
638 /* no space after the command */
639 else if (SPACE_ASCII_VALUE != *in_ptr)
640 goto error;
641
642 /* remove empty spaces */
643 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
644 in_ptr++;
645
646 /* no argument followed by spaces */
647 if ('\0' == *in_ptr)
648 goto error;
649
650 v = sscanf(in_ptr, "%17s", mac_addr);
651 if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700652 hdd_err("Invalid MAC address or All hex inputs are not read (%d)",
653 v);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800654 goto error;
655 }
656
657 bssid[0] = hex_to_bin(mac_addr[0]) << 4 |
658 hex_to_bin(mac_addr[1]);
659 bssid[1] = hex_to_bin(mac_addr[3]) << 4 |
660 hex_to_bin(mac_addr[4]);
661 bssid[2] = hex_to_bin(mac_addr[6]) << 4 |
662 hex_to_bin(mac_addr[7]);
663 bssid[3] = hex_to_bin(mac_addr[9]) << 4 |
664 hex_to_bin(mac_addr[10]);
665 bssid[4] = hex_to_bin(mac_addr[12]) << 4 |
666 hex_to_bin(mac_addr[13]);
667 bssid[5] = hex_to_bin(mac_addr[15]) << 4 |
668 hex_to_bin(mac_addr[16]);
669
670 /* point to the next argument */
671 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
672 /* no argument after the command */
673 if (NULL == in_ptr)
674 goto error;
675
676 /* remove empty spaces */
677 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
678 in_ptr++;
679
680 /* no argument followed by spaces */
681 if ('\0' == *in_ptr)
682 goto error;
683
684 /* get the next argument ie the channel number */
685 v = sscanf(in_ptr, "%31s ", temp_buf);
686 if (1 != v)
687 goto error;
688
689 v = kstrtos32(temp_buf, 10, &temp_int);
690 if ((v < 0) || (temp_int < 0) ||
691 (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX))
692 return -EINVAL;
693
694 *channel = temp_int;
695 *data = in_ptr;
696 return 0;
697error:
698 *data = in_ptr;
699 return -EINVAL;
700}
701
702/**
703 * hdd_parse_send_action_frame_data() - HDD Parse send action frame data
704 * @pValue: Pointer to input data
705 * @pTargetApBssid: Pointer to target Ap bssid
706 * @pChannel: Pointer to the Target AP channel
707 * @pDwellTime: Pointer to the time to stay off-channel
708 * after transmitting action frame
709 * @pBuf: Pointer to data
710 * @pBufLen: Pointer to data length
711 *
712 * This function parses the send action frame data passed in the format
713 * SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
714 *
715 * Return: 0 for success non-zero for failure
716 */
717static int
718hdd_parse_send_action_frame_v1_data(const uint8_t *pValue,
719 uint8_t *pTargetApBssid,
720 uint8_t *pChannel, uint8_t *pDwellTime,
721 uint8_t **pBuf, uint8_t *pBufLen)
722{
723 const uint8_t *inPtr = pValue;
724 const uint8_t *dataEnd;
725 int tempInt;
726 int j = 0;
727 int i = 0;
728 int v = 0;
729 uint8_t tempBuf[32];
730 uint8_t tempByte = 0;
731
732 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
733 return -EINVAL;
734
735 /* point to the next argument */
736 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
737 /* no argument after the command */
738 if (NULL == inPtr)
739 return -EINVAL;
740 /* removing empty spaces */
741 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
742 inPtr++;
743
744 /* no argument followed by spaces */
745 if ('\0' == *inPtr) {
746 return -EINVAL;
747 }
748
749 /* getting the next argument ie the dwell time */
750 v = sscanf(inPtr, "%31s ", tempBuf);
751 if (1 != v)
752 return -EINVAL;
753
754 v = kstrtos32(tempBuf, 10, &tempInt);
755 if (v < 0 || tempInt < 0)
756 return -EINVAL;
757
758 *pDwellTime = tempInt;
759
760 /* point to the next argument */
761 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
762 /* no argument after the command */
763 if (NULL == inPtr)
764 return -EINVAL;
765 /* removing empty spaces */
766 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
767 inPtr++;
768
769 /* no argument followed by spaces */
770 if ('\0' == *inPtr) {
771 return -EINVAL;
772 }
773
774 /* find the length of data */
775 dataEnd = inPtr;
776 while (('\0' != *dataEnd)) {
777 dataEnd++;
778 }
779 *pBufLen = dataEnd - inPtr;
780 if (*pBufLen <= 0)
781 return -EINVAL;
782
783 /*
784 * Allocate the number of bytes based on the number of input characters
785 * whether it is even or odd.
786 * if the number of input characters are even, then we need N/2 byte.
787 * if the number of input characters are odd, then we need do (N+1)/2
788 * to compensate rounding off.
789 * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
790 * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes
791 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530792 *pBuf = qdf_mem_malloc((*pBufLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800793 if (NULL == *pBuf) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700794 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800795 return -ENOMEM;
796 }
797
798 /* the buffer received from the upper layer is character buffer,
799 * we need to prepare the buffer taking 2 characters in to a U8 hex
800 * decimal number for example 7f0000f0...form a buffer to contain 7f
801 * in 0th location, 00 in 1st and f0 in 3rd location
802 */
803 for (i = 0, j = 0; j < *pBufLen; j += 2) {
804 if (j + 1 == *pBufLen) {
805 tempByte = hex_to_bin(inPtr[j]);
806 } else {
807 tempByte =
808 (hex_to_bin(inPtr[j]) << 4) |
809 (hex_to_bin(inPtr[j + 1]));
810 }
811 (*pBuf)[i++] = tempByte;
812 }
813 *pBufLen = i;
814 return 0;
815}
816
817/**
818 * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data
819 * @pValue: Pointer to input data (its a NULL terminated string)
820 * @pTargetApBssid: Pointer to target Ap bssid
821 * @pChannel: Pointer to the Target AP channel
822 *
823 * This function parses the reasoc command data passed in the format
824 * REASSOC<space><bssid><space><channel>
825 *
826 * Return: 0 for success non-zero for failure
827 */
828static int hdd_parse_reassoc_command_v1_data(const uint8_t *pValue,
829 uint8_t *pTargetApBssid,
830 uint8_t *pChannel)
831{
832 const uint8_t *inPtr = pValue;
833
834 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
835 return -EINVAL;
836
837 return 0;
838}
839
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800840/**
841 * hdd_reassoc() - perform a userspace-directed reassoc
842 * @adapter: Adapter upon which the command was received
843 * @bssid: BSSID with which to reassociate
844 * @channel: channel upon which to reassociate
845 *
846 * This function performs a userspace-directed reassoc operation
847 *
848 * Return: 0 for success non-zero for failure
849 */
850static int
851hdd_reassoc(hdd_adapter_t *adapter, const uint8_t *bssid,
852 const uint8_t channel)
853{
854 hdd_station_ctx_t *pHddStaCtx;
855 int ret = 0;
856
Krunal Sonibe766b02016-03-10 13:00:44 -0800857 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800858 hdd_warn("Unsupported in mode %s(%d)",
859 hdd_device_mode_to_string(adapter->device_mode),
860 adapter->device_mode);
861 return -EINVAL;
862 }
863
864 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
865
866 /* if not associated, no need to proceed with reassoc */
867 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700868 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800869 ret = -EINVAL;
870 goto exit;
871 }
872
873 /*
874 * if the target bssid is same as currently associated AP,
875 * then no need to proceed with reassoc
876 */
877 if (!memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530878 QDF_MAC_ADDR_SIZE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700879 hdd_info("Reassoc BSSID is same as currently associated AP bssid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800880 ret = -EINVAL;
881 goto exit;
882 }
883
884 /* Check channel number is a valid channel number */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530885 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800886 wlan_hdd_validate_operation_channel(adapter, channel)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700887 hdd_err("Invalid Channel %d", channel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800888 ret = -EINVAL;
889 goto exit;
890 }
891
892 /* Proceed with reassoc */
893 {
894 tCsrHandoffRequest handoffInfo;
895 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
896
897 handoffInfo.channel = channel;
898 handoffInfo.src = REASSOC;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530899 qdf_mem_copy(handoffInfo.bssid.bytes, bssid, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800900 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
901 &handoffInfo);
902 }
903exit:
904 return ret;
905}
906
907/**
908 * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command
909 * @adapter: Adapter upon which the command was received
910 * @command: ASCII text command that was received
911 *
912 * This function parses the v1 REASSOC command with the format
913 *
914 * REASSOC xx:xx:xx:xx:xx:xx CH
915 *
916 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
917 * BSSID and CH is the ASCII representation of the channel. For
918 * example
919 *
920 * REASSOC 00:0a:0b:11:22:33 48
921 *
922 * Return: 0 for success non-zero for failure
923 */
924static int hdd_parse_reassoc_v1(hdd_adapter_t *adapter, const char *command)
925{
926 uint8_t channel = 0;
927 tSirMacAddr bssid;
928 int ret;
929
930 ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel);
931 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700932 hdd_err("Failed to parse reassoc command data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800933 } else {
934 ret = hdd_reassoc(adapter, bssid, channel);
935 }
936 return ret;
937}
938
939/**
940 * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command
941 * @adapter: Adapter upon which the command was received
942 * @command: Command that was received, ASCII command
943 * followed by binary data
944 *
945 * This function parses the v2 REASSOC command with the format
946 *
947 * REASSOC <android_wifi_reassoc_params>
948 *
949 * Return: 0 for success non-zero for failure
950 */
951static int hdd_parse_reassoc_v2(hdd_adapter_t *adapter, const char *command)
952{
953 struct android_wifi_reassoc_params params;
954 tSirMacAddr bssid;
955 int ret;
956
957 /* The params are located after "REASSOC " */
958 memcpy(&params, command + 8, sizeof(params));
959
960 if (!mac_pton(params.bssid, (u8 *) &bssid)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -0700961 hdd_err("MAC address parsing failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800962 ret = -EINVAL;
963 } else {
964 ret = hdd_reassoc(adapter, bssid, params.channel);
965 }
966 return ret;
967}
968
969/**
970 * hdd_parse_reassoc() - parse the REASSOC command
971 * @adapter: Adapter upon which the command was received
972 * @command: Command that was received
973 *
974 * There are two different versions of the REASSOC command. Version 1
975 * of the command contains a parameter list that is ASCII characters
976 * whereas version 2 contains a combination of ASCII and binary
977 * payload. Determine if a version 1 or a version 2 command is being
978 * parsed by examining the parameters, and then dispatch the parser
979 * that is appropriate for the command.
980 *
981 * Return: 0 for success non-zero for failure
982 */
983static int hdd_parse_reassoc(hdd_adapter_t *adapter, const char *command)
984{
985 int ret;
986
987 /* both versions start with "REASSOC "
988 * v1 has a bssid and channel # as an ASCII string
989 * REASSOC xx:xx:xx:xx:xx:xx CH
990 * v2 has a C struct
991 * REASSOC <binary c struct>
992 *
993 * The first field in the v2 struct is also the bssid in ASCII.
994 * But in the case of a v2 message the BSSID is NUL-terminated.
995 * Hence we can peek at that offset to see if this is V1 or V2
996 * REASSOC xx:xx:xx:xx:xx:xx*
997 * 1111111111222222
998 * 01234567890123456789012345
999 */
1000 if (command[25]) {
1001 ret = hdd_parse_reassoc_v1(adapter, command);
1002 } else {
1003 ret = hdd_parse_reassoc_v2(adapter, command);
1004 }
1005
1006 return ret;
1007}
1008
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001009/**
1010 * hdd_sendactionframe() - send a userspace-supplied action frame
1011 * @adapter: Adapter upon which the command was received
1012 * @bssid: BSSID target of the action frame
1013 * @channel: Channel upon which to send the frame
1014 * @dwell_time: Amount of time to dwell when the frame is sent
1015 * @payload_len:Length of the payload
1016 * @payload: Payload of the frame
1017 *
1018 * This function sends a userspace-supplied action frame
1019 *
1020 * Return: 0 for success non-zero for failure
1021 */
1022static int
1023hdd_sendactionframe(hdd_adapter_t *adapter, const uint8_t *bssid,
1024 const uint8_t channel, const uint8_t dwell_time,
1025 const uint8_t payload_len, const uint8_t *payload)
1026{
1027 struct ieee80211_channel chan;
1028 uint8_t frame_len;
1029 uint8_t *frame;
1030 struct ieee80211_hdr_3addr *hdr;
1031 u64 cookie;
1032 hdd_station_ctx_t *pHddStaCtx;
1033 hdd_context_t *hdd_ctx;
1034 int ret = 0;
1035 tpSirMacVendorSpecificFrameHdr pVendorSpecific =
1036 (tpSirMacVendorSpecificFrameHdr) payload;
1037#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1038 struct cfg80211_mgmt_tx_params params;
1039#endif
1040
Krunal Sonibe766b02016-03-10 13:00:44 -08001041 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001042 hdd_warn("Unsupported in mode %s(%d)",
1043 hdd_device_mode_to_string(adapter->device_mode),
1044 adapter->device_mode);
1045 return -EINVAL;
1046 }
1047
1048 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
1049 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1050
1051 /* if not associated, no need to send action frame */
1052 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001053 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001054 ret = -EINVAL;
1055 goto exit;
1056 }
1057
1058 /*
1059 * if the target bssid is different from currently associated AP,
1060 * then no need to send action frame
1061 */
1062 if (memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301063 QDF_MAC_ADDR_SIZE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001064 hdd_info("STA is not associated to this AP");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001065 ret = -EINVAL;
1066 goto exit;
1067 }
1068
1069 chan.center_freq = sme_chn_to_freq(channel);
1070 /* Check if it is specific action frame */
1071 if (pVendorSpecific->category ==
1072 SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) {
1073 static const uint8_t Oui[] = { 0x00, 0x00, 0xf0 };
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301074 if (!qdf_mem_cmp(pVendorSpecific->Oui, (void *)Oui, 3)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001075 /*
1076 * if the channel number is different from operating
1077 * channel then no need to send action frame
1078 */
1079 if (channel != 0) {
1080 if (channel !=
1081 pHddStaCtx->conn_info.operationChannel) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001082 hdd_info("channel(%d) is different from operating channel(%d)",
1083 channel,
1084 pHddStaCtx->conn_info.
1085 operationChannel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001086 ret = -EINVAL;
1087 goto exit;
1088 }
1089 /*
1090 * If channel number is specified and same
1091 * as home channel, ensure that action frame
1092 * is sent immediately by cancelling
1093 * roaming scans. Otherwise large dwell times
1094 * may cause long delays in sending action
1095 * frames.
1096 */
1097 sme_abort_roam_scan(hdd_ctx->hHal,
1098 adapter->sessionId);
1099 } else {
1100 /*
1101 * 0 is accepted as current home channel,
1102 * delayed transmission of action frame is ok.
1103 */
1104 chan.center_freq =
1105 sme_chn_to_freq(pHddStaCtx->conn_info.
1106 operationChannel);
1107 }
1108 }
1109 }
1110 if (chan.center_freq == 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001111 hdd_err("Invalid channel number %d", channel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001112 ret = -EINVAL;
1113 goto exit;
1114 }
1115
1116 frame_len = payload_len + 24;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301117 frame = qdf_mem_malloc(frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001118 if (!frame) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001119 hdd_err("memory allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001120 ret = -ENOMEM;
1121 goto exit;
1122 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301123 qdf_mem_zero(frame, frame_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001124
1125 hdr = (struct ieee80211_hdr_3addr *)frame;
1126 hdr->frame_control =
1127 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301128 qdf_mem_copy(hdr->addr1, bssid, QDF_MAC_ADDR_SIZE);
1129 qdf_mem_copy(hdr->addr2, adapter->macAddressCurrent.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05301130 QDF_MAC_ADDR_SIZE);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301131 qdf_mem_copy(hdr->addr3, bssid, QDF_MAC_ADDR_SIZE);
1132 qdf_mem_copy(hdr + 1, payload, payload_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001133
1134#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
1135 params.chan = &chan;
1136 params.offchan = 0;
1137 params.wait = dwell_time;
1138 params.buf = frame;
1139 params.len = frame_len;
1140 params.no_cck = 1;
1141 params.dont_wait_for_ack = 1;
1142 ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, &params, &cookie);
1143#else
1144 ret = wlan_hdd_mgmt_tx(NULL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001145 &(adapter->wdev),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001146 &chan, 0,
Amar Singhal01098f72015-10-08 11:55:32 -07001147
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001148 dwell_time, frame, frame_len, 1, 1, &cookie);
1149#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
1150
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301151 qdf_mem_free(frame);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001152exit:
1153 return ret;
1154}
1155
1156/**
1157 * hdd_parse_sendactionframe_v1() - parse version 1 of the
1158 * SENDACTIONFRAME command
1159 * @adapter: Adapter upon which the command was received
1160 * @command: ASCII text command that was received
1161 *
1162 * This function parses the v1 SENDACTIONFRAME command with the format
1163 *
1164 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx
1165 *
1166 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
1167 * BSSID, CH is the ASCII representation of the channel, DW is the
1168 * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII
1169 * payload. For example
1170 *
1171 * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee
1172 *
1173 * Return: 0 for success non-zero for failure
1174 */
1175static int
1176hdd_parse_sendactionframe_v1(hdd_adapter_t *adapter, const char *command)
1177{
1178 uint8_t channel = 0;
1179 uint8_t dwell_time = 0;
1180 uint8_t payload_len = 0;
1181 uint8_t *payload = NULL;
1182 tSirMacAddr bssid;
1183 int ret;
1184
1185 ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel,
1186 &dwell_time, &payload,
1187 &payload_len);
1188 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001189 hdd_err("Failed to parse send action frame data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001190 } else {
1191 ret = hdd_sendactionframe(adapter, bssid, channel,
1192 dwell_time, payload_len, payload);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301193 qdf_mem_free(payload);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001194 }
1195
1196 return ret;
1197}
1198
1199/**
1200 * hdd_parse_sendactionframe_v2() - parse version 2 of the
1201 * SENDACTIONFRAME command
1202 * @adapter: Adapter upon which the command was received
1203 * @command: Command that was received, ASCII command
1204 * followed by binary data
1205 *
1206 * This function parses the v2 SENDACTIONFRAME command with the format
1207 *
1208 * SENDACTIONFRAME <android_wifi_af_params>
1209 *
1210 * Return: 0 for success non-zero for failure
1211 */
1212static int
1213hdd_parse_sendactionframe_v2(hdd_adapter_t *adapter, const char *command)
1214{
1215 struct android_wifi_af_params *params;
1216 tSirMacAddr bssid;
1217 int ret;
1218
1219 /* params are large so keep off the stack */
1220 params = kmalloc(sizeof(*params), GFP_KERNEL);
1221 if (!params)
1222 return -ENOMEM;
1223
1224 /* The params are located after "SENDACTIONFRAME " */
1225 memcpy(params, command + 16, sizeof(*params));
1226
1227 if (!mac_pton(params->bssid, (u8 *) &bssid)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001228 hdd_err("MAC address parsing failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001229 ret = -EINVAL;
1230 } else {
1231 ret = hdd_sendactionframe(adapter, bssid, params->channel,
1232 params->dwell_time, params->len,
1233 params->data);
1234 }
1235 kfree(params);
1236 return ret;
1237}
1238
1239/**
1240 * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command
1241 * @adapter: Adapter upon which the command was received
1242 * @command: Command that was received
1243 *
1244 * There are two different versions of the SENDACTIONFRAME command.
1245 * Version 1 of the command contains a parameter list that is ASCII
1246 * characters whereas version 2 contains a combination of ASCII and
1247 * binary payload. Determine if a version 1 or a version 2 command is
1248 * being parsed by examining the parameters, and then dispatch the
1249 * parser that is appropriate for the version of the command.
1250 *
1251 * Return: 0 for success non-zero for failure
1252 */
1253static int
1254hdd_parse_sendactionframe(hdd_adapter_t *adapter, const char *command)
1255{
1256 int ret;
1257
1258 /*
1259 * both versions start with "SENDACTIONFRAME "
1260 * v1 has a bssid and other parameters as an ASCII string
1261 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME
1262 * v2 has a C struct
1263 * SENDACTIONFRAME <binary c struct>
1264 *
1265 * The first field in the v2 struct is also the bssid in ASCII.
1266 * But in the case of a v2 message the BSSID is NUL-terminated.
1267 * Hence we can peek at that offset to see if this is V1 or V2
1268 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx*
1269 * 111111111122222222223333
1270 * 0123456789012345678901234567890123
1271 */
1272 if (command[33]) {
1273 ret = hdd_parse_sendactionframe_v1(adapter, command);
1274 } else {
1275 ret = hdd_parse_sendactionframe_v2(adapter, command);
1276 }
1277
1278 return ret;
1279}
1280
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001281/**
1282 * hdd_parse_channellist() - HDD Parse channel list
1283 * @pValue: Pointer to input channel list
1284 * @ChannelList: Pointer to local output array to record
1285 * channel list
1286 * @pNumChannels: Pointer to number of roam scan channels
1287 *
1288 * This function parses the channel list passed in the format
1289 * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space>Channel 2<space>Channel N
1290 * if the Number of channels (N) does not match with the actual number
1291 * of channels passed then take the minimum of N and count of
1292 * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48,
1293 * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48,
1294 * ignore 5 and take 36, 40, 44 and 48. This function does not take care of
1295 * removing duplicate channels from the list
1296 *
1297 * Return: 0 for success non-zero for failure
1298 */
1299static int
1300hdd_parse_channellist(const uint8_t *pValue, uint8_t *pChannelList,
1301 uint8_t *pNumChannels)
1302{
1303 const uint8_t *inPtr = pValue;
1304 int tempInt;
1305 int j = 0;
1306 int v = 0;
1307 char buf[32];
1308
1309 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1310 /* no argument after the command */
1311 if (NULL == inPtr) {
1312 return -EINVAL;
1313 }
1314
1315 /* no space after the command */
1316 else if (SPACE_ASCII_VALUE != *inPtr) {
1317 return -EINVAL;
1318 }
1319
1320 /* remove empty spaces */
1321 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1322 inPtr++;
1323
1324 /* no argument followed by spaces */
1325 if ('\0' == *inPtr) {
1326 return -EINVAL;
1327 }
1328
1329 /* get the first argument ie the number of channels */
1330 v = sscanf(inPtr, "%31s ", buf);
1331 if (1 != v)
1332 return -EINVAL;
1333
1334 v = kstrtos32(buf, 10, &tempInt);
1335 if ((v < 0) ||
1336 (tempInt <= 0) || (tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN)) {
1337 return -EINVAL;
1338 }
1339
1340 *pNumChannels = tempInt;
1341
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001342 hdd_info("Number of channels are: %d", *pNumChannels);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001343
1344 for (j = 0; j < (*pNumChannels); j++) {
1345 /*
1346 * inPtr pointing to the beginning of first space after number
1347 * of channels
1348 */
1349 inPtr = strpbrk(inPtr, " ");
1350 /* no channel list after the number of channels argument */
1351 if (NULL == inPtr) {
1352 if (0 != j) {
1353 *pNumChannels = j;
1354 return 0;
1355 } else {
1356 return -EINVAL;
1357 }
1358 }
1359
1360 /* remove empty space */
1361 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1362 inPtr++;
1363
1364 /*
1365 * no channel list after the number of channels
1366 * argument and spaces
1367 */
1368 if ('\0' == *inPtr) {
1369 if (0 != j) {
1370 *pNumChannels = j;
1371 return 0;
1372 } else {
1373 return -EINVAL;
1374 }
1375 }
1376
1377 v = sscanf(inPtr, "%31s ", buf);
1378 if (1 != v)
1379 return -EINVAL;
1380
1381 v = kstrtos32(buf, 10, &tempInt);
1382 if ((v < 0) ||
1383 (tempInt <= 0) ||
1384 (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
1385 return -EINVAL;
1386 }
1387 pChannelList[j] = tempInt;
1388
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001389 hdd_info("Channel %d added to preferred channel list",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001390 pChannelList[j]);
1391 }
1392
1393 return 0;
1394}
1395
1396/**
1397 * hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the
1398 * SETROAMSCANCHANNELS command
1399 * @adapter: Adapter upon which the command was received
1400 * @command: ASCII text command that was received
1401 *
1402 * This function parses the v1 SETROAMSCANCHANNELS command with the format
1403 *
1404 * SETROAMSCANCHANNELS N C1 C2 ... Cn
1405 *
1406 * Where "N" is the ASCII representation of the number of channels and
1407 * C1 thru Cn is the ASCII representation of the channels. For example
1408 *
1409 * SETROAMSCANCHANNELS 4 36 40 44 48
1410 *
1411 * Return: 0 for success non-zero for failure
1412 */
1413static int
1414hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *adapter,
1415 const char *command)
1416{
1417 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1418 uint8_t num_chan = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301419 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001420 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1421 int ret;
1422
1423 ret = hdd_parse_channellist(command, channel_list, &num_chan);
1424 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001425 hdd_err("Failed to parse channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001426 goto exit;
1427 }
1428
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301429 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001430 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1431 adapter->sessionId, num_chan));
1432
1433 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001434 hdd_err("number of channels (%d) supported exceeded max (%d)",
1435 num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001436 ret = -EINVAL;
1437 goto exit;
1438 }
1439
1440 status =
1441 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1442 adapter->sessionId,
1443 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301444 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001445 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001446 ret = -EINVAL;
1447 goto exit;
1448 }
1449exit:
1450 return ret;
1451}
1452
1453/**
1454 * hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the
1455 * SETROAMSCANCHANNELS command
1456 * @adapter: Adapter upon which the command was received
1457 * @command: Command that was received, ASCII command
1458 * followed by binary data
1459 *
1460 * This function parses the v2 SETROAMSCANCHANNELS command with the format
1461 *
1462 * SETROAMSCANCHANNELS [N][C1][C2][Cn]
1463 *
1464 * The command begins with SETROAMSCANCHANNELS followed by a space, but
1465 * what follows the space is an array of u08 parameters. For example
1466 *
1467 * SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30]
1468 *
1469 * Return: 0 for success non-zero for failure
1470 */
1471static int
1472hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *adapter,
1473 const char *command)
1474{
1475 const uint8_t *value;
1476 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1477 uint8_t channel;
1478 uint8_t num_chan;
1479 int i;
1480 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301481 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001482 int ret = 0;
1483
1484 /* array of values begins after "SETROAMSCANCHANNELS " */
1485 value = command + 20;
1486
1487 num_chan = *value++;
1488 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001489 hdd_err("number of channels (%d) supported exceeded max (%d)",
1490 num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001491 ret = -EINVAL;
1492 goto exit;
1493 }
1494
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301495 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001496 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1497 adapter->sessionId, num_chan));
1498
1499 for (i = 0; i < num_chan; i++) {
1500 channel = *value++;
1501 if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001502 hdd_err("index %d invalid channel %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001503 i, channel);
1504 ret = -EINVAL;
1505 goto exit;
1506 }
1507 channel_list[i] = channel;
1508 }
1509 status =
1510 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1511 adapter->sessionId,
1512 channel_list, num_chan);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301513 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001514 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001515 ret = -EINVAL;
1516 goto exit;
1517 }
1518exit:
1519 return ret;
1520}
1521
1522/**
1523 * hdd_parse_set_roam_scan_channels() - parse the
1524 * SETROAMSCANCHANNELS command
1525 * @adapter: Adapter upon which the command was received
1526 * @command: Command that was received
1527 *
1528 * There are two different versions of the SETROAMSCANCHANNELS command.
1529 * Version 1 of the command contains a parameter list that is ASCII
1530 * characters whereas version 2 contains a binary payload. Determine
1531 * if a version 1 or a version 2 command is being parsed by examining
1532 * the parameters, and then dispatch the parser that is appropriate for
1533 * the command.
1534 *
1535 * Return: 0 for success non-zero for failure
1536 */
1537static int
1538hdd_parse_set_roam_scan_channels(hdd_adapter_t *adapter, const char *command)
1539{
1540 const char *cursor;
1541 char ch;
1542 bool v1;
1543 int ret;
1544
1545 /* start after "SETROAMSCANCHANNELS " */
1546 cursor = command + 20;
1547
1548 /* assume we have a version 1 command until proven otherwise */
1549 v1 = true;
1550
1551 /* v1 params will only contain ASCII digits and space */
1552 while ((ch = *cursor++) && v1) {
1553 if (!(isdigit(ch) || isspace(ch))) {
1554 v1 = false;
1555 }
1556 }
1557 if (v1) {
1558 ret = hdd_parse_set_roam_scan_channels_v1(adapter, command);
1559 } else {
1560 ret = hdd_parse_set_roam_scan_channels_v2(adapter, command);
1561 }
1562
1563 return ret;
1564}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001565
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08001566#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001567/**
1568 * hdd_parse_plm_cmd() - HDD Parse Plm command
1569 * @pValue: Pointer to input data
1570 * @pPlmRequest:Pointer to output struct tpSirPlmReq
1571 *
1572 * This function parses the plm command passed in the format
1573 * CCXPLMREQ<space><enable><space><dialog_token><space>
1574 * <meas_token><space><num_of_bursts><space><burst_int><space>
1575 * <measu duration><space><burst_len><space><desired_tx_pwr>
1576 * <space><multcast_addr><space><number_of_channels>
1577 * <space><channel_numbers>
1578 *
1579 * Return: 0 for success non-zero for failure
1580 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301581QDF_STATUS hdd_parse_plm_cmd(uint8_t *pValue, tSirPlmReq *pPlmRequest)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001582{
1583 uint8_t *cmdPtr = NULL;
1584 int count, content = 0, ret = 0;
1585 char buf[32];
1586
1587 /* move to argument list */
1588 cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1589 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301590 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001591
1592 /* no space after the command */
1593 if (SPACE_ASCII_VALUE != *cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301594 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001595
1596 /* remove empty spaces */
1597 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1598 cmdPtr++;
1599
1600 /* START/STOP PLM req */
1601 ret = sscanf(cmdPtr, "%31s ", buf);
1602 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301603 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001604
1605 ret = kstrtos32(buf, 10, &content);
1606 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301607 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001608
1609 pPlmRequest->enable = content;
1610 cmdPtr = strpbrk(cmdPtr, " ");
1611
1612 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301613 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001614
1615 /* remove empty spaces */
1616 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1617 cmdPtr++;
1618
1619 /* Dialog token of radio meas req containing meas reqIE */
1620 ret = sscanf(cmdPtr, "%31s ", buf);
1621 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301622 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001623
1624 ret = kstrtos32(buf, 10, &content);
1625 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301626 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001627
1628 pPlmRequest->diag_token = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001629 hdd_debug("diag token %d", pPlmRequest->diag_token);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001630 cmdPtr = strpbrk(cmdPtr, " ");
1631
1632 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301633 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001634
1635 /* remove empty spaces */
1636 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1637 cmdPtr++;
1638
1639 /* measurement token of meas req IE */
1640 ret = sscanf(cmdPtr, "%31s ", buf);
1641 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301642 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001643
1644 ret = kstrtos32(buf, 10, &content);
1645 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301646 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001647
1648 pPlmRequest->meas_token = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001649 hdd_debug("meas token %d", pPlmRequest->meas_token);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001650
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001651 hdd_err("PLM req %s", pPlmRequest->enable ? "START" : "STOP");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001652 if (pPlmRequest->enable) {
1653
1654 cmdPtr = strpbrk(cmdPtr, " ");
1655
1656 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301657 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001658
1659 /* remove empty spaces */
1660 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1661 cmdPtr++;
1662
1663 /* total number of bursts after which STA stops sending */
1664 ret = sscanf(cmdPtr, "%31s ", buf);
1665 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301666 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001667
1668 ret = kstrtos32(buf, 10, &content);
1669 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301670 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001671
1672 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301673 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001674
1675 pPlmRequest->numBursts = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001676 hdd_debug("num burst %d", pPlmRequest->numBursts);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001677 cmdPtr = strpbrk(cmdPtr, " ");
1678
1679 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301680 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001681
1682 /* remove empty spaces */
1683 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1684 cmdPtr++;
1685
1686 /* burst interval in seconds */
1687 ret = sscanf(cmdPtr, "%31s ", buf);
1688 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301689 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001690
1691 ret = kstrtos32(buf, 10, &content);
1692 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301693 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001694
1695 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301696 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001697
1698 pPlmRequest->burstInt = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001699 hdd_debug("burst Int %d", pPlmRequest->burstInt);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001700 cmdPtr = strpbrk(cmdPtr, " ");
1701
1702 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301703 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001704
1705 /* remove empty spaces */
1706 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1707 cmdPtr++;
1708
1709 /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */
1710 ret = sscanf(cmdPtr, "%31s ", buf);
1711 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301712 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001713
1714 ret = kstrtos32(buf, 10, &content);
1715 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301716 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001717
1718 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301719 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001720
1721 pPlmRequest->measDuration = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001722 hdd_debug("measDur %d", pPlmRequest->measDuration);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001723 cmdPtr = strpbrk(cmdPtr, " ");
1724
1725 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301726 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001727
1728 /* remove empty spaces */
1729 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1730 cmdPtr++;
1731
1732 /* burst length of PLM bursts */
1733 ret = sscanf(cmdPtr, "%31s ", buf);
1734 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301735 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001736
1737 ret = kstrtos32(buf, 10, &content);
1738 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301739 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001740
1741 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301742 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001743
1744 pPlmRequest->burstLen = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001745 hdd_debug("burstLen %d", pPlmRequest->burstLen);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001746 cmdPtr = strpbrk(cmdPtr, " ");
1747
1748 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301749 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001750
1751 /* remove empty spaces */
1752 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1753 cmdPtr++;
1754
1755 /* desired tx power for transmission of PLM bursts */
1756 ret = sscanf(cmdPtr, "%31s ", buf);
1757 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301758 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001759
1760 ret = kstrtos32(buf, 10, &content);
1761 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301762 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001763
1764 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301765 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001766
1767 pPlmRequest->desiredTxPwr = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001768 hdd_debug("desiredTxPwr %d",
1769 pPlmRequest->desiredTxPwr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001770
Anurag Chouhan6d760662016-02-20 16:05:43 +05301771 for (count = 0; count < QDF_MAC_ADDR_SIZE; count++) {
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)
1779 && ('\0' != *cmdPtr))
1780 cmdPtr++;
1781
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, 16, &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
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001790 pPlmRequest->mac_addr.bytes[count] = content;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001791 }
1792
Srinivas Girigowda5146dee2015-11-18 21:46:48 -08001793 hdd_debug("MC addr " MAC_ADDRESS_STR,
1794 MAC_ADDR_ARRAY(pPlmRequest->mac_addr.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001795
1796 cmdPtr = strpbrk(cmdPtr, " ");
1797
1798 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301799 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001800
1801 /* remove empty spaces */
1802 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1803 cmdPtr++;
1804
1805 /* number of channels */
1806 ret = sscanf(cmdPtr, "%31s ", buf);
1807 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301808 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001809
1810 ret = kstrtos32(buf, 10, &content);
1811 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301812 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001813
1814 if (content < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301815 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001816
1817 pPlmRequest->plmNumCh = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001818 hdd_debug("numch %d", pPlmRequest->plmNumCh);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001819
1820 /* Channel numbers */
1821 for (count = 0; count < pPlmRequest->plmNumCh; count++) {
1822 cmdPtr = strpbrk(cmdPtr, " ");
1823
1824 if (NULL == cmdPtr)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301825 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001826
1827 /* remove empty spaces */
1828 while ((SPACE_ASCII_VALUE == *cmdPtr)
1829 && ('\0' != *cmdPtr))
1830 cmdPtr++;
1831
1832 ret = sscanf(cmdPtr, "%31s ", buf);
1833 if (1 != ret)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301834 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001835
1836 ret = kstrtos32(buf, 10, &content);
1837 if (ret < 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301838 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001839
1840 if (content <= 0)
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301841 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001842
1843 pPlmRequest->plmChList[count] = content;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001844 hdd_debug(" ch- %d", pPlmRequest->plmChList[count]);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001845 }
1846 }
1847 /* If PLM START */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301848 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001849}
1850#endif
1851
1852#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
1853static void wlan_hdd_ready_to_extwow(void *callbackContext, bool is_success)
1854{
1855 hdd_context_t *hdd_ctx = (hdd_context_t *) callbackContext;
1856 int rc;
1857
1858 rc = wlan_hdd_validate_context(hdd_ctx);
1859 if (0 != rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001860 hdd_err("HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001861 return;
1862 }
1863 hdd_ctx->ext_wow_should_suspend = is_success;
1864 complete(&hdd_ctx->ready_to_extwow);
1865}
1866
1867static int hdd_enable_ext_wow(hdd_adapter_t *adapter,
1868 tpSirExtWoWParams arg_params)
1869{
1870 tSirExtWoWParams params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301871 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001872 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1873 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1874 int rc;
1875
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301876 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001877
1878 INIT_COMPLETION(hdd_ctx->ready_to_extwow);
1879
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301880 qdf_ret_status = sme_configure_ext_wow(hHal, &params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001881 &wlan_hdd_ready_to_extwow,
1882 hdd_ctx);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301883 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001884 hdd_err("sme_configure_ext_wow returned failure %d",
1885 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001886 return -EPERM;
1887 }
1888
1889 rc = wait_for_completion_timeout(&hdd_ctx->ready_to_extwow,
1890 msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_EXTWOW));
1891 if (!rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001892 hdd_err("Failed to get ready to extwow");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001893 return -EPERM;
1894 }
1895
1896 if (hdd_ctx->ext_wow_should_suspend) {
1897 if (hdd_ctx->config->extWowGotoSuspend) {
1898 pm_message_t state;
1899
1900 state.event = PM_EVENT_SUSPEND;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001901 hdd_info("Received ready to ExtWoW. Going to suspend");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001902
1903 rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL);
1904 if (rc < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001905 hdd_err("wlan_hdd_cfg80211_suspend_wlan failed, error = %d",
1906 rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001907 return rc;
1908 }
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301909 qdf_ret_status = wlan_hdd_bus_suspend(state);
1910 if (qdf_ret_status != QDF_STATUS_SUCCESS) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001911 hdd_err("wlan_hdd_suspend failed, status = %d",
1912 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001913 wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy);
1914 return -EPERM;
1915 }
1916 }
1917 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001918 hdd_err("Received ready to ExtWoW failure");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001919 return -EPERM;
1920 }
1921
1922 return 0;
1923}
1924
1925static int hdd_enable_ext_wow_parser(hdd_adapter_t *adapter, int vdev_id,
1926 int value)
1927{
1928 tSirExtWoWParams params;
1929 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1930 int rc;
1931
1932 rc = wlan_hdd_validate_context(hdd_ctx);
1933 if (0 != rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001934 hdd_err("HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001935 return -EINVAL;
1936 }
1937
1938 if (value < EXT_WOW_TYPE_APP_TYPE1 ||
1939 value > EXT_WOW_TYPE_APP_TYPE1_2) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001940 hdd_err("Invalid type");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001941 return -EINVAL;
1942 }
1943
1944 if (value == EXT_WOW_TYPE_APP_TYPE1 &&
1945 hdd_ctx->is_extwow_app_type1_param_set)
1946 params.type = value;
1947 else if (value == EXT_WOW_TYPE_APP_TYPE2 &&
1948 hdd_ctx->is_extwow_app_type2_param_set)
1949 params.type = value;
1950 else if (value == EXT_WOW_TYPE_APP_TYPE1_2 &&
1951 hdd_ctx->is_extwow_app_type1_param_set &&
1952 hdd_ctx->is_extwow_app_type2_param_set)
1953 params.type = value;
1954 else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001955 hdd_err("Set app params before enable it value %d",
1956 value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001957 return -EINVAL;
1958 }
1959
1960 params.vdev_id = vdev_id;
1961 params.wakeup_pin_num = hdd_ctx->config->extWowApp1WakeupPinNumber |
1962 (hdd_ctx->config->extWowApp2WakeupPinNumber
1963 << 8);
1964
1965 return hdd_enable_ext_wow(adapter, &params);
1966}
1967
1968static int hdd_set_app_type1_params(tHalHandle hHal,
1969 tpSirAppType1Params arg_params)
1970{
1971 tSirAppType1Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301972 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001973
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301974 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001975
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301976 qdf_ret_status = sme_configure_app_type1_params(hHal, &params);
1977 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001978 hdd_err("sme_configure_app_type1_params returned failure %d",
1979 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001980 return -EPERM;
1981 }
1982
1983 return 0;
1984}
1985
1986static int hdd_set_app_type1_parser(hdd_adapter_t *adapter,
1987 char *arg, int len)
1988{
1989 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1990 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1991 char id[20], password[20];
1992 tSirAppType1Params params;
Srinivas Girigowda04209912015-11-24 12:11:13 -08001993 int rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001994
1995 rc = wlan_hdd_validate_context(hdd_ctx);
1996 if (0 != rc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07001997 hdd_err("HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001998 return -EINVAL;
1999 }
2000
2001 if (2 != sscanf(arg, "%8s %16s", id, password)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002002 hdd_err("Invalid Number of arguments");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002003 return -EINVAL;
2004 }
2005
2006 memset(&params, 0, sizeof(tSirAppType1Params));
2007 params.vdev_id = adapter->sessionId;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302008 qdf_copy_macaddr(&params.wakee_mac_addr, &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002009
2010 params.id_length = strlen(id);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302011 qdf_mem_copy(params.identification_id, id, params.id_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002012 params.pass_length = strlen(password);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302013 qdf_mem_copy(params.password, password, params.pass_length);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002014
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002015 hdd_info("%d %pM %.8s %u %.16s %u",
2016 params.vdev_id, params.wakee_mac_addr.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002017 params.identification_id, params.id_length,
2018 params.password, params.pass_length);
2019
2020 return hdd_set_app_type1_params(hHal, &params);
2021}
2022
2023static int hdd_set_app_type2_params(tHalHandle hHal,
2024 tpSirAppType2Params arg_params)
2025{
2026 tSirAppType2Params params;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302027 QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002028
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302029 qdf_mem_copy(&params, arg_params, sizeof(params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002030
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05302031 qdf_ret_status = sme_configure_app_type2_params(hHal, &params);
2032 if (QDF_STATUS_SUCCESS != qdf_ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002033 hdd_err("sme_configure_app_type2_params returned failure %d",
2034 qdf_ret_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002035 return -EPERM;
2036 }
2037
2038 return 0;
2039}
2040
2041static int hdd_set_app_type2_parser(hdd_adapter_t *adapter,
2042 char *arg, int len)
2043{
2044 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2045 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2046 char mac_addr[20], rc4_key[20];
Anurag Chouhan6d760662016-02-20 16:05:43 +05302047 unsigned int gateway_mac[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002048 tSirAppType2Params params;
2049 int ret;
2050
2051 ret = wlan_hdd_validate_context(hdd_ctx);
2052 if (0 != ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002053 hdd_err("HDD context is not valid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002054 return -EINVAL;
2055 }
2056
2057 memset(&params, 0, sizeof(tSirAppType2Params));
2058
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302059 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 -08002060 mac_addr, rc4_key, (unsigned int *)&params.ip_id,
2061 (unsigned int *)&params.ip_device_ip,
2062 (unsigned int *)&params.ip_server_ip,
2063 (unsigned int *)&params.tcp_seq,
2064 (unsigned int *)&params.tcp_ack_seq,
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05302065 (uint16_t *)&params.tcp_src_port,
2066 (uint16_t *)&params.tcp_dst_port,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002067 (unsigned int *)&params.keepalive_init,
2068 (unsigned int *)&params.keepalive_min,
2069 (unsigned int *)&params.keepalive_max,
2070 (unsigned int *)&params.keepalive_inc,
2071 (unsigned int *)&params.tcp_tx_timeout_val,
2072 (unsigned int *)&params.tcp_rx_timeout_val);
2073
2074 if (ret != 15 && ret != 7) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002075 hdd_err("Invalid Number of arguments");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002076 return -EINVAL;
2077 }
2078
2079 if (6 !=
2080 sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0],
2081 &gateway_mac[1], &gateway_mac[2], &gateway_mac[3],
2082 &gateway_mac[4], &gateway_mac[5])) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002083 hdd_err("Invalid MacAddress Input %s", mac_addr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002084 return -EINVAL;
2085 }
2086
2087 if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT ||
2088 params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002089 hdd_err("Invalid TCP Port Number");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002090 return -EINVAL;
2091 }
2092
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302093 qdf_mem_copy(&params.gateway_mac.bytes, (uint8_t *) &gateway_mac,
Anurag Chouhan6d760662016-02-20 16:05:43 +05302094 QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002095
2096 params.rc4_key_len = strlen(rc4_key);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302097 qdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002098
2099 params.vdev_id = adapter->sessionId;
2100 params.tcp_src_port = (params.tcp_src_port != 0) ?
2101 params.tcp_src_port : hdd_ctx->config->extWowApp2TcpSrcPort;
2102 params.tcp_dst_port = (params.tcp_dst_port != 0) ?
2103 params.tcp_dst_port : hdd_ctx->config->extWowApp2TcpDstPort;
2104 params.keepalive_init = (params.keepalive_init != 0) ?
2105 params.keepalive_init : hdd_ctx->config->
2106 extWowApp2KAInitPingInterval;
2107 params.keepalive_min =
2108 (params.keepalive_min != 0) ?
2109 params.keepalive_min :
2110 hdd_ctx->config->extWowApp2KAMinPingInterval;
2111 params.keepalive_max =
2112 (params.keepalive_max != 0) ?
2113 params.keepalive_max :
2114 hdd_ctx->config->extWowApp2KAMaxPingInterval;
2115 params.keepalive_inc =
2116 (params.keepalive_inc != 0) ?
2117 params.keepalive_inc :
2118 hdd_ctx->config->extWowApp2KAIncPingInterval;
2119 params.tcp_tx_timeout_val =
2120 (params.tcp_tx_timeout_val != 0) ?
2121 params.tcp_tx_timeout_val :
2122 hdd_ctx->config->extWowApp2TcpTxTimeout;
2123 params.tcp_rx_timeout_val =
2124 (params.tcp_rx_timeout_val != 0) ?
2125 params.tcp_rx_timeout_val :
2126 hdd_ctx->config->extWowApp2TcpRxTimeout;
2127
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002128 hdd_info("%pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u",
2129 gateway_mac, rc4_key, params.ip_id,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002130 params.ip_device_ip, params.ip_server_ip, params.tcp_seq,
2131 params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port,
2132 params.keepalive_init, params.keepalive_min,
2133 params.keepalive_max, params.keepalive_inc,
2134 params.tcp_tx_timeout_val, params.tcp_rx_timeout_val);
2135
2136 return hdd_set_app_type2_params(hHal, &params);
2137}
2138#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
2139
2140/**
2141 * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
2142 * @pValue: Pointer to MAXTXPOWER command
2143 * @pDbm: Pointer to tx power
2144 *
2145 * This function parses the MAXTXPOWER command passed in the format
2146 * MAXTXPOWER<space>X(Tx power in dbm)
2147 *
2148 * For example input commands:
2149 * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm
2150 * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm
2151 *
2152 * Return: 0 for success non-zero for failure
2153 */
2154static int hdd_parse_setmaxtxpower_command(uint8_t *pValue, int *pTxPower)
2155{
2156 uint8_t *inPtr = pValue;
2157 int tempInt;
2158 int v = 0;
2159 *pTxPower = 0;
2160
2161 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2162 /* no argument after the command */
2163 if (NULL == inPtr) {
2164 return -EINVAL;
2165 }
2166
2167 /* no space after the command */
2168 else if (SPACE_ASCII_VALUE != *inPtr) {
2169 return -EINVAL;
2170 }
2171
2172 /* remove empty spaces */
2173 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2174 inPtr++;
2175
2176 /* no argument followed by spaces */
2177 if ('\0' == *inPtr) {
2178 return 0;
2179 }
2180
2181 v = kstrtos32(inPtr, 10, &tempInt);
2182
2183 /* Range checking for passed parameter */
2184 if ((tempInt < HDD_MIN_TX_POWER) || (tempInt > HDD_MAX_TX_POWER)) {
2185 return -EINVAL;
2186 }
2187
2188 *pTxPower = tempInt;
2189
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002190 hdd_info("SETMAXTXPOWER: %d", *pTxPower);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002191
2192 return 0;
2193} /* End of hdd_parse_setmaxtxpower_command */
2194
2195static int hdd_get_dwell_time(struct hdd_config *pCfg, uint8_t *command,
2196 char *extra, uint8_t n, uint8_t *len)
2197{
2198 int ret = 0;
2199
2200 if (!pCfg || !command || !extra || !len) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002201 hdd_err("argument passed for GETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002202 ret = -EINVAL;
2203 return ret;
2204 }
2205
2206 if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) {
2207 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n",
2208 (int)pCfg->nActiveMaxChnTime);
2209 return ret;
2210 } else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0) {
2211 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n",
2212 (int)pCfg->nActiveMinChnTime);
2213 return ret;
2214 } else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) {
2215 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n",
2216 (int)pCfg->nPassiveMaxChnTime);
2217 return ret;
2218 } else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0) {
2219 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n",
2220 (int)pCfg->nPassiveMinChnTime);
2221 return ret;
2222 } else if (strncmp(command, "GETDWELLTIME", 12) == 0) {
2223 *len = scnprintf(extra, n, "GETDWELLTIME %u \n",
2224 (int)pCfg->nActiveMaxChnTime);
2225 return ret;
2226 } else {
2227 ret = -EINVAL;
2228 }
2229
2230 return ret;
2231}
2232
2233static int hdd_set_dwell_time(hdd_adapter_t *adapter, uint8_t *command)
2234{
2235 tHalHandle hHal;
2236 struct hdd_config *pCfg;
2237 uint8_t *value = command;
2238 tSmeConfigParams smeConfig;
2239 int val = 0, temp = 0;
2240
2241 pCfg = (WLAN_HDD_GET_CTX(adapter))->config;
2242 hHal = WLAN_HDD_GET_HAL_CTX(adapter);
2243 if (!pCfg || !hHal) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002244 hdd_err("argument passed for SETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002245 return -EINVAL;
2246 }
2247
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302248 qdf_mem_zero(&smeConfig, sizeof(smeConfig));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002249 sme_get_config_param(hHal, &smeConfig);
2250
2251 if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) {
2252 value = value + 24;
2253 temp = kstrtou32(value, 10, &val);
2254 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2255 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002256 hdd_err("argument passed for SETDWELLTIME ACTIVE MAX is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002257 return -EFAULT;
2258 }
2259 pCfg->nActiveMaxChnTime = val;
2260 smeConfig.csrConfig.nActiveMaxChnTime = val;
2261 sme_update_config(hHal, &smeConfig);
2262 } else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0) {
2263 value = value + 24;
2264 temp = kstrtou32(value, 10, &val);
2265 if (temp != 0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN ||
2266 val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002267 hdd_err("argument passed for SETDWELLTIME ACTIVE MIN is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002268 return -EFAULT;
2269 }
2270 pCfg->nActiveMinChnTime = val;
2271 smeConfig.csrConfig.nActiveMinChnTime = val;
2272 sme_update_config(hHal, &smeConfig);
2273 } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) {
2274 value = value + 25;
2275 temp = kstrtou32(value, 10, &val);
2276 if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN ||
2277 val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002278 hdd_err("argument passed for SETDWELLTIME PASSIVE MAX is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002279 return -EFAULT;
2280 }
2281 pCfg->nPassiveMaxChnTime = val;
2282 smeConfig.csrConfig.nPassiveMaxChnTime = val;
2283 sme_update_config(hHal, &smeConfig);
2284 } else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0) {
2285 value = value + 25;
2286 temp = kstrtou32(value, 10, &val);
2287 if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN ||
2288 val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002289 hdd_err("argument passed for SETDWELLTIME PASSIVE MIN is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002290 return -EFAULT;
2291 }
2292 pCfg->nPassiveMinChnTime = val;
2293 smeConfig.csrConfig.nPassiveMinChnTime = val;
2294 sme_update_config(hHal, &smeConfig);
2295 } else if (strncmp(command, "SETDWELLTIME", 12) == 0) {
2296 value = value + 13;
2297 temp = kstrtou32(value, 10, &val);
2298 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2299 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002300 hdd_err("argument passed for SETDWELLTIME is incorrect");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002301 return -EFAULT;
2302 }
2303 pCfg->nActiveMaxChnTime = val;
2304 smeConfig.csrConfig.nActiveMaxChnTime = val;
2305 sme_update_config(hHal, &smeConfig);
2306 } else {
2307 return -EINVAL;
2308 }
2309
2310 return 0;
2311}
2312
2313static void hdd_get_link_status_cb(uint8_t status, void *context)
2314{
2315 struct statsContext *pLinkContext;
2316 hdd_adapter_t *adapter;
2317
2318 if (NULL == context) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002319 hdd_err("Bad context [%p]", context);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002320 return;
2321 }
2322
2323 pLinkContext = context;
2324 adapter = pLinkContext->pAdapter;
2325
2326 spin_lock(&hdd_context_lock);
2327
2328 if ((NULL == adapter) ||
2329 (LINK_STATUS_MAGIC != pLinkContext->magic)) {
2330 /*
2331 * the caller presumably timed out so there is
2332 * nothing we can do
2333 */
2334 spin_unlock(&hdd_context_lock);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002335 hdd_warn("Invalid context, adapter [%p] magic [%08x]",
2336 adapter, pLinkContext->magic);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002337 return;
2338 }
2339
2340 /* context is valid so caller is still waiting */
2341
2342 /* paranoia: invalidate the magic */
2343 pLinkContext->magic = 0;
2344
2345 /* copy over the status */
2346 adapter->linkStatus = status;
2347
2348 /* notify the caller */
2349 complete(&pLinkContext->completion);
2350
2351 /* serialization is complete */
2352 spin_unlock(&hdd_context_lock);
2353}
2354
2355/**
2356 * wlan_hdd_get_link_status() - get link status
2357 * @pAdapter: pointer to the adapter
2358 *
2359 * This function sends a request to query the link status and waits
2360 * on a timer to invoke the callback. if the callback is invoked then
2361 * latest link status shall be returned or otherwise cached value
2362 * will be returned.
2363 *
2364 * Return: On success, link status shall be returned.
2365 * On error or not associated, link status 0 will be returned.
2366 */
2367static int wlan_hdd_get_link_status(hdd_adapter_t *adapter)
2368{
2369
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002370 hdd_station_ctx_t *pHddStaCtx =
2371 WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2372 struct statsContext context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302373 QDF_STATUS hstatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002374 unsigned long rc;
2375
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002376 if (cds_is_driver_recovering()) {
2377 hdd_warn("Recovery in Progress. State: 0x%x Ignore!!!",
2378 cds_get_driver_state());
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002379 return 0;
2380 }
2381
Krunal Sonibe766b02016-03-10 13:00:44 -08002382 if ((QDF_STA_MODE != adapter->device_mode) &&
2383 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002384 hdd_warn("Unsupported in mode %s(%d)",
2385 hdd_device_mode_to_string(adapter->device_mode),
2386 adapter->device_mode);
2387 return 0;
2388 }
2389
2390 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2391 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
2392 /* If not associated, then expected link status return
2393 * value is 0
2394 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002395 hdd_info("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002396 return 0;
2397 }
2398
2399 init_completion(&context.completion);
2400 context.pAdapter = adapter;
2401 context.magic = LINK_STATUS_MAGIC;
2402 hstatus = sme_get_link_status(WLAN_HDD_GET_HAL_CTX(adapter),
2403 hdd_get_link_status_cb,
2404 &context, adapter->sessionId);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302405 if (QDF_STATUS_SUCCESS != hstatus) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002406 hdd_err("Unable to retrieve link status");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002407 /* return a cached value */
2408 } else {
2409 /* request is sent -- wait for the response */
2410 rc = wait_for_completion_timeout(&context.completion,
2411 msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
2412 if (!rc)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002413 hdd_err("SME timed out while retrieving link status");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002414 }
2415
2416 spin_lock(&hdd_context_lock);
2417 context.magic = 0;
2418 spin_unlock(&hdd_context_lock);
2419
2420 /* either callback updated adapter stats or it has cached data */
2421 return adapter->linkStatus;
2422}
2423
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002424static void hdd_tx_fail_ind_callback(uint8_t *MacAddr, uint8_t seqNo)
2425{
2426 int payload_len;
2427 struct sk_buff *skb;
2428 struct nlmsghdr *nlh;
2429 uint8_t *data;
2430
2431 payload_len = ETH_ALEN;
2432
2433 if (0 == cesium_pid) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002434 hdd_err("cesium process not registered");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002435 return;
2436 }
2437
2438 skb = nlmsg_new(payload_len, GFP_ATOMIC);
2439 if (skb == NULL) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002440 hdd_err("nlmsg_new() failed for msg size[%d]",
2441 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002442 return;
2443 }
2444
2445 nlh = nlmsg_put(skb, cesium_pid, seqNo, 0, payload_len, NLM_F_REQUEST);
2446
2447 if (NULL == nlh) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002448 hdd_err("nlmsg_put() failed for msg size[%d]",
2449 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002450
2451 kfree_skb(skb);
2452 return;
2453 }
2454
2455 data = nlmsg_data(nlh);
2456 memcpy(data, MacAddr, ETH_ALEN);
2457
2458 if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002459 hdd_err("nlmsg_unicast() failed for msg size[%d]",
2460 NLMSG_SPACE(payload_len));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08002461 }
2462
2463 return;
2464}
2465
2466
2467/**
2468 * hdd_ParseuserParams - return a pointer to the next argument
2469 * @pValue: Input argument string
2470 * @ppArg: Output pointer to the next argument
2471 *
2472 * This function parses argument stream and finds the pointer
2473 * to the next argument
2474 *
2475 * Return: 0 if the next argument found; -EINVAL otherwise
2476 */
2477static int hdd_parse_user_params(uint8_t *pValue, uint8_t **ppArg)
2478{
2479 uint8_t *pVal;
2480
2481 pVal = strnchr(pValue, strlen(pValue), ' ');
2482
2483 if (NULL == pVal) {
2484 /* no argument remains */
2485 return -EINVAL;
2486 } else if (SPACE_ASCII_VALUE != *pVal) {
2487 /* no space after the current argument */
2488 return -EINVAL;
2489 }
2490
2491 pVal++;
2492
2493 /* remove empty spaces */
2494 while ((SPACE_ASCII_VALUE == *pVal) && ('\0' != *pVal)) {
2495 pVal++;
2496 }
2497
2498 /* no argument followed by spaces */
2499 if ('\0' == *pVal) {
2500 return -EINVAL;
2501 }
2502
2503 *ppArg = pVal;
2504
2505 return 0;
2506}
2507
2508/**
2509 * hdd_parse_ibsstx_fail_event_params - Parse params
2510 * for SETIBSSTXFAILEVENT
2511 * @pValue: Input ibss tx fail event argument
2512 * @tx_fail_count: (Output parameter) Tx fail counter
2513 * @pid: (Output parameter) PID
2514 *
2515 * Return: 0 if the parsing succeeds; -EINVAL otherwise
2516 */
2517static int hdd_parse_ibsstx_fail_event_params(uint8_t *pValue,
2518 uint8_t *tx_fail_count,
2519 uint16_t *pid)
2520{
2521 uint8_t *param = NULL;
2522 int ret;
2523
2524 ret = hdd_parse_user_params(pValue, &param);
2525
2526 if (0 == ret && NULL != param) {
2527 if (1 != sscanf(param, "%hhu", tx_fail_count)) {
2528 ret = -EINVAL;
2529 goto done;
2530 }
2531 } else {
2532 goto done;
2533 }
2534
2535 if (0 == *tx_fail_count) {
2536 *pid = 0;
2537 goto done;
2538 }
2539
2540 pValue = param;
2541 pValue++;
2542
2543 ret = hdd_parse_user_params(pValue, &param);
2544
2545 if (0 == ret) {
2546 if (1 != sscanf(param, "%hu", pid)) {
2547 ret = -EINVAL;
2548 goto done;
2549 }
2550 } else {
2551 goto done;
2552 }
2553
2554done:
2555 return ret;
2556}
2557
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08002558#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002559/**
2560 * hdd_parse_ese_beacon_req() - Parse ese beacon request
2561 * @pValue: Pointer to data
2562 * @pEseBcnReq: Output pointer to store parsed ie information
2563 *
2564 * This function parses the ese beacon request passed in the format
2565 * CCXBEACONREQ<space><Number of fields><space><Measurement token>
2566 * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N
2567 * <space>Scan Mode N<space>Meas Duration N
2568 *
2569 * If the Number of bcn req fields (N) does not match with the
2570 * actual number of fields passed then take N.
2571 * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated
2572 * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40.
2573 * This function does not take care of removing duplicate channels from the
2574 * list
2575 *
2576 * Return: 0 for success non-zero for failure
2577 */
2578static int hdd_parse_ese_beacon_req(uint8_t *pValue,
2579 tCsrEseBeaconReq *pEseBcnReq)
2580{
2581 uint8_t *inPtr = pValue;
2582 int tempInt = 0;
2583 int j = 0, i = 0, v = 0;
2584 char buf[32];
2585
2586 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2587 /* no argument after the command */
2588 if (NULL == inPtr) {
2589 return -EINVAL;
2590 }
2591 /* no space after the command */
2592 else if (SPACE_ASCII_VALUE != *inPtr) {
2593 return -EINVAL;
2594 }
2595
2596 /* remove empty spaces */
2597 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2598 inPtr++;
2599
2600 /* no argument followed by spaces */
2601 if ('\0' == *inPtr)
2602 return -EINVAL;
2603
2604 /* get the first argument ie measurement token */
2605 v = sscanf(inPtr, "%31s ", buf);
2606 if (1 != v)
2607 return -EINVAL;
2608
2609 v = kstrtos32(buf, 10, &tempInt);
2610 if (v < 0)
2611 return -EINVAL;
2612
2613 pEseBcnReq->numBcnReqIe = tempInt;
2614
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002615 hdd_info("Number of Bcn Req Ie fields(%d)",
2616 pEseBcnReq->numBcnReqIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002617
2618 for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++) {
2619 for (i = 0; i < 4; i++) {
2620 /*
2621 * inPtr pointing to the beginning of 1st space
2622 * after number of ie fields
2623 */
2624 inPtr = strpbrk(inPtr, " ");
2625 /* no ie data after the number of ie fields argument */
2626 if (NULL == inPtr)
2627 return -EINVAL;
2628
2629 /* remove empty space */
2630 while ((SPACE_ASCII_VALUE == *inPtr)
2631 && ('\0' != *inPtr))
2632 inPtr++;
2633
2634 /*
2635 * no ie data after the number of ie fields
2636 * argument and spaces
2637 */
2638 if ('\0' == *inPtr)
2639 return -EINVAL;
2640
2641 v = sscanf(inPtr, "%31s ", buf);
2642 if (1 != v)
2643 return -EINVAL;
2644
2645 v = kstrtos32(buf, 10, &tempInt);
2646 if (v < 0)
2647 return -EINVAL;
2648
2649 switch (i) {
2650 case 0: /* Measurement token */
2651 if (tempInt <= 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002652 hdd_err("Invalid Measurement Token(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002653 tempInt);
2654 return -EINVAL;
2655 }
2656 pEseBcnReq->bcnReq[j].measurementToken =
2657 tempInt;
2658 break;
2659
2660 case 1: /* Channel number */
2661 if ((tempInt <= 0) ||
2662 (tempInt >
2663 WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002664 hdd_err("Invalid Channel Number(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002665 tempInt);
2666 return -EINVAL;
2667 }
2668 pEseBcnReq->bcnReq[j].channel = tempInt;
2669 break;
2670
2671 case 2: /* Scan mode */
2672 if ((tempInt < eSIR_PASSIVE_SCAN)
2673 || (tempInt > eSIR_BEACON_TABLE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002674 hdd_err("Invalid Scan Mode(%d) Expected{0|1|2}",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002675 tempInt);
2676 return -EINVAL;
2677 }
2678 pEseBcnReq->bcnReq[j].scanMode = tempInt;
2679 break;
2680
2681 case 3: /* Measurement duration */
2682 if (((tempInt <= 0)
2683 && (pEseBcnReq->bcnReq[j].scanMode !=
2684 eSIR_BEACON_TABLE)) ||
2685 ((tempInt < 0) &&
2686 (pEseBcnReq->bcnReq[j].scanMode ==
2687 eSIR_BEACON_TABLE))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002688 hdd_err("Invalid Measurement Duration(%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002689 tempInt);
2690 return -EINVAL;
2691 }
2692 pEseBcnReq->bcnReq[j].measurementDuration =
2693 tempInt;
2694 break;
2695 }
2696 }
2697 }
2698
2699 for (j = 0; j < pEseBcnReq->numBcnReqIe; j++) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002700 hdd_info("Index(%d) Measurement Token(%u) Channel(%u) Scan Mode(%u) Measurement Duration(%u)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002701 j,
2702 pEseBcnReq->bcnReq[j].measurementToken,
2703 pEseBcnReq->bcnReq[j].channel,
2704 pEseBcnReq->bcnReq[j].scanMode,
2705 pEseBcnReq->bcnReq[j].measurementDuration);
2706 }
2707
2708 return 0;
2709}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002710
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002711/**
2712 * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
2713 * @pValue: Pointer to input data
2714 * @pCckmIe: Pointer to output cckm Ie
2715 * @pCckmIeLen: Pointer to output cckm ie length
2716 *
2717 * This function parses the SETCCKM IE command
2718 * SETCCKMIE<space><ie data>
2719 *
2720 * Return: 0 for success non-zero for failure
2721 */
2722static int hdd_parse_get_cckm_ie(uint8_t *pValue, uint8_t **pCckmIe,
2723 uint8_t *pCckmIeLen)
2724{
2725 uint8_t *inPtr = pValue;
2726 uint8_t *dataEnd;
2727 int j = 0;
2728 int i = 0;
2729 uint8_t tempByte = 0;
2730 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2731 /* no argument after the command */
2732 if (NULL == inPtr) {
2733 return -EINVAL;
2734 }
2735 /* no space after the command */
2736 else if (SPACE_ASCII_VALUE != *inPtr) {
2737 return -EINVAL;
2738 }
2739 /* remove empty spaces */
2740 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2741 inPtr++;
2742 /* no argument followed by spaces */
2743 if ('\0' == *inPtr) {
2744 return -EINVAL;
2745 }
2746 /* find the length of data */
2747 dataEnd = inPtr;
2748 while (('\0' != *dataEnd)) {
2749 dataEnd++;
2750 ++(*pCckmIeLen);
2751 }
2752 if (*pCckmIeLen <= 0)
2753 return -EINVAL;
2754 /*
2755 * Allocate the number of bytes based on the number of input characters
2756 * whether it is even or odd.
2757 * if the number of input characters are even, then we need N / 2 byte.
2758 * if the number of input characters are odd, then we need do
2759 * (N + 1) / 2 to compensate rounding off.
2760 * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough.
2761 * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes
2762 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302763 *pCckmIe = qdf_mem_malloc((*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002764 if (NULL == *pCckmIe) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002765 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002766 return -ENOMEM;
2767 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302768 qdf_mem_zero(*pCckmIe, (*pCckmIeLen + 1) / 2);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002769 /*
2770 * the buffer received from the upper layer is character buffer,
2771 * we need to prepare the buffer taking 2 characters in to a U8 hex
2772 * decimal number for example 7f0000f0...form a buffer to contain
2773 * 7f in 0th location, 00 in 1st and f0 in 3rd location
2774 */
2775 for (i = 0, j = 0; j < *pCckmIeLen; j += 2) {
2776 tempByte = (hex_to_bin(inPtr[j]) << 4) |
2777 (hex_to_bin(inPtr[j + 1]));
2778 (*pCckmIe)[i++] = tempByte;
2779 }
2780 *pCckmIeLen = i;
2781 return 0;
2782}
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08002783#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002784
2785int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate)
2786{
2787 tSirRateUpdateInd rateUpdate = {0};
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302788 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002789 hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
2790 struct hdd_config *pConfig = NULL;
2791
2792 if (pHddCtx == NULL) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002793 hdd_err("HDD context is null");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002794 return -EINVAL;
2795 }
Krunal Sonibe766b02016-03-10 13:00:44 -08002796 if ((QDF_IBSS_MODE != pAdapter->device_mode) &&
2797 (QDF_SAP_MODE != pAdapter->device_mode) &&
2798 (QDF_STA_MODE != pAdapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002799 hdd_err("Received SETMCRATE cmd in invalid mode %s(%d)",
2800 hdd_device_mode_to_string(pAdapter->device_mode),
2801 pAdapter->device_mode);
2802 hdd_err("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002803 return -EINVAL;
2804 }
2805 pConfig = pHddCtx->config;
2806 rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
2807 rateUpdate.dev_mode = pAdapter->device_mode;
2808 rateUpdate.mcastDataRate24GHz = targetRate;
2809 rateUpdate.mcastDataRate24GHzTxFlag = 1;
2810 rateUpdate.mcastDataRate5GHz = targetRate;
2811 rateUpdate.bcastDataRate = -1;
Anurag Chouhanc5548422016-02-24 18:33:27 +05302812 qdf_copy_macaddr(&rateUpdate.bssid, &pAdapter->macAddressCurrent);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002813 hdd_info("MC Target rate %d, mac = %pM, dev_mode %s(%d)",
2814 rateUpdate.mcastDataRate24GHz, rateUpdate.bssid.bytes,
2815 hdd_device_mode_to_string(pAdapter->device_mode),
2816 pAdapter->device_mode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002817 status = sme_send_rate_update_ind(pHddCtx->hHal, &rateUpdate);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302818 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002819 hdd_err("SETMCRATE failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002820 return -EFAULT;
2821 }
2822 return 0;
2823}
2824
2825static int drv_cmd_p2p_dev_addr(hdd_adapter_t *adapter,
2826 hdd_context_t *hdd_ctx,
2827 uint8_t *command,
2828 uint8_t command_len,
2829 hdd_priv_data_t *priv_data)
2830{
2831 int ret = 0;
2832
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302833 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002834 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
2835 adapter->sessionId,
2836 (unsigned)(*(hdd_ctx->p2pDeviceAddress.bytes + 2)
2837 << 24 | *(hdd_ctx->p2pDeviceAddress.bytes
2838 + 3) << 16 | *(hdd_ctx->
2839 p2pDeviceAddress.bytes + 4) << 8 |
2840 *(hdd_ctx->p2pDeviceAddress.bytes +
2841 5))));
2842
2843 if (copy_to_user(priv_data->buf, hdd_ctx->p2pDeviceAddress.bytes,
2844 sizeof(tSirMacAddr))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002845 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002846 ret = -EFAULT;
2847 }
2848
2849 return ret;
2850}
2851
2852/**
2853 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command
2854 * @adapter: Adapter on which the command was received
2855 * @hdd_ctx: HDD global context
2856 * @command: Entire driver command received from userspace
2857 * @command_len: Length of @command
2858 * @priv_data: Pointer to ioctl private data structure
2859 *
2860 * This is a trivial command hander function which simply forwards the
2861 * command to the actual command processor within the P2P module.
2862 *
2863 * Return: 0 on success, non-zero on failure
2864 */
2865static int drv_cmd_p2p_set_noa(hdd_adapter_t *adapter,
2866 hdd_context_t *hdd_ctx,
2867 uint8_t *command,
2868 uint8_t command_len,
2869 hdd_priv_data_t *priv_data)
2870{
2871 return hdd_set_p2p_noa(adapter->dev, command);
2872}
2873
2874/**
2875 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command
2876 * @adapter: Adapter on which the command was received
2877 * @hdd_ctx: HDD global context
2878 * @command: Entire driver command received from userspace
2879 * @command_len: Length of @command
2880 * @priv_data: Pointer to ioctl private data structure
2881 *
2882 * This is a trivial command hander function which simply forwards the
2883 * command to the actual command processor within the P2P module.
2884 *
2885 * Return: 0 on success, non-zero on failure
2886 */
2887static int drv_cmd_p2p_set_ps(hdd_adapter_t *adapter,
2888 hdd_context_t *hdd_ctx,
2889 uint8_t *command,
2890 uint8_t command_len,
2891 hdd_priv_data_t *priv_data)
2892{
2893 return hdd_set_p2p_opps(adapter->dev, command);
2894}
2895
2896static int drv_cmd_set_band(hdd_adapter_t *adapter,
2897 hdd_context_t *hdd_ctx,
2898 uint8_t *command,
2899 uint8_t command_len,
2900 hdd_priv_data_t *priv_data)
2901{
2902 int ret = 0;
2903
2904 uint8_t *ptr = command;
2905
2906 /* Change band request received */
2907
2908 /*
2909 * First 8 bytes will have "SETBAND " and
2910 * 9 byte will have band setting value
2911 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002912 hdd_info("SetBandCommand Info comm %s UL %d, TL %d",
2913 command, priv_data->used_len,
2914 priv_data->total_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002915
2916 /* Change band request received */
2917 ret = hdd_set_band_helper(adapter->dev, ptr);
2918
2919 return ret;
2920}
2921
2922static int drv_cmd_set_wmmps(hdd_adapter_t *adapter,
2923 hdd_context_t *hdd_ctx,
2924 uint8_t *command,
2925 uint8_t command_len,
2926 hdd_priv_data_t *priv_data)
2927{
2928 return hdd_wmmps_helper(adapter, command);
2929}
2930
2931static int drv_cmd_country(hdd_adapter_t *adapter,
2932 hdd_context_t *hdd_ctx,
2933 uint8_t *command,
2934 uint8_t command_len,
2935 hdd_priv_data_t *priv_data)
2936{
2937 int ret = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302938 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002939 unsigned long rc;
2940 char *country_code;
2941
2942 country_code = command + 8;
2943
2944 INIT_COMPLETION(adapter->change_country_code);
2945
2946 status = sme_change_country_code(hdd_ctx->hHal,
2947 wlan_hdd_change_country_code_callback,
2948 country_code,
2949 adapter,
2950 hdd_ctx->pcds_context,
2951 eSIR_TRUE,
2952 eSIR_TRUE);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302953 if (status == QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002954 rc = wait_for_completion_timeout(
2955 &adapter->change_country_code,
2956 msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
2957 if (!rc)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002958 hdd_err("SME while setting country code timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002959 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002960 hdd_err("SME Change Country code fail, status %d",
2961 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002962 ret = -EINVAL;
2963 }
2964
2965 return ret;
2966}
2967
2968static int drv_cmd_set_roam_trigger(hdd_adapter_t *adapter,
2969 hdd_context_t *hdd_ctx,
2970 uint8_t *command,
2971 uint8_t command_len,
2972 hdd_priv_data_t *priv_data)
2973{
2974 int ret = 0;
2975 uint8_t *value = command;
2976 int8_t rssi = 0;
2977 uint8_t lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302978 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002979
2980 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
2981 value = value + command_len + 1;
2982
2983 /* Convert the value from ascii to integer */
2984 ret = kstrtos8(value, 10, &rssi);
2985 if (ret < 0) {
2986 /*
2987 * If the input value is greater than max value of datatype,
2988 * then also kstrtou8 fails
2989 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07002990 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
2991 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
2992 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002993 ret = -EINVAL;
2994 goto exit;
2995 }
2996
2997 lookUpThreshold = abs(rssi);
2998
2999 if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN)
3000 || (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003001 hdd_err("Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003002 lookUpThreshold,
3003 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
3004 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
3005 ret = -EINVAL;
3006 goto exit;
3007 }
3008
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303009 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003010 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
3011 adapter->sessionId, lookUpThreshold));
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003012 hdd_info("Received Command to Set Roam trigger (Neighbor lookup threshold) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003013 lookUpThreshold);
3014
3015 hdd_ctx->config->nNeighborLookupRssiThreshold = lookUpThreshold;
3016 status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->hHal,
3017 adapter->sessionId,
3018 lookUpThreshold);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303019 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003020 hdd_err("Failed to set roam trigger, try again");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003021 ret = -EPERM;
3022 goto exit;
3023 }
3024
3025exit:
3026 return ret;
3027}
3028
3029static int drv_cmd_get_roam_trigger(hdd_adapter_t *adapter,
3030 hdd_context_t *hdd_ctx,
3031 uint8_t *command,
3032 uint8_t command_len,
3033 hdd_priv_data_t *priv_data)
3034{
3035 int ret = 0;
3036 uint8_t lookUpThreshold =
3037 sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->hHal);
3038 int rssi = (-1) * lookUpThreshold;
3039 char extra[32];
3040 uint8_t len = 0;
3041
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303042 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003043 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
3044 adapter->sessionId, lookUpThreshold));
3045
3046 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303047 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003048 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003049 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003050 ret = -EFAULT;
3051 }
3052
3053 return ret;
3054}
3055
3056static int drv_cmd_set_roam_scan_period(hdd_adapter_t *adapter,
3057 hdd_context_t *hdd_ctx,
3058 uint8_t *command,
3059 uint8_t command_len,
3060 hdd_priv_data_t *priv_data)
3061{
3062 int ret = 0;
3063 uint8_t *value = command;
3064 uint8_t roamScanPeriod = 0;
3065 uint16_t neighborEmptyScanRefreshPeriod =
3066 CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
3067
3068 /* input refresh period is in terms of seconds */
3069
3070 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
3071 value = value + command_len + 1;
3072
3073 /* Convert the value from ascii to integer */
3074 ret = kstrtou8(value, 10, &roamScanPeriod);
3075 if (ret < 0) {
3076 /*
3077 * If the input value is greater than max value of datatype,
3078 * then also kstrtou8 fails
3079 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003080 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
3081 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3082 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003083 ret = -EINVAL;
3084 goto exit;
3085 }
3086
3087 if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000))
3088 || (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003089 hdd_err("Roam scan period value %d is out of range (Min: %d Max: %d)",
3090 roamScanPeriod,
3091 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
3092 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003093 ret = -EINVAL;
3094 goto exit;
3095 }
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303096 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003097 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
3098 adapter->sessionId, roamScanPeriod));
3099 neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
3100
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003101 hdd_info("Received Command to Set roam scan period (Empty Scan refresh period) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003102 roamScanPeriod);
3103
3104 hdd_ctx->config->nEmptyScanRefreshPeriod =
3105 neighborEmptyScanRefreshPeriod;
3106 sme_update_empty_scan_refresh_period(hdd_ctx->hHal,
3107 adapter->sessionId,
3108 neighborEmptyScanRefreshPeriod);
3109
3110exit:
3111 return ret;
3112}
3113
3114static int drv_cmd_get_roam_scan_period(hdd_adapter_t *adapter,
3115 hdd_context_t *hdd_ctx,
3116 uint8_t *command,
3117 uint8_t command_len,
3118 hdd_priv_data_t *priv_data)
3119{
3120 int ret = 0;
3121 uint16_t nEmptyScanRefreshPeriod =
3122 sme_get_empty_scan_refresh_period(hdd_ctx->hHal);
3123 char extra[32];
3124 uint8_t len = 0;
3125
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303126 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003127 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
3128 adapter->sessionId,
3129 nEmptyScanRefreshPeriod));
3130 len = scnprintf(extra, sizeof(extra), "%s %d",
3131 "GETROAMSCANPERIOD",
3132 (nEmptyScanRefreshPeriod / 1000));
3133 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303134 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003135 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003136 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003137 ret = -EFAULT;
3138 }
3139
3140 return ret;
3141}
3142
3143static int drv_cmd_set_roam_scan_refresh_period(hdd_adapter_t *adapter,
3144 hdd_context_t *hdd_ctx,
3145 uint8_t *command,
3146 uint8_t command_len,
3147 hdd_priv_data_t *priv_data)
3148{
3149 int ret = 0;
3150 uint8_t *value = command;
3151 uint8_t roamScanRefreshPeriod = 0;
3152 uint16_t neighborScanRefreshPeriod =
3153 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
3154
3155 /* input refresh period is in terms of seconds */
3156 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
3157 value = value + command_len + 1;
3158
3159 /* Convert the value from ascii to integer */
3160 ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
3161 if (ret < 0) {
3162 /*
3163 * If the input value is greater than max value of datatype,
3164 * then also kstrtou8 fails
3165 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003166 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]",
3167 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000,
3168 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003169 ret = -EINVAL;
3170 goto exit;
3171 }
3172
3173 if ((roamScanRefreshPeriod <
3174 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000))
3175 || (roamScanRefreshPeriod >
3176 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003177 hdd_err("Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)",
3178 roamScanRefreshPeriod,
3179 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN
3180 / 1000),
3181 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX
3182 / 1000));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003183 ret = -EINVAL;
3184 goto exit;
3185 }
3186 neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
3187
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003188 hdd_info("Received Command to Set roam scan refresh period (Scan refresh period) = %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003189 roamScanRefreshPeriod);
3190
3191 hdd_ctx->config->nNeighborResultsRefreshPeriod =
3192 neighborScanRefreshPeriod;
3193 sme_set_neighbor_scan_refresh_period(hdd_ctx->hHal,
3194 adapter->sessionId,
3195 neighborScanRefreshPeriod);
3196
3197exit:
3198 return ret;
3199}
3200
3201static int drv_cmd_get_roam_scan_refresh_period(hdd_adapter_t *adapter,
3202 hdd_context_t *hdd_ctx,
3203 uint8_t *command,
3204 uint8_t command_len,
3205 hdd_priv_data_t *priv_data)
3206{
3207 int ret = 0;
3208 uint16_t value =
3209 sme_get_neighbor_scan_refresh_period(hdd_ctx->hHal);
3210 char extra[32];
3211 uint8_t len = 0;
3212
3213 len = scnprintf(extra, sizeof(extra), "%s %d",
3214 "GETROAMSCANREFRESHPERIOD",
3215 (value / 1000));
3216 /* Returned value is in units of seconds */
Anurag Chouhan6d760662016-02-20 16:05:43 +05303217 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003218 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003219 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003220 ret = -EFAULT;
3221 }
3222
3223 return ret;
3224}
3225
3226static int drv_cmd_set_roam_mode(hdd_adapter_t *adapter,
3227 hdd_context_t *hdd_ctx,
3228 uint8_t *command,
3229 uint8_t command_len,
3230 hdd_priv_data_t *priv_data)
3231{
3232 int ret = 0;
3233 uint8_t *value = command;
3234 uint8_t roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
3235
3236 /* Move pointer to ahead of SETROAMMODE<delimiter> */
3237 value = value + SIZE_OF_SETROAMMODE + 1;
3238
3239 /* Convert the value from ascii to integer */
3240 ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
3241 if (ret < 0) {
3242 /*
3243 * If the input value is greater than max value of datatype,
3244 * then also kstrtou8 fails
3245 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003246 hdd_err("kstrtou8 failed range [%d - %d]",
3247 CFG_LFR_FEATURE_ENABLED_MIN,
3248 CFG_LFR_FEATURE_ENABLED_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003249 ret = -EINVAL;
3250 goto exit;
3251 }
3252 if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
3253 (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003254 hdd_err("Roam Mode value %d is out of range (Min: %d Max: %d)",
3255 roamMode,
3256 CFG_LFR_FEATURE_ENABLED_MIN,
3257 CFG_LFR_FEATURE_ENABLED_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003258 ret = -EINVAL;
3259 goto exit;
3260 }
3261
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003262 hdd_debug("Received Command to Set Roam Mode = %d",
3263 roamMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003264 /*
3265 * Note that
3266 * SETROAMMODE 0 is to enable LFR while
3267 * SETROAMMODE 1 is to disable LFR, but
3268 * notify_is_fast_roam_ini_feature_enabled 0/1 is to
3269 * enable/disable. So, we have to invert the value
3270 * to call sme_update_is_fast_roam_ini_feature_enabled.
3271 */
3272 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3273 roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
3274 else
3275 roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
3276
3277 hdd_ctx->config->isFastRoamIniFeatureEnabled = roamMode;
3278 if (roamMode) {
3279 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3280 sme_update_roam_scan_offload_enabled(
3281 (tHalHandle)(hdd_ctx->hHal),
3282 hdd_ctx->config->isRoamOffloadScanEnabled);
3283 sme_update_is_fast_roam_ini_feature_enabled(
3284 hdd_ctx->hHal,
3285 adapter->sessionId,
3286 roamMode);
3287 } else {
3288 sme_update_is_fast_roam_ini_feature_enabled(
3289 hdd_ctx->hHal,
3290 adapter->sessionId,
3291 roamMode);
3292 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
3293 sme_update_roam_scan_offload_enabled(
3294 (tHalHandle)(hdd_ctx->hHal),
3295 hdd_ctx->config->isRoamOffloadScanEnabled);
3296 }
3297
3298
3299exit:
3300 return ret;
3301}
3302
3303static int drv_cmd_get_roam_mode(hdd_adapter_t *adapter,
3304 hdd_context_t *hdd_ctx,
3305 uint8_t *command,
3306 uint8_t command_len,
3307 hdd_priv_data_t *priv_data)
3308{
3309 int ret = 0;
3310 bool roamMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3311 char extra[32];
3312 uint8_t len = 0;
3313
3314 /*
3315 * roamMode value shall be inverted because the sementics is different.
3316 */
3317 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
3318 roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
3319 else
3320 roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
3321
3322 len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303323 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003324 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003325 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003326 ret = -EFAULT;
3327 }
3328
3329 return ret;
3330}
3331
3332static int drv_cmd_set_roam_delta(hdd_adapter_t *adapter,
3333 hdd_context_t *hdd_ctx,
3334 uint8_t *command,
3335 uint8_t command_len,
3336 hdd_priv_data_t *priv_data)
3337{
3338 int ret = 0;
3339 uint8_t *value = command;
3340 uint8_t roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
3341
3342 /* Move pointer to ahead of SETROAMDELTA<delimiter> */
3343 value = value + command_len + 1;
3344
3345 /* Convert the value from ascii to integer */
3346 ret = kstrtou8(value, 10, &roamRssiDiff);
3347 if (ret < 0) {
3348 /*
3349 * If the input value is greater than max value of datatype,
3350 * then also kstrtou8 fails
3351 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003352 hdd_err("kstrtou8 failed range [%d - %d]",
3353 CFG_ROAM_RSSI_DIFF_MIN,
3354 CFG_ROAM_RSSI_DIFF_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003355 ret = -EINVAL;
3356 goto exit;
3357 }
3358
3359 if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
3360 (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003361 hdd_err("Roam rssi diff value %d is out of range (Min: %d Max: %d)",
3362 roamRssiDiff,
3363 CFG_ROAM_RSSI_DIFF_MIN,
3364 CFG_ROAM_RSSI_DIFF_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003365 ret = -EINVAL;
3366 goto exit;
3367 }
3368
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003369 hdd_info("Received Command to Set roam rssi diff = %d",
3370 roamRssiDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003371
3372 hdd_ctx->config->RoamRssiDiff = roamRssiDiff;
3373 sme_update_roam_rssi_diff(hdd_ctx->hHal,
3374 adapter->sessionId,
3375 roamRssiDiff);
3376
3377exit:
3378 return ret;
3379}
3380
3381static int drv_cmd_get_roam_delta(hdd_adapter_t *adapter,
3382 hdd_context_t *hdd_ctx,
3383 uint8_t *command,
3384 uint8_t command_len,
3385 hdd_priv_data_t *priv_data)
3386{
3387 int ret = 0;
3388 uint8_t roamRssiDiff =
3389 sme_get_roam_rssi_diff(hdd_ctx->hHal);
3390 char extra[32];
3391 uint8_t len = 0;
3392
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303393 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003394 TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
3395 adapter->sessionId, roamRssiDiff));
3396
3397 len = scnprintf(extra, sizeof(extra), "%s %d",
3398 command, roamRssiDiff);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303399 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003400
3401 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003402 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003403 ret = -EFAULT;
3404 }
3405
3406 return ret;
3407}
3408
3409static int drv_cmd_get_band(hdd_adapter_t *adapter,
3410 hdd_context_t *hdd_ctx,
3411 uint8_t *command,
3412 uint8_t command_len,
3413 hdd_priv_data_t *priv_data)
3414{
3415 int ret = 0;
3416 int band = -1;
3417 char extra[32];
3418 uint8_t len = 0;
3419
3420 hdd_get_band_helper(hdd_ctx, &band);
3421
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303422 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003423 TRACE_CODE_HDD_GETBAND_IOCTL,
3424 adapter->sessionId, band));
3425
3426 len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303427 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003428
3429 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003430 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003431 ret = -EFAULT;
3432 }
3433
3434 return ret;
3435}
3436
3437static int drv_cmd_set_roam_scan_channels(hdd_adapter_t *adapter,
3438 hdd_context_t *hdd_ctx,
3439 uint8_t *command,
3440 uint8_t command_len,
3441 hdd_priv_data_t *priv_data)
3442{
3443 return hdd_parse_set_roam_scan_channels(adapter, command);
3444}
3445
3446static int drv_cmd_get_roam_scan_channels(hdd_adapter_t *adapter,
3447 hdd_context_t *hdd_ctx,
3448 uint8_t *command,
3449 uint8_t command_len,
3450 hdd_priv_data_t *priv_data)
3451{
3452 int ret = 0;
3453 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
3454 uint8_t numChannels = 0;
3455 uint8_t j = 0;
3456 char extra[128] = { 0 };
3457 int len;
3458
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303459 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003460 sme_get_roam_scan_channel_list(hdd_ctx->hHal,
3461 ChannelList,
3462 &numChannels,
3463 adapter->sessionId)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003464 hdd_alert("failed to get roam scan channel list");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003465 ret = -EFAULT;
3466 goto exit;
3467 }
3468
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303469 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003470 TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
3471 adapter->sessionId, numChannels));
3472 /*
3473 * output channel list is of the format
3474 * [Number of roam scan channels][Channel1][Channel2]...
3475 * copy the number of channels in the 0th index
3476 */
3477 len = scnprintf(extra, sizeof(extra), "%s %d", command,
3478 numChannels);
3479 for (j = 0; (j < numChannels); j++)
3480 len += scnprintf(extra + len, sizeof(extra) - len,
3481 " %d", ChannelList[j]);
3482
Anurag Chouhan6d760662016-02-20 16:05:43 +05303483 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003484 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003485 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003486 ret = -EFAULT;
3487 goto exit;
3488 }
3489
3490exit:
3491 return ret;
3492}
3493
3494static int drv_cmd_get_ccx_mode(hdd_adapter_t *adapter,
3495 hdd_context_t *hdd_ctx,
3496 uint8_t *command,
3497 uint8_t command_len,
3498 hdd_priv_data_t *priv_data)
3499{
3500 int ret = 0;
3501 bool eseMode = sme_get_is_ese_feature_enabled(hdd_ctx->hHal);
3502 char extra[32];
3503 uint8_t len = 0;
3504
3505 /*
3506 * Check if the features OKC/ESE/11R are supported simultaneously,
3507 * then this operation is not permitted (return FAILURE)
3508 */
3509 if (eseMode &&
3510 hdd_is_okc_mode_enabled(hdd_ctx) &&
3511 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003512 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003513 ret = -EPERM;
3514 goto exit;
3515 }
3516
3517 len = scnprintf(extra, sizeof(extra), "%s %d",
3518 "GETCCXMODE", eseMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303519 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003520 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003521 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003522 ret = -EFAULT;
3523 goto exit;
3524 }
3525
3526exit:
3527 return ret;
3528}
3529
3530static int drv_cmd_get_okc_mode(hdd_adapter_t *adapter,
3531 hdd_context_t *hdd_ctx,
3532 uint8_t *command,
3533 uint8_t command_len,
3534 hdd_priv_data_t *priv_data)
3535{
3536 int ret = 0;
3537 bool okcMode = hdd_is_okc_mode_enabled(hdd_ctx);
3538 char extra[32];
3539 uint8_t len = 0;
3540
3541 /*
3542 * Check if the features OKC/ESE/11R are supported simultaneously,
3543 * then this operation is not permitted (return FAILURE)
3544 */
3545 if (okcMode &&
3546 sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
3547 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003548 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003549 ret = -EPERM;
3550 goto exit;
3551 }
3552
3553 len = scnprintf(extra, sizeof(extra), "%s %d",
3554 "GETOKCMODE", okcMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303555 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003556
3557 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003558 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003559 ret = -EFAULT;
3560 goto exit;
3561 }
3562
3563exit:
3564 return ret;
3565}
3566
3567static int drv_cmd_get_fast_roam(hdd_adapter_t *adapter,
3568 hdd_context_t *hdd_ctx,
3569 uint8_t *command,
3570 uint8_t command_len,
3571 hdd_priv_data_t *priv_data)
3572{
3573 int ret = 0;
3574 bool lfrMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3575 char extra[32];
3576 uint8_t len = 0;
3577
3578 len = scnprintf(extra, sizeof(extra), "%s %d",
3579 "GETFASTROAM", lfrMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303580 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003581
3582 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003583 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003584 ret = -EFAULT;
3585 }
3586
3587 return ret;
3588}
3589
3590static int drv_cmd_get_fast_transition(hdd_adapter_t *adapter,
3591 hdd_context_t *hdd_ctx,
3592 uint8_t *command,
3593 uint8_t command_len,
3594 hdd_priv_data_t *priv_data)
3595{
3596 int ret = 0;
3597 bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->hHal);
3598 char extra[32];
3599 uint8_t len = 0;
3600
3601 len = scnprintf(extra, sizeof(extra), "%s %d",
3602 "GETFASTTRANSITION", ft);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303603 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003604
3605 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003606 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003607 ret = -EFAULT;
3608 }
3609
3610 return ret;
3611}
3612
3613static int drv_cmd_set_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3614 hdd_context_t *hdd_ctx,
3615 uint8_t *command,
3616 uint8_t command_len,
3617 hdd_priv_data_t *priv_data)
3618{
3619 int ret = 0;
3620 uint8_t *value = command;
3621 uint8_t minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
3622
3623 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
3624 value = value + command_len + 1;
3625
3626 /* Convert the value from ascii to integer */
3627 ret = kstrtou8(value, 10, &minTime);
3628 if (ret < 0) {
3629 /*
3630 * If the input value is greater than max value of datatype,
3631 * then also kstrtou8 fails
3632 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003633 hdd_err("kstrtou8 failed range [%d - %d]",
3634 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3635 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003636 ret = -EINVAL;
3637 goto exit;
3638 }
3639
3640 if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
3641 (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003642 hdd_err("scan min channel time value %d is out of range (Min: %d Max: %d)",
3643 minTime,
3644 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3645 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003646 ret = -EINVAL;
3647 goto exit;
3648 }
3649
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303650 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003651 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
3652 adapter->sessionId, minTime));
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003653 hdd_info("Received Command to change channel min time = %d",
3654 minTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003655
3656 hdd_ctx->config->nNeighborScanMinChanTime = minTime;
3657 sme_set_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3658 minTime,
3659 adapter->sessionId);
3660
3661exit:
3662 return ret;
3663}
3664
3665static int drv_cmd_send_action_frame(hdd_adapter_t *adapter,
3666 hdd_context_t *hdd_ctx,
3667 uint8_t *command,
3668 uint8_t command_len,
3669 hdd_priv_data_t *priv_data)
3670{
3671 return hdd_parse_sendactionframe(adapter, command);
3672}
3673
3674static int drv_cmd_get_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3675 hdd_context_t *hdd_ctx,
3676 uint8_t *command,
3677 uint8_t command_len,
3678 hdd_priv_data_t *priv_data)
3679{
3680 int ret = 0;
3681 uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3682 adapter->sessionId);
3683 char extra[32];
3684 uint8_t len = 0;
3685
3686 /* value is interms of msec */
3687 len = scnprintf(extra, sizeof(extra), "%s %d",
3688 "GETROAMSCANCHANNELMINTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303689 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003690
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_GETROAMSCANCHANNELMINTIME_IOCTL,
3693 adapter->sessionId, val));
3694
3695 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003696 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003697 ret = -EFAULT;
3698 }
3699
3700 return ret;
3701}
3702
3703static int drv_cmd_set_scan_channel_time(hdd_adapter_t *adapter,
3704 hdd_context_t *hdd_ctx,
3705 uint8_t *command,
3706 uint8_t command_len,
3707 hdd_priv_data_t *priv_data)
3708{
3709 int ret = 0;
3710 uint8_t *value = command;
3711 uint16_t maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
3712
3713 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
3714 value = value + command_len + 1;
3715
3716 /* Convert the value from ascii to integer */
3717 ret = kstrtou16(value, 10, &maxTime);
3718 if (ret < 0) {
3719 /*
3720 * If the input value is greater than max value of datatype,
3721 * then also kstrtou8 fails
3722 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003723 hdd_err("kstrtou16 failed range [%d - %d]",
3724 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3725 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003726 ret = -EINVAL;
3727 goto exit;
3728 }
3729
3730 if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
3731 (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003732 hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)",
3733 maxTime,
3734 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3735 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003736 ret = -EINVAL;
3737 goto exit;
3738 }
3739
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003740 hdd_info("Received Command to change channel max time = %d",
3741 maxTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003742
3743 hdd_ctx->config->nNeighborScanMaxChanTime = maxTime;
3744 sme_set_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3745 adapter->sessionId,
3746 maxTime);
3747
3748exit:
3749 return ret;
3750}
3751
3752static int drv_cmd_get_scan_channel_time(hdd_adapter_t *adapter,
3753 hdd_context_t *hdd_ctx,
3754 uint8_t *command,
3755 uint8_t command_len,
3756 hdd_priv_data_t *priv_data)
3757{
3758 int ret = 0;
3759 uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3760 adapter->sessionId);
3761 char extra[32];
3762 uint8_t len = 0;
3763
3764 /* value is interms of msec */
3765 len = scnprintf(extra, sizeof(extra), "%s %d",
3766 "GETSCANCHANNELTIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303767 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003768
3769 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003770 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003771 ret = -EFAULT;
3772 }
3773
3774 return ret;
3775}
3776
3777static int drv_cmd_set_scan_home_time(hdd_adapter_t *adapter,
3778 hdd_context_t *hdd_ctx,
3779 uint8_t *command,
3780 uint8_t command_len,
3781 hdd_priv_data_t *priv_data)
3782{
3783 int ret = 0;
3784 uint8_t *value = command;
3785 uint16_t val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
3786
3787 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
3788 value = value + command_len + 1;
3789
3790 /* Convert the value from ascii to integer */
3791 ret = kstrtou16(value, 10, &val);
3792 if (ret < 0) {
3793 /*
3794 * If the input value is greater than max value of datatype,
3795 * then also kstrtou8 fails
3796 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003797 hdd_err("kstrtou16 failed range [%d - %d]",
3798 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3799 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003800 ret = -EINVAL;
3801 goto exit;
3802 }
3803
3804 if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
3805 (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003806 hdd_err("scan home time value %d is out of range (Min: %d Max: %d)",
3807 val,
3808 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3809 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003810 ret = -EINVAL;
3811 goto exit;
3812 }
3813
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003814 hdd_info("Received Command to change scan home time = %d",
3815 val);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003816
3817 hdd_ctx->config->nNeighborScanPeriod = val;
3818 sme_set_neighbor_scan_period(hdd_ctx->hHal,
3819 adapter->sessionId, val);
3820
3821exit:
3822 return ret;
3823}
3824
3825static int drv_cmd_get_scan_home_time(hdd_adapter_t *adapter,
3826 hdd_context_t *hdd_ctx,
3827 uint8_t *command,
3828 uint8_t command_len,
3829 hdd_priv_data_t *priv_data)
3830{
3831 int ret = 0;
3832 uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->hHal,
3833 adapter->
3834 sessionId);
3835 char extra[32];
3836 uint8_t len = 0;
3837
3838 /* value is interms of msec */
3839 len = scnprintf(extra, sizeof(extra), "%s %d",
3840 "GETSCANHOMETIME", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303841 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003842
3843 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003844 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003845 ret = -EFAULT;
3846 }
3847
3848 return ret;
3849}
3850
3851static int drv_cmd_set_roam_intra_band(hdd_adapter_t *adapter,
3852 hdd_context_t *hdd_ctx,
3853 uint8_t *command,
3854 uint8_t command_len,
3855 hdd_priv_data_t *priv_data)
3856{
3857 int ret = 0;
3858 uint8_t *value = command;
3859 uint8_t val = CFG_ROAM_INTRA_BAND_DEFAULT;
3860
3861 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
3862 value = value + command_len + 1;
3863
3864 /* Convert the value from ascii to integer */
3865 ret = kstrtou8(value, 10, &val);
3866 if (ret < 0) {
3867 /*
3868 * If the input value is greater than max value of datatype,
3869 * then also kstrtou8 fails
3870 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003871 hdd_err("kstrtou8 failed range [%d - %d]",
3872 CFG_ROAM_INTRA_BAND_MIN,
3873 CFG_ROAM_INTRA_BAND_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003874 ret = -EINVAL;
3875 goto exit;
3876 }
3877
3878 if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
3879 (val > CFG_ROAM_INTRA_BAND_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003880 hdd_err("intra band mode value %d is out of range (Min: %d Max: %d)",
3881 val,
3882 CFG_ROAM_INTRA_BAND_MIN,
3883 CFG_ROAM_INTRA_BAND_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003884 ret = -EINVAL;
3885 goto exit;
3886 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003887 hdd_info("Received Command to change intra band = %d",
3888 val);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003889
3890 hdd_ctx->config->nRoamIntraBand = val;
3891 sme_set_roam_intra_band(hdd_ctx->hHal, val);
3892
3893exit:
3894 return ret;
3895}
3896
3897static int drv_cmd_get_roam_intra_band(hdd_adapter_t *adapter,
3898 hdd_context_t *hdd_ctx,
3899 uint8_t *command,
3900 uint8_t command_len,
3901 hdd_priv_data_t *priv_data)
3902{
3903 int ret = 0;
3904 uint16_t val = sme_get_roam_intra_band(hdd_ctx->hHal);
3905 char extra[32];
3906 uint8_t len = 0;
3907
3908 /* value is interms of msec */
3909 len = scnprintf(extra, sizeof(extra), "%s %d",
3910 "GETROAMINTRABAND", val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303911 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003912 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003913 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003914 ret = -EFAULT;
3915 }
3916
3917 return ret;
3918}
3919
3920static int drv_cmd_set_scan_n_probes(hdd_adapter_t *adapter,
3921 hdd_context_t *hdd_ctx,
3922 uint8_t *command,
3923 uint8_t command_len,
3924 hdd_priv_data_t *priv_data)
3925{
3926 int ret = 0;
3927 uint8_t *value = command;
3928 uint8_t nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
3929
3930 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */
3931 value = value + command_len + 1;
3932
3933 /* Convert the value from ascii to integer */
3934 ret = kstrtou8(value, 10, &nProbes);
3935 if (ret < 0) {
3936 /*
3937 * If the input value is greater than max value of datatype,
3938 * then also kstrtou8 fails
3939 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003940 hdd_err("kstrtou8 failed range [%d - %d]",
3941 CFG_ROAM_SCAN_N_PROBES_MIN,
3942 CFG_ROAM_SCAN_N_PROBES_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003943 ret = -EINVAL;
3944 goto exit;
3945 }
3946
3947 if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
3948 (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003949 hdd_err("NProbes value %d is out of range (Min: %d Max: %d)",
3950 nProbes,
3951 CFG_ROAM_SCAN_N_PROBES_MIN,
3952 CFG_ROAM_SCAN_N_PROBES_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003953 ret = -EINVAL;
3954 goto exit;
3955 }
3956
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003957 hdd_info("Received Command to Set nProbes = %d",
3958 nProbes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003959
3960 hdd_ctx->config->nProbes = nProbes;
3961 sme_update_roam_scan_n_probes(hdd_ctx->hHal,
3962 adapter->sessionId, nProbes);
3963
3964exit:
3965 return ret;
3966}
3967
3968static int drv_cmd_get_scan_n_probes(hdd_adapter_t *adapter,
3969 hdd_context_t *hdd_ctx,
3970 uint8_t *command,
3971 uint8_t command_len,
3972 hdd_priv_data_t *priv_data)
3973{
3974 int ret = 0;
3975 uint8_t val = sme_get_roam_scan_n_probes(hdd_ctx->hHal);
3976 char extra[32];
3977 uint8_t len = 0;
3978
3979 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05303980 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003981 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07003982 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003983 ret = -EFAULT;
3984 }
3985
3986 return ret;
3987}
3988
3989static int drv_cmd_set_scan_home_away_time(hdd_adapter_t *adapter,
3990 hdd_context_t *hdd_ctx,
3991 uint8_t *command,
3992 uint8_t command_len,
3993 hdd_priv_data_t *priv_data)
3994{
3995 int ret = 0;
3996 uint8_t *value = command;
3997 uint16_t homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
3998
3999 /* input value is in units of msec */
4000
4001 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
4002 value = value + command_len + 1;
4003
4004 /* Convert the value from ascii to integer */
4005 ret = kstrtou16(value, 10, &homeAwayTime);
4006 if (ret < 0) {
4007 /*
4008 * If the input value is greater than max value of datatype,
4009 * then also kstrtou8 fails
4010 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004011 hdd_err("kstrtou8 failed range [%d - %d]",
4012 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4013 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004014 ret = -EINVAL;
4015 goto exit;
4016 }
4017
4018 if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
4019 (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004020 hdd_err("homeAwayTime value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004021 homeAwayTime,
4022 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
4023 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
4024 ret = -EINVAL;
4025 goto exit;
4026 }
4027
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004028 hdd_info("Received Command to Set scan away time = %d",
4029 homeAwayTime);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004030
4031 if (hdd_ctx->config->nRoamScanHomeAwayTime !=
4032 homeAwayTime) {
4033 hdd_ctx->config->nRoamScanHomeAwayTime = homeAwayTime;
4034 sme_update_roam_scan_home_away_time(hdd_ctx->hHal,
4035 adapter->sessionId,
4036 homeAwayTime,
4037 true);
4038 }
4039
4040exit:
4041 return ret;
4042}
4043
4044static int drv_cmd_get_scan_home_away_time(hdd_adapter_t *adapter,
4045 hdd_context_t *hdd_ctx,
4046 uint8_t *command,
4047 uint8_t command_len,
4048 hdd_priv_data_t *priv_data)
4049{
4050 int ret = 0;
4051 uint16_t val = sme_get_roam_scan_home_away_time(hdd_ctx->hHal);
4052 char extra[32];
4053 uint8_t len = 0;
4054
4055 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304056 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004057
4058 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004059 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004060 ret = -EFAULT;
4061 }
4062
4063 return ret;
4064}
4065
4066static int drv_cmd_reassoc(hdd_adapter_t *adapter,
4067 hdd_context_t *hdd_ctx,
4068 uint8_t *command,
4069 uint8_t command_len,
4070 hdd_priv_data_t *priv_data)
4071{
4072 return hdd_parse_reassoc(adapter, command);
4073}
4074
4075static int drv_cmd_set_wes_mode(hdd_adapter_t *adapter,
4076 hdd_context_t *hdd_ctx,
4077 uint8_t *command,
4078 uint8_t command_len,
4079 hdd_priv_data_t *priv_data)
4080{
4081 int ret = 0;
4082 uint8_t *value = command;
4083 uint8_t wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
4084
4085 /* Move pointer to ahead of SETWESMODE<delimiter> */
4086 value = value + command_len + 1;
4087
4088 /* Convert the value from ascii to integer */
4089 ret = kstrtou8(value, 10, &wesMode);
4090 if (ret < 0) {
4091 /*
4092 * If the input value is greater than max value of datatype,
4093 * then also kstrtou8 fails
4094 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004095 hdd_err("kstrtou8 failed range [%d - %d]",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004096 CFG_ENABLE_WES_MODE_NAME_MIN,
4097 CFG_ENABLE_WES_MODE_NAME_MAX);
4098 ret = -EINVAL;
4099 goto exit;
4100 }
4101
4102 if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
4103 (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004104 hdd_err("WES Mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004105 wesMode,
4106 CFG_ENABLE_WES_MODE_NAME_MIN,
4107 CFG_ENABLE_WES_MODE_NAME_MAX);
4108 ret = -EINVAL;
4109 goto exit;
4110 }
4111
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004112 hdd_info("Received Command to Set WES Mode rssi diff = %d",
4113 wesMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004114
4115 hdd_ctx->config->isWESModeEnabled = wesMode;
4116 sme_update_wes_mode(hdd_ctx->hHal, wesMode, adapter->sessionId);
4117
4118exit:
4119 return ret;
4120}
4121
4122static int drv_cmd_get_wes_mode(hdd_adapter_t *adapter,
4123 hdd_context_t *hdd_ctx,
4124 uint8_t *command,
4125 uint8_t command_len,
4126 hdd_priv_data_t *priv_data)
4127{
4128 int ret = 0;
4129 bool wesMode = sme_get_wes_mode(hdd_ctx->hHal);
4130 char extra[32];
4131 uint8_t len = 0;
4132
4133 len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304134 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004135 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004136 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004137 ret = -EFAULT;
4138 }
4139
4140 return ret;
4141}
4142
4143static int drv_cmd_set_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4144 hdd_context_t *hdd_ctx,
4145 uint8_t *command,
4146 uint8_t command_len,
4147 hdd_priv_data_t *priv_data)
4148{
4149 int ret = 0;
4150 uint8_t *value = command;
4151 uint8_t nOpportunisticThresholdDiff =
4152 CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
4153
4154 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
4155 value = value + command_len + 1;
4156
4157 /* Convert the value from ascii to integer */
4158 ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
4159 if (ret < 0) {
4160 /*
4161 * If the input value is greater than max value of datatype,
4162 * then also kstrtou8 fails
4163 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004164 hdd_err("kstrtou8 failed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004165 ret = -EINVAL;
4166 goto exit;
4167 }
4168
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004169 hdd_info("Received Command to Set Opportunistic Threshold diff = %d",
4170 nOpportunisticThresholdDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004171
4172 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->hHal,
4173 adapter->sessionId,
4174 nOpportunisticThresholdDiff);
4175
4176exit:
4177 return ret;
4178}
4179
4180static int drv_cmd_get_opportunistic_rssi_diff(hdd_adapter_t *adapter,
4181 hdd_context_t *hdd_ctx,
4182 uint8_t *command,
4183 uint8_t command_len,
4184 hdd_priv_data_t *priv_data)
4185{
4186 int ret = 0;
4187 int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(
4188 hdd_ctx->hHal);
4189 char extra[32];
4190 uint8_t len = 0;
4191
4192 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304193 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004194 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004195 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004196 ret = -EFAULT;
4197 }
4198
4199 return ret;
4200}
4201
4202static int drv_cmd_set_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4203 hdd_context_t *hdd_ctx,
4204 uint8_t *command,
4205 uint8_t command_len,
4206 hdd_priv_data_t *priv_data)
4207{
4208 int ret = 0;
4209 uint8_t *value = command;
4210 uint8_t nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
4211
4212 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
4213 value = value + command_len + 1;
4214
4215 /* Convert the value from ascii to integer */
4216 ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
4217 if (ret < 0) {
4218 /*
4219 * If the input value is greater than max value of datatype,
4220 * then also kstrtou8 fails
4221 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004222 hdd_err("kstrtou8 failed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004223 ret = -EINVAL;
4224 goto exit;
4225 }
4226
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004227 hdd_info("Received Command to Set Roam Rescan RSSI Diff = %d",
4228 nRoamRescanRssiDiff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004229
4230 sme_set_roam_rescan_rssi_diff(hdd_ctx->hHal,
4231 adapter->sessionId,
4232 nRoamRescanRssiDiff);
4233
4234exit:
4235 return ret;
4236}
4237
4238static int drv_cmd_get_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4239 hdd_context_t *hdd_ctx,
4240 uint8_t *command,
4241 uint8_t command_len,
4242 hdd_priv_data_t *priv_data)
4243{
4244 int ret = 0;
4245 uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->hHal);
4246 char extra[32];
4247 uint8_t len = 0;
4248
4249 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304250 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004251 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004252 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004253 ret = -EFAULT;
4254 }
4255
4256 return ret;
4257}
4258
4259static int drv_cmd_set_fast_roam(hdd_adapter_t *adapter,
4260 hdd_context_t *hdd_ctx,
4261 uint8_t *command,
4262 uint8_t command_len,
4263 hdd_priv_data_t *priv_data)
4264{
4265 int ret = 0;
4266 uint8_t *value = command;
4267 uint8_t lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
4268
4269 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4270 value = value + command_len + 1;
4271
4272 /* Convert the value from ascii to integer */
4273 ret = kstrtou8(value, 10, &lfrMode);
4274 if (ret < 0) {
4275 /*
4276 * If the input value is greater than max value of datatype,
4277 * then also kstrtou8 fails
4278 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004279 hdd_err("kstrtou8 failed range [%d - %d]",
4280 CFG_LFR_FEATURE_ENABLED_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004281 CFG_LFR_FEATURE_ENABLED_MAX);
4282 ret = -EINVAL;
4283 goto exit;
4284 }
4285
4286 if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
4287 (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004288 hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004289 lfrMode,
4290 CFG_LFR_FEATURE_ENABLED_MIN,
4291 CFG_LFR_FEATURE_ENABLED_MAX);
4292 ret = -EINVAL;
4293 goto exit;
4294 }
4295
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004296 hdd_info("Received Command to change lfr mode = %d",
4297 lfrMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004298
4299 hdd_ctx->config->isFastRoamIniFeatureEnabled = lfrMode;
4300 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->hHal,
4301 adapter->
4302 sessionId,
4303 lfrMode);
4304
4305exit:
4306 return ret;
4307}
4308
4309static int drv_cmd_set_fast_transition(hdd_adapter_t *adapter,
4310 hdd_context_t *hdd_ctx,
4311 uint8_t *command,
4312 uint8_t command_len,
4313 hdd_priv_data_t *priv_data)
4314{
4315 int ret = 0;
4316 uint8_t *value = command;
4317 uint8_t ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
4318
4319 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4320 value = value + command_len + 1;
4321
4322 /* Convert the value from ascii to integer */
4323 ret = kstrtou8(value, 10, &ft);
4324 if (ret < 0) {
4325 /*
4326 * If the input value is greater than max value of datatype,
4327 * then also kstrtou8 fails
4328 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004329 hdd_err("kstrtou8 failed range [%d - %d]",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004330 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4331 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4332 ret = -EINVAL;
4333 goto exit;
4334 }
4335
4336 if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
4337 (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004338 hdd_err("ft mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004339 ft,
4340 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4341 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4342 ret = -EINVAL;
4343 goto exit;
4344 }
4345
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004346 hdd_info("Received Command to change ft mode = %d", ft);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004347
4348 hdd_ctx->config->isFastTransitionEnabled = ft;
4349 sme_update_fast_transition_enabled(hdd_ctx->hHal, ft);
4350
4351exit:
4352 return ret;
4353}
4354
4355#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4356static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid,
4357 int channel)
4358{
4359 struct wma_roam_invoke_cmd *fastreassoc;
4360 cds_msg_t msg = {0};
4361
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304362 fastreassoc = qdf_mem_malloc(sizeof(*fastreassoc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004363 if (NULL == fastreassoc) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004364 hdd_err("qdf_mem_malloc failed for fastreassoc");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004365 return;
4366 }
4367 fastreassoc->vdev_id = sessionId;
4368 fastreassoc->channel = channel;
4369 fastreassoc->bssid[0] = bssid[0];
4370 fastreassoc->bssid[1] = bssid[1];
4371 fastreassoc->bssid[2] = bssid[2];
4372 fastreassoc->bssid[3] = bssid[3];
4373 fastreassoc->bssid[4] = bssid[4];
4374 fastreassoc->bssid[5] = bssid[5];
4375
4376 msg.type = SIR_HAL_ROAM_INVOKE;
4377 msg.reserved = 0;
4378 msg.bodyptr = fastreassoc;
Anurag Chouhan6d760662016-02-20 16:05:43 +05304379 if (QDF_STATUS_SUCCESS != cds_mq_post_message(QDF_MODULE_ID_WMA,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004380 &msg)) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304381 qdf_mem_free(fastreassoc);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004382 hdd_err("Not able to post ROAM_INVOKE_CMD message to WMA");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004383 }
4384}
Varun Reddy Yeturubbbbe232016-02-29 14:01:57 -08004385#else
4386static inline void hdd_wma_send_fastreassoc_cmd(int sessionId,
4387 tSirMacAddr bssid, int channel)
4388{}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004389#endif
4390static int drv_cmd_fast_reassoc(hdd_adapter_t *adapter,
4391 hdd_context_t *hdd_ctx,
4392 uint8_t *command,
4393 uint8_t command_len,
4394 hdd_priv_data_t *priv_data)
4395{
4396 int ret = 0;
4397 uint8_t *value = command;
4398 uint8_t channel = 0;
4399 tSirMacAddr targetApBssid;
4400 uint32_t roamId = 0;
4401 tCsrRoamModifyProfileFields modProfileFields;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004402 tCsrHandoffRequest handoffInfo;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004403 hdd_station_ctx_t *pHddStaCtx;
4404
Krunal Sonibe766b02016-03-10 13:00:44 -08004405 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004406 hdd_warn("Unsupported in mode %s(%d)",
4407 hdd_device_mode_to_string(adapter->device_mode),
4408 adapter->device_mode);
4409 return -EINVAL;
4410 }
4411
4412 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4413
4414 /* if not associated, no need to proceed with reassoc */
4415 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004416 hdd_info("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004417 ret = -EINVAL;
4418 goto exit;
4419 }
4420
4421 ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
4422 &channel);
4423 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004424 hdd_err("Failed to parse reassoc command data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004425 goto exit;
4426 }
4427
4428 /*
4429 * if the target bssid is same as currently associated AP,
4430 * issue reassoc to same AP
4431 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304432 if (true != qdf_mem_cmp(targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004433 pHddStaCtx->conn_info.bssId.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +05304434 QDF_MAC_ADDR_SIZE)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004435 /* Reassoc to same AP, only supported for Open Security*/
4436 if ((pHddStaCtx->conn_info.ucEncryptionType ||
4437 pHddStaCtx->conn_info.mcEncryptionType)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004438 hdd_err("Reassoc to same AP, only supported for Open Security");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004439 return -ENOTSUPP;
4440 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004441 hdd_info("Reassoc BSSID is same as currently associated AP bssid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004442 sme_get_modify_profile_fields(hdd_ctx->hHal, adapter->sessionId,
4443 &modProfileFields);
4444 sme_roam_reassoc(hdd_ctx->hHal, adapter->sessionId,
4445 NULL, modProfileFields, &roamId, 1);
4446 return 0;
4447 }
4448
4449 /* Check channel number is a valid channel number */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304450 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004451 wlan_hdd_validate_operation_channel(adapter, channel)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004452 hdd_err("Invalid Channel [%d]", channel);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004453 return -EINVAL;
4454 }
Varun Reddy Yeturud351a6c2016-03-16 14:01:00 -07004455 if (roaming_offload_enabled(hdd_ctx)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004456 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4457 targetApBssid, (int)channel);
4458 goto exit;
4459 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004460 /* Proceed with reassoc */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004461 handoffInfo.channel = channel;
4462 handoffInfo.src = FASTREASSOC;
Varun Reddy Yeturua66f9c52016-02-17 09:58:40 -08004463 qdf_mem_copy(handoffInfo.bssid.bytes, targetApBssid,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004464 sizeof(tSirMacAddr));
4465 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
4466 &handoffInfo);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004467exit:
4468 return ret;
4469}
4470
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004471static int drv_cmd_set_roam_scan_control(hdd_adapter_t *adapter,
4472 hdd_context_t *hdd_ctx,
4473 uint8_t *command,
4474 uint8_t command_len,
4475 hdd_priv_data_t *priv_data)
4476{
4477 int ret = 0;
4478 uint8_t *value = command;
4479 uint8_t roamScanControl = 0;
4480
4481 /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
4482 value = value + command_len + 1;
4483
4484 /* Convert the value from ascii to integer */
4485 ret = kstrtou8(value, 10, &roamScanControl);
4486 if (ret < 0) {
4487 /*
4488 * If the input value is greater than max value of datatype,
4489 * then also kstrtou8 fails
4490 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004491 hdd_err("kstrtou8 failed ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004492 ret = -EINVAL;
4493 goto exit;
4494 }
4495
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004496 hdd_info("Received Command to Set roam scan control = %d",
4497 roamScanControl);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004498
4499 if (0 != roamScanControl) {
4500 ret = 0; /* return success but ignore param value "true" */
4501 goto exit;
4502 }
4503
4504 sme_set_roam_scan_control(hdd_ctx->hHal,
4505 adapter->sessionId,
4506 roamScanControl);
4507
4508exit:
4509 return ret;
4510}
4511
4512static int drv_cmd_set_okc_mode(hdd_adapter_t *adapter,
4513 hdd_context_t *hdd_ctx,
4514 uint8_t *command,
4515 uint8_t command_len,
4516 hdd_priv_data_t *priv_data)
4517{
4518 int ret = 0;
4519 uint8_t *value = command;
4520 uint8_t okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
4521
4522 /*
4523 * Check if the features OKC/ESE/11R are supported simultaneously,
4524 * then this operation is not permitted (return FAILURE)
4525 */
4526 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4527 hdd_is_okc_mode_enabled(hdd_ctx) &&
4528 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004529 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004530 ret = -EPERM;
4531 goto exit;
4532 }
4533
4534 /* Move pointer to ahead of SETOKCMODE<delimiter> */
4535 value = value + command_len + 1;
4536
4537 /* Convert the value from ascii to integer */
4538 ret = kstrtou8(value, 10, &okcMode);
4539 if (ret < 0) {
4540 /*
4541 * If the input value is greater than max value of datatype,
4542 * then also kstrtou8 fails
4543 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004544 hdd_err("kstrtou8 failed range [%d - %d]",
4545 CFG_OKC_FEATURE_ENABLED_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004546 CFG_OKC_FEATURE_ENABLED_MAX);
4547 ret = -EINVAL;
4548 goto exit;
4549 }
4550
4551 if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
4552 (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004553 hdd_err("Okc mode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004554 okcMode,
4555 CFG_OKC_FEATURE_ENABLED_MIN,
4556 CFG_OKC_FEATURE_ENABLED_MAX);
4557 ret = -EINVAL;
4558 goto exit;
4559 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004560 hdd_info("Received Command to change okc mode = %d",
4561 okcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004562
4563 hdd_ctx->config->isOkcIniFeatureEnabled = okcMode;
4564
4565exit:
4566 return ret;
4567}
4568
4569static int drv_cmd_get_roam_scan_control(hdd_adapter_t *adapter,
4570 hdd_context_t *hdd_ctx,
4571 uint8_t *command,
4572 uint8_t command_len,
4573 hdd_priv_data_t *priv_data)
4574{
4575 int ret = 0;
4576 bool roamScanControl = sme_get_roam_scan_control(hdd_ctx->hHal);
4577 char extra[32];
4578 uint8_t len = 0;
4579
4580 len = scnprintf(extra, sizeof(extra), "%s %d",
4581 command, roamScanControl);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304582 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004583 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004584 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004585 ret = -EFAULT;
4586 }
4587
4588 return ret;
4589}
4590
4591static int drv_cmd_bt_coex_mode(hdd_adapter_t *adapter,
4592 hdd_context_t *hdd_ctx,
4593 uint8_t *command,
4594 uint8_t command_len,
4595 hdd_priv_data_t *priv_data)
4596{
4597 int ret = 0;
4598 char *bcMode;
4599
4600 bcMode = command + 11;
4601 if ('1' == *bcMode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004602 hdd_debug("BTCOEXMODE %d", *bcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004603 hdd_ctx->btCoexModeSet = true;
4604 ret = wlan_hdd_scan_abort(adapter);
4605 if (ret < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004606 hdd_err("Failed to abort existing scan status: %d",
4607 ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004608 }
4609 } else if ('2' == *bcMode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004610 hdd_debug("BTCOEXMODE %d", *bcMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004611 hdd_ctx->btCoexModeSet = false;
4612 }
4613
4614 return ret;
4615}
4616
4617static int drv_cmd_scan_active(hdd_adapter_t *adapter,
4618 hdd_context_t *hdd_ctx,
4619 uint8_t *command,
4620 uint8_t command_len,
4621 hdd_priv_data_t *priv_data)
4622{
4623 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
4624 return 0;
4625}
4626
4627static int drv_cmd_scan_passive(hdd_adapter_t *adapter,
4628 hdd_context_t *hdd_ctx,
4629 uint8_t *command,
4630 uint8_t command_len,
4631 hdd_priv_data_t *priv_data)
4632{
4633 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN;
4634 return 0;
4635}
4636
4637static int drv_cmd_get_dwell_time(hdd_adapter_t *adapter,
4638 hdd_context_t *hdd_ctx,
4639 uint8_t *command,
4640 uint8_t command_len,
4641 hdd_priv_data_t *priv_data)
4642{
4643 int ret = 0;
4644 struct hdd_config *pCfg =
4645 (WLAN_HDD_GET_CTX(adapter))->config;
4646 char extra[32];
4647 uint8_t len = 0;
4648
4649 memset(extra, 0, sizeof(extra));
4650 ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
Anurag Chouhan6d760662016-02-20 16:05:43 +05304651 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004652 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004653 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004654 ret = -EFAULT;
4655 goto exit;
4656 }
4657 ret = len;
4658exit:
4659 return ret;
4660}
4661
4662static int drv_cmd_set_dwell_time(hdd_adapter_t *adapter,
4663 hdd_context_t *hdd_ctx,
4664 uint8_t *command,
4665 uint8_t command_len,
4666 hdd_priv_data_t *priv_data)
4667{
4668 return hdd_set_dwell_time(adapter, command);
4669}
4670
4671static int drv_cmd_miracast(hdd_adapter_t *adapter,
4672 hdd_context_t *hdd_ctx,
4673 uint8_t *command,
4674 uint8_t command_len,
4675 hdd_priv_data_t *priv_data)
4676{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304677 QDF_STATUS ret_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004678 int ret = 0;
4679 tHalHandle hHal;
4680 uint8_t filterType = 0;
4681 hdd_context_t *pHddCtx = NULL;
4682 uint8_t *value;
4683
4684 pHddCtx = WLAN_HDD_GET_CTX(adapter);
4685 if (0 != wlan_hdd_validate_context(pHddCtx)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004686 hdd_err("pHddCtx is not valid, Unable to set miracast mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004687 return -EINVAL;
4688 }
4689
4690 hHal = pHddCtx->hHal;
4691 value = command + 9;
4692
4693 /* Convert the value from ascii to integer */
4694 ret = kstrtou8(value, 10, &filterType);
4695 if (ret < 0) {
4696 /*
4697 * If the input value is greater than max value of datatype,
4698 * then also kstrtou8 fails
4699 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004700 hdd_err("kstrtou8 failed range ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004701 ret = -EINVAL;
4702 goto exit;
4703 }
4704 if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL)
4705 || (filterType >
4706 WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004707 hdd_err("Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004708 ret = -EINVAL;
4709 goto exit;
4710 }
4711 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
4712 pHddCtx->miracast_value = filterType;
4713
4714 ret_status = sme_set_miracast(hHal, filterType);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304715 if (QDF_STATUS_SUCCESS != ret_status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004716 hdd_err("Failed to set miracast");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004717 return -EBUSY;
4718 }
4719
Tushnim Bhattacharyyaca50b322015-12-28 17:14:36 -08004720 if (cds_is_mcc_in_24G())
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004721 return cds_set_mas(adapter, filterType);
4722
4723exit:
4724 return ret;
4725}
4726
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004727/* Function header is left blank intentionally */
4728static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie,
4729 int32_t *oui_length, int32_t limit)
4730{
4731 uint8_t len;
4732 uint8_t data;
4733
4734 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
4735 command++;
4736 limit--;
4737 }
4738
4739 len = 2;
4740
4741 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
4742 (limit > 1)) {
4743 sscanf(command, "%02x", (unsigned int *)&data);
4744 ie[len++] = data;
4745 command += 2;
4746 limit -= 2;
4747 }
4748
4749 *oui_length = len - 2;
4750
4751 while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
4752 command++;
4753 limit--;
4754 }
4755
4756 while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
4757 (limit > 1)) {
4758 sscanf(command, "%02x", (unsigned int *)&data);
4759 ie[len++] = data;
4760 command += 2;
4761 limit -= 2;
4762 }
4763
4764 ie[0] = IE_EID_VENDOR;
4765 ie[1] = len - 2;
4766
4767 return len;
4768}
4769
4770/**
4771 * drv_cmd_set_ibss_beacon_oui_data() - set ibss oui data command
4772 * @adapter: Pointer to adapter
4773 * @hdd_ctx: Pointer to HDD context
4774 * @command: Pointer to command string
4775 * @command_len : Command length
4776 * @priv_data : Pointer to priv data
4777 *
4778 * Return:
4779 * int status code
4780 */
4781static int drv_cmd_set_ibss_beacon_oui_data(hdd_adapter_t *adapter,
4782 hdd_context_t *hdd_ctx,
4783 uint8_t *command,
4784 uint8_t command_len,
4785 hdd_priv_data_t *priv_data)
4786{
4787 int i = 0;
4788 int status;
4789 int ret = 0;
4790 uint8_t *ibss_ie;
4791 int32_t oui_length = 0;
4792 uint32_t ibss_ie_length;
4793 uint8_t *value = command;
4794 tSirModifyIE ibssModifyIE;
4795 tCsrRoamProfile *pRoamProfile;
4796 hdd_wext_state_t *pWextState;
4797
4798
Krunal Sonibe766b02016-03-10 13:00:44 -08004799 if (QDF_IBSS_MODE != adapter->device_mode) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004800 hdd_info("Device_mode %s(%d) not IBSS",
4801 hdd_device_mode_to_string(adapter->device_mode),
4802 adapter->device_mode);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004803 return ret;
4804 }
4805
4806 pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(adapter);
4807
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004808 hdd_info("received command %s", ((char *)value));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004809
4810
4811 /* validate argument of command */
4812 if (strlen(value) <= command_len) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004813 hdd_err("No arguments in command length %zu",
4814 strlen(value));
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004815 ret = -EFAULT;
4816 goto exit;
4817 }
4818
4819 /* moving to arguments of commands */
4820 value = value + command_len;
4821 command_len = strlen(value);
4822
4823 /* oui_data can't be less than 3 bytes */
4824 if (command_len < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004825 hdd_err("Invalid SETIBSSBEACONOUIDATA command length %d",
4826 command_len);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004827 ret = -EFAULT;
4828 goto exit;
4829 }
4830
4831 ibss_ie = qdf_mem_malloc(command_len);
4832 if (!ibss_ie) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004833 hdd_err("Could not allocate memory for command length %d",
4834 command_len);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004835 ret = -ENOMEM;
4836 goto exit;
4837 }
4838 qdf_mem_zero(ibss_ie, command_len);
4839
4840 ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie,
4841 &oui_length,
4842 command_len);
4843 if (ibss_ie_length <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004844 hdd_err("Could not parse command %s return length %d",
4845 value, ibss_ie_length);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004846 ret = -EFAULT;
4847 qdf_mem_free(ibss_ie);
4848 goto exit;
4849 }
4850
4851 pRoamProfile = &pWextState->roamProfile;
4852
4853 qdf_copy_macaddr(&ibssModifyIE.bssid,
4854 pRoamProfile->BSSIDs.bssid);
4855
4856 ibssModifyIE.smeSessionId = adapter->sessionId;
4857 ibssModifyIE.notify = true;
4858 ibssModifyIE.ieID = IE_EID_VENDOR;
4859 ibssModifyIE.ieIDLen = ibss_ie_length;
4860 ibssModifyIE.ieBufferlength = ibss_ie_length;
4861 ibssModifyIE.pIEBuffer = ibss_ie;
4862 ibssModifyIE.oui_length = oui_length;
4863
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004864 hdd_warn("ibss_ie length %d oui_length %d ibss_ie:",
4865 ibss_ie_length, oui_length);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004866 while (i < ibssModifyIE.ieBufferlength)
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004867 hdd_warn("0x%x", ibss_ie[i++]);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004868
4869 /* Probe Bcn modification */
4870 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
4871 &ibssModifyIE, eUPDATE_IE_PROBE_BCN);
4872
4873 /* Populating probe resp frame */
4874 sme_modify_add_ie(WLAN_HDD_GET_HAL_CTX(adapter),
4875 &ibssModifyIE, eUPDATE_IE_PROBE_RESP);
4876
4877 qdf_mem_free(ibss_ie);
4878
4879 status = sme_send_cesium_enable_ind((tHalHandle)(hdd_ctx->hHal),
4880 adapter->sessionId);
4881 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004882 hdd_err("Could not send cesium enable indication %d",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004883 status);
4884 ret = -EINVAL;
4885 goto exit;
4886 }
4887
4888exit:
4889 return ret;
4890}
4891
4892static int drv_cmd_set_rmc_enable(hdd_adapter_t *adapter,
4893 hdd_context_t *hdd_ctx,
4894 uint8_t *command,
4895 uint8_t command_len,
4896 hdd_priv_data_t *priv_data)
4897{
4898 int ret = 0;
4899 uint8_t *value = command;
4900 uint8_t ucRmcEnable = 0;
4901 int status;
4902
Krunal Sonibe766b02016-03-10 13:00:44 -08004903 if ((QDF_IBSS_MODE != adapter->device_mode) &&
4904 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004905 hdd_err("Received SETRMCENABLE cmd in invalid mode %s(%d)",
4906 hdd_device_mode_to_string(adapter->device_mode),
4907 adapter->device_mode);
4908 hdd_err("SETRMCENABLE cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004909 ret = -EINVAL;
4910 goto exit;
4911 }
4912
4913 status = hdd_parse_setrmcenable_command(value, &ucRmcEnable);
4914 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004915 hdd_err("Invalid SETRMCENABLE command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004916 ret = -EINVAL;
4917 goto exit;
4918 }
4919
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004920 hdd_info("ucRmcEnable %d ", ucRmcEnable);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004921
4922 if (true == ucRmcEnable) {
4923 status = sme_enable_rmc((tHalHandle)
4924 (hdd_ctx->hHal),
4925 adapter->sessionId);
4926 } else if (false == ucRmcEnable) {
4927 status = sme_disable_rmc((tHalHandle)
4928 (hdd_ctx->hHal),
4929 adapter->sessionId);
4930 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004931 hdd_err("Invalid SETRMCENABLE command %d",
4932 ucRmcEnable);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004933 ret = -EINVAL;
4934 goto exit;
4935 }
4936
4937 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004938 hdd_err("SETRMC %d failed status %d",
4939 ucRmcEnable, status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004940 ret = -EINVAL;
4941 goto exit;
4942 }
4943
4944exit:
4945 return ret;
4946}
4947
4948static int drv_cmd_set_rmc_action_period(hdd_adapter_t *adapter,
4949 hdd_context_t *hdd_ctx,
4950 uint8_t *command,
4951 uint8_t command_len,
4952 hdd_priv_data_t *priv_data)
4953{
4954 int ret = 0;
4955 uint8_t *value = command;
4956 uint32_t uActionPeriod = 0;
4957 int status;
4958
Krunal Sonibe766b02016-03-10 13:00:44 -08004959 if ((QDF_IBSS_MODE != adapter->device_mode) &&
4960 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004961 hdd_err("Received SETRMC cmd in invalid mode %s(%d)",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004962 hdd_device_mode_to_string(adapter->device_mode),
4963 adapter->device_mode);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004964 hdd_err("SETRMC cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004965 ret = -EINVAL;
4966 goto exit;
4967 }
4968
4969 status = hdd_parse_setrmcactionperiod_command(value, &uActionPeriod);
4970 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004971 hdd_err("Invalid SETRMCACTIONPERIOD command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004972 ret = -EINVAL;
4973 goto exit;
4974 }
4975
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004976 hdd_info("uActionPeriod %d ",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004977 uActionPeriod);
4978
4979 if (sme_cfg_set_int(hdd_ctx->hHal,
4980 WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY,
4981 uActionPeriod)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004982 hdd_err("Could not set SETRMCACTIONPERIOD %d",
4983 uActionPeriod);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004984 ret = -EINVAL;
4985 goto exit;
4986 }
4987
4988 status = sme_send_rmc_action_period((tHalHandle)(hdd_ctx->hHal),
4989 adapter->sessionId);
4990 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07004991 hdd_err("Could not send cesium enable indication %d",
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08004992 status);
4993 ret = -EINVAL;
4994 goto exit;
4995 }
4996
4997exit:
4998 return ret;
4999}
5000
5001static int drv_cmd_get_ibss_peer_info_all(hdd_adapter_t *adapter,
5002 hdd_context_t *hdd_ctx,
5003 uint8_t *command,
5004 uint8_t command_len,
5005 hdd_priv_data_t *priv_data)
5006{
5007 int ret = 0;
5008 int status = QDF_STATUS_SUCCESS;
5009 hdd_station_ctx_t *pHddStaCtx = NULL;
5010 char *extra = NULL;
5011 int idx = 0;
5012 int length = 0;
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005013 uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005014 uint32_t numOfBytestoPrint = 0;
5015
Krunal Sonibe766b02016-03-10 13:00:44 -08005016 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005017 hdd_warn("Unsupported in mode %s(%d)",
5018 hdd_device_mode_to_string(adapter->device_mode),
5019 adapter->device_mode);
5020 return -EINVAL;
5021 }
5022
5023 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005024 hdd_info("Received GETIBSSPEERINFOALL Command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005025
5026 /* Handle the command */
5027 status = hdd_cfg80211_get_ibss_peer_info_all(adapter);
5028 if (QDF_STATUS_SUCCESS == status) {
5029 /*
5030 * The variable extra needed to be allocated on the heap since
5031 * amount of memory required to copy the data for 32 devices
5032 * exceeds the size of 1024 bytes of default stack size. On
5033 * 64 bit devices, the default max stack size of 2048 bytes
5034 */
5035 extra = kmalloc(WLAN_MAX_BUF_SIZE, GFP_KERNEL);
5036
5037 if (NULL == extra) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005038 hdd_err("kmalloc failed");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005039 ret = -EINVAL;
5040 goto exit;
5041 }
5042
5043 /* Copy number of stations */
5044 length = scnprintf(extra, WLAN_MAX_BUF_SIZE, "%d ",
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005045 pHddStaCtx->ibss_peer_info.numPeers);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005046 numOfBytestoPrint = length;
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005047 for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numPeers;
5048 idx++) {
5049 int8_t rssi;
5050 uint32_t tx_rate;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005051
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005052 qdf_mem_copy(mac_addr,
5053 pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5054 mac_addr, sizeof(mac_addr));
5055
5056 tx_rate =
5057 pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5058 txRate;
5059 rssi = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].
5060 rssi;
5061
5062 length += scnprintf((extra + length),
5063 WLAN_MAX_BUF_SIZE - length,
5064 "%02x:%02x:%02x:%02x:%02x:%02x %d %d ",
5065 mac_addr[0], mac_addr[1], mac_addr[2],
5066 mac_addr[3], mac_addr[4], mac_addr[5],
5067 tx_rate, rssi);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005068 /*
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005069 * cdf_trace_msg has limitation of 512 bytes for the
5070 * print buffer. Hence printing the data in two chunks.
5071 * The first chunk will have the data for 16 devices
5072 * and the second chunk will have the rest.
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005073 */
5074 if (idx < NUM_OF_STA_DATA_TO_PRINT)
5075 numOfBytestoPrint = length;
5076 }
5077
5078 /*
5079 * Copy the data back into buffer, if the data to copy is
5080 * more than 512 bytes than we will split the data and do
5081 * it in two shots
5082 */
5083 if (copy_to_user(priv_data->buf, extra, numOfBytestoPrint)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005084 hdd_err("Copy into user data buffer failed ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005085 ret = -EFAULT;
5086 goto exit;
5087 }
5088
5089 priv_data->buf[numOfBytestoPrint] = '\0';
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005090 hdd_debug("%s", priv_data->buf);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005091
5092 if (length > numOfBytestoPrint) {
5093 if (copy_to_user
5094 (priv_data->buf + numOfBytestoPrint,
5095 extra + numOfBytestoPrint,
5096 length - numOfBytestoPrint + 1)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005097 hdd_err("Copy into user data buffer failed ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005098 ret = -EFAULT;
5099 goto exit;
5100 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005101 hdd_debug("%s", &priv_data->buf[numOfBytestoPrint]);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005102 }
5103
5104 /* Free temporary buffer */
5105 kfree(extra);
5106 } else {
5107 /* Command failed, log error */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005108 hdd_err("GETIBSSPEERINFOALL command failed with status code %d",
5109 status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005110 ret = -EINVAL;
5111 goto exit;
5112 }
5113 ret = 0;
5114
5115exit:
5116 return ret;
5117}
5118
5119/* Peer Info <Peer Addr> command */
5120static int drv_cmd_get_ibss_peer_info(hdd_adapter_t *adapter,
5121 hdd_context_t *hdd_ctx,
5122 uint8_t *command,
5123 uint8_t command_len,
5124 hdd_priv_data_t *priv_data)
5125{
5126 int ret = 0;
5127 uint8_t *value = command;
5128 QDF_STATUS status;
5129 hdd_station_ctx_t *pHddStaCtx = NULL;
5130 char extra[128] = { 0 };
5131 uint32_t length = 0;
5132 uint8_t staIdx = 0;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005133 struct qdf_mac_addr peerMacAddr;
5134
Krunal Sonibe766b02016-03-10 13:00:44 -08005135 if (QDF_IBSS_MODE != adapter->device_mode) {
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005136 hdd_warn("Unsupported in mode %s(%d)",
5137 hdd_device_mode_to_string(adapter->device_mode),
5138 adapter->device_mode);
5139 return -EINVAL;
5140 }
5141
5142 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5143
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005144 hdd_info("Received GETIBSSPEERINFO Command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005145
5146 /* if there are no peers, no need to continue with the command */
5147 if (eConnectionState_IbssConnected !=
5148 pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005149 hdd_info("No IBSS Peers coalesced");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005150 ret = -EINVAL;
5151 goto exit;
5152 }
5153
5154 /* Parse the incoming command buffer */
5155 status = hdd_parse_get_ibss_peer_info(value, &peerMacAddr);
5156 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005157 hdd_err("Invalid GETIBSSPEERINFO command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005158 ret = -EINVAL;
5159 goto exit;
5160 }
5161
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005162 /* Get station index for the peer mac address and sanitize it */
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005163 hdd_ibss_get_sta_id(pHddStaCtx, &peerMacAddr, &staIdx);
5164
5165 if (staIdx > MAX_IBSS_PEERS) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005166 hdd_err("Invalid StaIdx %d returned", staIdx);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005167 ret = -EINVAL;
5168 goto exit;
5169 }
5170
5171 /* Handle the command */
5172 status = hdd_cfg80211_get_ibss_peer_info(adapter, staIdx);
5173 if (QDF_STATUS_SUCCESS == status) {
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005174 uint32_t txRate =
5175 pHddStaCtx->ibss_peer_info.peerInfoParams[0].txRate;
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005176
5177 length = scnprintf(extra, sizeof(extra), "%d %d",
Rajeev Kumar94c9b452016-03-24 12:58:47 -07005178 (int)txRate,
5179 (int)pHddStaCtx->ibss_peer_info.
5180 peerInfoParams[0].rssi);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005181
5182 /* Copy the data back into buffer */
5183 if (copy_to_user(priv_data->buf, &extra, length + 1)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005184 hdd_err("copy data to user buffer failed GETIBSSPEERINFO command");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005185 ret = -EFAULT;
5186 goto exit;
5187 }
5188 } else {
5189 /* Command failed, log error */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005190 hdd_err("GETIBSSPEERINFO command failed with status code %d",
5191 status);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005192 ret = -EINVAL;
5193 goto exit;
5194 }
5195
5196 /* Success ! */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005197 hdd_debug("%s", priv_data->buf);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005198 ret = 0;
5199
5200exit:
5201 return ret;
5202}
5203
5204static int drv_cmd_set_rmc_tx_rate(hdd_adapter_t *adapter,
5205 hdd_context_t *hdd_ctx,
5206 uint8_t *command,
5207 uint8_t command_len,
5208 hdd_priv_data_t *priv_data)
5209{
5210 int ret = 0;
5211 uint8_t *value = command;
5212 uint32_t uRate = 0;
5213 tTxrateinfoflags txFlags = 0;
5214 tSirRateUpdateInd rateUpdateParams = {0};
5215 int status;
5216 struct hdd_config *pConfig = hdd_ctx->config;
5217
Krunal Sonibe766b02016-03-10 13:00:44 -08005218 if ((QDF_IBSS_MODE != adapter->device_mode) &&
5219 (QDF_SAP_MODE != adapter->device_mode)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005220 hdd_err("Received SETRMCTXRATE cmd in invalid mode %s(%d)",
5221 hdd_device_mode_to_string(adapter->device_mode),
5222 adapter->device_mode);
5223 hdd_err("SETRMCTXRATE cmd is allowed only in IBSS/SOFTAP mode");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005224 ret = -EINVAL;
5225 goto exit;
5226 }
5227
5228 status = hdd_parse_setrmcrate_command(value, &uRate, &txFlags);
5229 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005230 hdd_err("Invalid SETRMCTXRATE command ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005231 ret = -EINVAL;
5232 goto exit;
5233 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005234 hdd_info("uRate %d ", uRate);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005235 /* -1 implies ignore this param */
5236 rateUpdateParams.ucastDataRate = -1;
5237
5238 /*
5239 * Fill the user specifieed RMC rate param
5240 * and the derived tx flags.
5241 */
5242 rateUpdateParams.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
5243 rateUpdateParams.reliableMcastDataRate = uRate;
5244 rateUpdateParams.reliableMcastDataRateTxFlag = txFlags;
5245 rateUpdateParams.dev_mode = adapter->device_mode;
5246 rateUpdateParams.bcastDataRate = -1;
5247 memcpy(rateUpdateParams.bssid.bytes,
5248 adapter->macAddressCurrent.bytes,
5249 sizeof(rateUpdateParams.bssid));
5250 status = sme_send_rate_update_ind((tHalHandle) (hdd_ctx->hHal),
5251 &rateUpdateParams);
5252
5253exit:
5254 return ret;
5255}
5256
5257static int drv_cmd_set_ibss_tx_fail_event(hdd_adapter_t *adapter,
5258 hdd_context_t *hdd_ctx,
5259 uint8_t *command,
5260 uint8_t command_len,
5261 hdd_priv_data_t *priv_data)
5262{
5263 int ret = 0;
5264 char *value;
5265 uint8_t tx_fail_count = 0;
5266 uint16_t pid = 0;
5267
5268 value = command;
5269
5270 ret = hdd_parse_ibsstx_fail_event_params(value, &tx_fail_count, &pid);
5271
5272 if (0 != ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005273 hdd_info("Failed to parse SETIBSSTXFAILEVENT arguments");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005274 goto exit;
5275 }
5276
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005277 hdd_info("tx_fail_cnt=%hhu, pid=%hu", tx_fail_count, pid);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005278
5279 if (0 == tx_fail_count) {
5280 /* Disable TX Fail Indication */
5281 if (QDF_STATUS_SUCCESS ==
5282 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5283 tx_fail_count,
5284 NULL)) {
5285 cesium_pid = 0;
5286 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005287 hdd_err("failed to disable TX Fail Event ");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005288 ret = -EINVAL;
5289 }
5290 } else {
5291 if (QDF_STATUS_SUCCESS ==
5292 sme_tx_fail_monitor_start_stop_ind(hdd_ctx->hHal,
5293 tx_fail_count,
5294 (void *)hdd_tx_fail_ind_callback)) {
5295 cesium_pid = pid;
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005296 hdd_info("Registered Cesium pid %u",
5297 cesium_pid);
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005298 } else {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005299 hdd_err("Failed to enable TX Fail Monitoring");
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08005300 ret = -EINVAL;
5301 }
5302 }
5303
5304exit:
5305 return ret;
5306}
5307
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08005308#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005309static int drv_cmd_set_ccx_roam_scan_channels(hdd_adapter_t *adapter,
5310 hdd_context_t *hdd_ctx,
5311 uint8_t *command,
5312 uint8_t command_len,
5313 hdd_priv_data_t *priv_data)
5314{
5315 int ret = 0;
5316 uint8_t *value = command;
5317 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
5318 uint8_t numChannels = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305319 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005320
5321 ret = hdd_parse_channellist(value, ChannelList, &numChannels);
5322 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005323 hdd_err("Failed to parse channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005324 goto exit;
5325 }
5326 if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005327 hdd_err("number of channels (%d) supported exceeded max (%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005328 numChannels,
5329 WNI_CFG_VALID_CHANNEL_LIST_LEN);
5330 ret = -EINVAL;
5331 goto exit;
5332 }
5333 status = sme_set_ese_roam_scan_channel_list(hdd_ctx->hHal,
5334 adapter->sessionId,
5335 ChannelList,
5336 numChannels);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305337 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005338 hdd_err("Failed to update channel list information");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005339 ret = -EINVAL;
5340 goto exit;
5341 }
5342
5343exit:
5344 return ret;
5345}
5346
5347static int drv_cmd_get_tsm_stats(hdd_adapter_t *adapter,
5348 hdd_context_t *hdd_ctx,
5349 uint8_t *command,
5350 uint8_t command_len,
5351 hdd_priv_data_t *priv_data)
5352{
5353 int ret = 0;
5354 uint8_t *value = command;
5355 char extra[128] = { 0 };
5356 int len = 0;
5357 uint8_t tid = 0;
5358 hdd_station_ctx_t *pHddStaCtx;
5359 tAniTrafStrmMetrics tsm_metrics;
5360
Krunal Sonibe766b02016-03-10 13:00:44 -08005361 if ((QDF_STA_MODE != adapter->device_mode) &&
5362 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005363 hdd_warn("Unsupported in mode %s(%d)",
5364 hdd_device_mode_to_string(adapter->device_mode),
5365 adapter->device_mode);
5366 return -EINVAL;
5367 }
5368
5369 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
5370
5371 /* if not associated, return error */
5372 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005373 hdd_err("Not associated!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005374 ret = -EINVAL;
5375 goto exit;
5376 }
5377
5378 /* Move pointer to ahead of GETTSMSTATS<delimiter> */
5379 value = value + command_len + 1;
5380
5381 /* Convert the value from ascii to integer */
5382 ret = kstrtou8(value, 10, &tid);
5383 if (ret < 0) {
5384 /*
5385 * If the input value is greater than max value of datatype,
5386 * then also kstrtou8 fails
5387 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005388 hdd_err("kstrtou8 failed range [%d - %d]",
5389 TID_MIN_VALUE,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005390 TID_MAX_VALUE);
5391 ret = -EINVAL;
5392 goto exit;
5393 }
5394 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005395 hdd_err("tid value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005396 tid, TID_MIN_VALUE, TID_MAX_VALUE);
5397 ret = -EINVAL;
5398 goto exit;
5399 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005400 hdd_info("Received Command to get tsm stats tid = %d",
5401 tid);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305402 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005403 hdd_get_tsm_stats(adapter, tid, &tsm_metrics)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005404 hdd_err("failed to get tsm stats");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005405 ret = -EFAULT;
5406 goto exit;
5407 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005408 hdd_info(
5409 "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 -08005410 tsm_metrics.UplinkPktQueueDly,
5411 tsm_metrics.UplinkPktQueueDlyHist[0],
5412 tsm_metrics.UplinkPktQueueDlyHist[1],
5413 tsm_metrics.UplinkPktQueueDlyHist[2],
5414 tsm_metrics.UplinkPktQueueDlyHist[3],
5415 tsm_metrics.UplinkPktTxDly,
5416 tsm_metrics.UplinkPktLoss,
5417 tsm_metrics.UplinkPktCount,
5418 tsm_metrics.RoamingCount,
5419 tsm_metrics.RoamingDly);
5420 /*
5421 * Output TSM stats is of the format
5422 * GETTSMSTATS [PktQueueDly]
5423 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
5424 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800
5425 */
5426 len = scnprintf(extra,
5427 sizeof(extra),
5428 "%s %d %d:%d:%d:%d %u %d %d %d %d",
5429 command,
5430 tsm_metrics.UplinkPktQueueDly,
5431 tsm_metrics.UplinkPktQueueDlyHist[0],
5432 tsm_metrics.UplinkPktQueueDlyHist[1],
5433 tsm_metrics.UplinkPktQueueDlyHist[2],
5434 tsm_metrics.UplinkPktQueueDlyHist[3],
5435 tsm_metrics.UplinkPktTxDly,
5436 tsm_metrics.UplinkPktLoss,
5437 tsm_metrics.UplinkPktCount,
5438 tsm_metrics.RoamingCount,
5439 tsm_metrics.RoamingDly);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305440 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005441 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005442 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005443 ret = -EFAULT;
5444 goto exit;
5445 }
5446
5447exit:
5448 return ret;
5449}
5450
5451static int drv_cmd_set_cckm_ie(hdd_adapter_t *adapter,
5452 hdd_context_t *hdd_ctx,
5453 uint8_t *command,
5454 uint8_t command_len,
5455 hdd_priv_data_t *priv_data)
5456{
5457 int ret;
5458 uint8_t *value = command;
5459 uint8_t *cckmIe = NULL;
5460 uint8_t cckmIeLen = 0;
5461
5462 ret = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
5463 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005464 hdd_err("Failed to parse cckm ie data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005465 goto exit;
5466 }
5467
5468 if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005469 hdd_err("CCKM Ie input length is more than max[%d]",
5470 DOT11F_IE_RSN_MAX_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005471 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05305472 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005473 cckmIe = NULL;
5474 }
5475 ret = -EINVAL;
5476 goto exit;
5477 }
5478
5479 sme_set_cckm_ie(hdd_ctx->hHal, adapter->sessionId,
5480 cckmIe, cckmIeLen);
5481 if (NULL != cckmIe) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05305482 qdf_mem_free(cckmIe);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005483 cckmIe = NULL;
5484 }
5485
5486exit:
5487 return ret;
5488}
5489
5490static int drv_cmd_ccx_beacon_req(hdd_adapter_t *adapter,
5491 hdd_context_t *hdd_ctx,
5492 uint8_t *command,
5493 uint8_t command_len,
5494 hdd_priv_data_t *priv_data)
5495{
5496 int ret;
5497 uint8_t *value = command;
5498 tCsrEseBeaconReq eseBcnReq;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305499 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005500
Krunal Sonibe766b02016-03-10 13:00:44 -08005501 if (QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005502 hdd_warn("Unsupported in mode %s(%d)",
5503 hdd_device_mode_to_string(adapter->device_mode),
5504 adapter->device_mode);
5505 return -EINVAL;
5506 }
5507
5508 ret = hdd_parse_ese_beacon_req(value, &eseBcnReq);
5509 if (ret) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005510 hdd_err("Failed to parse ese beacon req");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005511 goto exit;
5512 }
5513
5514 if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005515 hdd_info("Not associated");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005516 hdd_indicate_ese_bcn_report_no_results(adapter,
5517 eseBcnReq.bcnReq[0].measurementToken,
5518 0x02, /* BIT(1) set for measurement done */
5519 0); /* no BSS */
5520 goto exit;
5521 }
5522
5523 status = sme_set_ese_beacon_request(hdd_ctx->hHal,
5524 adapter->sessionId,
5525 &eseBcnReq);
5526
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305527 if (QDF_STATUS_E_RESOURCES == status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005528 hdd_info("sme_set_ese_beacon_request failed (%d), a request already in progress",
5529 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005530 ret = -EBUSY;
5531 goto exit;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305532 } else if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005533 hdd_err("sme_set_ese_beacon_request failed (%d)",
5534 status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005535 ret = -EINVAL;
5536 goto exit;
5537 }
5538
5539exit:
5540 return ret;
5541}
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08005542#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005543
5544static int drv_cmd_set_mc_rate(hdd_adapter_t *adapter,
5545 hdd_context_t *hdd_ctx,
5546 uint8_t *command,
5547 uint8_t command_len,
5548 hdd_priv_data_t *priv_data)
5549{
5550 int ret = 0;
5551 uint8_t *value = command;
5552 int targetRate;
5553
5554 /* input value is in units of hundred kbps */
5555
5556 /* Move pointer to ahead of SETMCRATE<delimiter> */
5557 value = value + command_len + 1;
5558
5559 /* Convert the value from ascii to integer, decimal base */
5560 ret = kstrtouint(value, 10, &targetRate);
5561
5562 ret = wlan_hdd_set_mc_rate(adapter, targetRate);
5563 return ret;
5564}
5565
5566static int drv_cmd_max_tx_power(hdd_adapter_t *adapter,
5567 hdd_context_t *hdd_ctx,
5568 uint8_t *command,
5569 uint8_t command_len,
5570 hdd_priv_data_t *priv_data)
5571{
5572 int ret = 0;
5573 int status;
5574 int txPower;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305575 QDF_STATUS qdf_status;
5576 QDF_STATUS smeStatus;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005577 uint8_t *value = command;
Anurag Chouhan6d760662016-02-20 16:05:43 +05305578 struct qdf_mac_addr bssid = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
5579 struct qdf_mac_addr selfMac = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005580 hdd_adapter_list_node_t *pAdapterNode = NULL;
5581 hdd_adapter_list_node_t *pNext = NULL;
5582
5583 status = hdd_parse_setmaxtxpower_command(value, &txPower);
5584 if (status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005585 hdd_err("Invalid MAXTXPOWER command ");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005586 ret = -EINVAL;
5587 goto exit;
5588 }
5589
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305590 qdf_status = hdd_get_front_adapter(hdd_ctx, &pAdapterNode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005591 while (NULL != pAdapterNode
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305592 && QDF_STATUS_SUCCESS == qdf_status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005593 adapter = pAdapterNode->pAdapter;
5594 /* Assign correct self MAC address */
Anurag Chouhanc5548422016-02-24 18:33:27 +05305595 qdf_copy_macaddr(&bssid,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005596 &adapter->macAddressCurrent);
Anurag Chouhanc5548422016-02-24 18:33:27 +05305597 qdf_copy_macaddr(&selfMac,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005598 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005599
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005600 hdd_info("Device mode %d max tx power %d selfMac: "
5601 MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005602 adapter->device_mode, txPower,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005603 MAC_ADDR_ARRAY(selfMac.bytes),
5604 MAC_ADDR_ARRAY(bssid.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005605
Srinivas Girigowda97215232015-09-24 12:26:28 -07005606 smeStatus = sme_set_max_tx_power(hdd_ctx->hHal,
5607 bssid, selfMac, txPower);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305608 if (QDF_STATUS_SUCCESS != status) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005609 hdd_err("Set max tx power failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005610 ret = -EINVAL;
5611 goto exit;
5612 }
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005613 hdd_info("Set max tx power success");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05305614 qdf_status = hdd_get_next_adapter(hdd_ctx, pAdapterNode,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005615 &pNext);
5616 pAdapterNode = pNext;
5617 }
5618
5619exit:
5620 return ret;
5621}
5622
5623static int drv_cmd_set_dfs_scan_mode(hdd_adapter_t *adapter,
5624 hdd_context_t *hdd_ctx,
5625 uint8_t *command,
5626 uint8_t command_len,
5627 hdd_priv_data_t *priv_data)
5628{
5629 int ret = 0;
5630 uint8_t *value = command;
5631 uint8_t dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
5632
5633 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
5634 value = value + command_len + 1;
5635
5636 /* Convert the value from ascii to integer */
5637 ret = kstrtou8(value, 10, &dfsScanMode);
5638 if (ret < 0) {
5639 /*
5640 * If the input value is greater than max value of datatype,
5641 * then also kstrtou8 fails
5642 */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005643 hdd_err("kstrtou8 failed range [%d - %d]",
5644 CFG_ROAMING_DFS_CHANNEL_MIN,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005645 CFG_ROAMING_DFS_CHANNEL_MAX);
5646 ret = -EINVAL;
5647 goto exit;
5648 }
5649
5650 if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
5651 (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005652 hdd_err("dfsScanMode value %d is out of range (Min: %d Max: %d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005653 dfsScanMode,
5654 CFG_ROAMING_DFS_CHANNEL_MIN,
5655 CFG_ROAMING_DFS_CHANNEL_MAX);
5656 ret = -EINVAL;
5657 goto exit;
5658 }
5659
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005660 hdd_info("Received Command to Set DFS Scan Mode = %d",
5661 dfsScanMode);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005662
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08005663 /* When DFS scanning is disabled, the DFS channels need to be
5664 * removed from the operation of device.
5665 */
5666 ret = wlan_hdd_disable_dfs_chan_scan(hdd_ctx, adapter,
5667 (dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED));
5668 if (ret < 0) {
5669 /* Some conditions prevented it from disabling DFS channels */
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005670 hdd_err("disable/enable DFS channel request was denied");
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08005671 goto exit;
5672 }
5673
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005674 hdd_ctx->config->allowDFSChannelRoam = dfsScanMode;
5675 sme_update_dfs_scan_mode(hdd_ctx->hHal, adapter->sessionId,
5676 dfsScanMode);
5677
5678exit:
5679 return ret;
5680}
5681
5682static int drv_cmd_get_dfs_scan_mode(hdd_adapter_t *adapter,
5683 hdd_context_t *hdd_ctx,
5684 uint8_t *command,
5685 uint8_t command_len,
5686 hdd_priv_data_t *priv_data)
5687{
5688 int ret = 0;
5689 uint8_t dfsScanMode = sme_get_dfs_scan_mode(hdd_ctx->hHal);
5690 char extra[32];
5691 uint8_t len = 0;
5692
5693 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305694 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005695 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005696 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005697 ret = -EFAULT;
5698 }
5699
5700 return ret;
5701}
5702
5703static int drv_cmd_get_link_status(hdd_adapter_t *adapter,
5704 hdd_context_t *hdd_ctx,
5705 uint8_t *command,
5706 uint8_t command_len,
5707 hdd_priv_data_t *priv_data)
5708{
5709 int ret = 0;
5710 int value = wlan_hdd_get_link_status(adapter);
5711 char extra[32];
5712 uint8_t len;
5713
5714 len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305715 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005716 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005717 hdd_err("failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005718 ret = -EFAULT;
5719 }
5720
5721 return ret;
5722}
5723
5724#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
5725static int drv_cmd_enable_ext_wow(hdd_adapter_t *adapter,
5726 hdd_context_t *hdd_ctx,
5727 uint8_t *command,
5728 uint8_t command_len,
5729 hdd_priv_data_t *priv_data)
5730{
5731 uint8_t *value = command;
5732 int set_value;
5733
5734 /* Move pointer to ahead of ENABLEEXTWOW */
5735 value = value + command_len;
5736
5737 sscanf(value, "%d", &set_value);
5738
5739 return hdd_enable_ext_wow_parser(adapter,
5740 adapter->sessionId,
5741 set_value);
5742}
5743
5744static int drv_cmd_set_app1_params(hdd_adapter_t *adapter,
5745 hdd_context_t *hdd_ctx,
5746 uint8_t *command,
5747 uint8_t command_len,
5748 hdd_priv_data_t *priv_data)
5749{
5750 int ret;
5751 uint8_t *value = command;
5752
5753 /* Move pointer to ahead of SETAPP1PARAMS */
5754 value = value + command_len;
5755
5756 ret = hdd_set_app_type1_parser(adapter,
5757 value, strlen(value));
5758 if (ret >= 0)
5759 hdd_ctx->is_extwow_app_type1_param_set = true;
5760
5761 return ret;
5762}
5763
5764static int drv_cmd_set_app2_params(hdd_adapter_t *adapter,
5765 hdd_context_t *hdd_ctx,
5766 uint8_t *command,
5767 uint8_t command_len,
5768 hdd_priv_data_t *priv_data)
5769{
5770 int ret;
5771 uint8_t *value = command;
5772
5773 /* Move pointer to ahead of SETAPP2PARAMS */
5774 value = value + command_len;
5775
5776 ret = hdd_set_app_type2_parser(adapter, value, strlen(value));
5777 if (ret >= 0)
5778 hdd_ctx->is_extwow_app_type2_param_set = true;
5779
5780 return ret;
5781}
5782#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
5783
5784#ifdef FEATURE_WLAN_TDLS
5785/**
5786 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset
5787 * @adapter: Pointer to the HDD adapter
5788 * @hdd_ctx: Pointer to the HDD context
5789 * @command: Driver command string
5790 * @command_len: Driver command string length
5791 * @priv_data: Private data coming with the driver command. Unused here
5792 *
5793 * This function handles driver command that sets the secondary tdls off channel
5794 * offset
5795 *
5796 * Return: 0 on success; negative errno otherwise
5797 */
5798static int drv_cmd_tdls_secondary_channel_offset(hdd_adapter_t *adapter,
5799 hdd_context_t *hdd_ctx,
5800 uint8_t *command,
5801 uint8_t command_len,
5802 hdd_priv_data_t *priv_data)
5803{
5804 int ret;
5805 uint8_t *value = command;
5806 int set_value;
5807
5808 /* Move pointer to point the string */
5809 value += command_len;
5810
5811 ret = sscanf(value, "%d", &set_value);
5812 if (ret != 1)
5813 return -EINVAL;
5814
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005815 hdd_info("Tdls offchannel offset:%d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005816
5817 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, set_value);
5818
5819 return ret;
5820}
5821
5822/**
5823 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode
5824 * @adapter: Pointer to the HDD adapter
5825 * @hdd_ctx: Pointer to the HDD context
5826 * @command: Driver command string
5827 * @command_len: Driver command string length
5828 * @priv_data: Private data coming with the driver command. Unused here
5829 *
5830 * This function handles driver command that sets tdls off channel mode
5831 *
5832 * Return: 0 on success; negative errno otherwise
5833 */
5834static int drv_cmd_tdls_off_channel_mode(hdd_adapter_t *adapter,
5835 hdd_context_t *hdd_ctx,
5836 uint8_t *command,
5837 uint8_t command_len,
5838 hdd_priv_data_t *priv_data)
5839{
5840 int ret;
5841 uint8_t *value = command;
5842 int set_value;
5843
5844 /* Move pointer to point the string */
5845 value += command_len;
5846
5847 ret = sscanf(value, "%d", &set_value);
5848 if (ret != 1)
5849 return -EINVAL;
5850
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005851 hdd_info("Tdls offchannel mode:%d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005852
5853 ret = hdd_set_tdls_offchannelmode(adapter, set_value);
5854
5855 return ret;
5856}
5857
5858/**
5859 * drv_cmd_tdls_off_channel() - set tdls off channel number
5860 * @adapter: Pointer to the HDD adapter
5861 * @hdd_ctx: Pointer to the HDD context
5862 * @command: Driver command string
5863 * @command_len: Driver command string length
5864 * @priv_data: Private data coming with the driver command. Unused here
5865 *
5866 * This function handles driver command that sets tdls off channel number
5867 *
5868 * Return: 0 on success; negative errno otherwise
5869 */
5870static int drv_cmd_tdls_off_channel(hdd_adapter_t *adapter,
5871 hdd_context_t *hdd_ctx,
5872 uint8_t *command,
5873 uint8_t command_len,
5874 hdd_priv_data_t *priv_data)
5875{
5876 int ret;
5877 uint8_t *value = command;
5878 int set_value;
5879
5880 /* Move pointer to point the string */
5881 value += command_len;
5882
5883 ret = sscanf(value, "%d", &set_value);
5884 if (ret != 1)
5885 return -EINVAL;
5886
Krishna Kumaar Natarajan4d090352015-10-26 18:30:53 -07005887 if (CDS_IS_DFS_CH(set_value)) {
5888 hdd_err("DFS channel %d is passed for hdd_set_tdls_offchannel",
5889 set_value);
5890 return -EINVAL;
5891 }
5892
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005893 hdd_info("Tdls offchannel num: %d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005894
5895 ret = hdd_set_tdls_offchannel(hdd_ctx, set_value);
5896
5897 return ret;
5898}
5899
5900/**
5901 * drv_cmd_tdls_scan() - set tdls scan type
5902 * @adapter: Pointer to the HDD adapter
5903 * @hdd_ctx: Pointer to the HDD context
5904 * @command: Driver command string
5905 * @command_len: Driver command string length
5906 * @priv_data: Private data coming with the driver command. Unused here
5907 *
5908 * This function handles driver command that sets tdls scan type
5909 *
5910 * Return: 0 on success; negative errno otherwise
5911 */
5912static int drv_cmd_tdls_scan(hdd_adapter_t *adapter,
5913 hdd_context_t *hdd_ctx,
5914 uint8_t *command,
5915 uint8_t command_len,
5916 hdd_priv_data_t *priv_data)
5917{
5918 int ret;
5919 uint8_t *value = command;
5920 int set_value;
5921
5922 /* Move pointer to point the string */
5923 value += command_len;
5924
5925 ret = sscanf(value, "%d", &set_value);
5926 if (ret != 1)
5927 return -EINVAL;
5928
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005929 hdd_info("Tdls scan type val: %d", set_value);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005930
5931 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value);
5932
5933 return ret;
5934}
5935#endif
5936
5937static int drv_cmd_get_rssi(hdd_adapter_t *adapter,
5938 hdd_context_t *hdd_ctx,
5939 uint8_t *command,
5940 uint8_t command_len,
5941 hdd_priv_data_t *priv_data)
5942{
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005943 int ret = 0;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005944 int8_t rssi = 0;
5945 char extra[32];
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005946
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005947 uint8_t len = 0;
5948
5949 wlan_hdd_get_rssi(adapter, &rssi);
5950
5951 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305952 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005953
5954 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005955 hdd_err("Failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005956 ret = -EFAULT;
5957 }
5958
5959 return ret;
5960}
5961
5962static int drv_cmd_get_linkspeed(hdd_adapter_t *adapter,
5963 hdd_context_t *hdd_ctx,
5964 uint8_t *command,
5965 uint8_t command_len,
5966 hdd_priv_data_t *priv_data)
5967{
5968 int ret;
5969 uint32_t link_speed = 0;
5970 char extra[32];
5971 uint8_t len = 0;
5972
5973 ret = wlan_hdd_get_link_speed(adapter, &link_speed);
5974 if (0 != ret)
5975 return ret;
5976
5977 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
Anurag Chouhan6d760662016-02-20 16:05:43 +05305978 len = QDF_MIN(priv_data->total_len, len + 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005979 if (copy_to_user(priv_data->buf, &extra, len)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07005980 hdd_err("Failed to copy data to user buffer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005981 ret = -EFAULT;
5982 }
5983
5984 return ret;
5985}
5986
5987#ifdef FEATURE_NAPI
5988/**
5989 * hdd_parse_napi() - helper functions to drv_cmd_napi
5990 * @str : source string to parse
5991 * @cmd : pointer to cmd part after parsing
5992 * @sub : pointer to subcmd part after parsing
5993 * @aux : pointer to optional aux part after parsing
5994 *
5995 * Example:
5996 * NAPI SCALE <n> +-- IN str
5997 * | | +------ OUT aux
5998 * | +------------ OUT subcmd
5999 * +----------------- OUT cmd
6000 *
6001 * Return: ==0: success; !=0: failure
6002 */
6003static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
6004{
6005 int rc;
6006 char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
6007
6008 NAPI_DEBUG("-->\n");
6009
6010 token = strsep(str, " \t");
6011 if (NULL == token) {
6012 hdd_err("cannot parse cmd");
6013 goto parse_end;
6014 }
6015 lcmd = token;
6016
6017 token = strsep(str, " \t");
6018 if (NULL == token) {
6019 hdd_err("cannot parse subcmd");
6020 goto parse_end;
6021 }
6022 lsub = token;
6023
6024 token = strsep(str, " \t");
6025 if (NULL == token)
6026 hdd_warn("cannot parse aux\n");
6027 else
6028 laux = token;
6029
6030parse_end:
6031 if ((NULL == lcmd) || (NULL == lsub))
6032 rc = -EINVAL;
6033 else {
6034 rc = 0;
6035 *cmd = lcmd;
6036 *sub = lsub;
6037 if (NULL != aux)
6038 *aux = laux;
6039 }
6040 NAPI_DEBUG("<--[rc=%d]\n", rc);
6041 return rc;
6042}
6043
6044
6045/**
6046 * hdd_parse_stats() - print NAPI stats into a buffer
6047 * @buf : buffer to write stats into
6048 * @max : "size of buffer"
6049 * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
6050 * @napid: binary structure to retrieve the stats from
6051 *
6052 * Return: number of bytes written into the buffer
6053 */
6054int hdd_napi_stats(char *buf,
6055 int max,
6056 char *indp,
6057 struct qca_napi_data *napid)
6058{
6059 int n = 0;
6060 int i, j, k; /* NAPI, CPU, bucket indices */
6061 int from, to;
6062 struct qca_napi_info *napii;
6063 struct qca_napi_stat *napis;
6064
6065 NAPI_DEBUG("-->\n");
6066
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006067 if (NULL == napid)
6068 return n;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006069 if (NULL == indp) {
6070 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006071 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006072 } else {
6073 if (0 > kstrtoint(indp, 10, &to)) {
6074 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006075 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006076 } else
6077 from = to;
6078 }
6079
6080 for (i = from; i < to; i++)
6081 if (napid->ce_map & (0x01 << i)) {
6082 napii = &(napid->napis[i]);
6083 for (j = 0; j < NR_CPUS; j++) {
6084 napis = &(napii->stats[j]);
6085 n += scnprintf(buf + n, max - n,
6086 "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
6087 i, j,
6088 napis->napi_schedules,
6089 napis->napi_polls,
6090 napis->napi_completes,
6091 napis->napi_workdone);
6092
6093 for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
6094 n += scnprintf(
6095 buf + n, max - n,
6096 " %d",
6097 napis->napi_budget_uses[k]);
6098 }
6099 n += scnprintf(buf+n, max - n, "\n");
6100 }
6101 }
6102
6103 NAPI_DEBUG("<--[n=%d]\n", n);
6104 return n;
6105}
6106
6107/**
6108 * napi_set_scale() - sets the scale attribute in all NAPI entries
6109 * @sc : scale to set
6110 *
6111 * Return: void
6112 */
6113static void napi_set_scale(uint8_t sc)
6114{
6115 uint32_t i;
6116 struct qca_napi_data *napi_data;
6117
6118 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006119 if (likely(NULL != napi_data))
6120 for (i = 0; i < CE_COUNT_MAX; i++)
6121 if (napi_data->ce_map & (0x01 << i))
6122 napi_data->napis[i].scale = sc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006123
6124 return;
6125}
6126/**
6127 * drv_cmd_napi() - processes NAPI commands
6128 * @adapter : net_device
6129 * @hdd_ctx : HDD context
6130 * @command : command string from user command (including "NAPI")
6131 * @command_len: length of command
6132 * @priv_data : ifr_data
6133 *
6134 * Commands supported:
6135 * NAPI ENABLE : enables NAPI administratively. Note that this may not
6136 * enable NAPI functionally, as some other conditions
6137 * may not have been satisfied yet
6138 * NAPI DISABLE : reverse operation of "enable"
6139 * NAPI STATUS : get global status of NAPI instances
6140 * NAPI STATS [<n>] : get the stats for a given NAPI instance
6141 * NAPI SCALE <n> : set the scale factor
6142 *
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006143 * Return: 0: success; !0: failure
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006144 */
6145static int drv_cmd_napi(hdd_adapter_t *adapter,
6146 hdd_context_t *hdd_ctx,
6147 uint8_t *command,
6148 uint8_t command_len,
6149 hdd_priv_data_t *priv_data)
6150{
6151 int rc = 0;
6152 int n, l;
6153 char *cmd = NULL, *subcmd = NULL, *aux = NULL;
6154 char *synopsis = "NAPI ENABLE\n"
6155 "NAPI DISABLE\n"
6156 "NAPI STATUS\n"
6157 "NAPI STATS [<n>] -- if no <n> then all\n"
6158 "NAPI SCALE <n> -- set the scale\n";
6159 char *reply = NULL;
6160
6161 /* make a local copy, as strsep modifies the str in place */
6162 char *str = NULL;
6163
6164 NAPI_DEBUG("-->\n");
6165
6166 /**
6167 * NOTE TO MAINTAINER: from this point to the end of the function,
6168 * please do not return anywhere in the code except the very end
6169 * to avoid memory leakage (goto end_drv_napi instead)
6170 * or make sure that reply+str is freed
6171 */
6172 reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
6173 if (NULL == reply) {
6174 hdd_err("could not allocate reply buffer");
6175 rc = -ENOMEM;
6176 goto end_drv_napi;
6177 }
6178
6179 str = kmalloc(strlen(command) + 1, GFP_KERNEL);
6180 if (NULL == str) {
6181 hdd_err("could not allocate copy of input buffer");
6182 rc = -ENOMEM;
6183 goto end_drv_napi;
6184 }
6185
6186 strlcpy(str, command, strlen(command) + 1);
6187 hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
6188 cmd, subcmd, aux);
6189
6190
6191 rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
6192
6193 if (0 != rc) {
6194 const char *msg = "unknown or badly formatted cmd\n%s";
Anurag Chouhan6d760662016-02-20 16:05:43 +05306195 l = QDF_MIN(MAX_USER_COMMAND_SIZE,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006196 strlen(msg)+strlen(synopsis));
6197 n = scnprintf(reply, l, msg, synopsis);
6198
6199 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306200 QDF_MIN(priv_data->total_len, l)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006201 hdd_err("failed to copy data to user buffer");
6202 hdd_debug("reply: %s", reply);
6203
6204 rc = -EINVAL;
6205 } else {
6206 hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
6207 cmd, subcmd, aux);
6208 if (!strcmp(subcmd, "ENABLE"))
6209 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
6210 else if (!strcmp(subcmd, "DISABLE"))
6211 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
6212 else if (!strcmp(subcmd, "STATUS")) {
6213 int n = 0;
6214 uint32_t i;
6215 struct qca_napi_data *napi_data;
6216
6217 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006218 if (unlikely(NULL == napi_data))
6219 goto status_end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006220 n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
6221 "NAPI state: 0x%08x map: 0x%08x\n",
6222 napi_data->state,
6223 napi_data->ce_map);
6224
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006225 for (i = 0; i < CE_COUNT_MAX; i++)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006226 if (napi_data->ce_map & (0x01 << i)) {
6227 n += scnprintf(
6228 reply + n,
6229 MAX_USER_COMMAND_SIZE - n,
6230 "#%d: id: %d, scale=%d\n",
6231 i,
6232 napi_data->napis[i].id,
6233 napi_data->napis[i].scale);
6234 }
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006235 status_end:
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006236 hdd_info("wlan: STATUS DATA:\n%s", reply);
6237 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306238 QDF_MIN(n, priv_data->total_len)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006239 rc = -EINVAL;
6240 } else if (!strcmp(subcmd, "STATS")) {
6241 int n = 0;
6242 struct qca_napi_data *napi_data;
6243
6244 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08006245 if (NULL != napi_data) {
6246 n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
6247 aux, napi_data);
6248 NAPI_DEBUG("STATS: returns %d\n", n);
6249 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006250 if (n > 0) {
6251 if (copy_to_user(priv_data->buf, reply,
Anurag Chouhan6d760662016-02-20 16:05:43 +05306252 QDF_MIN(priv_data->total_len,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006253 n)))
6254 rc = -EINVAL;
6255 hdd_info("wlan: STATS_DATA\n%s\n", reply);
6256 } else
6257 rc = -EINVAL;
6258 } else if (!strcmp(subcmd, "SCALE")) {
6259 if (NULL == aux) {
6260 rc = -EINVAL;
6261 hdd_err("wlan: SCALE cmd requires <n>");
6262 } else {
6263 uint8_t sc;
6264 rc = kstrtou8(aux, 10, &sc);
6265 if (rc) {
6266 hdd_err("wlan: bad scale (%s)", aux);
6267 rc = -EINVAL;
6268 } else
6269 napi_set_scale(sc);
6270 }
6271 } /* SCALE */
6272 }
6273end_drv_napi:
6274 if (NULL != str)
6275 kfree(str);
6276 if (NULL != reply)
6277 kfree(reply);
6278
6279 NAPI_DEBUG("<--[rc=%d]\n", rc);
6280 return rc;
6281}
6282#endif /* FEATURE_NAPI */
6283
6284/**
6285 * hdd_set_rx_filter() - set RX filter
6286 * @adapter: Pointer to adapter
6287 * @action: Filter action
6288 * @pattern: Address pattern
6289 *
6290 * Address pattern is most significant byte of address for example
6291 * 0x01 for IPV4 multicast address
6292 * 0x33 for IPV6 multicast address
6293 * 0xFF for broadcast address
6294 *
6295 * Return: 0 for success, non-zero for failure
6296 */
6297static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
6298 uint8_t pattern)
6299{
6300 int ret;
6301 uint8_t i;
6302 tHalHandle handle;
6303 tSirRcvFltMcAddrList *filter;
6304 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
6305
6306 ret = wlan_hdd_validate_context(hdd_ctx);
6307 if (0 != ret)
6308 return ret;
6309
6310 handle = hdd_ctx->hHal;
6311
6312 if (NULL == handle) {
6313 hdd_err("HAL Handle is NULL");
6314 return -EINVAL;
6315 }
6316
6317 /*
6318 * If action is false it means start dropping packets
6319 * Set addr_filter_pattern which will be used when sending
6320 * MC/BC address list to target
6321 */
6322 if (!action)
6323 adapter->addr_filter_pattern = pattern;
6324 else
6325 adapter->addr_filter_pattern = 0;
6326
Krunal Sonibe766b02016-03-10 13:00:44 -08006327 if (((adapter->device_mode == QDF_STA_MODE) ||
6328 (adapter->device_mode == QDF_P2P_CLIENT_MODE)) &&
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006329 adapter->mc_addr_list.mc_cnt &&
6330 hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
6331
6332
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306333 filter = qdf_mem_malloc(sizeof(*filter));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006334 if (NULL == filter) {
6335 hdd_err("Could not allocate Memory");
6336 return -ENOMEM;
6337 }
6338 filter->action = action;
6339 for (i = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
6340 if (!memcmp(adapter->mc_addr_list.addr[i],
6341 &pattern, 1)) {
Srinivas Girigowda98530492015-11-20 17:39:24 -08006342 memcpy(filter->multicastAddr[i].bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006343 adapter->mc_addr_list.addr[i],
6344 sizeof(adapter->mc_addr_list.addr[i]));
6345 filter->ulMulticastAddrCnt++;
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006346 hdd_info("%s RX filter : addr ="
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006347 MAC_ADDRESS_STR,
6348 action ? "setting" : "clearing",
Srinivas Girigowda98530492015-11-20 17:39:24 -08006349 MAC_ADDR_ARRAY(filter->multicastAddr[i].bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006350 }
6351 }
6352 /* Set rx filter */
6353 sme_8023_multicast_list(handle, adapter->sessionId, filter);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05306354 qdf_mem_free(filter);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006355 } else {
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08006356 hdd_info("mode %d mc_cnt %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006357 adapter->device_mode, adapter->mc_addr_list.mc_cnt);
6358 }
6359
6360 return 0;
6361}
6362
6363/**
6364 * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
6365 * @command: Pointer to input string driver command
6366 * @adapter: Pointer to adapter
6367 * @action: Action to enable/disable filtering
6368 *
6369 * If action == false
6370 * Start filtering out data packets based on type
6371 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
6372 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
6373 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
6374 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
6375 *
6376 * if action == true
6377 * Stop filtering data packets based on type
6378 * RXFILTER-ADD 0 -> Stop filtering unicast data packets
6379 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets
6380 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
6381 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
6382 *
6383 * Current implementation only supports IPV4 address filtering by
6384 * selectively allowing IPV4 multicast data packest based on
6385 * address list received in .ndo_set_rx_mode
6386 *
6387 * Return: 0 for success, non-zero for failure
6388 */
6389static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
6390 hdd_adapter_t *adapter,
6391 bool action)
6392{
6393 int ret = 0;
6394 uint8_t *value;
6395 uint8_t type;
6396
6397 value = command;
6398 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
6399 if (!action)
6400 value = command + 16;
6401 else
6402 value = command + 13;
6403 ret = kstrtou8(value, 10, &type);
6404 if (ret < 0) {
6405 hdd_err("kstrtou8 failed invalid input value %d", type);
6406 return -EINVAL;
6407 }
6408
6409 switch (type) {
6410 case 2:
6411 /* Set rx filter for IPV4 multicast data packets */
6412 ret = hdd_set_rx_filter(adapter, action, 0x01);
6413 break;
6414 default:
6415 hdd_info("Unsupported RXFILTER type %d", type);
6416 break;
6417 }
6418
6419 return ret;
6420}
6421
6422/**
6423 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler
6424 * @adapter: Pointer to network adapter
6425 * @hdd_ctx: Pointer to hdd context
6426 * @command: Pointer to input command
6427 * @command_len: Command length
6428 * @priv_data: Pointer to private data in command
6429 */
6430static int drv_cmd_rx_filter_remove(hdd_adapter_t *adapter,
6431 hdd_context_t *hdd_ctx,
6432 uint8_t *command,
6433 uint8_t command_len,
6434 hdd_priv_data_t *priv_data)
6435{
6436 return hdd_driver_rxfilter_comand_handler(command, adapter, false);
6437}
6438
6439/**
6440 * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler
6441 * @adapter: Pointer to network adapter
6442 * @hdd_ctx: Pointer to hdd context
6443 * @command: Pointer to input command
6444 * @command_len: Command length
6445 * @priv_data: Pointer to private data in command
6446 */
6447static int drv_cmd_rx_filter_add(hdd_adapter_t *adapter,
6448 hdd_context_t *hdd_ctx,
6449 uint8_t *command,
6450 uint8_t command_len,
6451 hdd_priv_data_t *priv_data)
6452{
6453 return hdd_driver_rxfilter_comand_handler(command, adapter, true);
6454}
6455
Archana Ramachandran393f3792015-11-13 17:13:21 -08006456/**
6457 * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE
6458 * command
6459 * @value: Pointer to SETANTENNAMODE command
6460 * @mode: Pointer to antenna mode
6461 * @reason: Pointer to reason for set antenna mode
6462 *
6463 * This function parses the SETANTENNAMODE command passed in the format
6464 * SETANTENNAMODE<space>mode
6465 *
6466 * Return: 0 for success non-zero for failure
6467 */
6468static int hdd_parse_setantennamode_command(const uint8_t *value)
6469{
6470 const uint8_t *in_ptr = value;
6471 int tmp, v;
6472 char arg1[32];
6473
6474 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
6475
6476 /* no argument after the command */
6477 if (NULL == in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006478 hdd_err("No argument after the command");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006479 return -EINVAL;
6480 }
6481
6482 /* no space after the command */
6483 if (SPACE_ASCII_VALUE != *in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006484 hdd_err("No space after the command");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006485 return -EINVAL;
6486 }
6487
6488 /* remove empty spaces */
6489 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
6490 in_ptr++;
6491
6492 /* no argument followed by spaces */
6493 if ('\0' == *in_ptr) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006494 hdd_err("No argument followed by spaces");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006495 return -EINVAL;
6496 }
6497
6498 /* get the argument i.e. antenna mode */
6499 v = sscanf(in_ptr, "%31s ", arg1);
6500 if (1 != v) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006501 hdd_err("argument retrieval from cmd string failed");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006502 return -EINVAL;
6503 }
6504
6505 v = kstrtos32(arg1, 10, &tmp);
6506 if (v < 0) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07006507 hdd_err("argument string to int conversion failed");
Archana Ramachandran393f3792015-11-13 17:13:21 -08006508 return -EINVAL;
6509 }
6510
6511 return tmp;
6512}
6513
6514/**
6515 * hdd_is_supported_chain_mask_2x2() - Verify if supported chain
6516 * mask is 2x2 mode
6517 * @hdd_ctx: Pointer to hdd contex
6518 *
6519 * Return: true if supported chain mask 2x2 else false
6520 */
6521static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx)
6522{
6523 /*
6524 * Revisit and the update logic to determine the number
6525 * of TX/RX chains supported in the system when
6526 * antenna sharing per band chain mask support is
6527 * brought in
6528 */
6529 return (hdd_ctx->config->enable2x2 == 0x01) ? true : false;
6530}
6531
6532/**
6533 * hdd_is_supported_chain_mask_1x1() - Verify if the supported
6534 * chain mask is 1x1
6535 * @hdd_ctx: Pointer to hdd contex
6536 *
6537 * Return: true if supported chain mask 1x1 else false
6538 */
6539static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx)
6540{
6541 /*
6542 * Revisit and update the logic to determine the number
6543 * of TX/RX chains supported in the system when
6544 * antenna sharing per band chain mask support is
6545 * brought in
6546 */
6547 return (!hdd_ctx->config->enable2x2) ? true : false;
6548}
6549
6550/**
6551 * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command
6552 * handler
6553 * @adapter: Pointer to network adapter
6554 * @hdd_ctx: Pointer to hdd context
6555 * @command: Pointer to input command
6556 * @command_len: Command length
6557 * @priv_data: Pointer to private data in command
6558 */
6559static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter,
6560 hdd_context_t *hdd_ctx,
6561 uint8_t *command,
6562 uint8_t command_len,
6563 hdd_priv_data_t *priv_data)
6564{
6565 struct sir_antenna_mode_param params;
6566 QDF_STATUS status;
6567 int ret = 0;
6568 int mode;
6569 uint8_t *value = command;
6570 uint8_t smps_mode;
6571 uint8_t smps_enable;
6572
6573 if (((1 << QDF_STA_MODE) != hdd_ctx->concurrency_mode) ||
6574 (hdd_ctx->no_of_active_sessions[QDF_STA_MODE] > 1)) {
6575 hdd_err("Operation invalid in non sta or concurrent mode");
6576 ret = -EPERM;
6577 goto exit;
6578 }
6579
6580 mode = hdd_parse_setantennamode_command(value);
6581 if (mode < 0) {
6582 hdd_err("Invalid SETANTENNA command");
6583 ret = mode;
6584 goto exit;
6585 }
6586
6587 hdd_info("Processing antenna mode switch to: %d", mode);
6588
6589 if (hdd_ctx->current_antenna_mode == mode) {
6590 hdd_err("System already in the requested mode");
6591 ret = 0;
6592 goto exit;
6593 }
6594
6595 if ((HDD_ANTENNA_MODE_2X2 == mode) &&
6596 (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) {
6597 hdd_err("System does not support 2x2 mode");
6598 ret = -EPERM;
6599 goto exit;
6600 }
6601
6602 if ((HDD_ANTENNA_MODE_1X1 == mode) &&
6603 hdd_is_supported_chain_mask_1x1(hdd_ctx)) {
6604 hdd_err("System only supports 1x1 mode");
6605 ret = 0;
6606 goto exit;
6607 }
6608
6609 switch (mode) {
6610 case HDD_ANTENNA_MODE_1X1:
6611 params.num_rx_chains = 1;
6612 params.num_tx_chains = 1;
6613 break;
6614 case HDD_ANTENNA_MODE_2X2:
6615 params.num_rx_chains = 2;
6616 params.num_tx_chains = 2;
6617 break;
6618 default:
6619 hdd_err("unsupported antenna mode");
6620 ret = -EINVAL;
6621 goto exit;
6622 }
6623
6624 params.set_antenna_mode_resp =
6625 (void *)wlan_hdd_soc_set_antenna_mode_cb;
6626 hdd_info("Set antenna mode rx chains: %d tx chains: %d",
6627 params.num_rx_chains,
6628 params.num_tx_chains);
6629
6630
6631 INIT_COMPLETION(hdd_ctx->set_antenna_mode_cmpl);
6632 status = sme_soc_set_antenna_mode(hdd_ctx->hHal, &params);
6633 if (QDF_STATUS_SUCCESS != status) {
6634 hdd_err("set antenna mode failed status : %d", status);
6635 ret = -EFAULT;
6636 goto exit;
6637 }
6638
6639 ret = wait_for_completion_timeout(
6640 &hdd_ctx->set_antenna_mode_cmpl,
6641 msecs_to_jiffies(WLAN_WAIT_TIME_ANTENNA_MODE_REQ));
6642 if (!ret) {
6643 ret = -EFAULT;
6644 hdd_err("send set antenna mode timed out");
6645 goto exit;
6646 }
6647
6648 /* Update SME SMPS config */
6649 if (HDD_ANTENNA_MODE_1X1 == mode) {
6650 smps_enable = true;
6651 smps_mode = HDD_SMPS_MODE_STATIC;
6652 } else {
6653 smps_enable = false;
6654 smps_mode = HDD_SMPS_MODE_DISABLED;
6655 }
6656
6657 hdd_info("Update SME SMPS enable: %d mode: %d",
6658 smps_enable, smps_mode);
6659 status = sme_update_mimo_power_save(
6660 hdd_ctx->hHal, smps_enable, smps_mode, false);
6661 if (QDF_STATUS_SUCCESS != status) {
6662 hdd_err("Update SMPS config failed enable: %d mode: %d status: %d",
6663 smps_enable, smps_mode, status);
6664 ret = -EFAULT;
6665 goto exit;
6666 }
6667
6668 hdd_info("Successfully switched to mode: %d x %d", mode, mode);
6669 ret = 0;
6670 hdd_ctx->current_antenna_mode = mode;
6671
6672exit:
6673 hdd_info("Set antenna status: %d current mode: %d",
6674 ret, hdd_ctx->current_antenna_mode);
6675 return ret;
6676
6677}
6678
6679/**
6680 * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command
6681 * handler
6682 * @adapter: Pointer to hdd adapter
6683 * @hdd_ctx: Pointer to hdd context
6684 * @command: Pointer to input command
6685 * @command_len: length of the command
6686 * @priv_data: private data coming with the driver command
6687 *
6688 * Return: 0 for success non-zero for failure
6689 */
6690static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter,
6691 hdd_context_t *hdd_ctx,
6692 uint8_t *command,
6693 uint8_t command_len,
6694 hdd_priv_data_t *priv_data)
6695{
6696 uint32_t antenna_mode = 0;
6697 char extra[32];
6698 uint8_t len = 0;
6699
6700 antenna_mode = hdd_ctx->current_antenna_mode;
6701 len = scnprintf(extra, sizeof(extra), "%s %d", command,
6702 antenna_mode);
6703 len = QDF_MIN(priv_data->total_len, len + 1);
6704 if (copy_to_user(priv_data->buf, &extra, len)) {
6705 hdd_err("Failed to copy data to user buffer");
6706 return -EFAULT;
6707 }
6708
6709 hdd_info("Get antenna mode: %d", antenna_mode);
6710
6711 return 0;
6712}
6713
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006714/*
6715 * dummy (no-op) hdd driver command handler
6716 */
6717static int drv_cmd_dummy(hdd_adapter_t *adapter,
6718 hdd_context_t *hdd_ctx,
6719 uint8_t *command,
6720 uint8_t command_len,
6721 hdd_priv_data_t *priv_data)
6722{
6723 hdd_info("%s: Ignoring driver command \"%s\"",
6724 adapter->dev->name, command);
6725 return 0;
6726}
6727
6728/*
6729 * handler for any unsupported wlan hdd driver command
6730 */
6731static int drv_cmd_invalid(hdd_adapter_t *adapter,
6732 hdd_context_t *hdd_ctx,
6733 uint8_t *command,
6734 uint8_t command_len,
6735 hdd_priv_data_t *priv_data)
6736{
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05306737 MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006738 TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
6739 adapter->sessionId, 0));
6740
6741 hdd_warn("%s: Unsupported driver command \"%s\"",
6742 adapter->dev->name, command);
6743
6744 return -ENOTSUPP;
6745}
6746
6747/**
6748 * drv_cmd_set_fcc_channel() - handle fcc constraint request
6749 * @adapter: HDD adapter
6750 * @hdd_ctx: HDD context
6751 * @command: command ptr, SET_FCC_CHANNEL 0/1 is the command
6752 * @command_len: command len
6753 * @priv_data: private data
6754 *
6755 * Return: status
6756 */
6757static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter,
6758 hdd_context_t *hdd_ctx,
6759 uint8_t *command,
6760 uint8_t command_len,
6761 hdd_priv_data_t *priv_data)
6762{
6763 uint8_t *value;
6764 uint8_t fcc_constraint;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306765 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006766 int ret = 0;
6767
6768 /*
6769 * this command would be called by user-space when it detects WLAN
6770 * ON after airplane mode is set. When APM is set, WLAN turns off.
6771 * But it can be turned back on. Otherwise; when APM is turned back
6772 * off, WLAN would turn back on. So at that point the command is
6773 * expected to come down. 0 means disable, 1 means enable. The
6774 * constraint is removed when parameter 1 is set or different
6775 * country code is set
6776 */
6777
6778 value = command + command_len + 1;
6779
6780 ret = kstrtou8(value, 10, &fcc_constraint);
6781 if ((ret < 0) || (fcc_constraint > 1)) {
6782 /*
6783 * If the input value is greater than max value of datatype,
6784 * then also it is a failure
6785 */
6786 hdd_err("value out of range");
6787 return -EINVAL;
6788 }
6789
6790 status = sme_disable_non_fcc_channel(hdd_ctx->hHal, !fcc_constraint);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05306791 if (status != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006792 hdd_err("sme disable fn. returned err");
6793 ret = -EPERM;
6794 }
6795
6796 return ret;
6797}
6798
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05306799/**
6800 * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH
6801 * command
6802 * @value: Pointer to the command
6803 * @chan_number: Pointer to the channel number
6804 * @chan_bw: Pointer to the channel bandwidth
6805 *
6806 * Parses and provides the channel number and channel width from the input
6807 * command which is expected to be of the format: CHANNEL_SWITCH <CH> <BW>
6808 * <CH> is channel number to move (where 1 = channel 1, 149 = channel 149, ...)
6809 * <BW> is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80)
6810 *
6811 * Return: 0 for success, non-zero for failure
6812 */
6813static int hdd_parse_set_channel_switch_command(uint8_t *value,
6814 uint32_t *chan_number,
6815 uint32_t *chan_bw)
6816{
6817 const uint8_t *in_ptr = value;
6818 int ret;
6819
6820 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
6821
6822 /* no argument after the command */
6823 if (NULL == in_ptr) {
6824 hdd_err("No argument after the command");
6825 return -EINVAL;
6826 }
6827
6828 /* no space after the command */
6829 if (SPACE_ASCII_VALUE != *in_ptr) {
6830 hdd_err("No space after the command ");
6831 return -EINVAL;
6832 }
6833
6834 /* remove empty spaces and move the next argument */
6835 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
6836 in_ptr++;
6837
6838 /* no argument followed by spaces */
6839 if ('\0' == *in_ptr) {
6840 hdd_err("No argument followed by spaces");
6841 return -EINVAL;
6842 }
6843
6844 /* get the two arguments: channel number and bandwidth */
6845 ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw);
6846 if (ret != 2) {
6847 hdd_err("Arguments retrieval from cmd string failed");
6848 return -EINVAL;
6849 }
6850
6851 return 0;
6852}
6853
6854/**
6855 * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel
6856 * @adapter: HDD adapter
6857 * @hdd_ctx: HDD context
6858 * @command: Pointer to the input command CHANNEL_SWITCH
6859 * @command_len: Command len
6860 * @priv_data: Private data
6861 *
6862 * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel
6863 * of SAP/P2P-GO
6864 *
6865 * Return: 0 for success, non-zero for failure
6866 */
6867static int drv_cmd_set_channel_switch(hdd_adapter_t *adapter,
6868 hdd_context_t *hdd_ctx,
6869 uint8_t *command,
6870 uint8_t command_len,
6871 hdd_priv_data_t *priv_data)
6872{
6873 struct net_device *dev = adapter->dev;
6874 int status;
6875 uint32_t chan_number = 0, chan_bw = 0;
6876 uint8_t *value = command;
Kiran Kumar Lokere13644672016-02-29 15:40:10 -08006877 enum phy_ch_width width;
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05306878
Krunal Sonibe766b02016-03-10 13:00:44 -08006879 if ((adapter->device_mode != QDF_P2P_GO_MODE) &&
6880 (adapter->device_mode != QDF_SAP_MODE)) {
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05306881 hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d",
6882 adapter->device_mode);
6883 return -EINVAL;
6884 }
6885
6886 status = hdd_parse_set_channel_switch_command(value,
6887 &chan_number, &chan_bw);
6888 if (status) {
6889 hdd_err("Invalid CHANNEL_SWITCH command");
6890 return status;
6891 }
6892
6893 if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80)) {
6894 hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw);
6895 return -EINVAL;
6896 }
6897
6898 if (chan_bw == 80)
6899 width = CH_WIDTH_80MHZ;
6900 else if (chan_bw == 40)
6901 width = CH_WIDTH_40MHZ;
6902 else
6903 width = CH_WIDTH_20MHZ;
6904
6905 hdd_info("CH:%d BW:%d", chan_number, chan_bw);
6906
6907 status = hdd_softap_set_channel_change(dev, chan_number, width);
6908 if (status) {
6909 hdd_err("Set channel change fail");
6910 return status;
6911 }
6912
6913 return 0;
6914}
6915
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006916/*
6917 * The following table contains all supported WLAN HDD
6918 * IOCTL driver commands and the handler for each of them.
6919 */
6920static const hdd_drv_cmd_t hdd_drv_cmds[] = {
6921 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr},
6922 {"P2P_SET_NOA", drv_cmd_p2p_set_noa},
6923 {"P2P_SET_PS", drv_cmd_p2p_set_ps},
6924 {"SETBAND", drv_cmd_set_band},
6925 {"SETWMMPS", drv_cmd_set_wmmps},
6926 {"COUNTRY", drv_cmd_country},
6927 {"SETSUSPENDMODE", drv_cmd_dummy},
6928 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy},
6929 {"BTCOEXSCAN", drv_cmd_dummy},
6930 {"RXFILTER", drv_cmd_dummy},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006931 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger},
6932 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger},
6933 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period},
6934 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period},
6935 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period},
6936 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006937 {"SETROAMMODE", drv_cmd_set_roam_mode},
6938 {"GETROAMMODE", drv_cmd_get_roam_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006939 {"SETROAMDELTA", drv_cmd_set_roam_delta},
6940 {"GETROAMDELTA", drv_cmd_get_roam_delta},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006941 {"GETBAND", drv_cmd_get_band},
6942 {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels},
6943 {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels},
6944 {"GETCCXMODE", drv_cmd_get_ccx_mode},
6945 {"GETOKCMODE", drv_cmd_get_okc_mode},
6946 {"GETFASTROAM", drv_cmd_get_fast_roam},
6947 {"GETFASTTRANSITION", drv_cmd_get_fast_transition},
6948 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time},
6949 {"SENDACTIONFRAME", drv_cmd_send_action_frame},
6950 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time},
6951 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time},
6952 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time},
6953 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time},
6954 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time},
6955 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band},
6956 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band},
6957 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes},
6958 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes},
6959 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time},
6960 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time},
6961 {"REASSOC", drv_cmd_reassoc},
6962 {"SETWESMODE", drv_cmd_set_wes_mode},
6963 {"GETWESMODE", drv_cmd_get_wes_mode},
6964 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff},
6965 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff},
6966 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff},
6967 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006968 {"SETFASTROAM", drv_cmd_set_fast_roam},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006969 {"SETFASTTRANSITION", drv_cmd_set_fast_transition},
6970 {"FASTREASSOC", drv_cmd_fast_reassoc},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006971 {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control},
6972#ifdef FEATURE_WLAN_OKC
6973 {"SETOKCMODE", drv_cmd_set_okc_mode},
6974#endif /* FEATURE_WLAN_OKC */
6975 {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control},
6976 {"BTCOEXMODE", drv_cmd_bt_coex_mode},
6977 {"SCAN-ACTIVE", drv_cmd_scan_active},
6978 {"SCAN-PASSIVE", drv_cmd_scan_passive},
6979 {"GETDWELLTIME", drv_cmd_get_dwell_time},
6980 {"SETDWELLTIME", drv_cmd_set_dwell_time},
6981 {"MIRACAST", drv_cmd_miracast},
Rajeev Kumar8e3e2832015-11-06 16:02:54 -08006982 {"SETIBSSBEACONOUIDATA", drv_cmd_set_ibss_beacon_oui_data},
6983 {"SETRMCENABLE", drv_cmd_set_rmc_enable},
6984 {"SETRMCACTIONPERIOD", drv_cmd_set_rmc_action_period},
6985 {"GETIBSSPEERINFOALL", drv_cmd_get_ibss_peer_info_all},
6986 {"GETIBSSPEERINFO", drv_cmd_get_ibss_peer_info},
6987 {"SETRMCTXRATE", drv_cmd_set_rmc_tx_rate},
6988 {"SETIBSSTXFAILEVENT", drv_cmd_set_ibss_tx_fail_event},
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08006989#ifdef FEATURE_WLAN_ESE
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006990 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels},
6991 {"GETTSMSTATS", drv_cmd_get_tsm_stats},
6992 {"SETCCKMIE", drv_cmd_set_cckm_ie},
6993 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req},
Srinivas Girigowda515a9ef2015-12-11 11:00:48 -08006994#endif /* FEATURE_WLAN_ESE */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006995 {"SETMCRATE", drv_cmd_set_mc_rate},
6996 {"MAXTXPOWER", drv_cmd_max_tx_power},
6997 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode},
6998 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode},
6999 {"GETLINKSTATUS", drv_cmd_get_link_status},
7000#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
7001 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow},
7002 {"SETAPP1PARAMS", drv_cmd_set_app1_params},
7003 {"SETAPP2PARAMS", drv_cmd_set_app2_params},
7004#endif
7005#ifdef FEATURE_WLAN_TDLS
7006 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset},
7007 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode},
7008 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel},
7009 {"TDLSSCAN", drv_cmd_tdls_scan},
7010#endif
7011 {"RSSI", drv_cmd_get_rssi},
7012 {"LINKSPEED", drv_cmd_get_linkspeed},
7013#ifdef FEATURE_NAPI
7014 {"NAPI", drv_cmd_napi},
7015#endif /* FEATURE_NAPI */
7016 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove},
7017 {"RXFILTER-ADD", drv_cmd_rx_filter_add},
7018 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel},
Chandrasekaran, Manishekar794a0982016-01-12 19:42:20 +05307019 {"CHANNEL_SWITCH", drv_cmd_set_channel_switch},
Archana Ramachandran393f3792015-11-13 17:13:21 -08007020 {"SETANTENNAMODE", drv_cmd_set_antenna_mode},
7021 {"GETANTENNAMODE", drv_cmd_get_antenna_mode},
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007022};
7023
7024/**
7025 * hdd_drv_cmd_process() - chooses and runs the proper
7026 * handler based on the input command
7027 * @adapter: Pointer to the hdd adapter
7028 * @cmd: Pointer to the driver command
7029 * @priv_data: Pointer to the data associated with the command
7030 *
7031 * This function parses the input hdd driver command and runs
7032 * the proper handler
7033 *
7034 * Return: 0 for success non-zero for failure
7035 */
7036static int hdd_drv_cmd_process(hdd_adapter_t *adapter,
7037 uint8_t *cmd,
7038 hdd_priv_data_t *priv_data)
7039{
7040 hdd_context_t *hdd_ctx;
7041 int i;
7042 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds);
7043 uint8_t *cmd_i = NULL;
7044 hdd_drv_cmd_handler_t handler = NULL;
7045 int len = 0;
7046
7047 if (!adapter || !cmd || !priv_data) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007048 hdd_err("at least 1 param is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007049 return -EINVAL;
7050 }
7051
7052 hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
7053
7054 for (i = 0; i < cmd_num_total; i++) {
7055
7056 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd;
7057 handler = hdd_drv_cmds[i].handler;
7058 len = strlen(cmd_i);
7059
7060 if (!handler) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007061 hdd_err("no. %d handler is NULL", i);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007062 return -EINVAL;
7063 }
7064
7065 if (strncasecmp(cmd, cmd_i, len) == 0)
7066 return handler(adapter, hdd_ctx,
7067 cmd, len, priv_data);
7068 }
7069
7070 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data);
7071}
7072
7073/**
7074 * hdd_driver_command() - top level wlan hdd driver command handler
7075 * @adapter: Pointer to the hdd adapter
7076 * @priv_data: Pointer to the raw command data
7077 *
7078 * This function is the top level wlan hdd driver command handler. It
7079 * handles the command with the help of hdd_drv_cmd_process()
7080 *
7081 * Return: 0 for success non-zero for failure
7082 */
7083static int hdd_driver_command(hdd_adapter_t *adapter,
7084 hdd_priv_data_t *priv_data)
7085{
7086 uint8_t *command = NULL;
7087 int ret = 0;
7088
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307089 ENTER();
7090
Anurag Chouhan6d760662016-02-20 16:05:43 +05307091 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007092 hdd_err("Command not allowed in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007093 return -EINVAL;
7094 }
7095
7096 /*
7097 * Note that valid pointers are provided by caller
7098 */
7099
7100 /* copy to local struct to avoid numerous changes to legacy code */
7101 if (priv_data->total_len <= 0 ||
7102 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007103 hdd_warn("Invalid priv_data.total_len(%d)!!!",
7104 priv_data->total_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007105 ret = -EINVAL;
7106 goto exit;
7107 }
7108
7109 /* Allocate +1 for '\0' */
7110 command = kmalloc(priv_data->total_len + 1, GFP_KERNEL);
7111 if (!command) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007112 hdd_err("failed to allocate memory");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007113 ret = -ENOMEM;
7114 goto exit;
7115 }
7116
7117 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) {
7118 ret = -EFAULT;
7119 goto exit;
7120 }
7121
7122 /* Make sure the command is NUL-terminated */
7123 command[priv_data->total_len] = '\0';
7124
7125 hdd_info("%s: %s", adapter->dev->name, command);
7126 ret = hdd_drv_cmd_process(adapter, command, priv_data);
7127
7128exit:
7129 if (command)
7130 kfree(command);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307131 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007132 return ret;
7133}
7134
7135#ifdef CONFIG_COMPAT
7136static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7137{
7138 struct {
7139 compat_uptr_t buf;
7140 int used_len;
7141 int total_len;
7142 } compat_priv_data;
7143 hdd_priv_data_t priv_data;
7144 int ret = 0;
7145
7146 /*
7147 * Note that adapter and ifr have already been verified by caller,
7148 * and HDD context has also been validated
7149 */
7150 if (copy_from_user(&compat_priv_data, ifr->ifr_data,
7151 sizeof(compat_priv_data))) {
7152 ret = -EFAULT;
7153 goto exit;
7154 }
7155 priv_data.buf = compat_ptr(compat_priv_data.buf);
7156 priv_data.used_len = compat_priv_data.used_len;
7157 priv_data.total_len = compat_priv_data.total_len;
7158 ret = hdd_driver_command(adapter, &priv_data);
7159exit:
7160 return ret;
7161}
7162#else /* CONFIG_COMPAT */
7163static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7164{
7165 /* will never be invoked */
7166 return 0;
7167}
7168#endif /* CONFIG_COMPAT */
7169
7170static int hdd_driver_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
7171{
7172 hdd_priv_data_t priv_data;
7173 int ret = 0;
7174
7175 /*
7176 * Note that adapter and ifr have already been verified by caller,
7177 * and HDD context has also been validated
7178 */
7179 if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data)))
7180 ret = -EFAULT;
7181 else
7182 ret = hdd_driver_command(adapter, &priv_data);
7183
7184 return ret;
7185}
7186
7187/**
7188 * __hdd_ioctl() - ioctl handler for wlan network interfaces
7189 * @dev: device upon which the ioctl was received
7190 * @ifr: ioctl request information
7191 * @cmd: ioctl command
7192 *
7193 * This function does initial processing of wlan device ioctls.
7194 * Currently two flavors of ioctls are supported. The primary ioctl
7195 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used
7196 * for Android "DRIVER" commands. The other ioctl that is
7197 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used
7198 * for FTM on some platforms. This function simply verifies that the
7199 * driver is in a sane state, and that the ioctl is one of the
7200 * supported flavors, in which case flavor-specific handlers are
7201 * dispatched.
7202 *
7203 * Return: 0 on success, non-zero on error
7204 */
7205static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7206{
7207 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
7208 hdd_context_t *hdd_ctx;
7209 int ret;
7210
Jeff Johnson3c3994a2016-02-11 08:12:30 -08007211 ENTER_DEV(dev);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307212
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007213 if (dev != adapter->dev) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007214 hdd_alert("HDD adapter/dev inconsistency");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007215 ret = -ENODEV;
7216 goto exit;
7217 }
7218
7219 if ((!ifr) || (!ifr->ifr_data)) {
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007220 hdd_err("invalid data");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007221 ret = -EINVAL;
7222 goto exit;
7223 }
7224#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
Anurag Chouhan6d760662016-02-20 16:05:43 +05307225 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007226 if (SIOCIOCTLTX99 == cmd) {
7227 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr);
7228 goto exit;
7229 }
7230 }
7231#endif
7232
7233 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
7234 ret = wlan_hdd_validate_context(hdd_ctx);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307235 if (ret)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007236 goto exit;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007237
7238 switch (cmd) {
7239 case (SIOCDEVPRIVATE + 1):
7240 if (is_compat_task())
7241 ret = hdd_driver_compat_ioctl(adapter, ifr);
7242 else
7243 ret = hdd_driver_ioctl(adapter, ifr);
7244 break;
7245 default:
Archana Ramachandran3abc3912016-04-29 17:01:32 -07007246 hdd_err("unknown ioctl %d", cmd);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007247 ret = -EINVAL;
7248 break;
7249 }
7250exit:
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05307251 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08007252 return ret;
7253}
7254
7255/**
7256 * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces
7257 * @dev: device upon which the ioctl was received
7258 * @ifr: ioctl request information
7259 * @cmd: ioctl command
7260 *
7261 * This function acts as an SSR-protecting wrapper to __hdd_ioctl()
7262 * which is where the ioctls are really handled.
7263 *
7264 * Return: 0 on success, non-zero on error
7265 */
7266int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
7267{
7268 int ret;
7269
7270 cds_ssr_protect(__func__);
7271 ret = __hdd_ioctl(dev, ifr, cmd);
7272 cds_ssr_unprotect(__func__);
7273 return ret;
7274}