blob: 885817a58d00d69cc84b51b306be8a34c14ea20c [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/* Include Files */
29
30#include <wlan_hdd_includes.h>
31#include <wlan_hdd_wowl.h>
32#include "wlan_hdd_trace.h"
33#include "wlan_hdd_ioctl.h"
34#include "wlan_hdd_power.h"
35#include "wlan_hdd_driver_ops.h"
36#include "cds_concurrency.h"
37
38#include "wlan_hdd_p2p.h"
39#include <linux/ctype.h>
40#include "wma.h"
41#include "wlan_hdd_napi.h"
42
43#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
44#include <sme_api.h>
45#include <sir_api.h>
46#endif
47#include "hif.h"
48
49#if defined(LINUX_QCMBR)
50#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
51#endif
52
53/*
54 * Size of Driver command strings from upper layer
55 */
56#define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */
57#define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */
58
59
60#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
61#define TID_MIN_VALUE 0
62#define TID_MAX_VALUE 15
63#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
64
65/*
66 * Maximum buffer size used for returning the data back to user space
67 */
68#define WLAN_MAX_BUF_SIZE 1024
69#define WLAN_PRIV_DATA_MAX_LEN 8192
70
71/*
72 * Driver miracast parameters 0-Disabled
73 * 1-Source, 2-Sink
74 */
75#define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0
76#define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2
77
78/*
79 * When ever we need to print IBSSPEERINFOALL for more than 16 STA
80 * we will split the printing.
81 */
82#define NUM_OF_STA_DATA_TO_PRINT 16
83
84/*
85 * Android DRIVER command structures
86 */
87struct android_wifi_reassoc_params {
88 unsigned char bssid[18];
89 int channel;
90};
91
92#define ANDROID_WIFI_ACTION_FRAME_SIZE 1040
93struct android_wifi_af_params {
94 unsigned char bssid[18];
95 int channel;
96 int dwell_time;
97 int len;
98 unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE];
99};
100
101/*
102 * Define HDD driver command handling entry, each contains a command
103 * string and the handler.
104 */
105typedef int (*hdd_drv_cmd_handler_t)(hdd_adapter_t *adapter,
106 hdd_context_t *hdd_ctx,
107 uint8_t *cmd,
108 uint8_t cmd_name_len,
109 hdd_priv_data_t *priv_data);
110
111typedef struct {
112 const char *cmd;
113 hdd_drv_cmd_handler_t handler;
114} hdd_drv_cmd_t;
115
116#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
117#define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000
118#define WLAN_HDD_MAX_TCP_PORT 65535
119#endif
120
121#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
122static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics,
123 const uint32_t staId, void *context)
124{
125 struct statsContext *stats_context = NULL;
126 hdd_adapter_t *adapter = NULL;
127
128 if (NULL == context) {
129 hddLog(CDF_TRACE_LEVEL_ERROR,
130 "%s: Bad param, context [%p]", __func__, context);
131 return;
132 }
133
134 /*
135 * there is a race condition that exists between this callback
136 * function and the caller since the caller could time out either
137 * before or while this code is executing. we use a spinlock to
138 * serialize these actions
139 */
140 spin_lock(&hdd_context_lock);
141
142 stats_context = context;
143 adapter = stats_context->pAdapter;
144 if ((NULL == adapter) ||
145 (STATS_CONTEXT_MAGIC != stats_context->magic)) {
146 /*
147 * the caller presumably timed out so there is
148 * nothing we can do
149 */
150 spin_unlock(&hdd_context_lock);
151 hddLog(CDF_TRACE_LEVEL_WARN,
152 "%s: Invalid context, adapter [%p] magic [%08x]",
153 __func__, adapter, stats_context->magic);
154 return;
155 }
156
157 /* context is valid so caller is still waiting */
158
159 /* paranoia: invalidate the magic */
160 stats_context->magic = 0;
161
162 /* copy over the tsm stats */
163 adapter->tsmStats.UplinkPktQueueDly = tsm_metrics.UplinkPktQueueDly;
164 cdf_mem_copy(adapter->tsmStats.UplinkPktQueueDlyHist,
165 tsm_metrics.UplinkPktQueueDlyHist,
166 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
167 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist[0]));
168 adapter->tsmStats.UplinkPktTxDly = tsm_metrics.UplinkPktTxDly;
169 adapter->tsmStats.UplinkPktLoss = tsm_metrics.UplinkPktLoss;
170 adapter->tsmStats.UplinkPktCount = tsm_metrics.UplinkPktCount;
171 adapter->tsmStats.RoamingCount = tsm_metrics.RoamingCount;
172 adapter->tsmStats.RoamingDly = tsm_metrics.RoamingDly;
173
174 /* notify the caller */
175 complete(&stats_context->completion);
176
177 /* serialization is complete */
178 spin_unlock(&hdd_context_lock);
179}
180
181static
182CDF_STATUS hdd_get_tsm_stats(hdd_adapter_t *adapter,
183 const uint8_t tid,
184 tAniTrafStrmMetrics *tsm_metrics)
185{
186 hdd_station_ctx_t *hdd_sta_ctx = NULL;
187 CDF_STATUS hstatus;
188 CDF_STATUS vstatus = CDF_STATUS_SUCCESS;
189 unsigned long rc;
190 struct statsContext context;
191 hdd_context_t *hdd_ctx = NULL;
192
193 if (NULL == adapter) {
194 hddLog(CDF_TRACE_LEVEL_ERROR,
195 "%s: adapter is NULL", __func__);
196 return CDF_STATUS_E_FAULT;
197 }
198
199 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
200 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
201
202 /* we are connected prepare our callback context */
203 init_completion(&context.completion);
204 context.pAdapter = adapter;
205 context.magic = STATS_CONTEXT_MAGIC;
206
207 /* query tsm stats */
208 hstatus = sme_get_tsm_stats(hdd_ctx->hHal, hdd_get_tsm_stats_cb,
209 hdd_sta_ctx->conn_info.staId[0],
210 hdd_sta_ctx->conn_info.bssId,
211 &context, hdd_ctx->pcds_context, tid);
212 if (CDF_STATUS_SUCCESS != hstatus) {
213 hddLog(CDF_TRACE_LEVEL_ERROR,
214 "%s: Unable to retrieve statistics", __func__);
215 vstatus = CDF_STATUS_E_FAULT;
216 } else {
217 /* request was sent -- wait for the response */
218 rc = wait_for_completion_timeout(&context.completion,
219 msecs_to_jiffies(WLAN_WAIT_TIME_STATS));
220 if (!rc) {
221 hddLog(CDF_TRACE_LEVEL_ERROR,
222 "%s: SME timed out while retrieving statistics",
223 __func__);
224 vstatus = CDF_STATUS_E_TIMEOUT;
225 }
226 }
227
228 /*
229 * either we never sent a request, we sent a request and received a
230 * response or we sent a request and timed out. if we never sent a
231 * request or if we sent a request and got a response, we want to
232 * clear the magic out of paranoia. if we timed out there is a
233 * race condition such that the callback function could be
234 * executing at the same time we are. of primary concern is if the
235 * callback function had already verified the "magic" but had not
236 * yet set the completion variable when a timeout occurred. we
237 * serialize these activities by invalidating the magic while
238 * holding a shared spinlock which will cause us to block if the
239 * callback is currently executing
240 */
241 spin_lock(&hdd_context_lock);
242 context.magic = 0;
243 spin_unlock(&hdd_context_lock);
244
245 if (CDF_STATUS_SUCCESS == vstatus) {
246 tsm_metrics->UplinkPktQueueDly =
247 adapter->tsmStats.UplinkPktQueueDly;
248 cdf_mem_copy(tsm_metrics->UplinkPktQueueDlyHist,
249 adapter->tsmStats.UplinkPktQueueDlyHist,
250 sizeof(adapter->tsmStats.UplinkPktQueueDlyHist) /
251 sizeof(adapter->tsmStats.
252 UplinkPktQueueDlyHist[0]));
253 tsm_metrics->UplinkPktTxDly = adapter->tsmStats.UplinkPktTxDly;
254 tsm_metrics->UplinkPktLoss = adapter->tsmStats.UplinkPktLoss;
255 tsm_metrics->UplinkPktCount = adapter->tsmStats.UplinkPktCount;
256 tsm_metrics->RoamingCount = adapter->tsmStats.RoamingCount;
257 tsm_metrics->RoamingDly = adapter->tsmStats.RoamingDly;
258 }
259 return vstatus;
260}
261#endif /*FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
262
263#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
264static void hdd_get_band_helper(hdd_context_t *hdd_ctx, int *pBand)
265{
266 eCsrBand band = -1;
267 sme_get_freq_band((tHalHandle) (hdd_ctx->hHal), &band);
268 switch (band) {
269 case eCSR_BAND_ALL:
270 *pBand = WLAN_HDD_UI_BAND_AUTO;
271 break;
272
273 case eCSR_BAND_24:
274 *pBand = WLAN_HDD_UI_BAND_2_4_GHZ;
275 break;
276
277 case eCSR_BAND_5G:
278 *pBand = WLAN_HDD_UI_BAND_5_GHZ;
279 break;
280
281 default:
282 hddLog(CDF_TRACE_LEVEL_WARN, "%s: Invalid Band %d", __func__,
283 band);
284 *pBand = -1;
285 break;
286 }
287}
288
289/**
290 * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel
291 * @data: input data
292 * @target_ap_bssid: pointer to bssid (output parameter)
293 * @channel: pointer to channel (output parameter)
294 *
295 * Return: 0 if parsing is successful; -EINVAL otherwise
296 */
297static int _hdd_parse_bssid_and_chan(const uint8_t **data,
298 uint8_t *bssid,
299 uint8_t *channel)
300{
301 const uint8_t *in_ptr;
302 int v = 0;
303 int temp_int;
304 uint8_t temp_buf[32];
305
306 /* 12 hexa decimal digits, 5 ':' and '\0' */
307 uint8_t mac_addr[18];
308
309 if (!data || !*data)
310 return -EINVAL;
311
312 in_ptr = *data;
313
314 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
315 /* no argument after the command */
316 if (NULL == in_ptr)
317 goto error;
318 /* no space after the command */
319 else if (SPACE_ASCII_VALUE != *in_ptr)
320 goto error;
321
322 /* remove empty spaces */
323 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
324 in_ptr++;
325
326 /* no argument followed by spaces */
327 if ('\0' == *in_ptr)
328 goto error;
329
330 v = sscanf(in_ptr, "%17s", mac_addr);
331 if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) {
332 hddLog(LOGE,
333 FL(
334 "Invalid MAC address or All hex inputs are not read (%d)"
335 ),
336 v);
337 goto error;
338 }
339
340 bssid[0] = hex_to_bin(mac_addr[0]) << 4 |
341 hex_to_bin(mac_addr[1]);
342 bssid[1] = hex_to_bin(mac_addr[3]) << 4 |
343 hex_to_bin(mac_addr[4]);
344 bssid[2] = hex_to_bin(mac_addr[6]) << 4 |
345 hex_to_bin(mac_addr[7]);
346 bssid[3] = hex_to_bin(mac_addr[9]) << 4 |
347 hex_to_bin(mac_addr[10]);
348 bssid[4] = hex_to_bin(mac_addr[12]) << 4 |
349 hex_to_bin(mac_addr[13]);
350 bssid[5] = hex_to_bin(mac_addr[15]) << 4 |
351 hex_to_bin(mac_addr[16]);
352
353 /* point to the next argument */
354 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE);
355 /* no argument after the command */
356 if (NULL == in_ptr)
357 goto error;
358
359 /* remove empty spaces */
360 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
361 in_ptr++;
362
363 /* no argument followed by spaces */
364 if ('\0' == *in_ptr)
365 goto error;
366
367 /* get the next argument ie the channel number */
368 v = sscanf(in_ptr, "%31s ", temp_buf);
369 if (1 != v)
370 goto error;
371
372 v = kstrtos32(temp_buf, 10, &temp_int);
373 if ((v < 0) || (temp_int < 0) ||
374 (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX))
375 return -EINVAL;
376
377 *channel = temp_int;
378 *data = in_ptr;
379 return 0;
380error:
381 *data = in_ptr;
382 return -EINVAL;
383}
384
385/**
386 * hdd_parse_send_action_frame_data() - HDD Parse send action frame data
387 * @pValue: Pointer to input data
388 * @pTargetApBssid: Pointer to target Ap bssid
389 * @pChannel: Pointer to the Target AP channel
390 * @pDwellTime: Pointer to the time to stay off-channel
391 * after transmitting action frame
392 * @pBuf: Pointer to data
393 * @pBufLen: Pointer to data length
394 *
395 * This function parses the send action frame data passed in the format
396 * SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
397 *
398 * Return: 0 for success non-zero for failure
399 */
400static int
401hdd_parse_send_action_frame_v1_data(const uint8_t *pValue,
402 uint8_t *pTargetApBssid,
403 uint8_t *pChannel, uint8_t *pDwellTime,
404 uint8_t **pBuf, uint8_t *pBufLen)
405{
406 const uint8_t *inPtr = pValue;
407 const uint8_t *dataEnd;
408 int tempInt;
409 int j = 0;
410 int i = 0;
411 int v = 0;
412 uint8_t tempBuf[32];
413 uint8_t tempByte = 0;
414
415 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
416 return -EINVAL;
417
418 /* point to the next argument */
419 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
420 /* no argument after the command */
421 if (NULL == inPtr)
422 return -EINVAL;
423 /* removing empty spaces */
424 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
425 inPtr++;
426
427 /* no argument followed by spaces */
428 if ('\0' == *inPtr) {
429 return -EINVAL;
430 }
431
432 /* getting the next argument ie the dwell time */
433 v = sscanf(inPtr, "%31s ", tempBuf);
434 if (1 != v)
435 return -EINVAL;
436
437 v = kstrtos32(tempBuf, 10, &tempInt);
438 if (v < 0 || tempInt < 0)
439 return -EINVAL;
440
441 *pDwellTime = tempInt;
442
443 /* point to the next argument */
444 inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
445 /* no argument after the command */
446 if (NULL == inPtr)
447 return -EINVAL;
448 /* removing empty spaces */
449 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
450 inPtr++;
451
452 /* no argument followed by spaces */
453 if ('\0' == *inPtr) {
454 return -EINVAL;
455 }
456
457 /* find the length of data */
458 dataEnd = inPtr;
459 while (('\0' != *dataEnd)) {
460 dataEnd++;
461 }
462 *pBufLen = dataEnd - inPtr;
463 if (*pBufLen <= 0)
464 return -EINVAL;
465
466 /*
467 * Allocate the number of bytes based on the number of input characters
468 * whether it is even or odd.
469 * if the number of input characters are even, then we need N/2 byte.
470 * if the number of input characters are odd, then we need do (N+1)/2
471 * to compensate rounding off.
472 * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
473 * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes
474 */
475 *pBuf = cdf_mem_malloc((*pBufLen + 1) / 2);
476 if (NULL == *pBuf) {
477 hddLog(LOGE, FL("cdf_mem_alloc failed"));
478 return -ENOMEM;
479 }
480
481 /* the buffer received from the upper layer is character buffer,
482 * we need to prepare the buffer taking 2 characters in to a U8 hex
483 * decimal number for example 7f0000f0...form a buffer to contain 7f
484 * in 0th location, 00 in 1st and f0 in 3rd location
485 */
486 for (i = 0, j = 0; j < *pBufLen; j += 2) {
487 if (j + 1 == *pBufLen) {
488 tempByte = hex_to_bin(inPtr[j]);
489 } else {
490 tempByte =
491 (hex_to_bin(inPtr[j]) << 4) |
492 (hex_to_bin(inPtr[j + 1]));
493 }
494 (*pBuf)[i++] = tempByte;
495 }
496 *pBufLen = i;
497 return 0;
498}
499
500/**
501 * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data
502 * @pValue: Pointer to input data (its a NULL terminated string)
503 * @pTargetApBssid: Pointer to target Ap bssid
504 * @pChannel: Pointer to the Target AP channel
505 *
506 * This function parses the reasoc command data passed in the format
507 * REASSOC<space><bssid><space><channel>
508 *
509 * Return: 0 for success non-zero for failure
510 */
511static int hdd_parse_reassoc_command_v1_data(const uint8_t *pValue,
512 uint8_t *pTargetApBssid,
513 uint8_t *pChannel)
514{
515 const uint8_t *inPtr = pValue;
516
517 if (_hdd_parse_bssid_and_chan(&inPtr, pTargetApBssid, pChannel))
518 return -EINVAL;
519
520 return 0;
521}
522
523#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_ESE FEATURE_WLAN_LFR */
524
525#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
526/**
527 * hdd_reassoc() - perform a userspace-directed reassoc
528 * @adapter: Adapter upon which the command was received
529 * @bssid: BSSID with which to reassociate
530 * @channel: channel upon which to reassociate
531 *
532 * This function performs a userspace-directed reassoc operation
533 *
534 * Return: 0 for success non-zero for failure
535 */
536static int
537hdd_reassoc(hdd_adapter_t *adapter, const uint8_t *bssid,
538 const uint8_t channel)
539{
540 hdd_station_ctx_t *pHddStaCtx;
541 int ret = 0;
542
543 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
544 hdd_warn("Unsupported in mode %s(%d)",
545 hdd_device_mode_to_string(adapter->device_mode),
546 adapter->device_mode);
547 return -EINVAL;
548 }
549
550 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
551
552 /* if not associated, no need to proceed with reassoc */
553 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
554 hddLog(CDF_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
555 ret = -EINVAL;
556 goto exit;
557 }
558
559 /*
560 * if the target bssid is same as currently associated AP,
561 * then no need to proceed with reassoc
562 */
563 if (!memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
564 CDF_MAC_ADDR_SIZE)) {
565 hddLog(LOG1,
566 FL("Reassoc BSSID is same as currently associated AP bssid"));
567 ret = -EINVAL;
568 goto exit;
569 }
570
571 /* Check channel number is a valid channel number */
572 if (CDF_STATUS_SUCCESS !=
573 wlan_hdd_validate_operation_channel(adapter, channel)) {
574 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: Invalid Channel %d",
575 __func__, channel);
576 ret = -EINVAL;
577 goto exit;
578 }
579
580 /* Proceed with reassoc */
581 {
582 tCsrHandoffRequest handoffInfo;
583 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
584
585 handoffInfo.channel = channel;
586 handoffInfo.src = REASSOC;
587 cdf_mem_copy(handoffInfo.bssid.bytes, bssid, CDF_MAC_ADDR_SIZE);
588 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
589 &handoffInfo);
590 }
591exit:
592 return ret;
593}
594
595/**
596 * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command
597 * @adapter: Adapter upon which the command was received
598 * @command: ASCII text command that was received
599 *
600 * This function parses the v1 REASSOC command with the format
601 *
602 * REASSOC xx:xx:xx:xx:xx:xx CH
603 *
604 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
605 * BSSID and CH is the ASCII representation of the channel. For
606 * example
607 *
608 * REASSOC 00:0a:0b:11:22:33 48
609 *
610 * Return: 0 for success non-zero for failure
611 */
612static int hdd_parse_reassoc_v1(hdd_adapter_t *adapter, const char *command)
613{
614 uint8_t channel = 0;
615 tSirMacAddr bssid;
616 int ret;
617
618 ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel);
619 if (ret) {
620 hddLog(CDF_TRACE_LEVEL_ERROR,
621 "%s: Failed to parse reassoc command data", __func__);
622 } else {
623 ret = hdd_reassoc(adapter, bssid, channel);
624 }
625 return ret;
626}
627
628/**
629 * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command
630 * @adapter: Adapter upon which the command was received
631 * @command: Command that was received, ASCII command
632 * followed by binary data
633 *
634 * This function parses the v2 REASSOC command with the format
635 *
636 * REASSOC <android_wifi_reassoc_params>
637 *
638 * Return: 0 for success non-zero for failure
639 */
640static int hdd_parse_reassoc_v2(hdd_adapter_t *adapter, const char *command)
641{
642 struct android_wifi_reassoc_params params;
643 tSirMacAddr bssid;
644 int ret;
645
646 /* The params are located after "REASSOC " */
647 memcpy(&params, command + 8, sizeof(params));
648
649 if (!mac_pton(params.bssid, (u8 *) &bssid)) {
650 hddLog(LOGE, "%s: MAC address parsing failed", __func__);
651 ret = -EINVAL;
652 } else {
653 ret = hdd_reassoc(adapter, bssid, params.channel);
654 }
655 return ret;
656}
657
658/**
659 * hdd_parse_reassoc() - parse the REASSOC command
660 * @adapter: Adapter upon which the command was received
661 * @command: Command that was received
662 *
663 * There are two different versions of the REASSOC command. Version 1
664 * of the command contains a parameter list that is ASCII characters
665 * whereas version 2 contains a combination of ASCII and binary
666 * payload. Determine if a version 1 or a version 2 command is being
667 * parsed by examining the parameters, and then dispatch the parser
668 * that is appropriate for the command.
669 *
670 * Return: 0 for success non-zero for failure
671 */
672static int hdd_parse_reassoc(hdd_adapter_t *adapter, const char *command)
673{
674 int ret;
675
676 /* both versions start with "REASSOC "
677 * v1 has a bssid and channel # as an ASCII string
678 * REASSOC xx:xx:xx:xx:xx:xx CH
679 * v2 has a C struct
680 * REASSOC <binary c struct>
681 *
682 * The first field in the v2 struct is also the bssid in ASCII.
683 * But in the case of a v2 message the BSSID is NUL-terminated.
684 * Hence we can peek at that offset to see if this is V1 or V2
685 * REASSOC xx:xx:xx:xx:xx:xx*
686 * 1111111111222222
687 * 01234567890123456789012345
688 */
689 if (command[25]) {
690 ret = hdd_parse_reassoc_v1(adapter, command);
691 } else {
692 ret = hdd_parse_reassoc_v2(adapter, command);
693 }
694
695 return ret;
696}
697
698#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_ESE FEATURE_WLAN_LFR */
699
700#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
701/**
702 * hdd_sendactionframe() - send a userspace-supplied action frame
703 * @adapter: Adapter upon which the command was received
704 * @bssid: BSSID target of the action frame
705 * @channel: Channel upon which to send the frame
706 * @dwell_time: Amount of time to dwell when the frame is sent
707 * @payload_len:Length of the payload
708 * @payload: Payload of the frame
709 *
710 * This function sends a userspace-supplied action frame
711 *
712 * Return: 0 for success non-zero for failure
713 */
714static int
715hdd_sendactionframe(hdd_adapter_t *adapter, const uint8_t *bssid,
716 const uint8_t channel, const uint8_t dwell_time,
717 const uint8_t payload_len, const uint8_t *payload)
718{
719 struct ieee80211_channel chan;
720 uint8_t frame_len;
721 uint8_t *frame;
722 struct ieee80211_hdr_3addr *hdr;
723 u64 cookie;
724 hdd_station_ctx_t *pHddStaCtx;
725 hdd_context_t *hdd_ctx;
726 int ret = 0;
727 tpSirMacVendorSpecificFrameHdr pVendorSpecific =
728 (tpSirMacVendorSpecificFrameHdr) payload;
729#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
730 struct cfg80211_mgmt_tx_params params;
731#endif
732
733 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
734 hdd_warn("Unsupported in mode %s(%d)",
735 hdd_device_mode_to_string(adapter->device_mode),
736 adapter->device_mode);
737 return -EINVAL;
738 }
739
740 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
741 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
742
743 /* if not associated, no need to send action frame */
744 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
745 hddLog(CDF_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
746 ret = -EINVAL;
747 goto exit;
748 }
749
750 /*
751 * if the target bssid is different from currently associated AP,
752 * then no need to send action frame
753 */
754 if (memcmp(bssid, pHddStaCtx->conn_info.bssId.bytes,
755 CDF_MAC_ADDR_SIZE)) {
756 hddLog(LOG1, FL("STA is not associated to this AP"));
757 ret = -EINVAL;
758 goto exit;
759 }
760
761 chan.center_freq = sme_chn_to_freq(channel);
762 /* Check if it is specific action frame */
763 if (pVendorSpecific->category ==
764 SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) {
765 static const uint8_t Oui[] = { 0x00, 0x00, 0xf0 };
766 if (cdf_mem_compare(pVendorSpecific->Oui, (void *)Oui, 3)) {
767 /*
768 * if the channel number is different from operating
769 * channel then no need to send action frame
770 */
771 if (channel != 0) {
772 if (channel !=
773 pHddStaCtx->conn_info.operationChannel) {
774 hddLog(CDF_TRACE_LEVEL_INFO,
775 "%s: channel(%d) is different from operating channel(%d)",
776 __func__, channel,
777 pHddStaCtx->conn_info.
778 operationChannel);
779 ret = -EINVAL;
780 goto exit;
781 }
782 /*
783 * If channel number is specified and same
784 * as home channel, ensure that action frame
785 * is sent immediately by cancelling
786 * roaming scans. Otherwise large dwell times
787 * may cause long delays in sending action
788 * frames.
789 */
790 sme_abort_roam_scan(hdd_ctx->hHal,
791 adapter->sessionId);
792 } else {
793 /*
794 * 0 is accepted as current home channel,
795 * delayed transmission of action frame is ok.
796 */
797 chan.center_freq =
798 sme_chn_to_freq(pHddStaCtx->conn_info.
799 operationChannel);
800 }
801 }
802 }
803 if (chan.center_freq == 0) {
804 hddLog(CDF_TRACE_LEVEL_ERROR, "%s:invalid channel number %d",
805 __func__, channel);
806 ret = -EINVAL;
807 goto exit;
808 }
809
810 frame_len = payload_len + 24;
811 frame = cdf_mem_malloc(frame_len);
812 if (!frame) {
813 hddLog(LOGE, FL("memory allocation failed"));
814 ret = -ENOMEM;
815 goto exit;
816 }
817 cdf_mem_zero(frame, frame_len);
818
819 hdr = (struct ieee80211_hdr_3addr *)frame;
820 hdr->frame_control =
821 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
822 cdf_mem_copy(hdr->addr1, bssid, CDF_MAC_ADDR_SIZE);
823 cdf_mem_copy(hdr->addr2, adapter->macAddressCurrent.bytes,
824 CDF_MAC_ADDR_SIZE);
825 cdf_mem_copy(hdr->addr3, bssid, CDF_MAC_ADDR_SIZE);
826 cdf_mem_copy(hdr + 1, payload, payload_len);
827
828#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
829 params.chan = &chan;
830 params.offchan = 0;
831 params.wait = dwell_time;
832 params.buf = frame;
833 params.len = frame_len;
834 params.no_cck = 1;
835 params.dont_wait_for_ack = 1;
836 ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, &params, &cookie);
837#else
838 ret = wlan_hdd_mgmt_tx(NULL,
839#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
840 &(adapter->wdev),
841#else
842 adapter->dev,
843#endif
844 &chan, 0,
845#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
846 NL80211_CHAN_HT20, 1,
847#endif
848 dwell_time, frame, frame_len, 1, 1, &cookie);
849#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
850
851 cdf_mem_free(frame);
852exit:
853 return ret;
854}
855
856/**
857 * hdd_parse_sendactionframe_v1() - parse version 1 of the
858 * SENDACTIONFRAME command
859 * @adapter: Adapter upon which the command was received
860 * @command: ASCII text command that was received
861 *
862 * This function parses the v1 SENDACTIONFRAME command with the format
863 *
864 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx
865 *
866 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
867 * BSSID, CH is the ASCII representation of the channel, DW is the
868 * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII
869 * payload. For example
870 *
871 * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee
872 *
873 * Return: 0 for success non-zero for failure
874 */
875static int
876hdd_parse_sendactionframe_v1(hdd_adapter_t *adapter, const char *command)
877{
878 uint8_t channel = 0;
879 uint8_t dwell_time = 0;
880 uint8_t payload_len = 0;
881 uint8_t *payload = NULL;
882 tSirMacAddr bssid;
883 int ret;
884
885 ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel,
886 &dwell_time, &payload,
887 &payload_len);
888 if (ret) {
889 hddLog(CDF_TRACE_LEVEL_ERROR,
890 "%s: Failed to parse send action frame data", __func__);
891 } else {
892 ret = hdd_sendactionframe(adapter, bssid, channel,
893 dwell_time, payload_len, payload);
894 cdf_mem_free(payload);
895 }
896
897 return ret;
898}
899
900/**
901 * hdd_parse_sendactionframe_v2() - parse version 2 of the
902 * SENDACTIONFRAME command
903 * @adapter: Adapter upon which the command was received
904 * @command: Command that was received, ASCII command
905 * followed by binary data
906 *
907 * This function parses the v2 SENDACTIONFRAME command with the format
908 *
909 * SENDACTIONFRAME <android_wifi_af_params>
910 *
911 * Return: 0 for success non-zero for failure
912 */
913static int
914hdd_parse_sendactionframe_v2(hdd_adapter_t *adapter, const char *command)
915{
916 struct android_wifi_af_params *params;
917 tSirMacAddr bssid;
918 int ret;
919
920 /* params are large so keep off the stack */
921 params = kmalloc(sizeof(*params), GFP_KERNEL);
922 if (!params)
923 return -ENOMEM;
924
925 /* The params are located after "SENDACTIONFRAME " */
926 memcpy(params, command + 16, sizeof(*params));
927
928 if (!mac_pton(params->bssid, (u8 *) &bssid)) {
929 hddLog(LOGE, "%s: MAC address parsing failed", __func__);
930 ret = -EINVAL;
931 } else {
932 ret = hdd_sendactionframe(adapter, bssid, params->channel,
933 params->dwell_time, params->len,
934 params->data);
935 }
936 kfree(params);
937 return ret;
938}
939
940/**
941 * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command
942 * @adapter: Adapter upon which the command was received
943 * @command: Command that was received
944 *
945 * There are two different versions of the SENDACTIONFRAME command.
946 * Version 1 of the command contains a parameter list that is ASCII
947 * characters whereas version 2 contains a combination of ASCII and
948 * binary payload. Determine if a version 1 or a version 2 command is
949 * being parsed by examining the parameters, and then dispatch the
950 * parser that is appropriate for the version of the command.
951 *
952 * Return: 0 for success non-zero for failure
953 */
954static int
955hdd_parse_sendactionframe(hdd_adapter_t *adapter, const char *command)
956{
957 int ret;
958
959 /*
960 * both versions start with "SENDACTIONFRAME "
961 * v1 has a bssid and other parameters as an ASCII string
962 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME
963 * v2 has a C struct
964 * SENDACTIONFRAME <binary c struct>
965 *
966 * The first field in the v2 struct is also the bssid in ASCII.
967 * But in the case of a v2 message the BSSID is NUL-terminated.
968 * Hence we can peek at that offset to see if this is V1 or V2
969 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx*
970 * 111111111122222222223333
971 * 0123456789012345678901234567890123
972 */
973 if (command[33]) {
974 ret = hdd_parse_sendactionframe_v1(adapter, command);
975 } else {
976 ret = hdd_parse_sendactionframe_v2(adapter, command);
977 }
978
979 return ret;
980}
981
982#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_ESE FEATURE_WLAN_LFR */
983
984#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
985/**
986 * hdd_parse_channellist() - HDD Parse channel list
987 * @pValue: Pointer to input channel list
988 * @ChannelList: Pointer to local output array to record
989 * channel list
990 * @pNumChannels: Pointer to number of roam scan channels
991 *
992 * This function parses the channel list passed in the format
993 * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space>Channel 2<space>Channel N
994 * if the Number of channels (N) does not match with the actual number
995 * of channels passed then take the minimum of N and count of
996 * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48,
997 * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48,
998 * ignore 5 and take 36, 40, 44 and 48. This function does not take care of
999 * removing duplicate channels from the list
1000 *
1001 * Return: 0 for success non-zero for failure
1002 */
1003static int
1004hdd_parse_channellist(const uint8_t *pValue, uint8_t *pChannelList,
1005 uint8_t *pNumChannels)
1006{
1007 const uint8_t *inPtr = pValue;
1008 int tempInt;
1009 int j = 0;
1010 int v = 0;
1011 char buf[32];
1012
1013 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1014 /* no argument after the command */
1015 if (NULL == inPtr) {
1016 return -EINVAL;
1017 }
1018
1019 /* no space after the command */
1020 else if (SPACE_ASCII_VALUE != *inPtr) {
1021 return -EINVAL;
1022 }
1023
1024 /* remove empty spaces */
1025 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1026 inPtr++;
1027
1028 /* no argument followed by spaces */
1029 if ('\0' == *inPtr) {
1030 return -EINVAL;
1031 }
1032
1033 /* get the first argument ie the number of channels */
1034 v = sscanf(inPtr, "%31s ", buf);
1035 if (1 != v)
1036 return -EINVAL;
1037
1038 v = kstrtos32(buf, 10, &tempInt);
1039 if ((v < 0) ||
1040 (tempInt <= 0) || (tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN)) {
1041 return -EINVAL;
1042 }
1043
1044 *pNumChannels = tempInt;
1045
1046 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
1047 "Number of channels are: %d", *pNumChannels);
1048
1049 for (j = 0; j < (*pNumChannels); j++) {
1050 /*
1051 * inPtr pointing to the beginning of first space after number
1052 * of channels
1053 */
1054 inPtr = strpbrk(inPtr, " ");
1055 /* no channel list after the number of channels argument */
1056 if (NULL == inPtr) {
1057 if (0 != j) {
1058 *pNumChannels = j;
1059 return 0;
1060 } else {
1061 return -EINVAL;
1062 }
1063 }
1064
1065 /* remove empty space */
1066 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1067 inPtr++;
1068
1069 /*
1070 * no channel list after the number of channels
1071 * argument and spaces
1072 */
1073 if ('\0' == *inPtr) {
1074 if (0 != j) {
1075 *pNumChannels = j;
1076 return 0;
1077 } else {
1078 return -EINVAL;
1079 }
1080 }
1081
1082 v = sscanf(inPtr, "%31s ", buf);
1083 if (1 != v)
1084 return -EINVAL;
1085
1086 v = kstrtos32(buf, 10, &tempInt);
1087 if ((v < 0) ||
1088 (tempInt <= 0) ||
1089 (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
1090 return -EINVAL;
1091 }
1092 pChannelList[j] = tempInt;
1093
1094 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
1095 "Channel %d added to preferred channel list",
1096 pChannelList[j]);
1097 }
1098
1099 return 0;
1100}
1101
1102/**
1103 * hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the
1104 * SETROAMSCANCHANNELS command
1105 * @adapter: Adapter upon which the command was received
1106 * @command: ASCII text command that was received
1107 *
1108 * This function parses the v1 SETROAMSCANCHANNELS command with the format
1109 *
1110 * SETROAMSCANCHANNELS N C1 C2 ... Cn
1111 *
1112 * Where "N" is the ASCII representation of the number of channels and
1113 * C1 thru Cn is the ASCII representation of the channels. For example
1114 *
1115 * SETROAMSCANCHANNELS 4 36 40 44 48
1116 *
1117 * Return: 0 for success non-zero for failure
1118 */
1119static int
1120hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *adapter,
1121 const char *command)
1122{
1123 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1124 uint8_t num_chan = 0;
1125 CDF_STATUS status;
1126 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1127 int ret;
1128
1129 ret = hdd_parse_channellist(command, channel_list, &num_chan);
1130 if (ret) {
1131 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1132 "%s: Failed to parse channel list information",
1133 __func__);
1134 goto exit;
1135 }
1136
1137 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
1138 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1139 adapter->sessionId, num_chan));
1140
1141 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
1142 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1143 "%s: number of channels (%d) supported exceeded max (%d)",
1144 __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
1145 ret = -EINVAL;
1146 goto exit;
1147 }
1148
1149 status =
1150 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1151 adapter->sessionId,
1152 channel_list, num_chan);
1153 if (CDF_STATUS_SUCCESS != status) {
1154 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1155 "%s: Failed to update channel list information",
1156 __func__);
1157 ret = -EINVAL;
1158 goto exit;
1159 }
1160exit:
1161 return ret;
1162}
1163
1164/**
1165 * hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the
1166 * SETROAMSCANCHANNELS command
1167 * @adapter: Adapter upon which the command was received
1168 * @command: Command that was received, ASCII command
1169 * followed by binary data
1170 *
1171 * This function parses the v2 SETROAMSCANCHANNELS command with the format
1172 *
1173 * SETROAMSCANCHANNELS [N][C1][C2][Cn]
1174 *
1175 * The command begins with SETROAMSCANCHANNELS followed by a space, but
1176 * what follows the space is an array of u08 parameters. For example
1177 *
1178 * SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30]
1179 *
1180 * Return: 0 for success non-zero for failure
1181 */
1182static int
1183hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *adapter,
1184 const char *command)
1185{
1186 const uint8_t *value;
1187 uint8_t channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
1188 uint8_t channel;
1189 uint8_t num_chan;
1190 int i;
1191 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1192 CDF_STATUS status;
1193 int ret = 0;
1194
1195 /* array of values begins after "SETROAMSCANCHANNELS " */
1196 value = command + 20;
1197
1198 num_chan = *value++;
1199 if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
1200 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1201 "%s: number of channels (%d) supported exceeded max (%d)",
1202 __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
1203 ret = -EINVAL;
1204 goto exit;
1205 }
1206
1207 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
1208 TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
1209 adapter->sessionId, num_chan));
1210
1211 for (i = 0; i < num_chan; i++) {
1212 channel = *value++;
1213 if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
1214 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1215 "%s: index %d invalid channel %d", __func__,
1216 i, channel);
1217 ret = -EINVAL;
1218 goto exit;
1219 }
1220 channel_list[i] = channel;
1221 }
1222 status =
1223 sme_change_roam_scan_channel_list(hdd_ctx->hHal,
1224 adapter->sessionId,
1225 channel_list, num_chan);
1226 if (CDF_STATUS_SUCCESS != status) {
1227 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1228 "%s: Failed to update channel list information",
1229 __func__);
1230 ret = -EINVAL;
1231 goto exit;
1232 }
1233exit:
1234 return ret;
1235}
1236
1237/**
1238 * hdd_parse_set_roam_scan_channels() - parse the
1239 * SETROAMSCANCHANNELS command
1240 * @adapter: Adapter upon which the command was received
1241 * @command: Command that was received
1242 *
1243 * There are two different versions of the SETROAMSCANCHANNELS command.
1244 * Version 1 of the command contains a parameter list that is ASCII
1245 * characters whereas version 2 contains a binary payload. Determine
1246 * if a version 1 or a version 2 command is being parsed by examining
1247 * the parameters, and then dispatch the parser that is appropriate for
1248 * the command.
1249 *
1250 * Return: 0 for success non-zero for failure
1251 */
1252static int
1253hdd_parse_set_roam_scan_channels(hdd_adapter_t *adapter, const char *command)
1254{
1255 const char *cursor;
1256 char ch;
1257 bool v1;
1258 int ret;
1259
1260 /* start after "SETROAMSCANCHANNELS " */
1261 cursor = command + 20;
1262
1263 /* assume we have a version 1 command until proven otherwise */
1264 v1 = true;
1265
1266 /* v1 params will only contain ASCII digits and space */
1267 while ((ch = *cursor++) && v1) {
1268 if (!(isdigit(ch) || isspace(ch))) {
1269 v1 = false;
1270 }
1271 }
1272 if (v1) {
1273 ret = hdd_parse_set_roam_scan_channels_v1(adapter, command);
1274 } else {
1275 ret = hdd_parse_set_roam_scan_channels_v2(adapter, command);
1276 }
1277
1278 return ret;
1279}
1280#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */
1281
1282#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
1283/**
1284 * hdd_parse_plm_cmd() - HDD Parse Plm command
1285 * @pValue: Pointer to input data
1286 * @pPlmRequest:Pointer to output struct tpSirPlmReq
1287 *
1288 * This function parses the plm command passed in the format
1289 * CCXPLMREQ<space><enable><space><dialog_token><space>
1290 * <meas_token><space><num_of_bursts><space><burst_int><space>
1291 * <measu duration><space><burst_len><space><desired_tx_pwr>
1292 * <space><multcast_addr><space><number_of_channels>
1293 * <space><channel_numbers>
1294 *
1295 * Return: 0 for success non-zero for failure
1296 */
1297CDF_STATUS hdd_parse_plm_cmd(uint8_t *pValue, tSirPlmReq *pPlmRequest)
1298{
1299 uint8_t *cmdPtr = NULL;
1300 int count, content = 0, ret = 0;
1301 char buf[32];
1302
1303 /* move to argument list */
1304 cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1305 if (NULL == cmdPtr)
1306 return CDF_STATUS_E_FAILURE;
1307
1308 /* no space after the command */
1309 if (SPACE_ASCII_VALUE != *cmdPtr)
1310 return CDF_STATUS_E_FAILURE;
1311
1312 /* remove empty spaces */
1313 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1314 cmdPtr++;
1315
1316 /* START/STOP PLM req */
1317 ret = sscanf(cmdPtr, "%31s ", buf);
1318 if (1 != ret)
1319 return CDF_STATUS_E_FAILURE;
1320
1321 ret = kstrtos32(buf, 10, &content);
1322 if (ret < 0)
1323 return CDF_STATUS_E_FAILURE;
1324
1325 pPlmRequest->enable = content;
1326 cmdPtr = strpbrk(cmdPtr, " ");
1327
1328 if (NULL == cmdPtr)
1329 return CDF_STATUS_E_FAILURE;
1330
1331 /* remove empty spaces */
1332 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1333 cmdPtr++;
1334
1335 /* Dialog token of radio meas req containing meas reqIE */
1336 ret = sscanf(cmdPtr, "%31s ", buf);
1337 if (1 != ret)
1338 return CDF_STATUS_E_FAILURE;
1339
1340 ret = kstrtos32(buf, 10, &content);
1341 if (ret < 0)
1342 return CDF_STATUS_E_FAILURE;
1343
1344 pPlmRequest->diag_token = content;
1345 hddLog(CDF_TRACE_LEVEL_DEBUG, "diag token %d",
1346 pPlmRequest->diag_token);
1347 cmdPtr = strpbrk(cmdPtr, " ");
1348
1349 if (NULL == cmdPtr)
1350 return CDF_STATUS_E_FAILURE;
1351
1352 /* remove empty spaces */
1353 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1354 cmdPtr++;
1355
1356 /* measurement token of meas req IE */
1357 ret = sscanf(cmdPtr, "%31s ", buf);
1358 if (1 != ret)
1359 return CDF_STATUS_E_FAILURE;
1360
1361 ret = kstrtos32(buf, 10, &content);
1362 if (ret < 0)
1363 return CDF_STATUS_E_FAILURE;
1364
1365 pPlmRequest->meas_token = content;
1366 hddLog(CDF_TRACE_LEVEL_DEBUG, "meas token %d",
1367 pPlmRequest->meas_token);
1368
1369 hddLog(CDF_TRACE_LEVEL_ERROR,
1370 "PLM req %s", pPlmRequest->enable ? "START" : "STOP");
1371 if (pPlmRequest->enable) {
1372
1373 cmdPtr = strpbrk(cmdPtr, " ");
1374
1375 if (NULL == cmdPtr)
1376 return CDF_STATUS_E_FAILURE;
1377
1378 /* remove empty spaces */
1379 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1380 cmdPtr++;
1381
1382 /* total number of bursts after which STA stops sending */
1383 ret = sscanf(cmdPtr, "%31s ", buf);
1384 if (1 != ret)
1385 return CDF_STATUS_E_FAILURE;
1386
1387 ret = kstrtos32(buf, 10, &content);
1388 if (ret < 0)
1389 return CDF_STATUS_E_FAILURE;
1390
1391 if (content < 0)
1392 return CDF_STATUS_E_FAILURE;
1393
1394 pPlmRequest->numBursts = content;
1395 hddLog(CDF_TRACE_LEVEL_DEBUG, "num burst %d",
1396 pPlmRequest->numBursts);
1397 cmdPtr = strpbrk(cmdPtr, " ");
1398
1399 if (NULL == cmdPtr)
1400 return CDF_STATUS_E_FAILURE;
1401
1402 /* remove empty spaces */
1403 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1404 cmdPtr++;
1405
1406 /* burst interval in seconds */
1407 ret = sscanf(cmdPtr, "%31s ", buf);
1408 if (1 != ret)
1409 return CDF_STATUS_E_FAILURE;
1410
1411 ret = kstrtos32(buf, 10, &content);
1412 if (ret < 0)
1413 return CDF_STATUS_E_FAILURE;
1414
1415 if (content <= 0)
1416 return CDF_STATUS_E_FAILURE;
1417
1418 pPlmRequest->burstInt = content;
1419 hddLog(CDF_TRACE_LEVEL_DEBUG, "burst Int %d",
1420 pPlmRequest->burstInt);
1421 cmdPtr = strpbrk(cmdPtr, " ");
1422
1423 if (NULL == cmdPtr)
1424 return CDF_STATUS_E_FAILURE;
1425
1426 /* remove empty spaces */
1427 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1428 cmdPtr++;
1429
1430 /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */
1431 ret = sscanf(cmdPtr, "%31s ", buf);
1432 if (1 != ret)
1433 return CDF_STATUS_E_FAILURE;
1434
1435 ret = kstrtos32(buf, 10, &content);
1436 if (ret < 0)
1437 return CDF_STATUS_E_FAILURE;
1438
1439 if (content <= 0)
1440 return CDF_STATUS_E_FAILURE;
1441
1442 pPlmRequest->measDuration = content;
1443 hddLog(CDF_TRACE_LEVEL_DEBUG, "measDur %d",
1444 pPlmRequest->measDuration);
1445 cmdPtr = strpbrk(cmdPtr, " ");
1446
1447 if (NULL == cmdPtr)
1448 return CDF_STATUS_E_FAILURE;
1449
1450 /* remove empty spaces */
1451 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1452 cmdPtr++;
1453
1454 /* burst length of PLM bursts */
1455 ret = sscanf(cmdPtr, "%31s ", buf);
1456 if (1 != ret)
1457 return CDF_STATUS_E_FAILURE;
1458
1459 ret = kstrtos32(buf, 10, &content);
1460 if (ret < 0)
1461 return CDF_STATUS_E_FAILURE;
1462
1463 if (content <= 0)
1464 return CDF_STATUS_E_FAILURE;
1465
1466 pPlmRequest->burstLen = content;
1467 hddLog(CDF_TRACE_LEVEL_DEBUG, "burstLen %d",
1468 pPlmRequest->burstLen);
1469 cmdPtr = strpbrk(cmdPtr, " ");
1470
1471 if (NULL == cmdPtr)
1472 return CDF_STATUS_E_FAILURE;
1473
1474 /* remove empty spaces */
1475 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1476 cmdPtr++;
1477
1478 /* desired tx power for transmission of PLM bursts */
1479 ret = sscanf(cmdPtr, "%31s ", buf);
1480 if (1 != ret)
1481 return CDF_STATUS_E_FAILURE;
1482
1483 ret = kstrtos32(buf, 10, &content);
1484 if (ret < 0)
1485 return CDF_STATUS_E_FAILURE;
1486
1487 if (content <= 0)
1488 return CDF_STATUS_E_FAILURE;
1489
1490 pPlmRequest->desiredTxPwr = content;
1491 hddLog(CDF_TRACE_LEVEL_DEBUG,
1492 "desiredTxPwr %d", pPlmRequest->desiredTxPwr);
1493
1494 for (count = 0; count < CDF_MAC_ADDR_SIZE; count++) {
1495 cmdPtr = strpbrk(cmdPtr, " ");
1496
1497 if (NULL == cmdPtr)
1498 return CDF_STATUS_E_FAILURE;
1499
1500 /* remove empty spaces */
1501 while ((SPACE_ASCII_VALUE == *cmdPtr)
1502 && ('\0' != *cmdPtr))
1503 cmdPtr++;
1504
1505 ret = sscanf(cmdPtr, "%31s ", buf);
1506 if (1 != ret)
1507 return CDF_STATUS_E_FAILURE;
1508
1509 ret = kstrtos32(buf, 16, &content);
1510 if (ret < 0)
1511 return CDF_STATUS_E_FAILURE;
1512
1513 pPlmRequest->macAddr[count] = content;
1514 }
1515
1516 hddLog(CDF_TRACE_LEVEL_DEBUG, "MC addr " MAC_ADDRESS_STR,
1517 MAC_ADDR_ARRAY(pPlmRequest->macAddr));
1518
1519 cmdPtr = strpbrk(cmdPtr, " ");
1520
1521 if (NULL == cmdPtr)
1522 return CDF_STATUS_E_FAILURE;
1523
1524 /* remove empty spaces */
1525 while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
1526 cmdPtr++;
1527
1528 /* number of channels */
1529 ret = sscanf(cmdPtr, "%31s ", buf);
1530 if (1 != ret)
1531 return CDF_STATUS_E_FAILURE;
1532
1533 ret = kstrtos32(buf, 10, &content);
1534 if (ret < 0)
1535 return CDF_STATUS_E_FAILURE;
1536
1537 if (content < 0)
1538 return CDF_STATUS_E_FAILURE;
1539
1540 pPlmRequest->plmNumCh = content;
1541 hddLog(CDF_TRACE_LEVEL_DEBUG, "numch %d",
1542 pPlmRequest->plmNumCh);
1543
1544 /* Channel numbers */
1545 for (count = 0; count < pPlmRequest->plmNumCh; count++) {
1546 cmdPtr = strpbrk(cmdPtr, " ");
1547
1548 if (NULL == cmdPtr)
1549 return CDF_STATUS_E_FAILURE;
1550
1551 /* remove empty spaces */
1552 while ((SPACE_ASCII_VALUE == *cmdPtr)
1553 && ('\0' != *cmdPtr))
1554 cmdPtr++;
1555
1556 ret = sscanf(cmdPtr, "%31s ", buf);
1557 if (1 != ret)
1558 return CDF_STATUS_E_FAILURE;
1559
1560 ret = kstrtos32(buf, 10, &content);
1561 if (ret < 0)
1562 return CDF_STATUS_E_FAILURE;
1563
1564 if (content <= 0)
1565 return CDF_STATUS_E_FAILURE;
1566
1567 pPlmRequest->plmChList[count] = content;
1568 hddLog(CDF_TRACE_LEVEL_DEBUG, " ch- %d",
1569 pPlmRequest->plmChList[count]);
1570 }
1571 }
1572 /* If PLM START */
1573 return CDF_STATUS_SUCCESS;
1574}
1575#endif
1576
1577#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
1578static void wlan_hdd_ready_to_extwow(void *callbackContext, bool is_success)
1579{
1580 hdd_context_t *hdd_ctx = (hdd_context_t *) callbackContext;
1581 int rc;
1582
1583 rc = wlan_hdd_validate_context(hdd_ctx);
1584 if (0 != rc) {
1585 hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
1586 return;
1587 }
1588 hdd_ctx->ext_wow_should_suspend = is_success;
1589 complete(&hdd_ctx->ready_to_extwow);
1590}
1591
1592static int hdd_enable_ext_wow(hdd_adapter_t *adapter,
1593 tpSirExtWoWParams arg_params)
1594{
1595 tSirExtWoWParams params;
1596 CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
1597 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1598 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1599 int rc;
1600
1601 cdf_mem_copy(&params, arg_params, sizeof(params));
1602
1603 INIT_COMPLETION(hdd_ctx->ready_to_extwow);
1604
Krishna Kumaar Natarajand9131902015-10-19 11:52:47 -07001605 cdf_ret_status = sme_configure_ext_wow(hHal, &params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001606 &wlan_hdd_ready_to_extwow,
1607 hdd_ctx);
1608 if (CDF_STATUS_SUCCESS != cdf_ret_status) {
1609 hddLog(CDF_TRACE_LEVEL_ERROR,
Krishna Kumaar Natarajand9131902015-10-19 11:52:47 -07001610 FL("sme_configure_ext_wow returned failure %d"),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001611 cdf_ret_status);
1612 return -EPERM;
1613 }
1614
1615 rc = wait_for_completion_timeout(&hdd_ctx->ready_to_extwow,
1616 msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_EXTWOW));
1617 if (!rc) {
1618 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1619 "%s: Failed to get ready to extwow", __func__);
1620 return -EPERM;
1621 }
1622
1623 if (hdd_ctx->ext_wow_should_suspend) {
1624 if (hdd_ctx->config->extWowGotoSuspend) {
1625 pm_message_t state;
1626
1627 state.event = PM_EVENT_SUSPEND;
1628 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
1629 "%s: Received ready to ExtWoW. Going to suspend",
1630 __func__);
1631
1632 rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL);
1633 if (rc < 0) {
1634 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1635 "%s: wlan_hdd_cfg80211_suspend_wlan failed, error = %d",
1636 __func__, rc);
1637 return rc;
1638 }
1639 cdf_ret_status = wlan_hdd_bus_suspend(state);
1640 if (cdf_ret_status != CDF_STATUS_SUCCESS) {
1641 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1642 "%s: wlan_hdd_suspend failed, status = %d",
1643 __func__, cdf_ret_status);
1644 wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy);
1645 return -EPERM;
1646 }
1647 }
1648 } else {
1649 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1650 "%s: Received ready to ExtWoW failure", __func__);
1651 return -EPERM;
1652 }
1653
1654 return 0;
1655}
1656
1657static int hdd_enable_ext_wow_parser(hdd_adapter_t *adapter, int vdev_id,
1658 int value)
1659{
1660 tSirExtWoWParams params;
1661 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1662 int rc;
1663
1664 rc = wlan_hdd_validate_context(hdd_ctx);
1665 if (0 != rc) {
1666 hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
1667 return -EINVAL;
1668 }
1669
1670 if (value < EXT_WOW_TYPE_APP_TYPE1 ||
1671 value > EXT_WOW_TYPE_APP_TYPE1_2) {
1672 hddLog(CDF_TRACE_LEVEL_ERROR, FL("Invalid type"));
1673 return -EINVAL;
1674 }
1675
1676 if (value == EXT_WOW_TYPE_APP_TYPE1 &&
1677 hdd_ctx->is_extwow_app_type1_param_set)
1678 params.type = value;
1679 else if (value == EXT_WOW_TYPE_APP_TYPE2 &&
1680 hdd_ctx->is_extwow_app_type2_param_set)
1681 params.type = value;
1682 else if (value == EXT_WOW_TYPE_APP_TYPE1_2 &&
1683 hdd_ctx->is_extwow_app_type1_param_set &&
1684 hdd_ctx->is_extwow_app_type2_param_set)
1685 params.type = value;
1686 else {
1687 hddLog(CDF_TRACE_LEVEL_ERROR,
1688 FL("Set app params before enable it value %d"), value);
1689 return -EINVAL;
1690 }
1691
1692 params.vdev_id = vdev_id;
1693 params.wakeup_pin_num = hdd_ctx->config->extWowApp1WakeupPinNumber |
1694 (hdd_ctx->config->extWowApp2WakeupPinNumber
1695 << 8);
1696
1697 return hdd_enable_ext_wow(adapter, &params);
1698}
1699
1700static int hdd_set_app_type1_params(tHalHandle hHal,
1701 tpSirAppType1Params arg_params)
1702{
1703 tSirAppType1Params params;
1704 CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
1705
1706 cdf_mem_copy(&params, arg_params, sizeof(params));
1707
1708 cdf_ret_status = sme_configure_app_type1_params(hHal, &params);
1709 if (CDF_STATUS_SUCCESS != cdf_ret_status) {
1710 hddLog(CDF_TRACE_LEVEL_ERROR,
1711 FL("sme_configure_app_type1_params returned failure %d"),
1712 cdf_ret_status);
1713 return -EPERM;
1714 }
1715
1716 return 0;
1717}
1718
1719static int hdd_set_app_type1_parser(hdd_adapter_t *adapter,
1720 char *arg, int len)
1721{
1722 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1723 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1724 char id[20], password[20];
1725 tSirAppType1Params params;
1726 int rc, i;
1727
1728 rc = wlan_hdd_validate_context(hdd_ctx);
1729 if (0 != rc) {
1730 hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
1731 return -EINVAL;
1732 }
1733
1734 if (2 != sscanf(arg, "%8s %16s", id, password)) {
1735 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1736 FL("Invalid Number of arguments"));
1737 return -EINVAL;
1738 }
1739
1740 memset(&params, 0, sizeof(tSirAppType1Params));
1741 params.vdev_id = adapter->sessionId;
1742 for (i = 0; i < ETHER_ADDR_LEN; i++)
1743 params.wakee_mac_addr[i] =
1744 adapter->macAddressCurrent.bytes[i];
1745
1746 params.id_length = strlen(id);
1747 cdf_mem_copy(params.identification_id, id, params.id_length);
1748 params.pass_length = strlen(password);
1749 cdf_mem_copy(params.password, password, params.pass_length);
1750
1751 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
1752 "%s: %d %pM %.8s %u %.16s %u",
1753 __func__, params.vdev_id, params.wakee_mac_addr,
1754 params.identification_id, params.id_length,
1755 params.password, params.pass_length);
1756
1757 return hdd_set_app_type1_params(hHal, &params);
1758}
1759
1760static int hdd_set_app_type2_params(tHalHandle hHal,
1761 tpSirAppType2Params arg_params)
1762{
1763 tSirAppType2Params params;
1764 CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
1765
1766 cdf_mem_copy(&params, arg_params, sizeof(params));
1767
1768 cdf_ret_status = sme_configure_app_type2_params(hHal, &params);
1769 if (CDF_STATUS_SUCCESS != cdf_ret_status) {
1770 hddLog(CDF_TRACE_LEVEL_ERROR,
1771 FL("sme_configure_app_type2_params returned failure %d"),
1772 cdf_ret_status);
1773 return -EPERM;
1774 }
1775
1776 return 0;
1777}
1778
1779static int hdd_set_app_type2_parser(hdd_adapter_t *adapter,
1780 char *arg, int len)
1781{
1782 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1783 tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1784 char mac_addr[20], rc4_key[20];
1785 unsigned int gateway_mac[6], i;
1786 tSirAppType2Params params;
1787 int ret;
1788
1789 ret = wlan_hdd_validate_context(hdd_ctx);
1790 if (0 != ret) {
1791 hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
1792 return -EINVAL;
1793 }
1794
1795 memset(&params, 0, sizeof(tSirAppType2Params));
1796
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05301797 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 -08001798 mac_addr, rc4_key, (unsigned int *)&params.ip_id,
1799 (unsigned int *)&params.ip_device_ip,
1800 (unsigned int *)&params.ip_server_ip,
1801 (unsigned int *)&params.tcp_seq,
1802 (unsigned int *)&params.tcp_ack_seq,
Bhargav Shahf4fd97d2015-07-08 10:21:37 +05301803 (uint16_t *)&params.tcp_src_port,
1804 (uint16_t *)&params.tcp_dst_port,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001805 (unsigned int *)&params.keepalive_init,
1806 (unsigned int *)&params.keepalive_min,
1807 (unsigned int *)&params.keepalive_max,
1808 (unsigned int *)&params.keepalive_inc,
1809 (unsigned int *)&params.tcp_tx_timeout_val,
1810 (unsigned int *)&params.tcp_rx_timeout_val);
1811
1812 if (ret != 15 && ret != 7) {
1813 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1814 "Invalid Number of arguments");
1815 return -EINVAL;
1816 }
1817
1818 if (6 !=
1819 sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0],
1820 &gateway_mac[1], &gateway_mac[2], &gateway_mac[3],
1821 &gateway_mac[4], &gateway_mac[5])) {
1822 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1823 "Invalid MacAddress Input %s", mac_addr);
1824 return -EINVAL;
1825 }
1826
1827 if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT ||
1828 params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) {
1829 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1830 "Invalid TCP Port Number");
1831 return -EINVAL;
1832 }
1833
1834 for (i = 0; i < ETHER_ADDR_LEN; i++)
1835 params.gateway_mac[i] = (uint8_t) gateway_mac[i];
1836
1837 params.rc4_key_len = strlen(rc4_key);
1838 cdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len);
1839
1840 params.vdev_id = adapter->sessionId;
1841 params.tcp_src_port = (params.tcp_src_port != 0) ?
1842 params.tcp_src_port : hdd_ctx->config->extWowApp2TcpSrcPort;
1843 params.tcp_dst_port = (params.tcp_dst_port != 0) ?
1844 params.tcp_dst_port : hdd_ctx->config->extWowApp2TcpDstPort;
1845 params.keepalive_init = (params.keepalive_init != 0) ?
1846 params.keepalive_init : hdd_ctx->config->
1847 extWowApp2KAInitPingInterval;
1848 params.keepalive_min =
1849 (params.keepalive_min != 0) ?
1850 params.keepalive_min :
1851 hdd_ctx->config->extWowApp2KAMinPingInterval;
1852 params.keepalive_max =
1853 (params.keepalive_max != 0) ?
1854 params.keepalive_max :
1855 hdd_ctx->config->extWowApp2KAMaxPingInterval;
1856 params.keepalive_inc =
1857 (params.keepalive_inc != 0) ?
1858 params.keepalive_inc :
1859 hdd_ctx->config->extWowApp2KAIncPingInterval;
1860 params.tcp_tx_timeout_val =
1861 (params.tcp_tx_timeout_val != 0) ?
1862 params.tcp_tx_timeout_val :
1863 hdd_ctx->config->extWowApp2TcpTxTimeout;
1864 params.tcp_rx_timeout_val =
1865 (params.tcp_rx_timeout_val != 0) ?
1866 params.tcp_rx_timeout_val :
1867 hdd_ctx->config->extWowApp2TcpRxTimeout;
1868
1869 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
1870 "%s: %pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u",
1871 __func__, gateway_mac, rc4_key, params.ip_id,
1872 params.ip_device_ip, params.ip_server_ip, params.tcp_seq,
1873 params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port,
1874 params.keepalive_init, params.keepalive_min,
1875 params.keepalive_max, params.keepalive_inc,
1876 params.tcp_tx_timeout_val, params.tcp_rx_timeout_val);
1877
1878 return hdd_set_app_type2_params(hHal, &params);
1879}
1880#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
1881
1882/**
1883 * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
1884 * @pValue: Pointer to MAXTXPOWER command
1885 * @pDbm: Pointer to tx power
1886 *
1887 * This function parses the MAXTXPOWER command passed in the format
1888 * MAXTXPOWER<space>X(Tx power in dbm)
1889 *
1890 * For example input commands:
1891 * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm
1892 * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm
1893 *
1894 * Return: 0 for success non-zero for failure
1895 */
1896static int hdd_parse_setmaxtxpower_command(uint8_t *pValue, int *pTxPower)
1897{
1898 uint8_t *inPtr = pValue;
1899 int tempInt;
1900 int v = 0;
1901 *pTxPower = 0;
1902
1903 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
1904 /* no argument after the command */
1905 if (NULL == inPtr) {
1906 return -EINVAL;
1907 }
1908
1909 /* no space after the command */
1910 else if (SPACE_ASCII_VALUE != *inPtr) {
1911 return -EINVAL;
1912 }
1913
1914 /* remove empty spaces */
1915 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
1916 inPtr++;
1917
1918 /* no argument followed by spaces */
1919 if ('\0' == *inPtr) {
1920 return 0;
1921 }
1922
1923 v = kstrtos32(inPtr, 10, &tempInt);
1924
1925 /* Range checking for passed parameter */
1926 if ((tempInt < HDD_MIN_TX_POWER) || (tempInt > HDD_MAX_TX_POWER)) {
1927 return -EINVAL;
1928 }
1929
1930 *pTxPower = tempInt;
1931
1932 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
1933 "SETMAXTXPOWER: %d", *pTxPower);
1934
1935 return 0;
1936} /* End of hdd_parse_setmaxtxpower_command */
1937
1938static int hdd_get_dwell_time(struct hdd_config *pCfg, uint8_t *command,
1939 char *extra, uint8_t n, uint8_t *len)
1940{
1941 int ret = 0;
1942
1943 if (!pCfg || !command || !extra || !len) {
1944 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
1945 "%s: argument passed for GETDWELLTIME is incorrect",
1946 __func__);
1947 ret = -EINVAL;
1948 return ret;
1949 }
1950
1951 if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) {
1952 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n",
1953 (int)pCfg->nActiveMaxChnTime);
1954 return ret;
1955 } else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0) {
1956 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n",
1957 (int)pCfg->nActiveMinChnTime);
1958 return ret;
1959 } else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) {
1960 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n",
1961 (int)pCfg->nPassiveMaxChnTime);
1962 return ret;
1963 } else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0) {
1964 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n",
1965 (int)pCfg->nPassiveMinChnTime);
1966 return ret;
1967 } else if (strncmp(command, "GETDWELLTIME", 12) == 0) {
1968 *len = scnprintf(extra, n, "GETDWELLTIME %u \n",
1969 (int)pCfg->nActiveMaxChnTime);
1970 return ret;
1971 } else {
1972 ret = -EINVAL;
1973 }
1974
1975 return ret;
1976}
1977
1978static int hdd_set_dwell_time(hdd_adapter_t *adapter, uint8_t *command)
1979{
1980 tHalHandle hHal;
1981 struct hdd_config *pCfg;
1982 uint8_t *value = command;
1983 tSmeConfigParams smeConfig;
1984 int val = 0, temp = 0;
1985
1986 pCfg = (WLAN_HDD_GET_CTX(adapter))->config;
1987 hHal = WLAN_HDD_GET_HAL_CTX(adapter);
1988 if (!pCfg || !hHal) {
1989 hddLog(LOGE,
1990 FL("argument passed for SETDWELLTIME is incorrect"));
1991 return -EINVAL;
1992 }
1993
1994 cdf_mem_zero(&smeConfig, sizeof(smeConfig));
1995 sme_get_config_param(hHal, &smeConfig);
1996
1997 if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) {
1998 value = value + 24;
1999 temp = kstrtou32(value, 10, &val);
2000 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2001 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
2002 hddLog(LOGE,
2003 FL("argument passed for SETDWELLTIME ACTIVE MAX is incorrect"));
2004 return -EFAULT;
2005 }
2006 pCfg->nActiveMaxChnTime = val;
2007 smeConfig.csrConfig.nActiveMaxChnTime = val;
2008 sme_update_config(hHal, &smeConfig);
2009 } else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0) {
2010 value = value + 24;
2011 temp = kstrtou32(value, 10, &val);
2012 if (temp != 0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN ||
2013 val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX) {
2014 hddLog(LOGE,
2015 FL("argument passed for SETDWELLTIME ACTIVE MIN is incorrect"));
2016 return -EFAULT;
2017 }
2018 pCfg->nActiveMinChnTime = val;
2019 smeConfig.csrConfig.nActiveMinChnTime = val;
2020 sme_update_config(hHal, &smeConfig);
2021 } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) {
2022 value = value + 25;
2023 temp = kstrtou32(value, 10, &val);
2024 if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN ||
2025 val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX) {
2026 hddLog(LOGE,
2027 FL("argument passed for SETDWELLTIME PASSIVE MAX is incorrect"));
2028 return -EFAULT;
2029 }
2030 pCfg->nPassiveMaxChnTime = val;
2031 smeConfig.csrConfig.nPassiveMaxChnTime = val;
2032 sme_update_config(hHal, &smeConfig);
2033 } else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0) {
2034 value = value + 25;
2035 temp = kstrtou32(value, 10, &val);
2036 if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN ||
2037 val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX) {
2038 hddLog(LOGE,
2039 FL("argument passed for SETDWELLTIME PASSIVE MIN is incorrect"));
2040 return -EFAULT;
2041 }
2042 pCfg->nPassiveMinChnTime = val;
2043 smeConfig.csrConfig.nPassiveMinChnTime = val;
2044 sme_update_config(hHal, &smeConfig);
2045 } else if (strncmp(command, "SETDWELLTIME", 12) == 0) {
2046 value = value + 13;
2047 temp = kstrtou32(value, 10, &val);
2048 if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
2049 val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX) {
2050 hddLog(LOGE,
2051 FL("argument passed for SETDWELLTIME is incorrect"));
2052 return -EFAULT;
2053 }
2054 pCfg->nActiveMaxChnTime = val;
2055 smeConfig.csrConfig.nActiveMaxChnTime = val;
2056 sme_update_config(hHal, &smeConfig);
2057 } else {
2058 return -EINVAL;
2059 }
2060
2061 return 0;
2062}
2063
2064static void hdd_get_link_status_cb(uint8_t status, void *context)
2065{
2066 struct statsContext *pLinkContext;
2067 hdd_adapter_t *adapter;
2068
2069 if (NULL == context) {
2070 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: Bad context [%p]",
2071 __func__, context);
2072 return;
2073 }
2074
2075 pLinkContext = context;
2076 adapter = pLinkContext->pAdapter;
2077
2078 spin_lock(&hdd_context_lock);
2079
2080 if ((NULL == adapter) ||
2081 (LINK_STATUS_MAGIC != pLinkContext->magic)) {
2082 /*
2083 * the caller presumably timed out so there is
2084 * nothing we can do
2085 */
2086 spin_unlock(&hdd_context_lock);
2087 hddLog(CDF_TRACE_LEVEL_WARN,
2088 "%s: Invalid context, adapter [%p] magic [%08x]",
2089 __func__, adapter, pLinkContext->magic);
2090 return;
2091 }
2092
2093 /* context is valid so caller is still waiting */
2094
2095 /* paranoia: invalidate the magic */
2096 pLinkContext->magic = 0;
2097
2098 /* copy over the status */
2099 adapter->linkStatus = status;
2100
2101 /* notify the caller */
2102 complete(&pLinkContext->completion);
2103
2104 /* serialization is complete */
2105 spin_unlock(&hdd_context_lock);
2106}
2107
2108/**
2109 * wlan_hdd_get_link_status() - get link status
2110 * @pAdapter: pointer to the adapter
2111 *
2112 * This function sends a request to query the link status and waits
2113 * on a timer to invoke the callback. if the callback is invoked then
2114 * latest link status shall be returned or otherwise cached value
2115 * will be returned.
2116 *
2117 * Return: On success, link status shall be returned.
2118 * On error or not associated, link status 0 will be returned.
2119 */
2120static int wlan_hdd_get_link_status(hdd_adapter_t *adapter)
2121{
2122
2123 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2124 hdd_station_ctx_t *pHddStaCtx =
2125 WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2126 struct statsContext context;
2127 CDF_STATUS hstatus;
2128 unsigned long rc;
2129
2130 if (hdd_ctx->isLogpInProgress) {
2131 hddLog(LOGW, FL("LOGP in Progress. Ignore!!!"));
2132 return 0;
2133 }
2134
2135 if ((WLAN_HDD_INFRA_STATION != adapter->device_mode) &&
2136 (WLAN_HDD_P2P_CLIENT != adapter->device_mode)) {
2137 hdd_warn("Unsupported in mode %s(%d)",
2138 hdd_device_mode_to_string(adapter->device_mode),
2139 adapter->device_mode);
2140 return 0;
2141 }
2142
2143 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
2144 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
2145 /* If not associated, then expected link status return
2146 * value is 0
2147 */
2148 hddLog(LOG1, FL("Not associated!"));
2149 return 0;
2150 }
2151
2152 init_completion(&context.completion);
2153 context.pAdapter = adapter;
2154 context.magic = LINK_STATUS_MAGIC;
2155 hstatus = sme_get_link_status(WLAN_HDD_GET_HAL_CTX(adapter),
2156 hdd_get_link_status_cb,
2157 &context, adapter->sessionId);
2158 if (CDF_STATUS_SUCCESS != hstatus) {
2159 hddLog(CDF_TRACE_LEVEL_ERROR,
2160 "%s: Unable to retrieve link status", __func__);
2161 /* return a cached value */
2162 } else {
2163 /* request is sent -- wait for the response */
2164 rc = wait_for_completion_timeout(&context.completion,
2165 msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
2166 if (!rc)
2167 hddLog(CDF_TRACE_LEVEL_ERROR,
2168 FL("SME timed out while retrieving link status"));
2169 }
2170
2171 spin_lock(&hdd_context_lock);
2172 context.magic = 0;
2173 spin_unlock(&hdd_context_lock);
2174
2175 /* either callback updated adapter stats or it has cached data */
2176 return adapter->linkStatus;
2177}
2178
2179#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
2180/**
2181 * hdd_parse_ese_beacon_req() - Parse ese beacon request
2182 * @pValue: Pointer to data
2183 * @pEseBcnReq: Output pointer to store parsed ie information
2184 *
2185 * This function parses the ese beacon request passed in the format
2186 * CCXBEACONREQ<space><Number of fields><space><Measurement token>
2187 * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N
2188 * <space>Scan Mode N<space>Meas Duration N
2189 *
2190 * If the Number of bcn req fields (N) does not match with the
2191 * actual number of fields passed then take N.
2192 * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated
2193 * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40.
2194 * This function does not take care of removing duplicate channels from the
2195 * list
2196 *
2197 * Return: 0 for success non-zero for failure
2198 */
2199static int hdd_parse_ese_beacon_req(uint8_t *pValue,
2200 tCsrEseBeaconReq *pEseBcnReq)
2201{
2202 uint8_t *inPtr = pValue;
2203 int tempInt = 0;
2204 int j = 0, i = 0, v = 0;
2205 char buf[32];
2206
2207 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2208 /* no argument after the command */
2209 if (NULL == inPtr) {
2210 return -EINVAL;
2211 }
2212 /* no space after the command */
2213 else if (SPACE_ASCII_VALUE != *inPtr) {
2214 return -EINVAL;
2215 }
2216
2217 /* remove empty spaces */
2218 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2219 inPtr++;
2220
2221 /* no argument followed by spaces */
2222 if ('\0' == *inPtr)
2223 return -EINVAL;
2224
2225 /* get the first argument ie measurement token */
2226 v = sscanf(inPtr, "%31s ", buf);
2227 if (1 != v)
2228 return -EINVAL;
2229
2230 v = kstrtos32(buf, 10, &tempInt);
2231 if (v < 0)
2232 return -EINVAL;
2233
2234 pEseBcnReq->numBcnReqIe = tempInt;
2235
2236 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
2237 "Number of Bcn Req Ie fields(%d)", pEseBcnReq->numBcnReqIe);
2238
2239 for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++) {
2240 for (i = 0; i < 4; i++) {
2241 /*
2242 * inPtr pointing to the beginning of 1st space
2243 * after number of ie fields
2244 */
2245 inPtr = strpbrk(inPtr, " ");
2246 /* no ie data after the number of ie fields argument */
2247 if (NULL == inPtr)
2248 return -EINVAL;
2249
2250 /* remove empty space */
2251 while ((SPACE_ASCII_VALUE == *inPtr)
2252 && ('\0' != *inPtr))
2253 inPtr++;
2254
2255 /*
2256 * no ie data after the number of ie fields
2257 * argument and spaces
2258 */
2259 if ('\0' == *inPtr)
2260 return -EINVAL;
2261
2262 v = sscanf(inPtr, "%31s ", buf);
2263 if (1 != v)
2264 return -EINVAL;
2265
2266 v = kstrtos32(buf, 10, &tempInt);
2267 if (v < 0)
2268 return -EINVAL;
2269
2270 switch (i) {
2271 case 0: /* Measurement token */
2272 if (tempInt <= 0) {
2273 CDF_TRACE(CDF_MODULE_ID_HDD,
2274 CDF_TRACE_LEVEL_ERROR,
2275 "Invalid Measurement Token(%d)",
2276 tempInt);
2277 return -EINVAL;
2278 }
2279 pEseBcnReq->bcnReq[j].measurementToken =
2280 tempInt;
2281 break;
2282
2283 case 1: /* Channel number */
2284 if ((tempInt <= 0) ||
2285 (tempInt >
2286 WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
2287 CDF_TRACE(CDF_MODULE_ID_HDD,
2288 CDF_TRACE_LEVEL_ERROR,
2289 "Invalid Channel Number(%d)",
2290 tempInt);
2291 return -EINVAL;
2292 }
2293 pEseBcnReq->bcnReq[j].channel = tempInt;
2294 break;
2295
2296 case 2: /* Scan mode */
2297 if ((tempInt < eSIR_PASSIVE_SCAN)
2298 || (tempInt > eSIR_BEACON_TABLE)) {
2299 CDF_TRACE(CDF_MODULE_ID_HDD,
2300 CDF_TRACE_LEVEL_ERROR,
2301 "Invalid Scan Mode(%d) Expected{0|1|2}",
2302 tempInt);
2303 return -EINVAL;
2304 }
2305 pEseBcnReq->bcnReq[j].scanMode = tempInt;
2306 break;
2307
2308 case 3: /* Measurement duration */
2309 if (((tempInt <= 0)
2310 && (pEseBcnReq->bcnReq[j].scanMode !=
2311 eSIR_BEACON_TABLE)) ||
2312 ((tempInt < 0) &&
2313 (pEseBcnReq->bcnReq[j].scanMode ==
2314 eSIR_BEACON_TABLE))) {
2315 CDF_TRACE(CDF_MODULE_ID_HDD,
2316 CDF_TRACE_LEVEL_ERROR,
2317 "Invalid Measurement Duration(%d)",
2318 tempInt);
2319 return -EINVAL;
2320 }
2321 pEseBcnReq->bcnReq[j].measurementDuration =
2322 tempInt;
2323 break;
2324 }
2325 }
2326 }
2327
2328 for (j = 0; j < pEseBcnReq->numBcnReqIe; j++) {
2329 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2330 "Index(%d) Measurement Token(%u) Channel(%u) Scan Mode(%u) Measurement Duration(%u)",
2331 j,
2332 pEseBcnReq->bcnReq[j].measurementToken,
2333 pEseBcnReq->bcnReq[j].channel,
2334 pEseBcnReq->bcnReq[j].scanMode,
2335 pEseBcnReq->bcnReq[j].measurementDuration);
2336 }
2337
2338 return 0;
2339}
2340#endif /* defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) */
2341
2342#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
2343/**
2344 * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
2345 * @pValue: Pointer to input data
2346 * @pCckmIe: Pointer to output cckm Ie
2347 * @pCckmIeLen: Pointer to output cckm ie length
2348 *
2349 * This function parses the SETCCKM IE command
2350 * SETCCKMIE<space><ie data>
2351 *
2352 * Return: 0 for success non-zero for failure
2353 */
2354static int hdd_parse_get_cckm_ie(uint8_t *pValue, uint8_t **pCckmIe,
2355 uint8_t *pCckmIeLen)
2356{
2357 uint8_t *inPtr = pValue;
2358 uint8_t *dataEnd;
2359 int j = 0;
2360 int i = 0;
2361 uint8_t tempByte = 0;
2362 inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
2363 /* no argument after the command */
2364 if (NULL == inPtr) {
2365 return -EINVAL;
2366 }
2367 /* no space after the command */
2368 else if (SPACE_ASCII_VALUE != *inPtr) {
2369 return -EINVAL;
2370 }
2371 /* remove empty spaces */
2372 while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr))
2373 inPtr++;
2374 /* no argument followed by spaces */
2375 if ('\0' == *inPtr) {
2376 return -EINVAL;
2377 }
2378 /* find the length of data */
2379 dataEnd = inPtr;
2380 while (('\0' != *dataEnd)) {
2381 dataEnd++;
2382 ++(*pCckmIeLen);
2383 }
2384 if (*pCckmIeLen <= 0)
2385 return -EINVAL;
2386 /*
2387 * Allocate the number of bytes based on the number of input characters
2388 * whether it is even or odd.
2389 * if the number of input characters are even, then we need N / 2 byte.
2390 * if the number of input characters are odd, then we need do
2391 * (N + 1) / 2 to compensate rounding off.
2392 * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough.
2393 * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes
2394 */
2395 *pCckmIe = cdf_mem_malloc((*pCckmIeLen + 1) / 2);
2396 if (NULL == *pCckmIe) {
2397 hddLog(LOGE, FL("cdf_mem_alloc failed"));
2398 return -ENOMEM;
2399 }
2400 cdf_mem_zero(*pCckmIe, (*pCckmIeLen + 1) / 2);
2401 /*
2402 * the buffer received from the upper layer is character buffer,
2403 * we need to prepare the buffer taking 2 characters in to a U8 hex
2404 * decimal number for example 7f0000f0...form a buffer to contain
2405 * 7f in 0th location, 00 in 1st and f0 in 3rd location
2406 */
2407 for (i = 0, j = 0; j < *pCckmIeLen; j += 2) {
2408 tempByte = (hex_to_bin(inPtr[j]) << 4) |
2409 (hex_to_bin(inPtr[j + 1]));
2410 (*pCckmIe)[i++] = tempByte;
2411 }
2412 *pCckmIeLen = i;
2413 return 0;
2414}
2415#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
2416
2417int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate)
2418{
2419 tSirRateUpdateInd rateUpdate = {0};
2420 CDF_STATUS status;
2421 hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
2422 struct hdd_config *pConfig = NULL;
2423
2424 if (pHddCtx == NULL) {
2425 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
2426 "%s: HDD context is null", __func__);
2427 return -EINVAL;
2428 }
2429 if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
2430 (WLAN_HDD_SOFTAP != pAdapter->device_mode) &&
2431 (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)) {
2432 hddLog(LOGE,
2433 FL("Received SETMCRATE cmd in invalid mode %s(%d)"),
2434 hdd_device_mode_to_string(pAdapter->device_mode),
2435 pAdapter->device_mode);
2436 hddLog(LOGE,
2437 FL("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode"));
2438 return -EINVAL;
2439 }
2440 pConfig = pHddCtx->config;
2441 rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
2442 rateUpdate.dev_mode = pAdapter->device_mode;
2443 rateUpdate.mcastDataRate24GHz = targetRate;
2444 rateUpdate.mcastDataRate24GHzTxFlag = 1;
2445 rateUpdate.mcastDataRate5GHz = targetRate;
2446 rateUpdate.bcastDataRate = -1;
Srinivas Girigowdaafede182015-11-18 22:36:12 -08002447 cdf_copy_macaddr(&rateUpdate.bssid, &pAdapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002448 hddLog(LOG1,
2449 FL("MC Target rate %d, mac = %pM, dev_mode %s(%d)"),
Srinivas Girigowdaafede182015-11-18 22:36:12 -08002450 rateUpdate.mcastDataRate24GHz, rateUpdate.bssid.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002451 hdd_device_mode_to_string(pAdapter->device_mode),
2452 pAdapter->device_mode);
2453 status = sme_send_rate_update_ind(pHddCtx->hHal, &rateUpdate);
2454 if (CDF_STATUS_SUCCESS != status) {
2455 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: SETMCRATE failed",
2456 __func__);
2457 return -EFAULT;
2458 }
2459 return 0;
2460}
2461
2462static int drv_cmd_p2p_dev_addr(hdd_adapter_t *adapter,
2463 hdd_context_t *hdd_ctx,
2464 uint8_t *command,
2465 uint8_t command_len,
2466 hdd_priv_data_t *priv_data)
2467{
2468 int ret = 0;
2469
2470 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2471 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
2472 adapter->sessionId,
2473 (unsigned)(*(hdd_ctx->p2pDeviceAddress.bytes + 2)
2474 << 24 | *(hdd_ctx->p2pDeviceAddress.bytes
2475 + 3) << 16 | *(hdd_ctx->
2476 p2pDeviceAddress.bytes + 4) << 8 |
2477 *(hdd_ctx->p2pDeviceAddress.bytes +
2478 5))));
2479
2480 if (copy_to_user(priv_data->buf, hdd_ctx->p2pDeviceAddress.bytes,
2481 sizeof(tSirMacAddr))) {
2482 hddLog(CDF_TRACE_LEVEL_ERROR,
2483 "%s: failed to copy data to user buffer",
2484 __func__);
2485 ret = -EFAULT;
2486 }
2487
2488 return ret;
2489}
2490
2491/**
2492 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command
2493 * @adapter: Adapter on which the command was received
2494 * @hdd_ctx: HDD global context
2495 * @command: Entire driver command received from userspace
2496 * @command_len: Length of @command
2497 * @priv_data: Pointer to ioctl private data structure
2498 *
2499 * This is a trivial command hander function which simply forwards the
2500 * command to the actual command processor within the P2P module.
2501 *
2502 * Return: 0 on success, non-zero on failure
2503 */
2504static int drv_cmd_p2p_set_noa(hdd_adapter_t *adapter,
2505 hdd_context_t *hdd_ctx,
2506 uint8_t *command,
2507 uint8_t command_len,
2508 hdd_priv_data_t *priv_data)
2509{
2510 return hdd_set_p2p_noa(adapter->dev, command);
2511}
2512
2513/**
2514 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command
2515 * @adapter: Adapter on which the command was received
2516 * @hdd_ctx: HDD global context
2517 * @command: Entire driver command received from userspace
2518 * @command_len: Length of @command
2519 * @priv_data: Pointer to ioctl private data structure
2520 *
2521 * This is a trivial command hander function which simply forwards the
2522 * command to the actual command processor within the P2P module.
2523 *
2524 * Return: 0 on success, non-zero on failure
2525 */
2526static int drv_cmd_p2p_set_ps(hdd_adapter_t *adapter,
2527 hdd_context_t *hdd_ctx,
2528 uint8_t *command,
2529 uint8_t command_len,
2530 hdd_priv_data_t *priv_data)
2531{
2532 return hdd_set_p2p_opps(adapter->dev, command);
2533}
2534
2535static int drv_cmd_set_band(hdd_adapter_t *adapter,
2536 hdd_context_t *hdd_ctx,
2537 uint8_t *command,
2538 uint8_t command_len,
2539 hdd_priv_data_t *priv_data)
2540{
2541 int ret = 0;
2542
2543 uint8_t *ptr = command;
2544
2545 /* Change band request received */
2546
2547 /*
2548 * First 8 bytes will have "SETBAND " and
2549 * 9 byte will have band setting value
2550 */
2551 hddLog(CDF_TRACE_LEVEL_INFO,
2552 "%s: SetBandCommand Info comm %s UL %d, TL %d",
2553 __func__, command, priv_data->used_len,
2554 priv_data->total_len);
2555
2556 /* Change band request received */
2557 ret = hdd_set_band_helper(adapter->dev, ptr);
2558
2559 return ret;
2560}
2561
2562static int drv_cmd_set_wmmps(hdd_adapter_t *adapter,
2563 hdd_context_t *hdd_ctx,
2564 uint8_t *command,
2565 uint8_t command_len,
2566 hdd_priv_data_t *priv_data)
2567{
2568 return hdd_wmmps_helper(adapter, command);
2569}
2570
2571static int drv_cmd_country(hdd_adapter_t *adapter,
2572 hdd_context_t *hdd_ctx,
2573 uint8_t *command,
2574 uint8_t command_len,
2575 hdd_priv_data_t *priv_data)
2576{
2577 int ret = 0;
2578 CDF_STATUS status;
2579 unsigned long rc;
2580 char *country_code;
2581
2582 country_code = command + 8;
2583
2584 INIT_COMPLETION(adapter->change_country_code);
2585
2586 status = sme_change_country_code(hdd_ctx->hHal,
2587 wlan_hdd_change_country_code_callback,
2588 country_code,
2589 adapter,
2590 hdd_ctx->pcds_context,
2591 eSIR_TRUE,
2592 eSIR_TRUE);
2593 if (status == CDF_STATUS_SUCCESS) {
2594 rc = wait_for_completion_timeout(
2595 &adapter->change_country_code,
2596 msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
2597 if (!rc)
2598 hddLog(CDF_TRACE_LEVEL_ERROR,
2599 "%s: SME while setting country code timed out",
2600 __func__);
2601 } else {
2602 hddLog(CDF_TRACE_LEVEL_ERROR,
2603 "%s: SME Change Country code fail, status=%d",
2604 __func__, status);
2605 ret = -EINVAL;
2606 }
2607
2608 return ret;
2609}
2610
2611static int drv_cmd_set_roam_trigger(hdd_adapter_t *adapter,
2612 hdd_context_t *hdd_ctx,
2613 uint8_t *command,
2614 uint8_t command_len,
2615 hdd_priv_data_t *priv_data)
2616{
2617 int ret = 0;
2618 uint8_t *value = command;
2619 int8_t rssi = 0;
2620 uint8_t lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
2621 CDF_STATUS status = CDF_STATUS_SUCCESS;
2622
2623 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
2624 value = value + command_len + 1;
2625
2626 /* Convert the value from ascii to integer */
2627 ret = kstrtos8(value, 10, &rssi);
2628 if (ret < 0) {
2629 /*
2630 * If the input value is greater than max value of datatype,
2631 * then also kstrtou8 fails
2632 */
2633 CDF_TRACE(CDF_MODULE_ID_HDD,
2634 CDF_TRACE_LEVEL_ERROR,
2635 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2636 __func__,
2637 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
2638 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
2639 ret = -EINVAL;
2640 goto exit;
2641 }
2642
2643 lookUpThreshold = abs(rssi);
2644
2645 if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN)
2646 || (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) {
2647 CDF_TRACE(CDF_MODULE_ID_HDD,
2648 CDF_TRACE_LEVEL_ERROR,
2649 "Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)",
2650 lookUpThreshold,
2651 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
2652 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
2653 ret = -EINVAL;
2654 goto exit;
2655 }
2656
2657 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2658 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
2659 adapter->sessionId, lookUpThreshold));
2660 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2661 "%s: Received Command to Set Roam trigger (Neighbor lookup threshold) = %d",
2662 __func__,
2663 lookUpThreshold);
2664
2665 hdd_ctx->config->nNeighborLookupRssiThreshold = lookUpThreshold;
2666 status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->hHal,
2667 adapter->sessionId,
2668 lookUpThreshold);
2669 if (CDF_STATUS_SUCCESS != status) {
2670 CDF_TRACE(CDF_MODULE_ID_HDD,
2671 CDF_TRACE_LEVEL_ERROR,
2672 "%s: Failed to set roam trigger, try again",
2673 __func__);
2674 ret = -EPERM;
2675 goto exit;
2676 }
2677
2678exit:
2679 return ret;
2680}
2681
2682static int drv_cmd_get_roam_trigger(hdd_adapter_t *adapter,
2683 hdd_context_t *hdd_ctx,
2684 uint8_t *command,
2685 uint8_t command_len,
2686 hdd_priv_data_t *priv_data)
2687{
2688 int ret = 0;
2689 uint8_t lookUpThreshold =
2690 sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->hHal);
2691 int rssi = (-1) * lookUpThreshold;
2692 char extra[32];
2693 uint8_t len = 0;
2694
2695 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2696 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
2697 adapter->sessionId, lookUpThreshold));
2698
2699 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
2700 len = CDF_MIN(priv_data->total_len, len + 1);
2701 if (copy_to_user(priv_data->buf, &extra, len)) {
2702 CDF_TRACE(CDF_MODULE_ID_HDD,
2703 CDF_TRACE_LEVEL_ERROR,
2704 "%s: failed to copy data to user buffer",
2705 __func__);
2706 ret = -EFAULT;
2707 }
2708
2709 return ret;
2710}
2711
2712static int drv_cmd_set_roam_scan_period(hdd_adapter_t *adapter,
2713 hdd_context_t *hdd_ctx,
2714 uint8_t *command,
2715 uint8_t command_len,
2716 hdd_priv_data_t *priv_data)
2717{
2718 int ret = 0;
2719 uint8_t *value = command;
2720 uint8_t roamScanPeriod = 0;
2721 uint16_t neighborEmptyScanRefreshPeriod =
2722 CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
2723
2724 /* input refresh period is in terms of seconds */
2725
2726 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
2727 value = value + command_len + 1;
2728
2729 /* Convert the value from ascii to integer */
2730 ret = kstrtou8(value, 10, &roamScanPeriod);
2731 if (ret < 0) {
2732 /*
2733 * If the input value is greater than max value of datatype,
2734 * then also kstrtou8 fails
2735 */
2736 CDF_TRACE(CDF_MODULE_ID_HDD,
2737 CDF_TRACE_LEVEL_ERROR,
2738 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2739 __func__,
2740 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
2741 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
2742 ret = -EINVAL;
2743 goto exit;
2744 }
2745
2746 if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000))
2747 || (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000))) {
2748 CDF_TRACE(CDF_MODULE_ID_HDD,
2749 CDF_TRACE_LEVEL_ERROR,
2750 "Roam scan period value %d is out of range (Min: %d Max: %d)",
2751 roamScanPeriod,
2752 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
2753 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
2754 ret = -EINVAL;
2755 goto exit;
2756 }
2757 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2758 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
2759 adapter->sessionId, roamScanPeriod));
2760 neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
2761
2762 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2763 "%s: Received Command to Set roam scan period (Empty Scan refresh period) = %d",
2764 __func__,
2765 roamScanPeriod);
2766
2767 hdd_ctx->config->nEmptyScanRefreshPeriod =
2768 neighborEmptyScanRefreshPeriod;
2769 sme_update_empty_scan_refresh_period(hdd_ctx->hHal,
2770 adapter->sessionId,
2771 neighborEmptyScanRefreshPeriod);
2772
2773exit:
2774 return ret;
2775}
2776
2777static int drv_cmd_get_roam_scan_period(hdd_adapter_t *adapter,
2778 hdd_context_t *hdd_ctx,
2779 uint8_t *command,
2780 uint8_t command_len,
2781 hdd_priv_data_t *priv_data)
2782{
2783 int ret = 0;
2784 uint16_t nEmptyScanRefreshPeriod =
2785 sme_get_empty_scan_refresh_period(hdd_ctx->hHal);
2786 char extra[32];
2787 uint8_t len = 0;
2788
2789 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2790 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
2791 adapter->sessionId,
2792 nEmptyScanRefreshPeriod));
2793 len = scnprintf(extra, sizeof(extra), "%s %d",
2794 "GETROAMSCANPERIOD",
2795 (nEmptyScanRefreshPeriod / 1000));
2796 /* Returned value is in units of seconds */
2797 len = CDF_MIN(priv_data->total_len, len + 1);
2798 if (copy_to_user(priv_data->buf, &extra, len)) {
2799 hddLog(LOGE,
2800 FL("failed to copy data to user buffer"));
2801 ret = -EFAULT;
2802 }
2803
2804 return ret;
2805}
2806
2807static int drv_cmd_set_roam_scan_refresh_period(hdd_adapter_t *adapter,
2808 hdd_context_t *hdd_ctx,
2809 uint8_t *command,
2810 uint8_t command_len,
2811 hdd_priv_data_t *priv_data)
2812{
2813 int ret = 0;
2814 uint8_t *value = command;
2815 uint8_t roamScanRefreshPeriod = 0;
2816 uint16_t neighborScanRefreshPeriod =
2817 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
2818
2819 /* input refresh period is in terms of seconds */
2820 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
2821 value = value + command_len + 1;
2822
2823 /* Convert the value from ascii to integer */
2824 ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
2825 if (ret < 0) {
2826 /*
2827 * If the input value is greater than max value of datatype,
2828 * then also kstrtou8 fails
2829 */
2830 CDF_TRACE(CDF_MODULE_ID_HDD,
2831 CDF_TRACE_LEVEL_ERROR,
2832 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2833 __func__,
2834 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000,
2835 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000);
2836 ret = -EINVAL;
2837 goto exit;
2838 }
2839
2840 if ((roamScanRefreshPeriod <
2841 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000))
2842 || (roamScanRefreshPeriod >
2843 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000))) {
2844 CDF_TRACE(CDF_MODULE_ID_HDD,
2845 CDF_TRACE_LEVEL_ERROR,
2846 "Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)",
2847 roamScanRefreshPeriod,
2848 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN
2849 / 1000),
2850 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX
2851 / 1000));
2852 ret = -EINVAL;
2853 goto exit;
2854 }
2855 neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
2856
2857 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2858 "%s: Received Command to Set roam scan refresh period (Scan refresh period) = %d",
2859 __func__,
2860 roamScanRefreshPeriod);
2861
2862 hdd_ctx->config->nNeighborResultsRefreshPeriod =
2863 neighborScanRefreshPeriod;
2864 sme_set_neighbor_scan_refresh_period(hdd_ctx->hHal,
2865 adapter->sessionId,
2866 neighborScanRefreshPeriod);
2867
2868exit:
2869 return ret;
2870}
2871
2872static int drv_cmd_get_roam_scan_refresh_period(hdd_adapter_t *adapter,
2873 hdd_context_t *hdd_ctx,
2874 uint8_t *command,
2875 uint8_t command_len,
2876 hdd_priv_data_t *priv_data)
2877{
2878 int ret = 0;
2879 uint16_t value =
2880 sme_get_neighbor_scan_refresh_period(hdd_ctx->hHal);
2881 char extra[32];
2882 uint8_t len = 0;
2883
2884 len = scnprintf(extra, sizeof(extra), "%s %d",
2885 "GETROAMSCANREFRESHPERIOD",
2886 (value / 1000));
2887 /* Returned value is in units of seconds */
2888 len = CDF_MIN(priv_data->total_len, len + 1);
2889 if (copy_to_user(priv_data->buf, &extra, len)) {
2890 CDF_TRACE(CDF_MODULE_ID_HDD,
2891 CDF_TRACE_LEVEL_ERROR,
2892 "%s: failed to copy data to user buffer",
2893 __func__);
2894 ret = -EFAULT;
2895 }
2896
2897 return ret;
2898}
2899
2900static int drv_cmd_set_roam_mode(hdd_adapter_t *adapter,
2901 hdd_context_t *hdd_ctx,
2902 uint8_t *command,
2903 uint8_t command_len,
2904 hdd_priv_data_t *priv_data)
2905{
2906 int ret = 0;
2907 uint8_t *value = command;
2908 uint8_t roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
2909
2910 /* Move pointer to ahead of SETROAMMODE<delimiter> */
2911 value = value + SIZE_OF_SETROAMMODE + 1;
2912
2913 /* Convert the value from ascii to integer */
2914 ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
2915 if (ret < 0) {
2916 /*
2917 * If the input value is greater than max value of datatype,
2918 * then also kstrtou8 fails
2919 */
2920 CDF_TRACE(CDF_MODULE_ID_HDD,
2921 CDF_TRACE_LEVEL_ERROR,
2922 "%s: kstrtou8 failed range [%d - %d]",
2923 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
2924 CFG_LFR_FEATURE_ENABLED_MAX);
2925 ret = -EINVAL;
2926 goto exit;
2927 }
2928 if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
2929 (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
2930 CDF_TRACE(CDF_MODULE_ID_HDD,
2931 CDF_TRACE_LEVEL_ERROR,
2932 "Roam Mode value %d is out of range (Min: %d Max: %d)",
2933 roamMode,
2934 CFG_LFR_FEATURE_ENABLED_MIN,
2935 CFG_LFR_FEATURE_ENABLED_MAX);
2936 ret = -EINVAL;
2937 goto exit;
2938 }
2939
2940 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_DEBUG,
2941 "%s: Received Command to Set Roam Mode = %d",
2942 __func__, roamMode);
2943 /*
2944 * Note that
2945 * SETROAMMODE 0 is to enable LFR while
2946 * SETROAMMODE 1 is to disable LFR, but
2947 * notify_is_fast_roam_ini_feature_enabled 0/1 is to
2948 * enable/disable. So, we have to invert the value
2949 * to call sme_update_is_fast_roam_ini_feature_enabled.
2950 */
2951 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
2952 roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
2953 else
2954 roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
2955
2956 hdd_ctx->config->isFastRoamIniFeatureEnabled = roamMode;
2957 if (roamMode) {
2958 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
2959 sme_update_roam_scan_offload_enabled(
2960 (tHalHandle)(hdd_ctx->hHal),
2961 hdd_ctx->config->isRoamOffloadScanEnabled);
2962 sme_update_is_fast_roam_ini_feature_enabled(
2963 hdd_ctx->hHal,
2964 adapter->sessionId,
2965 roamMode);
2966 } else {
2967 sme_update_is_fast_roam_ini_feature_enabled(
2968 hdd_ctx->hHal,
2969 adapter->sessionId,
2970 roamMode);
2971 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
2972 sme_update_roam_scan_offload_enabled(
2973 (tHalHandle)(hdd_ctx->hHal),
2974 hdd_ctx->config->isRoamOffloadScanEnabled);
2975 }
2976
2977
2978exit:
2979 return ret;
2980}
2981
2982static int drv_cmd_get_roam_mode(hdd_adapter_t *adapter,
2983 hdd_context_t *hdd_ctx,
2984 uint8_t *command,
2985 uint8_t command_len,
2986 hdd_priv_data_t *priv_data)
2987{
2988 int ret = 0;
2989 bool roamMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
2990 char extra[32];
2991 uint8_t len = 0;
2992
2993 /*
2994 * roamMode value shall be inverted because the sementics is different.
2995 */
2996 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
2997 roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
2998 else
2999 roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
3000
3001 len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
3002 len = CDF_MIN(priv_data->total_len, len + 1);
3003 if (copy_to_user(priv_data->buf, &extra, len)) {
3004 CDF_TRACE(CDF_MODULE_ID_HDD,
3005 CDF_TRACE_LEVEL_ERROR,
3006 "%s: failed to copy data to user buffer",
3007 __func__);
3008 ret = -EFAULT;
3009 }
3010
3011 return ret;
3012}
3013
3014static int drv_cmd_set_roam_delta(hdd_adapter_t *adapter,
3015 hdd_context_t *hdd_ctx,
3016 uint8_t *command,
3017 uint8_t command_len,
3018 hdd_priv_data_t *priv_data)
3019{
3020 int ret = 0;
3021 uint8_t *value = command;
3022 uint8_t roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
3023
3024 /* Move pointer to ahead of SETROAMDELTA<delimiter> */
3025 value = value + command_len + 1;
3026
3027 /* Convert the value from ascii to integer */
3028 ret = kstrtou8(value, 10, &roamRssiDiff);
3029 if (ret < 0) {
3030 /*
3031 * If the input value is greater than max value of datatype,
3032 * then also kstrtou8 fails
3033 */
3034 CDF_TRACE(CDF_MODULE_ID_HDD,
3035 CDF_TRACE_LEVEL_ERROR,
3036 "%s: kstrtou8 failed range [%d - %d]",
3037 __func__, CFG_ROAM_RSSI_DIFF_MIN,
3038 CFG_ROAM_RSSI_DIFF_MAX);
3039 ret = -EINVAL;
3040 goto exit;
3041 }
3042
3043 if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
3044 (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) {
3045 CDF_TRACE(CDF_MODULE_ID_HDD,
3046 CDF_TRACE_LEVEL_ERROR,
3047 "Roam rssi diff value %d is out of range (Min: %d Max: %d)",
3048 roamRssiDiff,
3049 CFG_ROAM_RSSI_DIFF_MIN,
3050 CFG_ROAM_RSSI_DIFF_MAX);
3051 ret = -EINVAL;
3052 goto exit;
3053 }
3054
3055 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3056 "%s: Received Command to Set roam rssi diff = %d",
3057 __func__, roamRssiDiff);
3058
3059 hdd_ctx->config->RoamRssiDiff = roamRssiDiff;
3060 sme_update_roam_rssi_diff(hdd_ctx->hHal,
3061 adapter->sessionId,
3062 roamRssiDiff);
3063
3064exit:
3065 return ret;
3066}
3067
3068static int drv_cmd_get_roam_delta(hdd_adapter_t *adapter,
3069 hdd_context_t *hdd_ctx,
3070 uint8_t *command,
3071 uint8_t command_len,
3072 hdd_priv_data_t *priv_data)
3073{
3074 int ret = 0;
3075 uint8_t roamRssiDiff =
3076 sme_get_roam_rssi_diff(hdd_ctx->hHal);
3077 char extra[32];
3078 uint8_t len = 0;
3079
3080 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3081 TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
3082 adapter->sessionId, roamRssiDiff));
3083
3084 len = scnprintf(extra, sizeof(extra), "%s %d",
3085 command, roamRssiDiff);
3086 len = CDF_MIN(priv_data->total_len, len + 1);
3087
3088 if (copy_to_user(priv_data->buf, &extra, len)) {
3089 CDF_TRACE(CDF_MODULE_ID_HDD,
3090 CDF_TRACE_LEVEL_ERROR,
3091 "%s: failed to copy data to user buffer",
3092 __func__);
3093 ret = -EFAULT;
3094 }
3095
3096 return ret;
3097}
3098
3099static int drv_cmd_get_band(hdd_adapter_t *adapter,
3100 hdd_context_t *hdd_ctx,
3101 uint8_t *command,
3102 uint8_t command_len,
3103 hdd_priv_data_t *priv_data)
3104{
3105 int ret = 0;
3106 int band = -1;
3107 char extra[32];
3108 uint8_t len = 0;
3109
3110 hdd_get_band_helper(hdd_ctx, &band);
3111
3112 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3113 TRACE_CODE_HDD_GETBAND_IOCTL,
3114 adapter->sessionId, band));
3115
3116 len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
3117 len = CDF_MIN(priv_data->total_len, len + 1);
3118
3119 if (copy_to_user(priv_data->buf, &extra, len)) {
3120 CDF_TRACE(CDF_MODULE_ID_HDD,
3121 CDF_TRACE_LEVEL_ERROR,
3122 "%s: failed to copy data to user buffer",
3123 __func__);
3124 ret = -EFAULT;
3125 }
3126
3127 return ret;
3128}
3129
3130static int drv_cmd_set_roam_scan_channels(hdd_adapter_t *adapter,
3131 hdd_context_t *hdd_ctx,
3132 uint8_t *command,
3133 uint8_t command_len,
3134 hdd_priv_data_t *priv_data)
3135{
3136 return hdd_parse_set_roam_scan_channels(adapter, command);
3137}
3138
3139static int drv_cmd_get_roam_scan_channels(hdd_adapter_t *adapter,
3140 hdd_context_t *hdd_ctx,
3141 uint8_t *command,
3142 uint8_t command_len,
3143 hdd_priv_data_t *priv_data)
3144{
3145 int ret = 0;
3146 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
3147 uint8_t numChannels = 0;
3148 uint8_t j = 0;
3149 char extra[128] = { 0 };
3150 int len;
3151
3152 if (CDF_STATUS_SUCCESS !=
3153 sme_get_roam_scan_channel_list(hdd_ctx->hHal,
3154 ChannelList,
3155 &numChannels,
3156 adapter->sessionId)) {
3157 CDF_TRACE(CDF_MODULE_ID_HDD,
3158 CDF_TRACE_LEVEL_FATAL,
3159 "%s: failed to get roam scan channel list",
3160 __func__);
3161 ret = -EFAULT;
3162 goto exit;
3163 }
3164
3165 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3166 TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
3167 adapter->sessionId, numChannels));
3168 /*
3169 * output channel list is of the format
3170 * [Number of roam scan channels][Channel1][Channel2]...
3171 * copy the number of channels in the 0th index
3172 */
3173 len = scnprintf(extra, sizeof(extra), "%s %d", command,
3174 numChannels);
3175 for (j = 0; (j < numChannels); j++)
3176 len += scnprintf(extra + len, sizeof(extra) - len,
3177 " %d", ChannelList[j]);
3178
3179 len = CDF_MIN(priv_data->total_len, len + 1);
3180 if (copy_to_user(priv_data->buf, &extra, len)) {
3181 CDF_TRACE(CDF_MODULE_ID_HDD,
3182 CDF_TRACE_LEVEL_ERROR,
3183 "%s: failed to copy data to user buffer",
3184 __func__);
3185 ret = -EFAULT;
3186 goto exit;
3187 }
3188
3189exit:
3190 return ret;
3191}
3192
3193static int drv_cmd_get_ccx_mode(hdd_adapter_t *adapter,
3194 hdd_context_t *hdd_ctx,
3195 uint8_t *command,
3196 uint8_t command_len,
3197 hdd_priv_data_t *priv_data)
3198{
3199 int ret = 0;
3200 bool eseMode = sme_get_is_ese_feature_enabled(hdd_ctx->hHal);
3201 char extra[32];
3202 uint8_t len = 0;
3203
3204 /*
3205 * Check if the features OKC/ESE/11R are supported simultaneously,
3206 * then this operation is not permitted (return FAILURE)
3207 */
3208 if (eseMode &&
3209 hdd_is_okc_mode_enabled(hdd_ctx) &&
3210 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
3211 CDF_TRACE(CDF_MODULE_ID_HDD,
3212 CDF_TRACE_LEVEL_WARN,
3213 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3214 __func__);
3215 ret = -EPERM;
3216 goto exit;
3217 }
3218
3219 len = scnprintf(extra, sizeof(extra), "%s %d",
3220 "GETCCXMODE", eseMode);
3221 len = CDF_MIN(priv_data->total_len, len + 1);
3222 if (copy_to_user(priv_data->buf, &extra, len)) {
3223 CDF_TRACE(CDF_MODULE_ID_HDD,
3224 CDF_TRACE_LEVEL_ERROR,
3225 "%s: failed to copy data to user buffer",
3226 __func__);
3227 ret = -EFAULT;
3228 goto exit;
3229 }
3230
3231exit:
3232 return ret;
3233}
3234
3235static int drv_cmd_get_okc_mode(hdd_adapter_t *adapter,
3236 hdd_context_t *hdd_ctx,
3237 uint8_t *command,
3238 uint8_t command_len,
3239 hdd_priv_data_t *priv_data)
3240{
3241 int ret = 0;
3242 bool okcMode = hdd_is_okc_mode_enabled(hdd_ctx);
3243 char extra[32];
3244 uint8_t len = 0;
3245
3246 /*
3247 * Check if the features OKC/ESE/11R are supported simultaneously,
3248 * then this operation is not permitted (return FAILURE)
3249 */
3250 if (okcMode &&
3251 sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
3252 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
3253 CDF_TRACE(CDF_MODULE_ID_HDD,
3254 CDF_TRACE_LEVEL_WARN,
3255 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3256 __func__);
3257 ret = -EPERM;
3258 goto exit;
3259 }
3260
3261 len = scnprintf(extra, sizeof(extra), "%s %d",
3262 "GETOKCMODE", okcMode);
3263 len = CDF_MIN(priv_data->total_len, len + 1);
3264
3265 if (copy_to_user(priv_data->buf, &extra, len)) {
3266 CDF_TRACE(CDF_MODULE_ID_HDD,
3267 CDF_TRACE_LEVEL_ERROR,
3268 "%s: failed to copy data to user buffer",
3269 __func__);
3270 ret = -EFAULT;
3271 goto exit;
3272 }
3273
3274exit:
3275 return ret;
3276}
3277
3278static int drv_cmd_get_fast_roam(hdd_adapter_t *adapter,
3279 hdd_context_t *hdd_ctx,
3280 uint8_t *command,
3281 uint8_t command_len,
3282 hdd_priv_data_t *priv_data)
3283{
3284 int ret = 0;
3285 bool lfrMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3286 char extra[32];
3287 uint8_t len = 0;
3288
3289 len = scnprintf(extra, sizeof(extra), "%s %d",
3290 "GETFASTROAM", lfrMode);
3291 len = CDF_MIN(priv_data->total_len, len + 1);
3292
3293 if (copy_to_user(priv_data->buf, &extra, len)) {
3294 CDF_TRACE(CDF_MODULE_ID_HDD,
3295 CDF_TRACE_LEVEL_ERROR,
3296 "%s: failed to copy data to user buffer",
3297 __func__);
3298 ret = -EFAULT;
3299 }
3300
3301 return ret;
3302}
3303
3304static int drv_cmd_get_fast_transition(hdd_adapter_t *adapter,
3305 hdd_context_t *hdd_ctx,
3306 uint8_t *command,
3307 uint8_t command_len,
3308 hdd_priv_data_t *priv_data)
3309{
3310 int ret = 0;
3311 bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->hHal);
3312 char extra[32];
3313 uint8_t len = 0;
3314
3315 len = scnprintf(extra, sizeof(extra), "%s %d",
3316 "GETFASTTRANSITION", ft);
3317 len = CDF_MIN(priv_data->total_len, len + 1);
3318
3319 if (copy_to_user(priv_data->buf, &extra, len)) {
3320 CDF_TRACE(CDF_MODULE_ID_HDD,
3321 CDF_TRACE_LEVEL_ERROR,
3322 "%s: failed to copy data to user buffer",
3323 __func__);
3324 ret = -EFAULT;
3325 }
3326
3327 return ret;
3328}
3329
3330static int drv_cmd_set_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3331 hdd_context_t *hdd_ctx,
3332 uint8_t *command,
3333 uint8_t command_len,
3334 hdd_priv_data_t *priv_data)
3335{
3336 int ret = 0;
3337 uint8_t *value = command;
3338 uint8_t minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
3339
3340 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
3341 value = value + command_len + 1;
3342
3343 /* Convert the value from ascii to integer */
3344 ret = kstrtou8(value, 10, &minTime);
3345 if (ret < 0) {
3346 /*
3347 * If the input value is greater than max value of datatype,
3348 * then also kstrtou8 fails
3349 */
3350 CDF_TRACE(CDF_MODULE_ID_HDD,
3351 CDF_TRACE_LEVEL_ERROR,
3352 "%s: kstrtou8 failed range [%d - %d]",
3353 __func__,
3354 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3355 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3356 ret = -EINVAL;
3357 goto exit;
3358 }
3359
3360 if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
3361 (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) {
3362 CDF_TRACE(CDF_MODULE_ID_HDD,
3363 CDF_TRACE_LEVEL_ERROR,
3364 "scan min channel time value %d is out of range (Min: %d Max: %d)",
3365 minTime,
3366 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3367 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3368 ret = -EINVAL;
3369 goto exit;
3370 }
3371
3372 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3373 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
3374 adapter->sessionId, minTime));
3375 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3376 "%s: Received Command to change channel min time = %d",
3377 __func__, minTime);
3378
3379 hdd_ctx->config->nNeighborScanMinChanTime = minTime;
3380 sme_set_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3381 minTime,
3382 adapter->sessionId);
3383
3384exit:
3385 return ret;
3386}
3387
3388static int drv_cmd_send_action_frame(hdd_adapter_t *adapter,
3389 hdd_context_t *hdd_ctx,
3390 uint8_t *command,
3391 uint8_t command_len,
3392 hdd_priv_data_t *priv_data)
3393{
3394 return hdd_parse_sendactionframe(adapter, command);
3395}
3396
3397static int drv_cmd_get_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3398 hdd_context_t *hdd_ctx,
3399 uint8_t *command,
3400 uint8_t command_len,
3401 hdd_priv_data_t *priv_data)
3402{
3403 int ret = 0;
3404 uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3405 adapter->sessionId);
3406 char extra[32];
3407 uint8_t len = 0;
3408
3409 /* value is interms of msec */
3410 len = scnprintf(extra, sizeof(extra), "%s %d",
3411 "GETROAMSCANCHANNELMINTIME", val);
3412 len = CDF_MIN(priv_data->total_len, len + 1);
3413
3414 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3415 TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL,
3416 adapter->sessionId, val));
3417
3418 if (copy_to_user(priv_data->buf, &extra, len)) {
3419 CDF_TRACE(CDF_MODULE_ID_HDD,
3420 CDF_TRACE_LEVEL_ERROR,
3421 "%s: failed to copy data to user buffer",
3422 __func__);
3423 ret = -EFAULT;
3424 }
3425
3426 return ret;
3427}
3428
3429static int drv_cmd_set_scan_channel_time(hdd_adapter_t *adapter,
3430 hdd_context_t *hdd_ctx,
3431 uint8_t *command,
3432 uint8_t command_len,
3433 hdd_priv_data_t *priv_data)
3434{
3435 int ret = 0;
3436 uint8_t *value = command;
3437 uint16_t maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
3438
3439 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
3440 value = value + command_len + 1;
3441
3442 /* Convert the value from ascii to integer */
3443 ret = kstrtou16(value, 10, &maxTime);
3444 if (ret < 0) {
3445 /*
3446 * If the input value is greater than max value of datatype,
3447 * then also kstrtou8 fails
3448 */
3449 CDF_TRACE(CDF_MODULE_ID_HDD,
3450 CDF_TRACE_LEVEL_ERROR,
3451 "%s: kstrtou16 failed range [%d - %d]",
3452 __func__,
3453 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3454 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3455 ret = -EINVAL;
3456 goto exit;
3457 }
3458
3459 if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
3460 (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) {
3461 CDF_TRACE(CDF_MODULE_ID_HDD,
3462 CDF_TRACE_LEVEL_ERROR,
3463 "lfr mode value %d is out of range (Min: %d Max: %d)",
3464 maxTime,
3465 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3466 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3467 ret = -EINVAL;
3468 goto exit;
3469 }
3470
3471 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3472 "%s: Received Command to change channel max time = %d",
3473 __func__, maxTime);
3474
3475 hdd_ctx->config->nNeighborScanMaxChanTime = maxTime;
3476 sme_set_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3477 adapter->sessionId,
3478 maxTime);
3479
3480exit:
3481 return ret;
3482}
3483
3484static int drv_cmd_get_scan_channel_time(hdd_adapter_t *adapter,
3485 hdd_context_t *hdd_ctx,
3486 uint8_t *command,
3487 uint8_t command_len,
3488 hdd_priv_data_t *priv_data)
3489{
3490 int ret = 0;
3491 uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3492 adapter->sessionId);
3493 char extra[32];
3494 uint8_t len = 0;
3495
3496 /* value is interms of msec */
3497 len = scnprintf(extra, sizeof(extra), "%s %d",
3498 "GETSCANCHANNELTIME", val);
3499 len = CDF_MIN(priv_data->total_len, len + 1);
3500
3501 if (copy_to_user(priv_data->buf, &extra, len)) {
3502 CDF_TRACE(CDF_MODULE_ID_HDD,
3503 CDF_TRACE_LEVEL_ERROR,
3504 "%s: failed to copy data to user buffer",
3505 __func__);
3506 ret = -EFAULT;
3507 }
3508
3509 return ret;
3510}
3511
3512static int drv_cmd_set_scan_home_time(hdd_adapter_t *adapter,
3513 hdd_context_t *hdd_ctx,
3514 uint8_t *command,
3515 uint8_t command_len,
3516 hdd_priv_data_t *priv_data)
3517{
3518 int ret = 0;
3519 uint8_t *value = command;
3520 uint16_t val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
3521
3522 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
3523 value = value + command_len + 1;
3524
3525 /* Convert the value from ascii to integer */
3526 ret = kstrtou16(value, 10, &val);
3527 if (ret < 0) {
3528 /*
3529 * If the input value is greater than max value of datatype,
3530 * then also kstrtou8 fails
3531 */
3532 CDF_TRACE(CDF_MODULE_ID_HDD,
3533 CDF_TRACE_LEVEL_ERROR,
3534 "%s: kstrtou16 failed range [%d - %d]",
3535 __func__,
3536 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3537 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
3538 ret = -EINVAL;
3539 goto exit;
3540 }
3541
3542 if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
3543 (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) {
3544 CDF_TRACE(CDF_MODULE_ID_HDD,
3545 CDF_TRACE_LEVEL_ERROR,
3546 "scan home time value %d is out of range (Min: %d Max: %d)",
3547 val,
3548 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3549 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
3550 ret = -EINVAL;
3551 goto exit;
3552 }
3553
3554 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3555 "%s: Received Command to change scan home time = %d",
3556 __func__, val);
3557
3558 hdd_ctx->config->nNeighborScanPeriod = val;
3559 sme_set_neighbor_scan_period(hdd_ctx->hHal,
3560 adapter->sessionId, val);
3561
3562exit:
3563 return ret;
3564}
3565
3566static int drv_cmd_get_scan_home_time(hdd_adapter_t *adapter,
3567 hdd_context_t *hdd_ctx,
3568 uint8_t *command,
3569 uint8_t command_len,
3570 hdd_priv_data_t *priv_data)
3571{
3572 int ret = 0;
3573 uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->hHal,
3574 adapter->
3575 sessionId);
3576 char extra[32];
3577 uint8_t len = 0;
3578
3579 /* value is interms of msec */
3580 len = scnprintf(extra, sizeof(extra), "%s %d",
3581 "GETSCANHOMETIME", val);
3582 len = CDF_MIN(priv_data->total_len, len + 1);
3583
3584 if (copy_to_user(priv_data->buf, &extra, len)) {
3585 CDF_TRACE(CDF_MODULE_ID_HDD,
3586 CDF_TRACE_LEVEL_ERROR,
3587 "%s: failed to copy data to user buffer",
3588 __func__);
3589 ret = -EFAULT;
3590 }
3591
3592 return ret;
3593}
3594
3595static int drv_cmd_set_roam_intra_band(hdd_adapter_t *adapter,
3596 hdd_context_t *hdd_ctx,
3597 uint8_t *command,
3598 uint8_t command_len,
3599 hdd_priv_data_t *priv_data)
3600{
3601 int ret = 0;
3602 uint8_t *value = command;
3603 uint8_t val = CFG_ROAM_INTRA_BAND_DEFAULT;
3604
3605 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
3606 value = value + command_len + 1;
3607
3608 /* Convert the value from ascii to integer */
3609 ret = kstrtou8(value, 10, &val);
3610 if (ret < 0) {
3611 /*
3612 * If the input value is greater than max value of datatype,
3613 * then also kstrtou8 fails
3614 */
3615 CDF_TRACE(CDF_MODULE_ID_HDD,
3616 CDF_TRACE_LEVEL_ERROR,
3617 "%s: kstrtou8 failed range [%d - %d]",
3618 __func__, CFG_ROAM_INTRA_BAND_MIN,
3619 CFG_ROAM_INTRA_BAND_MAX);
3620 ret = -EINVAL;
3621 goto exit;
3622 }
3623
3624 if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
3625 (val > CFG_ROAM_INTRA_BAND_MAX)) {
3626 CDF_TRACE(CDF_MODULE_ID_HDD,
3627 CDF_TRACE_LEVEL_ERROR,
3628 "intra band mode value %d is out of range (Min: %d Max: %d)",
3629 val,
3630 CFG_ROAM_INTRA_BAND_MIN,
3631 CFG_ROAM_INTRA_BAND_MAX);
3632 ret = -EINVAL;
3633 goto exit;
3634 }
3635 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3636 "%s: Received Command to change intra band = %d",
3637 __func__, val);
3638
3639 hdd_ctx->config->nRoamIntraBand = val;
3640 sme_set_roam_intra_band(hdd_ctx->hHal, val);
3641
3642exit:
3643 return ret;
3644}
3645
3646static int drv_cmd_get_roam_intra_band(hdd_adapter_t *adapter,
3647 hdd_context_t *hdd_ctx,
3648 uint8_t *command,
3649 uint8_t command_len,
3650 hdd_priv_data_t *priv_data)
3651{
3652 int ret = 0;
3653 uint16_t val = sme_get_roam_intra_band(hdd_ctx->hHal);
3654 char extra[32];
3655 uint8_t len = 0;
3656
3657 /* value is interms of msec */
3658 len = scnprintf(extra, sizeof(extra), "%s %d",
3659 "GETROAMINTRABAND", val);
3660 len = CDF_MIN(priv_data->total_len, len + 1);
3661 if (copy_to_user(priv_data->buf, &extra, len)) {
3662 CDF_TRACE(CDF_MODULE_ID_HDD,
3663 CDF_TRACE_LEVEL_ERROR,
3664 "%s: failed to copy data to user buffer",
3665 __func__);
3666 ret = -EFAULT;
3667 }
3668
3669 return ret;
3670}
3671
3672static int drv_cmd_set_scan_n_probes(hdd_adapter_t *adapter,
3673 hdd_context_t *hdd_ctx,
3674 uint8_t *command,
3675 uint8_t command_len,
3676 hdd_priv_data_t *priv_data)
3677{
3678 int ret = 0;
3679 uint8_t *value = command;
3680 uint8_t nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
3681
3682 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */
3683 value = value + command_len + 1;
3684
3685 /* Convert the value from ascii to integer */
3686 ret = kstrtou8(value, 10, &nProbes);
3687 if (ret < 0) {
3688 /*
3689 * If the input value is greater than max value of datatype,
3690 * then also kstrtou8 fails
3691 */
3692 CDF_TRACE(CDF_MODULE_ID_HDD,
3693 CDF_TRACE_LEVEL_ERROR,
3694 "%s: kstrtou8 failed range [%d - %d]",
3695 __func__, CFG_ROAM_SCAN_N_PROBES_MIN,
3696 CFG_ROAM_SCAN_N_PROBES_MAX);
3697 ret = -EINVAL;
3698 goto exit;
3699 }
3700
3701 if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
3702 (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) {
3703 CDF_TRACE(CDF_MODULE_ID_HDD,
3704 CDF_TRACE_LEVEL_ERROR,
3705 "NProbes value %d is out of range (Min: %d Max: %d)",
3706 nProbes,
3707 CFG_ROAM_SCAN_N_PROBES_MIN,
3708 CFG_ROAM_SCAN_N_PROBES_MAX);
3709 ret = -EINVAL;
3710 goto exit;
3711 }
3712
3713 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3714 "%s: Received Command to Set nProbes = %d",
3715 __func__, nProbes);
3716
3717 hdd_ctx->config->nProbes = nProbes;
3718 sme_update_roam_scan_n_probes(hdd_ctx->hHal,
3719 adapter->sessionId, nProbes);
3720
3721exit:
3722 return ret;
3723}
3724
3725static int drv_cmd_get_scan_n_probes(hdd_adapter_t *adapter,
3726 hdd_context_t *hdd_ctx,
3727 uint8_t *command,
3728 uint8_t command_len,
3729 hdd_priv_data_t *priv_data)
3730{
3731 int ret = 0;
3732 uint8_t val = sme_get_roam_scan_n_probes(hdd_ctx->hHal);
3733 char extra[32];
3734 uint8_t len = 0;
3735
3736 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3737 len = CDF_MIN(priv_data->total_len, len + 1);
3738 if (copy_to_user(priv_data->buf, &extra, len)) {
3739 CDF_TRACE(CDF_MODULE_ID_HDD,
3740 CDF_TRACE_LEVEL_ERROR,
3741 "%s: failed to copy data to user buffer",
3742 __func__);
3743 ret = -EFAULT;
3744 }
3745
3746 return ret;
3747}
3748
3749static int drv_cmd_set_scan_home_away_time(hdd_adapter_t *adapter,
3750 hdd_context_t *hdd_ctx,
3751 uint8_t *command,
3752 uint8_t command_len,
3753 hdd_priv_data_t *priv_data)
3754{
3755 int ret = 0;
3756 uint8_t *value = command;
3757 uint16_t homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
3758
3759 /* input value is in units of msec */
3760
3761 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
3762 value = value + command_len + 1;
3763
3764 /* Convert the value from ascii to integer */
3765 ret = kstrtou16(value, 10, &homeAwayTime);
3766 if (ret < 0) {
3767 /*
3768 * If the input value is greater than max value of datatype,
3769 * then also kstrtou8 fails
3770 */
3771 CDF_TRACE(CDF_MODULE_ID_HDD,
3772 CDF_TRACE_LEVEL_ERROR,
3773 "%s: kstrtou8 failed range [%d - %d]",
3774 __func__,
3775 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
3776 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
3777 ret = -EINVAL;
3778 goto exit;
3779 }
3780
3781 if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
3782 (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) {
3783 CDF_TRACE(CDF_MODULE_ID_HDD,
3784 CDF_TRACE_LEVEL_ERROR,
3785 "homeAwayTime value %d is out of range (Min: %d Max: %d)",
3786 homeAwayTime,
3787 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
3788 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
3789 ret = -EINVAL;
3790 goto exit;
3791 }
3792
3793 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3794 "%s: Received Command to Set scan away time = %d",
3795 __func__, homeAwayTime);
3796
3797 if (hdd_ctx->config->nRoamScanHomeAwayTime !=
3798 homeAwayTime) {
3799 hdd_ctx->config->nRoamScanHomeAwayTime = homeAwayTime;
3800 sme_update_roam_scan_home_away_time(hdd_ctx->hHal,
3801 adapter->sessionId,
3802 homeAwayTime,
3803 true);
3804 }
3805
3806exit:
3807 return ret;
3808}
3809
3810static int drv_cmd_get_scan_home_away_time(hdd_adapter_t *adapter,
3811 hdd_context_t *hdd_ctx,
3812 uint8_t *command,
3813 uint8_t command_len,
3814 hdd_priv_data_t *priv_data)
3815{
3816 int ret = 0;
3817 uint16_t val = sme_get_roam_scan_home_away_time(hdd_ctx->hHal);
3818 char extra[32];
3819 uint8_t len = 0;
3820
3821 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3822 len = CDF_MIN(priv_data->total_len, len + 1);
3823
3824 if (copy_to_user(priv_data->buf, &extra, len)) {
3825 CDF_TRACE(CDF_MODULE_ID_HDD,
3826 CDF_TRACE_LEVEL_ERROR,
3827 "%s: failed to copy data to user buffer",
3828 __func__);
3829 ret = -EFAULT;
3830 }
3831
3832 return ret;
3833}
3834
3835static int drv_cmd_reassoc(hdd_adapter_t *adapter,
3836 hdd_context_t *hdd_ctx,
3837 uint8_t *command,
3838 uint8_t command_len,
3839 hdd_priv_data_t *priv_data)
3840{
3841 return hdd_parse_reassoc(adapter, command);
3842}
3843
3844static int drv_cmd_set_wes_mode(hdd_adapter_t *adapter,
3845 hdd_context_t *hdd_ctx,
3846 uint8_t *command,
3847 uint8_t command_len,
3848 hdd_priv_data_t *priv_data)
3849{
3850 int ret = 0;
3851 uint8_t *value = command;
3852 uint8_t wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
3853
3854 /* Move pointer to ahead of SETWESMODE<delimiter> */
3855 value = value + command_len + 1;
3856
3857 /* Convert the value from ascii to integer */
3858 ret = kstrtou8(value, 10, &wesMode);
3859 if (ret < 0) {
3860 /*
3861 * If the input value is greater than max value of datatype,
3862 * then also kstrtou8 fails
3863 */
3864 CDF_TRACE(CDF_MODULE_ID_HDD,
3865 CDF_TRACE_LEVEL_ERROR,
3866 "%s: kstrtou8 failed range [%d - %d]",
3867 __func__,
3868 CFG_ENABLE_WES_MODE_NAME_MIN,
3869 CFG_ENABLE_WES_MODE_NAME_MAX);
3870 ret = -EINVAL;
3871 goto exit;
3872 }
3873
3874 if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
3875 (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) {
3876 CDF_TRACE(CDF_MODULE_ID_HDD,
3877 CDF_TRACE_LEVEL_ERROR,
3878 "WES Mode value %d is out of range (Min: %d Max: %d)",
3879 wesMode,
3880 CFG_ENABLE_WES_MODE_NAME_MIN,
3881 CFG_ENABLE_WES_MODE_NAME_MAX);
3882 ret = -EINVAL;
3883 goto exit;
3884 }
3885
3886 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3887 "%s: Received Command to Set WES Mode rssi diff = %d",
3888 __func__, wesMode);
3889
3890 hdd_ctx->config->isWESModeEnabled = wesMode;
3891 sme_update_wes_mode(hdd_ctx->hHal, wesMode, adapter->sessionId);
3892
3893exit:
3894 return ret;
3895}
3896
3897static int drv_cmd_get_wes_mode(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 bool wesMode = sme_get_wes_mode(hdd_ctx->hHal);
3905 char extra[32];
3906 uint8_t len = 0;
3907
3908 len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
3909 len = CDF_MIN(priv_data->total_len, len + 1);
3910 if (copy_to_user(priv_data->buf, &extra, len)) {
3911 CDF_TRACE(CDF_MODULE_ID_HDD,
3912 CDF_TRACE_LEVEL_ERROR,
3913 "%s: failed to copy data to user buffer",
3914 __func__);
3915 ret = -EFAULT;
3916 }
3917
3918 return ret;
3919}
3920
3921static int drv_cmd_set_opportunistic_rssi_diff(hdd_adapter_t *adapter,
3922 hdd_context_t *hdd_ctx,
3923 uint8_t *command,
3924 uint8_t command_len,
3925 hdd_priv_data_t *priv_data)
3926{
3927 int ret = 0;
3928 uint8_t *value = command;
3929 uint8_t nOpportunisticThresholdDiff =
3930 CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
3931
3932 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
3933 value = value + command_len + 1;
3934
3935 /* Convert the value from ascii to integer */
3936 ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
3937 if (ret < 0) {
3938 /*
3939 * If the input value is greater than max value of datatype,
3940 * then also kstrtou8 fails
3941 */
3942 CDF_TRACE(CDF_MODULE_ID_HDD,
3943 CDF_TRACE_LEVEL_ERROR,
3944 "%s: kstrtou8 failed.", __func__);
3945 ret = -EINVAL;
3946 goto exit;
3947 }
3948
3949 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3950 "%s: Received Command to Set Opportunistic Threshold diff = %d",
3951 __func__, nOpportunisticThresholdDiff);
3952
3953 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->hHal,
3954 adapter->sessionId,
3955 nOpportunisticThresholdDiff);
3956
3957exit:
3958 return ret;
3959}
3960
3961static int drv_cmd_get_opportunistic_rssi_diff(hdd_adapter_t *adapter,
3962 hdd_context_t *hdd_ctx,
3963 uint8_t *command,
3964 uint8_t command_len,
3965 hdd_priv_data_t *priv_data)
3966{
3967 int ret = 0;
3968 int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(
3969 hdd_ctx->hHal);
3970 char extra[32];
3971 uint8_t len = 0;
3972
3973 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3974 len = CDF_MIN(priv_data->total_len, len + 1);
3975 if (copy_to_user(priv_data->buf, &extra, len)) {
3976 CDF_TRACE(CDF_MODULE_ID_HDD,
3977 CDF_TRACE_LEVEL_ERROR,
3978 "%s: failed to copy data to user buffer",
3979 __func__);
3980 ret = -EFAULT;
3981 }
3982
3983 return ret;
3984}
3985
3986static int drv_cmd_set_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
3987 hdd_context_t *hdd_ctx,
3988 uint8_t *command,
3989 uint8_t command_len,
3990 hdd_priv_data_t *priv_data)
3991{
3992 int ret = 0;
3993 uint8_t *value = command;
3994 uint8_t nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
3995
3996 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
3997 value = value + command_len + 1;
3998
3999 /* Convert the value from ascii to integer */
4000 ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
4001 if (ret < 0) {
4002 /*
4003 * If the input value is greater than max value of datatype,
4004 * then also kstrtou8 fails
4005 */
4006 CDF_TRACE(CDF_MODULE_ID_HDD,
4007 CDF_TRACE_LEVEL_ERROR,
4008 "%s: kstrtou8 failed.", __func__);
4009 ret = -EINVAL;
4010 goto exit;
4011 }
4012
4013 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4014 "%s: Received Command to Set Roam Rescan RSSI Diff = %d",
4015 __func__, nRoamRescanRssiDiff);
4016
4017 sme_set_roam_rescan_rssi_diff(hdd_ctx->hHal,
4018 adapter->sessionId,
4019 nRoamRescanRssiDiff);
4020
4021exit:
4022 return ret;
4023}
4024
4025static int drv_cmd_get_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4026 hdd_context_t *hdd_ctx,
4027 uint8_t *command,
4028 uint8_t command_len,
4029 hdd_priv_data_t *priv_data)
4030{
4031 int ret = 0;
4032 uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->hHal);
4033 char extra[32];
4034 uint8_t len = 0;
4035
4036 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
4037 len = CDF_MIN(priv_data->total_len, len + 1);
4038 if (copy_to_user(priv_data->buf, &extra, len)) {
4039 CDF_TRACE(CDF_MODULE_ID_HDD,
4040 CDF_TRACE_LEVEL_ERROR,
4041 "%s: failed to copy data to user buffer",
4042 __func__);
4043 ret = -EFAULT;
4044 }
4045
4046 return ret;
4047}
4048
4049static int drv_cmd_set_fast_roam(hdd_adapter_t *adapter,
4050 hdd_context_t *hdd_ctx,
4051 uint8_t *command,
4052 uint8_t command_len,
4053 hdd_priv_data_t *priv_data)
4054{
4055 int ret = 0;
4056 uint8_t *value = command;
4057 uint8_t lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
4058
4059 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4060 value = value + command_len + 1;
4061
4062 /* Convert the value from ascii to integer */
4063 ret = kstrtou8(value, 10, &lfrMode);
4064 if (ret < 0) {
4065 /*
4066 * If the input value is greater than max value of datatype,
4067 * then also kstrtou8 fails
4068 */
4069 CDF_TRACE(CDF_MODULE_ID_HDD,
4070 CDF_TRACE_LEVEL_ERROR,
4071 "%s: kstrtou8 failed range [%d - %d]",
4072 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
4073 CFG_LFR_FEATURE_ENABLED_MAX);
4074 ret = -EINVAL;
4075 goto exit;
4076 }
4077
4078 if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
4079 (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
4080 CDF_TRACE(CDF_MODULE_ID_HDD,
4081 CDF_TRACE_LEVEL_ERROR,
4082 "lfr mode value %d is out of range (Min: %d Max: %d)",
4083 lfrMode,
4084 CFG_LFR_FEATURE_ENABLED_MIN,
4085 CFG_LFR_FEATURE_ENABLED_MAX);
4086 ret = -EINVAL;
4087 goto exit;
4088 }
4089
4090 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4091 "%s: Received Command to change lfr mode = %d",
4092 __func__, lfrMode);
4093
4094 hdd_ctx->config->isFastRoamIniFeatureEnabled = lfrMode;
4095 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->hHal,
4096 adapter->
4097 sessionId,
4098 lfrMode);
4099
4100exit:
4101 return ret;
4102}
4103
4104static int drv_cmd_set_fast_transition(hdd_adapter_t *adapter,
4105 hdd_context_t *hdd_ctx,
4106 uint8_t *command,
4107 uint8_t command_len,
4108 hdd_priv_data_t *priv_data)
4109{
4110 int ret = 0;
4111 uint8_t *value = command;
4112 uint8_t ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
4113
4114 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4115 value = value + command_len + 1;
4116
4117 /* Convert the value from ascii to integer */
4118 ret = kstrtou8(value, 10, &ft);
4119 if (ret < 0) {
4120 /*
4121 * If the input value is greater than max value of datatype,
4122 * then also kstrtou8 fails
4123 */
4124 CDF_TRACE(CDF_MODULE_ID_HDD,
4125 CDF_TRACE_LEVEL_ERROR,
4126 "%s: kstrtou8 failed range [%d - %d]",
4127 __func__,
4128 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4129 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4130 ret = -EINVAL;
4131 goto exit;
4132 }
4133
4134 if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
4135 (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) {
4136 CDF_TRACE(CDF_MODULE_ID_HDD,
4137 CDF_TRACE_LEVEL_ERROR,
4138 "ft mode value %d is out of range (Min: %d Max: %d)",
4139 ft,
4140 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4141 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4142 ret = -EINVAL;
4143 goto exit;
4144 }
4145
4146 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4147 "%s: Received Command to change ft mode = %d",
4148 __func__, ft);
4149
4150 hdd_ctx->config->isFastTransitionEnabled = ft;
4151 sme_update_fast_transition_enabled(hdd_ctx->hHal, ft);
4152
4153exit:
4154 return ret;
4155}
4156
4157#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4158static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid,
4159 int channel)
4160{
4161 struct wma_roam_invoke_cmd *fastreassoc;
4162 cds_msg_t msg = {0};
4163
4164 fastreassoc = cdf_mem_malloc(sizeof(*fastreassoc));
4165 if (NULL == fastreassoc) {
4166 hddLog(LOGE, FL("cdf_mem_alloc failed for fastreassoc"));
4167 return;
4168 }
4169 fastreassoc->vdev_id = sessionId;
4170 fastreassoc->channel = channel;
4171 fastreassoc->bssid[0] = bssid[0];
4172 fastreassoc->bssid[1] = bssid[1];
4173 fastreassoc->bssid[2] = bssid[2];
4174 fastreassoc->bssid[3] = bssid[3];
4175 fastreassoc->bssid[4] = bssid[4];
4176 fastreassoc->bssid[5] = bssid[5];
4177
4178 msg.type = SIR_HAL_ROAM_INVOKE;
4179 msg.reserved = 0;
4180 msg.bodyptr = fastreassoc;
4181 if (CDF_STATUS_SUCCESS != cds_mq_post_message(CDF_MODULE_ID_WMA,
4182 &msg)) {
4183 cdf_mem_free(fastreassoc);
4184 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
4185 FL("Not able to post ROAM_INVOKE_CMD message to WMA"));
4186 }
4187}
4188#endif
4189static int drv_cmd_fast_reassoc(hdd_adapter_t *adapter,
4190 hdd_context_t *hdd_ctx,
4191 uint8_t *command,
4192 uint8_t command_len,
4193 hdd_priv_data_t *priv_data)
4194{
4195 int ret = 0;
4196 uint8_t *value = command;
4197 uint8_t channel = 0;
4198 tSirMacAddr targetApBssid;
4199 uint32_t roamId = 0;
4200 tCsrRoamModifyProfileFields modProfileFields;
4201#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4202 tCsrHandoffRequest handoffInfo;
4203#endif
4204 hdd_station_ctx_t *pHddStaCtx;
4205
4206 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
4207 hdd_warn("Unsupported in mode %s(%d)",
4208 hdd_device_mode_to_string(adapter->device_mode),
4209 adapter->device_mode);
4210 return -EINVAL;
4211 }
4212
4213 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4214
4215 /* if not associated, no need to proceed with reassoc */
4216 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
4217 CDF_TRACE(CDF_MODULE_ID_HDD,
4218 CDF_TRACE_LEVEL_INFO,
4219 "%s:Not associated!", __func__);
4220 ret = -EINVAL;
4221 goto exit;
4222 }
4223
4224 ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
4225 &channel);
4226 if (ret) {
4227 CDF_TRACE(CDF_MODULE_ID_HDD,
4228 CDF_TRACE_LEVEL_ERROR,
4229 "%s: Failed to parse reassoc command data",
4230 __func__);
4231 goto exit;
4232 }
4233
4234 /*
4235 * if the target bssid is same as currently associated AP,
4236 * issue reassoc to same AP
4237 */
4238 if (true == cdf_mem_compare(targetApBssid,
4239 pHddStaCtx->conn_info.bssId.bytes,
4240 CDF_MAC_ADDR_SIZE)) {
4241 /* Reassoc to same AP, only supported for Open Security*/
4242 if ((pHddStaCtx->conn_info.ucEncryptionType ||
4243 pHddStaCtx->conn_info.mcEncryptionType)) {
4244 hddLog(LOGE,
4245 FL("Reassoc to same AP, only supported for Open Security"));
4246 return -ENOTSUPP;
4247 }
4248 hddLog(LOG1,
4249 FL("Reassoc BSSID is same as currently associated AP bssid"));
4250 sme_get_modify_profile_fields(hdd_ctx->hHal, adapter->sessionId,
4251 &modProfileFields);
4252 sme_roam_reassoc(hdd_ctx->hHal, adapter->sessionId,
4253 NULL, modProfileFields, &roamId, 1);
4254 return 0;
4255 }
4256
4257 /* Check channel number is a valid channel number */
4258 if (CDF_STATUS_SUCCESS !=
4259 wlan_hdd_validate_operation_channel(adapter, channel)) {
4260 hddLog(LOGE, FL("Invalid Channel [%d]"), channel);
4261 return -EINVAL;
4262 }
4263#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4264 if (hdd_ctx->config->isRoamOffloadEnabled) {
4265 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4266 targetApBssid, (int)channel);
4267 goto exit;
4268 }
4269#endif
4270 /* Proceed with reassoc */
4271#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4272 handoffInfo.channel = channel;
4273 handoffInfo.src = FASTREASSOC;
4274 cdf_mem_copy(handoffInfo.bssid, targetApBssid,
4275 sizeof(tSirMacAddr));
4276 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
4277 &handoffInfo);
4278#endif
4279
4280exit:
4281 return ret;
4282}
4283
4284#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
4285static int drv_cmd_ccx_plm_req(hdd_adapter_t *adapter,
4286 hdd_context_t *hdd_ctx,
4287 uint8_t *command,
4288 uint8_t command_len,
4289 hdd_priv_data_t *priv_data)
4290{
4291 int ret = 0;
4292 uint8_t *value = command;
4293 CDF_STATUS status = CDF_STATUS_SUCCESS;
4294 tpSirPlmReq pPlmRequest = NULL;
4295
4296 pPlmRequest = cdf_mem_malloc(sizeof(tSirPlmReq));
4297 if (NULL == pPlmRequest) {
4298 ret = -ENOMEM;
4299 goto exit;
4300 }
4301
4302 status = hdd_parse_plm_cmd(value, pPlmRequest);
4303 if (CDF_STATUS_SUCCESS != status) {
4304 cdf_mem_free(pPlmRequest);
4305 pPlmRequest = NULL;
4306 ret = -EINVAL;
4307 goto exit;
4308 }
4309 pPlmRequest->sessionId = adapter->sessionId;
4310
4311 status = sme_set_plm_request(hdd_ctx->hHal, pPlmRequest);
4312 if (CDF_STATUS_SUCCESS != status) {
4313 cdf_mem_free(pPlmRequest);
4314 pPlmRequest = NULL;
4315 ret = -EINVAL;
4316 goto exit;
4317 }
4318
4319exit:
4320 return ret;
4321}
4322#endif
4323
4324#ifdef FEATURE_WLAN_ESE
4325static int drv_cmd_set_ccx_mode(hdd_adapter_t *adapter,
4326 hdd_context_t *hdd_ctx,
4327 uint8_t *command,
4328 uint8_t command_len,
4329 hdd_priv_data_t *priv_data)
4330{
4331 int ret = 0;
4332 uint8_t *value = command;
4333 uint8_t eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT;
4334
4335 /*
4336 * Check if the features OKC/ESE/11R are supported simultaneously,
4337 * then this operation is not permitted (return FAILURE)
4338 */
4339 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4340 hdd_is_okc_mode_enabled(hdd_ctx) &&
4341 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
4342 CDF_TRACE(CDF_MODULE_ID_HDD,
4343 CDF_TRACE_LEVEL_WARN,
4344 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4345 __func__);
4346 ret = -EPERM;
4347 goto exit;
4348 }
4349
4350 /* Move pointer to ahead of SETCCXMODE<delimiter> */
4351 value = value + command_len + 1;
4352
4353 /* Convert the value from ascii to integer */
4354 ret = kstrtou8(value, 10, &eseMode);
4355 if (ret < 0) {
4356 /*
4357 * If the input value is greater than max value of datatype,
4358 * then also kstrtou8 fails
4359 */
4360 CDF_TRACE(CDF_MODULE_ID_HDD,
4361 CDF_TRACE_LEVEL_ERROR,
4362 "%s: kstrtou8 failed range [%d - %d]",
4363 __func__, CFG_ESE_FEATURE_ENABLED_MIN,
4364 CFG_ESE_FEATURE_ENABLED_MAX);
4365 ret = -EINVAL;
4366 goto exit;
4367 }
4368
4369 if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) ||
4370 (eseMode > CFG_ESE_FEATURE_ENABLED_MAX)) {
4371 CDF_TRACE(CDF_MODULE_ID_HDD,
4372 CDF_TRACE_LEVEL_ERROR,
4373 "Ese mode value %d is out of range (Min: %d Max: %d)",
4374 eseMode,
4375 CFG_ESE_FEATURE_ENABLED_MIN,
4376 CFG_ESE_FEATURE_ENABLED_MAX);
4377 ret = -EINVAL;
4378 goto exit;
4379 }
4380 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4381 "%s: Received Command to change ese mode = %d",
4382 __func__, eseMode);
4383
4384 hdd_ctx->config->isEseIniFeatureEnabled = eseMode;
4385 sme_update_is_ese_feature_enabled(hdd_ctx->hHal,
4386 adapter->sessionId,
4387 eseMode);
4388
4389exit:
4390 return ret;
4391}
4392#endif
4393
4394static int drv_cmd_set_roam_scan_control(hdd_adapter_t *adapter,
4395 hdd_context_t *hdd_ctx,
4396 uint8_t *command,
4397 uint8_t command_len,
4398 hdd_priv_data_t *priv_data)
4399{
4400 int ret = 0;
4401 uint8_t *value = command;
4402 uint8_t roamScanControl = 0;
4403
4404 /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
4405 value = value + command_len + 1;
4406
4407 /* Convert the value from ascii to integer */
4408 ret = kstrtou8(value, 10, &roamScanControl);
4409 if (ret < 0) {
4410 /*
4411 * If the input value is greater than max value of datatype,
4412 * then also kstrtou8 fails
4413 */
4414 CDF_TRACE(CDF_MODULE_ID_HDD,
4415 CDF_TRACE_LEVEL_ERROR,
4416 "%s: kstrtou8 failed ", __func__);
4417 ret = -EINVAL;
4418 goto exit;
4419 }
4420
4421 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4422 "%s: Received Command to Set roam scan control = %d",
4423 __func__, roamScanControl);
4424
4425 if (0 != roamScanControl) {
4426 ret = 0; /* return success but ignore param value "true" */
4427 goto exit;
4428 }
4429
4430 sme_set_roam_scan_control(hdd_ctx->hHal,
4431 adapter->sessionId,
4432 roamScanControl);
4433
4434exit:
4435 return ret;
4436}
4437
4438static int drv_cmd_set_okc_mode(hdd_adapter_t *adapter,
4439 hdd_context_t *hdd_ctx,
4440 uint8_t *command,
4441 uint8_t command_len,
4442 hdd_priv_data_t *priv_data)
4443{
4444 int ret = 0;
4445 uint8_t *value = command;
4446 uint8_t okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
4447
4448 /*
4449 * Check if the features OKC/ESE/11R are supported simultaneously,
4450 * then this operation is not permitted (return FAILURE)
4451 */
4452 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4453 hdd_is_okc_mode_enabled(hdd_ctx) &&
4454 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
4455 CDF_TRACE(CDF_MODULE_ID_HDD,
4456 CDF_TRACE_LEVEL_WARN,
4457 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4458 __func__);
4459 ret = -EPERM;
4460 goto exit;
4461 }
4462
4463 /* Move pointer to ahead of SETOKCMODE<delimiter> */
4464 value = value + command_len + 1;
4465
4466 /* Convert the value from ascii to integer */
4467 ret = kstrtou8(value, 10, &okcMode);
4468 if (ret < 0) {
4469 /*
4470 * If the input value is greater than max value of datatype,
4471 * then also kstrtou8 fails
4472 */
4473 CDF_TRACE(CDF_MODULE_ID_HDD,
4474 CDF_TRACE_LEVEL_ERROR,
4475 "%s: kstrtou8 failed range [%d - %d]",
4476 __func__, CFG_OKC_FEATURE_ENABLED_MIN,
4477 CFG_OKC_FEATURE_ENABLED_MAX);
4478 ret = -EINVAL;
4479 goto exit;
4480 }
4481
4482 if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
4483 (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) {
4484 CDF_TRACE(CDF_MODULE_ID_HDD,
4485 CDF_TRACE_LEVEL_ERROR,
4486 "Okc mode value %d is out of range (Min: %d Max: %d)",
4487 okcMode,
4488 CFG_OKC_FEATURE_ENABLED_MIN,
4489 CFG_OKC_FEATURE_ENABLED_MAX);
4490 ret = -EINVAL;
4491 goto exit;
4492 }
4493 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4494 "%s: Received Command to change okc mode = %d",
4495 __func__, okcMode);
4496
4497 hdd_ctx->config->isOkcIniFeatureEnabled = okcMode;
4498
4499exit:
4500 return ret;
4501}
4502
4503static int drv_cmd_get_roam_scan_control(hdd_adapter_t *adapter,
4504 hdd_context_t *hdd_ctx,
4505 uint8_t *command,
4506 uint8_t command_len,
4507 hdd_priv_data_t *priv_data)
4508{
4509 int ret = 0;
4510 bool roamScanControl = sme_get_roam_scan_control(hdd_ctx->hHal);
4511 char extra[32];
4512 uint8_t len = 0;
4513
4514 len = scnprintf(extra, sizeof(extra), "%s %d",
4515 command, roamScanControl);
4516 len = CDF_MIN(priv_data->total_len, len + 1);
4517 if (copy_to_user(priv_data->buf, &extra, len)) {
4518 CDF_TRACE(CDF_MODULE_ID_HDD,
4519 CDF_TRACE_LEVEL_ERROR,
4520 "%s: failed to copy data to user buffer",
4521 __func__);
4522 ret = -EFAULT;
4523 }
4524
4525 return ret;
4526}
4527
4528static int drv_cmd_bt_coex_mode(hdd_adapter_t *adapter,
4529 hdd_context_t *hdd_ctx,
4530 uint8_t *command,
4531 uint8_t command_len,
4532 hdd_priv_data_t *priv_data)
4533{
4534 int ret = 0;
4535 char *bcMode;
4536
4537 bcMode = command + 11;
4538 if ('1' == *bcMode) {
4539 CDF_TRACE(CDF_MODULE_ID_HDD,
4540 CDF_TRACE_LEVEL_DEBUG,
4541 FL("BTCOEXMODE %d"), *bcMode);
4542 hdd_ctx->btCoexModeSet = true;
4543 ret = wlan_hdd_scan_abort(adapter);
4544 if (ret < 0) {
4545 hddLog(LOGE,
4546 FL("Failed to abort existing scan status:%d"), ret);
4547 }
4548 } else if ('2' == *bcMode) {
4549 CDF_TRACE(CDF_MODULE_ID_HDD,
4550 CDF_TRACE_LEVEL_DEBUG,
4551 FL("BTCOEXMODE %d"), *bcMode);
4552 hdd_ctx->btCoexModeSet = false;
4553 }
4554
4555 return ret;
4556}
4557
4558static int drv_cmd_scan_active(hdd_adapter_t *adapter,
4559 hdd_context_t *hdd_ctx,
4560 uint8_t *command,
4561 uint8_t command_len,
4562 hdd_priv_data_t *priv_data)
4563{
4564 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
4565 return 0;
4566}
4567
4568static int drv_cmd_scan_passive(hdd_adapter_t *adapter,
4569 hdd_context_t *hdd_ctx,
4570 uint8_t *command,
4571 uint8_t command_len,
4572 hdd_priv_data_t *priv_data)
4573{
4574 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN;
4575 return 0;
4576}
4577
4578static int drv_cmd_get_dwell_time(hdd_adapter_t *adapter,
4579 hdd_context_t *hdd_ctx,
4580 uint8_t *command,
4581 uint8_t command_len,
4582 hdd_priv_data_t *priv_data)
4583{
4584 int ret = 0;
4585 struct hdd_config *pCfg =
4586 (WLAN_HDD_GET_CTX(adapter))->config;
4587 char extra[32];
4588 uint8_t len = 0;
4589
4590 memset(extra, 0, sizeof(extra));
4591 ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
4592 len = CDF_MIN(priv_data->total_len, len + 1);
4593 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) {
4594 CDF_TRACE(CDF_MODULE_ID_HDD,
4595 CDF_TRACE_LEVEL_ERROR,
4596 "%s: failed to copy data to user buffer",
4597 __func__);
4598 ret = -EFAULT;
4599 goto exit;
4600 }
4601 ret = len;
4602exit:
4603 return ret;
4604}
4605
4606static int drv_cmd_set_dwell_time(hdd_adapter_t *adapter,
4607 hdd_context_t *hdd_ctx,
4608 uint8_t *command,
4609 uint8_t command_len,
4610 hdd_priv_data_t *priv_data)
4611{
4612 return hdd_set_dwell_time(adapter, command);
4613}
4614
4615static int drv_cmd_miracast(hdd_adapter_t *adapter,
4616 hdd_context_t *hdd_ctx,
4617 uint8_t *command,
4618 uint8_t command_len,
4619 hdd_priv_data_t *priv_data)
4620{
4621 CDF_STATUS ret_status;
4622 int ret = 0;
4623 tHalHandle hHal;
4624 uint8_t filterType = 0;
4625 hdd_context_t *pHddCtx = NULL;
4626 uint8_t *value;
4627
4628 pHddCtx = WLAN_HDD_GET_CTX(adapter);
4629 if (0 != wlan_hdd_validate_context(pHddCtx)) {
4630 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
4631 "%s pHddCtx is not valid, Unable to set miracast mode",
4632 __func__);
4633 return -EINVAL;
4634 }
4635
4636 hHal = pHddCtx->hHal;
4637 value = command + 9;
4638
4639 /* Convert the value from ascii to integer */
4640 ret = kstrtou8(value, 10, &filterType);
4641 if (ret < 0) {
4642 /*
4643 * If the input value is greater than max value of datatype,
4644 * then also kstrtou8 fails
4645 */
4646 CDF_TRACE(CDF_MODULE_ID_HDD,
4647 CDF_TRACE_LEVEL_ERROR,
4648 "%s: kstrtou8 failed range ",
4649 __func__);
4650 ret = -EINVAL;
4651 goto exit;
4652 }
4653 if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL)
4654 || (filterType >
4655 WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
4656 CDF_TRACE(CDF_MODULE_ID_HDD,
4657 CDF_TRACE_LEVEL_ERROR,
4658 "%s: Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink ",
4659 __func__);
4660 ret = -EINVAL;
4661 goto exit;
4662 }
4663 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
4664 pHddCtx->miracast_value = filterType;
4665
4666 ret_status = sme_set_miracast(hHal, filterType);
4667 if (CDF_STATUS_SUCCESS != ret_status) {
4668 hddLog(LOGE, "Failed to set miracast");
4669 return -EBUSY;
4670 }
4671
4672 if (cds_is_mcc_in_24G(pHddCtx))
4673 return cds_set_mas(adapter, filterType);
4674
4675exit:
4676 return ret;
4677}
4678
4679#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
4680static int drv_cmd_set_ccx_roam_scan_channels(hdd_adapter_t *adapter,
4681 hdd_context_t *hdd_ctx,
4682 uint8_t *command,
4683 uint8_t command_len,
4684 hdd_priv_data_t *priv_data)
4685{
4686 int ret = 0;
4687 uint8_t *value = command;
4688 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
4689 uint8_t numChannels = 0;
4690 CDF_STATUS status;
4691
4692 ret = hdd_parse_channellist(value, ChannelList, &numChannels);
4693 if (ret) {
4694 CDF_TRACE(CDF_MODULE_ID_HDD,
4695 CDF_TRACE_LEVEL_ERROR,
4696 "%s: Failed to parse channel list information",
4697 __func__);
4698 goto exit;
4699 }
4700 if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
4701 CDF_TRACE(CDF_MODULE_ID_HDD,
4702 CDF_TRACE_LEVEL_ERROR,
4703 "%s: number of channels (%d) supported exceeded max (%d)",
4704 __func__,
4705 numChannels,
4706 WNI_CFG_VALID_CHANNEL_LIST_LEN);
4707 ret = -EINVAL;
4708 goto exit;
4709 }
4710 status = sme_set_ese_roam_scan_channel_list(hdd_ctx->hHal,
4711 adapter->sessionId,
4712 ChannelList,
4713 numChannels);
4714 if (CDF_STATUS_SUCCESS != status) {
4715 CDF_TRACE(CDF_MODULE_ID_HDD,
4716 CDF_TRACE_LEVEL_ERROR,
4717 "%s: Failed to update channel list information",
4718 __func__);
4719 ret = -EINVAL;
4720 goto exit;
4721 }
4722
4723exit:
4724 return ret;
4725}
4726
4727static int drv_cmd_get_tsm_stats(hdd_adapter_t *adapter,
4728 hdd_context_t *hdd_ctx,
4729 uint8_t *command,
4730 uint8_t command_len,
4731 hdd_priv_data_t *priv_data)
4732{
4733 int ret = 0;
4734 uint8_t *value = command;
4735 char extra[128] = { 0 };
4736 int len = 0;
4737 uint8_t tid = 0;
4738 hdd_station_ctx_t *pHddStaCtx;
4739 tAniTrafStrmMetrics tsm_metrics;
4740
4741 if ((WLAN_HDD_INFRA_STATION != adapter->device_mode) &&
4742 (WLAN_HDD_P2P_CLIENT != adapter->device_mode)) {
4743 hdd_warn("Unsupported in mode %s(%d)",
4744 hdd_device_mode_to_string(adapter->device_mode),
4745 adapter->device_mode);
4746 return -EINVAL;
4747 }
4748
4749 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4750
4751 /* if not associated, return error */
4752 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
4753 CDF_TRACE(CDF_MODULE_ID_HDD,
4754 CDF_TRACE_LEVEL_ERROR,
4755 "%s:Not associated!", __func__);
4756 ret = -EINVAL;
4757 goto exit;
4758 }
4759
4760 /* Move pointer to ahead of GETTSMSTATS<delimiter> */
4761 value = value + command_len + 1;
4762
4763 /* Convert the value from ascii to integer */
4764 ret = kstrtou8(value, 10, &tid);
4765 if (ret < 0) {
4766 /*
4767 * If the input value is greater than max value of datatype,
4768 * then also kstrtou8 fails
4769 */
4770 CDF_TRACE(CDF_MODULE_ID_HDD,
4771 CDF_TRACE_LEVEL_ERROR,
4772 "%s: kstrtou8 failed range [%d - %d]",
4773 __func__, TID_MIN_VALUE,
4774 TID_MAX_VALUE);
4775 ret = -EINVAL;
4776 goto exit;
4777 }
4778 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) {
4779 CDF_TRACE(CDF_MODULE_ID_HDD,
4780 CDF_TRACE_LEVEL_ERROR,
4781 "tid value %d is out of range (Min: %d Max: %d)",
4782 tid, TID_MIN_VALUE, TID_MAX_VALUE);
4783 ret = -EINVAL;
4784 goto exit;
4785 }
4786 CDF_TRACE(CDF_MODULE_ID_HDD,
4787 CDF_TRACE_LEVEL_INFO,
4788 "%s: Received Command to get tsm stats tid = %d",
4789 __func__, tid);
4790 if (CDF_STATUS_SUCCESS !=
4791 hdd_get_tsm_stats(adapter, tid, &tsm_metrics)) {
4792 CDF_TRACE(CDF_MODULE_ID_HDD,
4793 CDF_TRACE_LEVEL_ERROR,
4794 "%s: failed to get tsm stats",
4795 __func__);
4796 ret = -EFAULT;
4797 goto exit;
4798 }
4799 CDF_TRACE(CDF_MODULE_ID_HDD,
4800 CDF_TRACE_LEVEL_INFO,
4801 "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)",
4802 tsm_metrics.UplinkPktQueueDly,
4803 tsm_metrics.UplinkPktQueueDlyHist[0],
4804 tsm_metrics.UplinkPktQueueDlyHist[1],
4805 tsm_metrics.UplinkPktQueueDlyHist[2],
4806 tsm_metrics.UplinkPktQueueDlyHist[3],
4807 tsm_metrics.UplinkPktTxDly,
4808 tsm_metrics.UplinkPktLoss,
4809 tsm_metrics.UplinkPktCount,
4810 tsm_metrics.RoamingCount,
4811 tsm_metrics.RoamingDly);
4812 /*
4813 * Output TSM stats is of the format
4814 * GETTSMSTATS [PktQueueDly]
4815 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
4816 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800
4817 */
4818 len = scnprintf(extra,
4819 sizeof(extra),
4820 "%s %d %d:%d:%d:%d %u %d %d %d %d",
4821 command,
4822 tsm_metrics.UplinkPktQueueDly,
4823 tsm_metrics.UplinkPktQueueDlyHist[0],
4824 tsm_metrics.UplinkPktQueueDlyHist[1],
4825 tsm_metrics.UplinkPktQueueDlyHist[2],
4826 tsm_metrics.UplinkPktQueueDlyHist[3],
4827 tsm_metrics.UplinkPktTxDly,
4828 tsm_metrics.UplinkPktLoss,
4829 tsm_metrics.UplinkPktCount,
4830 tsm_metrics.RoamingCount,
4831 tsm_metrics.RoamingDly);
4832 len = CDF_MIN(priv_data->total_len, len + 1);
4833 if (copy_to_user(priv_data->buf, &extra, len)) {
4834 CDF_TRACE(CDF_MODULE_ID_HDD,
4835 CDF_TRACE_LEVEL_ERROR,
4836 "%s: failed to copy data to user buffer",
4837 __func__);
4838 ret = -EFAULT;
4839 goto exit;
4840 }
4841
4842exit:
4843 return ret;
4844}
4845
4846static int drv_cmd_set_cckm_ie(hdd_adapter_t *adapter,
4847 hdd_context_t *hdd_ctx,
4848 uint8_t *command,
4849 uint8_t command_len,
4850 hdd_priv_data_t *priv_data)
4851{
4852 int ret;
4853 uint8_t *value = command;
4854 uint8_t *cckmIe = NULL;
4855 uint8_t cckmIeLen = 0;
4856
4857 ret = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
4858 if (ret) {
4859 CDF_TRACE(CDF_MODULE_ID_HDD,
4860 CDF_TRACE_LEVEL_ERROR,
4861 "%s: Failed to parse cckm ie data",
4862 __func__);
4863 goto exit;
4864 }
4865
4866 if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) {
4867 CDF_TRACE(CDF_MODULE_ID_HDD,
4868 CDF_TRACE_LEVEL_ERROR,
4869 "%s: CCKM Ie input length is more than max[%d]",
4870 __func__, DOT11F_IE_RSN_MAX_LEN);
4871 if (NULL != cckmIe) {
4872 cdf_mem_free(cckmIe);
4873 cckmIe = NULL;
4874 }
4875 ret = -EINVAL;
4876 goto exit;
4877 }
4878
4879 sme_set_cckm_ie(hdd_ctx->hHal, adapter->sessionId,
4880 cckmIe, cckmIeLen);
4881 if (NULL != cckmIe) {
4882 cdf_mem_free(cckmIe);
4883 cckmIe = NULL;
4884 }
4885
4886exit:
4887 return ret;
4888}
4889
4890static int drv_cmd_ccx_beacon_req(hdd_adapter_t *adapter,
4891 hdd_context_t *hdd_ctx,
4892 uint8_t *command,
4893 uint8_t command_len,
4894 hdd_priv_data_t *priv_data)
4895{
4896 int ret;
4897 uint8_t *value = command;
4898 tCsrEseBeaconReq eseBcnReq;
4899 CDF_STATUS status = CDF_STATUS_SUCCESS;
4900
4901 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
4902 hdd_warn("Unsupported in mode %s(%d)",
4903 hdd_device_mode_to_string(adapter->device_mode),
4904 adapter->device_mode);
4905 return -EINVAL;
4906 }
4907
4908 ret = hdd_parse_ese_beacon_req(value, &eseBcnReq);
4909 if (ret) {
4910 CDF_TRACE(CDF_MODULE_ID_HDD,
4911 CDF_TRACE_LEVEL_ERROR,
4912 "%s: Failed to parse ese beacon req",
4913 __func__);
4914 goto exit;
4915 }
4916
4917 if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
4918 hddLog(CDF_TRACE_LEVEL_INFO, FL("Not associated"));
4919 hdd_indicate_ese_bcn_report_no_results(adapter,
4920 eseBcnReq.bcnReq[0].measurementToken,
4921 0x02, /* BIT(1) set for measurement done */
4922 0); /* no BSS */
4923 goto exit;
4924 }
4925
4926 status = sme_set_ese_beacon_request(hdd_ctx->hHal,
4927 adapter->sessionId,
4928 &eseBcnReq);
4929
4930 if (CDF_STATUS_E_RESOURCES == status) {
4931 hddLog(CDF_TRACE_LEVEL_INFO,
4932 FL("sme_set_ese_beacon_request failed (%d), a request already in progress"),
4933 status);
4934 ret = -EBUSY;
4935 goto exit;
4936 } else if (CDF_STATUS_SUCCESS != status) {
4937 CDF_TRACE(CDF_MODULE_ID_HDD,
4938 CDF_TRACE_LEVEL_ERROR,
4939 "%s: sme_set_ese_beacon_request failed (%d)",
4940 __func__, status);
4941 ret = -EINVAL;
4942 goto exit;
4943 }
4944
4945exit:
4946 return ret;
4947}
4948#endif /* #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) */
4949
4950static int drv_cmd_set_mc_rate(hdd_adapter_t *adapter,
4951 hdd_context_t *hdd_ctx,
4952 uint8_t *command,
4953 uint8_t command_len,
4954 hdd_priv_data_t *priv_data)
4955{
4956 int ret = 0;
4957 uint8_t *value = command;
4958 int targetRate;
4959
4960 /* input value is in units of hundred kbps */
4961
4962 /* Move pointer to ahead of SETMCRATE<delimiter> */
4963 value = value + command_len + 1;
4964
4965 /* Convert the value from ascii to integer, decimal base */
4966 ret = kstrtouint(value, 10, &targetRate);
4967
4968 ret = wlan_hdd_set_mc_rate(adapter, targetRate);
4969 return ret;
4970}
4971
4972static int drv_cmd_max_tx_power(hdd_adapter_t *adapter,
4973 hdd_context_t *hdd_ctx,
4974 uint8_t *command,
4975 uint8_t command_len,
4976 hdd_priv_data_t *priv_data)
4977{
4978 int ret = 0;
4979 int status;
4980 int txPower;
4981 CDF_STATUS cdf_status;
4982 CDF_STATUS smeStatus;
4983 uint8_t *value = command;
Srinivas Girigowda97215232015-09-24 12:26:28 -07004984 struct cdf_mac_addr bssid = CDF_MAC_ADDR_BROADCAST_INITIALIZER;
4985 struct cdf_mac_addr selfMac = CDF_MAC_ADDR_BROADCAST_INITIALIZER;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004986 hdd_adapter_list_node_t *pAdapterNode = NULL;
4987 hdd_adapter_list_node_t *pNext = NULL;
4988
4989 status = hdd_parse_setmaxtxpower_command(value, &txPower);
4990 if (status) {
4991 CDF_TRACE(CDF_MODULE_ID_HDD,
4992 CDF_TRACE_LEVEL_ERROR,
4993 "Invalid MAXTXPOWER command ");
4994 ret = -EINVAL;
4995 goto exit;
4996 }
4997
4998 cdf_status = hdd_get_front_adapter(hdd_ctx, &pAdapterNode);
4999 while (NULL != pAdapterNode
5000 && CDF_STATUS_SUCCESS == cdf_status) {
5001 adapter = pAdapterNode->pAdapter;
5002 /* Assign correct self MAC address */
Srinivas Girigowda97215232015-09-24 12:26:28 -07005003 cdf_copy_macaddr(&bssid,
5004 &adapter->macAddressCurrent);
5005 cdf_copy_macaddr(&selfMac,
5006 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005007
5008 hddLog(CDF_TRACE_LEVEL_INFO,
5009 "Device mode %d max tx power %d selfMac: " MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
5010 adapter->device_mode, txPower,
Srinivas Girigowda97215232015-09-24 12:26:28 -07005011 MAC_ADDR_ARRAY(selfMac.bytes),
5012 MAC_ADDR_ARRAY(bssid.bytes));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005013
Srinivas Girigowda97215232015-09-24 12:26:28 -07005014 smeStatus = sme_set_max_tx_power(hdd_ctx->hHal,
5015 bssid, selfMac, txPower);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005016 if (CDF_STATUS_SUCCESS != status) {
5017 hddLog(CDF_TRACE_LEVEL_ERROR,
5018 "%s:Set max tx power failed",
5019 __func__);
5020 ret = -EINVAL;
5021 goto exit;
5022 }
5023 hddLog(CDF_TRACE_LEVEL_INFO,
5024 "%s: Set max tx power success",
5025 __func__);
5026 cdf_status = hdd_get_next_adapter(hdd_ctx, pAdapterNode,
5027 &pNext);
5028 pAdapterNode = pNext;
5029 }
5030
5031exit:
5032 return ret;
5033}
5034
5035static int drv_cmd_set_dfs_scan_mode(hdd_adapter_t *adapter,
5036 hdd_context_t *hdd_ctx,
5037 uint8_t *command,
5038 uint8_t command_len,
5039 hdd_priv_data_t *priv_data)
5040{
5041 int ret = 0;
5042 uint8_t *value = command;
5043 uint8_t dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
5044
5045 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
5046 value = value + command_len + 1;
5047
5048 /* Convert the value from ascii to integer */
5049 ret = kstrtou8(value, 10, &dfsScanMode);
5050 if (ret < 0) {
5051 /*
5052 * If the input value is greater than max value of datatype,
5053 * then also kstrtou8 fails
5054 */
5055 CDF_TRACE(CDF_MODULE_ID_HDD,
5056 CDF_TRACE_LEVEL_ERROR,
5057 "%s: kstrtou8 failed range [%d - %d]",
5058 __func__, CFG_ROAMING_DFS_CHANNEL_MIN,
5059 CFG_ROAMING_DFS_CHANNEL_MAX);
5060 ret = -EINVAL;
5061 goto exit;
5062 }
5063
5064 if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
5065 (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) {
5066 CDF_TRACE(CDF_MODULE_ID_HDD,
5067 CDF_TRACE_LEVEL_ERROR,
5068 "dfsScanMode value %d is out of range (Min: %d Max: %d)",
5069 dfsScanMode,
5070 CFG_ROAMING_DFS_CHANNEL_MIN,
5071 CFG_ROAMING_DFS_CHANNEL_MAX);
5072 ret = -EINVAL;
5073 goto exit;
5074 }
5075
5076 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
5077 "%s: Received Command to Set DFS Scan Mode = %d",
5078 __func__, dfsScanMode);
5079
Deepak Dhamdhere29b3b2f2015-01-22 11:09:55 -08005080 /* When DFS scanning is disabled, the DFS channels need to be
5081 * removed from the operation of device.
5082 */
5083 ret = wlan_hdd_disable_dfs_chan_scan(hdd_ctx, adapter,
5084 (dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED));
5085 if (ret < 0) {
5086 /* Some conditions prevented it from disabling DFS channels */
5087 hddLog(LOGE,
5088 FL("disable/enable DFS channel request was denied"));
5089 goto exit;
5090 }
5091
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005092 hdd_ctx->config->allowDFSChannelRoam = dfsScanMode;
5093 sme_update_dfs_scan_mode(hdd_ctx->hHal, adapter->sessionId,
5094 dfsScanMode);
5095
5096exit:
5097 return ret;
5098}
5099
5100static int drv_cmd_get_dfs_scan_mode(hdd_adapter_t *adapter,
5101 hdd_context_t *hdd_ctx,
5102 uint8_t *command,
5103 uint8_t command_len,
5104 hdd_priv_data_t *priv_data)
5105{
5106 int ret = 0;
5107 uint8_t dfsScanMode = sme_get_dfs_scan_mode(hdd_ctx->hHal);
5108 char extra[32];
5109 uint8_t len = 0;
5110
5111 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
5112 len = CDF_MIN(priv_data->total_len, len + 1);
5113 if (copy_to_user(priv_data->buf, &extra, len)) {
5114 CDF_TRACE(CDF_MODULE_ID_HDD,
5115 CDF_TRACE_LEVEL_ERROR,
5116 "%s: failed to copy data to user buffer",
5117 __func__);
5118 ret = -EFAULT;
5119 }
5120
5121 return ret;
5122}
5123
5124static int drv_cmd_get_link_status(hdd_adapter_t *adapter,
5125 hdd_context_t *hdd_ctx,
5126 uint8_t *command,
5127 uint8_t command_len,
5128 hdd_priv_data_t *priv_data)
5129{
5130 int ret = 0;
5131 int value = wlan_hdd_get_link_status(adapter);
5132 char extra[32];
5133 uint8_t len;
5134
5135 len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
5136 len = CDF_MIN(priv_data->total_len, len + 1);
5137 if (copy_to_user(priv_data->buf, &extra, len)) {
5138 CDF_TRACE(CDF_MODULE_ID_HDD,
5139 CDF_TRACE_LEVEL_ERROR,
5140 "%s: failed to copy data to user buffer",
5141 __func__);
5142 ret = -EFAULT;
5143 }
5144
5145 return ret;
5146}
5147
5148#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
5149static int drv_cmd_enable_ext_wow(hdd_adapter_t *adapter,
5150 hdd_context_t *hdd_ctx,
5151 uint8_t *command,
5152 uint8_t command_len,
5153 hdd_priv_data_t *priv_data)
5154{
5155 uint8_t *value = command;
5156 int set_value;
5157
5158 /* Move pointer to ahead of ENABLEEXTWOW */
5159 value = value + command_len;
5160
5161 sscanf(value, "%d", &set_value);
5162
5163 return hdd_enable_ext_wow_parser(adapter,
5164 adapter->sessionId,
5165 set_value);
5166}
5167
5168static int drv_cmd_set_app1_params(hdd_adapter_t *adapter,
5169 hdd_context_t *hdd_ctx,
5170 uint8_t *command,
5171 uint8_t command_len,
5172 hdd_priv_data_t *priv_data)
5173{
5174 int ret;
5175 uint8_t *value = command;
5176
5177 /* Move pointer to ahead of SETAPP1PARAMS */
5178 value = value + command_len;
5179
5180 ret = hdd_set_app_type1_parser(adapter,
5181 value, strlen(value));
5182 if (ret >= 0)
5183 hdd_ctx->is_extwow_app_type1_param_set = true;
5184
5185 return ret;
5186}
5187
5188static int drv_cmd_set_app2_params(hdd_adapter_t *adapter,
5189 hdd_context_t *hdd_ctx,
5190 uint8_t *command,
5191 uint8_t command_len,
5192 hdd_priv_data_t *priv_data)
5193{
5194 int ret;
5195 uint8_t *value = command;
5196
5197 /* Move pointer to ahead of SETAPP2PARAMS */
5198 value = value + command_len;
5199
5200 ret = hdd_set_app_type2_parser(adapter, value, strlen(value));
5201 if (ret >= 0)
5202 hdd_ctx->is_extwow_app_type2_param_set = true;
5203
5204 return ret;
5205}
5206#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
5207
5208#ifdef FEATURE_WLAN_TDLS
5209/**
5210 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset
5211 * @adapter: Pointer to the HDD adapter
5212 * @hdd_ctx: Pointer to the HDD context
5213 * @command: Driver command string
5214 * @command_len: Driver command string length
5215 * @priv_data: Private data coming with the driver command. Unused here
5216 *
5217 * This function handles driver command that sets the secondary tdls off channel
5218 * offset
5219 *
5220 * Return: 0 on success; negative errno otherwise
5221 */
5222static int drv_cmd_tdls_secondary_channel_offset(hdd_adapter_t *adapter,
5223 hdd_context_t *hdd_ctx,
5224 uint8_t *command,
5225 uint8_t command_len,
5226 hdd_priv_data_t *priv_data)
5227{
5228 int ret;
5229 uint8_t *value = command;
5230 int set_value;
5231
5232 /* Move pointer to point the string */
5233 value += command_len;
5234
5235 ret = sscanf(value, "%d", &set_value);
5236 if (ret != 1)
5237 return -EINVAL;
5238
5239 hddLog(LOG1, FL("Tdls offchannel offset:%d"), set_value);
5240
5241 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, set_value);
5242
5243 return ret;
5244}
5245
5246/**
5247 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode
5248 * @adapter: Pointer to the HDD adapter
5249 * @hdd_ctx: Pointer to the HDD context
5250 * @command: Driver command string
5251 * @command_len: Driver command string length
5252 * @priv_data: Private data coming with the driver command. Unused here
5253 *
5254 * This function handles driver command that sets tdls off channel mode
5255 *
5256 * Return: 0 on success; negative errno otherwise
5257 */
5258static int drv_cmd_tdls_off_channel_mode(hdd_adapter_t *adapter,
5259 hdd_context_t *hdd_ctx,
5260 uint8_t *command,
5261 uint8_t command_len,
5262 hdd_priv_data_t *priv_data)
5263{
5264 int ret;
5265 uint8_t *value = command;
5266 int set_value;
5267
5268 /* Move pointer to point the string */
5269 value += command_len;
5270
5271 ret = sscanf(value, "%d", &set_value);
5272 if (ret != 1)
5273 return -EINVAL;
5274
5275 hddLog(LOG1, FL("Tdls offchannel mode:%d"), set_value);
5276
5277 ret = hdd_set_tdls_offchannelmode(adapter, set_value);
5278
5279 return ret;
5280}
5281
5282/**
5283 * drv_cmd_tdls_off_channel() - set tdls off channel number
5284 * @adapter: Pointer to the HDD adapter
5285 * @hdd_ctx: Pointer to the HDD context
5286 * @command: Driver command string
5287 * @command_len: Driver command string length
5288 * @priv_data: Private data coming with the driver command. Unused here
5289 *
5290 * This function handles driver command that sets tdls off channel number
5291 *
5292 * Return: 0 on success; negative errno otherwise
5293 */
5294static int drv_cmd_tdls_off_channel(hdd_adapter_t *adapter,
5295 hdd_context_t *hdd_ctx,
5296 uint8_t *command,
5297 uint8_t command_len,
5298 hdd_priv_data_t *priv_data)
5299{
5300 int ret;
5301 uint8_t *value = command;
5302 int set_value;
5303
5304 /* Move pointer to point the string */
5305 value += command_len;
5306
5307 ret = sscanf(value, "%d", &set_value);
5308 if (ret != 1)
5309 return -EINVAL;
5310
5311 hddLog(LOG1, FL("Tdls offchannel num: %d"), set_value);
5312
5313 ret = hdd_set_tdls_offchannel(hdd_ctx, set_value);
5314
5315 return ret;
5316}
5317
5318/**
5319 * drv_cmd_tdls_scan() - set tdls scan type
5320 * @adapter: Pointer to the HDD adapter
5321 * @hdd_ctx: Pointer to the HDD context
5322 * @command: Driver command string
5323 * @command_len: Driver command string length
5324 * @priv_data: Private data coming with the driver command. Unused here
5325 *
5326 * This function handles driver command that sets tdls scan type
5327 *
5328 * Return: 0 on success; negative errno otherwise
5329 */
5330static int drv_cmd_tdls_scan(hdd_adapter_t *adapter,
5331 hdd_context_t *hdd_ctx,
5332 uint8_t *command,
5333 uint8_t command_len,
5334 hdd_priv_data_t *priv_data)
5335{
5336 int ret;
5337 uint8_t *value = command;
5338 int set_value;
5339
5340 /* Move pointer to point the string */
5341 value += command_len;
5342
5343 ret = sscanf(value, "%d", &set_value);
5344 if (ret != 1)
5345 return -EINVAL;
5346
5347 hddLog(LOG1, FL("Tdls scan type val: %d"), set_value);
5348
5349 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value);
5350
5351 return ret;
5352}
5353#endif
5354
5355static int drv_cmd_get_rssi(hdd_adapter_t *adapter,
5356 hdd_context_t *hdd_ctx,
5357 uint8_t *command,
5358 uint8_t command_len,
5359 hdd_priv_data_t *priv_data)
5360{
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005361 int ret = 0;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005362 int8_t rssi = 0;
5363 char extra[32];
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005364
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005365 uint8_t len = 0;
5366
5367 wlan_hdd_get_rssi(adapter, &rssi);
5368
5369 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
5370 len = CDF_MIN(priv_data->total_len, len + 1);
5371
5372 if (copy_to_user(priv_data->buf, &extra, len)) {
5373 hddLog(LOGE, FL("Failed to copy data to user buffer"));
5374 ret = -EFAULT;
5375 }
5376
5377 return ret;
5378}
5379
5380static int drv_cmd_get_linkspeed(hdd_adapter_t *adapter,
5381 hdd_context_t *hdd_ctx,
5382 uint8_t *command,
5383 uint8_t command_len,
5384 hdd_priv_data_t *priv_data)
5385{
5386 int ret;
5387 uint32_t link_speed = 0;
5388 char extra[32];
5389 uint8_t len = 0;
5390
5391 ret = wlan_hdd_get_link_speed(adapter, &link_speed);
5392 if (0 != ret)
5393 return ret;
5394
5395 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
5396 len = CDF_MIN(priv_data->total_len, len + 1);
5397 if (copy_to_user(priv_data->buf, &extra, len)) {
5398 hddLog(LOGE, FL("Failed to copy data to user buffer"));
5399 ret = -EFAULT;
5400 }
5401
5402 return ret;
5403}
5404
5405#ifdef FEATURE_NAPI
5406/**
5407 * hdd_parse_napi() - helper functions to drv_cmd_napi
5408 * @str : source string to parse
5409 * @cmd : pointer to cmd part after parsing
5410 * @sub : pointer to subcmd part after parsing
5411 * @aux : pointer to optional aux part after parsing
5412 *
5413 * Example:
5414 * NAPI SCALE <n> +-- IN str
5415 * | | +------ OUT aux
5416 * | +------------ OUT subcmd
5417 * +----------------- OUT cmd
5418 *
5419 * Return: ==0: success; !=0: failure
5420 */
5421static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
5422{
5423 int rc;
5424 char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
5425
5426 NAPI_DEBUG("-->\n");
5427
5428 token = strsep(str, " \t");
5429 if (NULL == token) {
5430 hdd_err("cannot parse cmd");
5431 goto parse_end;
5432 }
5433 lcmd = token;
5434
5435 token = strsep(str, " \t");
5436 if (NULL == token) {
5437 hdd_err("cannot parse subcmd");
5438 goto parse_end;
5439 }
5440 lsub = token;
5441
5442 token = strsep(str, " \t");
5443 if (NULL == token)
5444 hdd_warn("cannot parse aux\n");
5445 else
5446 laux = token;
5447
5448parse_end:
5449 if ((NULL == lcmd) || (NULL == lsub))
5450 rc = -EINVAL;
5451 else {
5452 rc = 0;
5453 *cmd = lcmd;
5454 *sub = lsub;
5455 if (NULL != aux)
5456 *aux = laux;
5457 }
5458 NAPI_DEBUG("<--[rc=%d]\n", rc);
5459 return rc;
5460}
5461
5462
5463/**
5464 * hdd_parse_stats() - print NAPI stats into a buffer
5465 * @buf : buffer to write stats into
5466 * @max : "size of buffer"
5467 * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
5468 * @napid: binary structure to retrieve the stats from
5469 *
5470 * Return: number of bytes written into the buffer
5471 */
5472int hdd_napi_stats(char *buf,
5473 int max,
5474 char *indp,
5475 struct qca_napi_data *napid)
5476{
5477 int n = 0;
5478 int i, j, k; /* NAPI, CPU, bucket indices */
5479 int from, to;
5480 struct qca_napi_info *napii;
5481 struct qca_napi_stat *napis;
5482
5483 NAPI_DEBUG("-->\n");
5484
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005485 if (NULL == napid)
5486 return n;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005487 if (NULL == indp) {
5488 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005489 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005490 } else {
5491 if (0 > kstrtoint(indp, 10, &to)) {
5492 from = 0;
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005493 to = CE_COUNT_MAX;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005494 } else
5495 from = to;
5496 }
5497
5498 for (i = from; i < to; i++)
5499 if (napid->ce_map & (0x01 << i)) {
5500 napii = &(napid->napis[i]);
5501 for (j = 0; j < NR_CPUS; j++) {
5502 napis = &(napii->stats[j]);
5503 n += scnprintf(buf + n, max - n,
5504 "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
5505 i, j,
5506 napis->napi_schedules,
5507 napis->napi_polls,
5508 napis->napi_completes,
5509 napis->napi_workdone);
5510
5511 for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
5512 n += scnprintf(
5513 buf + n, max - n,
5514 " %d",
5515 napis->napi_budget_uses[k]);
5516 }
5517 n += scnprintf(buf+n, max - n, "\n");
5518 }
5519 }
5520
5521 NAPI_DEBUG("<--[n=%d]\n", n);
5522 return n;
5523}
5524
5525/**
5526 * napi_set_scale() - sets the scale attribute in all NAPI entries
5527 * @sc : scale to set
5528 *
5529 * Return: void
5530 */
5531static void napi_set_scale(uint8_t sc)
5532{
5533 uint32_t i;
5534 struct qca_napi_data *napi_data;
5535
5536 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005537 if (likely(NULL != napi_data))
5538 for (i = 0; i < CE_COUNT_MAX; i++)
5539 if (napi_data->ce_map & (0x01 << i))
5540 napi_data->napis[i].scale = sc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005541
5542 return;
5543}
5544/**
5545 * drv_cmd_napi() - processes NAPI commands
5546 * @adapter : net_device
5547 * @hdd_ctx : HDD context
5548 * @command : command string from user command (including "NAPI")
5549 * @command_len: length of command
5550 * @priv_data : ifr_data
5551 *
5552 * Commands supported:
5553 * NAPI ENABLE : enables NAPI administratively. Note that this may not
5554 * enable NAPI functionally, as some other conditions
5555 * may not have been satisfied yet
5556 * NAPI DISABLE : reverse operation of "enable"
5557 * NAPI STATUS : get global status of NAPI instances
5558 * NAPI STATS [<n>] : get the stats for a given NAPI instance
5559 * NAPI SCALE <n> : set the scale factor
5560 *
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005561 * Return: 0: success; !0: failure
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005562 */
5563static int drv_cmd_napi(hdd_adapter_t *adapter,
5564 hdd_context_t *hdd_ctx,
5565 uint8_t *command,
5566 uint8_t command_len,
5567 hdd_priv_data_t *priv_data)
5568{
5569 int rc = 0;
5570 int n, l;
5571 char *cmd = NULL, *subcmd = NULL, *aux = NULL;
5572 char *synopsis = "NAPI ENABLE\n"
5573 "NAPI DISABLE\n"
5574 "NAPI STATUS\n"
5575 "NAPI STATS [<n>] -- if no <n> then all\n"
5576 "NAPI SCALE <n> -- set the scale\n";
5577 char *reply = NULL;
5578
5579 /* make a local copy, as strsep modifies the str in place */
5580 char *str = NULL;
5581
5582 NAPI_DEBUG("-->\n");
5583
5584 /**
5585 * NOTE TO MAINTAINER: from this point to the end of the function,
5586 * please do not return anywhere in the code except the very end
5587 * to avoid memory leakage (goto end_drv_napi instead)
5588 * or make sure that reply+str is freed
5589 */
5590 reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
5591 if (NULL == reply) {
5592 hdd_err("could not allocate reply buffer");
5593 rc = -ENOMEM;
5594 goto end_drv_napi;
5595 }
5596
5597 str = kmalloc(strlen(command) + 1, GFP_KERNEL);
5598 if (NULL == str) {
5599 hdd_err("could not allocate copy of input buffer");
5600 rc = -ENOMEM;
5601 goto end_drv_napi;
5602 }
5603
5604 strlcpy(str, command, strlen(command) + 1);
5605 hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
5606 cmd, subcmd, aux);
5607
5608
5609 rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
5610
5611 if (0 != rc) {
5612 const char *msg = "unknown or badly formatted cmd\n%s";
5613 l = CDF_MIN(MAX_USER_COMMAND_SIZE,
5614 strlen(msg)+strlen(synopsis));
5615 n = scnprintf(reply, l, msg, synopsis);
5616
5617 if (copy_to_user(priv_data->buf, reply,
5618 CDF_MIN(priv_data->total_len, l)))
5619 hdd_err("failed to copy data to user buffer");
5620 hdd_debug("reply: %s", reply);
5621
5622 rc = -EINVAL;
5623 } else {
5624 hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
5625 cmd, subcmd, aux);
5626 if (!strcmp(subcmd, "ENABLE"))
5627 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
5628 else if (!strcmp(subcmd, "DISABLE"))
5629 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
5630 else if (!strcmp(subcmd, "STATUS")) {
5631 int n = 0;
5632 uint32_t i;
5633 struct qca_napi_data *napi_data;
5634
5635 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005636 if (unlikely(NULL == napi_data))
5637 goto status_end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005638 n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
5639 "NAPI state: 0x%08x map: 0x%08x\n",
5640 napi_data->state,
5641 napi_data->ce_map);
5642
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005643 for (i = 0; i < CE_COUNT_MAX; i++)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005644 if (napi_data->ce_map & (0x01 << i)) {
5645 n += scnprintf(
5646 reply + n,
5647 MAX_USER_COMMAND_SIZE - n,
5648 "#%d: id: %d, scale=%d\n",
5649 i,
5650 napi_data->napis[i].id,
5651 napi_data->napis[i].scale);
5652 }
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005653 status_end:
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005654 hdd_info("wlan: STATUS DATA:\n%s", reply);
5655 if (copy_to_user(priv_data->buf, reply,
5656 CDF_MIN(n, priv_data->total_len)))
5657 rc = -EINVAL;
5658 } else if (!strcmp(subcmd, "STATS")) {
5659 int n = 0;
5660 struct qca_napi_data *napi_data;
5661
5662 napi_data = hdd_napi_get_all();
Orhan K AKYILDIZc4094612015-11-11 18:01:15 -08005663 if (NULL != napi_data) {
5664 n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
5665 aux, napi_data);
5666 NAPI_DEBUG("STATS: returns %d\n", n);
5667 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005668 if (n > 0) {
5669 if (copy_to_user(priv_data->buf, reply,
5670 CDF_MIN(priv_data->total_len,
5671 n)))
5672 rc = -EINVAL;
5673 hdd_info("wlan: STATS_DATA\n%s\n", reply);
5674 } else
5675 rc = -EINVAL;
5676 } else if (!strcmp(subcmd, "SCALE")) {
5677 if (NULL == aux) {
5678 rc = -EINVAL;
5679 hdd_err("wlan: SCALE cmd requires <n>");
5680 } else {
5681 uint8_t sc;
5682 rc = kstrtou8(aux, 10, &sc);
5683 if (rc) {
5684 hdd_err("wlan: bad scale (%s)", aux);
5685 rc = -EINVAL;
5686 } else
5687 napi_set_scale(sc);
5688 }
5689 } /* SCALE */
5690 }
5691end_drv_napi:
5692 if (NULL != str)
5693 kfree(str);
5694 if (NULL != reply)
5695 kfree(reply);
5696
5697 NAPI_DEBUG("<--[rc=%d]\n", rc);
5698 return rc;
5699}
5700#endif /* FEATURE_NAPI */
5701
5702/**
5703 * hdd_set_rx_filter() - set RX filter
5704 * @adapter: Pointer to adapter
5705 * @action: Filter action
5706 * @pattern: Address pattern
5707 *
5708 * Address pattern is most significant byte of address for example
5709 * 0x01 for IPV4 multicast address
5710 * 0x33 for IPV6 multicast address
5711 * 0xFF for broadcast address
5712 *
5713 * Return: 0 for success, non-zero for failure
5714 */
5715static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
5716 uint8_t pattern)
5717{
5718 int ret;
5719 uint8_t i;
5720 tHalHandle handle;
5721 tSirRcvFltMcAddrList *filter;
5722 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
5723
5724 ret = wlan_hdd_validate_context(hdd_ctx);
5725 if (0 != ret)
5726 return ret;
5727
5728 handle = hdd_ctx->hHal;
5729
5730 if (NULL == handle) {
5731 hdd_err("HAL Handle is NULL");
5732 return -EINVAL;
5733 }
5734
5735 /*
5736 * If action is false it means start dropping packets
5737 * Set addr_filter_pattern which will be used when sending
5738 * MC/BC address list to target
5739 */
5740 if (!action)
5741 adapter->addr_filter_pattern = pattern;
5742 else
5743 adapter->addr_filter_pattern = 0;
5744
5745 if (((adapter->device_mode == WLAN_HDD_INFRA_STATION) ||
5746 (adapter->device_mode == WLAN_HDD_P2P_CLIENT)) &&
5747 adapter->mc_addr_list.mc_cnt &&
5748 hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
5749
5750
5751 filter = cdf_mem_malloc(sizeof(*filter));
5752 if (NULL == filter) {
5753 hdd_err("Could not allocate Memory");
5754 return -ENOMEM;
5755 }
5756 filter->action = action;
5757 for (i = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
5758 if (!memcmp(adapter->mc_addr_list.addr[i],
5759 &pattern, 1)) {
5760 memcpy(filter->multicastAddr[i],
5761 adapter->mc_addr_list.addr[i],
5762 sizeof(adapter->mc_addr_list.addr[i]));
5763 filter->ulMulticastAddrCnt++;
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08005764 hdd_info("%s RX filter : addr ="
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005765 MAC_ADDRESS_STR,
5766 action ? "setting" : "clearing",
5767 MAC_ADDR_ARRAY(filter->multicastAddr[i]));
5768 }
5769 }
5770 /* Set rx filter */
5771 sme_8023_multicast_list(handle, adapter->sessionId, filter);
5772 cdf_mem_free(filter);
5773 } else {
Srinivas Girigowdaf2599dd2015-11-16 18:20:46 -08005774 hdd_info("mode %d mc_cnt %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08005775 adapter->device_mode, adapter->mc_addr_list.mc_cnt);
5776 }
5777
5778 return 0;
5779}
5780
5781/**
5782 * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
5783 * @command: Pointer to input string driver command
5784 * @adapter: Pointer to adapter
5785 * @action: Action to enable/disable filtering
5786 *
5787 * If action == false
5788 * Start filtering out data packets based on type
5789 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
5790 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
5791 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
5792 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
5793 *
5794 * if action == true
5795 * Stop filtering data packets based on type
5796 * RXFILTER-ADD 0 -> Stop filtering unicast data packets
5797 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets
5798 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
5799 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
5800 *
5801 * Current implementation only supports IPV4 address filtering by
5802 * selectively allowing IPV4 multicast data packest based on
5803 * address list received in .ndo_set_rx_mode
5804 *
5805 * Return: 0 for success, non-zero for failure
5806 */
5807static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
5808 hdd_adapter_t *adapter,
5809 bool action)
5810{
5811 int ret = 0;
5812 uint8_t *value;
5813 uint8_t type;
5814
5815 value = command;
5816 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
5817 if (!action)
5818 value = command + 16;
5819 else
5820 value = command + 13;
5821 ret = kstrtou8(value, 10, &type);
5822 if (ret < 0) {
5823 hdd_err("kstrtou8 failed invalid input value %d", type);
5824 return -EINVAL;
5825 }
5826
5827 switch (type) {
5828 case 2:
5829 /* Set rx filter for IPV4 multicast data packets */
5830 ret = hdd_set_rx_filter(adapter, action, 0x01);
5831 break;
5832 default:
5833 hdd_info("Unsupported RXFILTER type %d", type);
5834 break;
5835 }
5836
5837 return ret;
5838}
5839
5840/**
5841 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler
5842 * @adapter: Pointer to network adapter
5843 * @hdd_ctx: Pointer to hdd context
5844 * @command: Pointer to input command
5845 * @command_len: Command length
5846 * @priv_data: Pointer to private data in command
5847 */
5848static int drv_cmd_rx_filter_remove(hdd_adapter_t *adapter,
5849 hdd_context_t *hdd_ctx,
5850 uint8_t *command,
5851 uint8_t command_len,
5852 hdd_priv_data_t *priv_data)
5853{
5854 return hdd_driver_rxfilter_comand_handler(command, adapter, false);
5855}
5856
5857/**
5858 * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler
5859 * @adapter: Pointer to network adapter
5860 * @hdd_ctx: Pointer to hdd context
5861 * @command: Pointer to input command
5862 * @command_len: Command length
5863 * @priv_data: Pointer to private data in command
5864 */
5865static int drv_cmd_rx_filter_add(hdd_adapter_t *adapter,
5866 hdd_context_t *hdd_ctx,
5867 uint8_t *command,
5868 uint8_t command_len,
5869 hdd_priv_data_t *priv_data)
5870{
5871 return hdd_driver_rxfilter_comand_handler(command, adapter, true);
5872}
5873
5874/*
5875 * dummy (no-op) hdd driver command handler
5876 */
5877static int drv_cmd_dummy(hdd_adapter_t *adapter,
5878 hdd_context_t *hdd_ctx,
5879 uint8_t *command,
5880 uint8_t command_len,
5881 hdd_priv_data_t *priv_data)
5882{
5883 hdd_info("%s: Ignoring driver command \"%s\"",
5884 adapter->dev->name, command);
5885 return 0;
5886}
5887
5888/*
5889 * handler for any unsupported wlan hdd driver command
5890 */
5891static int drv_cmd_invalid(hdd_adapter_t *adapter,
5892 hdd_context_t *hdd_ctx,
5893 uint8_t *command,
5894 uint8_t command_len,
5895 hdd_priv_data_t *priv_data)
5896{
5897 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
5898 TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
5899 adapter->sessionId, 0));
5900
5901 hdd_warn("%s: Unsupported driver command \"%s\"",
5902 adapter->dev->name, command);
5903
5904 return -ENOTSUPP;
5905}
5906
5907/**
5908 * drv_cmd_set_fcc_channel() - handle fcc constraint request
5909 * @adapter: HDD adapter
5910 * @hdd_ctx: HDD context
5911 * @command: command ptr, SET_FCC_CHANNEL 0/1 is the command
5912 * @command_len: command len
5913 * @priv_data: private data
5914 *
5915 * Return: status
5916 */
5917static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter,
5918 hdd_context_t *hdd_ctx,
5919 uint8_t *command,
5920 uint8_t command_len,
5921 hdd_priv_data_t *priv_data)
5922{
5923 uint8_t *value;
5924 uint8_t fcc_constraint;
5925 CDF_STATUS status;
5926 int ret = 0;
5927
5928 /*
5929 * this command would be called by user-space when it detects WLAN
5930 * ON after airplane mode is set. When APM is set, WLAN turns off.
5931 * But it can be turned back on. Otherwise; when APM is turned back
5932 * off, WLAN would turn back on. So at that point the command is
5933 * expected to come down. 0 means disable, 1 means enable. The
5934 * constraint is removed when parameter 1 is set or different
5935 * country code is set
5936 */
5937
5938 value = command + command_len + 1;
5939
5940 ret = kstrtou8(value, 10, &fcc_constraint);
5941 if ((ret < 0) || (fcc_constraint > 1)) {
5942 /*
5943 * If the input value is greater than max value of datatype,
5944 * then also it is a failure
5945 */
5946 hdd_err("value out of range");
5947 return -EINVAL;
5948 }
5949
5950 status = sme_disable_non_fcc_channel(hdd_ctx->hHal, !fcc_constraint);
5951 if (status != CDF_STATUS_SUCCESS) {
5952 hdd_err("sme disable fn. returned err");
5953 ret = -EPERM;
5954 }
5955
5956 return ret;
5957}
5958
5959/*
5960 * The following table contains all supported WLAN HDD
5961 * IOCTL driver commands and the handler for each of them.
5962 */
5963static const hdd_drv_cmd_t hdd_drv_cmds[] = {
5964 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr},
5965 {"P2P_SET_NOA", drv_cmd_p2p_set_noa},
5966 {"P2P_SET_PS", drv_cmd_p2p_set_ps},
5967 {"SETBAND", drv_cmd_set_band},
5968 {"SETWMMPS", drv_cmd_set_wmmps},
5969 {"COUNTRY", drv_cmd_country},
5970 {"SETSUSPENDMODE", drv_cmd_dummy},
5971 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy},
5972 {"BTCOEXSCAN", drv_cmd_dummy},
5973 {"RXFILTER", drv_cmd_dummy},
5974#ifdef WLAN_FEATURE_NEIGHBOR_ROAMING
5975 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger},
5976 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger},
5977 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period},
5978 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period},
5979 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period},
5980 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period},
5981#ifdef FEATURE_WLAN_LFR
5982 {"SETROAMMODE", drv_cmd_set_roam_mode},
5983 {"GETROAMMODE", drv_cmd_get_roam_mode},
5984#endif
5985#endif
5986#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
5987 {"SETROAMDELTA", drv_cmd_set_roam_delta},
5988 {"GETROAMDELTA", drv_cmd_get_roam_delta},
5989#endif
5990#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
5991 {"GETBAND", drv_cmd_get_band},
5992 {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels},
5993 {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels},
5994 {"GETCCXMODE", drv_cmd_get_ccx_mode},
5995 {"GETOKCMODE", drv_cmd_get_okc_mode},
5996 {"GETFASTROAM", drv_cmd_get_fast_roam},
5997 {"GETFASTTRANSITION", drv_cmd_get_fast_transition},
5998 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time},
5999 {"SENDACTIONFRAME", drv_cmd_send_action_frame},
6000 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time},
6001 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time},
6002 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time},
6003 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time},
6004 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time},
6005 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band},
6006 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band},
6007 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes},
6008 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes},
6009 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time},
6010 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time},
6011 {"REASSOC", drv_cmd_reassoc},
6012 {"SETWESMODE", drv_cmd_set_wes_mode},
6013 {"GETWESMODE", drv_cmd_get_wes_mode},
6014 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff},
6015 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff},
6016 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff},
6017 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff},
6018#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */
6019#ifdef FEATURE_WLAN_LFR
6020 {"SETFASTROAM", drv_cmd_set_fast_roam},
6021#endif
6022#ifdef WLAN_FEATURE_VOWIFI_11R
6023 {"SETFASTTRANSITION", drv_cmd_set_fast_transition},
6024 {"FASTREASSOC", drv_cmd_fast_reassoc},
6025#endif
6026#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
6027 {"CCXPLMREQ", drv_cmd_ccx_plm_req},
6028#endif
6029#ifdef FEATURE_WLAN_ESE
6030 {"SETCCXMODE", drv_cmd_set_ccx_mode},
6031#endif
6032 {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control},
6033#ifdef FEATURE_WLAN_OKC
6034 {"SETOKCMODE", drv_cmd_set_okc_mode},
6035#endif /* FEATURE_WLAN_OKC */
6036 {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control},
6037 {"BTCOEXMODE", drv_cmd_bt_coex_mode},
6038 {"SCAN-ACTIVE", drv_cmd_scan_active},
6039 {"SCAN-PASSIVE", drv_cmd_scan_passive},
6040 {"GETDWELLTIME", drv_cmd_get_dwell_time},
6041 {"SETDWELLTIME", drv_cmd_set_dwell_time},
6042 {"MIRACAST", drv_cmd_miracast},
6043#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
6044 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels},
6045 {"GETTSMSTATS", drv_cmd_get_tsm_stats},
6046 {"SETCCKMIE", drv_cmd_set_cckm_ie},
6047 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req},
6048#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
6049 {"SETMCRATE", drv_cmd_set_mc_rate},
6050 {"MAXTXPOWER", drv_cmd_max_tx_power},
6051 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode},
6052 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode},
6053 {"GETLINKSTATUS", drv_cmd_get_link_status},
6054#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
6055 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow},
6056 {"SETAPP1PARAMS", drv_cmd_set_app1_params},
6057 {"SETAPP2PARAMS", drv_cmd_set_app2_params},
6058#endif
6059#ifdef FEATURE_WLAN_TDLS
6060 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset},
6061 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode},
6062 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel},
6063 {"TDLSSCAN", drv_cmd_tdls_scan},
6064#endif
6065 {"RSSI", drv_cmd_get_rssi},
6066 {"LINKSPEED", drv_cmd_get_linkspeed},
6067#ifdef FEATURE_NAPI
6068 {"NAPI", drv_cmd_napi},
6069#endif /* FEATURE_NAPI */
6070 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove},
6071 {"RXFILTER-ADD", drv_cmd_rx_filter_add},
6072 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel},
6073};
6074
6075/**
6076 * hdd_drv_cmd_process() - chooses and runs the proper
6077 * handler based on the input command
6078 * @adapter: Pointer to the hdd adapter
6079 * @cmd: Pointer to the driver command
6080 * @priv_data: Pointer to the data associated with the command
6081 *
6082 * This function parses the input hdd driver command and runs
6083 * the proper handler
6084 *
6085 * Return: 0 for success non-zero for failure
6086 */
6087static int hdd_drv_cmd_process(hdd_adapter_t *adapter,
6088 uint8_t *cmd,
6089 hdd_priv_data_t *priv_data)
6090{
6091 hdd_context_t *hdd_ctx;
6092 int i;
6093 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds);
6094 uint8_t *cmd_i = NULL;
6095 hdd_drv_cmd_handler_t handler = NULL;
6096 int len = 0;
6097
6098 if (!adapter || !cmd || !priv_data) {
6099 hddLog(CDF_TRACE_LEVEL_ERROR,
6100 "%s: at least 1 param is NULL", __func__);
6101 return -EINVAL;
6102 }
6103
6104 hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
6105
6106 for (i = 0; i < cmd_num_total; i++) {
6107
6108 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd;
6109 handler = hdd_drv_cmds[i].handler;
6110 len = strlen(cmd_i);
6111
6112 if (!handler) {
6113 hddLog(CDF_TRACE_LEVEL_ERROR,
6114 "%s: no. %d handler is NULL", __func__, i);
6115 return -EINVAL;
6116 }
6117
6118 if (strncasecmp(cmd, cmd_i, len) == 0)
6119 return handler(adapter, hdd_ctx,
6120 cmd, len, priv_data);
6121 }
6122
6123 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data);
6124}
6125
6126/**
6127 * hdd_driver_command() - top level wlan hdd driver command handler
6128 * @adapter: Pointer to the hdd adapter
6129 * @priv_data: Pointer to the raw command data
6130 *
6131 * This function is the top level wlan hdd driver command handler. It
6132 * handles the command with the help of hdd_drv_cmd_process()
6133 *
6134 * Return: 0 for success non-zero for failure
6135 */
6136static int hdd_driver_command(hdd_adapter_t *adapter,
6137 hdd_priv_data_t *priv_data)
6138{
6139 uint8_t *command = NULL;
6140 int ret = 0;
6141
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05306142 ENTER();
6143
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006144 if (CDF_FTM_MODE == hdd_get_conparam()) {
6145 hddLog(LOGE, FL("Command not allowed in FTM mode"));
6146 return -EINVAL;
6147 }
6148
6149 /*
6150 * Note that valid pointers are provided by caller
6151 */
6152
6153 /* copy to local struct to avoid numerous changes to legacy code */
6154 if (priv_data->total_len <= 0 ||
6155 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) {
6156 hddLog(CDF_TRACE_LEVEL_WARN,
6157 "%s:invalid priv_data.total_len(%d)!!!", __func__,
6158 priv_data->total_len);
6159 ret = -EINVAL;
6160 goto exit;
6161 }
6162
6163 /* Allocate +1 for '\0' */
6164 command = kmalloc(priv_data->total_len + 1, GFP_KERNEL);
6165 if (!command) {
6166 hddLog(CDF_TRACE_LEVEL_ERROR,
6167 "%s: failed to allocate memory", __func__);
6168 ret = -ENOMEM;
6169 goto exit;
6170 }
6171
6172 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) {
6173 ret = -EFAULT;
6174 goto exit;
6175 }
6176
6177 /* Make sure the command is NUL-terminated */
6178 command[priv_data->total_len] = '\0';
6179
6180 hdd_info("%s: %s", adapter->dev->name, command);
6181 ret = hdd_drv_cmd_process(adapter, command, priv_data);
6182
6183exit:
6184 if (command)
6185 kfree(command);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05306186 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006187 return ret;
6188}
6189
6190#ifdef CONFIG_COMPAT
6191static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6192{
6193 struct {
6194 compat_uptr_t buf;
6195 int used_len;
6196 int total_len;
6197 } compat_priv_data;
6198 hdd_priv_data_t priv_data;
6199 int ret = 0;
6200
6201 /*
6202 * Note that adapter and ifr have already been verified by caller,
6203 * and HDD context has also been validated
6204 */
6205 if (copy_from_user(&compat_priv_data, ifr->ifr_data,
6206 sizeof(compat_priv_data))) {
6207 ret = -EFAULT;
6208 goto exit;
6209 }
6210 priv_data.buf = compat_ptr(compat_priv_data.buf);
6211 priv_data.used_len = compat_priv_data.used_len;
6212 priv_data.total_len = compat_priv_data.total_len;
6213 ret = hdd_driver_command(adapter, &priv_data);
6214exit:
6215 return ret;
6216}
6217#else /* CONFIG_COMPAT */
6218static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6219{
6220 /* will never be invoked */
6221 return 0;
6222}
6223#endif /* CONFIG_COMPAT */
6224
6225static int hdd_driver_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6226{
6227 hdd_priv_data_t priv_data;
6228 int ret = 0;
6229
6230 /*
6231 * Note that adapter and ifr have already been verified by caller,
6232 * and HDD context has also been validated
6233 */
6234 if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data)))
6235 ret = -EFAULT;
6236 else
6237 ret = hdd_driver_command(adapter, &priv_data);
6238
6239 return ret;
6240}
6241
6242/**
6243 * __hdd_ioctl() - ioctl handler for wlan network interfaces
6244 * @dev: device upon which the ioctl was received
6245 * @ifr: ioctl request information
6246 * @cmd: ioctl command
6247 *
6248 * This function does initial processing of wlan device ioctls.
6249 * Currently two flavors of ioctls are supported. The primary ioctl
6250 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used
6251 * for Android "DRIVER" commands. The other ioctl that is
6252 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used
6253 * for FTM on some platforms. This function simply verifies that the
6254 * driver is in a sane state, and that the ioctl is one of the
6255 * supported flavors, in which case flavor-specific handlers are
6256 * dispatched.
6257 *
6258 * Return: 0 on success, non-zero on error
6259 */
6260static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
6261{
6262 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
6263 hdd_context_t *hdd_ctx;
6264 int ret;
6265
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05306266 ENTER();
6267
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006268 if (dev != adapter->dev) {
6269 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
6270 "%s: HDD adapter/dev inconsistency", __func__);
6271 ret = -ENODEV;
6272 goto exit;
6273 }
6274
6275 if ((!ifr) || (!ifr->ifr_data)) {
6276 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
6277 "%s: invalid data", __func__);
6278 ret = -EINVAL;
6279 goto exit;
6280 }
6281#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
6282 if (CDF_FTM_MODE == hdd_get_conparam()) {
6283 if (SIOCIOCTLTX99 == cmd) {
6284 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr);
6285 goto exit;
6286 }
6287 }
6288#endif
6289
6290 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
6291 ret = wlan_hdd_validate_context(hdd_ctx);
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05306292 if (ret)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006293 goto exit;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006294
6295 switch (cmd) {
6296 case (SIOCDEVPRIVATE + 1):
6297 if (is_compat_task())
6298 ret = hdd_driver_compat_ioctl(adapter, ifr);
6299 else
6300 ret = hdd_driver_ioctl(adapter, ifr);
6301 break;
6302 default:
6303 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
6304 __func__, cmd);
6305 ret = -EINVAL;
6306 break;
6307 }
6308exit:
Hanumantha Reddy Pothula2db50ed2015-11-23 10:48:33 +05306309 EXIT();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08006310 return ret;
6311}
6312
6313/**
6314 * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces
6315 * @dev: device upon which the ioctl was received
6316 * @ifr: ioctl request information
6317 * @cmd: ioctl command
6318 *
6319 * This function acts as an SSR-protecting wrapper to __hdd_ioctl()
6320 * which is where the ioctls are really handled.
6321 *
6322 * Return: 0 on success, non-zero on error
6323 */
6324int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
6325{
6326 int ret;
6327
6328 cds_ssr_protect(__func__);
6329 ret = __hdd_ioctl(dev, ifr, cmd);
6330 cds_ssr_unprotect(__func__);
6331 return ret;
6332}