Commit 537faf26 authored by Sergey Matyukevich's avatar Sergey Matyukevich Committed by Kalle Valo

qtnfmac: modify supported interface combinations

Update existing code handling configuration of supported interface
combinations. Current implementation is not complete since it does
not report multiple interface combinations which are incompatible
with each other. Instead current implementation packs all the
supported combinations into single entry.

In fact currently qsr10g wireless card supports the following
two distinct interface combinations:

1. STA/repeater: 1 STA and/or 1 AP
   {
     { .max = 1, .types = NL80211_IFTYPE_AP},
     { .max = 1, .types = NL80211_IFTYPE_STA},
   }

2. AP/mBSS
   {
     { .max = 8, .types = NL80211_IFTYPE_AP},
   }

The list of supported configuration is reported by firmware during
wireless card bring-up. Communication protocol between firmware
and host has been updated accordingly in order to accommodate passing
multiple interface combination entries.
Signed-off-by: default avatarSergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 0b419d01
...@@ -876,29 +876,29 @@ struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) ...@@ -876,29 +876,29 @@ struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus)
return wiphy; return wiphy;
} }
static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, static int
struct ieee80211_iface_combination *if_comb, qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, struct qtnf_mac_info *mac_info)
const struct qtnf_mac_info *mac_info)
{ {
size_t max_interfaces = 0; struct ieee80211_iface_combination *if_comb;
size_t n_if_comb;
u16 interface_modes = 0; u16 interface_modes = 0;
size_t i; size_t i, j;
if_comb = mac_info->if_comb;
n_if_comb = mac_info->n_if_comb;
if (unlikely(!mac_info->limits || !mac_info->n_limits)) if (!if_comb || !n_if_comb)
return -ENOENT; return -ENOENT;
if_comb->limits = mac_info->limits; for (i = 0; i < n_if_comb; i++) {
if_comb->n_limits = mac_info->n_limits; if_comb[i].radar_detect_widths = mac_info->radar_detect_widths;
for (i = 0; i < mac_info->n_limits; i++) { for (j = 0; j < if_comb[i].n_limits; j++)
max_interfaces += mac_info->limits[i].max; interface_modes |= if_comb[i].limits[j].types;
interface_modes |= mac_info->limits[i].types;
} }
if_comb->num_different_channels = 1; wiphy->iface_combinations = if_comb;
if_comb->beacon_int_infra_match = true; wiphy->n_iface_combinations = n_if_comb;
if_comb->max_interfaces = max_interfaces;
if_comb->radar_detect_widths = mac_info->radar_detect_widths;
wiphy->interface_modes = interface_modes; wiphy->interface_modes = interface_modes;
return 0; return 0;
...@@ -907,7 +907,6 @@ static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, ...@@ -907,7 +907,6 @@ static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy,
int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
{ {
struct wiphy *wiphy = priv_to_wiphy(mac); struct wiphy *wiphy = priv_to_wiphy(mac);
struct ieee80211_iface_combination *iface_comb = NULL;
int ret; int ret;
if (!wiphy) { if (!wiphy) {
...@@ -915,14 +914,6 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) ...@@ -915,14 +914,6 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
return -EFAULT; return -EFAULT;
} }
iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL);
if (!iface_comb)
return -ENOMEM;
ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo);
if (ret)
goto out;
wiphy->frag_threshold = mac->macinfo.frag_thr; wiphy->frag_threshold = mac->macinfo.frag_thr;
wiphy->rts_threshold = mac->macinfo.rts_thr; wiphy->rts_threshold = mac->macinfo.rts_thr;
wiphy->retry_short = mac->macinfo.sretry_limit; wiphy->retry_short = mac->macinfo.sretry_limit;
...@@ -934,11 +925,12 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) ...@@ -934,11 +925,12 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->mgmt_stypes = qtnf_mgmt_stypes; wiphy->mgmt_stypes = qtnf_mgmt_stypes;
wiphy->max_remain_on_channel_duration = 5000; wiphy->max_remain_on_channel_duration = 5000;
wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs; wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
wiphy->iface_combinations = iface_comb;
wiphy->n_iface_combinations = 1;
wiphy->max_num_csa_counters = 2; wiphy->max_num_csa_counters = 2;
ret = qtnf_wiphy_setup_if_comb(wiphy, &mac->macinfo);
if (ret)
goto out;
/* Initialize cipher suits */ /* Initialize cipher suits */
wiphy->cipher_suites = qtnf_cipher_suites; wiphy->cipher_suites = qtnf_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites);
...@@ -987,12 +979,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) ...@@ -987,12 +979,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
ret = regulatory_hint(wiphy, hw_info->rd->alpha2); ret = regulatory_hint(wiphy, hw_info->rd->alpha2);
out: out:
if (ret) { return ret;
kfree(iface_comb);
return ret;
}
return 0;
} }
void qtnf_netdev_updown(struct net_device *ndev, bool up) void qtnf_netdev_updown(struct net_device *ndev, bool up)
......
...@@ -1116,19 +1116,22 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, ...@@ -1116,19 +1116,22 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
const u8 *tlv_buf, size_t tlv_buf_size) const u8 *tlv_buf, size_t tlv_buf_size)
{ {
struct ieee80211_iface_limit *limits = NULL; struct ieee80211_iface_combination *comb = NULL;
const struct qlink_iface_limit *limit_record; size_t n_comb = 0;
size_t record_count = 0, rec = 0; struct ieee80211_iface_limit *limits;
u16 tlv_type, tlv_value_len; const struct qlink_iface_comb_num *comb_num;
struct qlink_iface_comb_num *comb; const struct qlink_iface_limit_record *rec;
const struct qlink_iface_limit *lim;
u16 rec_len;
u16 tlv_type;
u16 tlv_value_len;
size_t tlv_full_len; size_t tlv_full_len;
const struct qlink_tlv_hdr *tlv; const struct qlink_tlv_hdr *tlv;
u8 *ext_capa = NULL; u8 *ext_capa = NULL;
u8 *ext_capa_mask = NULL; u8 *ext_capa_mask = NULL;
u8 ext_capa_len = 0; u8 ext_capa_len = 0;
u8 ext_capa_mask_len = 0; u8 ext_capa_mask_len = 0;
int i = 0;
mac->macinfo.n_limits = 0;
tlv = (const struct qlink_tlv_hdr *)tlv_buf; tlv = (const struct qlink_tlv_hdr *)tlv_buf;
while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) { while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) {
...@@ -1143,52 +1146,77 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, ...@@ -1143,52 +1146,77 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
switch (tlv_type) { switch (tlv_type) {
case QTN_TLV_ID_NUM_IFACE_COMB: case QTN_TLV_ID_NUM_IFACE_COMB:
if (unlikely(tlv_value_len != sizeof(*comb))) if (tlv_value_len != sizeof(*comb_num))
return -EINVAL; return -EINVAL;
comb = (void *)tlv->val; comb_num = (void *)tlv->val;
record_count = le16_to_cpu(comb->iface_comb_num);
/* free earlier iface comb memory */
qtnf_mac_iface_comb_free(mac);
mac->macinfo.n_limits = record_count; mac->macinfo.n_if_comb =
/* free earlier iface limits memory */ le32_to_cpu(comb_num->iface_comb_num);
kfree(mac->macinfo.limits);
mac->macinfo.limits =
kzalloc(sizeof(*mac->macinfo.limits) *
record_count, GFP_KERNEL);
if (unlikely(!mac->macinfo.limits)) mac->macinfo.if_comb =
kcalloc(mac->macinfo.n_if_comb,
sizeof(*mac->macinfo.if_comb),
GFP_KERNEL);
if (!mac->macinfo.if_comb)
return -ENOMEM; return -ENOMEM;
limits = mac->macinfo.limits; comb = mac->macinfo.if_comb;
pr_debug("MAC%u: %zu iface combinations\n",
mac->macid, mac->macinfo.n_if_comb);
break; break;
case QTN_TLV_ID_IFACE_LIMIT: case QTN_TLV_ID_IFACE_LIMIT:
if (unlikely(!limits)) { if (unlikely(!comb)) {
pr_warn("MAC%u: limits are not inited\n", pr_warn("MAC%u: no combinations advertised\n",
mac->macid); mac->macid);
return -EINVAL; return -EINVAL;
} }
if (unlikely(tlv_value_len != sizeof(*limit_record))) { if (n_comb >= mac->macinfo.n_if_comb) {
pr_warn("MAC%u: record size mismatch\n", pr_warn("MAC%u: combinations count exceeded\n",
mac->macid); mac->macid);
return -EINVAL; n_comb++;
break;
} }
limit_record = (void *)tlv->val; rec = (void *)tlv->val;
limits[rec].max = le16_to_cpu(limit_record->max_num); rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim);
limits[rec].types = qlink_iface_type_to_nl_mask(
le16_to_cpu(limit_record->type));
/* supported modes: STA, AP */ if (unlikely(tlv_value_len != rec_len)) {
limits[rec].types &= BIT(NL80211_IFTYPE_AP) | pr_warn("MAC%u: record %zu size mismatch\n",
BIT(NL80211_IFTYPE_AP_VLAN) | mac->macid, n_comb);
BIT(NL80211_IFTYPE_STATION); return -EINVAL;
}
pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid, limits = kzalloc(sizeof(*limits) * rec->n_limits,
limits[rec].max, limits[rec].types); GFP_KERNEL);
if (!limits)
return -ENOMEM;
comb[n_comb].num_different_channels =
rec->num_different_channels;
comb[n_comb].max_interfaces =
le16_to_cpu(rec->max_interfaces);
comb[n_comb].n_limits = rec->n_limits;
comb[n_comb].limits = limits;
for (i = 0; i < rec->n_limits; i++) {
lim = &rec->limits[i];
limits[i].max = le16_to_cpu(lim->max_num);
limits[i].types =
qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type));
pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n",
mac->macid, n_comb,
limits[i].max, limits[i].types);
}
if (limits[rec].types) n_comb++;
rec++;
break; break;
case WLAN_EID_EXT_CAPABILITY: case WLAN_EID_EXT_CAPABILITY:
if (unlikely(tlv_value_len > U8_MAX)) if (unlikely(tlv_value_len > U8_MAX))
...@@ -1216,9 +1244,9 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, ...@@ -1216,9 +1244,9 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
return -EINVAL; return -EINVAL;
} }
if (mac->macinfo.n_limits != rec) { if (mac->macinfo.n_if_comb != n_comb) {
pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n", pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n",
mac->macid, mac->macinfo.n_limits, rec); mac->macid, mac->macinfo.n_if_comb, n_comb);
return -EINVAL; return -EINVAL;
} }
......
...@@ -262,6 +262,23 @@ struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac) ...@@ -262,6 +262,23 @@ struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac)
return vif; return vif;
} }
void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac)
{
struct ieee80211_iface_combination *comb;
int i;
if (mac->macinfo.if_comb) {
for (i = 0; i < mac->macinfo.n_if_comb; i++) {
comb = &mac->macinfo.if_comb[i];
kfree(comb->limits);
comb->limits = NULL;
}
kfree(mac->macinfo.if_comb);
mac->macinfo.if_comb = NULL;
}
}
static void qtnf_vif_reset_handler(struct work_struct *work) static void qtnf_vif_reset_handler(struct work_struct *work)
{ {
struct qtnf_vif *vif = container_of(work, struct qtnf_vif, reset_work); struct qtnf_vif *vif = container_of(work, struct qtnf_vif, reset_work);
...@@ -420,10 +437,9 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid) ...@@ -420,10 +437,9 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
wiphy->bands[band] = NULL; wiphy->bands[band] = NULL;
} }
kfree(mac->macinfo.limits); qtnf_mac_iface_comb_free(mac);
kfree(mac->macinfo.extended_capabilities); kfree(mac->macinfo.extended_capabilities);
kfree(mac->macinfo.extended_capabilities_mask); kfree(mac->macinfo.extended_capabilities_mask);
kfree(wiphy->iface_combinations);
wiphy_free(wiphy); wiphy_free(wiphy);
bus->mac[macid] = NULL; bus->mac[macid] = NULL;
} }
......
...@@ -108,8 +108,8 @@ struct qtnf_mac_info { ...@@ -108,8 +108,8 @@ struct qtnf_mac_info {
u32 max_acl_mac_addrs; u32 max_acl_mac_addrs;
struct ieee80211_ht_cap ht_cap_mod_mask; struct ieee80211_ht_cap ht_cap_mod_mask;
struct ieee80211_vht_cap vht_cap_mod_mask; struct ieee80211_vht_cap vht_cap_mod_mask;
struct ieee80211_iface_limit *limits; struct ieee80211_iface_combination *if_comb;
size_t n_limits; size_t n_if_comb;
u8 *extended_capabilities; u8 *extended_capabilities;
u8 *extended_capabilities_mask; u8 *extended_capabilities_mask;
u8 extended_capabilities_len; u8 extended_capabilities_len;
...@@ -151,6 +151,7 @@ struct qtnf_hw_info { ...@@ -151,6 +151,7 @@ struct qtnf_hw_info {
struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac); struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac);
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac); struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac);
void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac);
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus); struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus);
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv, int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv,
const char *name, unsigned char name_assign_type); const char *name, unsigned char name_assign_type);
......
...@@ -1092,13 +1092,20 @@ struct qlink_tlv_hdr { ...@@ -1092,13 +1092,20 @@ struct qlink_tlv_hdr {
u8 val[0]; u8 val[0];
} __packed; } __packed;
struct qlink_iface_comb_num {
__le32 iface_comb_num;
} __packed;
struct qlink_iface_limit { struct qlink_iface_limit {
__le16 max_num; __le16 max_num;
__le16 type; __le16 type;
} __packed; } __packed;
struct qlink_iface_comb_num { struct qlink_iface_limit_record {
__le16 iface_comb_num; __le16 max_interfaces;
u8 num_different_channels;
u8 n_limits;
struct qlink_iface_limit limits[0];
} __packed; } __packed;
#define QLINK_RSSI_OFFSET 120 #define QLINK_RSSI_OFFSET 120
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment