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