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