mirror of https://gitee.com/openkylin/linux.git
qtnfmac: implement cfg80211 dump_survey handler
This patch implements cfg80211 dump_survey handler enabling per-channel survey data reports. Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> Signed-off-by: Avinash Patil <avinashp@quantenna.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
parent
9ef7509505
commit
7c04b43984
|
@ -677,6 +677,72 @@ qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, struct survey_info *survey)
|
||||
{
|
||||
struct qtnf_wmac *mac = wiphy_priv(wiphy);
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *chan;
|
||||
struct qtnf_chan_stats stats;
|
||||
int ret;
|
||||
|
||||
sband = wiphy->bands[NL80211_BAND_2GHZ];
|
||||
if (sband && idx >= sband->n_channels) {
|
||||
idx -= sband->n_channels;
|
||||
sband = NULL;
|
||||
}
|
||||
|
||||
if (!sband)
|
||||
sband = wiphy->bands[NL80211_BAND_5GHZ];
|
||||
|
||||
if (!sband || idx >= sband->n_channels)
|
||||
return -ENOENT;
|
||||
|
||||
chan = &sband->channels[idx];
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
survey->channel = chan;
|
||||
survey->filled = 0x0;
|
||||
|
||||
ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
if (unlikely(stats.chan_num != chan->hw_value)) {
|
||||
pr_err("received stats for channel %d instead of %d\n",
|
||||
stats.chan_num, chan->hw_value);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
survey->filled = SURVEY_INFO_TIME |
|
||||
SURVEY_INFO_TIME_SCAN |
|
||||
SURVEY_INFO_TIME_BUSY |
|
||||
SURVEY_INFO_TIME_RX |
|
||||
SURVEY_INFO_TIME_TX |
|
||||
SURVEY_INFO_NOISE_DBM;
|
||||
|
||||
survey->time_scan = stats.cca_try;
|
||||
survey->time = stats.cca_try;
|
||||
survey->time_tx = stats.cca_tx;
|
||||
survey->time_rx = stats.cca_rx;
|
||||
survey->time_busy = stats.cca_busy;
|
||||
survey->noise = stats.chan_noise;
|
||||
break;
|
||||
case -ENOENT:
|
||||
pr_debug("no stats for channel %u\n", chan->hw_value);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
pr_debug("failed to get chan(%d) stats from card\n",
|
||||
chan->hw_value);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cfg80211_ops qtn_cfg80211_ops = {
|
||||
.add_virtual_intf = qtnf_add_virtual_intf,
|
||||
.change_virtual_intf = qtnf_change_virtual_intf,
|
||||
|
@ -697,7 +763,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = {
|
|||
.set_default_mgmt_key = qtnf_set_default_mgmt_key,
|
||||
.scan = qtnf_scan,
|
||||
.connect = qtnf_connect,
|
||||
.disconnect = qtnf_disconnect
|
||||
.disconnect = qtnf_disconnect,
|
||||
.dump_survey = qtnf_dump_survey
|
||||
};
|
||||
|
||||
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
|
||||
|
|
|
@ -1333,6 +1333,62 @@ static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats,
|
||||
const u8 *payload, size_t payload_len)
|
||||
{
|
||||
struct qlink_chan_stats *qlink_stats;
|
||||
const struct qlink_tlv_hdr *tlv;
|
||||
size_t tlv_full_len;
|
||||
u16 tlv_value_len;
|
||||
u16 tlv_type;
|
||||
|
||||
tlv = (struct qlink_tlv_hdr *)payload;
|
||||
while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
|
||||
tlv_type = le16_to_cpu(tlv->type);
|
||||
tlv_value_len = le16_to_cpu(tlv->len);
|
||||
tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
|
||||
if (tlv_full_len > payload_len) {
|
||||
pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
|
||||
tlv_type, tlv_value_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (tlv_type) {
|
||||
case QTN_TLV_ID_CHANNEL_STATS:
|
||||
if (unlikely(tlv_value_len != sizeof(*qlink_stats))) {
|
||||
pr_err("invalid CHANNEL_STATS entry size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qlink_stats = (void *)tlv->val;
|
||||
|
||||
stats->chan_num = le32_to_cpu(qlink_stats->chan_num);
|
||||
stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx);
|
||||
stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx);
|
||||
stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy);
|
||||
stats->cca_try = le32_to_cpu(qlink_stats->cca_try);
|
||||
stats->chan_noise = qlink_stats->chan_noise;
|
||||
|
||||
pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n",
|
||||
stats->chan_num, stats->cca_try,
|
||||
stats->cca_busy, stats->chan_noise);
|
||||
break;
|
||||
default:
|
||||
pr_warn("Unknown TLV type: %#x\n",
|
||||
le16_to_cpu(tlv->type));
|
||||
}
|
||||
payload_len -= tlv_full_len;
|
||||
tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
|
||||
}
|
||||
|
||||
if (payload_len) {
|
||||
pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
|
||||
{
|
||||
struct sk_buff *cmd_skb, *resp_skb = NULL;
|
||||
|
@ -2176,3 +2232,54 @@ int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
|
||||
struct qtnf_chan_stats *stats)
|
||||
{
|
||||
struct sk_buff *cmd_skb, *resp_skb = NULL;
|
||||
struct qlink_cmd_get_chan_stats *cmd;
|
||||
struct qlink_resp_get_chan_stats *resp;
|
||||
size_t var_data_len;
|
||||
u16 res_code = QLINK_CMD_RESULT_OK;
|
||||
int ret = 0;
|
||||
|
||||
cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
|
||||
QLINK_CMD_CHAN_STATS,
|
||||
sizeof(*cmd));
|
||||
if (!cmd_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
qtnf_bus_lock(mac->bus);
|
||||
|
||||
cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data;
|
||||
cmd->channel = cpu_to_le16(channel);
|
||||
|
||||
ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code,
|
||||
sizeof(*resp), &var_data_len);
|
||||
if (unlikely(ret)) {
|
||||
qtnf_bus_unlock(mac->bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (unlikely(res_code != QLINK_CMD_RESULT_OK)) {
|
||||
switch (res_code) {
|
||||
case QLINK_CMD_RESULT_ENOTFOUND:
|
||||
ret = -ENOENT;
|
||||
break;
|
||||
default:
|
||||
pr_err("cmd exec failed: 0x%.4X\n", res_code);
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
resp = (struct qlink_resp_get_chan_stats *)resp_skb->data;
|
||||
ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info,
|
||||
var_data_len);
|
||||
|
||||
out:
|
||||
qtnf_bus_unlock(mac->bus);
|
||||
consume_skb(resp_skb);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -71,5 +71,7 @@ int qtnf_cmd_send_disconnect(struct qtnf_vif *vif,
|
|||
int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif,
|
||||
bool up);
|
||||
int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req);
|
||||
int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
|
||||
struct qtnf_chan_stats *stats);
|
||||
|
||||
#endif /* QLINK_COMMANDS_H_ */
|
||||
|
|
|
@ -124,6 +124,15 @@ struct qtnf_mac_info {
|
|||
size_t n_limits;
|
||||
};
|
||||
|
||||
struct qtnf_chan_stats {
|
||||
u32 chan_num;
|
||||
u32 cca_tx;
|
||||
u32 cca_rx;
|
||||
u32 cca_busy;
|
||||
u32 cca_try;
|
||||
s8 chan_noise;
|
||||
};
|
||||
|
||||
struct qtnf_wmac {
|
||||
u8 macid;
|
||||
u8 wiphy_registered;
|
||||
|
|
|
@ -164,6 +164,7 @@ enum qlink_cmd_type {
|
|||
QLINK_CMD_CHANGE_STA = 0x0051,
|
||||
QLINK_CMD_DEL_STA = 0x0052,
|
||||
QLINK_CMD_SCAN = 0x0053,
|
||||
QLINK_CMD_CHAN_STATS = 0x0054,
|
||||
QLINK_CMD_CONNECT = 0x0060,
|
||||
QLINK_CMD_DISCONNECT = 0x0061,
|
||||
};
|
||||
|
@ -433,6 +434,16 @@ struct qlink_cmd_chans_info_get {
|
|||
u8 band;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qlink_cmd_get_chan_stats - data for QLINK_CMD_CHAN_STATS command
|
||||
*
|
||||
* @channel: channel number according to 802.11 17.3.8.3.2 and Annex J
|
||||
*/
|
||||
struct qlink_cmd_get_chan_stats {
|
||||
struct qlink_cmd chdr;
|
||||
__le16 channel;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* enum qlink_reg_initiator - Indicates the initiator of a reg domain request
|
||||
*
|
||||
|
@ -635,6 +646,16 @@ struct qlink_resp_phy_params {
|
|||
u8 info[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qlink_resp_get_chan_stats - response for QLINK_CMD_CHAN_STATS cmd
|
||||
*
|
||||
* @info: variable-length channel info.
|
||||
*/
|
||||
struct qlink_resp_get_chan_stats {
|
||||
struct qlink_cmd rhdr;
|
||||
u8 info[0];
|
||||
} __packed;
|
||||
|
||||
/* QLINK Events messages related definitions
|
||||
*/
|
||||
|
||||
|
@ -807,6 +828,7 @@ enum qlink_tlv_id {
|
|||
QTN_TLV_ID_COVERAGE_CLASS = 0x0213,
|
||||
QTN_TLV_ID_IFACE_LIMIT = 0x0214,
|
||||
QTN_TLV_ID_NUM_IFACE_COMB = 0x0215,
|
||||
QTN_TLV_ID_CHANNEL_STATS = 0x0216,
|
||||
QTN_TLV_ID_STA_BASIC_COUNTERS = 0x0300,
|
||||
QTN_TLV_ID_STA_GENERIC_INFO = 0x0301,
|
||||
QTN_TLV_ID_KEY = 0x0302,
|
||||
|
@ -1008,4 +1030,13 @@ struct qlink_auth_encr {
|
|||
u8 control_port_no_encrypt;
|
||||
} __packed;
|
||||
|
||||
struct qlink_chan_stats {
|
||||
__le32 chan_num;
|
||||
__le32 cca_tx;
|
||||
__le32 cca_rx;
|
||||
__le32 cca_busy;
|
||||
__le32 cca_try;
|
||||
s8 chan_noise;
|
||||
} __packed;
|
||||
|
||||
#endif /* _QTN_QLINK_H_ */
|
||||
|
|
Loading…
Reference in New Issue