ath6kl: Add support for setting tx rateset.
authorBala Shanmugam <bkamatch@qca.qualcomm.com>
Tue, 22 May 2012 07:53:12 +0000 (13:23 +0530)
committerKalle Valo <kvalo@qca.qualcomm.com>
Thu, 24 May 2012 07:22:22 +0000 (10:22 +0300)
Tx legacy and mcs rateset can configured using iw for
2.4 and 5 bands.  Add support for the same in driver.

kvalo: add an enum for the hw flags and rename the flag accordingly,
rename ath6kl_cfg80211_set_bitrate_mask() to a shorter version to make
it easier to indent

Signed-off-by: Bala Shanmugam <bkamatch@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h

index 7845d33deed9e1357ab4da5cdddfe24ac94a52c8..6a934e16ae83eb6280eb4ed5e678bd477b81ff88 100644 (file)
@@ -3320,6 +3320,18 @@ static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
        return 0;
 }
 
+static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      const u8 *addr,
+                                      const struct cfg80211_bitrate_mask *mask)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ath6kl_vif *vif = netdev_priv(dev);
+
+       return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
+                                          mask);
+}
+
 static const struct ieee80211_txrx_stypes
 ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
        [NL80211_IFTYPE_STATION] = {
@@ -3386,6 +3398,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .mgmt_frame_register = ath6kl_mgmt_frame_register,
        .sched_scan_start = ath6kl_cfg80211_sscan_start,
        .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
+       .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
 };
 
 void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
@@ -3616,6 +3629,17 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
                ath6kl_band_5ghz.ht_cap.cap = 0;
                ath6kl_band_5ghz.ht_cap.ht_supported = false;
        }
+
+       if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+       } else {
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+       }
+
        if (band_2gig)
                wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
        if (band_5gig)
index 991bd96f7cf645318bb5e2764bb3709941e43bf5..b1bc6bc69f2af3e1b5dbea9e512dfc803a09c9f7 100644 (file)
@@ -127,6 +127,10 @@ struct ath6kl_fw_ie {
        u8 data[0];
 };
 
+enum ath6kl_hw_flags {
+       ATH6KL_HW_FLAG_64BIT_RATES      = BIT(0),
+};
+
 #define ATH6KL_FW_API2_FILE "fw-2.bin"
 #define ATH6KL_FW_API3_FILE "fw-3.bin"
 
@@ -702,6 +706,8 @@ struct ath6kl {
                u32 testscript_addr;
                enum wmi_phy_cap cap;
 
+               u32 flags;
+
                struct ath6kl_hw_fw {
                        const char *dir;
                        const char *otp;
index 241febcd7f7c4b388b8f95ca8da0a40bb66294e4..daf24ee9d28a224f498495bdc2af03574c7cffa8 100644 (file)
@@ -42,6 +42,7 @@ static const struct ath6kl_hw hw_list[] = {
                .reserved_ram_size              = 6912,
                .refclk_hz                      = 26000000,
                .uarttx_pin                     = 8,
+               .flags                          = 0,
 
                /* hw2.0 needs override address hardcoded */
                .app_start_override_addr        = 0x944C00,
@@ -67,6 +68,7 @@ static const struct ath6kl_hw hw_list[] = {
                .refclk_hz                      = 26000000,
                .uarttx_pin                     = 8,
                .testscript_addr                = 0x57ef74,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6003_HW_2_1_1_FW_DIR,
@@ -91,6 +93,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x433900,
                .refclk_hz                      = 26000000,
                .uarttx_pin                     = 11,
+               .flags                          = ATH6KL_HW_FLAG_64BIT_RATES,
 
                .fw = {
                        .dir            = AR6004_HW_1_0_FW_DIR,
@@ -110,6 +113,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x43d400,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
+               .flags                          = ATH6KL_HW_FLAG_64BIT_RATES,
 
                .fw = {
                        .dir            = AR6004_HW_1_1_FW_DIR,
@@ -129,6 +133,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x435c00,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
+               .flags                          = ATH6KL_HW_FLAG_64BIT_RATES,
 
                .fw = {
                        .dir            = AR6004_HW_1_2_FW_DIR,
index 6ad762daa425ceebcf1353790212e5d1f12dee96..63dc4fd73c4ccc80555a939d8c9d100e4dff6bff 100644 (file)
@@ -2599,6 +2599,115 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi)
        spin_unlock_bh(&wmi->lock);
 }
 
+static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx,
+                                    const struct cfg80211_bitrate_mask *mask)
+{
+       struct sk_buff *skb;
+       int ret, mode, band;
+       u64 mcsrate, ratemask[IEEE80211_NUM_BANDS];
+       struct wmi_set_tx_select_rates64_cmd *cmd;
+
+       memset(&ratemask, 0, sizeof(ratemask));
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               /* copy legacy rate mask */
+               ratemask[band] = mask->control[band].legacy;
+               if (band == IEEE80211_BAND_5GHZ)
+                       ratemask[band] =
+                               mask->control[band].legacy << 4;
+
+               /* copy mcs rate mask */
+               mcsrate = mask->control[band].mcs[1];
+               mcsrate <<= 8;
+               mcsrate |= mask->control[band].mcs[0];
+               ratemask[band] |= mcsrate << 12;
+               ratemask[band] |= mcsrate << 28;
+       }
+
+       ath6kl_dbg(ATH6KL_DBG_WMI,
+                  "Ratemask 64 bit: 2.4:%llx 5:%llx\n",
+                  ratemask[0], ratemask[1]);
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_set_tx_select_rates64_cmd *) skb->data;
+       for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) {
+               /* A mode operate in 5GHZ band */
+               if (mode == WMI_RATES_MODE_11A ||
+                   mode == WMI_RATES_MODE_11A_HT20 ||
+                   mode == WMI_RATES_MODE_11A_HT40)
+                       band = IEEE80211_BAND_5GHZ;
+               else
+                       band = IEEE80211_BAND_2GHZ;
+               cmd->ratemask[mode] = cpu_to_le64(ratemask[band]);
+       }
+
+       ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+                                 WMI_SET_TX_SELECT_RATES_CMDID,
+                                 NO_SYNC_WMIFLAG);
+       return ret;
+}
+
+static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx,
+                                    const struct cfg80211_bitrate_mask *mask)
+{
+       struct sk_buff *skb;
+       int ret, mode, band;
+       u32 mcsrate, ratemask[IEEE80211_NUM_BANDS];
+       struct wmi_set_tx_select_rates32_cmd *cmd;
+
+       memset(&ratemask, 0, sizeof(ratemask));
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               /* copy legacy rate mask */
+               ratemask[band] = mask->control[band].legacy;
+               if (band == IEEE80211_BAND_5GHZ)
+                       ratemask[band] =
+                               mask->control[band].legacy << 4;
+
+               /* copy mcs rate mask */
+               mcsrate = mask->control[band].mcs[0];
+               ratemask[band] |= mcsrate << 12;
+               ratemask[band] |= mcsrate << 20;
+       }
+
+       ath6kl_dbg(ATH6KL_DBG_WMI,
+                  "Ratemask 32 bit: 2.4:%x 5:%x\n",
+                  ratemask[0], ratemask[1]);
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_set_tx_select_rates32_cmd *) skb->data;
+       for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) {
+               /* A mode operate in 5GHZ band */
+               if (mode == WMI_RATES_MODE_11A ||
+                   mode == WMI_RATES_MODE_11A_HT20 ||
+                   mode == WMI_RATES_MODE_11A_HT40)
+                       band = IEEE80211_BAND_5GHZ;
+               else
+                       band = IEEE80211_BAND_2GHZ;
+               cmd->ratemask[mode] = cpu_to_le32(ratemask[band]);
+       }
+
+       ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
+                                 WMI_SET_TX_SELECT_RATES_CMDID,
+                                 NO_SYNC_WMIFLAG);
+       return ret;
+}
+
+int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
+                               const struct cfg80211_bitrate_mask *mask)
+{
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES)
+               return ath6kl_set_bitrate_mask64(wmi, if_idx, mask);
+       else
+               return ath6kl_set_bitrate_mask32(wmi, if_idx, mask);
+}
+
 int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
                                       enum ath6kl_host_mode host_mode)
 {
index 47756795a26c6f6cc98af9633615d19a4dfa498c..7c94fe3e9e6d80a1dc78b63c45151540c8f66d5a 100644 (file)
@@ -1063,6 +1063,36 @@ struct wmi_power_params_cmd {
        __le16 ps_fail_event_policy;
 } __packed;
 
+/*
+ * Ratemask for below modes should be passed
+ * to WMI_SET_TX_SELECT_RATES_CMDID.
+ * AR6003 has 32 bit mask for each modes.
+ * First 12 bits for legacy rates, 13 to 20
+ * bits for HT 20 rates and 21 to 28 bits for
+ * HT 40 rates
+ */
+enum wmi_mode_phy {
+       WMI_RATES_MODE_11A = 0,
+       WMI_RATES_MODE_11G,
+       WMI_RATES_MODE_11B,
+       WMI_RATES_MODE_11GONLY,
+       WMI_RATES_MODE_11A_HT20,
+       WMI_RATES_MODE_11G_HT20,
+       WMI_RATES_MODE_11A_HT40,
+       WMI_RATES_MODE_11G_HT40,
+       WMI_RATES_MODE_MAX
+};
+
+/* WMI_SET_TX_SELECT_RATES_CMDID */
+struct wmi_set_tx_select_rates32_cmd {
+       __le32 ratemask[WMI_RATES_MODE_MAX];
+} __packed;
+
+/* WMI_SET_TX_SELECT_RATES_CMDID */
+struct wmi_set_tx_select_rates64_cmd {
+       __le64 ratemask[WMI_RATES_MODE_MAX];
+} __packed;
+
 /* WMI_SET_DISC_TIMEOUT_CMDID */
 struct wmi_disc_timeout_cmd {
        /* seconds */
@@ -2547,6 +2577,8 @@ int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx,
                          __be32 ips0, __be32 ips1);
 int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
                                       enum ath6kl_host_mode host_mode);
+int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
+                               const struct cfg80211_bitrate_mask *mask);
 int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
                                enum ath6kl_wow_mode wow_mode,
                                u32 filter, u16 host_req_delay);