blob: cf7747ee45fa83e6d7e5e64e38c58a4b6e829def [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2013-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/**
29 * DOC: wlan_hdd_debugfs.c
30 *
31 * This driver currently supports the following debugfs files:
32 * wlan_wcnss/wow_enable to enable/disable WoWL.
33 * wlan_wcnss/wow_pattern to configure WoWL patterns.
34 * wlan_wcnss/pattern_gen to configure periodic TX patterns.
35 */
36
37#ifdef WLAN_OPEN_SOURCE
38#include <wlan_hdd_includes.h>
39#include <wlan_hdd_wowl.h>
40#include <cds_sched.h>
41
42#define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8
43#define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
44#define MAX_USER_COMMAND_SIZE_FRAME 4096
45
46/**
47 * __wcnss_wowenable_write() - wow_enable debugfs handler
48 * @file: debugfs file handle
49 * @buf: text being written to the debugfs
50 * @count: size of @buf
51 * @ppos: (unused) offset into the virtual file system
52 *
53 * Return: number of bytes processed
54 */
55static ssize_t __wcnss_wowenable_write(struct file *file,
56 const char __user *buf, size_t count,
57 loff_t *ppos)
58{
59 hdd_adapter_t *pAdapter;
60 hdd_context_t *hdd_ctx;
61 char cmd[MAX_USER_COMMAND_SIZE_WOWL_ENABLE + 1];
62 char *sptr, *token;
63 uint8_t wow_enable = 0;
64 uint8_t wow_mp = 0;
65 uint8_t wow_pbm = 0;
66 int ret;
67
68 pAdapter = (hdd_adapter_t *)file->private_data;
69 if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
70 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
71 "%s: Invalid adapter or adapter has invalid magic.",
72 __func__);
73
74 return -EINVAL;
75 }
76
77 hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
78 ret = wlan_hdd_validate_context(hdd_ctx);
79 if (0 != ret)
80 return ret;
81
82
83 if (!sme_is_feature_supported_by_fw(WOW)) {
84 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
85 "%s: Wake-on-Wireless feature is not supported in firmware!",
86 __func__);
87
88 return -EINVAL;
89 }
90
91 if (count > MAX_USER_COMMAND_SIZE_WOWL_ENABLE) {
92 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
93 "%s: Command length is larger than %d bytes.",
94 __func__, MAX_USER_COMMAND_SIZE_WOWL_ENABLE);
95
96 return -EINVAL;
97 }
98
99 /* Get command from user */
100 if (copy_from_user(cmd, buf, count))
101 return -EFAULT;
102 cmd[count] = '\0';
103 sptr = cmd;
104
105 /* Get enable or disable wow */
106 token = strsep(&sptr, " ");
107 if (!token)
108 return -EINVAL;
109 if (kstrtou8(token, 0, &wow_enable))
110 return -EINVAL;
111
112 /* Disable wow */
113 if (!wow_enable) {
114 if (!hdd_exit_wowl(pAdapter)) {
115 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
116 "%s: hdd_exit_wowl failed!", __func__);
117
118 return -EFAULT;
119 }
120
121 return count;
122 }
123
124 /* Get enable or disable magic packet mode */
125 token = strsep(&sptr, " ");
126 if (!token)
127 return -EINVAL;
128 if (kstrtou8(token, 0, &wow_mp))
129 return -EINVAL;
130 if (wow_mp > 1)
131 wow_mp = 1;
132
133 /* Get enable or disable pattern byte matching mode */
134 token = strsep(&sptr, " ");
135 if (!token)
136 return -EINVAL;
137 if (kstrtou8(token, 0, &wow_pbm))
138 return -EINVAL;
139 if (wow_pbm > 1)
140 wow_pbm = 1;
141
142 if (!hdd_enter_wowl(pAdapter, wow_mp, wow_pbm)) {
143 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
144 "%s: hdd_enter_wowl failed!", __func__);
145
146 return -EFAULT;
147 }
148
149 return count;
150}
151
152/**
153 * wcnss_wowenable_write() - SSR wrapper for wcnss_wowenable_write
154 * @file: file pointer
155 * @buf: buffer
156 * @count: count
157 * @ppos: position pointer
158 *
159 * Return: 0 on success, error number otherwise
160 */
161static ssize_t wcnss_wowenable_write(struct file *file,
162 const char __user *buf,
163 size_t count, loff_t *ppos)
164{
165 ssize_t ret;
166
167 cds_ssr_protect(__func__);
168 ret = __wcnss_wowenable_write(file, buf, count, ppos);
169 cds_ssr_unprotect(__func__);
170
171 return ret;
172}
173
174/**
175 * __wcnss_wowpattern_write() - wow_pattern debugfs handler
176 * @file: debugfs file handle
177 * @buf: text being written to the debugfs
178 * @count: size of @buf
179 * @ppos: (unused) offset into the virtual file system
180 *
181 * Return: number of bytes processed
182 */
183static ssize_t __wcnss_wowpattern_write(struct file *file,
184 const char __user *buf, size_t count,
185 loff_t *ppos)
186{
187 hdd_adapter_t *pAdapter = (hdd_adapter_t *) file->private_data;
188 hdd_context_t *hdd_ctx;
189 char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN + 1];
190 char *sptr, *token;
191 uint8_t pattern_idx = 0;
192 uint8_t pattern_offset = 0;
193 char *pattern_buf;
194 char *pattern_mask;
195 int ret;
196
197 if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
198 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
199 "%s: Invalid adapter or adapter has invalid magic.",
200 __func__);
201
202 return -EINVAL;
203 }
204
205 hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
206 ret = wlan_hdd_validate_context(hdd_ctx);
207 if (0 != ret)
208 return ret;
209
210 if (!sme_is_feature_supported_by_fw(WOW)) {
211 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
212 "%s: Wake-on-Wireless feature is not supported in firmware!",
213 __func__);
214
215 return -EINVAL;
216 }
217
218 if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN) {
219 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
220 "%s: Command length is larger than %d bytes.",
221 __func__, MAX_USER_COMMAND_SIZE_WOWL_PATTERN);
222
223 return -EINVAL;
224 }
225
226 /* Get command from user */
227 if (copy_from_user(cmd, buf, count))
228 return -EFAULT;
229 cmd[count] = '\0';
230 sptr = cmd;
231
232 /* Get pattern idx */
233 token = strsep(&sptr, " ");
234 if (!token)
235 return -EINVAL;
236
237 if (kstrtou8(token, 0, &pattern_idx))
238 return -EINVAL;
239
240 /* Get pattern offset */
241 token = strsep(&sptr, " ");
242
243 /* Delete pattern if no further argument */
244 if (!token) {
245 hdd_del_wowl_ptrn_debugfs(pAdapter, pattern_idx);
246
247 return count;
248 }
249
250 if (kstrtou8(token, 0, &pattern_offset))
251 return -EINVAL;
252
253 /* Get pattern */
254 token = strsep(&sptr, " ");
255 if (!token)
256 return -EINVAL;
257
258 pattern_buf = token;
259
260 /* Get pattern mask */
261 token = strsep(&sptr, " ");
262 if (!token)
263 return -EINVAL;
264
265 pattern_mask = token;
266 pattern_mask[strlen(pattern_mask) - 1] = '\0';
267
268 hdd_add_wowl_ptrn_debugfs(pAdapter, pattern_idx, pattern_offset,
269 pattern_buf, pattern_mask);
270
271 return count;
272}
273
274/**
275 * wcnss_wowpattern_write() - SSR wrapper for __wcnss_wowpattern_write
276 * @file: file pointer
277 * @buf: buffer
278 * @count: count
279 * @ppos: position pointer
280 *
281 * Return: 0 on success, error number otherwise
282 */
283static ssize_t wcnss_wowpattern_write(struct file *file,
284 const char __user *buf,
285 size_t count, loff_t *ppos)
286{
287 ssize_t ret;
288
289 cds_ssr_protect(__func__);
290 ret = __wcnss_wowpattern_write(file, buf, count, ppos);
291 cds_ssr_unprotect(__func__);
292
293 return ret;
294}
295
296/**
297 * wcnss_patterngen_write() - pattern_gen debugfs handler
298 * @file: debugfs file handle
299 * @buf: text being written to the debugfs
300 * @count: size of @buf
301 * @ppos: (unused) offset into the virtual file system
302 *
303 * Return: number of bytes processed
304 */
305static ssize_t __wcnss_patterngen_write(struct file *file,
306 const char __user *buf, size_t count,
307 loff_t *ppos)
308{
309 hdd_adapter_t *pAdapter;
310 hdd_context_t *pHddCtx;
311 tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams;
312 tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams;
313
314 char *cmd, *sptr, *token;
315 uint8_t pattern_idx = 0;
316 uint8_t pattern_duration = 0;
317 char *pattern_buf;
318 uint16_t pattern_len = 0;
319 uint16_t i = 0;
320 CDF_STATUS status;
321 int ret;
322
323
324 pAdapter = (hdd_adapter_t *)file->private_data;
325 if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
326 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
327 "%s: Invalid adapter or adapter has invalid magic.",
328 __func__);
329
330 return -EINVAL;
331 }
332
333 pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
334 ret = wlan_hdd_validate_context(pHddCtx);
335 if (0 != ret)
336 return ret;
337
338 if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) {
339 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
340 "%s: Periodic Tx Pattern Offload feature is not supported in firmware!",
341 __func__);
342 return -EINVAL;
343 }
344
345 /* Get command from user */
346 if (count <= MAX_USER_COMMAND_SIZE_FRAME)
347 cmd = cdf_mem_malloc(count + 1);
348 else {
349 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
350 "%s: Command length is larger than %d bytes.",
351 __func__, MAX_USER_COMMAND_SIZE_FRAME);
352
353 return -EINVAL;
354 }
355
356 if (!cmd) {
357 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
358 FL("Memory allocation for cmd failed!"));
359 return -ENOMEM;
360 }
361
362 if (copy_from_user(cmd, buf, count)) {
363 cdf_mem_free(cmd);
364 return -EFAULT;
365 }
366 cmd[count] = '\0';
367 sptr = cmd;
368
369 /* Get pattern idx */
370 token = strsep(&sptr, " ");
371 if (!token)
372 goto failure;
373 if (kstrtou8(token, 0, &pattern_idx))
374 goto failure;
375
376 if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1)) {
377 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
378 "%s: Pattern index %d is not in the range (0 ~ %d).",
379 __func__, pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1);
380
381 goto failure;
382 }
383
384 /* Get pattern duration */
385 token = strsep(&sptr, " ");
386 if (!token)
387 goto failure;
388 if (kstrtou8(token, 0, &pattern_duration))
389 goto failure;
390
391 /* Delete pattern using index if duration is 0 */
392 if (!pattern_duration) {
393 delPeriodicTxPtrnParams =
394 cdf_mem_malloc(sizeof(tSirDelPeriodicTxPtrn));
395 if (!delPeriodicTxPtrnParams) {
396 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
397 FL("Memory allocation failed!"));
398 cdf_mem_free(cmd);
399 return -ENOMEM;
400 }
401 delPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
402 delPeriodicTxPtrnParams->ucPatternIdBitmap = 1 << pattern_idx;
403 cdf_mem_copy(delPeriodicTxPtrnParams->macAddress,
404 pAdapter->macAddressCurrent.bytes, 6);
405
406 /* Delete pattern */
407 status = sme_del_periodic_tx_ptrn(pHddCtx->hHal,
408 delPeriodicTxPtrnParams);
409 if (CDF_STATUS_SUCCESS != status) {
410 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
411 "%s: sme_del_periodic_tx_ptrn() failed!",
412 __func__);
413
414 cdf_mem_free(delPeriodicTxPtrnParams);
415 goto failure;
416 }
417 cdf_mem_free(cmd);
418 cdf_mem_free(delPeriodicTxPtrnParams);
419 return count;
420 }
421
422 /*
423 * In SAP mode allow configuration without any connection check
424 * In STA mode check if it's in connected state before adding
425 * patterns
426 */
427 hdd_info("device mode %d", pAdapter->device_mode);
428 if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
429 (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))) {
430 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
431 "%s: Not in Connected state!", __func__);
432 goto failure;
433 }
434
435 /* Get pattern */
436 token = strsep(&sptr, " ");
437 if (!token)
438 goto failure;
439
440 pattern_buf = token;
441 pattern_buf[strlen(pattern_buf) - 1] = '\0';
442 pattern_len = strlen(pattern_buf);
443
444 /* Since the pattern is a hex string, 2 characters represent 1 byte. */
445 if (pattern_len % 2) {
446 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
447 "%s: Malformed pattern!", __func__);
448
449 goto failure;
450 } else
451 pattern_len >>= 1;
452
453 if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE) {
454 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
455 "%s: Not an 802.3 frame!", __func__);
456
457 goto failure;
458 }
459
460 addPeriodicTxPtrnParams = cdf_mem_malloc(sizeof(tSirAddPeriodicTxPtrn));
461 if (!addPeriodicTxPtrnParams) {
462 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
463 FL("Memory allocation failed!"));
464 cdf_mem_free(cmd);
465 return -ENOMEM;
466 }
467
468 addPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
469 addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500;
470 addPeriodicTxPtrnParams->ucPtrnSize = pattern_len;
471 cdf_mem_copy(addPeriodicTxPtrnParams->macAddress,
472 pAdapter->macAddressCurrent.bytes, 6);
473
474 /* Extract the pattern */
475 for (i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++) {
476 addPeriodicTxPtrnParams->ucPattern[i] =
477 (hex_to_bin(pattern_buf[0]) << 4) +
478 hex_to_bin(pattern_buf[1]);
479
480 /* Skip to next byte */
481 pattern_buf += 2;
482 }
483
484 /* Add pattern */
485 status = sme_add_periodic_tx_ptrn(pHddCtx->hHal,
486 addPeriodicTxPtrnParams);
487 if (CDF_STATUS_SUCCESS != status) {
488 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
489 "%s: sme_add_periodic_tx_ptrn() failed!", __func__);
490
491 cdf_mem_free(addPeriodicTxPtrnParams);
492 goto failure;
493 }
494 cdf_mem_free(cmd);
495 cdf_mem_free(addPeriodicTxPtrnParams);
496 return count;
497
498failure:
499 cdf_mem_free(cmd);
500 return -EINVAL;
501}
502
503/**
504 * wcnss_patterngen_write() - SSR wrapper for __wcnss_patterngen_write
505 * @file: file pointer
506 * @buf: buffer
507 * @count: count
508 * @ppos: position pointer
509 *
510 * Return: 0 on success, error number otherwise
511 */
512static ssize_t wcnss_patterngen_write(struct file *file,
513 const char __user *buf,
514 size_t count, loff_t *ppos)
515{
516 ssize_t ret;
517
518 cds_ssr_protect(__func__);
519 ret = __wcnss_patterngen_write(file, buf, count, ppos);
520 cds_ssr_unprotect(__func__);
521
522 return ret;
523}
524
525/**
526 * __wcnss_debugfs_open() - Generic debugfs open() handler
527 * @inode: inode of the debugfs file
528 * @file: file handle of the debugfs file
529 *
530 * Return: 0
531 */
532static int __wcnss_debugfs_open(struct inode *inode, struct file *file)
533{
534 hdd_adapter_t *adapter;
535 hdd_context_t *hdd_ctx;
536 int ret;
537
538 ENTER();
539
540 if (inode->i_private)
541 file->private_data = inode->i_private;
542
543 adapter = (hdd_adapter_t *)file->private_data;
544 if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
545 CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_FATAL,
546 "%s: Invalid adapter or adapter has invalid magic.",
547 __func__);
548 return -EINVAL;
549 }
550
551 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
552 ret = wlan_hdd_validate_context(hdd_ctx);
553 if (0 != ret)
554 return ret;
555
556 return 0;
557}
558
559/**
560 * wcnss_debugfs_open() - SSR wrapper for __wcnss_debugfs_open
561 * @inode: inode pointer
562 * @file: file pointer
563 *
564 * Return: 0 on success, error number otherwise
565 */
566static int wcnss_debugfs_open(struct inode *inode, struct file *file)
567{
568 int ret;
569
570 cds_ssr_protect(__func__);
571 ret = __wcnss_debugfs_open(inode, file);
572 cds_ssr_unprotect(__func__);
573
574 return ret;
575}
576
577static const struct file_operations fops_wowenable = {
578 .write = wcnss_wowenable_write,
579 .open = wcnss_debugfs_open,
580 .owner = THIS_MODULE,
581 .llseek = default_llseek,
582};
583
584static const struct file_operations fops_wowpattern = {
585 .write = wcnss_wowpattern_write,
586 .open = wcnss_debugfs_open,
587 .owner = THIS_MODULE,
588 .llseek = default_llseek,
589};
590
591static const struct file_operations fops_patterngen = {
592 .write = wcnss_patterngen_write,
593 .open = wcnss_debugfs_open,
594 .owner = THIS_MODULE,
595 .llseek = default_llseek,
596};
597
598/**
599 * hdd_debugfs_init() - Initialize debugfs interface
600 * @pAdapter: primary wlan adapter
601 *
602 * Register support for the debugfs files supported by the driver.
603 *
604 * NB: The current implementation only supports debugfs operations
605 * on the primary interface, i.e. wlan0
606 *
607 * Return: CDF_STATUS_SUCCESS if all files registered,
608 * CDF_STATUS_E_FAILURE on failure
609 */
610CDF_STATUS hdd_debugfs_init(hdd_adapter_t *pAdapter)
611{
612 hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
613 pHddCtx->debugfs_phy = debugfs_create_dir("wlan_wcnss", 0);
614
615 if (NULL == pHddCtx->debugfs_phy)
616 return CDF_STATUS_E_FAILURE;
617
618 if (NULL == debugfs_create_file("wow_enable", S_IRUSR | S_IWUSR,
619 pHddCtx->debugfs_phy, pAdapter,
620 &fops_wowenable))
621 return CDF_STATUS_E_FAILURE;
622
623 if (NULL == debugfs_create_file("wow_pattern", S_IRUSR | S_IWUSR,
624 pHddCtx->debugfs_phy, pAdapter,
625 &fops_wowpattern))
626 return CDF_STATUS_E_FAILURE;
627
628 if (NULL == debugfs_create_file("pattern_gen", S_IRUSR | S_IWUSR,
629 pHddCtx->debugfs_phy, pAdapter,
630 &fops_patterngen))
631 return CDF_STATUS_E_FAILURE;
632
633 return CDF_STATUS_SUCCESS;
634}
635
636/**
637 * hdd_debugfs_exit() - Shutdown debugfs interface
638 * @pHddCtx: the global HDD context
639 *
640 * Unregister support for the debugfs files supported by the driver.
641 *
642 * Return: None
643 */
644void hdd_debugfs_exit(hdd_context_t *pHddCtx)
645{
646 debugfs_remove_recursive(pHddCtx->debugfs_phy);
647}
648#endif /* #ifdef WLAN_OPEN_SOURCE */