Libertas: Added 11d support using cfg80211

Added 11d support for libertas driver using cfg80211. This is based on Holger
Shurig's initial work to add cfg80211 support libertas.
(https://patchwork.kernel.org/patch/64286/)

Please let us know, if there are any improvements comments.

Code is added to send 11d enable command to firmware while
initialisation and pass 11d specific information to firmware
when notifier handler is called by cfg80211.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Kiran Divekar <dkiran@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 089f072..f36cc97 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -8,6 +8,7 @@
 
 #include <linux/slab.h>
 #include <linux/if_arp.h>
+#include <linux/ieee80211.h>
 #include <net/cfg80211.h>
 #include <asm/unaligned.h>
 
@@ -2042,6 +2043,7 @@
 	 */
 	wdev->wiphy->cipher_suites = cipher_suites;
 	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+	wdev->wiphy->reg_notifier = lbs_reg_notifier;
 
 	ret = wiphy_register(wdev->wiphy);
 	if (ret < 0)
@@ -2061,6 +2063,114 @@
 	return ret;
 }
 
+/**
+ *  @brief This function sets DOMAIN INFO to FW
+ *  @param priv       pointer to struct lbs_private
+ *  @return          0; -1
+*/
+static int lbs_11d_set_domain_info(struct lbs_private *priv)
+{
+	int ret;
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
+			CMD_ACT_SET,
+			CMD_OPTION_WAITFORRSP, 0, NULL);
+	if (ret)
+		lbs_deb_11d("fail to dnld domain info\n");
+
+	return ret;
+}
+
+static void lbs_send_domain_info_cmd_fw(struct wiphy *wiphy,
+					struct regulatory_request *request)
+{
+	u8   no_of_triplet = 0;
+	u8   no_of_parsed_chan = 0;
+	u8   first_channel = 0, next_chan = 0, max_pwr = 0;
+	u8   i, flag = 0;
+	enum ieee80211_band band;
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_channel *ch;
+	struct lbs_private *priv = wiphy_priv(wiphy);
+	struct lbs_802_11d_domain_reg *domain_info = &priv->domain_reg;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CFG80211);
+
+	/* Set country code */
+	domain_info->country_code[0] = request->alpha2[0];
+	domain_info->country_code[1] = request->alpha2[1];
+	domain_info->country_code[2] = ' ';
+
+	for (band = 0; band < IEEE80211_NUM_BANDS ; band++) {
+
+		if (!wiphy->bands[band])
+			continue;
+
+		sband = wiphy->bands[band];
+
+		for (i = 0; i < sband->n_channels ; i++) {
+			ch = &sband->channels[i];
+			if (ch->flags & IEEE80211_CHAN_DISABLED)
+				continue;
+
+			if (!flag) {
+				flag = 1;
+				next_chan = first_channel = (u32) ch->hw_value;
+				max_pwr = ch->max_power;
+				no_of_parsed_chan = 1;
+				continue;
+			}
+
+			if (ch->hw_value == next_chan + 1 &&
+					ch->max_power == max_pwr) {
+				next_chan++;
+				no_of_parsed_chan++;
+			} else {
+				domain_info->triplet[no_of_triplet]
+					.chans.first_channel = first_channel;
+				domain_info->triplet[no_of_triplet]
+					.chans.num_channels = no_of_parsed_chan;
+				domain_info->triplet[no_of_triplet]
+					.chans.max_power = max_pwr;
+				no_of_triplet++;
+				flag = 0;
+			}
+		}
+		if (flag) {
+			domain_info->triplet[no_of_triplet]
+				.chans.first_channel = first_channel;
+			domain_info->triplet[no_of_triplet]
+				.chans.num_channels = no_of_parsed_chan;
+			domain_info->triplet[no_of_triplet]
+				.chans.max_power = max_pwr;
+			no_of_triplet++;
+		}
+	}
+
+	domain_info->no_triplet = no_of_triplet;
+
+	/* Set domain info */
+	ret = lbs_11d_set_domain_info(priv);
+	if (ret)
+		lbs_pr_err("11D: error setting domain info in FW\n");
+
+	lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+int lbs_reg_notifier(struct wiphy *wiphy,
+		struct regulatory_request *request)
+{
+	lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
+			"callback for domain %c%c\n", request->alpha2[0],
+			request->alpha2[1]);
+
+	lbs_send_domain_info_cmd_fw(wiphy, request);
+
+	lbs_deb_leave(LBS_DEB_CFG80211);
+
+	return 0;
+}
 
 void lbs_scan_deinit(struct lbs_private *priv)
 {