blob: 43c213ffa348ee8446eaa46001446fa9fdff2841 [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
1605 cdf_ret_status = sme_configure_ext_wo_w(hHal, &params,
1606 &wlan_hdd_ready_to_extwow,
1607 hdd_ctx);
1608 if (CDF_STATUS_SUCCESS != cdf_ret_status) {
1609 hddLog(CDF_TRACE_LEVEL_ERROR,
1610 FL("sme_configure_ext_wo_w returned failure %d"),
1611 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
1797 ret = sscanf(arg, "%17s %16s %x %x %x %u %u %u %u %u %u %u %u %u %u",
1798 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,
1803 (unsigned int *)&params.tcp_src_port,
1804 (unsigned int *)&params.tcp_dst_port,
1805 (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;
2447 memcpy(rateUpdate.bssid, pAdapter->macAddressCurrent.bytes,
2448 sizeof(rateUpdate.bssid));
2449 hddLog(LOG1,
2450 FL("MC Target rate %d, mac = %pM, dev_mode %s(%d)"),
2451 rateUpdate.mcastDataRate24GHz, rateUpdate.bssid,
2452 hdd_device_mode_to_string(pAdapter->device_mode),
2453 pAdapter->device_mode);
2454 status = sme_send_rate_update_ind(pHddCtx->hHal, &rateUpdate);
2455 if (CDF_STATUS_SUCCESS != status) {
2456 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: SETMCRATE failed",
2457 __func__);
2458 return -EFAULT;
2459 }
2460 return 0;
2461}
2462
2463static int drv_cmd_p2p_dev_addr(hdd_adapter_t *adapter,
2464 hdd_context_t *hdd_ctx,
2465 uint8_t *command,
2466 uint8_t command_len,
2467 hdd_priv_data_t *priv_data)
2468{
2469 int ret = 0;
2470
2471 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2472 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
2473 adapter->sessionId,
2474 (unsigned)(*(hdd_ctx->p2pDeviceAddress.bytes + 2)
2475 << 24 | *(hdd_ctx->p2pDeviceAddress.bytes
2476 + 3) << 16 | *(hdd_ctx->
2477 p2pDeviceAddress.bytes + 4) << 8 |
2478 *(hdd_ctx->p2pDeviceAddress.bytes +
2479 5))));
2480
2481 if (copy_to_user(priv_data->buf, hdd_ctx->p2pDeviceAddress.bytes,
2482 sizeof(tSirMacAddr))) {
2483 hddLog(CDF_TRACE_LEVEL_ERROR,
2484 "%s: failed to copy data to user buffer",
2485 __func__);
2486 ret = -EFAULT;
2487 }
2488
2489 return ret;
2490}
2491
2492/**
2493 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command
2494 * @adapter: Adapter on which the command was received
2495 * @hdd_ctx: HDD global context
2496 * @command: Entire driver command received from userspace
2497 * @command_len: Length of @command
2498 * @priv_data: Pointer to ioctl private data structure
2499 *
2500 * This is a trivial command hander function which simply forwards the
2501 * command to the actual command processor within the P2P module.
2502 *
2503 * Return: 0 on success, non-zero on failure
2504 */
2505static int drv_cmd_p2p_set_noa(hdd_adapter_t *adapter,
2506 hdd_context_t *hdd_ctx,
2507 uint8_t *command,
2508 uint8_t command_len,
2509 hdd_priv_data_t *priv_data)
2510{
2511 return hdd_set_p2p_noa(adapter->dev, command);
2512}
2513
2514/**
2515 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command
2516 * @adapter: Adapter on which the command was received
2517 * @hdd_ctx: HDD global context
2518 * @command: Entire driver command received from userspace
2519 * @command_len: Length of @command
2520 * @priv_data: Pointer to ioctl private data structure
2521 *
2522 * This is a trivial command hander function which simply forwards the
2523 * command to the actual command processor within the P2P module.
2524 *
2525 * Return: 0 on success, non-zero on failure
2526 */
2527static int drv_cmd_p2p_set_ps(hdd_adapter_t *adapter,
2528 hdd_context_t *hdd_ctx,
2529 uint8_t *command,
2530 uint8_t command_len,
2531 hdd_priv_data_t *priv_data)
2532{
2533 return hdd_set_p2p_opps(adapter->dev, command);
2534}
2535
2536static int drv_cmd_set_band(hdd_adapter_t *adapter,
2537 hdd_context_t *hdd_ctx,
2538 uint8_t *command,
2539 uint8_t command_len,
2540 hdd_priv_data_t *priv_data)
2541{
2542 int ret = 0;
2543
2544 uint8_t *ptr = command;
2545
2546 /* Change band request received */
2547
2548 /*
2549 * First 8 bytes will have "SETBAND " and
2550 * 9 byte will have band setting value
2551 */
2552 hddLog(CDF_TRACE_LEVEL_INFO,
2553 "%s: SetBandCommand Info comm %s UL %d, TL %d",
2554 __func__, command, priv_data->used_len,
2555 priv_data->total_len);
2556
2557 /* Change band request received */
2558 ret = hdd_set_band_helper(adapter->dev, ptr);
2559
2560 return ret;
2561}
2562
2563static int drv_cmd_set_wmmps(hdd_adapter_t *adapter,
2564 hdd_context_t *hdd_ctx,
2565 uint8_t *command,
2566 uint8_t command_len,
2567 hdd_priv_data_t *priv_data)
2568{
2569 return hdd_wmmps_helper(adapter, command);
2570}
2571
2572static int drv_cmd_country(hdd_adapter_t *adapter,
2573 hdd_context_t *hdd_ctx,
2574 uint8_t *command,
2575 uint8_t command_len,
2576 hdd_priv_data_t *priv_data)
2577{
2578 int ret = 0;
2579 CDF_STATUS status;
2580 unsigned long rc;
2581 char *country_code;
2582
2583 country_code = command + 8;
2584
2585 INIT_COMPLETION(adapter->change_country_code);
2586
2587 status = sme_change_country_code(hdd_ctx->hHal,
2588 wlan_hdd_change_country_code_callback,
2589 country_code,
2590 adapter,
2591 hdd_ctx->pcds_context,
2592 eSIR_TRUE,
2593 eSIR_TRUE);
2594 if (status == CDF_STATUS_SUCCESS) {
2595 rc = wait_for_completion_timeout(
2596 &adapter->change_country_code,
2597 msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
2598 if (!rc)
2599 hddLog(CDF_TRACE_LEVEL_ERROR,
2600 "%s: SME while setting country code timed out",
2601 __func__);
2602 } else {
2603 hddLog(CDF_TRACE_LEVEL_ERROR,
2604 "%s: SME Change Country code fail, status=%d",
2605 __func__, status);
2606 ret = -EINVAL;
2607 }
2608
2609 return ret;
2610}
2611
2612static int drv_cmd_set_roam_trigger(hdd_adapter_t *adapter,
2613 hdd_context_t *hdd_ctx,
2614 uint8_t *command,
2615 uint8_t command_len,
2616 hdd_priv_data_t *priv_data)
2617{
2618 int ret = 0;
2619 uint8_t *value = command;
2620 int8_t rssi = 0;
2621 uint8_t lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
2622 CDF_STATUS status = CDF_STATUS_SUCCESS;
2623
2624 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
2625 value = value + command_len + 1;
2626
2627 /* Convert the value from ascii to integer */
2628 ret = kstrtos8(value, 10, &rssi);
2629 if (ret < 0) {
2630 /*
2631 * If the input value is greater than max value of datatype,
2632 * then also kstrtou8 fails
2633 */
2634 CDF_TRACE(CDF_MODULE_ID_HDD,
2635 CDF_TRACE_LEVEL_ERROR,
2636 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2637 __func__,
2638 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
2639 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
2640 ret = -EINVAL;
2641 goto exit;
2642 }
2643
2644 lookUpThreshold = abs(rssi);
2645
2646 if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN)
2647 || (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) {
2648 CDF_TRACE(CDF_MODULE_ID_HDD,
2649 CDF_TRACE_LEVEL_ERROR,
2650 "Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)",
2651 lookUpThreshold,
2652 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
2653 CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
2654 ret = -EINVAL;
2655 goto exit;
2656 }
2657
2658 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2659 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
2660 adapter->sessionId, lookUpThreshold));
2661 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2662 "%s: Received Command to Set Roam trigger (Neighbor lookup threshold) = %d",
2663 __func__,
2664 lookUpThreshold);
2665
2666 hdd_ctx->config->nNeighborLookupRssiThreshold = lookUpThreshold;
2667 status = sme_set_neighbor_lookup_rssi_threshold(hdd_ctx->hHal,
2668 adapter->sessionId,
2669 lookUpThreshold);
2670 if (CDF_STATUS_SUCCESS != status) {
2671 CDF_TRACE(CDF_MODULE_ID_HDD,
2672 CDF_TRACE_LEVEL_ERROR,
2673 "%s: Failed to set roam trigger, try again",
2674 __func__);
2675 ret = -EPERM;
2676 goto exit;
2677 }
2678
2679exit:
2680 return ret;
2681}
2682
2683static int drv_cmd_get_roam_trigger(hdd_adapter_t *adapter,
2684 hdd_context_t *hdd_ctx,
2685 uint8_t *command,
2686 uint8_t command_len,
2687 hdd_priv_data_t *priv_data)
2688{
2689 int ret = 0;
2690 uint8_t lookUpThreshold =
2691 sme_get_neighbor_lookup_rssi_threshold(hdd_ctx->hHal);
2692 int rssi = (-1) * lookUpThreshold;
2693 char extra[32];
2694 uint8_t len = 0;
2695
2696 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2697 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
2698 adapter->sessionId, lookUpThreshold));
2699
2700 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
2701 len = CDF_MIN(priv_data->total_len, len + 1);
2702 if (copy_to_user(priv_data->buf, &extra, len)) {
2703 CDF_TRACE(CDF_MODULE_ID_HDD,
2704 CDF_TRACE_LEVEL_ERROR,
2705 "%s: failed to copy data to user buffer",
2706 __func__);
2707 ret = -EFAULT;
2708 }
2709
2710 return ret;
2711}
2712
2713static int drv_cmd_set_roam_scan_period(hdd_adapter_t *adapter,
2714 hdd_context_t *hdd_ctx,
2715 uint8_t *command,
2716 uint8_t command_len,
2717 hdd_priv_data_t *priv_data)
2718{
2719 int ret = 0;
2720 uint8_t *value = command;
2721 uint8_t roamScanPeriod = 0;
2722 uint16_t neighborEmptyScanRefreshPeriod =
2723 CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
2724
2725 /* input refresh period is in terms of seconds */
2726
2727 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
2728 value = value + command_len + 1;
2729
2730 /* Convert the value from ascii to integer */
2731 ret = kstrtou8(value, 10, &roamScanPeriod);
2732 if (ret < 0) {
2733 /*
2734 * If the input value is greater than max value of datatype,
2735 * then also kstrtou8 fails
2736 */
2737 CDF_TRACE(CDF_MODULE_ID_HDD,
2738 CDF_TRACE_LEVEL_ERROR,
2739 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2740 __func__,
2741 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
2742 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
2743 ret = -EINVAL;
2744 goto exit;
2745 }
2746
2747 if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000))
2748 || (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000))) {
2749 CDF_TRACE(CDF_MODULE_ID_HDD,
2750 CDF_TRACE_LEVEL_ERROR,
2751 "Roam scan period value %d is out of range (Min: %d Max: %d)",
2752 roamScanPeriod,
2753 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN / 1000),
2754 (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX / 1000));
2755 ret = -EINVAL;
2756 goto exit;
2757 }
2758 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2759 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
2760 adapter->sessionId, roamScanPeriod));
2761 neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
2762
2763 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2764 "%s: Received Command to Set roam scan period (Empty Scan refresh period) = %d",
2765 __func__,
2766 roamScanPeriod);
2767
2768 hdd_ctx->config->nEmptyScanRefreshPeriod =
2769 neighborEmptyScanRefreshPeriod;
2770 sme_update_empty_scan_refresh_period(hdd_ctx->hHal,
2771 adapter->sessionId,
2772 neighborEmptyScanRefreshPeriod);
2773
2774exit:
2775 return ret;
2776}
2777
2778static int drv_cmd_get_roam_scan_period(hdd_adapter_t *adapter,
2779 hdd_context_t *hdd_ctx,
2780 uint8_t *command,
2781 uint8_t command_len,
2782 hdd_priv_data_t *priv_data)
2783{
2784 int ret = 0;
2785 uint16_t nEmptyScanRefreshPeriod =
2786 sme_get_empty_scan_refresh_period(hdd_ctx->hHal);
2787 char extra[32];
2788 uint8_t len = 0;
2789
2790 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
2791 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
2792 adapter->sessionId,
2793 nEmptyScanRefreshPeriod));
2794 len = scnprintf(extra, sizeof(extra), "%s %d",
2795 "GETROAMSCANPERIOD",
2796 (nEmptyScanRefreshPeriod / 1000));
2797 /* Returned value is in units of seconds */
2798 len = CDF_MIN(priv_data->total_len, len + 1);
2799 if (copy_to_user(priv_data->buf, &extra, len)) {
2800 hddLog(LOGE,
2801 FL("failed to copy data to user buffer"));
2802 ret = -EFAULT;
2803 }
2804
2805 return ret;
2806}
2807
2808static int drv_cmd_set_roam_scan_refresh_period(hdd_adapter_t *adapter,
2809 hdd_context_t *hdd_ctx,
2810 uint8_t *command,
2811 uint8_t command_len,
2812 hdd_priv_data_t *priv_data)
2813{
2814 int ret = 0;
2815 uint8_t *value = command;
2816 uint8_t roamScanRefreshPeriod = 0;
2817 uint16_t neighborScanRefreshPeriod =
2818 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
2819
2820 /* input refresh period is in terms of seconds */
2821 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
2822 value = value + command_len + 1;
2823
2824 /* Convert the value from ascii to integer */
2825 ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
2826 if (ret < 0) {
2827 /*
2828 * If the input value is greater than max value of datatype,
2829 * then also kstrtou8 fails
2830 */
2831 CDF_TRACE(CDF_MODULE_ID_HDD,
2832 CDF_TRACE_LEVEL_ERROR,
2833 "%s: kstrtou8 failed Input value may be out of range[%d - %d]",
2834 __func__,
2835 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000,
2836 CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000);
2837 ret = -EINVAL;
2838 goto exit;
2839 }
2840
2841 if ((roamScanRefreshPeriod <
2842 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN / 1000))
2843 || (roamScanRefreshPeriod >
2844 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX / 1000))) {
2845 CDF_TRACE(CDF_MODULE_ID_HDD,
2846 CDF_TRACE_LEVEL_ERROR,
2847 "Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)",
2848 roamScanRefreshPeriod,
2849 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN
2850 / 1000),
2851 (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX
2852 / 1000));
2853 ret = -EINVAL;
2854 goto exit;
2855 }
2856 neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
2857
2858 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
2859 "%s: Received Command to Set roam scan refresh period (Scan refresh period) = %d",
2860 __func__,
2861 roamScanRefreshPeriod);
2862
2863 hdd_ctx->config->nNeighborResultsRefreshPeriod =
2864 neighborScanRefreshPeriod;
2865 sme_set_neighbor_scan_refresh_period(hdd_ctx->hHal,
2866 adapter->sessionId,
2867 neighborScanRefreshPeriod);
2868
2869exit:
2870 return ret;
2871}
2872
2873static int drv_cmd_get_roam_scan_refresh_period(hdd_adapter_t *adapter,
2874 hdd_context_t *hdd_ctx,
2875 uint8_t *command,
2876 uint8_t command_len,
2877 hdd_priv_data_t *priv_data)
2878{
2879 int ret = 0;
2880 uint16_t value =
2881 sme_get_neighbor_scan_refresh_period(hdd_ctx->hHal);
2882 char extra[32];
2883 uint8_t len = 0;
2884
2885 len = scnprintf(extra, sizeof(extra), "%s %d",
2886 "GETROAMSCANREFRESHPERIOD",
2887 (value / 1000));
2888 /* Returned value is in units of seconds */
2889 len = CDF_MIN(priv_data->total_len, len + 1);
2890 if (copy_to_user(priv_data->buf, &extra, len)) {
2891 CDF_TRACE(CDF_MODULE_ID_HDD,
2892 CDF_TRACE_LEVEL_ERROR,
2893 "%s: failed to copy data to user buffer",
2894 __func__);
2895 ret = -EFAULT;
2896 }
2897
2898 return ret;
2899}
2900
2901static int drv_cmd_set_roam_mode(hdd_adapter_t *adapter,
2902 hdd_context_t *hdd_ctx,
2903 uint8_t *command,
2904 uint8_t command_len,
2905 hdd_priv_data_t *priv_data)
2906{
2907 int ret = 0;
2908 uint8_t *value = command;
2909 uint8_t roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
2910
2911 /* Move pointer to ahead of SETROAMMODE<delimiter> */
2912 value = value + SIZE_OF_SETROAMMODE + 1;
2913
2914 /* Convert the value from ascii to integer */
2915 ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
2916 if (ret < 0) {
2917 /*
2918 * If the input value is greater than max value of datatype,
2919 * then also kstrtou8 fails
2920 */
2921 CDF_TRACE(CDF_MODULE_ID_HDD,
2922 CDF_TRACE_LEVEL_ERROR,
2923 "%s: kstrtou8 failed range [%d - %d]",
2924 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
2925 CFG_LFR_FEATURE_ENABLED_MAX);
2926 ret = -EINVAL;
2927 goto exit;
2928 }
2929 if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
2930 (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
2931 CDF_TRACE(CDF_MODULE_ID_HDD,
2932 CDF_TRACE_LEVEL_ERROR,
2933 "Roam Mode value %d is out of range (Min: %d Max: %d)",
2934 roamMode,
2935 CFG_LFR_FEATURE_ENABLED_MIN,
2936 CFG_LFR_FEATURE_ENABLED_MAX);
2937 ret = -EINVAL;
2938 goto exit;
2939 }
2940
2941 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_DEBUG,
2942 "%s: Received Command to Set Roam Mode = %d",
2943 __func__, roamMode);
2944 /*
2945 * Note that
2946 * SETROAMMODE 0 is to enable LFR while
2947 * SETROAMMODE 1 is to disable LFR, but
2948 * notify_is_fast_roam_ini_feature_enabled 0/1 is to
2949 * enable/disable. So, we have to invert the value
2950 * to call sme_update_is_fast_roam_ini_feature_enabled.
2951 */
2952 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
2953 roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
2954 else
2955 roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
2956
2957 hdd_ctx->config->isFastRoamIniFeatureEnabled = roamMode;
2958 if (roamMode) {
2959 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
2960 sme_update_roam_scan_offload_enabled(
2961 (tHalHandle)(hdd_ctx->hHal),
2962 hdd_ctx->config->isRoamOffloadScanEnabled);
2963 sme_update_is_fast_roam_ini_feature_enabled(
2964 hdd_ctx->hHal,
2965 adapter->sessionId,
2966 roamMode);
2967 } else {
2968 sme_update_is_fast_roam_ini_feature_enabled(
2969 hdd_ctx->hHal,
2970 adapter->sessionId,
2971 roamMode);
2972 hdd_ctx->config->isRoamOffloadScanEnabled = roamMode;
2973 sme_update_roam_scan_offload_enabled(
2974 (tHalHandle)(hdd_ctx->hHal),
2975 hdd_ctx->config->isRoamOffloadScanEnabled);
2976 }
2977
2978
2979exit:
2980 return ret;
2981}
2982
2983static int drv_cmd_get_roam_mode(hdd_adapter_t *adapter,
2984 hdd_context_t *hdd_ctx,
2985 uint8_t *command,
2986 uint8_t command_len,
2987 hdd_priv_data_t *priv_data)
2988{
2989 int ret = 0;
2990 bool roamMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
2991 char extra[32];
2992 uint8_t len = 0;
2993
2994 /*
2995 * roamMode value shall be inverted because the sementics is different.
2996 */
2997 if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
2998 roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
2999 else
3000 roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
3001
3002 len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
3003 len = CDF_MIN(priv_data->total_len, len + 1);
3004 if (copy_to_user(priv_data->buf, &extra, len)) {
3005 CDF_TRACE(CDF_MODULE_ID_HDD,
3006 CDF_TRACE_LEVEL_ERROR,
3007 "%s: failed to copy data to user buffer",
3008 __func__);
3009 ret = -EFAULT;
3010 }
3011
3012 return ret;
3013}
3014
3015static int drv_cmd_set_roam_delta(hdd_adapter_t *adapter,
3016 hdd_context_t *hdd_ctx,
3017 uint8_t *command,
3018 uint8_t command_len,
3019 hdd_priv_data_t *priv_data)
3020{
3021 int ret = 0;
3022 uint8_t *value = command;
3023 uint8_t roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
3024
3025 /* Move pointer to ahead of SETROAMDELTA<delimiter> */
3026 value = value + command_len + 1;
3027
3028 /* Convert the value from ascii to integer */
3029 ret = kstrtou8(value, 10, &roamRssiDiff);
3030 if (ret < 0) {
3031 /*
3032 * If the input value is greater than max value of datatype,
3033 * then also kstrtou8 fails
3034 */
3035 CDF_TRACE(CDF_MODULE_ID_HDD,
3036 CDF_TRACE_LEVEL_ERROR,
3037 "%s: kstrtou8 failed range [%d - %d]",
3038 __func__, CFG_ROAM_RSSI_DIFF_MIN,
3039 CFG_ROAM_RSSI_DIFF_MAX);
3040 ret = -EINVAL;
3041 goto exit;
3042 }
3043
3044 if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
3045 (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) {
3046 CDF_TRACE(CDF_MODULE_ID_HDD,
3047 CDF_TRACE_LEVEL_ERROR,
3048 "Roam rssi diff value %d is out of range (Min: %d Max: %d)",
3049 roamRssiDiff,
3050 CFG_ROAM_RSSI_DIFF_MIN,
3051 CFG_ROAM_RSSI_DIFF_MAX);
3052 ret = -EINVAL;
3053 goto exit;
3054 }
3055
3056 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3057 "%s: Received Command to Set roam rssi diff = %d",
3058 __func__, roamRssiDiff);
3059
3060 hdd_ctx->config->RoamRssiDiff = roamRssiDiff;
3061 sme_update_roam_rssi_diff(hdd_ctx->hHal,
3062 adapter->sessionId,
3063 roamRssiDiff);
3064
3065exit:
3066 return ret;
3067}
3068
3069static int drv_cmd_get_roam_delta(hdd_adapter_t *adapter,
3070 hdd_context_t *hdd_ctx,
3071 uint8_t *command,
3072 uint8_t command_len,
3073 hdd_priv_data_t *priv_data)
3074{
3075 int ret = 0;
3076 uint8_t roamRssiDiff =
3077 sme_get_roam_rssi_diff(hdd_ctx->hHal);
3078 char extra[32];
3079 uint8_t len = 0;
3080
3081 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3082 TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
3083 adapter->sessionId, roamRssiDiff));
3084
3085 len = scnprintf(extra, sizeof(extra), "%s %d",
3086 command, roamRssiDiff);
3087 len = CDF_MIN(priv_data->total_len, len + 1);
3088
3089 if (copy_to_user(priv_data->buf, &extra, len)) {
3090 CDF_TRACE(CDF_MODULE_ID_HDD,
3091 CDF_TRACE_LEVEL_ERROR,
3092 "%s: failed to copy data to user buffer",
3093 __func__);
3094 ret = -EFAULT;
3095 }
3096
3097 return ret;
3098}
3099
3100static int drv_cmd_get_band(hdd_adapter_t *adapter,
3101 hdd_context_t *hdd_ctx,
3102 uint8_t *command,
3103 uint8_t command_len,
3104 hdd_priv_data_t *priv_data)
3105{
3106 int ret = 0;
3107 int band = -1;
3108 char extra[32];
3109 uint8_t len = 0;
3110
3111 hdd_get_band_helper(hdd_ctx, &band);
3112
3113 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3114 TRACE_CODE_HDD_GETBAND_IOCTL,
3115 adapter->sessionId, band));
3116
3117 len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
3118 len = CDF_MIN(priv_data->total_len, len + 1);
3119
3120 if (copy_to_user(priv_data->buf, &extra, len)) {
3121 CDF_TRACE(CDF_MODULE_ID_HDD,
3122 CDF_TRACE_LEVEL_ERROR,
3123 "%s: failed to copy data to user buffer",
3124 __func__);
3125 ret = -EFAULT;
3126 }
3127
3128 return ret;
3129}
3130
3131static int drv_cmd_set_roam_scan_channels(hdd_adapter_t *adapter,
3132 hdd_context_t *hdd_ctx,
3133 uint8_t *command,
3134 uint8_t command_len,
3135 hdd_priv_data_t *priv_data)
3136{
3137 return hdd_parse_set_roam_scan_channels(adapter, command);
3138}
3139
3140static int drv_cmd_get_roam_scan_channels(hdd_adapter_t *adapter,
3141 hdd_context_t *hdd_ctx,
3142 uint8_t *command,
3143 uint8_t command_len,
3144 hdd_priv_data_t *priv_data)
3145{
3146 int ret = 0;
3147 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
3148 uint8_t numChannels = 0;
3149 uint8_t j = 0;
3150 char extra[128] = { 0 };
3151 int len;
3152
3153 if (CDF_STATUS_SUCCESS !=
3154 sme_get_roam_scan_channel_list(hdd_ctx->hHal,
3155 ChannelList,
3156 &numChannels,
3157 adapter->sessionId)) {
3158 CDF_TRACE(CDF_MODULE_ID_HDD,
3159 CDF_TRACE_LEVEL_FATAL,
3160 "%s: failed to get roam scan channel list",
3161 __func__);
3162 ret = -EFAULT;
3163 goto exit;
3164 }
3165
3166 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3167 TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
3168 adapter->sessionId, numChannels));
3169 /*
3170 * output channel list is of the format
3171 * [Number of roam scan channels][Channel1][Channel2]...
3172 * copy the number of channels in the 0th index
3173 */
3174 len = scnprintf(extra, sizeof(extra), "%s %d", command,
3175 numChannels);
3176 for (j = 0; (j < numChannels); j++)
3177 len += scnprintf(extra + len, sizeof(extra) - len,
3178 " %d", ChannelList[j]);
3179
3180 len = CDF_MIN(priv_data->total_len, len + 1);
3181 if (copy_to_user(priv_data->buf, &extra, len)) {
3182 CDF_TRACE(CDF_MODULE_ID_HDD,
3183 CDF_TRACE_LEVEL_ERROR,
3184 "%s: failed to copy data to user buffer",
3185 __func__);
3186 ret = -EFAULT;
3187 goto exit;
3188 }
3189
3190exit:
3191 return ret;
3192}
3193
3194static int drv_cmd_get_ccx_mode(hdd_adapter_t *adapter,
3195 hdd_context_t *hdd_ctx,
3196 uint8_t *command,
3197 uint8_t command_len,
3198 hdd_priv_data_t *priv_data)
3199{
3200 int ret = 0;
3201 bool eseMode = sme_get_is_ese_feature_enabled(hdd_ctx->hHal);
3202 char extra[32];
3203 uint8_t len = 0;
3204
3205 /*
3206 * Check if the features OKC/ESE/11R are supported simultaneously,
3207 * then this operation is not permitted (return FAILURE)
3208 */
3209 if (eseMode &&
3210 hdd_is_okc_mode_enabled(hdd_ctx) &&
3211 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
3212 CDF_TRACE(CDF_MODULE_ID_HDD,
3213 CDF_TRACE_LEVEL_WARN,
3214 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3215 __func__);
3216 ret = -EPERM;
3217 goto exit;
3218 }
3219
3220 len = scnprintf(extra, sizeof(extra), "%s %d",
3221 "GETCCXMODE", eseMode);
3222 len = CDF_MIN(priv_data->total_len, len + 1);
3223 if (copy_to_user(priv_data->buf, &extra, len)) {
3224 CDF_TRACE(CDF_MODULE_ID_HDD,
3225 CDF_TRACE_LEVEL_ERROR,
3226 "%s: failed to copy data to user buffer",
3227 __func__);
3228 ret = -EFAULT;
3229 goto exit;
3230 }
3231
3232exit:
3233 return ret;
3234}
3235
3236static int drv_cmd_get_okc_mode(hdd_adapter_t *adapter,
3237 hdd_context_t *hdd_ctx,
3238 uint8_t *command,
3239 uint8_t command_len,
3240 hdd_priv_data_t *priv_data)
3241{
3242 int ret = 0;
3243 bool okcMode = hdd_is_okc_mode_enabled(hdd_ctx);
3244 char extra[32];
3245 uint8_t len = 0;
3246
3247 /*
3248 * Check if the features OKC/ESE/11R are supported simultaneously,
3249 * then this operation is not permitted (return FAILURE)
3250 */
3251 if (okcMode &&
3252 sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
3253 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
3254 CDF_TRACE(CDF_MODULE_ID_HDD,
3255 CDF_TRACE_LEVEL_WARN,
3256 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
3257 __func__);
3258 ret = -EPERM;
3259 goto exit;
3260 }
3261
3262 len = scnprintf(extra, sizeof(extra), "%s %d",
3263 "GETOKCMODE", okcMode);
3264 len = CDF_MIN(priv_data->total_len, len + 1);
3265
3266 if (copy_to_user(priv_data->buf, &extra, len)) {
3267 CDF_TRACE(CDF_MODULE_ID_HDD,
3268 CDF_TRACE_LEVEL_ERROR,
3269 "%s: failed to copy data to user buffer",
3270 __func__);
3271 ret = -EFAULT;
3272 goto exit;
3273 }
3274
3275exit:
3276 return ret;
3277}
3278
3279static int drv_cmd_get_fast_roam(hdd_adapter_t *adapter,
3280 hdd_context_t *hdd_ctx,
3281 uint8_t *command,
3282 uint8_t command_len,
3283 hdd_priv_data_t *priv_data)
3284{
3285 int ret = 0;
3286 bool lfrMode = sme_get_is_lfr_feature_enabled(hdd_ctx->hHal);
3287 char extra[32];
3288 uint8_t len = 0;
3289
3290 len = scnprintf(extra, sizeof(extra), "%s %d",
3291 "GETFASTROAM", lfrMode);
3292 len = CDF_MIN(priv_data->total_len, len + 1);
3293
3294 if (copy_to_user(priv_data->buf, &extra, len)) {
3295 CDF_TRACE(CDF_MODULE_ID_HDD,
3296 CDF_TRACE_LEVEL_ERROR,
3297 "%s: failed to copy data to user buffer",
3298 __func__);
3299 ret = -EFAULT;
3300 }
3301
3302 return ret;
3303}
3304
3305static int drv_cmd_get_fast_transition(hdd_adapter_t *adapter,
3306 hdd_context_t *hdd_ctx,
3307 uint8_t *command,
3308 uint8_t command_len,
3309 hdd_priv_data_t *priv_data)
3310{
3311 int ret = 0;
3312 bool ft = sme_get_is_ft_feature_enabled(hdd_ctx->hHal);
3313 char extra[32];
3314 uint8_t len = 0;
3315
3316 len = scnprintf(extra, sizeof(extra), "%s %d",
3317 "GETFASTTRANSITION", ft);
3318 len = CDF_MIN(priv_data->total_len, len + 1);
3319
3320 if (copy_to_user(priv_data->buf, &extra, len)) {
3321 CDF_TRACE(CDF_MODULE_ID_HDD,
3322 CDF_TRACE_LEVEL_ERROR,
3323 "%s: failed to copy data to user buffer",
3324 __func__);
3325 ret = -EFAULT;
3326 }
3327
3328 return ret;
3329}
3330
3331static int drv_cmd_set_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3332 hdd_context_t *hdd_ctx,
3333 uint8_t *command,
3334 uint8_t command_len,
3335 hdd_priv_data_t *priv_data)
3336{
3337 int ret = 0;
3338 uint8_t *value = command;
3339 uint8_t minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
3340
3341 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
3342 value = value + command_len + 1;
3343
3344 /* Convert the value from ascii to integer */
3345 ret = kstrtou8(value, 10, &minTime);
3346 if (ret < 0) {
3347 /*
3348 * If the input value is greater than max value of datatype,
3349 * then also kstrtou8 fails
3350 */
3351 CDF_TRACE(CDF_MODULE_ID_HDD,
3352 CDF_TRACE_LEVEL_ERROR,
3353 "%s: kstrtou8 failed range [%d - %d]",
3354 __func__,
3355 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3356 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3357 ret = -EINVAL;
3358 goto exit;
3359 }
3360
3361 if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
3362 (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) {
3363 CDF_TRACE(CDF_MODULE_ID_HDD,
3364 CDF_TRACE_LEVEL_ERROR,
3365 "scan min channel time value %d is out of range (Min: %d Max: %d)",
3366 minTime,
3367 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
3368 CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
3369 ret = -EINVAL;
3370 goto exit;
3371 }
3372
3373 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3374 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
3375 adapter->sessionId, minTime));
3376 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3377 "%s: Received Command to change channel min time = %d",
3378 __func__, minTime);
3379
3380 hdd_ctx->config->nNeighborScanMinChanTime = minTime;
3381 sme_set_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3382 minTime,
3383 adapter->sessionId);
3384
3385exit:
3386 return ret;
3387}
3388
3389static int drv_cmd_send_action_frame(hdd_adapter_t *adapter,
3390 hdd_context_t *hdd_ctx,
3391 uint8_t *command,
3392 uint8_t command_len,
3393 hdd_priv_data_t *priv_data)
3394{
3395 return hdd_parse_sendactionframe(adapter, command);
3396}
3397
3398static int drv_cmd_get_roam_scan_channel_min_time(hdd_adapter_t *adapter,
3399 hdd_context_t *hdd_ctx,
3400 uint8_t *command,
3401 uint8_t command_len,
3402 hdd_priv_data_t *priv_data)
3403{
3404 int ret = 0;
3405 uint16_t val = sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
3406 adapter->sessionId);
3407 char extra[32];
3408 uint8_t len = 0;
3409
3410 /* value is interms of msec */
3411 len = scnprintf(extra, sizeof(extra), "%s %d",
3412 "GETROAMSCANCHANNELMINTIME", val);
3413 len = CDF_MIN(priv_data->total_len, len + 1);
3414
3415 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
3416 TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL,
3417 adapter->sessionId, val));
3418
3419 if (copy_to_user(priv_data->buf, &extra, len)) {
3420 CDF_TRACE(CDF_MODULE_ID_HDD,
3421 CDF_TRACE_LEVEL_ERROR,
3422 "%s: failed to copy data to user buffer",
3423 __func__);
3424 ret = -EFAULT;
3425 }
3426
3427 return ret;
3428}
3429
3430static int drv_cmd_set_scan_channel_time(hdd_adapter_t *adapter,
3431 hdd_context_t *hdd_ctx,
3432 uint8_t *command,
3433 uint8_t command_len,
3434 hdd_priv_data_t *priv_data)
3435{
3436 int ret = 0;
3437 uint8_t *value = command;
3438 uint16_t maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
3439
3440 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
3441 value = value + command_len + 1;
3442
3443 /* Convert the value from ascii to integer */
3444 ret = kstrtou16(value, 10, &maxTime);
3445 if (ret < 0) {
3446 /*
3447 * If the input value is greater than max value of datatype,
3448 * then also kstrtou8 fails
3449 */
3450 CDF_TRACE(CDF_MODULE_ID_HDD,
3451 CDF_TRACE_LEVEL_ERROR,
3452 "%s: kstrtou16 failed range [%d - %d]",
3453 __func__,
3454 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3455 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3456 ret = -EINVAL;
3457 goto exit;
3458 }
3459
3460 if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
3461 (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) {
3462 CDF_TRACE(CDF_MODULE_ID_HDD,
3463 CDF_TRACE_LEVEL_ERROR,
3464 "lfr mode value %d is out of range (Min: %d Max: %d)",
3465 maxTime,
3466 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
3467 CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
3468 ret = -EINVAL;
3469 goto exit;
3470 }
3471
3472 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3473 "%s: Received Command to change channel max time = %d",
3474 __func__, maxTime);
3475
3476 hdd_ctx->config->nNeighborScanMaxChanTime = maxTime;
3477 sme_set_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3478 adapter->sessionId,
3479 maxTime);
3480
3481exit:
3482 return ret;
3483}
3484
3485static int drv_cmd_get_scan_channel_time(hdd_adapter_t *adapter,
3486 hdd_context_t *hdd_ctx,
3487 uint8_t *command,
3488 uint8_t command_len,
3489 hdd_priv_data_t *priv_data)
3490{
3491 int ret = 0;
3492 uint16_t val = sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
3493 adapter->sessionId);
3494 char extra[32];
3495 uint8_t len = 0;
3496
3497 /* value is interms of msec */
3498 len = scnprintf(extra, sizeof(extra), "%s %d",
3499 "GETSCANCHANNELTIME", val);
3500 len = CDF_MIN(priv_data->total_len, len + 1);
3501
3502 if (copy_to_user(priv_data->buf, &extra, len)) {
3503 CDF_TRACE(CDF_MODULE_ID_HDD,
3504 CDF_TRACE_LEVEL_ERROR,
3505 "%s: failed to copy data to user buffer",
3506 __func__);
3507 ret = -EFAULT;
3508 }
3509
3510 return ret;
3511}
3512
3513static int drv_cmd_set_scan_home_time(hdd_adapter_t *adapter,
3514 hdd_context_t *hdd_ctx,
3515 uint8_t *command,
3516 uint8_t command_len,
3517 hdd_priv_data_t *priv_data)
3518{
3519 int ret = 0;
3520 uint8_t *value = command;
3521 uint16_t val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
3522
3523 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
3524 value = value + command_len + 1;
3525
3526 /* Convert the value from ascii to integer */
3527 ret = kstrtou16(value, 10, &val);
3528 if (ret < 0) {
3529 /*
3530 * If the input value is greater than max value of datatype,
3531 * then also kstrtou8 fails
3532 */
3533 CDF_TRACE(CDF_MODULE_ID_HDD,
3534 CDF_TRACE_LEVEL_ERROR,
3535 "%s: kstrtou16 failed range [%d - %d]",
3536 __func__,
3537 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3538 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
3539 ret = -EINVAL;
3540 goto exit;
3541 }
3542
3543 if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
3544 (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) {
3545 CDF_TRACE(CDF_MODULE_ID_HDD,
3546 CDF_TRACE_LEVEL_ERROR,
3547 "scan home time value %d is out of range (Min: %d Max: %d)",
3548 val,
3549 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
3550 CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
3551 ret = -EINVAL;
3552 goto exit;
3553 }
3554
3555 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3556 "%s: Received Command to change scan home time = %d",
3557 __func__, val);
3558
3559 hdd_ctx->config->nNeighborScanPeriod = val;
3560 sme_set_neighbor_scan_period(hdd_ctx->hHal,
3561 adapter->sessionId, val);
3562
3563exit:
3564 return ret;
3565}
3566
3567static int drv_cmd_get_scan_home_time(hdd_adapter_t *adapter,
3568 hdd_context_t *hdd_ctx,
3569 uint8_t *command,
3570 uint8_t command_len,
3571 hdd_priv_data_t *priv_data)
3572{
3573 int ret = 0;
3574 uint16_t val = sme_get_neighbor_scan_period(hdd_ctx->hHal,
3575 adapter->
3576 sessionId);
3577 char extra[32];
3578 uint8_t len = 0;
3579
3580 /* value is interms of msec */
3581 len = scnprintf(extra, sizeof(extra), "%s %d",
3582 "GETSCANHOMETIME", val);
3583 len = CDF_MIN(priv_data->total_len, len + 1);
3584
3585 if (copy_to_user(priv_data->buf, &extra, len)) {
3586 CDF_TRACE(CDF_MODULE_ID_HDD,
3587 CDF_TRACE_LEVEL_ERROR,
3588 "%s: failed to copy data to user buffer",
3589 __func__);
3590 ret = -EFAULT;
3591 }
3592
3593 return ret;
3594}
3595
3596static int drv_cmd_set_roam_intra_band(hdd_adapter_t *adapter,
3597 hdd_context_t *hdd_ctx,
3598 uint8_t *command,
3599 uint8_t command_len,
3600 hdd_priv_data_t *priv_data)
3601{
3602 int ret = 0;
3603 uint8_t *value = command;
3604 uint8_t val = CFG_ROAM_INTRA_BAND_DEFAULT;
3605
3606 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
3607 value = value + command_len + 1;
3608
3609 /* Convert the value from ascii to integer */
3610 ret = kstrtou8(value, 10, &val);
3611 if (ret < 0) {
3612 /*
3613 * If the input value is greater than max value of datatype,
3614 * then also kstrtou8 fails
3615 */
3616 CDF_TRACE(CDF_MODULE_ID_HDD,
3617 CDF_TRACE_LEVEL_ERROR,
3618 "%s: kstrtou8 failed range [%d - %d]",
3619 __func__, CFG_ROAM_INTRA_BAND_MIN,
3620 CFG_ROAM_INTRA_BAND_MAX);
3621 ret = -EINVAL;
3622 goto exit;
3623 }
3624
3625 if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
3626 (val > CFG_ROAM_INTRA_BAND_MAX)) {
3627 CDF_TRACE(CDF_MODULE_ID_HDD,
3628 CDF_TRACE_LEVEL_ERROR,
3629 "intra band mode value %d is out of range (Min: %d Max: %d)",
3630 val,
3631 CFG_ROAM_INTRA_BAND_MIN,
3632 CFG_ROAM_INTRA_BAND_MAX);
3633 ret = -EINVAL;
3634 goto exit;
3635 }
3636 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3637 "%s: Received Command to change intra band = %d",
3638 __func__, val);
3639
3640 hdd_ctx->config->nRoamIntraBand = val;
3641 sme_set_roam_intra_band(hdd_ctx->hHal, val);
3642
3643exit:
3644 return ret;
3645}
3646
3647static int drv_cmd_get_roam_intra_band(hdd_adapter_t *adapter,
3648 hdd_context_t *hdd_ctx,
3649 uint8_t *command,
3650 uint8_t command_len,
3651 hdd_priv_data_t *priv_data)
3652{
3653 int ret = 0;
3654 uint16_t val = sme_get_roam_intra_band(hdd_ctx->hHal);
3655 char extra[32];
3656 uint8_t len = 0;
3657
3658 /* value is interms of msec */
3659 len = scnprintf(extra, sizeof(extra), "%s %d",
3660 "GETROAMINTRABAND", val);
3661 len = CDF_MIN(priv_data->total_len, len + 1);
3662 if (copy_to_user(priv_data->buf, &extra, len)) {
3663 CDF_TRACE(CDF_MODULE_ID_HDD,
3664 CDF_TRACE_LEVEL_ERROR,
3665 "%s: failed to copy data to user buffer",
3666 __func__);
3667 ret = -EFAULT;
3668 }
3669
3670 return ret;
3671}
3672
3673static int drv_cmd_set_scan_n_probes(hdd_adapter_t *adapter,
3674 hdd_context_t *hdd_ctx,
3675 uint8_t *command,
3676 uint8_t command_len,
3677 hdd_priv_data_t *priv_data)
3678{
3679 int ret = 0;
3680 uint8_t *value = command;
3681 uint8_t nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
3682
3683 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */
3684 value = value + command_len + 1;
3685
3686 /* Convert the value from ascii to integer */
3687 ret = kstrtou8(value, 10, &nProbes);
3688 if (ret < 0) {
3689 /*
3690 * If the input value is greater than max value of datatype,
3691 * then also kstrtou8 fails
3692 */
3693 CDF_TRACE(CDF_MODULE_ID_HDD,
3694 CDF_TRACE_LEVEL_ERROR,
3695 "%s: kstrtou8 failed range [%d - %d]",
3696 __func__, CFG_ROAM_SCAN_N_PROBES_MIN,
3697 CFG_ROAM_SCAN_N_PROBES_MAX);
3698 ret = -EINVAL;
3699 goto exit;
3700 }
3701
3702 if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
3703 (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) {
3704 CDF_TRACE(CDF_MODULE_ID_HDD,
3705 CDF_TRACE_LEVEL_ERROR,
3706 "NProbes value %d is out of range (Min: %d Max: %d)",
3707 nProbes,
3708 CFG_ROAM_SCAN_N_PROBES_MIN,
3709 CFG_ROAM_SCAN_N_PROBES_MAX);
3710 ret = -EINVAL;
3711 goto exit;
3712 }
3713
3714 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3715 "%s: Received Command to Set nProbes = %d",
3716 __func__, nProbes);
3717
3718 hdd_ctx->config->nProbes = nProbes;
3719 sme_update_roam_scan_n_probes(hdd_ctx->hHal,
3720 adapter->sessionId, nProbes);
3721
3722exit:
3723 return ret;
3724}
3725
3726static int drv_cmd_get_scan_n_probes(hdd_adapter_t *adapter,
3727 hdd_context_t *hdd_ctx,
3728 uint8_t *command,
3729 uint8_t command_len,
3730 hdd_priv_data_t *priv_data)
3731{
3732 int ret = 0;
3733 uint8_t val = sme_get_roam_scan_n_probes(hdd_ctx->hHal);
3734 char extra[32];
3735 uint8_t len = 0;
3736
3737 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3738 len = CDF_MIN(priv_data->total_len, len + 1);
3739 if (copy_to_user(priv_data->buf, &extra, len)) {
3740 CDF_TRACE(CDF_MODULE_ID_HDD,
3741 CDF_TRACE_LEVEL_ERROR,
3742 "%s: failed to copy data to user buffer",
3743 __func__);
3744 ret = -EFAULT;
3745 }
3746
3747 return ret;
3748}
3749
3750static int drv_cmd_set_scan_home_away_time(hdd_adapter_t *adapter,
3751 hdd_context_t *hdd_ctx,
3752 uint8_t *command,
3753 uint8_t command_len,
3754 hdd_priv_data_t *priv_data)
3755{
3756 int ret = 0;
3757 uint8_t *value = command;
3758 uint16_t homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
3759
3760 /* input value is in units of msec */
3761
3762 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
3763 value = value + command_len + 1;
3764
3765 /* Convert the value from ascii to integer */
3766 ret = kstrtou16(value, 10, &homeAwayTime);
3767 if (ret < 0) {
3768 /*
3769 * If the input value is greater than max value of datatype,
3770 * then also kstrtou8 fails
3771 */
3772 CDF_TRACE(CDF_MODULE_ID_HDD,
3773 CDF_TRACE_LEVEL_ERROR,
3774 "%s: kstrtou8 failed range [%d - %d]",
3775 __func__,
3776 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
3777 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
3778 ret = -EINVAL;
3779 goto exit;
3780 }
3781
3782 if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
3783 (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) {
3784 CDF_TRACE(CDF_MODULE_ID_HDD,
3785 CDF_TRACE_LEVEL_ERROR,
3786 "homeAwayTime value %d is out of range (Min: %d Max: %d)",
3787 homeAwayTime,
3788 CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
3789 CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
3790 ret = -EINVAL;
3791 goto exit;
3792 }
3793
3794 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3795 "%s: Received Command to Set scan away time = %d",
3796 __func__, homeAwayTime);
3797
3798 if (hdd_ctx->config->nRoamScanHomeAwayTime !=
3799 homeAwayTime) {
3800 hdd_ctx->config->nRoamScanHomeAwayTime = homeAwayTime;
3801 sme_update_roam_scan_home_away_time(hdd_ctx->hHal,
3802 adapter->sessionId,
3803 homeAwayTime,
3804 true);
3805 }
3806
3807exit:
3808 return ret;
3809}
3810
3811static int drv_cmd_get_scan_home_away_time(hdd_adapter_t *adapter,
3812 hdd_context_t *hdd_ctx,
3813 uint8_t *command,
3814 uint8_t command_len,
3815 hdd_priv_data_t *priv_data)
3816{
3817 int ret = 0;
3818 uint16_t val = sme_get_roam_scan_home_away_time(hdd_ctx->hHal);
3819 char extra[32];
3820 uint8_t len = 0;
3821
3822 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3823 len = CDF_MIN(priv_data->total_len, len + 1);
3824
3825 if (copy_to_user(priv_data->buf, &extra, len)) {
3826 CDF_TRACE(CDF_MODULE_ID_HDD,
3827 CDF_TRACE_LEVEL_ERROR,
3828 "%s: failed to copy data to user buffer",
3829 __func__);
3830 ret = -EFAULT;
3831 }
3832
3833 return ret;
3834}
3835
3836static int drv_cmd_reassoc(hdd_adapter_t *adapter,
3837 hdd_context_t *hdd_ctx,
3838 uint8_t *command,
3839 uint8_t command_len,
3840 hdd_priv_data_t *priv_data)
3841{
3842 return hdd_parse_reassoc(adapter, command);
3843}
3844
3845static int drv_cmd_set_wes_mode(hdd_adapter_t *adapter,
3846 hdd_context_t *hdd_ctx,
3847 uint8_t *command,
3848 uint8_t command_len,
3849 hdd_priv_data_t *priv_data)
3850{
3851 int ret = 0;
3852 uint8_t *value = command;
3853 uint8_t wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
3854
3855 /* Move pointer to ahead of SETWESMODE<delimiter> */
3856 value = value + command_len + 1;
3857
3858 /* Convert the value from ascii to integer */
3859 ret = kstrtou8(value, 10, &wesMode);
3860 if (ret < 0) {
3861 /*
3862 * If the input value is greater than max value of datatype,
3863 * then also kstrtou8 fails
3864 */
3865 CDF_TRACE(CDF_MODULE_ID_HDD,
3866 CDF_TRACE_LEVEL_ERROR,
3867 "%s: kstrtou8 failed range [%d - %d]",
3868 __func__,
3869 CFG_ENABLE_WES_MODE_NAME_MIN,
3870 CFG_ENABLE_WES_MODE_NAME_MAX);
3871 ret = -EINVAL;
3872 goto exit;
3873 }
3874
3875 if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
3876 (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) {
3877 CDF_TRACE(CDF_MODULE_ID_HDD,
3878 CDF_TRACE_LEVEL_ERROR,
3879 "WES Mode value %d is out of range (Min: %d Max: %d)",
3880 wesMode,
3881 CFG_ENABLE_WES_MODE_NAME_MIN,
3882 CFG_ENABLE_WES_MODE_NAME_MAX);
3883 ret = -EINVAL;
3884 goto exit;
3885 }
3886
3887 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3888 "%s: Received Command to Set WES Mode rssi diff = %d",
3889 __func__, wesMode);
3890
3891 hdd_ctx->config->isWESModeEnabled = wesMode;
3892 sme_update_wes_mode(hdd_ctx->hHal, wesMode, adapter->sessionId);
3893
3894exit:
3895 return ret;
3896}
3897
3898static int drv_cmd_get_wes_mode(hdd_adapter_t *adapter,
3899 hdd_context_t *hdd_ctx,
3900 uint8_t *command,
3901 uint8_t command_len,
3902 hdd_priv_data_t *priv_data)
3903{
3904 int ret = 0;
3905 bool wesMode = sme_get_wes_mode(hdd_ctx->hHal);
3906 char extra[32];
3907 uint8_t len = 0;
3908
3909 len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
3910 len = CDF_MIN(priv_data->total_len, len + 1);
3911 if (copy_to_user(priv_data->buf, &extra, len)) {
3912 CDF_TRACE(CDF_MODULE_ID_HDD,
3913 CDF_TRACE_LEVEL_ERROR,
3914 "%s: failed to copy data to user buffer",
3915 __func__);
3916 ret = -EFAULT;
3917 }
3918
3919 return ret;
3920}
3921
3922static int drv_cmd_set_opportunistic_rssi_diff(hdd_adapter_t *adapter,
3923 hdd_context_t *hdd_ctx,
3924 uint8_t *command,
3925 uint8_t command_len,
3926 hdd_priv_data_t *priv_data)
3927{
3928 int ret = 0;
3929 uint8_t *value = command;
3930 uint8_t nOpportunisticThresholdDiff =
3931 CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
3932
3933 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
3934 value = value + command_len + 1;
3935
3936 /* Convert the value from ascii to integer */
3937 ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
3938 if (ret < 0) {
3939 /*
3940 * If the input value is greater than max value of datatype,
3941 * then also kstrtou8 fails
3942 */
3943 CDF_TRACE(CDF_MODULE_ID_HDD,
3944 CDF_TRACE_LEVEL_ERROR,
3945 "%s: kstrtou8 failed.", __func__);
3946 ret = -EINVAL;
3947 goto exit;
3948 }
3949
3950 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
3951 "%s: Received Command to Set Opportunistic Threshold diff = %d",
3952 __func__, nOpportunisticThresholdDiff);
3953
3954 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->hHal,
3955 adapter->sessionId,
3956 nOpportunisticThresholdDiff);
3957
3958exit:
3959 return ret;
3960}
3961
3962static int drv_cmd_get_opportunistic_rssi_diff(hdd_adapter_t *adapter,
3963 hdd_context_t *hdd_ctx,
3964 uint8_t *command,
3965 uint8_t command_len,
3966 hdd_priv_data_t *priv_data)
3967{
3968 int ret = 0;
3969 int8_t val = sme_get_roam_opportunistic_scan_threshold_diff(
3970 hdd_ctx->hHal);
3971 char extra[32];
3972 uint8_t len = 0;
3973
3974 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
3975 len = CDF_MIN(priv_data->total_len, len + 1);
3976 if (copy_to_user(priv_data->buf, &extra, len)) {
3977 CDF_TRACE(CDF_MODULE_ID_HDD,
3978 CDF_TRACE_LEVEL_ERROR,
3979 "%s: failed to copy data to user buffer",
3980 __func__);
3981 ret = -EFAULT;
3982 }
3983
3984 return ret;
3985}
3986
3987static int drv_cmd_set_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
3988 hdd_context_t *hdd_ctx,
3989 uint8_t *command,
3990 uint8_t command_len,
3991 hdd_priv_data_t *priv_data)
3992{
3993 int ret = 0;
3994 uint8_t *value = command;
3995 uint8_t nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
3996
3997 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
3998 value = value + command_len + 1;
3999
4000 /* Convert the value from ascii to integer */
4001 ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
4002 if (ret < 0) {
4003 /*
4004 * If the input value is greater than max value of datatype,
4005 * then also kstrtou8 fails
4006 */
4007 CDF_TRACE(CDF_MODULE_ID_HDD,
4008 CDF_TRACE_LEVEL_ERROR,
4009 "%s: kstrtou8 failed.", __func__);
4010 ret = -EINVAL;
4011 goto exit;
4012 }
4013
4014 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4015 "%s: Received Command to Set Roam Rescan RSSI Diff = %d",
4016 __func__, nRoamRescanRssiDiff);
4017
4018 sme_set_roam_rescan_rssi_diff(hdd_ctx->hHal,
4019 adapter->sessionId,
4020 nRoamRescanRssiDiff);
4021
4022exit:
4023 return ret;
4024}
4025
4026static int drv_cmd_get_roam_rescan_rssi_diff(hdd_adapter_t *adapter,
4027 hdd_context_t *hdd_ctx,
4028 uint8_t *command,
4029 uint8_t command_len,
4030 hdd_priv_data_t *priv_data)
4031{
4032 int ret = 0;
4033 uint8_t val = sme_get_roam_rescan_rssi_diff(hdd_ctx->hHal);
4034 char extra[32];
4035 uint8_t len = 0;
4036
4037 len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
4038 len = CDF_MIN(priv_data->total_len, len + 1);
4039 if (copy_to_user(priv_data->buf, &extra, len)) {
4040 CDF_TRACE(CDF_MODULE_ID_HDD,
4041 CDF_TRACE_LEVEL_ERROR,
4042 "%s: failed to copy data to user buffer",
4043 __func__);
4044 ret = -EFAULT;
4045 }
4046
4047 return ret;
4048}
4049
4050static int drv_cmd_set_fast_roam(hdd_adapter_t *adapter,
4051 hdd_context_t *hdd_ctx,
4052 uint8_t *command,
4053 uint8_t command_len,
4054 hdd_priv_data_t *priv_data)
4055{
4056 int ret = 0;
4057 uint8_t *value = command;
4058 uint8_t lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
4059
4060 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4061 value = value + command_len + 1;
4062
4063 /* Convert the value from ascii to integer */
4064 ret = kstrtou8(value, 10, &lfrMode);
4065 if (ret < 0) {
4066 /*
4067 * If the input value is greater than max value of datatype,
4068 * then also kstrtou8 fails
4069 */
4070 CDF_TRACE(CDF_MODULE_ID_HDD,
4071 CDF_TRACE_LEVEL_ERROR,
4072 "%s: kstrtou8 failed range [%d - %d]",
4073 __func__, CFG_LFR_FEATURE_ENABLED_MIN,
4074 CFG_LFR_FEATURE_ENABLED_MAX);
4075 ret = -EINVAL;
4076 goto exit;
4077 }
4078
4079 if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
4080 (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) {
4081 CDF_TRACE(CDF_MODULE_ID_HDD,
4082 CDF_TRACE_LEVEL_ERROR,
4083 "lfr mode value %d is out of range (Min: %d Max: %d)",
4084 lfrMode,
4085 CFG_LFR_FEATURE_ENABLED_MIN,
4086 CFG_LFR_FEATURE_ENABLED_MAX);
4087 ret = -EINVAL;
4088 goto exit;
4089 }
4090
4091 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4092 "%s: Received Command to change lfr mode = %d",
4093 __func__, lfrMode);
4094
4095 hdd_ctx->config->isFastRoamIniFeatureEnabled = lfrMode;
4096 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->hHal,
4097 adapter->
4098 sessionId,
4099 lfrMode);
4100
4101exit:
4102 return ret;
4103}
4104
4105static int drv_cmd_set_fast_transition(hdd_adapter_t *adapter,
4106 hdd_context_t *hdd_ctx,
4107 uint8_t *command,
4108 uint8_t command_len,
4109 hdd_priv_data_t *priv_data)
4110{
4111 int ret = 0;
4112 uint8_t *value = command;
4113 uint8_t ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
4114
4115 /* Move pointer to ahead of SETFASTROAM<delimiter> */
4116 value = value + command_len + 1;
4117
4118 /* Convert the value from ascii to integer */
4119 ret = kstrtou8(value, 10, &ft);
4120 if (ret < 0) {
4121 /*
4122 * If the input value is greater than max value of datatype,
4123 * then also kstrtou8 fails
4124 */
4125 CDF_TRACE(CDF_MODULE_ID_HDD,
4126 CDF_TRACE_LEVEL_ERROR,
4127 "%s: kstrtou8 failed range [%d - %d]",
4128 __func__,
4129 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4130 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4131 ret = -EINVAL;
4132 goto exit;
4133 }
4134
4135 if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
4136 (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) {
4137 CDF_TRACE(CDF_MODULE_ID_HDD,
4138 CDF_TRACE_LEVEL_ERROR,
4139 "ft mode value %d is out of range (Min: %d Max: %d)",
4140 ft,
4141 CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
4142 CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
4143 ret = -EINVAL;
4144 goto exit;
4145 }
4146
4147 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4148 "%s: Received Command to change ft mode = %d",
4149 __func__, ft);
4150
4151 hdd_ctx->config->isFastTransitionEnabled = ft;
4152 sme_update_fast_transition_enabled(hdd_ctx->hHal, ft);
4153
4154exit:
4155 return ret;
4156}
4157
4158#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4159static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid,
4160 int channel)
4161{
4162 struct wma_roam_invoke_cmd *fastreassoc;
4163 cds_msg_t msg = {0};
4164
4165 fastreassoc = cdf_mem_malloc(sizeof(*fastreassoc));
4166 if (NULL == fastreassoc) {
4167 hddLog(LOGE, FL("cdf_mem_alloc failed for fastreassoc"));
4168 return;
4169 }
4170 fastreassoc->vdev_id = sessionId;
4171 fastreassoc->channel = channel;
4172 fastreassoc->bssid[0] = bssid[0];
4173 fastreassoc->bssid[1] = bssid[1];
4174 fastreassoc->bssid[2] = bssid[2];
4175 fastreassoc->bssid[3] = bssid[3];
4176 fastreassoc->bssid[4] = bssid[4];
4177 fastreassoc->bssid[5] = bssid[5];
4178
4179 msg.type = SIR_HAL_ROAM_INVOKE;
4180 msg.reserved = 0;
4181 msg.bodyptr = fastreassoc;
4182 if (CDF_STATUS_SUCCESS != cds_mq_post_message(CDF_MODULE_ID_WMA,
4183 &msg)) {
4184 cdf_mem_free(fastreassoc);
4185 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
4186 FL("Not able to post ROAM_INVOKE_CMD message to WMA"));
4187 }
4188}
4189#endif
4190static int drv_cmd_fast_reassoc(hdd_adapter_t *adapter,
4191 hdd_context_t *hdd_ctx,
4192 uint8_t *command,
4193 uint8_t command_len,
4194 hdd_priv_data_t *priv_data)
4195{
4196 int ret = 0;
4197 uint8_t *value = command;
4198 uint8_t channel = 0;
4199 tSirMacAddr targetApBssid;
4200 uint32_t roamId = 0;
4201 tCsrRoamModifyProfileFields modProfileFields;
4202#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4203 tCsrHandoffRequest handoffInfo;
4204#endif
4205 hdd_station_ctx_t *pHddStaCtx;
4206
4207 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
4208 hdd_warn("Unsupported in mode %s(%d)",
4209 hdd_device_mode_to_string(adapter->device_mode),
4210 adapter->device_mode);
4211 return -EINVAL;
4212 }
4213
4214 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4215
4216 /* if not associated, no need to proceed with reassoc */
4217 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
4218 CDF_TRACE(CDF_MODULE_ID_HDD,
4219 CDF_TRACE_LEVEL_INFO,
4220 "%s:Not associated!", __func__);
4221 ret = -EINVAL;
4222 goto exit;
4223 }
4224
4225 ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
4226 &channel);
4227 if (ret) {
4228 CDF_TRACE(CDF_MODULE_ID_HDD,
4229 CDF_TRACE_LEVEL_ERROR,
4230 "%s: Failed to parse reassoc command data",
4231 __func__);
4232 goto exit;
4233 }
4234
4235 /*
4236 * if the target bssid is same as currently associated AP,
4237 * issue reassoc to same AP
4238 */
4239 if (true == cdf_mem_compare(targetApBssid,
4240 pHddStaCtx->conn_info.bssId.bytes,
4241 CDF_MAC_ADDR_SIZE)) {
4242 /* Reassoc to same AP, only supported for Open Security*/
4243 if ((pHddStaCtx->conn_info.ucEncryptionType ||
4244 pHddStaCtx->conn_info.mcEncryptionType)) {
4245 hddLog(LOGE,
4246 FL("Reassoc to same AP, only supported for Open Security"));
4247 return -ENOTSUPP;
4248 }
4249 hddLog(LOG1,
4250 FL("Reassoc BSSID is same as currently associated AP bssid"));
4251 sme_get_modify_profile_fields(hdd_ctx->hHal, adapter->sessionId,
4252 &modProfileFields);
4253 sme_roam_reassoc(hdd_ctx->hHal, adapter->sessionId,
4254 NULL, modProfileFields, &roamId, 1);
4255 return 0;
4256 }
4257
4258 /* Check channel number is a valid channel number */
4259 if (CDF_STATUS_SUCCESS !=
4260 wlan_hdd_validate_operation_channel(adapter, channel)) {
4261 hddLog(LOGE, FL("Invalid Channel [%d]"), channel);
4262 return -EINVAL;
4263 }
4264#ifdef WLAN_FEATURE_ROAM_OFFLOAD
4265 if (hdd_ctx->config->isRoamOffloadEnabled) {
4266 hdd_wma_send_fastreassoc_cmd((int)adapter->sessionId,
4267 targetApBssid, (int)channel);
4268 goto exit;
4269 }
4270#endif
4271 /* Proceed with reassoc */
4272#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
4273 handoffInfo.channel = channel;
4274 handoffInfo.src = FASTREASSOC;
4275 cdf_mem_copy(handoffInfo.bssid, targetApBssid,
4276 sizeof(tSirMacAddr));
4277 sme_handoff_request(hdd_ctx->hHal, adapter->sessionId,
4278 &handoffInfo);
4279#endif
4280
4281exit:
4282 return ret;
4283}
4284
4285#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
4286static int drv_cmd_ccx_plm_req(hdd_adapter_t *adapter,
4287 hdd_context_t *hdd_ctx,
4288 uint8_t *command,
4289 uint8_t command_len,
4290 hdd_priv_data_t *priv_data)
4291{
4292 int ret = 0;
4293 uint8_t *value = command;
4294 CDF_STATUS status = CDF_STATUS_SUCCESS;
4295 tpSirPlmReq pPlmRequest = NULL;
4296
4297 pPlmRequest = cdf_mem_malloc(sizeof(tSirPlmReq));
4298 if (NULL == pPlmRequest) {
4299 ret = -ENOMEM;
4300 goto exit;
4301 }
4302
4303 status = hdd_parse_plm_cmd(value, pPlmRequest);
4304 if (CDF_STATUS_SUCCESS != status) {
4305 cdf_mem_free(pPlmRequest);
4306 pPlmRequest = NULL;
4307 ret = -EINVAL;
4308 goto exit;
4309 }
4310 pPlmRequest->sessionId = adapter->sessionId;
4311
4312 status = sme_set_plm_request(hdd_ctx->hHal, pPlmRequest);
4313 if (CDF_STATUS_SUCCESS != status) {
4314 cdf_mem_free(pPlmRequest);
4315 pPlmRequest = NULL;
4316 ret = -EINVAL;
4317 goto exit;
4318 }
4319
4320exit:
4321 return ret;
4322}
4323#endif
4324
4325#ifdef FEATURE_WLAN_ESE
4326static int drv_cmd_set_ccx_mode(hdd_adapter_t *adapter,
4327 hdd_context_t *hdd_ctx,
4328 uint8_t *command,
4329 uint8_t command_len,
4330 hdd_priv_data_t *priv_data)
4331{
4332 int ret = 0;
4333 uint8_t *value = command;
4334 uint8_t eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT;
4335
4336 /*
4337 * Check if the features OKC/ESE/11R are supported simultaneously,
4338 * then this operation is not permitted (return FAILURE)
4339 */
4340 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4341 hdd_is_okc_mode_enabled(hdd_ctx) &&
4342 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
4343 CDF_TRACE(CDF_MODULE_ID_HDD,
4344 CDF_TRACE_LEVEL_WARN,
4345 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4346 __func__);
4347 ret = -EPERM;
4348 goto exit;
4349 }
4350
4351 /* Move pointer to ahead of SETCCXMODE<delimiter> */
4352 value = value + command_len + 1;
4353
4354 /* Convert the value from ascii to integer */
4355 ret = kstrtou8(value, 10, &eseMode);
4356 if (ret < 0) {
4357 /*
4358 * If the input value is greater than max value of datatype,
4359 * then also kstrtou8 fails
4360 */
4361 CDF_TRACE(CDF_MODULE_ID_HDD,
4362 CDF_TRACE_LEVEL_ERROR,
4363 "%s: kstrtou8 failed range [%d - %d]",
4364 __func__, CFG_ESE_FEATURE_ENABLED_MIN,
4365 CFG_ESE_FEATURE_ENABLED_MAX);
4366 ret = -EINVAL;
4367 goto exit;
4368 }
4369
4370 if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) ||
4371 (eseMode > CFG_ESE_FEATURE_ENABLED_MAX)) {
4372 CDF_TRACE(CDF_MODULE_ID_HDD,
4373 CDF_TRACE_LEVEL_ERROR,
4374 "Ese mode value %d is out of range (Min: %d Max: %d)",
4375 eseMode,
4376 CFG_ESE_FEATURE_ENABLED_MIN,
4377 CFG_ESE_FEATURE_ENABLED_MAX);
4378 ret = -EINVAL;
4379 goto exit;
4380 }
4381 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4382 "%s: Received Command to change ese mode = %d",
4383 __func__, eseMode);
4384
4385 hdd_ctx->config->isEseIniFeatureEnabled = eseMode;
4386 sme_update_is_ese_feature_enabled(hdd_ctx->hHal,
4387 adapter->sessionId,
4388 eseMode);
4389
4390exit:
4391 return ret;
4392}
4393#endif
4394
4395static int drv_cmd_set_roam_scan_control(hdd_adapter_t *adapter,
4396 hdd_context_t *hdd_ctx,
4397 uint8_t *command,
4398 uint8_t command_len,
4399 hdd_priv_data_t *priv_data)
4400{
4401 int ret = 0;
4402 uint8_t *value = command;
4403 uint8_t roamScanControl = 0;
4404
4405 /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
4406 value = value + command_len + 1;
4407
4408 /* Convert the value from ascii to integer */
4409 ret = kstrtou8(value, 10, &roamScanControl);
4410 if (ret < 0) {
4411 /*
4412 * If the input value is greater than max value of datatype,
4413 * then also kstrtou8 fails
4414 */
4415 CDF_TRACE(CDF_MODULE_ID_HDD,
4416 CDF_TRACE_LEVEL_ERROR,
4417 "%s: kstrtou8 failed ", __func__);
4418 ret = -EINVAL;
4419 goto exit;
4420 }
4421
4422 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4423 "%s: Received Command to Set roam scan control = %d",
4424 __func__, roamScanControl);
4425
4426 if (0 != roamScanControl) {
4427 ret = 0; /* return success but ignore param value "true" */
4428 goto exit;
4429 }
4430
4431 sme_set_roam_scan_control(hdd_ctx->hHal,
4432 adapter->sessionId,
4433 roamScanControl);
4434
4435exit:
4436 return ret;
4437}
4438
4439static int drv_cmd_set_okc_mode(hdd_adapter_t *adapter,
4440 hdd_context_t *hdd_ctx,
4441 uint8_t *command,
4442 uint8_t command_len,
4443 hdd_priv_data_t *priv_data)
4444{
4445 int ret = 0;
4446 uint8_t *value = command;
4447 uint8_t okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
4448
4449 /*
4450 * Check if the features OKC/ESE/11R are supported simultaneously,
4451 * then this operation is not permitted (return FAILURE)
4452 */
4453 if (sme_get_is_ese_feature_enabled(hdd_ctx->hHal) &&
4454 hdd_is_okc_mode_enabled(hdd_ctx) &&
4455 sme_get_is_ft_feature_enabled(hdd_ctx->hHal)) {
4456 CDF_TRACE(CDF_MODULE_ID_HDD,
4457 CDF_TRACE_LEVEL_WARN,
4458 "%s: OKC/ESE/11R are supported simultaneously hence this operation is not permitted!",
4459 __func__);
4460 ret = -EPERM;
4461 goto exit;
4462 }
4463
4464 /* Move pointer to ahead of SETOKCMODE<delimiter> */
4465 value = value + command_len + 1;
4466
4467 /* Convert the value from ascii to integer */
4468 ret = kstrtou8(value, 10, &okcMode);
4469 if (ret < 0) {
4470 /*
4471 * If the input value is greater than max value of datatype,
4472 * then also kstrtou8 fails
4473 */
4474 CDF_TRACE(CDF_MODULE_ID_HDD,
4475 CDF_TRACE_LEVEL_ERROR,
4476 "%s: kstrtou8 failed range [%d - %d]",
4477 __func__, CFG_OKC_FEATURE_ENABLED_MIN,
4478 CFG_OKC_FEATURE_ENABLED_MAX);
4479 ret = -EINVAL;
4480 goto exit;
4481 }
4482
4483 if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
4484 (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) {
4485 CDF_TRACE(CDF_MODULE_ID_HDD,
4486 CDF_TRACE_LEVEL_ERROR,
4487 "Okc mode value %d is out of range (Min: %d Max: %d)",
4488 okcMode,
4489 CFG_OKC_FEATURE_ENABLED_MIN,
4490 CFG_OKC_FEATURE_ENABLED_MAX);
4491 ret = -EINVAL;
4492 goto exit;
4493 }
4494 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
4495 "%s: Received Command to change okc mode = %d",
4496 __func__, okcMode);
4497
4498 hdd_ctx->config->isOkcIniFeatureEnabled = okcMode;
4499
4500exit:
4501 return ret;
4502}
4503
4504static int drv_cmd_get_roam_scan_control(hdd_adapter_t *adapter,
4505 hdd_context_t *hdd_ctx,
4506 uint8_t *command,
4507 uint8_t command_len,
4508 hdd_priv_data_t *priv_data)
4509{
4510 int ret = 0;
4511 bool roamScanControl = sme_get_roam_scan_control(hdd_ctx->hHal);
4512 char extra[32];
4513 uint8_t len = 0;
4514
4515 len = scnprintf(extra, sizeof(extra), "%s %d",
4516 command, roamScanControl);
4517 len = CDF_MIN(priv_data->total_len, len + 1);
4518 if (copy_to_user(priv_data->buf, &extra, len)) {
4519 CDF_TRACE(CDF_MODULE_ID_HDD,
4520 CDF_TRACE_LEVEL_ERROR,
4521 "%s: failed to copy data to user buffer",
4522 __func__);
4523 ret = -EFAULT;
4524 }
4525
4526 return ret;
4527}
4528
4529static int drv_cmd_bt_coex_mode(hdd_adapter_t *adapter,
4530 hdd_context_t *hdd_ctx,
4531 uint8_t *command,
4532 uint8_t command_len,
4533 hdd_priv_data_t *priv_data)
4534{
4535 int ret = 0;
4536 char *bcMode;
4537
4538 bcMode = command + 11;
4539 if ('1' == *bcMode) {
4540 CDF_TRACE(CDF_MODULE_ID_HDD,
4541 CDF_TRACE_LEVEL_DEBUG,
4542 FL("BTCOEXMODE %d"), *bcMode);
4543 hdd_ctx->btCoexModeSet = true;
4544 ret = wlan_hdd_scan_abort(adapter);
4545 if (ret < 0) {
4546 hddLog(LOGE,
4547 FL("Failed to abort existing scan status:%d"), ret);
4548 }
4549 } else if ('2' == *bcMode) {
4550 CDF_TRACE(CDF_MODULE_ID_HDD,
4551 CDF_TRACE_LEVEL_DEBUG,
4552 FL("BTCOEXMODE %d"), *bcMode);
4553 hdd_ctx->btCoexModeSet = false;
4554 }
4555
4556 return ret;
4557}
4558
4559static int drv_cmd_scan_active(hdd_adapter_t *adapter,
4560 hdd_context_t *hdd_ctx,
4561 uint8_t *command,
4562 uint8_t command_len,
4563 hdd_priv_data_t *priv_data)
4564{
4565 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
4566 return 0;
4567}
4568
4569static int drv_cmd_scan_passive(hdd_adapter_t *adapter,
4570 hdd_context_t *hdd_ctx,
4571 uint8_t *command,
4572 uint8_t command_len,
4573 hdd_priv_data_t *priv_data)
4574{
4575 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN;
4576 return 0;
4577}
4578
4579static int drv_cmd_get_dwell_time(hdd_adapter_t *adapter,
4580 hdd_context_t *hdd_ctx,
4581 uint8_t *command,
4582 uint8_t command_len,
4583 hdd_priv_data_t *priv_data)
4584{
4585 int ret = 0;
4586 struct hdd_config *pCfg =
4587 (WLAN_HDD_GET_CTX(adapter))->config;
4588 char extra[32];
4589 uint8_t len = 0;
4590
4591 memset(extra, 0, sizeof(extra));
4592 ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
4593 len = CDF_MIN(priv_data->total_len, len + 1);
4594 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) {
4595 CDF_TRACE(CDF_MODULE_ID_HDD,
4596 CDF_TRACE_LEVEL_ERROR,
4597 "%s: failed to copy data to user buffer",
4598 __func__);
4599 ret = -EFAULT;
4600 goto exit;
4601 }
4602 ret = len;
4603exit:
4604 return ret;
4605}
4606
4607static int drv_cmd_set_dwell_time(hdd_adapter_t *adapter,
4608 hdd_context_t *hdd_ctx,
4609 uint8_t *command,
4610 uint8_t command_len,
4611 hdd_priv_data_t *priv_data)
4612{
4613 return hdd_set_dwell_time(adapter, command);
4614}
4615
4616static int drv_cmd_miracast(hdd_adapter_t *adapter,
4617 hdd_context_t *hdd_ctx,
4618 uint8_t *command,
4619 uint8_t command_len,
4620 hdd_priv_data_t *priv_data)
4621{
4622 CDF_STATUS ret_status;
4623 int ret = 0;
4624 tHalHandle hHal;
4625 uint8_t filterType = 0;
4626 hdd_context_t *pHddCtx = NULL;
4627 uint8_t *value;
4628
4629 pHddCtx = WLAN_HDD_GET_CTX(adapter);
4630 if (0 != wlan_hdd_validate_context(pHddCtx)) {
4631 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
4632 "%s pHddCtx is not valid, Unable to set miracast mode",
4633 __func__);
4634 return -EINVAL;
4635 }
4636
4637 hHal = pHddCtx->hHal;
4638 value = command + 9;
4639
4640 /* Convert the value from ascii to integer */
4641 ret = kstrtou8(value, 10, &filterType);
4642 if (ret < 0) {
4643 /*
4644 * If the input value is greater than max value of datatype,
4645 * then also kstrtou8 fails
4646 */
4647 CDF_TRACE(CDF_MODULE_ID_HDD,
4648 CDF_TRACE_LEVEL_ERROR,
4649 "%s: kstrtou8 failed range ",
4650 __func__);
4651 ret = -EINVAL;
4652 goto exit;
4653 }
4654 if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL)
4655 || (filterType >
4656 WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
4657 CDF_TRACE(CDF_MODULE_ID_HDD,
4658 CDF_TRACE_LEVEL_ERROR,
4659 "%s: Accepted Values are 0 to 2. 0-Disabled, 1-Source, 2-Sink ",
4660 __func__);
4661 ret = -EINVAL;
4662 goto exit;
4663 }
4664 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
4665 pHddCtx->miracast_value = filterType;
4666
4667 ret_status = sme_set_miracast(hHal, filterType);
4668 if (CDF_STATUS_SUCCESS != ret_status) {
4669 hddLog(LOGE, "Failed to set miracast");
4670 return -EBUSY;
4671 }
4672
4673 if (cds_is_mcc_in_24G(pHddCtx))
4674 return cds_set_mas(adapter, filterType);
4675
4676exit:
4677 return ret;
4678}
4679
4680#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
4681static int drv_cmd_set_ccx_roam_scan_channels(hdd_adapter_t *adapter,
4682 hdd_context_t *hdd_ctx,
4683 uint8_t *command,
4684 uint8_t command_len,
4685 hdd_priv_data_t *priv_data)
4686{
4687 int ret = 0;
4688 uint8_t *value = command;
4689 uint8_t ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = { 0 };
4690 uint8_t numChannels = 0;
4691 CDF_STATUS status;
4692
4693 ret = hdd_parse_channellist(value, ChannelList, &numChannels);
4694 if (ret) {
4695 CDF_TRACE(CDF_MODULE_ID_HDD,
4696 CDF_TRACE_LEVEL_ERROR,
4697 "%s: Failed to parse channel list information",
4698 __func__);
4699 goto exit;
4700 }
4701 if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) {
4702 CDF_TRACE(CDF_MODULE_ID_HDD,
4703 CDF_TRACE_LEVEL_ERROR,
4704 "%s: number of channels (%d) supported exceeded max (%d)",
4705 __func__,
4706 numChannels,
4707 WNI_CFG_VALID_CHANNEL_LIST_LEN);
4708 ret = -EINVAL;
4709 goto exit;
4710 }
4711 status = sme_set_ese_roam_scan_channel_list(hdd_ctx->hHal,
4712 adapter->sessionId,
4713 ChannelList,
4714 numChannels);
4715 if (CDF_STATUS_SUCCESS != status) {
4716 CDF_TRACE(CDF_MODULE_ID_HDD,
4717 CDF_TRACE_LEVEL_ERROR,
4718 "%s: Failed to update channel list information",
4719 __func__);
4720 ret = -EINVAL;
4721 goto exit;
4722 }
4723
4724exit:
4725 return ret;
4726}
4727
4728static int drv_cmd_get_tsm_stats(hdd_adapter_t *adapter,
4729 hdd_context_t *hdd_ctx,
4730 uint8_t *command,
4731 uint8_t command_len,
4732 hdd_priv_data_t *priv_data)
4733{
4734 int ret = 0;
4735 uint8_t *value = command;
4736 char extra[128] = { 0 };
4737 int len = 0;
4738 uint8_t tid = 0;
4739 hdd_station_ctx_t *pHddStaCtx;
4740 tAniTrafStrmMetrics tsm_metrics;
4741
4742 if ((WLAN_HDD_INFRA_STATION != adapter->device_mode) &&
4743 (WLAN_HDD_P2P_CLIENT != adapter->device_mode)) {
4744 hdd_warn("Unsupported in mode %s(%d)",
4745 hdd_device_mode_to_string(adapter->device_mode),
4746 adapter->device_mode);
4747 return -EINVAL;
4748 }
4749
4750 pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
4751
4752 /* if not associated, return error */
4753 if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
4754 CDF_TRACE(CDF_MODULE_ID_HDD,
4755 CDF_TRACE_LEVEL_ERROR,
4756 "%s:Not associated!", __func__);
4757 ret = -EINVAL;
4758 goto exit;
4759 }
4760
4761 /* Move pointer to ahead of GETTSMSTATS<delimiter> */
4762 value = value + command_len + 1;
4763
4764 /* Convert the value from ascii to integer */
4765 ret = kstrtou8(value, 10, &tid);
4766 if (ret < 0) {
4767 /*
4768 * If the input value is greater than max value of datatype,
4769 * then also kstrtou8 fails
4770 */
4771 CDF_TRACE(CDF_MODULE_ID_HDD,
4772 CDF_TRACE_LEVEL_ERROR,
4773 "%s: kstrtou8 failed range [%d - %d]",
4774 __func__, TID_MIN_VALUE,
4775 TID_MAX_VALUE);
4776 ret = -EINVAL;
4777 goto exit;
4778 }
4779 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) {
4780 CDF_TRACE(CDF_MODULE_ID_HDD,
4781 CDF_TRACE_LEVEL_ERROR,
4782 "tid value %d is out of range (Min: %d Max: %d)",
4783 tid, TID_MIN_VALUE, TID_MAX_VALUE);
4784 ret = -EINVAL;
4785 goto exit;
4786 }
4787 CDF_TRACE(CDF_MODULE_ID_HDD,
4788 CDF_TRACE_LEVEL_INFO,
4789 "%s: Received Command to get tsm stats tid = %d",
4790 __func__, tid);
4791 if (CDF_STATUS_SUCCESS !=
4792 hdd_get_tsm_stats(adapter, tid, &tsm_metrics)) {
4793 CDF_TRACE(CDF_MODULE_ID_HDD,
4794 CDF_TRACE_LEVEL_ERROR,
4795 "%s: failed to get tsm stats",
4796 __func__);
4797 ret = -EFAULT;
4798 goto exit;
4799 }
4800 CDF_TRACE(CDF_MODULE_ID_HDD,
4801 CDF_TRACE_LEVEL_INFO,
4802 "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)",
4803 tsm_metrics.UplinkPktQueueDly,
4804 tsm_metrics.UplinkPktQueueDlyHist[0],
4805 tsm_metrics.UplinkPktQueueDlyHist[1],
4806 tsm_metrics.UplinkPktQueueDlyHist[2],
4807 tsm_metrics.UplinkPktQueueDlyHist[3],
4808 tsm_metrics.UplinkPktTxDly,
4809 tsm_metrics.UplinkPktLoss,
4810 tsm_metrics.UplinkPktCount,
4811 tsm_metrics.RoamingCount,
4812 tsm_metrics.RoamingDly);
4813 /*
4814 * Output TSM stats is of the format
4815 * GETTSMSTATS [PktQueueDly]
4816 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
4817 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800
4818 */
4819 len = scnprintf(extra,
4820 sizeof(extra),
4821 "%s %d %d:%d:%d:%d %u %d %d %d %d",
4822 command,
4823 tsm_metrics.UplinkPktQueueDly,
4824 tsm_metrics.UplinkPktQueueDlyHist[0],
4825 tsm_metrics.UplinkPktQueueDlyHist[1],
4826 tsm_metrics.UplinkPktQueueDlyHist[2],
4827 tsm_metrics.UplinkPktQueueDlyHist[3],
4828 tsm_metrics.UplinkPktTxDly,
4829 tsm_metrics.UplinkPktLoss,
4830 tsm_metrics.UplinkPktCount,
4831 tsm_metrics.RoamingCount,
4832 tsm_metrics.RoamingDly);
4833 len = CDF_MIN(priv_data->total_len, len + 1);
4834 if (copy_to_user(priv_data->buf, &extra, len)) {
4835 CDF_TRACE(CDF_MODULE_ID_HDD,
4836 CDF_TRACE_LEVEL_ERROR,
4837 "%s: failed to copy data to user buffer",
4838 __func__);
4839 ret = -EFAULT;
4840 goto exit;
4841 }
4842
4843exit:
4844 return ret;
4845}
4846
4847static int drv_cmd_set_cckm_ie(hdd_adapter_t *adapter,
4848 hdd_context_t *hdd_ctx,
4849 uint8_t *command,
4850 uint8_t command_len,
4851 hdd_priv_data_t *priv_data)
4852{
4853 int ret;
4854 uint8_t *value = command;
4855 uint8_t *cckmIe = NULL;
4856 uint8_t cckmIeLen = 0;
4857
4858 ret = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
4859 if (ret) {
4860 CDF_TRACE(CDF_MODULE_ID_HDD,
4861 CDF_TRACE_LEVEL_ERROR,
4862 "%s: Failed to parse cckm ie data",
4863 __func__);
4864 goto exit;
4865 }
4866
4867 if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) {
4868 CDF_TRACE(CDF_MODULE_ID_HDD,
4869 CDF_TRACE_LEVEL_ERROR,
4870 "%s: CCKM Ie input length is more than max[%d]",
4871 __func__, DOT11F_IE_RSN_MAX_LEN);
4872 if (NULL != cckmIe) {
4873 cdf_mem_free(cckmIe);
4874 cckmIe = NULL;
4875 }
4876 ret = -EINVAL;
4877 goto exit;
4878 }
4879
4880 sme_set_cckm_ie(hdd_ctx->hHal, adapter->sessionId,
4881 cckmIe, cckmIeLen);
4882 if (NULL != cckmIe) {
4883 cdf_mem_free(cckmIe);
4884 cckmIe = NULL;
4885 }
4886
4887exit:
4888 return ret;
4889}
4890
4891static int drv_cmd_ccx_beacon_req(hdd_adapter_t *adapter,
4892 hdd_context_t *hdd_ctx,
4893 uint8_t *command,
4894 uint8_t command_len,
4895 hdd_priv_data_t *priv_data)
4896{
4897 int ret;
4898 uint8_t *value = command;
4899 tCsrEseBeaconReq eseBcnReq;
4900 CDF_STATUS status = CDF_STATUS_SUCCESS;
4901
4902 if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
4903 hdd_warn("Unsupported in mode %s(%d)",
4904 hdd_device_mode_to_string(adapter->device_mode),
4905 adapter->device_mode);
4906 return -EINVAL;
4907 }
4908
4909 ret = hdd_parse_ese_beacon_req(value, &eseBcnReq);
4910 if (ret) {
4911 CDF_TRACE(CDF_MODULE_ID_HDD,
4912 CDF_TRACE_LEVEL_ERROR,
4913 "%s: Failed to parse ese beacon req",
4914 __func__);
4915 goto exit;
4916 }
4917
4918 if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
4919 hddLog(CDF_TRACE_LEVEL_INFO, FL("Not associated"));
4920 hdd_indicate_ese_bcn_report_no_results(adapter,
4921 eseBcnReq.bcnReq[0].measurementToken,
4922 0x02, /* BIT(1) set for measurement done */
4923 0); /* no BSS */
4924 goto exit;
4925 }
4926
4927 status = sme_set_ese_beacon_request(hdd_ctx->hHal,
4928 adapter->sessionId,
4929 &eseBcnReq);
4930
4931 if (CDF_STATUS_E_RESOURCES == status) {
4932 hddLog(CDF_TRACE_LEVEL_INFO,
4933 FL("sme_set_ese_beacon_request failed (%d), a request already in progress"),
4934 status);
4935 ret = -EBUSY;
4936 goto exit;
4937 } else if (CDF_STATUS_SUCCESS != status) {
4938 CDF_TRACE(CDF_MODULE_ID_HDD,
4939 CDF_TRACE_LEVEL_ERROR,
4940 "%s: sme_set_ese_beacon_request failed (%d)",
4941 __func__, status);
4942 ret = -EINVAL;
4943 goto exit;
4944 }
4945
4946exit:
4947 return ret;
4948}
4949#endif /* #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) */
4950
4951static int drv_cmd_set_mc_rate(hdd_adapter_t *adapter,
4952 hdd_context_t *hdd_ctx,
4953 uint8_t *command,
4954 uint8_t command_len,
4955 hdd_priv_data_t *priv_data)
4956{
4957 int ret = 0;
4958 uint8_t *value = command;
4959 int targetRate;
4960
4961 /* input value is in units of hundred kbps */
4962
4963 /* Move pointer to ahead of SETMCRATE<delimiter> */
4964 value = value + command_len + 1;
4965
4966 /* Convert the value from ascii to integer, decimal base */
4967 ret = kstrtouint(value, 10, &targetRate);
4968
4969 ret = wlan_hdd_set_mc_rate(adapter, targetRate);
4970 return ret;
4971}
4972
4973static int drv_cmd_max_tx_power(hdd_adapter_t *adapter,
4974 hdd_context_t *hdd_ctx,
4975 uint8_t *command,
4976 uint8_t command_len,
4977 hdd_priv_data_t *priv_data)
4978{
4979 int ret = 0;
4980 int status;
4981 int txPower;
4982 CDF_STATUS cdf_status;
4983 CDF_STATUS smeStatus;
4984 uint8_t *value = command;
4985 tSirMacAddr bssid = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4986 tSirMacAddr selfMac = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4987 hdd_adapter_list_node_t *pAdapterNode = NULL;
4988 hdd_adapter_list_node_t *pNext = NULL;
4989
4990 status = hdd_parse_setmaxtxpower_command(value, &txPower);
4991 if (status) {
4992 CDF_TRACE(CDF_MODULE_ID_HDD,
4993 CDF_TRACE_LEVEL_ERROR,
4994 "Invalid MAXTXPOWER command ");
4995 ret = -EINVAL;
4996 goto exit;
4997 }
4998
4999 cdf_status = hdd_get_front_adapter(hdd_ctx, &pAdapterNode);
5000 while (NULL != pAdapterNode
5001 && CDF_STATUS_SUCCESS == cdf_status) {
5002 adapter = pAdapterNode->pAdapter;
5003 /* Assign correct self MAC address */
5004 cdf_mem_copy(bssid,
5005 adapter->macAddressCurrent.bytes,
5006 CDF_MAC_ADDR_SIZE);
5007 cdf_mem_copy(selfMac,
5008 adapter->macAddressCurrent.bytes,
5009 CDF_MAC_ADDR_SIZE);
5010
5011 hddLog(CDF_TRACE_LEVEL_INFO,
5012 "Device mode %d max tx power %d selfMac: " MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
5013 adapter->device_mode, txPower,
5014 MAC_ADDR_ARRAY(selfMac),
5015 MAC_ADDR_ARRAY(bssid));
5016
5017 smeStatus = sme_set_max_tx_power((tHalHandle)(hdd_ctx->hHal),
5018 bssid, selfMac, txPower);
5019 if (CDF_STATUS_SUCCESS != status) {
5020 hddLog(CDF_TRACE_LEVEL_ERROR,
5021 "%s:Set max tx power failed",
5022 __func__);
5023 ret = -EINVAL;
5024 goto exit;
5025 }
5026 hddLog(CDF_TRACE_LEVEL_INFO,
5027 "%s: Set max tx power success",
5028 __func__);
5029 cdf_status = hdd_get_next_adapter(hdd_ctx, pAdapterNode,
5030 &pNext);
5031 pAdapterNode = pNext;
5032 }
5033
5034exit:
5035 return ret;
5036}
5037
5038static int drv_cmd_set_dfs_scan_mode(hdd_adapter_t *adapter,
5039 hdd_context_t *hdd_ctx,
5040 uint8_t *command,
5041 uint8_t command_len,
5042 hdd_priv_data_t *priv_data)
5043{
5044 int ret = 0;
5045 uint8_t *value = command;
5046 uint8_t dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
5047
5048 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
5049 value = value + command_len + 1;
5050
5051 /* Convert the value from ascii to integer */
5052 ret = kstrtou8(value, 10, &dfsScanMode);
5053 if (ret < 0) {
5054 /*
5055 * If the input value is greater than max value of datatype,
5056 * then also kstrtou8 fails
5057 */
5058 CDF_TRACE(CDF_MODULE_ID_HDD,
5059 CDF_TRACE_LEVEL_ERROR,
5060 "%s: kstrtou8 failed range [%d - %d]",
5061 __func__, CFG_ROAMING_DFS_CHANNEL_MIN,
5062 CFG_ROAMING_DFS_CHANNEL_MAX);
5063 ret = -EINVAL;
5064 goto exit;
5065 }
5066
5067 if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
5068 (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) {
5069 CDF_TRACE(CDF_MODULE_ID_HDD,
5070 CDF_TRACE_LEVEL_ERROR,
5071 "dfsScanMode value %d is out of range (Min: %d Max: %d)",
5072 dfsScanMode,
5073 CFG_ROAMING_DFS_CHANNEL_MIN,
5074 CFG_ROAMING_DFS_CHANNEL_MAX);
5075 ret = -EINVAL;
5076 goto exit;
5077 }
5078
5079 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
5080 "%s: Received Command to Set DFS Scan Mode = %d",
5081 __func__, dfsScanMode);
5082
5083 hdd_ctx->config->allowDFSChannelRoam = dfsScanMode;
5084 sme_update_dfs_scan_mode(hdd_ctx->hHal, adapter->sessionId,
5085 dfsScanMode);
5086
5087exit:
5088 return ret;
5089}
5090
5091static int drv_cmd_get_dfs_scan_mode(hdd_adapter_t *adapter,
5092 hdd_context_t *hdd_ctx,
5093 uint8_t *command,
5094 uint8_t command_len,
5095 hdd_priv_data_t *priv_data)
5096{
5097 int ret = 0;
5098 uint8_t dfsScanMode = sme_get_dfs_scan_mode(hdd_ctx->hHal);
5099 char extra[32];
5100 uint8_t len = 0;
5101
5102 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
5103 len = CDF_MIN(priv_data->total_len, len + 1);
5104 if (copy_to_user(priv_data->buf, &extra, len)) {
5105 CDF_TRACE(CDF_MODULE_ID_HDD,
5106 CDF_TRACE_LEVEL_ERROR,
5107 "%s: failed to copy data to user buffer",
5108 __func__);
5109 ret = -EFAULT;
5110 }
5111
5112 return ret;
5113}
5114
5115static int drv_cmd_get_link_status(hdd_adapter_t *adapter,
5116 hdd_context_t *hdd_ctx,
5117 uint8_t *command,
5118 uint8_t command_len,
5119 hdd_priv_data_t *priv_data)
5120{
5121 int ret = 0;
5122 int value = wlan_hdd_get_link_status(adapter);
5123 char extra[32];
5124 uint8_t len;
5125
5126 len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
5127 len = CDF_MIN(priv_data->total_len, len + 1);
5128 if (copy_to_user(priv_data->buf, &extra, len)) {
5129 CDF_TRACE(CDF_MODULE_ID_HDD,
5130 CDF_TRACE_LEVEL_ERROR,
5131 "%s: failed to copy data to user buffer",
5132 __func__);
5133 ret = -EFAULT;
5134 }
5135
5136 return ret;
5137}
5138
5139#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
5140static int drv_cmd_enable_ext_wow(hdd_adapter_t *adapter,
5141 hdd_context_t *hdd_ctx,
5142 uint8_t *command,
5143 uint8_t command_len,
5144 hdd_priv_data_t *priv_data)
5145{
5146 uint8_t *value = command;
5147 int set_value;
5148
5149 /* Move pointer to ahead of ENABLEEXTWOW */
5150 value = value + command_len;
5151
5152 sscanf(value, "%d", &set_value);
5153
5154 return hdd_enable_ext_wow_parser(adapter,
5155 adapter->sessionId,
5156 set_value);
5157}
5158
5159static int drv_cmd_set_app1_params(hdd_adapter_t *adapter,
5160 hdd_context_t *hdd_ctx,
5161 uint8_t *command,
5162 uint8_t command_len,
5163 hdd_priv_data_t *priv_data)
5164{
5165 int ret;
5166 uint8_t *value = command;
5167
5168 /* Move pointer to ahead of SETAPP1PARAMS */
5169 value = value + command_len;
5170
5171 ret = hdd_set_app_type1_parser(adapter,
5172 value, strlen(value));
5173 if (ret >= 0)
5174 hdd_ctx->is_extwow_app_type1_param_set = true;
5175
5176 return ret;
5177}
5178
5179static int drv_cmd_set_app2_params(hdd_adapter_t *adapter,
5180 hdd_context_t *hdd_ctx,
5181 uint8_t *command,
5182 uint8_t command_len,
5183 hdd_priv_data_t *priv_data)
5184{
5185 int ret;
5186 uint8_t *value = command;
5187
5188 /* Move pointer to ahead of SETAPP2PARAMS */
5189 value = value + command_len;
5190
5191 ret = hdd_set_app_type2_parser(adapter, value, strlen(value));
5192 if (ret >= 0)
5193 hdd_ctx->is_extwow_app_type2_param_set = true;
5194
5195 return ret;
5196}
5197#endif /* WLAN_FEATURE_EXTWOW_SUPPORT */
5198
5199#ifdef FEATURE_WLAN_TDLS
5200/**
5201 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset
5202 * @adapter: Pointer to the HDD adapter
5203 * @hdd_ctx: Pointer to the HDD context
5204 * @command: Driver command string
5205 * @command_len: Driver command string length
5206 * @priv_data: Private data coming with the driver command. Unused here
5207 *
5208 * This function handles driver command that sets the secondary tdls off channel
5209 * offset
5210 *
5211 * Return: 0 on success; negative errno otherwise
5212 */
5213static int drv_cmd_tdls_secondary_channel_offset(hdd_adapter_t *adapter,
5214 hdd_context_t *hdd_ctx,
5215 uint8_t *command,
5216 uint8_t command_len,
5217 hdd_priv_data_t *priv_data)
5218{
5219 int ret;
5220 uint8_t *value = command;
5221 int set_value;
5222
5223 /* Move pointer to point the string */
5224 value += command_len;
5225
5226 ret = sscanf(value, "%d", &set_value);
5227 if (ret != 1)
5228 return -EINVAL;
5229
5230 hddLog(LOG1, FL("Tdls offchannel offset:%d"), set_value);
5231
5232 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, set_value);
5233
5234 return ret;
5235}
5236
5237/**
5238 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode
5239 * @adapter: Pointer to the HDD adapter
5240 * @hdd_ctx: Pointer to the HDD context
5241 * @command: Driver command string
5242 * @command_len: Driver command string length
5243 * @priv_data: Private data coming with the driver command. Unused here
5244 *
5245 * This function handles driver command that sets tdls off channel mode
5246 *
5247 * Return: 0 on success; negative errno otherwise
5248 */
5249static int drv_cmd_tdls_off_channel_mode(hdd_adapter_t *adapter,
5250 hdd_context_t *hdd_ctx,
5251 uint8_t *command,
5252 uint8_t command_len,
5253 hdd_priv_data_t *priv_data)
5254{
5255 int ret;
5256 uint8_t *value = command;
5257 int set_value;
5258
5259 /* Move pointer to point the string */
5260 value += command_len;
5261
5262 ret = sscanf(value, "%d", &set_value);
5263 if (ret != 1)
5264 return -EINVAL;
5265
5266 hddLog(LOG1, FL("Tdls offchannel mode:%d"), set_value);
5267
5268 ret = hdd_set_tdls_offchannelmode(adapter, set_value);
5269
5270 return ret;
5271}
5272
5273/**
5274 * drv_cmd_tdls_off_channel() - set tdls off channel number
5275 * @adapter: Pointer to the HDD adapter
5276 * @hdd_ctx: Pointer to the HDD context
5277 * @command: Driver command string
5278 * @command_len: Driver command string length
5279 * @priv_data: Private data coming with the driver command. Unused here
5280 *
5281 * This function handles driver command that sets tdls off channel number
5282 *
5283 * Return: 0 on success; negative errno otherwise
5284 */
5285static int drv_cmd_tdls_off_channel(hdd_adapter_t *adapter,
5286 hdd_context_t *hdd_ctx,
5287 uint8_t *command,
5288 uint8_t command_len,
5289 hdd_priv_data_t *priv_data)
5290{
5291 int ret;
5292 uint8_t *value = command;
5293 int set_value;
5294
5295 /* Move pointer to point the string */
5296 value += command_len;
5297
5298 ret = sscanf(value, "%d", &set_value);
5299 if (ret != 1)
5300 return -EINVAL;
5301
5302 hddLog(LOG1, FL("Tdls offchannel num: %d"), set_value);
5303
5304 ret = hdd_set_tdls_offchannel(hdd_ctx, set_value);
5305
5306 return ret;
5307}
5308
5309/**
5310 * drv_cmd_tdls_scan() - set tdls scan type
5311 * @adapter: Pointer to the HDD adapter
5312 * @hdd_ctx: Pointer to the HDD context
5313 * @command: Driver command string
5314 * @command_len: Driver command string length
5315 * @priv_data: Private data coming with the driver command. Unused here
5316 *
5317 * This function handles driver command that sets tdls scan type
5318 *
5319 * Return: 0 on success; negative errno otherwise
5320 */
5321static int drv_cmd_tdls_scan(hdd_adapter_t *adapter,
5322 hdd_context_t *hdd_ctx,
5323 uint8_t *command,
5324 uint8_t command_len,
5325 hdd_priv_data_t *priv_data)
5326{
5327 int ret;
5328 uint8_t *value = command;
5329 int set_value;
5330
5331 /* Move pointer to point the string */
5332 value += command_len;
5333
5334 ret = sscanf(value, "%d", &set_value);
5335 if (ret != 1)
5336 return -EINVAL;
5337
5338 hddLog(LOG1, FL("Tdls scan type val: %d"), set_value);
5339
5340 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value);
5341
5342 return ret;
5343}
5344#endif
5345
5346static int drv_cmd_get_rssi(hdd_adapter_t *adapter,
5347 hdd_context_t *hdd_ctx,
5348 uint8_t *command,
5349 uint8_t command_len,
5350 hdd_priv_data_t *priv_data)
5351{
5352 int ret;
5353 int8_t rssi = 0;
5354 char extra[32];
5355 uint8_t len = 0;
5356
5357 wlan_hdd_get_rssi(adapter, &rssi);
5358
5359 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
5360 len = CDF_MIN(priv_data->total_len, len + 1);
5361
5362 if (copy_to_user(priv_data->buf, &extra, len)) {
5363 hddLog(LOGE, FL("Failed to copy data to user buffer"));
5364 ret = -EFAULT;
5365 }
5366
5367 return ret;
5368}
5369
5370static int drv_cmd_get_linkspeed(hdd_adapter_t *adapter,
5371 hdd_context_t *hdd_ctx,
5372 uint8_t *command,
5373 uint8_t command_len,
5374 hdd_priv_data_t *priv_data)
5375{
5376 int ret;
5377 uint32_t link_speed = 0;
5378 char extra[32];
5379 uint8_t len = 0;
5380
5381 ret = wlan_hdd_get_link_speed(adapter, &link_speed);
5382 if (0 != ret)
5383 return ret;
5384
5385 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
5386 len = CDF_MIN(priv_data->total_len, len + 1);
5387 if (copy_to_user(priv_data->buf, &extra, len)) {
5388 hddLog(LOGE, FL("Failed to copy data to user buffer"));
5389 ret = -EFAULT;
5390 }
5391
5392 return ret;
5393}
5394
5395#ifdef FEATURE_NAPI
5396/**
5397 * hdd_parse_napi() - helper functions to drv_cmd_napi
5398 * @str : source string to parse
5399 * @cmd : pointer to cmd part after parsing
5400 * @sub : pointer to subcmd part after parsing
5401 * @aux : pointer to optional aux part after parsing
5402 *
5403 * Example:
5404 * NAPI SCALE <n> +-- IN str
5405 * | | +------ OUT aux
5406 * | +------------ OUT subcmd
5407 * +----------------- OUT cmd
5408 *
5409 * Return: ==0: success; !=0: failure
5410 */
5411static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
5412{
5413 int rc;
5414 char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
5415
5416 NAPI_DEBUG("-->\n");
5417
5418 token = strsep(str, " \t");
5419 if (NULL == token) {
5420 hdd_err("cannot parse cmd");
5421 goto parse_end;
5422 }
5423 lcmd = token;
5424
5425 token = strsep(str, " \t");
5426 if (NULL == token) {
5427 hdd_err("cannot parse subcmd");
5428 goto parse_end;
5429 }
5430 lsub = token;
5431
5432 token = strsep(str, " \t");
5433 if (NULL == token)
5434 hdd_warn("cannot parse aux\n");
5435 else
5436 laux = token;
5437
5438parse_end:
5439 if ((NULL == lcmd) || (NULL == lsub))
5440 rc = -EINVAL;
5441 else {
5442 rc = 0;
5443 *cmd = lcmd;
5444 *sub = lsub;
5445 if (NULL != aux)
5446 *aux = laux;
5447 }
5448 NAPI_DEBUG("<--[rc=%d]\n", rc);
5449 return rc;
5450}
5451
5452
5453/**
5454 * hdd_parse_stats() - print NAPI stats into a buffer
5455 * @buf : buffer to write stats into
5456 * @max : "size of buffer"
5457 * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
5458 * @napid: binary structure to retrieve the stats from
5459 *
5460 * Return: number of bytes written into the buffer
5461 */
5462int hdd_napi_stats(char *buf,
5463 int max,
5464 char *indp,
5465 struct qca_napi_data *napid)
5466{
5467 int n = 0;
5468 int i, j, k; /* NAPI, CPU, bucket indices */
5469 int from, to;
5470 struct qca_napi_info *napii;
5471 struct qca_napi_stat *napis;
5472
5473 NAPI_DEBUG("-->\n");
5474
5475 if (NULL == indp) {
5476 from = 0;
5477 to = sizeof(uint32_t) * CE_COUNT_MAX;
5478 } else {
5479 if (0 > kstrtoint(indp, 10, &to)) {
5480 from = 0;
5481 to = sizeof(uint32_t) * CE_COUNT_MAX;
5482 } else
5483 from = to;
5484 }
5485
5486 for (i = from; i < to; i++)
5487 if (napid->ce_map & (0x01 << i)) {
5488 napii = &(napid->napis[i]);
5489 for (j = 0; j < NR_CPUS; j++) {
5490 napis = &(napii->stats[j]);
5491 n += scnprintf(buf + n, max - n,
5492 "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
5493 i, j,
5494 napis->napi_schedules,
5495 napis->napi_polls,
5496 napis->napi_completes,
5497 napis->napi_workdone);
5498
5499 for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
5500 n += scnprintf(
5501 buf + n, max - n,
5502 " %d",
5503 napis->napi_budget_uses[k]);
5504 }
5505 n += scnprintf(buf+n, max - n, "\n");
5506 }
5507 }
5508
5509 NAPI_DEBUG("<--[n=%d]\n", n);
5510 return n;
5511}
5512
5513/**
5514 * napi_set_scale() - sets the scale attribute in all NAPI entries
5515 * @sc : scale to set
5516 *
5517 * Return: void
5518 */
5519static void napi_set_scale(uint8_t sc)
5520{
5521 uint32_t i;
5522 struct qca_napi_data *napi_data;
5523
5524 napi_data = hdd_napi_get_all();
5525 for (i = 0; i < sizeof(uint32_t)*8; i++)
5526 if (napi_data->ce_map & (0x01 << i))
5527 napi_data->napis[i].scale = sc;
5528
5529 return;
5530}
5531/**
5532 * drv_cmd_napi() - processes NAPI commands
5533 * @adapter : net_device
5534 * @hdd_ctx : HDD context
5535 * @command : command string from user command (including "NAPI")
5536 * @command_len: length of command
5537 * @priv_data : ifr_data
5538 *
5539 * Commands supported:
5540 * NAPI ENABLE : enables NAPI administratively. Note that this may not
5541 * enable NAPI functionally, as some other conditions
5542 * may not have been satisfied yet
5543 * NAPI DISABLE : reverse operation of "enable"
5544 * NAPI STATUS : get global status of NAPI instances
5545 * NAPI STATS [<n>] : get the stats for a given NAPI instance
5546 * NAPI SCALE <n> : set the scale factor
5547 *
5548 * Return: 0: success; 0>: failure
5549 */
5550static int drv_cmd_napi(hdd_adapter_t *adapter,
5551 hdd_context_t *hdd_ctx,
5552 uint8_t *command,
5553 uint8_t command_len,
5554 hdd_priv_data_t *priv_data)
5555{
5556 int rc = 0;
5557 int n, l;
5558 char *cmd = NULL, *subcmd = NULL, *aux = NULL;
5559 char *synopsis = "NAPI ENABLE\n"
5560 "NAPI DISABLE\n"
5561 "NAPI STATUS\n"
5562 "NAPI STATS [<n>] -- if no <n> then all\n"
5563 "NAPI SCALE <n> -- set the scale\n";
5564 char *reply = NULL;
5565
5566 /* make a local copy, as strsep modifies the str in place */
5567 char *str = NULL;
5568
5569 NAPI_DEBUG("-->\n");
5570
5571 /**
5572 * NOTE TO MAINTAINER: from this point to the end of the function,
5573 * please do not return anywhere in the code except the very end
5574 * to avoid memory leakage (goto end_drv_napi instead)
5575 * or make sure that reply+str is freed
5576 */
5577 reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
5578 if (NULL == reply) {
5579 hdd_err("could not allocate reply buffer");
5580 rc = -ENOMEM;
5581 goto end_drv_napi;
5582 }
5583
5584 str = kmalloc(strlen(command) + 1, GFP_KERNEL);
5585 if (NULL == str) {
5586 hdd_err("could not allocate copy of input buffer");
5587 rc = -ENOMEM;
5588 goto end_drv_napi;
5589 }
5590
5591 strlcpy(str, command, strlen(command) + 1);
5592 hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
5593 cmd, subcmd, aux);
5594
5595
5596 rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
5597
5598 if (0 != rc) {
5599 const char *msg = "unknown or badly formatted cmd\n%s";
5600 l = CDF_MIN(MAX_USER_COMMAND_SIZE,
5601 strlen(msg)+strlen(synopsis));
5602 n = scnprintf(reply, l, msg, synopsis);
5603
5604 if (copy_to_user(priv_data->buf, reply,
5605 CDF_MIN(priv_data->total_len, l)))
5606 hdd_err("failed to copy data to user buffer");
5607 hdd_debug("reply: %s", reply);
5608
5609 rc = -EINVAL;
5610 } else {
5611 hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
5612 cmd, subcmd, aux);
5613 if (!strcmp(subcmd, "ENABLE"))
5614 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
5615 else if (!strcmp(subcmd, "DISABLE"))
5616 hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
5617 else if (!strcmp(subcmd, "STATUS")) {
5618 int n = 0;
5619 uint32_t i;
5620 struct qca_napi_data *napi_data;
5621
5622 napi_data = hdd_napi_get_all();
5623 n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
5624 "NAPI state: 0x%08x map: 0x%08x\n",
5625 napi_data->state,
5626 napi_data->ce_map);
5627
5628 for (i = 0; i < sizeof(uint32_t)*8; i++)
5629 if (napi_data->ce_map & (0x01 << i)) {
5630 n += scnprintf(
5631 reply + n,
5632 MAX_USER_COMMAND_SIZE - n,
5633 "#%d: id: %d, scale=%d\n",
5634 i,
5635 napi_data->napis[i].id,
5636 napi_data->napis[i].scale);
5637 }
5638 hdd_info("wlan: STATUS DATA:\n%s", reply);
5639 if (copy_to_user(priv_data->buf, reply,
5640 CDF_MIN(n, priv_data->total_len)))
5641 rc = -EINVAL;
5642 } else if (!strcmp(subcmd, "STATS")) {
5643 int n = 0;
5644 struct qca_napi_data *napi_data;
5645
5646 napi_data = hdd_napi_get_all();
5647 n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
5648 aux, napi_data);
5649 NAPI_DEBUG("STATS: returns %d\n", n);
5650
5651 if (n > 0) {
5652 if (copy_to_user(priv_data->buf, reply,
5653 CDF_MIN(priv_data->total_len,
5654 n)))
5655 rc = -EINVAL;
5656 hdd_info("wlan: STATS_DATA\n%s\n", reply);
5657 } else
5658 rc = -EINVAL;
5659 } else if (!strcmp(subcmd, "SCALE")) {
5660 if (NULL == aux) {
5661 rc = -EINVAL;
5662 hdd_err("wlan: SCALE cmd requires <n>");
5663 } else {
5664 uint8_t sc;
5665 rc = kstrtou8(aux, 10, &sc);
5666 if (rc) {
5667 hdd_err("wlan: bad scale (%s)", aux);
5668 rc = -EINVAL;
5669 } else
5670 napi_set_scale(sc);
5671 }
5672 } /* SCALE */
5673 }
5674end_drv_napi:
5675 if (NULL != str)
5676 kfree(str);
5677 if (NULL != reply)
5678 kfree(reply);
5679
5680 NAPI_DEBUG("<--[rc=%d]\n", rc);
5681 return rc;
5682}
5683#endif /* FEATURE_NAPI */
5684
5685/**
5686 * hdd_set_rx_filter() - set RX filter
5687 * @adapter: Pointer to adapter
5688 * @action: Filter action
5689 * @pattern: Address pattern
5690 *
5691 * Address pattern is most significant byte of address for example
5692 * 0x01 for IPV4 multicast address
5693 * 0x33 for IPV6 multicast address
5694 * 0xFF for broadcast address
5695 *
5696 * Return: 0 for success, non-zero for failure
5697 */
5698static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
5699 uint8_t pattern)
5700{
5701 int ret;
5702 uint8_t i;
5703 tHalHandle handle;
5704 tSirRcvFltMcAddrList *filter;
5705 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
5706
5707 ret = wlan_hdd_validate_context(hdd_ctx);
5708 if (0 != ret)
5709 return ret;
5710
5711 handle = hdd_ctx->hHal;
5712
5713 if (NULL == handle) {
5714 hdd_err("HAL Handle is NULL");
5715 return -EINVAL;
5716 }
5717
5718 /*
5719 * If action is false it means start dropping packets
5720 * Set addr_filter_pattern which will be used when sending
5721 * MC/BC address list to target
5722 */
5723 if (!action)
5724 adapter->addr_filter_pattern = pattern;
5725 else
5726 adapter->addr_filter_pattern = 0;
5727
5728 if (((adapter->device_mode == WLAN_HDD_INFRA_STATION) ||
5729 (adapter->device_mode == WLAN_HDD_P2P_CLIENT)) &&
5730 adapter->mc_addr_list.mc_cnt &&
5731 hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
5732
5733
5734 filter = cdf_mem_malloc(sizeof(*filter));
5735 if (NULL == filter) {
5736 hdd_err("Could not allocate Memory");
5737 return -ENOMEM;
5738 }
5739 filter->action = action;
5740 for (i = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
5741 if (!memcmp(adapter->mc_addr_list.addr[i],
5742 &pattern, 1)) {
5743 memcpy(filter->multicastAddr[i],
5744 adapter->mc_addr_list.addr[i],
5745 sizeof(adapter->mc_addr_list.addr[i]));
5746 filter->ulMulticastAddrCnt++;
5747 hdd_err("%s RX filter : addr ="
5748 MAC_ADDRESS_STR,
5749 action ? "setting" : "clearing",
5750 MAC_ADDR_ARRAY(filter->multicastAddr[i]));
5751 }
5752 }
5753 /* Set rx filter */
5754 sme_8023_multicast_list(handle, adapter->sessionId, filter);
5755 cdf_mem_free(filter);
5756 } else {
5757 hdd_err("mode %d mc_cnt %d",
5758 adapter->device_mode, adapter->mc_addr_list.mc_cnt);
5759 }
5760
5761 return 0;
5762}
5763
5764/**
5765 * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
5766 * @command: Pointer to input string driver command
5767 * @adapter: Pointer to adapter
5768 * @action: Action to enable/disable filtering
5769 *
5770 * If action == false
5771 * Start filtering out data packets based on type
5772 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
5773 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
5774 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
5775 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
5776 *
5777 * if action == true
5778 * Stop filtering data packets based on type
5779 * RXFILTER-ADD 0 -> Stop filtering unicast data packets
5780 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets
5781 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
5782 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
5783 *
5784 * Current implementation only supports IPV4 address filtering by
5785 * selectively allowing IPV4 multicast data packest based on
5786 * address list received in .ndo_set_rx_mode
5787 *
5788 * Return: 0 for success, non-zero for failure
5789 */
5790static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
5791 hdd_adapter_t *adapter,
5792 bool action)
5793{
5794 int ret = 0;
5795 uint8_t *value;
5796 uint8_t type;
5797
5798 value = command;
5799 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
5800 if (!action)
5801 value = command + 16;
5802 else
5803 value = command + 13;
5804 ret = kstrtou8(value, 10, &type);
5805 if (ret < 0) {
5806 hdd_err("kstrtou8 failed invalid input value %d", type);
5807 return -EINVAL;
5808 }
5809
5810 switch (type) {
5811 case 2:
5812 /* Set rx filter for IPV4 multicast data packets */
5813 ret = hdd_set_rx_filter(adapter, action, 0x01);
5814 break;
5815 default:
5816 hdd_info("Unsupported RXFILTER type %d", type);
5817 break;
5818 }
5819
5820 return ret;
5821}
5822
5823/**
5824 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler
5825 * @adapter: Pointer to network adapter
5826 * @hdd_ctx: Pointer to hdd context
5827 * @command: Pointer to input command
5828 * @command_len: Command length
5829 * @priv_data: Pointer to private data in command
5830 */
5831static int drv_cmd_rx_filter_remove(hdd_adapter_t *adapter,
5832 hdd_context_t *hdd_ctx,
5833 uint8_t *command,
5834 uint8_t command_len,
5835 hdd_priv_data_t *priv_data)
5836{
5837 return hdd_driver_rxfilter_comand_handler(command, adapter, false);
5838}
5839
5840/**
5841 * drv_cmd_rx_filter_add() - RXFILTER ADD 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_add(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, true);
5855}
5856
5857/*
5858 * dummy (no-op) hdd driver command handler
5859 */
5860static int drv_cmd_dummy(hdd_adapter_t *adapter,
5861 hdd_context_t *hdd_ctx,
5862 uint8_t *command,
5863 uint8_t command_len,
5864 hdd_priv_data_t *priv_data)
5865{
5866 hdd_info("%s: Ignoring driver command \"%s\"",
5867 adapter->dev->name, command);
5868 return 0;
5869}
5870
5871/*
5872 * handler for any unsupported wlan hdd driver command
5873 */
5874static int drv_cmd_invalid(hdd_adapter_t *adapter,
5875 hdd_context_t *hdd_ctx,
5876 uint8_t *command,
5877 uint8_t command_len,
5878 hdd_priv_data_t *priv_data)
5879{
5880 MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
5881 TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
5882 adapter->sessionId, 0));
5883
5884 hdd_warn("%s: Unsupported driver command \"%s\"",
5885 adapter->dev->name, command);
5886
5887 return -ENOTSUPP;
5888}
5889
5890/**
5891 * drv_cmd_set_fcc_channel() - handle fcc constraint request
5892 * @adapter: HDD adapter
5893 * @hdd_ctx: HDD context
5894 * @command: command ptr, SET_FCC_CHANNEL 0/1 is the command
5895 * @command_len: command len
5896 * @priv_data: private data
5897 *
5898 * Return: status
5899 */
5900static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter,
5901 hdd_context_t *hdd_ctx,
5902 uint8_t *command,
5903 uint8_t command_len,
5904 hdd_priv_data_t *priv_data)
5905{
5906 uint8_t *value;
5907 uint8_t fcc_constraint;
5908 CDF_STATUS status;
5909 int ret = 0;
5910
5911 /*
5912 * this command would be called by user-space when it detects WLAN
5913 * ON after airplane mode is set. When APM is set, WLAN turns off.
5914 * But it can be turned back on. Otherwise; when APM is turned back
5915 * off, WLAN would turn back on. So at that point the command is
5916 * expected to come down. 0 means disable, 1 means enable. The
5917 * constraint is removed when parameter 1 is set or different
5918 * country code is set
5919 */
5920
5921 value = command + command_len + 1;
5922
5923 ret = kstrtou8(value, 10, &fcc_constraint);
5924 if ((ret < 0) || (fcc_constraint > 1)) {
5925 /*
5926 * If the input value is greater than max value of datatype,
5927 * then also it is a failure
5928 */
5929 hdd_err("value out of range");
5930 return -EINVAL;
5931 }
5932
5933 status = sme_disable_non_fcc_channel(hdd_ctx->hHal, !fcc_constraint);
5934 if (status != CDF_STATUS_SUCCESS) {
5935 hdd_err("sme disable fn. returned err");
5936 ret = -EPERM;
5937 }
5938
5939 return ret;
5940}
5941
5942/*
5943 * The following table contains all supported WLAN HDD
5944 * IOCTL driver commands and the handler for each of them.
5945 */
5946static const hdd_drv_cmd_t hdd_drv_cmds[] = {
5947 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr},
5948 {"P2P_SET_NOA", drv_cmd_p2p_set_noa},
5949 {"P2P_SET_PS", drv_cmd_p2p_set_ps},
5950 {"SETBAND", drv_cmd_set_band},
5951 {"SETWMMPS", drv_cmd_set_wmmps},
5952 {"COUNTRY", drv_cmd_country},
5953 {"SETSUSPENDMODE", drv_cmd_dummy},
5954 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy},
5955 {"BTCOEXSCAN", drv_cmd_dummy},
5956 {"RXFILTER", drv_cmd_dummy},
5957#ifdef WLAN_FEATURE_NEIGHBOR_ROAMING
5958 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger},
5959 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger},
5960 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period},
5961 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period},
5962 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period},
5963 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period},
5964#ifdef FEATURE_WLAN_LFR
5965 {"SETROAMMODE", drv_cmd_set_roam_mode},
5966 {"GETROAMMODE", drv_cmd_get_roam_mode},
5967#endif
5968#endif
5969#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
5970 {"SETROAMDELTA", drv_cmd_set_roam_delta},
5971 {"GETROAMDELTA", drv_cmd_get_roam_delta},
5972#endif
5973#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
5974 {"GETBAND", drv_cmd_get_band},
5975 {"SETROAMSCANCHANNELS", drv_cmd_set_roam_scan_channels},
5976 {"GETROAMSCANCHANNELS", drv_cmd_get_roam_scan_channels},
5977 {"GETCCXMODE", drv_cmd_get_ccx_mode},
5978 {"GETOKCMODE", drv_cmd_get_okc_mode},
5979 {"GETFASTROAM", drv_cmd_get_fast_roam},
5980 {"GETFASTTRANSITION", drv_cmd_get_fast_transition},
5981 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time},
5982 {"SENDACTIONFRAME", drv_cmd_send_action_frame},
5983 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time},
5984 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time},
5985 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time},
5986 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time},
5987 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time},
5988 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band},
5989 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band},
5990 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes},
5991 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes},
5992 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time},
5993 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time},
5994 {"REASSOC", drv_cmd_reassoc},
5995 {"SETWESMODE", drv_cmd_set_wes_mode},
5996 {"GETWESMODE", drv_cmd_get_wes_mode},
5997 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff},
5998 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff},
5999 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff},
6000 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff},
6001#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */
6002#ifdef FEATURE_WLAN_LFR
6003 {"SETFASTROAM", drv_cmd_set_fast_roam},
6004#endif
6005#ifdef WLAN_FEATURE_VOWIFI_11R
6006 {"SETFASTTRANSITION", drv_cmd_set_fast_transition},
6007 {"FASTREASSOC", drv_cmd_fast_reassoc},
6008#endif
6009#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
6010 {"CCXPLMREQ", drv_cmd_ccx_plm_req},
6011#endif
6012#ifdef FEATURE_WLAN_ESE
6013 {"SETCCXMODE", drv_cmd_set_ccx_mode},
6014#endif
6015 {"SETROAMSCANCONTROL", drv_cmd_set_roam_scan_control},
6016#ifdef FEATURE_WLAN_OKC
6017 {"SETOKCMODE", drv_cmd_set_okc_mode},
6018#endif /* FEATURE_WLAN_OKC */
6019 {"GETROAMSCANCONTROL", drv_cmd_get_roam_scan_control},
6020 {"BTCOEXMODE", drv_cmd_bt_coex_mode},
6021 {"SCAN-ACTIVE", drv_cmd_scan_active},
6022 {"SCAN-PASSIVE", drv_cmd_scan_passive},
6023 {"GETDWELLTIME", drv_cmd_get_dwell_time},
6024 {"SETDWELLTIME", drv_cmd_set_dwell_time},
6025 {"MIRACAST", drv_cmd_miracast},
6026#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
6027 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels},
6028 {"GETTSMSTATS", drv_cmd_get_tsm_stats},
6029 {"SETCCKMIE", drv_cmd_set_cckm_ie},
6030 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req},
6031#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
6032 {"SETMCRATE", drv_cmd_set_mc_rate},
6033 {"MAXTXPOWER", drv_cmd_max_tx_power},
6034 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode},
6035 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode},
6036 {"GETLINKSTATUS", drv_cmd_get_link_status},
6037#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
6038 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow},
6039 {"SETAPP1PARAMS", drv_cmd_set_app1_params},
6040 {"SETAPP2PARAMS", drv_cmd_set_app2_params},
6041#endif
6042#ifdef FEATURE_WLAN_TDLS
6043 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset},
6044 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode},
6045 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel},
6046 {"TDLSSCAN", drv_cmd_tdls_scan},
6047#endif
6048 {"RSSI", drv_cmd_get_rssi},
6049 {"LINKSPEED", drv_cmd_get_linkspeed},
6050#ifdef FEATURE_NAPI
6051 {"NAPI", drv_cmd_napi},
6052#endif /* FEATURE_NAPI */
6053 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove},
6054 {"RXFILTER-ADD", drv_cmd_rx_filter_add},
6055 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel},
6056};
6057
6058/**
6059 * hdd_drv_cmd_process() - chooses and runs the proper
6060 * handler based on the input command
6061 * @adapter: Pointer to the hdd adapter
6062 * @cmd: Pointer to the driver command
6063 * @priv_data: Pointer to the data associated with the command
6064 *
6065 * This function parses the input hdd driver command and runs
6066 * the proper handler
6067 *
6068 * Return: 0 for success non-zero for failure
6069 */
6070static int hdd_drv_cmd_process(hdd_adapter_t *adapter,
6071 uint8_t *cmd,
6072 hdd_priv_data_t *priv_data)
6073{
6074 hdd_context_t *hdd_ctx;
6075 int i;
6076 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds);
6077 uint8_t *cmd_i = NULL;
6078 hdd_drv_cmd_handler_t handler = NULL;
6079 int len = 0;
6080
6081 if (!adapter || !cmd || !priv_data) {
6082 hddLog(CDF_TRACE_LEVEL_ERROR,
6083 "%s: at least 1 param is NULL", __func__);
6084 return -EINVAL;
6085 }
6086
6087 hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
6088
6089 for (i = 0; i < cmd_num_total; i++) {
6090
6091 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd;
6092 handler = hdd_drv_cmds[i].handler;
6093 len = strlen(cmd_i);
6094
6095 if (!handler) {
6096 hddLog(CDF_TRACE_LEVEL_ERROR,
6097 "%s: no. %d handler is NULL", __func__, i);
6098 return -EINVAL;
6099 }
6100
6101 if (strncasecmp(cmd, cmd_i, len) == 0)
6102 return handler(adapter, hdd_ctx,
6103 cmd, len, priv_data);
6104 }
6105
6106 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data);
6107}
6108
6109/**
6110 * hdd_driver_command() - top level wlan hdd driver command handler
6111 * @adapter: Pointer to the hdd adapter
6112 * @priv_data: Pointer to the raw command data
6113 *
6114 * This function is the top level wlan hdd driver command handler. It
6115 * handles the command with the help of hdd_drv_cmd_process()
6116 *
6117 * Return: 0 for success non-zero for failure
6118 */
6119static int hdd_driver_command(hdd_adapter_t *adapter,
6120 hdd_priv_data_t *priv_data)
6121{
6122 uint8_t *command = NULL;
6123 int ret = 0;
6124
6125 if (CDF_FTM_MODE == hdd_get_conparam()) {
6126 hddLog(LOGE, FL("Command not allowed in FTM mode"));
6127 return -EINVAL;
6128 }
6129
6130 /*
6131 * Note that valid pointers are provided by caller
6132 */
6133
6134 /* copy to local struct to avoid numerous changes to legacy code */
6135 if (priv_data->total_len <= 0 ||
6136 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) {
6137 hddLog(CDF_TRACE_LEVEL_WARN,
6138 "%s:invalid priv_data.total_len(%d)!!!", __func__,
6139 priv_data->total_len);
6140 ret = -EINVAL;
6141 goto exit;
6142 }
6143
6144 /* Allocate +1 for '\0' */
6145 command = kmalloc(priv_data->total_len + 1, GFP_KERNEL);
6146 if (!command) {
6147 hddLog(CDF_TRACE_LEVEL_ERROR,
6148 "%s: failed to allocate memory", __func__);
6149 ret = -ENOMEM;
6150 goto exit;
6151 }
6152
6153 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) {
6154 ret = -EFAULT;
6155 goto exit;
6156 }
6157
6158 /* Make sure the command is NUL-terminated */
6159 command[priv_data->total_len] = '\0';
6160
6161 hdd_info("%s: %s", adapter->dev->name, command);
6162 ret = hdd_drv_cmd_process(adapter, command, priv_data);
6163
6164exit:
6165 if (command)
6166 kfree(command);
6167
6168 return ret;
6169}
6170
6171#ifdef CONFIG_COMPAT
6172static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6173{
6174 struct {
6175 compat_uptr_t buf;
6176 int used_len;
6177 int total_len;
6178 } compat_priv_data;
6179 hdd_priv_data_t priv_data;
6180 int ret = 0;
6181
6182 /*
6183 * Note that adapter and ifr have already been verified by caller,
6184 * and HDD context has also been validated
6185 */
6186 if (copy_from_user(&compat_priv_data, ifr->ifr_data,
6187 sizeof(compat_priv_data))) {
6188 ret = -EFAULT;
6189 goto exit;
6190 }
6191 priv_data.buf = compat_ptr(compat_priv_data.buf);
6192 priv_data.used_len = compat_priv_data.used_len;
6193 priv_data.total_len = compat_priv_data.total_len;
6194 ret = hdd_driver_command(adapter, &priv_data);
6195exit:
6196 return ret;
6197}
6198#else /* CONFIG_COMPAT */
6199static int hdd_driver_compat_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6200{
6201 /* will never be invoked */
6202 return 0;
6203}
6204#endif /* CONFIG_COMPAT */
6205
6206static int hdd_driver_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr)
6207{
6208 hdd_priv_data_t priv_data;
6209 int ret = 0;
6210
6211 /*
6212 * Note that adapter and ifr have already been verified by caller,
6213 * and HDD context has also been validated
6214 */
6215 if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data)))
6216 ret = -EFAULT;
6217 else
6218 ret = hdd_driver_command(adapter, &priv_data);
6219
6220 return ret;
6221}
6222
6223/**
6224 * __hdd_ioctl() - ioctl handler for wlan network interfaces
6225 * @dev: device upon which the ioctl was received
6226 * @ifr: ioctl request information
6227 * @cmd: ioctl command
6228 *
6229 * This function does initial processing of wlan device ioctls.
6230 * Currently two flavors of ioctls are supported. The primary ioctl
6231 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used
6232 * for Android "DRIVER" commands. The other ioctl that is
6233 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used
6234 * for FTM on some platforms. This function simply verifies that the
6235 * driver is in a sane state, and that the ioctl is one of the
6236 * supported flavors, in which case flavor-specific handlers are
6237 * dispatched.
6238 *
6239 * Return: 0 on success, non-zero on error
6240 */
6241static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
6242{
6243 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
6244 hdd_context_t *hdd_ctx;
6245 int ret;
6246
6247 if (dev != adapter->dev) {
6248 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
6249 "%s: HDD adapter/dev inconsistency", __func__);
6250 ret = -ENODEV;
6251 goto exit;
6252 }
6253
6254 if ((!ifr) || (!ifr->ifr_data)) {
6255 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
6256 "%s: invalid data", __func__);
6257 ret = -EINVAL;
6258 goto exit;
6259 }
6260#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
6261 if (CDF_FTM_MODE == hdd_get_conparam()) {
6262 if (SIOCIOCTLTX99 == cmd) {
6263 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, ifr);
6264 goto exit;
6265 }
6266 }
6267#endif
6268
6269 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
6270 ret = wlan_hdd_validate_context(hdd_ctx);
6271 if (ret) {
6272 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
6273 "%s: invalid context", __func__);
6274 ret = -EBUSY;
6275 goto exit;
6276 }
6277
6278 switch (cmd) {
6279 case (SIOCDEVPRIVATE + 1):
6280 if (is_compat_task())
6281 ret = hdd_driver_compat_ioctl(adapter, ifr);
6282 else
6283 ret = hdd_driver_ioctl(adapter, ifr);
6284 break;
6285 default:
6286 hddLog(CDF_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
6287 __func__, cmd);
6288 ret = -EINVAL;
6289 break;
6290 }
6291exit:
6292 return ret;
6293}
6294
6295/**
6296 * hdd_ioctl() - ioctl handler (wrapper) for wlan network interfaces
6297 * @dev: device upon which the ioctl was received
6298 * @ifr: ioctl request information
6299 * @cmd: ioctl command
6300 *
6301 * This function acts as an SSR-protecting wrapper to __hdd_ioctl()
6302 * which is where the ioctls are really handled.
6303 *
6304 * Return: 0 on success, non-zero on error
6305 */
6306int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
6307{
6308 int ret;
6309
6310 cds_ssr_protect(__func__);
6311 ret = __hdd_ioctl(dev, ifr, cmd);
6312 cds_ssr_unprotect(__func__);
6313 return ret;
6314}