]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge branch 'for-linville' of git://github.com/kvalo/ath6kl
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 12 Apr 2012 19:02:19 +0000 (15:02 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 12 Apr 2012 19:02:19 +0000 (15:02 -0400)
1  2 
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/txrx.c

index bdcc68fb1e37b98a2f1edb14e0eba789491c4863,86d388f5770867d0a37355ea01867f91a5e7f7a4..28a65d3a03d0df49af91e92668b8099c966e5191
@@@ -15,8 -15,6 +15,8 @@@
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 +
  #include <linux/moduleparam.h>
  #include <linux/inetdevice.h>
  #include <linux/export.h>
@@@ -51,6 -49,8 +51,8 @@@
        .max_power      = 30,                       \
  }
  
+ #define DEFAULT_BG_SCAN_PERIOD 60
  static struct ieee80211_rate ath6kl_rates[] = {
        RATETAB_ENT(10, 0x1, 0),
        RATETAB_ENT(20, 0x2, 0),
@@@ -71,7 -71,8 +73,8 @@@
  #define ath6kl_g_rates     (ath6kl_rates + 0)
  #define ath6kl_g_rates_size    12
  
- #define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+ #define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
+ #define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
                        IEEE80211_HT_CAP_SGI_20          | \
                        IEEE80211_HT_CAP_SGI_40)
  
@@@ -128,7 -129,7 +131,7 @@@ static struct ieee80211_supported_band 
        .channels = ath6kl_5ghz_a_channels,
        .n_bitrates = ath6kl_a_rates_size,
        .bitrates = ath6kl_a_rates,
-       .ht_cap.cap = ath6kl_g_htcap,
+       .ht_cap.cap = ath6kl_a_htcap,
        .ht_cap.ht_supported = true,
  };
  
@@@ -609,6 -610,17 +612,17 @@@ static int ath6kl_cfg80211_connect(stru
                                        vif->req_bssid, vif->ch_hint,
                                        ar->connect_ctrl_flags, nw_subtype);
  
+       /* disable background scan if period is 0 */
+       if (sme->bg_scan_period == 0)
+               sme->bg_scan_period = 0xffff;
+       /* configure default value if not specified */
+       if (sme->bg_scan_period == -1)
+               sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+       ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
+                                 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
        up(&ar->sem);
  
        if (status == -EINVAL) {
@@@ -943,6 -955,8 +957,8 @@@ static int ath6kl_cfg80211_scan(struct 
        if (test_bit(CONNECTED, &vif->flags))
                force_fg_scan = 1;
  
+       vif->scan_req = request;
        if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
                     ar->fw_capabilities)) {
                /*
                                                ATH6KL_FG_SCAN_INTERVAL,
                                                n_channels, channels);
        }
-       if (ret)
+       if (ret) {
                ath6kl_err("wmi_startscan_cmd failed\n");
-       else
-               vif->scan_req = request;
+               vif->scan_req = NULL;
+       }
  
        kfree(channels);
  
@@@ -1438,9 -1452,38 +1454,38 @@@ static int ath6kl_cfg80211_change_iface
                                        struct vif_params *params)
  {
        struct ath6kl_vif *vif = netdev_priv(ndev);
+       int i;
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
  
+       /*
+        * Don't bring up p2p on an interface which is not initialized
+        * for p2p operation where fw does not have capability to switch
+        * dynamically between non-p2p and p2p type interface.
+        */
+       if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+                     vif->ar->fw_capabilities) &&
+           (type == NL80211_IFTYPE_P2P_CLIENT ||
+            type == NL80211_IFTYPE_P2P_GO)) {
+               if (vif->ar->vif_max == 1) {
+                       if (vif->fw_vif_idx != 0)
+                               return -EINVAL;
+                       else
+                               goto set_iface_type;
+               }
+               for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
+                       if (i == vif->fw_vif_idx)
+                               break;
+               }
+               if (i == vif->ar->vif_max) {
+                       ath6kl_err("Invalid interface to bring up P2P\n");
+                       return -EINVAL;
+               }
+       }
+ set_iface_type:
        switch (type) {
        case NL80211_IFTYPE_STATION:
                vif->next_mode = INFRA_NETWORK;
@@@ -1926,12 -1969,61 +1971,61 @@@ static int ath6kl_wow_sta(struct ath6k
        return 0;
  }
  
+ static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
+ {
+       return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+ }
+ static bool is_ctrl_ep_empty(struct ath6kl *ar)
+ {
+       return !ar->tx_pending[ar->ctrl_ep];
+ }
+ static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
+ {
+       int ret, left;
+       clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+       ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                                ATH6KL_HOST_MODE_ASLEEP);
+       if (ret)
+               return ret;
+       left = wait_event_interruptible_timeout(ar->event_wq,
+                                               is_hsleep_mode_procsed(vif),
+                                               WMI_TIMEOUT);
+       if (left == 0) {
+               ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
+               ret = -ETIMEDOUT;
+       } else if (left < 0) {
+               ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
+                           left);
+               ret = left;
+       }
+       if (ar->tx_pending[ar->ctrl_ep]) {
+               left = wait_event_interruptible_timeout(ar->event_wq,
+                                                       is_ctrl_ep_empty(ar),
+                                                       WMI_TIMEOUT);
+               if (left == 0) {
+                       ath6kl_warn("clear wmi ctrl data timeout\n");
+                       ret = -ETIMEDOUT;
+               } else if (left < 0) {
+                       ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
+                       ret = left;
+               }
+       }
+       return ret;
+ }
  static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
  {
        struct in_device *in_dev;
        struct in_ifaddr *ifa;
        struct ath6kl_vif *vif;
-       int ret, left;
+       int ret;
        u32 filter = 0;
        u16 i, bmiss_time;
        u8 index = 0;
@@@ -2032,39 -2124,11 +2126,11 @@@ skip_arp
        if (ret)
                return ret;
  
-       clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
-       ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
-                                                ATH6KL_HOST_MODE_ASLEEP);
+       ret = ath6kl_cfg80211_host_sleep(ar, vif);
        if (ret)
                return ret;
  
-       left = wait_event_interruptible_timeout(ar->event_wq,
-                       test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
-                       WMI_TIMEOUT);
-       if (left == 0) {
-               ath6kl_warn("timeout, didn't get host sleep cmd "
-                           "processed event\n");
-               ret = -ETIMEDOUT;
-       } else if (left < 0) {
-               ath6kl_warn("error while waiting for host sleep cmd "
-                           "processed event %d\n", left);
-               ret = left;
-       }
-       if (ar->tx_pending[ar->ctrl_ep]) {
-               left = wait_event_interruptible_timeout(ar->event_wq,
-                               ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
-               if (left == 0) {
-                       ath6kl_warn("clear wmi ctrl data timeout\n");
-                       ret = -ETIMEDOUT;
-               } else if (left < 0) {
-                       ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
-                       ret = left;
-               }
-       }
-       return ret;
+       return 0;
  }
  
  static int ath6kl_wow_resume(struct ath6kl *ar)
        return 0;
  }
  
+ static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
+ {
+       struct ath6kl_vif *vif;
+       int ret;
+       vif = ath6kl_vif_first(ar);
+       if (!vif)
+               return -EIO;
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+       ath6kl_cfg80211_stop_all(ar);
+       /* Save the current power mode before enabling power save */
+       ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+       ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
+       if (ret)
+               return ret;
+       /* Disable WOW mode */
+       ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                         ATH6KL_WOW_MODE_DISABLE,
+                                         0, 0);
+       if (ret)
+               return ret;
+       /* Flush all non control pkts in TX path */
+       ath6kl_tx_data_cleanup(ar);
+       ret = ath6kl_cfg80211_host_sleep(ar, vif);
+       if (ret)
+               return ret;
+       return 0;
+ }
+ static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
+ {
+       struct ath6kl_vif *vif;
+       int ret;
+       vif = ath6kl_vif_first(ar);
+       if (!vif)
+               return -EIO;
+       if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
+               ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
+                                              ar->wmi->saved_pwr_mode);
+               if (ret)
+                       return ret;
+       }
+       ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                                ATH6KL_HOST_MODE_AWAKE);
+       if (ret)
+               return ret;
+       ar->state = ATH6KL_STATE_ON;
+       /* Reset scan parameter to default values */
+       ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+                                       0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+       if (ret)
+               return ret;
+       return 0;
+ }
  int ath6kl_cfg80211_suspend(struct ath6kl *ar,
                            enum ath6kl_cfg_suspend_mode mode,
                            struct cfg80211_wowlan *wow)
  {
+       struct ath6kl_vif *vif;
        enum ath6kl_state prev_state;
        int ret;
  
  
        case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
  
-               ath6kl_cfg80211_stop_all(ar);
-               /* save the current power mode before enabling power save */
-               ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
+               ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
  
-               ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
+               ret = ath6kl_cfg80211_deepsleep_suspend(ar);
                if (ret) {
-                       ath6kl_warn("wmi powermode command failed during suspend: %d\n",
-                                   ret);
+                       ath6kl_err("deepsleep suspend failed: %d\n", ret);
+                       return ret;
                }
  
                ar->state = ATH6KL_STATE_DEEPSLEEP;
                break;
        }
  
+       list_for_each_entry(vif, &ar->vif_list, list)
+               ath6kl_cfg80211_scan_complete_event(vif, true);
        return 0;
  }
  EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
@@@ -2208,17 -2344,13 +2346,13 @@@ int ath6kl_cfg80211_resume(struct ath6k
                break;
  
        case ATH6KL_STATE_DEEPSLEEP:
-               if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
-                       ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
-                                                      ar->wmi->saved_pwr_mode);
-                       if (ret) {
-                               ath6kl_warn("wmi powermode command failed during resume: %d\n",
-                                           ret);
-                       }
-               }
-               ar->state = ATH6KL_STATE_ON;
+               ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
  
+               ret = ath6kl_cfg80211_deepsleep_resume(ar);
+               if (ret) {
+                       ath6kl_warn("deep sleep resume failed: %d\n", ret);
+                       return ret;
+               }
                break;
  
        case ATH6KL_STATE_CUTPOWER:
@@@ -2292,31 -2424,25 +2426,25 @@@ void ath6kl_check_wow_status(struct ath
  }
  #endif
  
- static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
-                             struct ieee80211_channel *chan,
-                             enum nl80211_channel_type channel_type)
+ static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
+                           bool ht_enable)
  {
-       struct ath6kl_vif *vif;
-       /*
-        * 'dev' could be NULL if a channel change is required for the hardware
-        * device itself, instead of a particular VIF.
-        *
-        * FIXME: To be handled properly when monitor mode is supported.
-        */
-       if (!dev)
-               return -EBUSY;
-       vif = netdev_priv(dev);
+       struct ath6kl_htcap *htcap = &vif->htcap;
  
-       if (!ath6kl_cfg80211_ready(vif))
-               return -EIO;
+       if (htcap->ht_enable == ht_enable)
+               return 0;
  
-       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
-                  __func__, chan->center_freq, chan->hw_value);
-       vif->next_chan = chan->center_freq;
+       if (ht_enable) {
+               /* Set default ht capabilities */
+               htcap->ht_enable = true;
+               htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
+                                  ath6kl_g_htcap : ath6kl_a_htcap;
+               htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
+       } else /* Disable ht */
+               memset(htcap, 0, sizeof(*htcap));
  
-       return 0;
+       return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
+                                       band, htcap);
  }
  
  static bool ath6kl_is_p2p_ie(const u8 *pos)
@@@ -2393,6 -2519,81 +2521,81 @@@ static int ath6kl_set_ies(struct ath6kl
        return 0;
  }
  
+ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type channel_type)
+ {
+       struct ath6kl_vif *vif;
+       /*
+        * 'dev' could be NULL if a channel change is required for the hardware
+        * device itself, instead of a particular VIF.
+        *
+        * FIXME: To be handled properly when monitor mode is supported.
+        */
+       if (!dev)
+               return -EBUSY;
+       vif = netdev_priv(dev);
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
+                  __func__, chan->center_freq, chan->hw_value);
+       vif->next_chan = chan->center_freq;
+       vif->next_ch_type = channel_type;
+       vif->next_ch_band = chan->band;
+       return 0;
+ }
+ static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
+                               u8 *rsn_capab)
+ {
+       const u8 *rsn_ie;
+       size_t rsn_ie_len;
+       u16 cnt;
+       if (!beacon->tail)
+               return -EINVAL;
+       rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
+       if (!rsn_ie)
+               return -EINVAL;
+       rsn_ie_len = *(rsn_ie + 1);
+       /* skip element id and length */
+       rsn_ie += 2;
+       /* skip version, group cipher */
+       if (rsn_ie_len < 6)
+               return -EINVAL;
+       rsn_ie +=  6;
+       rsn_ie_len -= 6;
+       /* skip pairwise cipher suite */
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+       cnt = *((u16 *) rsn_ie);
+       rsn_ie += (2 + cnt * 4);
+       rsn_ie_len -= (2 + cnt * 4);
+       /* skip akm suite */
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+       cnt = *((u16 *) rsn_ie);
+       rsn_ie += (2 + cnt * 4);
+       rsn_ie_len -= (2 + cnt * 4);
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+       memcpy(rsn_capab, rsn_ie, 2);
+       return 0;
+ }
  static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
                           struct cfg80211_ap_settings *info)
  {
        struct wmi_connect_cmd p;
        int res;
        int i, ret;
+       u16 rsn_capab = 0;
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
  
                p.nw_subtype = SUBTYPE_NONE;
        }
  
+       if (info->inactivity_timeout) {
+               res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
+                                                 info->inactivity_timeout);
+               if (res < 0)
+                       return res;
+       }
+       if (ath6kl_set_htcap(vif, vif->next_ch_band,
+                            vif->next_ch_type != NL80211_CHAN_NO_HT))
+               return -EIO;
+       /*
+        * Get the PTKSA replay counter in the RSN IE. Supplicant
+        * will use the RSN IE in M3 message and firmware has to
+        * advertise the same in beacon/probe response. Send
+        * the complete RSN IE capability field to firmware
+        */
+       if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
+           test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+                    ar->fw_capabilities)) {
+               res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
+                                           WLAN_EID_RSN, WMI_RSN_IE_CAPB,
+                                           (const u8 *) &rsn_capab,
+                                           sizeof(rsn_capab));
+               if (res < 0)
+                       return res;
+       }
        res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
        if (res < 0)
                return res;
@@@ -2568,6 -2798,13 +2800,13 @@@ static int ath6kl_stop_ap(struct wiphy 
        ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
        clear_bit(CONNECTED, &vif->flags);
  
+       /* Restore ht setting in firmware */
+       if (ath6kl_set_htcap(vif, IEEE80211_BAND_2GHZ, true))
+               return -EIO;
+       if (ath6kl_set_htcap(vif, IEEE80211_BAND_5GHZ, true))
+               return -EIO;
        return 0;
  }
  
@@@ -2749,6 -2986,21 +2988,21 @@@ static bool ath6kl_mgmt_powersave_ap(st
        return false;
  }
  
+ /* Check if SSID length is greater than DIRECT- */
+ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
+ {
+       const struct ieee80211_mgmt *mgmt;
+       mgmt = (const struct ieee80211_mgmt *) buf;
+       /* variable[1] contains the SSID tag length */
+       if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
+           (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
+               return true;
+       }
+       return false;
+ }
  static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
                          struct ieee80211_channel *chan, bool offchan,
                          enum nl80211_channel_type channel_type,
        bool more_data, queued;
  
        mgmt = (const struct ieee80211_mgmt *) buf;
-       if (buf + len >= mgmt->u.probe_resp.variable &&
-           vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
-           ieee80211_is_probe_resp(mgmt->frame_control)) {
+       if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
+           ieee80211_is_probe_resp(mgmt->frame_control) &&
+           ath6kl_is_p2p_go_ssid(buf, len)) {
                /*
-                * Send Probe Response frame in AP mode using a separate WMI
+                * Send Probe Response frame in GO mode using a separate WMI
                 * command to allow the target to fill in the generic IEs.
                 */
                *cookie = 0; /* TX status not supported */
@@@ -2835,6 -3087,8 +3089,8 @@@ static int ath6kl_cfg80211_sscan_start(
        if (vif->sme_state != SME_DISCONNECTED)
                return -EBUSY;
  
+       ath6kl_cfg80211_scan_complete_event(vif, true);
        for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
                ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
                                          i, DISABLE_SSID_FLAG,
@@@ -3096,6 -3350,7 +3352,7 @@@ struct net_device *ath6kl_interface_add
        vif->next_mode = nw_type;
        vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
        vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
+       vif->htcap.ht_enable = true;
  
        memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
        if (fw_vif_idx != 0)
@@@ -3183,6 -3438,10 +3440,10 @@@ int ath6kl_cfg80211_init(struct ath6kl 
        if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
                ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
  
+       if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
+                    ar->fw_capabilities))
+               ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
        ar->wiphy->probe_resp_offload =
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
index d01403a263ff66b935e70fcc04f23d2f971b0a10,2bcd45095eb923401d546476d1f41a39122d8d01..1b76aff7850834a4be33f284c76395e21334e7f4
@@@ -217,6 -217,12 +217,6 @@@ void dump_cred_dist_stats(struct htc_ta
                   target->credit_info->cur_free_credits);
  }
  
 -static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
 -{
 -      file->private_data = inode->i_private;
 -      return 0;
 -}
 -
  void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
  {
        switch (war) {
@@@ -257,7 -263,7 +257,7 @@@ static ssize_t read_file_war_stats(stru
  
  static const struct file_operations fops_war_stats = {
        .read = read_file_war_stats,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -482,7 -488,7 +482,7 @@@ static ssize_t ath6kl_fwlog_mask_write(
  }
  
  static const struct file_operations fops_fwlog_mask = {
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .read = ath6kl_fwlog_mask_read,
        .write = ath6kl_fwlog_mask_write,
        .owner = THIS_MODULE,
@@@ -616,6 -622,12 +616,12 @@@ static ssize_t read_file_tgt_stats(stru
                         "Num disconnects", tgt_stats->cs_discon_cnt);
        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
                         "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+                        "ARP pkt received", tgt_stats->arp_received);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+                        "ARP pkt matched", tgt_stats->arp_matched);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+                        "ARP pkt replied", tgt_stats->arp_replied);
  
        if (len > buf_len)
                len = buf_len;
  
  static const struct file_operations fops_tgt_stats = {
        .read = read_file_tgt_stats,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -693,7 -705,7 +699,7 @@@ static ssize_t read_file_credit_dist_st
  
  static const struct file_operations fops_credit_dist_stats = {
        .read = read_file_credit_dist_stats,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -796,7 -808,7 +802,7 @@@ static ssize_t ath6kl_endpoint_stats_wr
  }
  
  static const struct file_operations fops_endpoint_stats = {
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .read = ath6kl_endpoint_stats_read,
        .write = ath6kl_endpoint_stats_write,
        .owner = THIS_MODULE,
@@@ -869,7 -881,7 +875,7 @@@ static ssize_t ath6kl_regread_write(str
  static const struct file_operations fops_diag_reg_read = {
        .read = ath6kl_regread_read,
        .write = ath6kl_regread_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -993,7 -1005,7 +999,7 @@@ static ssize_t ath6kl_lrssi_roam_read(s
  static const struct file_operations fops_lrssi_roam_threshold = {
        .read = ath6kl_lrssi_roam_read,
        .write = ath6kl_lrssi_roam_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1055,7 -1067,7 +1061,7 @@@ static ssize_t ath6kl_regwrite_write(st
  static const struct file_operations fops_diag_reg_write = {
        .read = ath6kl_regwrite_read,
        .write = ath6kl_regwrite_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1160,7 -1172,7 +1166,7 @@@ static ssize_t ath6kl_roam_table_read(s
  
  static const struct file_operations fops_roam_table = {
        .read = ath6kl_roam_table_read,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1198,7 -1210,7 +1204,7 @@@ static ssize_t ath6kl_force_roam_write(
  
  static const struct file_operations fops_force_roam = {
        .write = ath6kl_force_roam_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1238,7 -1250,7 +1244,7 @@@ static ssize_t ath6kl_roam_mode_write(s
  
  static const struct file_operations fops_roam_mode = {
        .write = ath6kl_roam_mode_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1280,7 -1292,7 +1286,7 @@@ static ssize_t ath6kl_keepalive_write(s
  }
  
  static const struct file_operations fops_keepalive = {
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .read = ath6kl_keepalive_read,
        .write = ath6kl_keepalive_write,
        .owner = THIS_MODULE,
@@@ -1325,7 -1337,7 +1331,7 @@@ static ssize_t ath6kl_disconnect_timeou
  }
  
  static const struct file_operations fops_disconnect_timeout = {
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .read = ath6kl_disconnect_timeout_read,
        .write = ath6kl_disconnect_timeout_write,
        .owner = THIS_MODULE,
@@@ -1506,7 -1518,7 +1512,7 @@@ static ssize_t ath6kl_create_qos_write(
  
  static const struct file_operations fops_create_qos = {
        .write = ath6kl_create_qos_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1554,7 -1566,7 +1560,7 @@@ static ssize_t ath6kl_delete_qos_write(
  
  static const struct file_operations fops_delete_qos = {
        .write = ath6kl_delete_qos_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1587,7 -1599,7 +1593,7 @@@ static ssize_t ath6kl_bgscan_int_write(
  
  static const struct file_operations fops_bgscan_int = {
        .write = ath6kl_bgscan_int_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1645,7 -1657,7 +1651,7 @@@ static ssize_t ath6kl_listen_int_read(s
  static const struct file_operations fops_listen_int = {
        .read = ath6kl_listen_int_read,
        .write = ath6kl_listen_int_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
@@@ -1705,7 -1717,7 +1711,7 @@@ static ssize_t ath6kl_power_params_writ
  
  static const struct file_operations fops_power_params = {
        .write = ath6kl_power_params_write,
 -      .open = ath6kl_debugfs_open,
 +      .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
  };
index eb7cc2f5b96f14f428f2f3590cf17abe92abdb79,edd778888aa0b04531462736a4be7b98349bb55f..29ef50ea07d570036154416fcca0848b1d83e34f
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 +
  #include <linux/moduleparam.h>
  #include <linux/errno.h>
  #include <linux/export.h>
  #include <linux/of.h>
  #include <linux/mmc/sdio_func.h>
+ #include <linux/vmalloc.h>
  
  #include "core.h"
  #include "cfg80211.h"
  #include "target.h"
  #include "debug.h"
  #include "hif-ops.h"
+ #include "htc-ops.h"
  
  static const struct ath6kl_hw hw_list[] = {
        {
@@@ -258,6 -258,7 +260,7 @@@ static int ath6kl_init_service_ep(struc
        memset(&connect, 0, sizeof(connect));
  
        /* these fields are the same for all service endpoints */
+       connect.ep_cb.tx_comp_multi = ath6kl_tx_complete;
        connect.ep_cb.rx = ath6kl_rx;
        connect.ep_cb.rx_refill = ath6kl_rx_refill;
        connect.ep_cb.tx_full = ath6kl_tx_queue_full;
@@@ -487,22 -488,31 +490,31 @@@ int ath6kl_configure_target(struct ath6
                fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS);
  
        /*
-        * By default, submodes :
+        * Submodes when fw does not support dynamic interface
+        * switching:
         *              vif[0] - AP/STA/IBSS
         *              vif[1] - "P2P dev"/"P2P GO"/"P2P Client"
         *              vif[2] - "P2P dev"/"P2P GO"/"P2P Client"
+        * Otherwise, All the interface are initialized to p2p dev.
         */
  
-       for (i = 0; i < ar->max_norm_iface; i++)
-               fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
-                             (i * HI_OPTION_FW_SUBMODE_BITS);
+       if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+                    ar->fw_capabilities)) {
+               for (i = 0; i < ar->vif_max; i++)
+                       fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+                               (i * HI_OPTION_FW_SUBMODE_BITS);
+       } else {
+               for (i = 0; i < ar->max_norm_iface; i++)
+                       fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
+                               (i * HI_OPTION_FW_SUBMODE_BITS);
  
-       for (i = ar->max_norm_iface; i < ar->vif_max; i++)
-               fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
-                             (i * HI_OPTION_FW_SUBMODE_BITS);
+               for (i = ar->max_norm_iface; i < ar->vif_max; i++)
+                       fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+                               (i * HI_OPTION_FW_SUBMODE_BITS);
  
-       if (ar->p2p && ar->vif_max == 1)
-               fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV;
+               if (ar->p2p && ar->vif_max == 1)
+                       fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV;
+       }
  
        if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest,
                                  HTC_PROTOCOL_VERSION) != 0) {
         * but possible in theory.
         */
  
-       param = ar->hw.board_ext_data_addr;
-       ram_reserved_size = ar->hw.reserved_ram_size;
+       if (ar->target_type == TARGET_TYPE_AR6003) {
+               param = ar->hw.board_ext_data_addr;
+               ram_reserved_size = ar->hw.reserved_ram_size;
  
-       if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) {
-               ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
-               return -EIO;
-       }
+               if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) {
+                       ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
+                       return -EIO;
+               }
  
-       if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz,
-                                 ram_reserved_size) != 0) {
-               ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
-               return -EIO;
+               if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz,
+                                         ram_reserved_size) != 0) {
+                       ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
+                       return -EIO;
+               }
        }
  
        /* set the block size for the target */
@@@ -926,13 -938,14 +940,14 @@@ static int ath6kl_fetch_fw_apin(struct 
                        if (ar->fw != NULL)
                                break;
  
-                       ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
+                       ar->fw = vmalloc(ie_len);
  
                        if (ar->fw == NULL) {
                                ret = -ENOMEM;
                                goto out;
                        }
  
+                       memcpy(ar->fw, data, ie_len);
                        ar->fw_len = ie_len;
                        break;
                case ATH6KL_FW_IE_PATCH_IMAGE:
@@@ -1509,7 -1522,7 +1524,7 @@@ int ath6kl_init_hw_start(struct ath6kl 
        }
  
        /* setup credit distribution */
-       ath6kl_credit_setup(ar->htc_target, &ar->credit_state_info);
+       ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info);
  
        /* start HTC */
        ret = ath6kl_htc_start(ar->htc_target);
index 07071fce8a0e0196391ad06d6d4d7e18ae47da57,7f3addd6c944ca511b5072622ec9d62ea69aac19..4d818f96c415e372f2e2a8ba6d4ffc0fb164a190
@@@ -15,8 -15,6 +15,8 @@@
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 +
  #include "core.h"
  #include "hif-ops.h"
  #include "cfg80211.h"
@@@ -758,6 -756,10 +758,10 @@@ static void ath6kl_update_target_stats(
        stats->wow_evt_discarded +=
                le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded);
  
+       stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received);
+       stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied);
+       stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched);
        if (test_bit(STATS_UPDATE_PEND, &vif->flags)) {
                clear_bit(STATS_UPDATE_PEND, &vif->flags);
                wake_up(&ar->event_wq);
index 521f0be990f1db589faca36aa2e7d98af963e668,0163182e79da2d9f650148645e6e30de49fe9caf..82f2f5cb475b0b3ba8e7bebf6fea1d86472d56fa
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 +
  #include "core.h"
  #include "debug.h"
+ #include "htc-ops.h"
  
  /*
   * tid - tid_mux0..tid_mux3
@@@ -324,6 -323,7 +325,7 @@@ int ath6kl_control_tx(void *devt, struc
        cookie->map_no = 0;
        set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
                         eid, ATH6KL_CONTROL_PKT_TAG);
+       cookie->htc_pkt.skb = skb;
  
        /*
         * This interface is asynchronous, if there is an error, cleanup
@@@ -492,6 -492,7 +494,7 @@@ int ath6kl_data_tx(struct sk_buff *skb
        cookie->map_no = map_no;
        set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
                         eid, htc_tag);
+       cookie->htc_pkt.skb = skb;
  
        ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
                        skb->data, skb->len);
@@@ -572,7 -573,7 +575,7 @@@ void ath6kl_indicate_tx_activity(void *
  
  notify_htc:
        /* notify HTC, this may cause credit distribution changes */
-       ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active);
+       ath6kl_htc_activity_changed(ar->htc_target, eid, active);
  }
  
  enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
@@@ -668,9 -669,10 +671,10 @@@ static void ath6kl_tx_clear_node_map(st
        }
  }
  
- void ath6kl_tx_complete(void *context, struct list_head *packet_queue)
+ void ath6kl_tx_complete(struct htc_target *target,
+                       struct list_head *packet_queue)
  {
-       struct ath6kl *ar = context;
+       struct ath6kl *ar = target->dev->ar;
        struct sk_buff_head skb_queue;
        struct htc_packet *packet;
        struct sk_buff *skb;
@@@ -889,6 -891,7 +893,7 @@@ void ath6kl_rx_refill(struct htc_targe
                        skb->data = PTR_ALIGN(skb->data - 4, 4);
                set_htc_rxpkt_info(packet, skb, skb->data,
                                   ATH6KL_BUFFER_SIZE, endpoint);
+               packet->skb = skb;
                list_add_tail(&packet->list, &queue);
        }
  
@@@ -911,6 -914,8 +916,8 @@@ void ath6kl_refill_amsdu_rxbufs(struct 
                        skb->data = PTR_ALIGN(skb->data - 4, 4);
                set_htc_rxpkt_info(packet, skb, skb->data,
                                   ATH6KL_AMSDU_BUFFER_SIZE, 0);
+               packet->skb = skb;
                spin_lock_bh(&ar->lock);
                list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue);
                spin_unlock_bh(&ar->lock);
@@@ -1283,6 -1288,7 +1290,7 @@@ void ath6kl_rx(struct htc_target *targe
        struct wmi_data_hdr *dhdr;
        int min_hdr_len;
        u8 meta_type, dot11_hdr = 0;
+       u8 pad_before_data_start;
        int status = packet->status;
        enum htc_endpoint_id ept = packet->endpoint;
        bool is_amsdu, prev_ps, ps_state = false;
        seq_no = wmi_data_hdr_get_seqno(dhdr);
        meta_type = wmi_data_hdr_get_meta(dhdr);
        dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
+       pad_before_data_start =
+               (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT)
+                       & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK;
        skb_pull(skb, sizeof(struct wmi_data_hdr));
  
        switch (meta_type) {
                break;
        }
  
+       skb_pull(skb, pad_before_data_start);
        if (dot11_hdr)
                status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb);
        else if (!is_amsdu)
                        /* aggregation code will handle the skb */
                        return;
                }
-       }
+       } else if (!is_broadcast_ether_addr(datap->h_dest))
+               vif->net_stats.multicast++;
  
        ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
  }