Commit 7bfe9e22 authored by Lior David's avatar Lior David Committed by Kalle Valo

wil6210: support concurrency record in FW file

New FW which supports multiple virtual interfaces, reports
its allowed interface combinations using a special comment
record in the FW file. The format of the interface combinations
is similar to the kernel wiphy->iface_combinations.
When parsing FW file during module initialization, also parse
and validate the concurrency record, and initialize
wiphy->n_iface_combinations and wiphy->iface_combinations
accordingly.
Signed-off-by: default avatarLior David <liord@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 9f38f286
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <net/netlink.h> #include <net/netlink.h>
#include "wil6210.h" #include "wil6210.h"
#include "wmi.h" #include "wmi.h"
#include "fw.h"
#define WIL_MAX_ROC_DURATION_MS 5000 #define WIL_MAX_ROC_DURATION_MS 5000
...@@ -1896,6 +1897,71 @@ static void wil_wiphy_init(struct wiphy *wiphy) ...@@ -1896,6 +1897,71 @@ static void wil_wiphy_init(struct wiphy *wiphy)
#endif #endif
} }
int wil_cfg80211_iface_combinations_from_fw(
struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
{
struct wiphy *wiphy = wil_to_wiphy(wil);
u32 total_limits = 0;
u16 n_combos;
const struct wil_fw_concurrency_combo *combo;
const struct wil_fw_concurrency_limit *limit;
struct ieee80211_iface_combination *iface_combinations;
struct ieee80211_iface_limit *iface_limit;
int i, j;
if (wiphy->iface_combinations) {
wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
return 0;
}
combo = conc->combos;
n_combos = le16_to_cpu(conc->n_combos);
for (i = 0; i < n_combos; i++) {
total_limits += combo->n_limits;
limit = combo->limits + combo->n_limits;
combo = (struct wil_fw_concurrency_combo *)limit;
}
iface_combinations =
kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
total_limits * sizeof(struct ieee80211_iface_limit),
GFP_KERNEL);
if (!iface_combinations)
return -ENOMEM;
iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
n_combos);
combo = conc->combos;
for (i = 0; i < n_combos; i++) {
iface_combinations[i].max_interfaces = combo->max_interfaces;
iface_combinations[i].num_different_channels =
combo->n_diff_channels;
iface_combinations[i].beacon_int_infra_match =
combo->same_bi;
iface_combinations[i].n_limits = combo->n_limits;
wil_dbg_misc(wil,
"iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
i, iface_combinations[i].max_interfaces,
iface_combinations[i].num_different_channels,
iface_combinations[i].beacon_int_infra_match);
limit = combo->limits;
for (j = 0; j < combo->n_limits; j++) {
iface_limit[j].max = le16_to_cpu(limit[j].max);
iface_limit[j].types = le16_to_cpu(limit[j].types);
wil_dbg_misc(wil,
"limit %d: max %d types 0x%x\n", j,
iface_limit[j].max, iface_limit[j].types);
}
iface_combinations[i].limits = iface_limit;
iface_limit += combo->n_limits;
limit += combo->n_limits;
combo = (struct wil_fw_concurrency_combo *)limit;
}
wiphy->n_iface_combinations = n_combos;
wiphy->iface_combinations = iface_combinations;
return 0;
}
struct wil6210_priv *wil_cfg80211_init(struct device *dev) struct wil6210_priv *wil_cfg80211_init(struct device *dev)
{ {
struct wiphy *wiphy; struct wiphy *wiphy;
...@@ -1934,6 +2000,9 @@ void wil_cfg80211_deinit(struct wil6210_priv *wil) ...@@ -1934,6 +2000,9 @@ void wil_cfg80211_deinit(struct wil6210_priv *wil)
if (!wiphy) if (!wiphy)
return; return;
kfree(wiphy->iface_combinations);
wiphy->iface_combinations = NULL;
wiphy_free(wiphy); wiphy_free(wiphy);
/* do not access wil6210_priv after returning from here */ /* do not access wil6210_priv after returning from here */
} }
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#ifndef __WIL_FW_H__
#define __WIL_FW_H__
#define WIL_FW_SIGNATURE (0x36323130) /* '0126' */ #define WIL_FW_SIGNATURE (0x36323130) /* '0126' */
#define WIL_FW_FMT_VERSION (1) /* format version driver supports */ #define WIL_FW_FMT_VERSION (1) /* format version driver supports */
...@@ -71,7 +73,39 @@ struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */ ...@@ -71,7 +73,39 @@ struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
struct wil_fw_record_comment_hdr hdr; struct wil_fw_record_comment_hdr hdr;
/* capabilities (variable size), see enum wmi_fw_capability */ /* capabilities (variable size), see enum wmi_fw_capability */
u8 capabilities[0]; u8 capabilities[0];
}; } __packed;
/* FW VIF concurrency encoded inside a comment record
* Format is similar to wiphy->iface_combinations
*/
#define WIL_FW_CONCURRENCY_MAGIC (0xfedccdef)
#define WIL_FW_CONCURRENCY_REC_VER 1
struct wil_fw_concurrency_limit {
__le16 max; /* maximum number of interfaces of these types */
__le16 types; /* interface types (bit mask of enum nl80211_iftype) */
} __packed;
struct wil_fw_concurrency_combo {
u8 n_limits; /* number of wil_fw_concurrency_limit entries */
u8 max_interfaces; /* max number of concurrent interfaces allowed */
u8 n_diff_channels; /* total number of different channels allowed */
u8 same_bi; /* for APs, 1 if all APs must have same BI */
/* keep last - concurrency limits, variable size by n_limits */
struct wil_fw_concurrency_limit limits[0];
} __packed;
struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */
/* identifies concurrency record */
__le32 magic;
/* structure version, currently always 1 */
u8 version;
/* maximum number of supported MIDs _in addition_ to MID 0 */
u8 n_mids;
/* number of concurrency combinations that follow */
__le16 n_combos;
/* keep last - combinations, variable size by n_combos */
struct wil_fw_concurrency_combo combos[0];
} __packed;
/* brd file info encoded inside a comment record */ /* brd file info encoded inside a comment record */
#define WIL_BRD_FILE_MAGIC (0xabcddcbb) #define WIL_BRD_FILE_MAGIC (0xabcddcbb)
...@@ -175,3 +209,5 @@ struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */ ...@@ -175,3 +209,5 @@ struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */
__le32 command; __le32 command;
struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */ struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */
} __packed; } __packed;
#endif /* __WIL_FW_H__ */
...@@ -136,8 +136,8 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data, ...@@ -136,8 +136,8 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
size_t capa_size; size_t capa_size;
if (size < sizeof(*rec)) { if (size < sizeof(*rec)) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, wil_err_fw(wil, "capabilities record too short: %zu\n", size);
data, size, true); /* let the FW load anyway */
return 0; return 0;
} }
...@@ -158,8 +158,7 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, ...@@ -158,8 +158,7 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
const struct wil_fw_record_brd_file *rec = data; const struct wil_fw_record_brd_file *rec = data;
if (size < sizeof(*rec)) { if (size < sizeof(*rec)) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, wil_err_fw(wil, "brd_file record too short: %zu\n", size);
data, size, true);
return 0; return 0;
} }
...@@ -172,6 +171,44 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, ...@@ -172,6 +171,44 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
return 0; return 0;
} }
static int
fw_handle_concurrency(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_concurrency *rec = data;
const struct wil_fw_concurrency_combo *combo;
const struct wil_fw_concurrency_limit *limit;
size_t remain, lsize;
int i, n_combos;
if (size < sizeof(*rec)) {
wil_err_fw(wil, "concurrency record too short: %zu\n", size);
/* continue, let the FW load anyway */
return 0;
}
n_combos = le16_to_cpu(rec->n_combos);
remain = size - offsetof(struct wil_fw_record_concurrency, combos);
combo = rec->combos;
for (i = 0; i < n_combos; i++) {
if (remain < sizeof(*combo))
goto out_short;
remain -= sizeof(*combo);
limit = combo->limits;
lsize = combo->n_limits * sizeof(*limit);
if (remain < lsize)
goto out_short;
remain -= lsize;
limit += combo->n_limits;
combo = (struct wil_fw_concurrency_combo *)limit;
}
return wil_cfg80211_iface_combinations_from_fw(wil, rec);
out_short:
wil_err_fw(wil, "concurrency record truncated\n");
return 0;
}
static int static int
fw_handle_comment(struct wil6210_priv *wil, const void *data, fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size) size_t size)
...@@ -194,6 +231,13 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data, ...@@ -194,6 +231,13 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n"); wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
rc = fw_handle_brd_file(wil, data, size); rc = fw_handle_brd_file(wil, data, size);
break; break;
case WIL_FW_CONCURRENCY_MAGIC:
wil_dbg_fw(wil, "magic is WIL_FW_CONCURRENCY_MAGIC\n");
rc = fw_handle_concurrency(wil, data, size);
break;
default:
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
} }
return rc; return rc;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/types.h> #include <linux/types.h>
#include "wmi.h" #include "wmi.h"
#include "wil_platform.h" #include "wil_platform.h"
#include "fw.h"
extern bool no_fw_recovery; extern bool no_fw_recovery;
extern unsigned int mtu_max; extern unsigned int mtu_max;
...@@ -1012,6 +1013,9 @@ int wmi_stop_discovery(struct wil6210_priv *wil); ...@@ -1012,6 +1013,9 @@ int wmi_stop_discovery(struct wil6210_priv *wil);
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params, struct cfg80211_mgmt_tx_params *params,
u64 *cookie); u64 *cookie);
int wil_cfg80211_iface_combinations_from_fw(
struct wil6210_priv *wil,
const struct wil_fw_record_concurrency *conc);
#if defined(CONFIG_WIL6210_DEBUGFS) #if defined(CONFIG_WIL6210_DEBUGFS)
int wil6210_debugfs_init(struct wil6210_priv *wil); int wil6210_debugfs_init(struct wil6210_priv *wil);
......
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