|
88 | 88 |
|
89 | 89 | #define BRCMF_PS_MAX_TIMEOUT_MS 2000 |
90 | 90 |
|
| 91 | +/* Dump obss definitions */ |
| 92 | +#define ACS_MSRMNT_DELAY 100 |
| 93 | +#define CHAN_NOISE_DUMMY (-80) |
| 94 | +#define OBSS_TOKEN_IDX 15 |
| 95 | +#define IBSS_TOKEN_IDX 15 |
| 96 | +#define TX_TOKEN_IDX 14 |
| 97 | +#define CTG_TOKEN_IDX 13 |
| 98 | +#define PKT_TOKEN_IDX 15 |
| 99 | +#define IDLE_TOKEN_IDX 12 |
| 100 | + |
91 | 101 | #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ |
92 | 102 | (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) |
93 | 103 |
|
| 104 | +struct brcmf_dump_survey { |
| 105 | + u32 obss; |
| 106 | + u32 ibss; |
| 107 | + u32 no_ctg; |
| 108 | + u32 no_pckt; |
| 109 | + u32 tx; |
| 110 | + u32 idle; |
| 111 | +}; |
| 112 | + |
| 113 | +struct cca_stats_n_flags { |
| 114 | + u32 msrmnt_time; /* Time for Measurement (msec) */ |
| 115 | + u32 msrmnt_done; /* flag set when measurement complete */ |
| 116 | + char buf[1]; |
| 117 | +}; |
| 118 | + |
| 119 | +struct cca_msrmnt_query { |
| 120 | + u32 msrmnt_query; |
| 121 | + u32 time_req; |
| 122 | +}; |
| 123 | + |
94 | 124 | static bool check_vif_up(struct brcmf_cfg80211_vif *vif) |
95 | 125 | { |
96 | 126 | if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { |
@@ -7525,6 +7555,229 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], |
7525 | 7555 | return 0; |
7526 | 7556 | } |
7527 | 7557 |
|
| 7558 | +static int |
| 7559 | +brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) |
| 7560 | +{ |
| 7561 | + int i; |
| 7562 | + char *token; |
| 7563 | + char delim[] = "\n "; |
| 7564 | + unsigned long val; |
| 7565 | + int err = 0; |
| 7566 | + |
| 7567 | + token = strsep(&buf, delim); |
| 7568 | + while (token) { |
| 7569 | + if (!strcmp(token, "OBSS")) { |
| 7570 | + for (i = 0; i < OBSS_TOKEN_IDX; i++) |
| 7571 | + token = strsep(&buf, delim); |
| 7572 | + err = kstrtoul(token, 10, &val); |
| 7573 | + if (err) |
| 7574 | + break; |
| 7575 | + survey->obss = val; |
| 7576 | + } |
| 7577 | + |
| 7578 | + if (!strcmp(token, "IBSS")) { |
| 7579 | + for (i = 0; i < IBSS_TOKEN_IDX; i++) |
| 7580 | + token = strsep(&buf, delim); |
| 7581 | + err = kstrtoul(token, 10, &val); |
| 7582 | + if (err) |
| 7583 | + break; |
| 7584 | + survey->ibss = val; |
| 7585 | + } |
| 7586 | + |
| 7587 | + if (!strcmp(token, "TXDur")) { |
| 7588 | + for (i = 0; i < TX_TOKEN_IDX; i++) |
| 7589 | + token = strsep(&buf, delim); |
| 7590 | + err = kstrtoul(token, 10, &val); |
| 7591 | + if (err) |
| 7592 | + break; |
| 7593 | + survey->tx = val; |
| 7594 | + } |
| 7595 | + |
| 7596 | + if (!strcmp(token, "Category")) { |
| 7597 | + for (i = 0; i < CTG_TOKEN_IDX; i++) |
| 7598 | + token = strsep(&buf, delim); |
| 7599 | + err = kstrtoul(token, 10, &val); |
| 7600 | + if (err) |
| 7601 | + break; |
| 7602 | + survey->no_ctg = val; |
| 7603 | + } |
| 7604 | + |
| 7605 | + if (!strcmp(token, "Packet")) { |
| 7606 | + for (i = 0; i < PKT_TOKEN_IDX; i++) |
| 7607 | + token = strsep(&buf, delim); |
| 7608 | + err = kstrtoul(token, 10, &val); |
| 7609 | + if (err) |
| 7610 | + break; |
| 7611 | + survey->no_pckt = val; |
| 7612 | + } |
| 7613 | + |
| 7614 | + if (!strcmp(token, "Opp(time):")) { |
| 7615 | + for (i = 0; i < IDLE_TOKEN_IDX; i++) |
| 7616 | + token = strsep(&buf, delim); |
| 7617 | + err = kstrtoul(token, 10, &val); |
| 7618 | + if (err) |
| 7619 | + break; |
| 7620 | + survey->idle = val; |
| 7621 | + } |
| 7622 | + |
| 7623 | + token = strsep(&buf, delim); |
| 7624 | + } |
| 7625 | + |
| 7626 | + return err; |
| 7627 | +} |
| 7628 | + |
| 7629 | +static int |
| 7630 | +brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, |
| 7631 | + struct brcmf_dump_survey *survey) |
| 7632 | +{ |
| 7633 | + struct cca_stats_n_flags *results; |
| 7634 | + char *buf; |
| 7635 | + int err; |
| 7636 | + |
| 7637 | + buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); |
| 7638 | + if (unlikely(!buf)) { |
| 7639 | + brcmf_err("%s: buf alloc failed\n", __func__); |
| 7640 | + return -ENOMEM; |
| 7641 | + } |
| 7642 | + |
| 7643 | + memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); |
| 7644 | + err = brcmf_fil_iovar_data_get(ifp, "dump_obss", |
| 7645 | + buf, BRCMF_DCMD_MEDLEN); |
| 7646 | + if (err < 0) { |
| 7647 | + brcmf_err("dump_obss error (%d)\n", err); |
| 7648 | + goto exit; |
| 7649 | + } |
| 7650 | + results = (struct cca_stats_n_flags *)(buf); |
| 7651 | + |
| 7652 | + if (req.msrmnt_query) |
| 7653 | + brcmf_parse_dump_obss(results->buf, survey); |
| 7654 | + |
| 7655 | + kfree(buf); |
| 7656 | + return 0; |
| 7657 | +exit: |
| 7658 | + kfree(buf); |
| 7659 | + return -EINVAL; |
| 7660 | +} |
| 7661 | + |
| 7662 | +static s32 |
| 7663 | +cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, |
| 7664 | + struct ieee80211_channel *chan, |
| 7665 | + enum nl80211_channel_type channel_type) |
| 7666 | +{ |
| 7667 | + u16 chspec = 0; |
| 7668 | + int err = 0; |
| 7669 | + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); |
| 7670 | + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); |
| 7671 | + |
| 7672 | + /* set_channel */ |
| 7673 | + chspec = channel_to_chanspec(&cfg->d11inf, chan); |
| 7674 | + if (chspec != INVCHANSPEC) { |
| 7675 | + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); |
| 7676 | + if (err) { |
| 7677 | + brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err); |
| 7678 | + err = -EINVAL; |
| 7679 | + } |
| 7680 | + } else { |
| 7681 | + brcmf_err("failed to convert host chanspec to fw chanspec\n"); |
| 7682 | + err = -EINVAL; |
| 7683 | + } |
| 7684 | + |
| 7685 | + return err; |
| 7686 | +} |
| 7687 | + |
| 7688 | +static int |
| 7689 | +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, |
| 7690 | + int idx, struct survey_info *info) |
| 7691 | +{ |
| 7692 | + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); |
| 7693 | + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); |
| 7694 | + struct brcmf_dump_survey survey = {}; |
| 7695 | + struct ieee80211_supported_band *band; |
| 7696 | + struct ieee80211_channel *chan; |
| 7697 | + struct cca_msrmnt_query req; |
| 7698 | + u32 val, noise; |
| 7699 | + int err; |
| 7700 | + |
| 7701 | + brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); |
| 7702 | + |
| 7703 | + band = wiphy->bands[NL80211_BAND_2GHZ]; |
| 7704 | + if (band && idx >= band->n_channels) { |
| 7705 | + idx -= band->n_channels; |
| 7706 | + band = NULL; |
| 7707 | + } |
| 7708 | + |
| 7709 | + if (!band || idx >= band->n_channels) { |
| 7710 | + band = wiphy->bands[NL80211_BAND_5GHZ]; |
| 7711 | + if (idx >= band->n_channels) |
| 7712 | + return -ENOENT; |
| 7713 | + } |
| 7714 | + |
| 7715 | + /* Setting current channel to the requested channel */ |
| 7716 | + chan = &band->channels[idx]; |
| 7717 | + err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20); |
| 7718 | + if (err) { |
| 7719 | + info->channel = chan; |
| 7720 | + info->filled = 0; |
| 7721 | + return 0; |
| 7722 | + } |
| 7723 | + |
| 7724 | + if (!idx) { |
| 7725 | + /* Disable mpc */ |
| 7726 | + val = 0; |
| 7727 | + brcmf_set_mpc(ifp, val); |
| 7728 | + /* Set interface up, explicitly. */ |
| 7729 | + val = 1; |
| 7730 | + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, val); |
| 7731 | + if (err) { |
| 7732 | + brcmf_err("BRCMF_C_UP error (%d)\n", err); |
| 7733 | + return -EIO; |
| 7734 | + } |
| 7735 | + } |
| 7736 | + |
| 7737 | + /* Get noise value */ |
| 7738 | + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); |
| 7739 | + if (err) { |
| 7740 | + brcmf_err("Get Phy Noise failed, error = %d\n", err); |
| 7741 | + noise = CHAN_NOISE_DUMMY; |
| 7742 | + } |
| 7743 | + |
| 7744 | + /* Start Measurement for obss stats on current channel */ |
| 7745 | + req.msrmnt_query = 0; |
| 7746 | + req.time_req = ACS_MSRMNT_DELAY; |
| 7747 | + err = brcmf_dump_obss(ifp, req, &survey); |
| 7748 | + if (err) |
| 7749 | + goto exit; |
| 7750 | + |
| 7751 | + /* Add 10 ms for IOVAR completion */ |
| 7752 | + msleep(ACS_MSRMNT_DELAY + 10); |
| 7753 | + |
| 7754 | + /* Issue IOVAR to collect measurement results */ |
| 7755 | + req.msrmnt_query = 1; |
| 7756 | + err = brcmf_dump_obss(ifp, req, &survey); |
| 7757 | + if (err < 0) |
| 7758 | + goto exit; |
| 7759 | + |
| 7760 | + info->channel = chan; |
| 7761 | + info->noise = noise; |
| 7762 | + info->time = ACS_MSRMNT_DELAY; |
| 7763 | + info->time_busy = ACS_MSRMNT_DELAY - survey.idle; |
| 7764 | + info->time_rx = survey.obss + survey.ibss + survey.no_ctg + |
| 7765 | + survey.no_pckt; |
| 7766 | + info->time_tx = survey.tx; |
| 7767 | + info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | |
| 7768 | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | |
| 7769 | + SURVEY_INFO_TIME_TX; |
| 7770 | + |
| 7771 | + brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", |
| 7772 | + ieee80211_frequency_to_channel(chan->center_freq), |
| 7773 | + ACS_MSRMNT_DELAY); |
| 7774 | + brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", |
| 7775 | + info->noise, info->time_busy, info->time_rx, info->time_tx); |
| 7776 | + |
| 7777 | +exit: |
| 7778 | + return err; |
| 7779 | +} |
| 7780 | + |
7528 | 7781 | static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, |
7529 | 7782 | struct regulatory_request *req) |
7530 | 7783 | { |
@@ -7676,6 +7929,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, |
7676 | 7929 | if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) |
7677 | 7930 | ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; |
7678 | 7931 | #endif |
| 7932 | + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) |
| 7933 | + ops->dump_survey = brcmf_cfg80211_dump_survey; |
| 7934 | + |
7679 | 7935 | err = wiphy_register(wiphy); |
7680 | 7936 | if (err < 0) { |
7681 | 7937 | bphy_err(drvr, "Could not register wiphy device (%d)\n", err); |
|
0 commit comments