2 ******************************************************************************
4 * @file ecrnx_debugfs.c
6 * @brief Definition of debugfs entries
8 * Copyright (C) ESWIN 2015-2020
10 ******************************************************************************
14 #include <linux/kernel.h>
15 #include <linux/kmod.h>
16 #include <linux/debugfs.h>
17 #include <linux/string.h>
18 #include <linux/sort.h>
20 #include "ecrnx_debugfs.h"
21 #include "ecrnx_msg_tx.h"
22 #include "ecrnx_radar.h"
25 #define CONFIG_ECRNX_DBGFS_FW_TRACE 0
27 #ifdef CONFIG_ECRNX_SOFTMAC
28 static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
29 char __user *user_buf,
30 size_t count, loff_t *ppos)
32 struct ecrnx_hw *priv = file->private_data;
38 int bufsz = (10 + NX_TX_PAYLOAD_MAX + NX_TXQ_CNT + IEEE80211_MAX_AMPDU_BUF) * 50;
40 buf = kmalloc(bufsz, GFP_ATOMIC);
44 if (priv->stats.agg_done)
45 per = DIV_ROUND_UP((priv->stats.agg_retries + priv->stats.agg_died) *
46 100, priv->stats.agg_done);
50 ret = scnprintf(buf, min_t(size_t, bufsz - 1, count),
53 "agg_retries_last %10d\n"
57 "queues_stops %10d\n\n",
59 priv->stats.agg_retries,
60 priv->stats.agg_retries_last,
62 priv->stats.ampdu_all_ko,
64 priv->stats.queues_stops);
66 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
67 "TXQs CFM balances ");
68 for (i = 0; i < NX_TXQ_CNT; i++)
69 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
71 priv->stats.cfm_balance[i]);
73 #ifdef CONFIG_ECRNX_SPLIT_TX_BUF
74 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
75 "\n\nAMSDU[len] done failed(%%)\n");
76 for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
77 if (priv->stats.amsdus[i].done) {
78 per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
79 100, priv->stats.amsdus[i].done);
86 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
87 " * * * %10d %10d\n", 0, 0);
91 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
92 " [%1d] %10d %10d\n", i + 1,
93 priv->stats.amsdus[i].done, per);
96 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
97 " * * * %10d %10d\n", 0, 0);
100 ret += scnprintf(&buf[ret], min_t(size_t, bufsz - ret - 1, count - ret),
101 "\nIn-AMPDU TX failures(%%) RX counts\n");
102 for (i = skipped = 0; i < IEEE80211_MAX_AMPDU_BUF; i++) {
105 if (priv->stats.in_ampdu[i].done) {
106 failed = DIV_ROUND_UP(priv->stats.in_ampdu[i].failed *
107 100, priv->stats.in_ampdu[i].done);
109 if (!priv->stats.rx_in_ampdu[i].cnt) {
116 ret += scnprintf(&buf[ret],
117 min_t(size_t, bufsz - ret - 1, count - ret),
118 " * * * %10d %10d\n", 0, 0);
121 ret += scnprintf(&buf[ret],
122 min_t(size_t, bufsz - ret - 1, count - ret),
123 " mpdu#%2d %10d %10d\n", i, failed,
124 priv->stats.rx_in_ampdu[i].cnt);
128 ret += scnprintf(&buf[ret],
129 min_t(size_t, bufsz - ret - 1, count - ret),
130 " * * * %10d %10d\n", 0, 0);
132 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
141 static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
142 char __user *user_buf,
143 size_t count, loff_t *ppos)
145 struct ecrnx_hw *priv = file->private_data;
150 int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
151 + (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
156 buf = kmalloc(bufsz, GFP_ATOMIC);
160 ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
161 for (i = 0; i < NX_TXQ_CNT; i++)
162 ret += scnprintf(&buf[ret], bufsz - ret,
164 priv->stats.cfm_balance[i]);
166 ret += scnprintf(&buf[ret], bufsz - ret, "\n");
168 #ifdef CONFIG_ECRNX_SPLIT_TX_BUF
170 ret += scnprintf(&buf[ret], bufsz - ret,
171 "\nAMSDU[len] done failed received\n");
172 for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
173 if (priv->stats.amsdus[i].done) {
174 per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
175 100, priv->stats.amsdus[i].done);
176 } else if (priv->stats.amsdus_rx[i]) {
184 ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
188 ret += scnprintf(&buf[ret], bufsz - ret,
189 " [%2d] %10d %8d(%3d%%) %10d\n", i ? i + 1 : i,
190 priv->stats.amsdus[i].done,
191 priv->stats.amsdus[i].failed, per,
192 priv->stats.amsdus_rx[i]);
195 for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
196 if (!priv->stats.amsdus_rx[i]) {
201 ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
205 ret += scnprintf(&buf[ret], bufsz - ret,
207 i + 1, priv->stats.amsdus_rx[i]);
210 ret += scnprintf(&buf[ret], bufsz - ret,
211 "\nAMSDU[len] received\n");
212 for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
213 if (!priv->stats.amsdus_rx[i]) {
218 ret += scnprintf(&buf[ret], bufsz - ret,
223 ret += scnprintf(&buf[ret], bufsz - ret,
225 i + 1, priv->stats.amsdus_rx[i]);
228 #endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
230 ret += scnprintf(&buf[ret], bufsz - ret,
231 "\nAMPDU[len] done received\n");
232 for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
233 if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
238 ret += scnprintf(&buf[ret], bufsz - ret,
243 ret += scnprintf(&buf[ret], bufsz - ret,
244 " [%2d] %9d %9d\n", i ? i + 1 : i,
245 priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
248 ret += scnprintf(&buf[ret], bufsz - ret,
249 "#mpdu missed %9d\n",
250 priv->stats.ampdus_rx_miss);
252 ret += scnprintf(&buf[ret], bufsz - ret,
253 "\nmsg_tx:%d,%d; data_tx:%d,%d\n",
254 priv->msg_tx, priv->msg_tx_done, priv->data_tx, priv->data_tx_done);
255 ret += scnprintf(&buf[ret], bufsz - ret,
256 "usb_rx:%d, data_rx:%d, msg_rx:%d\n",
257 priv->usb_rx, priv->data_rx, priv->msg_rx);
258 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
264 #endif /* CONFIG_ECRNX_SOFTMAC */
266 static ssize_t ecrnx_dbgfs_stats_write(struct file *file,
267 const char __user *user_buf,
268 size_t count, loff_t *ppos)
270 struct ecrnx_hw *priv = file->private_data;
272 /* Prevent from interrupt preemption as these statistics are updated under
274 spin_lock_bh(&priv->tx_lock);
276 memset(&priv->stats, 0, sizeof(priv->stats));
278 spin_unlock_bh(&priv->tx_lock);
283 DEBUGFS_READ_WRITE_FILE_OPS(stats);
285 #define TXQ_STA_PREF "tid|"
286 #define TXQ_STA_PREF_FMT "%3d|"
288 #ifdef CONFIG_ECRNX_FULLMAC
289 #define TXQ_VIF_PREF "type|"
290 #define TXQ_VIF_PREF_FMT "%4s|"
292 #define TXQ_VIF_PREF "AC|"
293 #define TXQ_VIF_PREF_FMT "%2s|"
294 #endif /* CONFIG_ECRNX_FULLMAC */
296 #define TXQ_HDR "idx| status|credit|ready|retry|pushed"
297 #define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s%s|%6d|%5d|%5d|%6d"
299 #ifdef CONFIG_ECRNX_AMSDUS_TX
300 #ifdef CONFIG_ECRNX_FULLMAC
301 #define TXQ_HDR_SUFF "|amsdu"
302 #define TXQ_HDR_SUFF_FMT "|%5d"
304 #define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
305 #define TXQ_HDR_SUFF_FMT "|%8d|%9d"
306 #endif /* CONFIG_ECRNX_FULLMAC */
308 #define TXQ_HDR_SUFF ""
309 #define TXQ_HDR_SUF_FMT ""
310 #endif /* CONFIG_ECRNX_AMSDUS_TX */
312 #define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
314 #ifdef CONFIG_ECRNX_FULLMAC
315 #define PS_HDR "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
316 #define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
317 #define PS_HDR_UAPSD "UAPSD: ready=%d, sp=%d"
318 #define PS_HDR_MAX_LEN sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
321 #define PS_HDR_MAX_LEN 0
322 #endif /* CONFIG_ECRNX_FULLMAC */
324 #define STA_HDR "** STA %d (%pM)\n"
325 #define STA_HDR_MAX_LEN sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN
327 #ifdef CONFIG_ECRNX_FULLMAC
328 #define VIF_HDR "* VIF [%d] %s\n"
329 #define VIF_HDR_MAX_LEN sizeof(VIF_HDR) + IFNAMSIZ
331 #define VIF_HDR "* VIF [%d]\n"
332 #define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
336 #ifdef CONFIG_ECRNX_AMSDUS_TX
338 #ifdef CONFIG_ECRNX_FULLMAC
339 #define VIF_SEP "---------------------------------------\n"
341 #define VIF_SEP "----------------------------------------------------\n"
342 #endif /* CONFIG_ECRNX_FULLMAC */
344 #else /* ! CONFIG_ECRNX_AMSDUS_TX */
345 #define VIF_SEP "---------------------------------\n"
346 #endif /* CONFIG_ECRNX_AMSDUS_TX*/
348 #define VIF_SEP_LEN sizeof(VIF_SEP)
350 #define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS,\
351 C=stop channel, S=stop CSA, M=stop MU, N=Ndev queue stopped"
352 #define CAPTION_LEN sizeof(CAPTION)
357 static int ecrnx_dbgfs_txq(char *buf, size_t size, struct ecrnx_txq *txq, int type, int tid, char *name)
362 if (type == STA_TXQ) {
363 res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
367 res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
372 for (i = 0; i < CONFIG_USER_MAX; i++) {
373 pushed += txq->pkt_pushed[i];
376 res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
377 (txq->status & ECRNX_TXQ_IN_HWQ_LIST) ? "L" : " ",
378 (txq->status & ECRNX_TXQ_STOP_FULL) ? "F" : " ",
379 (txq->status & ECRNX_TXQ_STOP_STA_PS) ? "P" : " ",
380 (txq->status & ECRNX_TXQ_STOP_VIF_PS) ? "V" : " ",
381 (txq->status & ECRNX_TXQ_STOP_CHAN) ? "C" : " ",
382 (txq->status & ECRNX_TXQ_STOP_CSA) ? "S" : " ",
383 (txq->status & ECRNX_TXQ_STOP_MU_POS) ? "M" : " ",
384 (txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) ? "N" : " ",
385 txq->credits, skb_queue_len(&txq->sk_list),
386 txq->nb_retry, pushed);
390 #ifdef CONFIG_ECRNX_AMSDUS_TX
391 if (type == STA_TXQ) {
392 res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
393 #ifdef CONFIG_ECRNX_FULLMAC
396 txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
397 #endif /* CONFIG_ECRNX_FULLMAC */
404 res = scnprintf(&buf[idx], size, "\n");
411 static int ecrnx_dbgfs_txq_sta(char *buf, size_t size, struct ecrnx_sta *ecrnx_sta,
412 struct ecrnx_hw *ecrnx_hw)
414 int tid, res, idx = 0;
415 struct ecrnx_txq *txq;
416 #ifdef CONFIG_ECRNX_SOFTMAC
417 struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
418 #endif /* CONFIG_ECRNX_SOFTMAC */
420 res = scnprintf(&buf[idx], size, "\n" STA_HDR,
422 #ifdef CONFIG_ECRNX_SOFTMAC
426 #endif /* CONFIG_ECRNX_SOFTMAC */
431 #ifdef CONFIG_ECRNX_FULLMAC
432 if (ecrnx_sta->ps.active) {
433 if (ecrnx_sta->uapsd_tids &&
434 (ecrnx_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
435 res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
436 ecrnx_sta->ps.pkt_ready[UAPSD_ID],
437 ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
438 else if (ecrnx_sta->uapsd_tids)
439 res = scnprintf(&buf[idx], size, PS_HDR "\n",
440 ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
441 ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
442 ecrnx_sta->ps.pkt_ready[UAPSD_ID],
443 ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
445 res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
446 ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
447 ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
451 res = scnprintf(&buf[idx], size, "\n");
455 #endif /* CONFIG_ECRNX_FULLMAC */
458 res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
463 foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
464 res = ecrnx_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
472 static int ecrnx_dbgfs_txq_vif(char *buf, size_t size, struct ecrnx_vif *ecrnx_vif,
473 struct ecrnx_hw *ecrnx_hw)
476 struct ecrnx_txq *txq;
477 struct ecrnx_sta *ecrnx_sta;
479 #ifdef CONFIG_ECRNX_FULLMAC
480 res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index, ecrnx_vif->ndev->name);
483 if (!ecrnx_vif->up || ecrnx_vif->ndev == NULL)
488 char ac_name[2] = {'0', '\0'};
490 res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index);
493 #endif /* CONFIG_ECRNX_FULLMAC */
495 #ifdef CONFIG_ECRNX_FULLMAC
496 if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP ||
497 ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO ||
498 ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) {
499 res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
502 txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
503 res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
506 txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
507 res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
510 ecrnx_sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
511 if (ecrnx_sta->ps.active) {
512 res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
513 ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
514 ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
518 res = scnprintf(&buf[idx], size, "\n");
523 list_for_each_entry(ecrnx_sta, &ecrnx_vif->ap.sta_list, list) {
524 res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
528 } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION ||
529 ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
530 if (ecrnx_vif->sta.ap) {
531 res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_vif->sta.ap, ecrnx_hw);
538 res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
542 foreach_vif_txq(ecrnx_vif, txq, ac) {
544 res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
549 list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
550 res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
554 #endif /* CONFIG_ECRNX_FULLMAC */
558 static ssize_t ecrnx_dbgfs_txq_read(struct file *file ,
559 char __user *user_buf,
560 size_t count, loff_t *ppos)
562 struct ecrnx_hw *ecrnx_hw = file->private_data;
563 struct ecrnx_vif *vif;
567 size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
568 (NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
569 ((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
570 TXQ_HDR_MAX_LEN) + CAPTION_LEN);
572 /* everything is read in one go */
576 bufsz = min_t(size_t, bufsz, count);
577 buf = kmalloc(bufsz, GFP_ATOMIC);
584 res = scnprintf(&buf[idx], bufsz, CAPTION);
588 //spin_lock_bh(&ecrnx_hw->tx_lock);
589 list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
590 res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
593 res = ecrnx_dbgfs_txq_vif(&buf[idx], bufsz, vif, ecrnx_hw);
596 res = scnprintf(&buf[idx], bufsz, VIF_SEP);
600 //spin_unlock_bh(&ecrnx_hw->tx_lock);
602 read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
607 DEBUGFS_READ_FILE_OPS(txq);
609 static ssize_t ecrnx_dbgfs_acsinfo_read(struct file *file,
610 char __user *user_buf,
611 size_t count, loff_t *ppos)
613 struct ecrnx_hw *priv = file->private_data;
614 #ifdef CONFIG_ECRNX_SOFTMAC
615 struct wiphy *wiphy = priv->hw->wiphy;
616 #else //CONFIG_ECRNX_SOFTMAC
617 struct wiphy *wiphy = priv->wiphy;
618 #endif //CONFIG_ECRNX_SOFTMAC
620 char *buf = kmalloc((SCAN_CHANNEL_MAX + 1) * 43, GFP_ATOMIC);
622 //char buf[(SCAN_CHANNEL_MAX + 1) * 43];
631 mutex_lock(&priv->dbgdump_elem.mutex);
633 len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
634 "FREQ TIME(ms) BUSY(ms) NOISE(dBm)\n");
636 #ifdef CONFIG_ECRNX_5G
637 for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
639 for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_2GHZ; band++) {
641 for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
642 struct ecrnx_survey_info *p_survey_info = &priv->survey[survey_cnt];
643 struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
645 if (p_survey_info->filled) {
646 len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
649 p_survey_info->chan_time_ms,
650 p_survey_info->chan_time_busy_ms,
651 p_survey_info->noise_dbm);
653 len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) -len -1, count),
654 "%d NOT AVAILABLE\n",
655 p_chan->center_freq);
662 mutex_unlock(&priv->dbgdump_elem.mutex);
663 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
669 DEBUGFS_READ_FILE_OPS(acsinfo);
671 static ssize_t ecrnx_dbgfs_fw_dbg_read(struct file *file,
672 char __user *user_buf,
673 size_t count, loff_t *ppos)
675 char help[]="usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
676 "[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
678 return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
682 static ssize_t ecrnx_dbgfs_fw_dbg_write(struct file *file,
683 const char __user *user_buf,
684 size_t count, loff_t *ppos)
686 struct ecrnx_hw *priv = file->private_data;
690 size_t len = min_t(size_t, count, sizeof(buf) - 1);
692 if (copy_from_user(buf, user_buf, len))
696 #define ECRNX_MOD_TOKEN(str, val) \
697 if (strncmp(&buf[idx], str, sizeof(str) - 1 ) == 0) { \
698 idx += sizeof(str) - 1; \
703 #define ECRNX_DBG_TOKEN(str, val) \
704 if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) { \
705 idx += sizeof(str) - 1; \
710 while ((idx + 4) < len) {
711 if (strncmp(&buf[idx], "MOD:", 4) == 0) {
713 ECRNX_MOD_TOKEN("ALL", 0xffffffff);
714 ECRNX_MOD_TOKEN("KE", BIT(0));
715 ECRNX_MOD_TOKEN("DBG", BIT(1));
716 ECRNX_MOD_TOKEN("IPC", BIT(2));
717 ECRNX_MOD_TOKEN("DMA", BIT(3));
718 ECRNX_MOD_TOKEN("MM", BIT(4));
719 ECRNX_MOD_TOKEN("TX", BIT(5));
720 ECRNX_MOD_TOKEN("RX", BIT(6));
721 ECRNX_MOD_TOKEN("PHY", BIT(7));
723 } else if (strncmp(&buf[idx], "DBG:", 4) == 0) {
726 ECRNX_DBG_TOKEN("NONE", 0);
727 ECRNX_DBG_TOKEN("CRT", 1);
728 ECRNX_DBG_TOKEN("ERR", 2);
729 ECRNX_DBG_TOKEN("WRN", 3);
730 ECRNX_DBG_TOKEN("INF", 4);
731 ECRNX_DBG_TOKEN("VRB", 5);
735 ecrnx_send_dbg_set_sev_filter_req(priv, dbg);
742 ecrnx_send_dbg_set_mod_filter_req(priv, mod);
748 DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
750 static ssize_t ecrnx_dbgfs_sys_stats_read(struct file *file,
751 char __user *user_buf,
752 size_t count, loff_t *ppos)
754 struct ecrnx_hw *priv = file->private_data;
759 struct dbg_get_sys_stat_cfm cfm;
760 u32 sleep_int, sleep_frac, doze_int, doze_frac;
762 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
764 /* Get the information from the FW */
765 if ((error = ecrnx_send_dbg_get_sys_stat_req(priv, &cfm)))
768 if (cfm.stats_time == 0)
771 sleep_int = ((cfm.cpu_sleep_time * 100) / cfm.stats_time);
772 sleep_frac = (((cfm.cpu_sleep_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
773 doze_int = ((cfm.doze_time * 100) / cfm.stats_time);
774 doze_frac = (((cfm.doze_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
776 len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
777 "\nSystem statistics:\n");
778 len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
779 " CPU sleep [%%]: %d.%d\n", sleep_int, sleep_frac);
780 len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
781 " Doze [%%]: %d.%d\n", doze_int, doze_frac);
783 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
788 DEBUGFS_READ_FILE_OPS(sys_stats);
790 #ifdef CONFIG_ECRNX_MUMIMO_TX
791 static ssize_t ecrnx_dbgfs_mu_group_read(struct file *file,
792 char __user *user_buf,
793 size_t count, loff_t *ppos)
795 struct ecrnx_hw *ecrnx_hw = file->private_data;
796 struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
797 struct ecrnx_mu_group *group;
798 size_t bufsz = NX_MU_GROUP_MAX * sizeof("xx = (xx - xx - xx - xx)\n") + 50;
805 buf = kmalloc(bufsz, GFP_ATOMIC);
809 res = scnprintf(&buf[idx], bufsz, "MU Group list (%d groups, %d users max)\n",
810 NX_MU_GROUP_MAX, CONFIG_USER_MAX);
814 list_for_each_entry(group, &mu->active_groups, list) {
815 if (group->user_cnt) {
816 res = scnprintf(&buf[idx], bufsz, "%2d = (", group->group_id);
819 for (j = 0; j < (CONFIG_USER_MAX - 1) ; j++) {
821 res = scnprintf(&buf[idx], bufsz, "%2d - ",
822 group->users[j]->sta_idx);
824 res = scnprintf(&buf[idx], bufsz, ".. - ");
831 res = scnprintf(&buf[idx], bufsz, "%2d)\n",
832 group->users[j]->sta_idx);
834 res = scnprintf(&buf[idx], bufsz, "..)\n");
841 res = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
847 DEBUGFS_READ_FILE_OPS(mu_group);
850 #ifdef CONFIG_ECRNX_P2P_DEBUGFS
851 static ssize_t ecrnx_dbgfs_oppps_write(struct file *file,
852 const char __user *user_buf,
853 size_t count, loff_t *ppos)
855 struct ecrnx_hw *rw_hw = file->private_data;
856 struct ecrnx_vif *rw_vif;
858 size_t len = min_t(size_t, count, sizeof(buf) - 1);
861 if (copy_from_user(buf, user_buf, len))
865 /* Read the written CT Window (provided in ms) value */
866 if (sscanf(buf, "ctw=%d", &ctw) > 0) {
867 /* Check if at least one VIF is configured as P2P GO */
868 list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
869 #ifdef CONFIG_ECRNX_SOFTMAC
870 if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
871 #else /* CONFIG_ECRNX_FULLMAC */
872 if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
873 #endif /* CONFIG_ECRNX_SOFTMAC */
874 struct mm_set_p2p_oppps_cfm cfm;
876 /* Forward request to the embedded and wait for confirmation */
877 ecrnx_send_p2p_oppps_req(rw_hw, rw_vif, (u8)ctw, &cfm);
887 DEBUGFS_WRITE_FILE_OPS(oppps);
889 static ssize_t ecrnx_dbgfs_noa_write(struct file *file,
890 const char __user *user_buf,
891 size_t count, loff_t *ppos)
893 struct ecrnx_hw *rw_hw = file->private_data;
894 struct ecrnx_vif *rw_vif;
896 size_t len = min_t(size_t, count, sizeof(buf) - 1);
897 int noa_count, interval, duration, dyn_noa;
899 if (copy_from_user(buf, user_buf, len))
903 /* Read the written NOA information */
904 if (sscanf(buf, "count=%d interval=%d duration=%d dyn=%d",
905 &noa_count, &interval, &duration, &dyn_noa) > 0) {
906 /* Check if at least one VIF is configured as P2P GO */
907 list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
908 #ifdef CONFIG_ECRNX_SOFTMAC
909 if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
910 #else /* CONFIG_ECRNX_FULLMAC */
911 if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
912 #endif /* CONFIG_ECRNX_SOFTMAC */
913 struct mm_set_p2p_noa_cfm cfm;
915 /* Forward request to the embedded and wait for confirmation */
916 ecrnx_send_p2p_noa_req(rw_hw, rw_vif, noa_count, interval,
917 duration, (dyn_noa > 0), &cfm);
927 DEBUGFS_WRITE_FILE_OPS(noa);
928 #endif /* CONFIG_ECRNX_P2P_DEBUGFS */
930 struct ecrnx_dbgfs_fw_trace {
931 struct ecrnx_fw_trace_local_buf lbuf;
932 struct ecrnx_fw_trace *trace;
933 struct ecrnx_hw *ecrnx_hw;
936 static int ecrnx_dbgfs_fw_trace_open(struct inode *inode, struct file *file)
938 struct ecrnx_dbgfs_fw_trace *ltrace = kmalloc(sizeof(*ltrace), GFP_KERNEL);
939 struct ecrnx_hw *priv = inode->i_private;
944 if (ecrnx_fw_trace_alloc_local(<race->lbuf, 5120)) {
948 ltrace->trace = &priv->debugfs.fw_trace;
949 ltrace->ecrnx_hw = priv;
950 file->private_data = ltrace;
954 static int ecrnx_dbgfs_fw_trace_release(struct inode *inode, struct file *file)
956 struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
959 ecrnx_fw_trace_free_local(<race->lbuf);
966 static ssize_t ecrnx_dbgfs_fw_trace_read(struct file *file,
967 char __user *user_buf,
968 size_t count, loff_t *ppos)
970 struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
971 bool dont_wait = ((file->f_flags & O_NONBLOCK) ||
972 ltrace->ecrnx_hw->debugfs.unregistering);
974 return ecrnx_fw_trace_read(ltrace->trace, <race->lbuf,
975 dont_wait, user_buf, count);
978 static ssize_t ecrnx_dbgfs_fw_trace_write(struct file *file,
979 const char __user *user_buf,
980 size_t count, loff_t *ppos)
982 struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
985 ret = _ecrnx_fw_trace_reset(ltrace->trace, true);
992 DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(fw_trace);
994 static ssize_t ecrnx_dbgfs_fw_trace_level_read(struct file *file,
995 char __user *user_buf,
996 size_t count, loff_t *ppos)
998 struct ecrnx_hw *priv = file->private_data;
999 return ecrnx_fw_trace_level_read(&priv->debugfs.fw_trace, user_buf,
1003 static ssize_t ecrnx_dbgfs_fw_trace_level_write(struct file *file,
1004 const char __user *user_buf,
1005 size_t count, loff_t *ppos)
1007 struct ecrnx_hw *priv = file->private_data;
1008 return ecrnx_fw_trace_level_write(&priv->debugfs.fw_trace, user_buf, count);
1010 DEBUGFS_READ_WRITE_FILE_OPS(fw_trace_level);
1013 #ifdef CONFIG_ECRNX_RADAR
1014 static ssize_t ecrnx_dbgfs_pulses_read(struct file *file,
1015 char __user *user_buf,
1016 size_t count, loff_t *ppos,
1019 struct ecrnx_hw *priv = file->private_data;
1025 struct ecrnx_radar_pulses *p = &priv->radar.pulses[rd_idx];
1031 /* Prevent from interrupt preemption */
1032 spin_lock_bh(&priv->radar.lock);
1033 bufsz = p->count * 34 + 51;
1034 bufsz += ecrnx_radar_dump_pattern_detector(NULL, 0, &priv->radar, rd_idx);
1035 buf = kmalloc(bufsz, GFP_ATOMIC);
1037 spin_unlock_bh(&priv->radar.lock);
1042 len += scnprintf(&buf[len], bufsz - len,
1043 " PRI WIDTH FOM FREQ\n");
1045 for (i = 0; i < p->count; i++) {
1046 struct radar_pulse *pulse;
1051 index = ECRNX_RADAR_PULSE_MAX - 1;
1053 pulse = (struct radar_pulse *) &p->buffer[index];
1055 len += scnprintf(&buf[len], bufsz - len,
1056 "%05dus %03dus %2d%% %+3dMHz\n", pulse->rep,
1057 2 * pulse->len, 6 * pulse->fom, 2*pulse->freq);
1061 len += ecrnx_radar_dump_pattern_detector(&buf[len], bufsz - len,
1062 &priv->radar, rd_idx);
1064 spin_unlock_bh(&priv->radar.lock);
1066 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1073 static ssize_t ecrnx_dbgfs_pulses_prim_read(struct file *file,
1074 char __user *user_buf,
1075 size_t count, loff_t *ppos)
1077 return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 0);
1080 DEBUGFS_READ_FILE_OPS(pulses_prim);
1082 static ssize_t ecrnx_dbgfs_pulses_sec_read(struct file *file,
1083 char __user *user_buf,
1084 size_t count, loff_t *ppos)
1086 return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 1);
1089 DEBUGFS_READ_FILE_OPS(pulses_sec);
1091 static ssize_t ecrnx_dbgfs_detected_read(struct file *file,
1092 char __user *user_buf,
1093 size_t count, loff_t *ppos)
1095 struct ecrnx_hw *priv = file->private_data;
1103 bufsz = 5; // RIU:\n
1104 bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
1107 if (priv->phy.cnt > 1) {
1108 bufsz += 5; // FCU:\n
1109 bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
1113 buf = kmalloc(bufsz, GFP_KERNEL);
1118 len = scnprintf(&buf[len], bufsz, "RIU:\n");
1119 len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len, &priv->radar,
1122 if (priv->phy.cnt > 1) {
1123 len += scnprintf(&buf[len], bufsz - len, "FCU:\n");
1124 len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len,
1125 &priv->radar, ECRNX_RADAR_FCU);
1128 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1135 DEBUGFS_READ_FILE_OPS(detected);
1137 static ssize_t ecrnx_dbgfs_enable_read(struct file *file,
1138 char __user *user_buf,
1139 size_t count, loff_t *ppos)
1141 struct ecrnx_hw *priv = file->private_data;
1146 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1147 "RIU=%d FCU=%d\n", priv->radar.dpd[ECRNX_RADAR_RIU]->enabled,
1148 priv->radar.dpd[ECRNX_RADAR_FCU]->enabled);
1150 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1155 static ssize_t ecrnx_dbgfs_enable_write(struct file *file,
1156 const char __user *user_buf,
1157 size_t count, loff_t *ppos)
1159 struct ecrnx_hw *priv = file->private_data;
1162 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1164 if (copy_from_user(buf, user_buf, len))
1169 if (sscanf(buf, "RIU=%d", &val) > 0)
1170 ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_RIU);
1172 if (sscanf(buf, "FCU=%d", &val) > 0)
1173 ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_FCU);
1178 DEBUGFS_READ_WRITE_FILE_OPS(enable);
1180 static ssize_t ecrnx_dbgfs_band_read(struct file *file,
1181 char __user *user_buf,
1182 size_t count, loff_t *ppos)
1184 struct ecrnx_hw *priv = file->private_data;
1189 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1190 "BAND=%d\n", priv->phy.sec_chan.band);
1192 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1197 static ssize_t ecrnx_dbgfs_band_write(struct file *file,
1198 const char __user *user_buf,
1199 size_t count, loff_t *ppos)
1201 struct ecrnx_hw *priv = file->private_data;
1204 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1206 if (copy_from_user(buf, user_buf, len))
1211 #ifdef CONFIG_ECRNX_5G
1212 if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_5GHZ))
1214 if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_2GHZ))
1216 priv->phy.sec_chan.band = val;
1221 DEBUGFS_READ_WRITE_FILE_OPS(band);
1223 static ssize_t ecrnx_dbgfs_type_read(struct file *file,
1224 char __user *user_buf,
1225 size_t count, loff_t *ppos)
1227 struct ecrnx_hw *priv = file->private_data;
1232 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1233 "TYPE=%d\n", priv->phy.sec_chan.type);
1235 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1240 static ssize_t ecrnx_dbgfs_type_write(struct file *file,
1241 const char __user *user_buf,
1242 size_t count, loff_t *ppos)
1244 struct ecrnx_hw *priv = file->private_data;
1247 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1249 if (copy_from_user(buf, user_buf, len))
1254 if ((sscanf(buf, "%d", &val) > 0) && (val >= PHY_CHNL_BW_20) &&
1255 (val <= PHY_CHNL_BW_80P80))
1256 priv->phy.sec_chan.type = val;
1261 DEBUGFS_READ_WRITE_FILE_OPS(type);
1263 static ssize_t ecrnx_dbgfs_prim20_read(struct file *file,
1264 char __user *user_buf,
1265 size_t count, loff_t *ppos)
1267 struct ecrnx_hw *priv = file->private_data;
1272 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1273 "PRIM20=%dMHz\n", priv->phy.sec_chan.prim20_freq);
1275 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1280 static ssize_t ecrnx_dbgfs_prim20_write(struct file *file,
1281 const char __user *user_buf,
1282 size_t count, loff_t *ppos)
1284 struct ecrnx_hw *priv = file->private_data;
1287 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1289 if (copy_from_user(buf, user_buf, len))
1294 if (sscanf(buf, "%d", &val) > 0)
1295 priv->phy.sec_chan.prim20_freq = val;
1300 DEBUGFS_READ_WRITE_FILE_OPS(prim20);
1302 static ssize_t ecrnx_dbgfs_center1_read(struct file *file,
1303 char __user *user_buf,
1304 size_t count, loff_t *ppos)
1306 struct ecrnx_hw *priv = file->private_data;
1311 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1312 "CENTER1=%dMHz\n", priv->phy.sec_chan.center1_freq);
1314 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1319 static ssize_t ecrnx_dbgfs_center1_write(struct file *file,
1320 const char __user *user_buf,
1321 size_t count, loff_t *ppos)
1323 struct ecrnx_hw *priv = file->private_data;
1326 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1328 if (copy_from_user(buf, user_buf, len))
1333 if (sscanf(buf, "%d", &val) > 0)
1334 priv->phy.sec_chan.center1_freq = val;
1339 DEBUGFS_READ_WRITE_FILE_OPS(center1);
1341 static ssize_t ecrnx_dbgfs_center2_read(struct file *file,
1342 char __user *user_buf,
1343 size_t count, loff_t *ppos)
1345 struct ecrnx_hw *priv = file->private_data;
1350 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1351 "CENTER2=%dMHz\n", priv->phy.sec_chan.center2_freq);
1353 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1358 static ssize_t ecrnx_dbgfs_center2_write(struct file *file,
1359 const char __user *user_buf,
1360 size_t count, loff_t *ppos)
1362 struct ecrnx_hw *priv = file->private_data;
1365 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1367 if (copy_from_user(buf, user_buf, len))
1372 if (sscanf(buf, "%d", &val) > 0)
1373 priv->phy.sec_chan.center2_freq = val;
1378 DEBUGFS_READ_WRITE_FILE_OPS(center2);
1381 static ssize_t ecrnx_dbgfs_set_read(struct file *file,
1382 char __user *user_buf,
1383 size_t count, loff_t *ppos)
1388 static ssize_t ecrnx_dbgfs_set_write(struct file *file,
1389 const char __user *user_buf,
1390 size_t count, loff_t *ppos)
1392 struct ecrnx_hw *priv = file->private_data;
1394 ecrnx_send_set_channel(priv, 1, NULL);
1395 ecrnx_radar_detection_enable(&priv->radar, ECRNX_RADAR_DETECT_ENABLE,
1401 DEBUGFS_READ_WRITE_FILE_OPS(set);
1402 #endif /* CONFIG_ECRNX_RADAR */
1404 #ifdef CONFIG_ECRNX_FULLMAC
1406 #define LINE_MAX_SZ 150
1409 char line[LINE_MAX_SZ + 1];
1413 static int compare_idx(const void *st1, const void *st2)
1415 int index1 = ((struct st *)st1)->r_idx;
1416 int index2 = ((struct st *)st2)->r_idx;
1418 if (index1 > index2) return 1;
1419 if (index1 < index2) return -1;
1424 static const int ru_size[] =
1434 static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
1435 int sgi, int pre, int dcm, int *r_idx)
1438 int bitrates_cck[4] = { 10, 20, 55, 110 };
1439 int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
1440 char he_gi[3][4] = {"0.8", "1.6", "3.2"};
1442 if (format < FORMATMOD_HT_MF) {
1445 *r_idx = (mcs * 2) + pre;
1446 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1448 res += scnprintf(&buf[res], size - res, "L-CCK/%cP %2u.%1uM ",
1449 pre > 0 ? 'L' : 'S',
1450 bitrates_cck[mcs] / 10,
1451 bitrates_cck[mcs] % 10);
1455 *r_idx = N_CCK + mcs;
1456 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1458 res += scnprintf(&buf[res], size - res, "L-OFDM %2u.0M ",
1459 bitrates_ofdm[mcs]);
1461 } else if (format < FORMATMOD_VHT) {
1463 *r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
1464 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1467 res += scnprintf(&buf[res], size - res, "HT%d/%cGI MCS%-2d ",
1468 20 * (1 << bw), sgi ? 'S' : 'L', mcs);
1469 } else if (format == FORMATMOD_VHT){
1471 *r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
1472 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1474 res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d ",
1475 20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 9 : 10, ' ',
1477 } else if (format == FORMATMOD_HE_SU){
1479 *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
1480 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1482 res += scnprintf(&buf[res], size - res, "HE%d/GI%s%4s%*cMCS%d/%1d%*c",
1483 20 * (1 << bw), he_gi[sgi], dcm ? "/DCM" : "",
1484 bw > 2 ? 4 : 5, ' ', mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
1487 *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU + nss * 216 + mcs * 18 + bw * 3 + sgi;
1488 res = scnprintf(buf, size - res, "%3d ", *r_idx);
1490 res += scnprintf(&buf[res], size - res, "HEMU-%d/GI%s%*cMCS%d/%1d%*c",
1491 ru_size[bw], he_gi[sgi], bw > 1 ? 1 : 2, ' ',
1492 mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
1499 static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx, int ru_size)
1501 union ecrnx_rate_ctrl_info *r_cfg = (union ecrnx_rate_ctrl_info *)&rate_config;
1502 union ecrnx_mcs_index *mcs_index = (union ecrnx_mcs_index *)&rate_config;
1503 unsigned int ft, pre, gi, bw, nss, mcs, dcm, len;
1505 ft = r_cfg->formatModTx;
1506 pre = r_cfg->giAndPreTypeTx >> 1;
1507 gi = r_cfg->giAndPreTypeTx;
1510 if (ft == FORMATMOD_HE_MU) {
1511 mcs = mcs_index->he.mcs;
1512 nss = mcs_index->he.nss;
1515 } else if (ft == FORMATMOD_HE_SU) {
1516 mcs = mcs_index->he.mcs;
1517 nss = mcs_index->he.nss;
1519 } else if (ft == FORMATMOD_VHT) {
1520 mcs = mcs_index->vht.mcs;
1521 nss = mcs_index->vht.nss;
1522 } else if (ft >= FORMATMOD_HT_MF) {
1523 mcs = mcs_index->ht.mcs;
1524 nss = mcs_index->ht.nss;
1526 mcs = mcs_index->legacy;
1530 len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, dcm, r_idx);
1534 static void idx_to_rate_cfg(int idx, union ecrnx_rate_ctrl_info *r_cfg, int *ru_size)
1539 r_cfg->formatModTx = FORMATMOD_NON_HT;
1540 r_cfg->giAndPreTypeTx = (idx & 1) << 1;
1541 r_cfg->mcsIndexTx = idx / 2;
1543 else if (idx < (N_CCK + N_OFDM))
1545 r_cfg->formatModTx = FORMATMOD_NON_HT;
1546 r_cfg->mcsIndexTx = idx - N_CCK + 4;
1548 else if (idx < (N_CCK + N_OFDM + N_HT))
1550 union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
1552 idx -= (N_CCK + N_OFDM);
1553 r_cfg->formatModTx = FORMATMOD_HT_MF;
1554 r->ht.nss = idx / (8*2*2);
1555 r->ht.mcs = (idx % (8*2*2)) / (2*2);
1556 r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
1557 r_cfg->giAndPreTypeTx = idx & 1;
1559 else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT))
1561 union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
1563 idx -= (N_CCK + N_OFDM + N_HT);
1564 r_cfg->formatModTx = FORMATMOD_VHT;
1565 r->vht.nss = idx / (10*4*2);
1566 r->vht.mcs = (idx % (10*4*2)) / (4*2);
1567 r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
1568 r_cfg->giAndPreTypeTx = idx & 1;
1570 else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU))
1572 union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
1574 idx -= (N_CCK + N_OFDM + N_HT + N_VHT);
1575 r_cfg->formatModTx = FORMATMOD_HE_SU;
1576 r->vht.nss = idx / (12*4*3);
1577 r->vht.mcs = (idx % (12*4*3)) / (4*3);
1578 r_cfg->bwTx = ((idx % (12*4*3)) % (4*3)) / 3;
1579 r_cfg->giAndPreTypeTx = idx % 3;
1583 union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
1585 BUG_ON(ru_size == NULL);
1587 idx -= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU);
1588 r_cfg->formatModTx = FORMATMOD_HE_MU;
1589 r->vht.nss = idx / (12*6*3);
1590 r->vht.mcs = (idx % (12*6*3)) / (6*3);
1591 *ru_size = ((idx % (12*6*3)) % (6*3)) / 3;
1592 r_cfg->giAndPreTypeTx = idx % 3;
1597 static struct ecrnx_sta* ecrnx_dbgfs_get_sta(struct ecrnx_hw *ecrnx_hw,
1602 if (sscanf(mac_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
1603 &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6)
1605 return ecrnx_get_sta(ecrnx_hw, mac);
1608 static ssize_t ecrnx_dbgfs_twt_request_read(struct file *file,
1609 char __user *user_buf,
1615 struct ecrnx_hw *priv = file->private_data;
1616 struct ecrnx_sta *sta = NULL;
1619 /* Get the station index from MAC address */
1620 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
1623 if (sta->twt_ind.sta_idx != ECRNX_INVALID_STA)
1625 struct twt_conf_tag *conf = &sta->twt_ind.conf;
1626 if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ACCEPT)
1627 len = scnprintf(buf, sizeof(buf) - 1, "Accepted configuration");
1628 else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ALTERNATE)
1629 len = scnprintf(buf, sizeof(buf) - 1, "Alternate configuration proposed by AP");
1630 else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_DICTATE)
1631 len = scnprintf(buf, sizeof(buf) - 1, "AP dictates the following configuration");
1632 else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_REJECT)
1633 len = scnprintf(buf, sizeof(buf) - 1, "AP rejects the following configuration");
1636 len = scnprintf(buf, sizeof(buf) - 1, "Invalid response from the peer");
1639 len += scnprintf(&buf[len], sizeof(buf) - 1 - len,":\n"
1641 "wake interval mantissa = %d\n"
1642 "wake interval exponent = %d\n"
1643 "wake interval = %d us\n"
1644 "nominal minimum wake duration = %d us\n",
1645 conf->flow_type, conf->wake_int_mantissa,
1647 conf->wake_int_mantissa << conf->wake_int_exp,
1648 conf->wake_dur_unit ?
1649 conf->min_twt_wake_dur * 1024:
1650 conf->min_twt_wake_dur * 256);
1654 len = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1655 "setup_command = <0: request, 1: suggest, 2: demand>,"
1656 "flow_type = <0: announced, 1: unannounced>,"
1657 "wake_interval_mantissa = <0 if setup request and no constraints>,"
1658 "wake_interval_exp = <0 if setup request and no constraints>,"
1659 "nominal_min_wake_dur = <0 if setup request and no constraints>,"
1660 "wake_dur_unit = <0: 256us, 1: tu>");
1663 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1667 static ssize_t ecrnx_dbgfs_twt_request_write(struct file *file,
1668 const char __user *user_buf,
1672 char *accepted_params[] = {"setup_command",
1674 "wake_interval_mantissa",
1675 "wake_interval_exp",
1676 "nominal_min_wake_dur",
1680 struct twt_conf_tag twt_conf;
1681 struct twt_setup_cfm twt_setup_cfm;
1682 struct ecrnx_sta *sta = NULL;
1683 struct ecrnx_hw *priv = file->private_data;
1686 int error = 1, i, val, setup_command = -1;
1688 char *buf = kmalloc(1024, GFP_ATOMIC);
1689 size_t len = 1024 - 1;
1695 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
1696 /* Get the station index from MAC address */
1697 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
1703 /* Get the content of the file */
1704 if (copy_from_user(buf, user_buf, len)){
1710 memset(&twt_conf, 0, sizeof(twt_conf));
1713 /* Get the content of the file */
1714 while (line != NULL)
1716 if (sscanf(line, "%s = %d", param, &val) == 2)
1720 // Check if parameter is valid
1721 while(accepted_params[i])
1723 if (strcmp(accepted_params[i], param) == 0)
1733 dev_err(priv->dev, "%s: parameter %s is not valid\n", __func__, param);
1738 if (!strcmp(param, "setup_command"))
1740 setup_command = val;
1742 else if (!strcmp(param, "flow_type"))
1744 twt_conf.flow_type = val;
1746 else if (!strcmp(param, "wake_interval_mantissa"))
1748 twt_conf.wake_int_mantissa = val;
1750 else if (!strcmp(param, "wake_interval_exp"))
1752 twt_conf.wake_int_exp = val;
1754 else if (!strcmp(param, "nominal_min_wake_dur"))
1756 twt_conf.min_twt_wake_dur = val;
1758 else if (!strcmp(param, "wake_dur_unit"))
1760 twt_conf.wake_dur_unit = val;
1765 dev_err(priv->dev, "%s: Impossible to read TWT configuration option\n", __func__);
1769 line = strchr(line, ',');
1775 if (setup_command == -1)
1777 dev_err(priv->dev, "%s: TWT missing setup command\n", __func__);
1782 // Forward the request to the LMAC
1783 if ((error = ecrnx_send_twt_request(priv, setup_command, sta->vif_idx,
1784 &twt_conf, &twt_setup_cfm)) != 0){
1790 if (twt_setup_cfm.status != CO_OK){
1798 DEBUGFS_READ_WRITE_FILE_OPS(twt_request);
1800 static ssize_t ecrnx_dbgfs_twt_teardown_read(struct file *file,
1801 char __user *user_buf,
1810 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
1811 "TWT teardown format:\n\n"
1812 "flow_id = <ID>\n");
1813 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1818 static ssize_t ecrnx_dbgfs_twt_teardown_write(struct file *file,
1819 const char __user *user_buf,
1823 struct twt_teardown_req twt_teardown;
1824 struct twt_teardown_cfm twt_teardown_cfm;
1825 struct ecrnx_sta *sta = NULL;
1826 struct ecrnx_hw *priv = file->private_data;
1830 size_t len = min_t(size_t, count, sizeof(buf) - 1);
1832 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
1833 /* Get the station index from MAC address */
1834 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
1838 /* Get the content of the file */
1839 if (copy_from_user(buf, user_buf, len))
1843 memset(&twt_teardown, 0, sizeof(twt_teardown));
1845 /* Get the content of the file */
1848 if (sscanf(line, "flow_id = %d", (int *) &twt_teardown.id) != 1)
1850 dev_err(priv->dev, "%s: Invalid TWT configuration\n", __func__);
1854 twt_teardown.neg_type = 0;
1855 twt_teardown.all_twt = 0;
1856 twt_teardown.vif_idx = sta->vif_idx;
1858 // Forward the request to the LMAC
1859 if ((error = ecrnx_send_twt_teardown(priv, &twt_teardown, &twt_teardown_cfm)) != 0)
1863 if (twt_teardown_cfm.status != CO_OK)
1868 DEBUGFS_READ_WRITE_FILE_OPS(twt_teardown);
1870 static ssize_t ecrnx_dbgfs_rc_stats_read(struct file *file,
1871 char __user *user_buf,
1872 size_t count, loff_t *ppos)
1874 struct ecrnx_sta *sta = NULL;
1875 struct ecrnx_hw *priv = file->private_data;
1881 struct me_rc_stats_cfm me_rc_stats_cfm;
1882 unsigned int no_samples;
1885 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
1887 /* everything should fit in one call */
1891 /* Get the station index from MAC address */
1892 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
1896 /* Forward the information to the LMAC */
1897 if ((error = ecrnx_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm)))
1900 no_samples = me_rc_stats_cfm.no_samples;
1901 if (no_samples == 0)
1904 bufsz = no_samples * LINE_MAX_SZ + 500;
1906 buf = kmalloc(bufsz + 1, GFP_ATOMIC);
1910 st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
1917 for (i = 0; i < no_samples; i++)
1919 unsigned int tp, eprob;
1920 len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
1921 me_rc_stats_cfm.rate_stats[i].rate_config,
1924 if (me_rc_stats_cfm.sw_retry_step != 0)
1926 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
1927 me_rc_stats_cfm.retry_step_idx[me_rc_stats_cfm.sw_retry_step] == i ? '*' : ' ');
1931 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
1933 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
1934 me_rc_stats_cfm.retry_step_idx[0] == i ? 'T' : ' ');
1935 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
1936 me_rc_stats_cfm.retry_step_idx[1] == i ? 't' : ' ');
1937 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
1938 me_rc_stats_cfm.retry_step_idx[2] == i ? 'P' : ' ');
1940 tp = me_rc_stats_cfm.tp[i] / 10;
1941 len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
1944 eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
1945 len += scnprintf(&st[i].line[len],LINE_MAX_SZ - len,
1946 " %4u.%1u %5u(%6u) %6u",
1947 eprob / 10, eprob % 10,
1948 me_rc_stats_cfm.rate_stats[i].success,
1949 me_rc_stats_cfm.rate_stats[i].attempts,
1950 me_rc_stats_cfm.rate_stats[i].sample_skipped);
1952 len = scnprintf(buf, bufsz ,
1953 "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
1954 sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
1955 sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
1957 len += scnprintf(&buf[len], bufsz - len,
1958 " # type rate tpt eprob ok( tot) skipped\n");
1960 // add sorted statistics to the buffer
1961 sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
1962 for (i = 0; i < no_samples; i++)
1964 len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
1967 // display HE TB statistics if any
1968 if (me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX].rate_config != 0) {
1969 unsigned int tp, eprob;
1970 struct rc_rate_stats *rate_stats = &me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX];
1971 int ru_index = rate_stats->ru_and_length & 0x07;
1972 int ul_length = rate_stats->ru_and_length >> 3;
1974 len += scnprintf(&buf[len], bufsz - len,
1975 "\nHE TB rate info:\n");
1977 len += scnprintf(&buf[len], bufsz - len,
1978 " type rate tpt eprob ok( tot) ul_length\n ");
1979 len += print_rate_from_cfg(&buf[len], bufsz - len, rate_stats->rate_config,
1982 tp = me_rc_stats_cfm.tp[RC_HE_STATS_IDX] / 10;
1983 len += scnprintf(&buf[len], bufsz - len, " %4u.%1u",
1986 eprob = ((rate_stats->probability * 1000) >> 16) + 1;
1987 len += scnprintf(&buf[len],bufsz - len,
1988 " %4u.%1u %5u(%6u) %6u\n",
1989 eprob / 10, eprob % 10,
1990 rate_stats->success,
1991 rate_stats->attempts,
1995 len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
1996 len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
1997 me_rc_stats_cfm.ampdu_len,
1998 me_rc_stats_cfm.ampdu_packets,
1999 me_rc_stats_cfm.avg_ampdu_len >> 16,
2000 ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
2001 me_rc_stats_cfm.sample_wait);
2003 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
2011 DEBUGFS_READ_FILE_OPS(rc_stats);
2013 static ssize_t ecrnx_dbgfs_rc_fixed_rate_idx_write(struct file *file,
2014 const char __user *user_buf,
2015 size_t count, loff_t *ppos)
2017 struct ecrnx_sta *sta = NULL;
2018 struct ecrnx_hw *priv = file->private_data;
2020 int fixed_rate_idx = -1;
2021 union ecrnx_rate_ctrl_info rate_config;
2023 size_t len = min_t(size_t, count, sizeof(buf) - 1);
2025 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
2027 /* Get the station index from MAC address */
2028 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
2032 /* Get the content of the file */
2033 if (copy_from_user(buf, user_buf, len))
2036 sscanf(buf, "%i\n", &fixed_rate_idx);
2038 /* Convert rate index into rate configuration */
2039 if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU)))
2041 // disable fixed rate
2042 rate_config.value = (u32)-1;
2046 idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
2049 // Forward the request to the LMAC
2050 if ((error = ecrnx_send_me_rc_set_rate(priv, sta->sta_idx,
2051 (u16)rate_config.value)) != 0)
2056 priv->debugfs.rc_config[sta->sta_idx] = (int)rate_config.value;
2060 DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
2062 static ssize_t ecrnx_dbgfs_last_rx_read(struct file *file,
2063 char __user *user_buf,
2064 size_t count, loff_t *ppos)
2066 struct ecrnx_sta *sta = NULL;
2067 struct ecrnx_hw *priv = file->private_data;
2068 struct ecrnx_rx_rate_stats *rate_stats;
2070 int bufsz, i, len = 0;
2072 unsigned int fmt, pre, bw, nss, mcs, gi, dcm = 0;
2073 struct rx_vector_1 *last_rx;
2074 char hist[] = "##################################################";
2075 int hist_len = sizeof(hist) - 1;
2078 ECRNX_DBG(ECRNX_FN_ENTRY_STR);
2080 /* everything should fit in one call */
2084 /* Get the station index from MAC address */
2085 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
2089 rate_stats = &sta->stats.rx_rate;
2090 bufsz = (rate_stats->rate_cnt * ( 50 + hist_len) + 200);
2091 buf = kmalloc(bufsz + 1, GFP_ATOMIC);
2095 // Get number of RX paths
2096 nrx = (priv->version_cfm.version_phy_1 & MDM_NRX_MASK) >> MDM_NRX_LSB;
2098 len += scnprintf(buf, bufsz,
2099 "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
2100 sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
2101 sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
2103 // Display Statistics
2104 for (i = 0 ; i < rate_stats->size ; i++ )
2106 if (rate_stats->table[i]) {
2107 union ecrnx_rate_ctrl_info rate_config;
2108 int percent = ((/*(u64)*/rate_stats->table[i]) * 1000) / rate_stats->cpt;
2112 idx_to_rate_cfg(i, &rate_config, &ru_size);
2113 len += print_rate_from_cfg(&buf[len], bufsz - len,
2114 rate_config.value, NULL, ru_size);
2115 p = (percent * hist_len) / 1000;
2116 len += scnprintf(&buf[len], bufsz - len, ": %9d(%2d.%1d%%)%.*s\n",
2117 rate_stats->table[i],
2118 percent / 10, percent % 10, p, hist);
2122 // Display detailed info of the last received rate
2123 last_rx = &sta->stats.last_rx.rx_vect1;
2125 len += scnprintf(&buf[len], bufsz - len,"\nLast received rate\n"
2126 " type rate LDPC STBC BEAMFM DCM DOPPLER %s\n",
2127 (nrx > 1) ? "rssi1(dBm) rssi2(dBm)" : "rssi(dBm)");
2129 fmt = last_rx->format_mod;
2130 bw = last_rx->ch_bw;
2131 pre = last_rx->pre_type;
2132 if (fmt >= FORMATMOD_HE_SU) {
2133 mcs = last_rx->he.mcs;
2134 nss = last_rx->he.nss;
2135 gi = last_rx->he.gi_type;
2136 if (fmt == FORMATMOD_HE_MU)
2137 bw = last_rx->he.ru_size;
2138 dcm = last_rx->he.dcm;
2139 } else if (fmt == FORMATMOD_VHT) {
2140 mcs = last_rx->vht.mcs;
2141 nss = last_rx->vht.nss;
2142 gi = last_rx->vht.short_gi;
2143 } else if (fmt >= FORMATMOD_HT_MF) {
2144 mcs = last_rx->ht.mcs % 8;
2145 nss = last_rx->ht.mcs / 8;;
2146 gi = last_rx->ht.short_gi;
2148 BUG_ON((mcs = legrates_lut[last_rx->leg_rate].idx) == -1);
2153 len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, gi, pre, dcm, NULL);
2155 /* flags for HT/VHT/HE */
2156 if (fmt >= FORMATMOD_HE_SU) {
2157 len += scnprintf(&buf[len], bufsz - len, " %c %c %c %c %c",
2158 last_rx->he.fec ? 'L' : ' ',
2159 last_rx->he.stbc ? 'S' : ' ',
2160 last_rx->he.beamformed ? 'B' : ' ',
2161 last_rx->he.dcm ? 'D' : ' ',
2162 last_rx->he.doppler ? 'D' : ' ');
2163 } else if (fmt == FORMATMOD_VHT) {
2164 len += scnprintf(&buf[len], bufsz - len, " %c %c %c ",
2165 last_rx->vht.fec ? 'L' : ' ',
2166 last_rx->vht.stbc ? 'S' : ' ',
2167 last_rx->vht.beamformed ? 'B' : ' ');
2168 } else if (fmt >= FORMATMOD_HT_MF) {
2169 len += scnprintf(&buf[len], bufsz - len, " %c %c ",
2170 last_rx->ht.fec ? 'L' : ' ',
2171 last_rx->ht.stbc ? 'S' : ' ');
2173 len += scnprintf(&buf[len], bufsz - len, " ");
2176 len += scnprintf(&buf[len], bufsz - len, " %-4d %d\n",
2177 last_rx->rssi1, last_rx->rssi1);
2179 len += scnprintf(&buf[len], bufsz - len, " %d\n", last_rx->rssi1);
2182 read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
2188 static ssize_t ecrnx_dbgfs_last_rx_write(struct file *file,
2189 const char __user *user_buf,
2190 size_t count, loff_t *ppos)
2192 struct ecrnx_sta *sta = NULL;
2193 struct ecrnx_hw *priv = file->private_data;
2195 /* Get the station index from MAC address */
2196 sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
2200 /* Prevent from interrupt preemption as these statistics are updated under
2202 spin_lock_bh(&priv->tx_lock);
2203 memset(sta->stats.rx_rate.table, 0,
2204 sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
2205 sta->stats.rx_rate.cpt = 0;
2206 sta->stats.rx_rate.rate_cnt = 0;
2207 spin_unlock_bh(&priv->tx_lock);
2212 DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
2214 #endif /* CONFIG_ECRNX_FULLMAC */
2219 void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw)
2221 #ifndef CONFIG_ECRNX_ESWIN
2222 /* may be called before ecrnx_dbgfs_register */
2223 if (ecrnx_hw->plat->enabled && !ecrnx_hw->debugfs.fw_trace.buf.data) {
2224 ecrnx_fw_trace_buf_init(&ecrnx_hw->debugfs.fw_trace.buf,
2225 ecrnx_ipc_fw_trace_desc_get(ecrnx_hw));
2228 if (!ecrnx_hw->debugfs.fw_trace.buf.data)
2231 _ecrnx_fw_trace_dump(&ecrnx_hw->debugfs.fw_trace.buf);
2235 void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw)
2237 _ecrnx_fw_trace_reset(&ecrnx_hw->debugfs.fw_trace, true);
2240 void ecrnx_dbgfs_trigger_fw_dump(struct ecrnx_hw *ecrnx_hw, char *reason)
2242 ecrnx_send_dbg_trigger_req(ecrnx_hw, reason);
2245 #ifdef CONFIG_ECRNX_FULLMAC
2246 static void _ecrnx_dbgfs_register_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
2248 struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
2249 struct dentry *dir_sta;
2251 struct dentry *dir_rc;
2252 struct dentry *file;
2253 struct ecrnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
2254 int nb_rx_rate = N_CCK + N_OFDM;
2255 struct ecrnx_rc_config_save *rc_cfg, *next;
2257 if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
2258 scnprintf(sta_name, sizeof(sta_name), "bc_mc");
2260 scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
2263 if (!(dir_sta = debugfs_create_dir(sta_name, ecrnx_debugfs->dir_stas)))
2265 ecrnx_debugfs->dir_sta[sta->sta_idx] = dir_sta;
2267 if (!(dir_rc = debugfs_create_dir("rc", ecrnx_debugfs->dir_sta[sta->sta_idx])))
2268 goto error_after_dir;
2270 ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = dir_rc;
2272 file = debugfs_create_file("stats", S_IRUSR, dir_rc, ecrnx_hw,
2273 &ecrnx_dbgfs_rc_stats_ops);
2274 if (IS_ERR_OR_NULL(file))
2275 goto error_after_dir;
2277 file = debugfs_create_file("fixed_rate_idx", S_IWUSR , dir_rc, ecrnx_hw,
2278 &ecrnx_dbgfs_rc_fixed_rate_idx_ops);
2279 if (IS_ERR_OR_NULL(file))
2280 goto error_after_dir;
2282 file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_rc, ecrnx_hw,
2283 &ecrnx_dbgfs_last_rx_ops);
2284 if (IS_ERR_OR_NULL(file))
2285 goto error_after_dir;
2287 if (ecrnx_hw->mod_params->ht_on)
2290 if (ecrnx_hw->mod_params->vht_on)
2291 nb_rx_rate += N_VHT;
2293 if (ecrnx_hw->mod_params->he_on)
2294 nb_rx_rate += N_HE_SU + N_HE_MU;
2296 rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
2298 if (!rate_stats->table)
2299 goto error_after_dir;
2301 rate_stats->size = nb_rx_rate;
2302 rate_stats->cpt = 0;
2303 rate_stats->rate_cnt = 0;
2305 /* By default enable rate contoller */
2306 ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
2308 /* Unless we already fix the rate for this station */
2309 list_for_each_entry_safe(rc_cfg, next, &ecrnx_debugfs->rc_config_save, list) {
2310 if (jiffies_to_msecs(jiffies - rc_cfg->timestamp) > RC_CONFIG_DUR) {
2311 list_del(&rc_cfg->list);
2313 } else if (!memcmp(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN)) {
2314 ecrnx_debugfs->rc_config[sta->sta_idx] = rc_cfg->rate;
2315 list_del(&rc_cfg->list);
2321 if ((ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) &&
2322 ecrnx_send_me_rc_set_rate(ecrnx_hw, sta->sta_idx,
2323 (u16)ecrnx_debugfs->rc_config[sta->sta_idx]))
2324 ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
2326 if (ECRNX_VIF_TYPE(ecrnx_hw->vif_table[sta->vif_idx]) == NL80211_IFTYPE_STATION)
2328 /* register the sta */
2329 struct dentry *dir_twt;
2330 struct dentry *file;
2332 if (!(dir_twt = debugfs_create_dir("twt", ecrnx_debugfs->dir_sta[sta->sta_idx])))
2333 goto error_after_dir;
2335 ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = dir_twt;
2337 file = debugfs_create_file("request", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
2338 &ecrnx_dbgfs_twt_request_ops);
2339 if (IS_ERR_OR_NULL(file))
2340 goto error_after_dir;
2342 file = debugfs_create_file("teardown", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
2343 &ecrnx_dbgfs_twt_teardown_ops);
2344 if (IS_ERR_OR_NULL(file))
2345 goto error_after_dir;
2347 sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
2352 debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
2353 ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
2354 ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
2355 ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
2357 dev_err(ecrnx_hw->dev,
2358 "Error while registering debug entry for sta %d\n", sta->sta_idx);
2361 static void _ecrnx_dbgfs_unregister_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
2363 debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
2364 /* unregister the sta */
2365 if (sta->stats.rx_rate.table) {
2366 kfree(sta->stats.rx_rate.table);
2367 sta->stats.rx_rate.table = NULL;
2369 sta->stats.rx_rate.size = 0;
2370 sta->stats.rx_rate.cpt = 0;
2371 sta->stats.rx_rate.rate_cnt = 0;
2373 /* If fix rate was set for this station, save the configuration in case
2374 we reconnect to this station within RC_CONFIG_DUR msec */
2375 if (ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) {
2376 struct ecrnx_rc_config_save *rc_cfg;
2377 rc_cfg = kmalloc(sizeof(*rc_cfg), GFP_ATOMIC);
2379 rc_cfg->rate = ecrnx_debugfs->rc_config[sta->sta_idx];
2380 rc_cfg->timestamp = jiffies;
2381 memcpy(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN);
2382 list_add_tail(&rc_cfg->list, &ecrnx_debugfs->rc_config_save);
2386 ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
2387 ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
2388 ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
2389 sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
2392 static void ecrnx_sta_work(struct work_struct *ws)
2394 struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs, sta_work);
2395 struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
2396 struct ecrnx_sta *sta;
2399 sta_idx = ecrnx_debugfs->sta_idx;
2400 if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
2401 WARN(1, "Invalid sta index %d", sta_idx);
2405 ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
2406 sta = &ecrnx_hw->sta_table[sta_idx];
2408 WARN(1, "Invalid sta %d", sta_idx);
2412 if (ecrnx_debugfs->dir_sta[sta_idx] == NULL)
2413 _ecrnx_dbgfs_register_sta(ecrnx_debugfs, sta);
2415 _ecrnx_dbgfs_unregister_sta(ecrnx_debugfs, sta);
2420 void _ecrnx_dbgfs_sta_write(struct ecrnx_debugfs *ecrnx_debugfs, uint8_t sta_idx)
2422 if (ecrnx_debugfs->unregistering)
2425 ecrnx_debugfs->sta_idx = sta_idx;
2426 schedule_work(&ecrnx_debugfs->sta_work);
2429 void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw,
2430 struct ecrnx_sta *sta)
2432 _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
2435 void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw,
2436 struct ecrnx_sta *sta)
2438 _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
2440 #endif /* CONFIG_ECRNX_FULLMAC */
2442 int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name)
2444 #ifdef CONFIG_ECRNX_SOFTMAC
2445 struct dentry *phyd = ecrnx_hw->hw->wiphy->debugfsdir;
2447 struct dentry *phyd = ecrnx_hw->wiphy->debugfsdir;
2448 #endif /* CONFIG_ECRNX_SOFTMAC */
2449 struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
2450 struct dentry *dir_drv, *dir_diags, *dir_stas;
2452 if (!(dir_drv = debugfs_create_dir(name, phyd)))
2455 ecrnx_debugfs->dir = dir_drv;
2457 if (!(dir_stas = debugfs_create_dir("stations", dir_drv)))
2460 ecrnx_debugfs->dir_stas = dir_stas;
2461 ecrnx_debugfs->unregistering = false;
2463 if (!(dir_diags = debugfs_create_dir("diags", dir_drv)))
2466 #ifdef CONFIG_ECRNX_FULLMAC
2467 INIT_WORK(&ecrnx_debugfs->sta_work, ecrnx_sta_work);
2468 INIT_LIST_HEAD(&ecrnx_debugfs->rc_config_save);
2469 ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
2472 DEBUGFS_ADD_U32(tcp_pacing_shift, dir_drv, &ecrnx_hw->tcp_pacing_shift,
2474 DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
2476 DEBUGFS_ADD_FILE(sys_stats, dir_drv, S_IRUSR);
2478 #ifdef CONFIG_ECRNX_SOFTMAC
2479 DEBUGFS_ADD_X64(rateidx, dir_drv, &ecrnx_hw->debugfs.rateidx);
2481 DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
2482 DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
2483 #ifdef CONFIG_ECRNX_MUMIMO_TX
2484 DEBUGFS_ADD_FILE(mu_group, dir_drv, S_IRUSR);
2487 #ifdef CONFIG_ECRNX_P2P_DEBUGFS
2489 /* Create a p2p directory */
2490 struct dentry *dir_p2p;
2491 if (!(dir_p2p = debugfs_create_dir("p2p", dir_drv)))
2494 /* Add file allowing to control Opportunistic PS */
2495 DEBUGFS_ADD_FILE(oppps, dir_p2p, S_IRUSR);
2496 /* Add file allowing to control Notice of Absence */
2497 DEBUGFS_ADD_FILE(noa, dir_p2p, S_IRUSR);
2499 #endif /* CONFIG_ECRNX_P2P_DEBUGFS */
2501 #if CONFIG_ECRNX_DBGFS_FW_TRACE
2502 if (ecrnx_dbgfs_register_fw_dump(ecrnx_hw, dir_drv, dir_diags))
2504 DEBUGFS_ADD_FILE(fw_dbg, dir_diags, S_IWUSR | S_IRUSR);
2506 if (!ecrnx_fw_trace_init(&ecrnx_hw->debugfs.fw_trace,
2507 ecrnx_ipc_fw_trace_desc_get(ecrnx_hw))) {
2508 DEBUGFS_ADD_FILE(fw_trace, dir_diags, S_IWUSR | S_IRUSR);
2509 if (ecrnx_hw->debugfs.fw_trace.buf.nb_compo)
2510 DEBUGFS_ADD_FILE(fw_trace_level, dir_diags, S_IWUSR | S_IRUSR);
2512 ecrnx_debugfs->fw_trace.buf.data = NULL;
2516 #ifdef CONFIG_ECRNX_RADAR
2518 struct dentry *dir_radar, *dir_sec;
2519 if (!(dir_radar = debugfs_create_dir("radar", dir_drv)))
2522 DEBUGFS_ADD_FILE(pulses_prim, dir_radar, S_IRUSR);
2523 DEBUGFS_ADD_FILE(detected, dir_radar, S_IRUSR);
2524 DEBUGFS_ADD_FILE(enable, dir_radar, S_IRUSR);
2526 if (ecrnx_hw->phy.cnt == 2) {
2527 DEBUGFS_ADD_FILE(pulses_sec, dir_radar, S_IRUSR);
2529 if (!(dir_sec = debugfs_create_dir("sec", dir_radar)))
2532 DEBUGFS_ADD_FILE(band, dir_sec, S_IWUSR | S_IRUSR);
2533 DEBUGFS_ADD_FILE(type, dir_sec, S_IWUSR | S_IRUSR);
2534 DEBUGFS_ADD_FILE(prim20, dir_sec, S_IWUSR | S_IRUSR);
2535 DEBUGFS_ADD_FILE(center1, dir_sec, S_IWUSR | S_IRUSR);
2536 DEBUGFS_ADD_FILE(center2, dir_sec, S_IWUSR | S_IRUSR);
2537 DEBUGFS_ADD_FILE(set, dir_sec, S_IWUSR | S_IRUSR);
2540 #endif /* CONFIG_ECRNX_RADAR */
2544 ecrnx_dbgfs_unregister(ecrnx_hw);
2548 void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw)
2550 struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
2552 #ifdef CONFIG_ECRNX_FULLMAC
2553 struct ecrnx_rc_config_save *cfg, *next;
2554 list_for_each_entry_safe(cfg, next, &ecrnx_debugfs->rc_config_save, list) {
2555 list_del(&cfg->list);
2558 #endif /* CONFIG_ECRNX_FULLMAC */
2560 if (!ecrnx_hw->debugfs.dir)
2563 spin_lock_bh(&ecrnx_debugfs->umh_lock);
2564 ecrnx_debugfs->unregistering = true;
2565 spin_unlock_bh(&ecrnx_debugfs->umh_lock);
2566 ecrnx_wait_um_helper(ecrnx_hw);
2567 #if CONFIG_ECRNX_DBGFS_FW_TRACE
2568 ecrnx_fw_trace_deinit(&ecrnx_hw->debugfs.fw_trace);
2570 #ifdef CONFIG_ECRNX_FULLMAC
2571 flush_work(&ecrnx_debugfs->sta_work);
2573 debugfs_remove_recursive(ecrnx_hw->debugfs.dir);
2574 ecrnx_hw->debugfs.dir = NULL;