wil6210: support concurrency record in FW file
authorLior David <liord@codeaurora.org>
Mon, 26 Feb 2018 18:12:12 +0000 (20:12 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 27 Feb 2018 16:50:11 +0000 (18:50 +0200)
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: Lior David <liord@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/fw.h
drivers/net/wireless/ath/wil6210/fw_inc.c
drivers/net/wireless/ath/wil6210/wil6210.h

index eeed85cf7b10978459d6dca38027c37c987b6042..c959f152091ff5d6a17fad3d6b878f8270021c10 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/netlink.h>
 #include "wil6210.h"
 #include "wmi.h"
+#include "fw.h"
 
 #define WIL_MAX_ROC_DURATION_MS 5000
 
@@ -1896,6 +1897,71 @@ static void wil_wiphy_init(struct wiphy *wiphy)
 #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 wiphy *wiphy;
@@ -1934,6 +2000,9 @@ void wil_cfg80211_deinit(struct wil6210_priv *wil)
        if (!wiphy)
                return;
 
+       kfree(wiphy->iface_combinations);
+       wiphy->iface_combinations = NULL;
+
        wiphy_free(wiphy);
        /* do not access wil6210_priv after returning from here */
 }
index 2c7b24f6158753395152e3859a08112e811a20e7..3e7a28045cab19eca68e15701ec0f52c00d13cc4 100644 (file)
@@ -14,6 +14,8 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * 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_FMT_VERSION (1) /* format version driver supports */
@@ -71,7 +73,39 @@ struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
        struct wil_fw_record_comment_hdr hdr;
        /* capabilities (variable size), see enum wmi_fw_capability */
        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 */
 #define WIL_BRD_FILE_MAGIC (0xabcddcbb)
@@ -175,3 +209,5 @@ struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */
        __le32 command;
        struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */
 } __packed;
+
+#endif /* __WIL_FW_H__ */
index 914c0106e94b19a17c1eec73a6f2365eedb936b7..718161b829c2b9ef741c3e4b28f992cfc129b930 100644 (file)
@@ -136,8 +136,8 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
        size_t capa_size;
 
        if (size < sizeof(*rec)) {
-               wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
-                               data, size, true);
+               wil_err_fw(wil, "capabilities record too short: %zu\n", size);
+               /* let the FW load anyway */
                return 0;
        }
 
@@ -158,8 +158,7 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
        const struct wil_fw_record_brd_file *rec = data;
 
        if (size < sizeof(*rec)) {
-               wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
-                               data, size, true);
+               wil_err_fw(wil, "brd_file record too short: %zu\n", size);
                return 0;
        }
 
@@ -172,6 +171,44 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
        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
 fw_handle_comment(struct wil6210_priv *wil, const void *data,
                  size_t size)
@@ -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");
                rc = fw_handle_brd_file(wil, data, size);
                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;
index b33aa7ddf24ef1683f1bed7433398f97f0816764..81cb27af64e42a074a31b978ed47882d2e4e9a99 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/types.h>
 #include "wmi.h"
 #include "wil_platform.h"
+#include "fw.h"
 
 extern bool no_fw_recovery;
 extern unsigned int mtu_max;
@@ -1012,6 +1013,9 @@ int wmi_stop_discovery(struct wil6210_priv *wil);
 int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                         struct cfg80211_mgmt_tx_params *params,
                         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)
 int wil6210_debugfs_init(struct wil6210_priv *wil);