return skb;
 }
 
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                            __be32 ip_addr)
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
-       int ret;
+       int ret, extra;
+       u16 fc;
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
-       struct wl12xx_arp_rsp_template tmpl;
+       struct sk_buff *skb;
+       struct wl12xx_arp_rsp_template *tmpl;
        struct ieee80211_hdr_3addr *hdr;
        struct arphdr *arp_hdr;
 
-       memset(&tmpl, 0, sizeof(tmpl));
+       skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) +
+                           WL1271_EXTRA_SPACE_MAX);
+       if (!skb) {
+               wl1271_error("failed to allocate buffer for arp rsp template");
+               return -ENOMEM;
+       }
 
-       /* mac80211 header */
-       hdr = &tmpl.hdr;
-       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
-                                        IEEE80211_STYPE_DATA |
-                                        IEEE80211_FCTL_TODS);
-       memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
-       memcpy(hdr->addr2, vif->addr, ETH_ALEN);
-       memset(hdr->addr3, 0xff, ETH_ALEN);
+       skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX);
+
+       tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl));
+       memset(tmpl, 0, sizeof(tmpl));
 
        /* llc layer */
-       memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header));
-       tmpl.llc_type = cpu_to_be16(ETH_P_ARP);
+       memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header));
+       tmpl->llc_type = cpu_to_be16(ETH_P_ARP);
 
        /* arp header */
-       arp_hdr = &tmpl.arp_hdr;
+       arp_hdr = &tmpl->arp_hdr;
        arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
        arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
        arp_hdr->ar_hln = ETH_ALEN;
        arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
 
        /* arp payload */
-       memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN);
-       tmpl.sender_ip = ip_addr;
+       memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN);
+       tmpl->sender_ip = wlvif->ip_addr;
+
+       /* encryption space */
+       switch (wlvif->encryption_type) {
+       case KEY_TKIP:
+               extra = WL1271_EXTRA_SPACE_TKIP;
+               break;
+       case KEY_AES:
+               extra = WL1271_EXTRA_SPACE_AES;
+               break;
+       case KEY_NONE:
+       case KEY_WEP:
+       case KEY_GEM:
+               extra = 0;
+               break;
+       default:
+               wl1271_warning("Unknown encryption type: %d",
+                              wlvif->encryption_type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (extra) {
+               u8 *space = skb_push(skb, extra);
+               memset(space, 0, extra);
+       }
+
+       /* QoS header - BE */
+       if (wlvif->sta.qos)
+               memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16));
+
+       /* mac80211 header */
+       hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr));
+       memset(hdr, 0, sizeof(hdr));
+       fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS;
+       if (wlvif->sta.qos)
+               fc |= IEEE80211_STYPE_QOS_DATA;
+       else
+               fc |= IEEE80211_STYPE_DATA;
+       if (wlvif->encryption_type != KEY_NONE)
+               fc |= IEEE80211_FCTL_PROTECTED;
+
+       hdr->frame_control = cpu_to_le16(fc);
+       memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
+       memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+       memset(hdr->addr3, 0xff, ETH_ALEN);
 
        ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP,
-                                     &tmpl, sizeof(tmpl), 0,
+                                     skb->data, skb->len, 0,
                                      wlvif->basic_rate);
-
+out:
+       dev_kfree_skb(skb);
        return ret;
 }
 
 
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
                                              struct sk_buff *skb);
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                            __be32 ip_addr);
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif);
 int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif);
 
                        VIF_STATE_PRINT_INT(sta.basic_rate_idx);
                        VIF_STATE_PRINT_INT(sta.ap_rate_idx);
                        VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
+                       VIF_STATE_PRINT_INT(sta.qos);
                } else {
                        VIF_STATE_PRINT_INT(ap.global_hlid);
                        VIF_STATE_PRINT_INT(ap.bcast_hlid);
 
 int wl1271_init_templates_config(struct wl1271 *wl)
 {
        int ret, i;
+       size_t max_size;
 
        /* send empty templates for fw memory reservation */
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
        if (ret < 0)
                return ret;
 
+       max_size = sizeof(struct wl12xx_arp_rsp_template) +
+                  WL1271_EXTRA_SPACE_MAX;
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
                                      CMD_TEMPL_ARP_RSP, NULL,
-                                     sizeof
-                                     (struct wl12xx_arp_rsp_template),
+                                     max_size,
                                      0, WL1271_RATE_AUTOMATIC);
        if (ret < 0)
                return ret;
 
        if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                wl1271_info("JOIN while associated.");
 
+       /* clear encryption type */
+       wlvif->encryption_type = KEY_NONE;
+
        if (set_assoc)
                set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
 
                        wl1271_error("Could not add or replace key");
                        goto out_sleep;
                }
+
+               /*
+                * reconfiguring arp response if the unicast (or common)
+                * encryption key type was changed
+                */
+               if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+                   (sta || key_type == KEY_WEP) &&
+                   wlvif->encryption_type != key_type) {
+                       wlvif->encryption_type = key_type;
+                       ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
+                       if (ret < 0) {
+                               wl1271_warning("build arp rsp failed: %d", ret);
+                               goto out_sleep;
+                       }
+               }
                break;
 
        case DISABLE_KEY:
        if (ret < 0)
                goto out;
 
-       if (changed & BSS_CHANGED_ARP_FILTER) {
+       if ((changed & BSS_CHANGED_ARP_FILTER) ||
+           (!is_ibss && (changed & BSS_CHANGED_QOS))) {
                __be32 addr = bss_conf->arp_addr_list[0];
+               wlvif->sta.qos = bss_conf->qos;
                WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
 
                if (bss_conf->arp_addr_cnt == 1 &&
                    bss_conf->arp_filter_enabled) {
+                       wlvif->ip_addr = addr;
                        /*
                         * The template should have been configured only upon
                         * association. however, it seems that the correct ip
                         * isn't being set (when sending), so we have to
                         * reconfigure the template upon every ip change.
                         */
-                       ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr);
+                       ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
                        if (ret < 0) {
                                wl1271_warning("build arp rsp failed: %d", ret);
                                goto out;
                        ret = wl1271_acx_arp_ip_filter(wl, wlvif,
                                ACX_ARP_FILTER_ARP_FILTERING,
                                addr);
-               } else
+               } else {
+                       wlvif->ip_addr = 0;
                        ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
+               }
 
                if (ret < 0)
                        goto out;
        };
 
        /* The tx descriptor buffer and the TKIP space. */
-       wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
+       wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
                sizeof(struct wl1271_tx_hw_descr);
 
        /* unit us */
 
 
        if (info->control.hw_key &&
            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
-               extra = WL1271_TKIP_IV_SPACE;
+               extra = WL1271_EXTRA_SPACE_TKIP;
 
        if (info->control.hw_key) {
                bool is_wep;
        if (info->control.hw_key &&
            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
                int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-               memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen);
-               skb_pull(skb, WL1271_TKIP_IV_SPACE);
+               memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data,
+                       hdrlen);
+               skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
        }
 
        wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
                            info->control.hw_key->cipher ==
                            WLAN_CIPHER_SUITE_TKIP) {
                                int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-                               memmove(skb->data + WL1271_TKIP_IV_SPACE,
+                               memmove(skb->data + WL1271_EXTRA_SPACE_TKIP,
                                        skb->data, hdrlen);
-                               skb_pull(skb, WL1271_TKIP_IV_SPACE);
+                               skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
                        }
 
                        info->status.rates[0].idx = -1;
 
 #define TX_HW_RESULT_QUEUE_LEN_MASK      0xf
 
 #define WL1271_TX_ALIGN_TO 4
-#define WL1271_TKIP_IV_SPACE 4
+#define WL1271_EXTRA_SPACE_TKIP 4
+#define WL1271_EXTRA_SPACE_AES  8
+#define WL1271_EXTRA_SPACE_MAX  8
 
 /* Used for management frames and dummy packets */
 #define WL1271_TID_MGMT 7
 
                        u8 basic_rate_idx;
                        u8 ap_rate_idx;
                        u8 p2p_rate_idx;
+
+                       bool qos;
                } sta;
                struct {
                        u8 global_hlid;
        int rssi_thold;
        int last_rssi_event;
 
+       /* save the current encryption type for auto-arp config */
+       u8 encryption_type;
+       __be32 ip_addr;
+
        /* RX BA constraint value */
        bool ba_support;
        bool ba_allowed;
 
 } __packed;
 
 struct wl12xx_arp_rsp_template {
-       struct ieee80211_hdr_3addr hdr;
+       /* not including ieee80211 header */
 
        u8 llc_hdr[sizeof(rfc1042_header)];
        __be16 llc_type;