Libertas: Added 11d support using cfg80211
authorKiran Divekar <dkiran@marvell.com>
Sat, 5 Jun 2010 06:20:42 +0000 (23:20 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 23 Jun 2010 19:14:00 +0000 (15:14 -0400)
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>
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/cfg.h
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmdresp.c
drivers/net/wireless/libertas/decl.h
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/host.h
drivers/net/wireless/libertas/main.c

index 089f0722fa20926161447ece6e1e688aecc1bef5..f36cc970ad1b272feab889de344e0ee6bd66a38d 100644 (file)
@@ -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 @@ int lbs_cfg_register(struct lbs_private *priv)
         */
        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 @@ int lbs_cfg_register(struct lbs_private *priv)
        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)
 {
index eae3fd911abb0de9fc19717b1a882c1b7f625583..756fb98f9f057233d64f648264b669b69ee2df28 100644 (file)
@@ -3,11 +3,16 @@
 
 struct device;
 struct lbs_private;
+struct regulatory_request;
+struct wiphy;
 
 struct wireless_dev *lbs_cfg_alloc(struct device *dev);
 int lbs_cfg_register(struct lbs_private *priv);
 void lbs_cfg_free(struct lbs_private *priv);
 
+int lbs_reg_notifier(struct wiphy *wiphy,
+               struct regulatory_request *request);
+
 /* All of those are TODOs: */
 #define lbs_cmd_802_11_rssi(priv, cmdptr) (0)
 #define lbs_ret_802_11_rssi(priv, resp) (0)
index d8838461e59674db8b202fcf4a3a83a7d15a7cfa..6c8a9d952a01b6f5ac1a5c531dde1c64f2b162ee 100644 (file)
@@ -898,6 +898,66 @@ void lbs_set_mac_control(struct lbs_private *priv)
        lbs_deb_leave(LBS_DEB_CMD);
 }
 
+/**
+ *  @brief This function implements command CMD_802_11D_DOMAIN_INFO
+ *  @param priv       pointer to struct lbs_private
+ *  @param cmd        pointer to cmd buffer
+ *  @param cmdno      cmd ID
+ *  @param cmdOption  cmd action
+ *  @return           0
+*/
+int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
+                                struct cmd_ds_command *cmd,
+                                u16 cmdoption)
+{
+       struct cmd_ds_802_11d_domain_info *pdomaininfo =
+           &cmd->params.domaininfo;
+       struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain;
+       u8 nr_triplet = priv->domain_reg.no_triplet;
+
+       lbs_deb_enter(LBS_DEB_11D);
+
+       lbs_deb_11d("nr_triplet=%x\n", nr_triplet);
+
+       pdomaininfo->action = cpu_to_le16(cmdoption);
+       if (cmdoption == CMD_ACT_GET) {
+               cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+                                       sizeof(struct cmd_header));
+               lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
+                       le16_to_cpu(cmd->size));
+               goto done;
+       }
+
+       domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+       memcpy(domain->countrycode, priv->domain_reg.country_code,
+              sizeof(domain->countrycode));
+
+       domain->header.len = cpu_to_le16(nr_triplet
+                               * sizeof(struct ieee80211_country_ie_triplet)
+                               + sizeof(domain->countrycode));
+
+       if (nr_triplet) {
+               memcpy(domain->triplet, priv->domain_reg.triplet,
+                               nr_triplet *
+                               sizeof(struct ieee80211_country_ie_triplet));
+
+               cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+                                            le16_to_cpu(domain->header.len) +
+                                            sizeof(struct mrvl_ie_header) +
+                                            sizeof(struct cmd_header));
+       } else {
+               cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+                                       sizeof(struct cmd_header));
+       }
+
+       lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
+                       le16_to_cpu(cmd->size));
+
+done:
+       lbs_deb_enter(LBS_DEB_11D);
+       return 0;
+}
+
 /**
  *  @brief This function prepare the command before send to firmware.
  *
@@ -996,6 +1056,11 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
                ret = 0;
                goto done;
 
+       case CMD_802_11D_DOMAIN_INFO:
+               cmdptr->command = cpu_to_le16(cmd_no);
+               ret = lbs_cmd_802_11d_domain_info(priv, cmdptr, cmd_action);
+               break;
+
        case CMD_802_11_TPC_CFG:
                cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
                cmdptr->size =
index 52b543c52a93717a8cebe8f0d3d57745609a3467..b4bc103d7970b51e921f9840c64ccb78f0f7b8c1 100644 (file)
@@ -97,6 +97,52 @@ static int lbs_ret_reg_access(struct lbs_private *priv,
        return ret;
 }
 
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to struct lbs_private
+ *  @param resp    pointer to command response buffer
+ *  @return        0; -1
+ */
+int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11d_domain_info *domaininfo =
+                       &resp->params.domaininforesp;
+       struct mrvl_ie_domain_param_set *domain = &domaininfo->domain;
+       u16 action = le16_to_cpu(domaininfo->action);
+       s16 ret = 0;
+       u8 nr_triplet = 0;
+
+       lbs_deb_enter(LBS_DEB_11D);
+
+       lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
+                       (int)le16_to_cpu(resp->size));
+
+       nr_triplet = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
+               sizeof(struct ieee80211_country_ie_triplet);
+
+       lbs_deb_11d("domain info resp: nr_triplet %d\n", nr_triplet);
+
+       if (nr_triplet > MRVDRV_MAX_TRIPLET_802_11D) {
+               lbs_deb_11d("invalid number of triplets returned!!\n");
+               return -1;
+       }
+
+       switch (action) {
+       case CMD_ACT_SET:       /*Proc set action */
+               break;
+
+       case CMD_ACT_GET:
+               break;
+       default:
+               lbs_deb_11d("invalid action:%d\n", domaininfo->action);
+               ret = -1;
+               break;
+       }
+
+       lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+       return ret;
+}
+
 static inline int handle_cmd_response(struct lbs_private *priv,
                                      struct cmd_header *cmd_response)
 {
@@ -130,6 +176,10 @@ static inline int handle_cmd_response(struct lbs_private *priv,
                ret = lbs_ret_802_11_rssi(priv, resp);
                break;
 
+       case CMD_RET(CMD_802_11D_DOMAIN_INFO):
+               ret = lbs_ret_802_11d_domain_info(resp);
+               break;
+
        case CMD_RET(CMD_802_11_TPC_CFG):
                spin_lock_irqsave(&priv->driver_lock, flags);
                memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,
index 85c97d3ed88dd7d4dfedec0e2251fd1e2760bdd1..ba5438a7ba1713c3f6a5f40e54c697bb698858eb 100644 (file)
@@ -13,6 +13,7 @@
 struct lbs_private;
 struct sk_buff;
 struct net_device;
+struct cmd_ds_command;
 
 
 /* ethtool.c */
@@ -52,5 +53,9 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
 u32 lbs_fw_index_to_data_rate(u8 index);
 u8 lbs_data_rate_to_fw_index(u32 rate);
 
+int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
+               struct cmd_ds_command *cmd, u16 cmdoption);
+
+int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp);
 
 #endif
index be263acf19c4b5e8ad2903e1955f08a878012a8e..4536d9c0ad8725952ff94ff8172e7446e6b1ce75 100644 (file)
@@ -60,6 +60,9 @@ struct lbs_private {
        struct dentry *regs_dir;
        struct dentry *debugfs_regs_files[6];
 
+       /** 11D and domain regulatory data */
+       struct lbs_802_11d_domain_reg domain_reg;
+
        /* Hardware debugging */
        u32 mac_offset;
        u32 bbp_offset;
index 3809c0b4946455786ebacd4736506f83fd7882d6..112fbf167dc81045bd878ff760947cf53c5352ec 100644 (file)
@@ -389,6 +389,30 @@ struct lbs_offset_value {
        u32 value;
 } __attribute__ ((packed));
 
+#define MRVDRV_MAX_TRIPLET_802_11D              83
+
+#define COUNTRY_CODE_LEN                        3
+
+struct mrvl_ie_domain_param_set {
+       struct mrvl_ie_header header;
+
+       u8 countrycode[COUNTRY_CODE_LEN];
+       struct ieee80211_country_ie_triplet triplet[1];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11d_domain_info {
+       __le16 action;
+       struct mrvl_ie_domain_param_set domain;
+} __attribute__ ((packed));
+
+struct lbs_802_11d_domain_reg {
+       /** Country code*/
+       u8 country_code[COUNTRY_CODE_LEN];
+       /** No. of triplet*/
+       u8 no_triplet;
+       struct ieee80211_country_ie_triplet triplet[MRVDRV_MAX_TRIPLET_802_11D];
+} __attribute__ ((packed));
+
 /*
  * Define data structure for CMD_GET_HW_SPEC
  * This structure defines the response for the GET_HW_SPEC command
@@ -949,6 +973,9 @@ struct cmd_ds_command {
                struct cmd_ds_bbp_reg_access bbpreg;
                struct cmd_ds_rf_reg_access rfreg;
 
+               struct cmd_ds_802_11d_domain_info domaininfo;
+               struct cmd_ds_802_11d_domain_info domaininforesp;
+
                struct cmd_ds_802_11_tpc_cfg tpccfg;
                struct cmd_ds_802_11_afc afc;
                struct cmd_ds_802_11_led_ctrl ledgpio;
@@ -958,5 +985,4 @@ struct cmd_ds_command {
                struct cmd_ds_802_11_beacon_control bcn_ctrl;
        } params;
 } __attribute__ ((packed));
-
 #endif
index 58b031c52410ba00e000c2cf67bca85419ae1616..b519fc70f04fe5c80a533c7e231c46236f69e105 100644 (file)
@@ -652,6 +652,9 @@ static int lbs_setup_firmware(struct lbs_private *priv)
                priv->txpower_max = maxlevel;
        }
 
+       /* Send cmd to FW to enable 11D function */
+       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1);
+
        lbs_set_mac_control(priv);
 done:
        lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);